docs(subgroups): what the implementation chose differently, and one floor that cannot move

The deviations table every doc in here carries, and it is the most-read part of
them. Seven entries, of which three are worth the reader's time.

`proposeBirthCertificate` was planned to take an `origin` parameter; it already
takes the whole `DkgSession`, so it reads `key.origin()` and nothing in the
repository or the ritual view model changed. A parameter would have been a
second source for one fact, and the proposer's copy of it at that.

Phase 3 put a button on four screens and Phase 7 replaced all four with one
`LazyListScope` extension, so they were written twice. The giveaway was already
in Phase 0.3, which argued the dialect's entry point had to be a screen because
a card cannot *grow* into a list -- the same argument applied to all four, and
the section should have been planned where the buttons were.

And Phase 8's own claim was wrong: the M3 adaptive floor cannot be raised by
this work. The floors count `MaterialTheme.breakpoint`, `BoxWithConstraints`,
the pane scaffolds and the navigation components; `readableContent()` and
`NavigateBackButton` are neither, so a screen that follows every rule in
CLAUDE.md adds nothing to either count. Raising a floor to the number the tree
happens to sit at would make it fail the next time something unrelated was
removed. The floors guard two-pane layout and the navigation shell, which a
detail screen is not, and they stay where phase 6 of the conformance work left
them. Recorded rather than quietly dropped, because the next plan will want to
make the same assumption.

Pointers from docs/README.md, from docs/subgroups.md beside the multiple-parents
line, and from docs/project-feature.md -- where a project's own room was the
first thing that file said it lacked, and which now says the rows stay in the
parent's tables all the same.

Phase 8 of docs/subgroups-for-entities.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-10-03 01:56:59 +02:00
parent 5f68112717
commit e5d966bc6a
4 changed files with 49 additions and 5 deletions

View File

@@ -10,6 +10,7 @@ silent, or a decision that looked arbitrary and was not.
| [shared-key-derivation.md](./shared-key-derivation.md) | deriving further keys from the group's threshold key with FROST tweaks — why not BIP32, why no chain code, and the one rule that must not be broken |
| [subgroups.md](./subgroups.md) | a group making another group — the four ceremonies, what the parent's signature actually covers, and why the child's key is fresh rather than derived |
| [subgroups-multiple-parents.md](./subgroups-multiple-parents.md) | adding a parent to a group that already exists — two signings and no ceremony, why the child's consent is the whole gate, and the certificate a group can truthfully sign about itself |
| [subgroups-for-entities.md](./subgroups-for-entities.md) | giving a dialect, an artifact, a translation or a project a room of its own — why the link belongs on the birth certificate rather than in a column, and the screen a dialect needed before it could be asked |
| [frost-batch-signing.md](./frost-batch-signing.md) | signing several events in one ceremony — why one nonce can never cover two messages, and the phased schema, wire and UI work that follows from it |
| [marmot-membership.md](./marmot-membership.md) | how members join an MLS group, and the epoch race that makes a missing member look like a successful invite |
| [member-chronicle.md](./member-chronicle.md) | handing a member added after the work was done the group's signed record — why the events are not on the wire at all, and why the room's id is enough to verify them |

View File

@@ -1071,6 +1071,13 @@ want to remove within a week.
- **Creator or owner on a membership.** One tag and one column away, deliberately
left out — see Phase 1.
- **Removal** until Phase 8, **cross-room** until Phase 9.
- **A room of the project's own** — built since, in
[subgroups-for-entities.md](./subgroups-for-entities.md). A project can be
given a differently-keyed subgroup of its room, and the parent's quorum signs
which project the room is for. It does not move the project: the rows stay in
the parent's tables, signed by the parent's quorum, and the child's members may
hold none of them. Giving the child its own copy is the cross-room question
above and is still Phase 9.
- **Project-level approval or progress.** The server has
`artifactApproval`, `artifactVersionApproval` and
`translationArtifactVersionApproval`, none of which exists here either. A

View File

