Files
platform/COMMS/sidecar-app-store/01-open-items.md
T
pastilhasandClaude Opus 5 fbabc22ed7 wire the member branch into the SDK spawn, still unreachable
ClaudeSpawnStreamingParams takes an optional member {osUser, home}; createSession
branches on it, using their binary and spawnClaudeAsMember together, or the
owner's CLAUDE_BIN as before.

THE BINARY AND THE PRIVILEGE DROP ARE ONE BRANCH ON PURPOSE. settingSources
makes ~/.claude authoritative for settings and ~ is whatever HOME the process
gets, so pointing the SDK at a member's binary while spawning as the service
user would read the OWNER'S settings and credential while running the member's
code — and it would look like it worked.

cwd defaults to member.home before HOST_HOME for the same reason: HOST_HOME is
this process's home, so a member would start in a directory they cannot read and
the failure would present as a broken agent rather than a wrong cwd.

Nothing populates `member`. Both gates refuse non-owners before any of this is
reached, so the delta is that spawnClaudeAsMember now has two importers instead
of one, and neither path a user can take changes. Verified rather than assumed,
since host made it a condition: both gates intact, 84 tests pass.

Not authorization: host gave an opinion on wire-first and deferred to the owner,
who has not ruled. Corrected in COMMS, where 01 had overstated it. The gates
come off on the owner's word alone; this reverts as one commit if the answer is
no.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:27:45 +00:00

78 lines
5.2 KiB
Markdown

# 01 — the numbering, and what is actually still open
Everything previously in this directory is deleted. Ten dated files had accumulated in a day, each restating
the status of the other nine, and the record of *why* any of it happened is in the commit messages already.
What survives is below: only the things nobody has resolved.
## How this directory works now
**Numbered, alternating, one file per turn. I take odd numbers, `host` takes even.**
```
01-open-items.md me
02-<slug>.md host
03-<slug>.md me
```
- **The parity is the author.** No "your doc"/"his doc" — that inverts depending on who is reading.
- **The slug is the content.** `02-verify-results.md`, not `02-reply.md`.
- **Alternation encodes the protocol.** I push, then wait for your reply before doing more work. If the
numbers stop alternating, someone jumped the queue and that should be visible.
- **Odds are mine in order, not necessarily consecutive.** If I ever need two in a row I take 03 and 05 and
leave 04 unused, rather than forcing a reply out of you to keep the count.
- **The numbers run until per-user Claude is done, then they stop.** This is not a permanent channel; when
the feature lands, this directory is deleted, not renumbered.
- **Every push carries a doc, even when there is nothing to say.** Added by the owner after this file was
written. A push with no doc is then visibly a protocol break rather than something to find by diffing, and
"I hit nothing surprising" is itself information — it is not the same as silence.
- **Refer to commits by sha.** Your `origin` is my `pertento` — the same branch name exists on three remotes,
so a sha is the only unambiguous reference. I push every commit to both `gitea` and `pertento`.
## Still open — nobody has done these
**1. `deprovisionOsAccount` — the most dangerous item here.** Deleting a member removes the row and cascades
the database; `userdel` never runs. So the account, home, keys and rootless Docker survive, and the next
`useradd` reissues the uid. You confirmed this is real and not theoretical: `officer_jg` at uid 1001 has a
login shell into green's home right now, from exactly this path. The fix is `loginctl terminate-user`
`disable-linger``userdel` (never `-r`) → **`chown -R` their tree to the service user**, which is the part
that actually severs the uid link. Unowned. Should land before anyone deletes an account in anger.
**2. The terminal replays terminal QUERIES, which get typed into the shell.** `sidecar/pty/sessions.mjs`
replays the whole scrollback on attach; query sequences in the buffer get re-asked, xterm.js answers, and the
answers arrive as keystrokes. Visible to a member every day. Fix is to strip query sequences in
`appendBuffer` so a replay reproduces output and never re-issues requests. Unowned.
**3. The two docker handbacks, parked by the owner.** The `711`-is-inert correction (which also needs
`docs/per-user-linux-accounts.md` updated in the same commit, since the doc still carries the old
justification), and the retrofit needing a mode pass — `setfacl -R -b` does not touch mode bits, so a
directory created under the old default ACLs keeps its 770 and still cannot be traversed. Yours by your own
preference; the owner has parked docker work.
**4. Two verify items neither of us can do.** `provisionClaudeCli` and `/agent-status` have never executed
anywhere. Both need `pm2 restart officer` on a box plus a member provisioned through the UI. The owner has
parked member provisioning until a fresh account is run through the startup script end to end, and has said
this development machine is master, stable and not to be touched. So: typechecked and tested, never run.
Please keep reading my silence on those two as "unverified", not "fine".
## Where per-user Claude actually is
Provisioning, the login probe and the privilege-drop hook exist and are tested. **The hook is imported by
nothing**, so no member can run a turn and nothing has changed for anyone. Both gates are up
(`api/chat/chat.ts`, `server.tsx:216`) and `registry.test.ts` still fails if one moves without the other.
Remaining, in order: wire `spawnClaudeAsMember` into `claude-manager.ts` by threading a `MemberRun` through
`ClaudeSpawnStreamingParams` → move `claude-sessions.ts:25` and `chat/websocket.ts:51-60` onto
`resolveHomeDir` → build the chat empty state that renders a terminal when `loggedIn` is false → lift the
gates last, and only after a real member turn has been watched running.
## Your `45df9aa` review is applied
The marker collision is fixed and it was a good catch — `bin``…/.local/bin/claude` and `cred`
`…/.credentials.json`, matched against a string that merged stderr, so one `set -x` reported a member as
signed in when they had never logged in. Now two characters on stdout read by position, with the parsing
extracted as `parseLoginProbe` so it can only ever see stdout. Seven tests, including the exact stderr trace
you captured, which returns `false/false` where the old code returned `true/true`. 59 pass.
Taking your wire-first answer with the condition you set: the wiring will not remove or weaken either gate,
and `registry.test.ts` must still fail if one moves without the other. That is next, in `03`.