diff --git a/docs/README.md b/docs/README.md index f47439a6..af3580e2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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 | diff --git a/docs/project-feature.md b/docs/project-feature.md index 8c06efc8..c68f916d 100644 --- a/docs/project-feature.md +++ b/docs/project-feature.md @@ -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 diff --git a/docs/subgroups-for-entities.md b/docs/subgroups-for-entities.md index 959297f5..4604095f 100644 --- a/docs/subgroups-for-entities.md +++ b/docs/subgroups-for-entities.md @@ -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 diff --git a/docs/subgroups.md b/docs/subgroups.md index 1ffdf8ad..905fb710 100644 --- a/docs/subgroups.md +++ b/docs/subgroups.md @@ -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 |