Phase 5 of docs/subgroups-multiple-parents.md, the UI. Phases 1-4 were dark; this is
the flag, and it is the button. A group can now be given a second parent from the app.
**Two affordances, on the child's own detail screen, under the parent cards.** Both
belong there and not on the adopting group's screen: the child is what acquires the
relationship, its quorum is what decides, and it mirrors the parent screen's existing
"add subgroup".
`SelectAdoptingGroupScreen` is the only new screen, because the acceptance needs none
-- by then there is something to accept and a card does it. Its list is the **profile's
own rooms** and cannot be anything else: a certificate is proposed inside the adopting
group, so a candidate has to be a room the key in the chair can act in.
`adoptingCandidatesFor` filters through `refuseAdoption` itself rather than through a
copy of its rules, so screen and manager cannot drift. An empty list is a fact about
this member rather than a failure, and the empty state says the reason outright -- a
group cannot be offered as a parent by somebody who is not in it, so the remedy is a
person rather than a button.
**Tapping a row asks; it does not adopt, and the words have to carry that.** The
snackbar says "asked that group to adopt this one", and the screen hands straight back
to the room rather than going anywhere new. The offer card then says a group is a
subgroup of another *only once its own quorum says so*, because rendered as a fact
about the lineage it would be the consent gap reopened from the other end -- the one
Phase 3 closed in the transcript.
**The accept button is offered to whoever opens the screen and can act**, not only to
the member who asked, for the reason docs/subgroups.md gives for the ritual screen's
rung: every step after a ceremony is open to any admin holding a share. After it, the
screen reloads rather than crossing the offer off -- the offer leaves the list when the
state is signed and filed, which is a quorum away, so the card stays until the group has
actually agreed.
`canAddParent` wants a key state as well as a share, which `canAddSubgroup` does not:
the certificate carries this group's threshold key and the acceptance re-announces that
very state, so a room that has agreed nothing has neither half to work with. Hidden
rather than disabled, the way the subgroup entry is.
The picker is a pushed screen, so it takes an `onNavigateBack` and puts
`NavigateBackButton` in its app bar's leading slot -- the rule `CLAUDE.md` gained while
this was still a note, and the desktop target has neither a system back nor an edge
swipe to fall back on.
`offersTo` reads every certificate this device holds in **any** room rather than asking
this one, because a certificate about this group is signed in the other group's room and
filed against it -- asking this room would never find it. Each is checked by `classify`,
and parents already in the lineage drop out, since an offer that has been accepted is
not an offer.
Ten strings in the catalogue in sentence case, spacing from `MaterialTheme.spacing`,
colour from roles, `Decorative` on every icon beside a label, and the notifier read
outside the handler with the caller's scope -- an acceptance is shown while the screen
reloads around it.
`inComparableGroups` stays private to `ChatRoomDetailScreen`: the upstream commit that
moved it to the key extensions is in the fork's tree and not this one, and a second copy
to add three spaces to an id is not worth having.
**What is reachable and what is not.** Adding a parent and accepting one are both
wired. `SubgroupManager.proposeDisavowal` is built, tested and has no button -- dropping
a parent is a destructive-looking act that wants a confirmation step, and it belongs in
its own change rather than bolted onto this one.
454 common tests, 962 jvm tests, `m3Audit` meets every budget with 0 title-case strings
and 0 dp literals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>