Phase 4 of docs/project-feature.md. Three branches in `ProposedEvent.summarize`,
or members would be asked to sign "Event of kind 30330".
The project's branch shows the creator, and that is not decoration. The creator
tag is the group attesting that X started this; the creator signs nothing, the
room does, so a member could propose a project crediting somebody else and a
quorum that does not read would sign it. Nothing checks this and nothing can --
the only mitigation is that the claim is in front of the person agreeing to it.
`summarize` gains two optional parameters. `chatRoomId` is read by one branch
and only to leave something out: a project owned by the room it is in is the
ordinary case, and saying "owned by this group" every time is how a member
learns to stop reading the line that matters when somebody else is on it. All
four call sites already had the room, so this is threaded rather than plumbed --
including through `WhatIsBeingSigned`, which took the events and not the state.
`resolve` is the seam for turning keys and ids into names, synchronous because
two of the four call sites are composables and cannot await a database. It
defaults to resolving nothing, which is honest rather than finished: a
membership event carries two 64-character ids, so without it the summary is a
truncated hash. That is tolerable in the batch that founds a project -- the
project's own line says what is happening and the memberships read as its
continuation, and the project id is unresolvable there anyway since nothing has
applied it yet. It is not tolerable for a membership proposed on its own, which
is Phase 7's second screen, and that is where the resolver gets filled in.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>