@@ -16,6 +16,28 @@ Read [subgroups.md](./subgroups.md) first — the four steps, what the birth
certificate covers, and why the child's key is fresh. Nothing here changes any of
that. Written against the code as it stood at `5875655a`.
**Built**, eight phases, one commit each. The phases below are kept as the
reasoning they were, because the code reads better against the argument it came
from than against a summary of itself. Where the implementation chose
differently the table says so, and it did so seven times worth reading:
| what the plan said | what it turned out to be |
|---|---|
| schema 23 is the next free number | 23, 24 and 25 are all taken on an unmerged branch. Taken anyway, and Phase 0.2 says why: a number is free relative to the merge base, and pre-allocating 26 moves the renumbering rather than avoiding it |
| `proposeBirthCertificate` takes an `origin` parameter, passed down from the view model | it already takes the whole `DkgSession`, so it reads `key.origin()`. A parameter would have been a second source for one fact, and the proposer's copy of it at that. Nothing in the repository or the ritual view model changed |
| Phase 3 puts a button on each of four screens | Phase 7 replaced all four with one `LazyListScope` extension. The buttons were written twice, and the section should have been planned in Phase 3 — the giveaway was there in Phase 0.3, which already argued the entry point had to *grow* into a list |
| `ArtifactDetailUIState` gains a capability flag | that, and a `ChatRepository` in the screen *and* the view model. `ProjectDetailViewModel` needed one too, which was not anticipated: `canSign` alone cannot answer whether a room is a Marmot one |
| Phase 8 raises the M3 adaptive floor | it cannot. The floors count `MaterialTheme.breakpoint`, `BoxWithConstraints`, the pane scaffolds and the navigation components; `readableContent()` and `NavigateBackButton` are neither. A new screen conforming to the rules adds nothing to either count, and raising a floor the work did not earn would make it regress for an unrelated reason |
| the card says what a subgroup is for in its supporting line | an overline. The supporting line was already carrying whether this device can open the room, which is a different question and both are worth a line |
| the empty-dialect test deletes the rows | neither DAO has a `@Delete`, and adding one for a test is a schema surface nobody asked for. It seeds a third dialect with nothing in it, which is also the truer fixture — a room defines a dialect before it has anything in it |
One more worth stating because it was a *drafting* error rather than a design
one. The first draft of this plan cited `DialectScaffold`, `AfricanLanguages` and
`docs/african-dialect-catalogue.md` as precedent. None of them are in this tree —
that work is on `claude/african-dialect-convenience-de2f69`, unmerged — so the
identity rule is now stated from the schema rather than cited, and Phase 1's
parallel is `TranslationScaffold` and `ChapterScaffolding`, which do exist here.
## What the four have in common
| entity | kind | name | room |
@@ -494,11 +516,18 @@ One commit.
- A pointer from `docs/subgroups.md` beside the multiple-parents one.
- A line in `docs/project-feature.md` under "what this does not do", since a
project's room is the first thing that file says it lacks.
- The M3 floors move. `docs/material-design-conformance.md` states "at least 12
adaptive API uses, at least 2 navigation components", checked from *below*
because they regress by being removed. Phase 2 adds a screen with
`readableContent()` and a `NavigateBackButton`, so the floor can be raised —
and should be, or a screen deleted later takes the guarantee with it silently.
- **The M3 floors do not move, and the reason is worth keeping.** The plan said
Phase 2's new screen would let the adaptive floor be raised. It does not:
`FLOOR_ADAPTIVE_APIS` counts `MaterialTheme.breakpoint`,
`BoxWithConstraints`, `currentWindowAdaptiveInfo`, the pane scaffolds and
`listPaneWidthFor`, and `FLOOR_NAVIGATION_COMPONENTS` counts the navigation
components. `readableContent()` and `NavigateBackButton` are in neither list.
So a screen that follows every rule in CLAUDE.md adds nothing to either count,
and raising a floor to the number the tree happens to sit at would make it fail
the next time something unrelated was removed. The floors guard two-pane layout
and the navigation shell; a detail screen is neither. Both stay where phase 6
of the conformance work left them.
## Verifying

View File

@@ -42,6 +42,13 @@ A subgroup is an ordinary Marmot room with an ordinary threshold key, made by an
ordinary ChillDKG ceremony, plus a **birth certificate** — the parent group's
signature over the child's room id — carried on the child's key state.
A certificate can also say what the room is *for* — a dialect, an artifact, a
translation or a project in the parent's own tables — which is one optional tag
and is the whole of [subgroups-for-entities.md](./subgroups-for-entities.md).
Nothing below changes for it: `certifies` never reads it, and a certificate with
no origin at all is valid, which it has to be, since every one signed before that
tag existed has none.
The key state grows two tags for it, not one, and only the first is an artefact:
| tag | what it is |