docs: record what the subgroups plan built, and the six places it chose differently

All nine phases are built, one commit each. The phases are kept as written --
they are the reasoning, and the code reads better against the argument it came
from than against a summary of itself -- with a table of where the building
disagreed with the plan.

Six worth reading. `openCeremony` was never built, because a wrapper over two
repository calls the picker already makes would be a third name for one act.
`MarmotGroupCreation` is reached through the repository rather than called from a
view model, because view models here talk to repositories and managers take the
database. The guards' tests are in jvmTest rather than pure, because every refusal
reads the database and a pure version would test less. Phase 4 added two tags
rather than one, the second fixing a bug older than subgroups -- every robust
group has been arriving nameless on every device but its creator's. `stateFrom`
needed a third reader and a wrapper, because "tag present and unreadable" looks
identical to "absent" through a parser, and "refused" has to be distinguishable
from "none claimed". And Phase 8's two capability refusals short-circuited the
tests already written, which is how it came out that the fixtures had never had a
parent that could sign.

Two the plan got right and worth keeping if this is ever rewritten: the
key-package check moved to the picker on review, before a line was built, and it
is the difference between a subgroup failing in a second and failing after three
ceremonies; and the founding-roster rule has a test whose job is to fail the day
somebody adds the comparison that looks obviously missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-08 23:52:19 +02:00
parent 3ae49bd338
commit 0180425904
2 changed files with 24 additions and 3 deletions

View File

@@ -9,8 +9,29 @@ Read [shared-key-derivation.md](./shared-key-derivation.md) and
built on the property they state — that a room's id *is* the key it signs with —
and on the ceremony they describe, which a subgroup runs again from scratch.
**Not built.** Phases 1-9, one commit each. Written against the code as it stands
at `b50b1762`.
**Built**, phases 1-9, one commit each. The phases are kept as written because
they are the reasoning, and the code reads better against the argument it came
from than against a summary of itself. Where the implementation chose differently
the section says so, and it did so six times worth reading:
| what the plan said | what it turned out to be |
|---|---|
| `SubgroupManager.openCeremony` | never built. The picker calls `createNip17ChatRoom` and `proposeRitual` directly, exactly as robust-group creation does -- a wrapper over two repository calls would have been a third name for one act |
| `MarmotGroupCreation.create` called from the view model | reached through `ChatRepository.createMarmotGroup`. View models here talk to repositories and managers take the database; the first cut reached for a `database` the interface does not expose, and should not |
| a pure `SubgroupGuardsTest` in commonTest | jvmTest. Every refusal reads the database -- a key-holding session, an admin flag, a ceremony in the derived room -- so a pure version would have tested less, not differently |
| Phase 4 adds one tag | two. `subject` fixes a bug older than subgroups: `getOrCreateNip17ChatRoom` has always read a subject off the payload and the ritual path never wrote one, so every robust group arrived nameless on every device but its creator's |
| `stateFrom` reads the parentage with two parsers | three, and a wrapper. A tag present and unreadable looks absent through a parser, so `claimsParentage` reads the tag *names*; and "refused" has to be told from "none claimed", which a bare nullable cannot carry |
| Phase 8 adds three refusals to a settled function | it short-circuited the tests that were already there. `canSign` and `isAdmin` run before the picking rules, so the fixtures had to grow a parent that can actually sign and a coordinator who is actually an admin -- see the note in that phase |
Two things the plan got right that are worth keeping if any of this is rewritten.
The key-package check moved to the picker before a line of it was built, on
review, and it is the difference between a subgroup failing in a second and
failing after three ceremonies. And the founding-roster rule -- that `certifies`
must not compare the name or the `p` tags against anything current -- has a test
whose job is to *fail* the day somebody adds the comparison that looks obviously
missing.
Written against the code as it stood at `b50b1762`.
## What a subgroup is