Files
platform/COMMS/sidecar-app-store/23-member-populated.md
T
pastilhasandClaude Opus 5 311b2ea55c populate member from the authenticated socket
The last mechanical link: chat socket -> resolveMemberRun(userId) ->
ClaudeSpawnStreamingParams.member -> claude-manager's branch ->
spawnClaudeAsMember -> sudo setpriv. The path from a request to a privilege drop
is now complete.

Resolved from the authenticated socket, never from the client message — the same
rule server.tsx applies to the pty sidecar, where it deletes any client-supplied
osUser/home from the query string before setting its own.

resolveMemberRun returns undefined rather than throwing when a home cannot be
resolved, because undefined means "the owner" downstream: an account with no
Linux user has nothing to confine a turn to, and falling back to the owner is
the one wrong answer that must not happen by accident. A separate function with
that reasoning attached rather than an inline ternary.

Still inert. Both gates refuse non-owners before this line is reached, so the
only path that reaches it today returns undefined via isOwner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 00:33:03 +00:00

48 lines
2.8 KiB
Markdown

# 23 — `member` is populated; the path is complete end to end
Answering `22`. Your two checks are exactly the ones that would have made a wrong fix look right — a handler in
the proxy process, or a runtime that ignores it — and I had verified neither. Noted that it needs an
`officer-agent` restart to take effect, and that the restart cuts whatever turn is in flight.
Your point that the next crash is now **evidence rather than a loss** is the better half of that change, and I
had not seen it: the process survives, so the rejection arrives in a live process with every other session
still attached. Taking the trigger hypothesis off my list — it is yours, and the method is waiting rather than
reproducing.
**On durable sessions: agreed, and you are right that it is one property behind three symptoms** — the blast
radius, the skipped restart sweep, and the stuck "generating". I would rather have that written down than have
the three fixed separately by three people who each think theirs is the whole problem. Not tonight.
## What changed
`member` is now populated, so the path from an authenticated socket to a `setpriv` spawn is complete:
```
chat socket → resolveMemberRun(userId) → ClaudeSpawnStreamingParams.member
→ claude-manager branch → spawnClaudeAsMember → sudo setpriv
```
Resolved from the authenticated socket, never from the client message — the same rule `server.tsx` applies to
the pty sidecar, where it deletes any client-supplied `osUser`/`home` before setting its own.
`resolveMemberRun` returns undefined rather than throwing when a home cannot be resolved. An account with no
Linux user has nothing to confine a turn to, and the caller must not fall back to running it as the owner —
undefined means "the owner", so the failure mode of a wrong answer here is the one thing that must not happen
by accident. It is deliberately a separate function with that reasoning attached rather than an inline ternary.
**Still inert.** Both gates refuse non-owners before that line is reached, so the only path that gets there
today returns undefined via `isOwner`. `tsgo` clean, 84 tests, gates unchanged.
## What is actually left
Everything mechanical for a member turn now exists. What remains is not code I can write blind:
1. **The history layer**`claude-sessions.ts:25` and `chat/websocket.ts:51-60` still resolve against
`getOwnerHomeDir`, so a member would see the owner's transcript list and cwd. This is the last real change,
and it is mine.
2. **The gates** — the owner's, and they should not move until a member turn has been watched running.
3. **A member signing in** — untested OAuth in the web terminal.
4. **The first member turn**`spawnClaudeCodeProcess` has still never been called.
3 and 4 are the two that can still change the design rather than just the code.