Phase 7b of docs/project-feature.md -- the write path. The room's "Create
project" button now opens a form instead of `ImplementationPendingRoute`.
Nothing is created here. What goes out is a proposal, and the project appears
on every member's device at once -- authored by the room's own key -- when
enough members sign. That is the difference from submitting one: a submission
says "I am putting this in front of the group" and the group's only recourse
afterwards is social; a signature is the group saying it, and it takes a quorum
to say.
One session over the project and its contents. The memberships cannot be built
by this screen, because each carries the project's id and that is a hash over
the group's key at the room's derivation path -- it does not exist until the
proposal is made. So the project leads and the memberships are derived from it,
the same shape `AddArtifactViewModel` uses for an artifact and its first
version.
The creator is not a field: it is whoever is filling the form in, the view model
already has it, and a picker would only let somebody credit the wrong person for
something the group is about to sign.
The owners are a field, pre-filled with this group. Most projects never touch it
and that is why it is pre-filled rather than hidden -- a group making something
for somebody else needs the question asked, and a group that is not needs to see
the answer it is giving. Two guards, both because the alternative is silent: an
entry that is not 64 characters is refused on the way in, since
`ContributorTag.parse` would drop it and the owner would vanish between the form
and the signature; and the last owner cannot be removed, since
`ProjectEvent.build` refuses an empty set and a button that throws is worse than
one that is off.
A project with no contents is allowed, unlike an artifact with no version. An
artifact with no version is inert because nothing can attach to it; an empty
project is a name waiting for work.
M3 budgets met.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>