You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Once the generic primitive exists, core/room's own verbs (spec/room.cddl:126-155) become a thin instance of capability-request/capability-grant-ok rather than a parallel hand-built copy of the same flow -- no dual implementation kept around, per the standing no-legacy-shims convention.
Design pass completed (2026-09-14) -- concrete plan below, corrected against the real current code rather than assumed from earlier issue summaries (several "already done" claims turned out to be wire-shape-only; see corrections).
The outer manage-command.verb question -- resolved, not a fork
capability-request/capability-grant (see #117) MUST be sent with the outer manage-command.verb set to the target capability (e.g. "room:member"), identical to every other domain. This is forced by capability-verb's own grammar (tokens.cddl: closed alternation of colon-delimited domain:noun regexes) -- "capability.request" (a dot) cannot legally be an outer verb. Not a stylistic choice.
room.invite -- resolved via a real capability-grant primitive (#117), not left bespoke
Tracked as its own issue (#117) since it's a genuinely separate wire primitive, not part of this migration's own diff.
Concrete migration plan for room.join
CDDL (spec/room.cddl): retire room-join/room-invite; rename room-join-ok to room-join-grant-ok, riding capability-grant-ok's own open tail rather than a free-standing rule (documentation/codegen convenience -- response bytes are unchanged).
Wire impact: request side is a breaking change (old {verb:"room:member", params:{verb:"room.join"}} bytes no longer dispatch); response side is byte-identical. No documented wire back-compat policy exists in this pre-1.0, rapidly-iterating spec -- accept the break, no shim, matching how #58/#63/#64 already broke and regenerated room's wire shape.
TypeScript: new ts/packages/core/src/domain/capability-request.ts (see #78) is the real prerequisite -- this issue is blocked on #78's domain-logic half landing, not just its CDDL. Once it exists:
room-client.ts's requestToJoin becomes a thin wrapper over requestCapability; its public signature and RoomJoinResult type stay unchanged, so callers above it (RoomPanel.tsx, use-room-messaging.ts, App.tsx) need no changes.
createRoomRouter's "room.join" dispatch branch delegates to the new generic handler with capability: ROOM_MEMBER_CAPABILITY; handleRoomJoin's existing body (bearer check, mintCapabilityToken, members list) becomes the decide callback almost verbatim.
canSatisfy -- confirmed still correctly deferred, confirmed not needed for this migration
Doesn't exist yet, gated behind #85 (generic predicate-list evaluator). Not needed here: room-join admission is either a human decision or the DM auto-approve check, neither of which is "would a held token satisfy this ask" -- and room-join grants are always root-level (no parent), so canGrant isn't load-bearing on the approval path either. Becomes relevant only if a future domain wants capability-request auto-approved based on the approver already holding a broader delegation -- a separate use case.
Two-repo follow-up (agent-comms, scope only -- not this issue's own work)
src/core/room-lifecycle.ts (joinRemoteRoom, the DM-flow join send, inviteToRoom) and src/core/room-protocol.ts (handleRoomJoin/admitRoomJoin/handleRoomInvite, registered by literal string in room-router.ts) would all need rewiring onto the new wire-mesh-core primitives once this lands. Concretely closes a real, currently-inconsistent gap: agent-comms' connect_request already has receiver-side expiry (its own PR #84) but pendingRoomJoins does not (confirmed: bare Promise, no timer) -- adopting the new generic handler unifies both admission gates onto one mechanism. Tracked as its own follow-up issue in agent-comms once #78/#83/#117 land in wire-mesh.
Verification plan
just spec && just conformance -- new/changed vectors: replace room-join vectors with capability-request vectors targeting room:member; add a capability-grant vector (covering retired room-invite); add an expired-capability-request vector exercising Add optional valid-until to capability-request params #82's obligation.
cargo test -p wire-mesh-wire -- verify (not assume) no new Rust structs are needed, per the precedent that the six room verbs already ride manage-request-frame's untyped params/response catch-all.
room-client.test.ts currently has zero invite coverage -- add real coverage as part of this migration, not just parity.
just build && just test && just lint.
Blocked by: #78 (domain-logic half), #81 (receiver-side timeout, reopened -- generic mechanism doesn't exist in wire-mesh yet), #82 (valid-until, closed at the wire level, needs the handler from #78 to actually check it), #117 (capability-grant, for the room.invite half).
Once the generic primitive exists,
core/room's own verbs (spec/room.cddl:126-155) become a thin instance ofcapability-request/capability-grant-okrather than a parallel hand-built copy of the same flow -- no dual implementation kept around, per the standing no-legacy-shims convention.Design pass completed (2026-09-14) -- concrete plan below, corrected against the real current code rather than assumed from earlier issue summaries (several "already done" claims turned out to be wire-shape-only; see corrections).
The outer
manage-command.verbquestion -- resolved, not a forkcapability-request/capability-grant(see #117) MUST be sent with the outermanage-command.verbset to the target capability (e.g."room:member"), identical to every other domain. This is forced bycapability-verb's own grammar (tokens.cddl: closed alternation of colon-delimiteddomain:nounregexes) --"capability.request"(a dot) cannot legally be an outer verb. Not a stylistic choice.room.invite -- resolved via a real capability-grant primitive (#117), not left bespoke
Tracked as its own issue (#117) since it's a genuinely separate wire primitive, not part of this migration's own diff.
Concrete migration plan for room.join
CDDL (
spec/room.cddl): retireroom-join/room-invite; renameroom-join-oktoroom-join-grant-ok, ridingcapability-grant-ok's own open tail rather than a free-standing rule (documentation/codegen convenience -- response bytes are unchanged).Wire impact: request side is a breaking change (old
{verb:"room:member", params:{verb:"room.join"}}bytes no longer dispatch); response side is byte-identical. No documented wire back-compat policy exists in this pre-1.0, rapidly-iterating spec -- accept the break, no shim, matching how #58/#63/#64 already broke and regenerated room's wire shape.TypeScript: new
ts/packages/core/src/domain/capability-request.ts(see #78) is the real prerequisite -- this issue is blocked on #78's domain-logic half landing, not just its CDDL. Once it exists:room-client.ts'srequestToJoinbecomes a thin wrapper overrequestCapability; its public signature andRoomJoinResulttype stay unchanged, so callers above it (RoomPanel.tsx,use-room-messaging.ts,App.tsx) need no changes.createRoomRouter's"room.join"dispatch branch delegates to the new generic handler withcapability: ROOM_MEMBER_CAPABILITY;handleRoomJoin's existing body (bearer check,mintCapabilityToken, members list) becomes thedecidecallback almost verbatim.handleRoomInvite/sendRoomInvite(net new -- wire-mesh itself has zeroroom.inviteimplementation today, confirmed by grep) built on Add capability-grant primitive (unsolicited push-grant counterpart to capability-request) #117'scapability-grant.canSatisfy -- confirmed still correctly deferred, confirmed not needed for this migration
Doesn't exist yet, gated behind #85 (generic predicate-list evaluator). Not needed here: room-join admission is either a human decision or the DM auto-approve check, neither of which is "would a held token satisfy this ask" -- and room-join grants are always root-level (no
parent), socanGrantisn't load-bearing on the approval path either. Becomes relevant only if a future domain wantscapability-requestauto-approved based on the approver already holding a broader delegation -- a separate use case.Two-repo follow-up (agent-comms, scope only -- not this issue's own work)
src/core/room-lifecycle.ts(joinRemoteRoom, the DM-flow join send,inviteToRoom) andsrc/core/room-protocol.ts(handleRoomJoin/admitRoomJoin/handleRoomInvite, registered by literal string inroom-router.ts) would all need rewiring onto the new wire-mesh-core primitives once this lands. Concretely closes a real, currently-inconsistent gap: agent-comms'connect_requestalready has receiver-side expiry (its own PR #84) butpendingRoomJoinsdoes not (confirmed: barePromise, no timer) -- adopting the new generic handler unifies both admission gates onto one mechanism. Tracked as its own follow-up issue in agent-comms once #78/#83/#117 land in wire-mesh.Verification plan
just spec && just conformance-- new/changed vectors: replace room-join vectors withcapability-requestvectors targetingroom:member; add acapability-grantvector (covering retiredroom-invite); add an expired-capability-requestvector exercising Add optional valid-until to capability-request params #82's obligation.cargo test -p wire-mesh-wire-- verify (not assume) no new Rust structs are needed, per the precedent that the six room verbs already ridemanage-request-frame's untyped params/response catch-all.ts/packages/core/test/capability-request.test.ts(mirrorsroom-token-verification.test.ts): ungated ask,valid-untilexpiry refusal, receiver-side timeout auto-reject, generic grant/deny round trip.room-client.test.tscurrently has zeroinvitecoverage -- add real coverage as part of this migration, not just parity.just build && just test && just lint.Blocked by: #78 (domain-logic half), #81 (receiver-side timeout, reopened -- generic mechanism doesn't exist in wire-mesh yet), #82 (valid-until, closed at the wire level, needs the handler from #78 to actually check it), #117 (capability-grant, for the room.invite half).