[#234] Control plane: token-free channel metadata for hosted rooms#235
Conversation
Extend the hosted-room control-plane payloads (GET /rooms and GET
/rooms/<id>) with a sanitized `channels: [{id, name, type}]` list read
from the room's host-owned boardroom store.
- Channels are reduced to exactly id/name/type; lifecycle, createdAt,
and every other internal field are stripped.
- `lifecycle === "removed"` channels are omitted.
- A room with no boardroom store record falls back to a single
#general chat channel, matching the room server's runtime projection.
- Owner scoping is unchanged: a non-owner still gets not_found and
never sees another owner's channel metadata (owner check runs before
any channel read).
- No token, invite/card URL, Authorization/Bearer, cursor, or message
content is derived from a channel; no remote fetch, no new dependency.
Tests cover present channels, removed exclusion, legacy fallback, owner
scoping, and token-free payloads at both the API and HTTP layers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Epic Alignment: PASS
The hosted-room channel projection advances #77/#234's local-first, token-free control-plane contract; its owner-first read order and local boardroom source match the ticket.
Checked (evidence)
- Structural gate: live PR body contains filled
## EPIC Alignment,## Self-Verification,## Design Fidelity: N/A, and## Deviations. - Contract projection:
src/platform/api.ts:69reads the local boardroom, filters removed entries, and projects onlyid,name,type;src/platform/types.ts:67fixes the public channel shape. - Owner and fallback behavior:
src/platform/api.ts:53checks ownership before the channel read;test/platform-api.test.ts:141covers the legacy general fallback. - Riskiest part of this diff: expanding both control-plane response shapes; the projection and tests keep the added channel data token-free and exclude lifecycle fields.
- Kill-list: scanned all items — hit listed below.
- CI:
gh pr checks 235→ Release gates pending (live check).
Findings
- [required] Inline the one-call-site helper layer.
- File:
src/platform/api.ts:69,src/platform/api.ts:86,src/platform/api.ts:90 - Why it fails:
readPublicChannels,defaultPublicChannel, andisNotFoundErrorare new private helpers with one call site each. This violates the required kill-list rule against one-use abstractions and adds indirection to a small, endpoint-local projection. - Do instead: inline the boardroom read/ENOENT fallback and exact channel projection into
attachChannels, which is already shared by both list and read handlers.
- File:
Decision
The implementation otherwise satisfies #234's projection, owner-scope, and token-free requirements, but the mandatory one-use-helper kill-list finding requires this small simplification before approval. Recheck live CI after the update.
@re2 Verdict: APPROVE ✅ (at fd24fb9)Posted as a comment (shared git identity blocks self-approve); chat is the authoritative verdict. Checked (evidence)
Non-blocking observationA room WITH a boardroom store whose channels are ALL removed yields No blocking findings. |
RE1 finding: readPublicChannels, defaultPublicChannel, and isNotFoundError
each had a single caller and formed an unnecessary helper layer. Inline
all three into the shared attachChannels function (which remains reused by
both listRoomsResponse and readRoomResponse). No behavior change — the
ENOENT-only fallback, removed-channel filter, and {id,name,type} projection
are identical.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@re2 delta re-confirm: APPROVE ✅ (at 3b02535)Re-reviewed the delta
My full-review approval (owner scoping, allowlist projection, boundary, token-free) stands. No blocking findings. |
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Epic Alignment: PASS
The final hosted-room-only, token-free channel projection remains aligned with #77/#234; the delta removes indirection without changing the local-first contract.
Checked (evidence)
- Re-review delta:
git diff fd24fb900bbfdbdadd617232fa47515fa4b96869..3b02535→ onlysrc/platform/api.tschanged. - Prior finding resolved:
src/platform/api.ts:66now inlines the boardroom read, ENOENT-only#generalfallback, and{id,name,type}projection in the sharedattachChannelsflow; the three one-use helpers are gone. - Owner scope preserved:
src/platform/api.ts:54still returnsnot_foundbeforeattachChannelscan read a non-owner's boardroom. - Riskiest part of this diff: moving the fallback/error test into the shared attachment function; it preserves the ENOENT-only fallback and rethrows non-ENOENT failures.
- Kill-list: scanned new range only — clean.
- CI:
gh pr checks 235→ Release gates pass (2m25s, live).
Findings
- None.
Decision
APPROVE. The prior mandatory helper-layout finding is resolved in the reviewed delta and live CI is green. Lead/sub-PO gate remains required before merge.
|
Lead sign-off: final commit 3b02535 was independently inspected. The hosted-only projection remains exact ; owner scoping precedes the local boardroom read; removed channels are excluded; the ENOENT-only legacy fallback is preserved; no token, card URL, auth, internal field, remote fetch, or dependency is added. CI and reviewer evidence are green. Canonical sub-PO may merge under the Agent Gather lead/sub-PO protocol. |
|
Correction to the prior lead sign-off wording: the public channel contract is exactly the three fields id, name, and type. All other sign-off findings are unchanged. |
Fixes #234
EPIC Alignment
GET /roomsandGET /rooms/<id>now expose hosted-roomchannelsas exactly{id, name, type}, sourced fromreadBoardroom; removed channels omitted; missing store falls back to#general; ownernot_foundsemantics unchanged; joined rooms get no channel fetch or projection.src/browser/*(Unified workspace left rail: single host-tagged boardroom list with 1/3-2/3 navigation split #233/Unify dashboard and boardroom shell navigation into a three-panel workspace #218) and nosrc/cli/*(Launcher UX: make bare agentgather open the local dashboard #232) changes. No remote-room proxy/fetch path added.Self-Verification
Adversarial diff re-read
readRoomResponseruns the owner check (room.owner_user_id !== owner → not_found) before any channel read, so a non-owner never triggers a boardroom read and cannot learn another owner's channel metadata.listRoomsResponsefilters by owner before attaching channels.readPublicChannelscatches only ENOENT (no host boardroom store / no room-state record) and returns the legacy[#general]default — matching the room server's own runtime projection. Any other failure (e.g. a corrupt store) propagates as a 500 rather than being masked. VerifiedreadRoomStatethrows native ENOENT so the no-store path is exercised by a real test.filter(lifecycle !== "removed").map({id,name,type})— internal fields (lifecycle,createdAt) and any token/URL are structurally dropped;PublicChannelis the only channel shape the payload can carry.Kill-list scan
console.*, noany/as any, no stub/placeholder markers. Changes limited tosrc/platform/api.ts,src/platform/types.ts, and the two named test files.Acceptance criteria (1:1)
GET /roomsandGET /rooms/<id>return hosted-roomchannelswith exactlyid,name,type— projection +deepEqualtests.#general— dedicated tests at API and HTTP layers.not_foundwithout metadata disclosure — owner-scope test asserts 404 and no channel leak.test/platform-api.test.tsandtest/platform-http.test.ts.pnpm build,lint,typecheck,kit-guard,no-stub,testall pass.Evidence
pnpm build— clean (tsc + copy-assets).pnpm lint— clean.pnpm typecheck— clean.pnpm kit-guard— 4 pane stylesheets clean.pnpm no-stub— clean.pnpm test— 402/402 pass (added 5 API + 1 HTTP channel tests).Security invariants
Design Fidelity
N/A — control-plane API only; no UI surface in this ticket (#218 owns rendering).
Deviations
None.
src/platform/http.tswas not modified: the HTTP layer forwards the API handlers' response body verbatim, so the channel projection lives entirely insrc/platform/api.ts(logic) andsrc/platform/types.ts(thePublicChanneltype), per the canonical dispatch scope.