Audited the whole crew surface. The trust model is genuinely solid:
membership is double-sided and unforgeable (founder p-tags + the
member's own NIP-32 kind-1985 attestation, cross-referenced in
useCollectiveMembers), roles are founder-controlled via the crew's
addressable event (useCrewRole), moderation only honours labels authored
by listed moderators, and the invite/join/endorse flow is symmetric with
proper author validation and batched, deduped queries throughout.
One real bug found — a stale-snapshot member clobber. The crew roster is
a single addressable event rewritten wholesale on every member add, but
three write sites rebuilt it from the CACHED crew/profile snapshot
without re-fetching first:
• useApproveJoinRequest (founder "Add to crew")
• useSendCrewInvite (founder pre-list on invite)
• useApproveCollectiveJoinRequest (Lab collective owner add)
So approving two join-requests back to back rebuilt the second write from
the pre-first-write roster and silently DROPPED the member added by the
first (addressable events replace by coordinate). This is exactly the
fetch-before-write discipline already applied to spots/events/shops/
contacts/stash — the crew/collective roster writes were the ones missed.
Fix: added fetchLatestCrewEvent() (and an inline equivalent for Lab
profiles) that re-queries the freshest roster event by coordinate and
merges the new member into THAT, never regressing to an older revision,
falling back to the passed-in snapshot only on a relay hiccup so an add
still goes through. Founder-only / owner-only authorization and the
membership-attestation trust model are unchanged.
Member removal / admin promotion go through the founder's own profile
editor (a deliberate full-roster overwrite by the sole owner), so they
aren't concurrent-merge scenarios and are left as-is.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Turn the collective join-request into a genuinely seamless flow instead of a
dead-end deep-link:
- New useApproveCollectiveJoinRequest: owner appends the requester to their own
Lab-profile roster (re-signed, preserving all tags) and clears the request in
one action — mirrors the crew founder's "Add to crew".
- CollectiveJoinRequestCard now resolves the collective and offers a single
"Add to collective" tap, with graceful states for loading, already-a-member,
and unresolvable-profile (never traps the owner).
- Removed the old ?manage=members deep-link (pointed at a read-only card).
- Requester-side copy now sets accurate expectations ("added in one tap").
Trust model unchanged: only the owner signs their profile event.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
Skaters can now request to join a Lab collective (studio/build team), mirroring
the crew join-request flow:
- New addressable kind 36387 (collective join-request), documented in NIP.md
- useCollectiveJoinRequests: send / incoming / "already requested" hooks
- Owner-only notification surfaced in the bell (All + Crews tabs) via
CollectiveJoinRequestCard, reusing the synced dismissal set
- "Request to join this collective" button on collective Lab profiles
(RequestToJoinCollective), hidden for the owner and existing members
Trust model preserved: only the owner can edit their own profile roster, so the
card links them to the member editor — no forged membership possible.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>