Skip to content

fix(seats): superviseRun passes the run's actor, task and pod to the trace store - #2736

Merged
lilyshen0722 merged 1 commit into
feat/jellymoon-recordfrom
fix/seats-record-hold-items
Oct 25, 2026
Merged

lilyshen0722 merged 1 commit into
feat/jellymoon-recordfrom
fix/seats-record-hold-items

Conversation

@lilyshen0722

Copy link
Copy Markdown
Contributor

The last item on the record branch's main-hold. superviseRun started every run with only runId, adapter and label. So the supervisor's own run.started entry, and anything an adapter or the valet emitted without naming an actor, recorded actor: null. No entry carried the board task or pod either, and onBehalfOf was never written.

superviseRun now takes taskId, podId and actor and passes them to store.startRun. That function already validates them: an actor must be well-formed, and a taskId without a podId is refused. A malformed call now fails before anything is spawned. Callers that pass none of these are unchanged.

Tests (packages/seats/test/supervisor.test.js, 2 new):

  • Every entry of a supervised run carries the task and pod. run.started and an emit that names no actor both take the run's actor, including onBehalfOf. The index row carries all three.
  • A taskId without a podId rejects, and the adapter is never spawned.

Container run (node 22, network none): seats 191/191. Mutant (startRun back to { runId, adapter, label }): both new tests fail.

The other two hold items are already on the branch: the pgroup kill (#2726, c226bca) and the fake REAL_HOME in valet-env (e1bd2cc).

🤖 Generated with Claude Code

https://claude.ai/code/session_013ENddpGzhE7S8qjDSYAMuu

…trace store

The supervisor started every run with only runId, adapter and label, so
its own run.started entry, and any entry an adapter or the valet emitted
without naming an actor, recorded actor: null, and no entry carried the
board task or pod. superviseRun now takes taskId, podId and actor and
hands them to store.startRun, which already validates them (a taskId
without a podId is refused before anything spawns).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ENddpGzhE7S8qjDSYAMuu
@coderabbitai

coderabbitai Bot commented Oct 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2736 security gate at ba85bf9 (base feat/jellymoon-record)

Scope: 2 files, packages/seats/src/supervisor.js (+12/-2) and packages/seats/test/supervisor.test.js (+39). No prior gate on this PR; read in full.

Verdict: PASS. No defect that changes the trust boundary. One non-blocking note below.

What changed

superviseRun now forwards taskId, podId and actor to store.startRun. Before, every run-owned entry (run.started, tool calls, permission requests, emits with no actor) was written with actor: null and no task or pod. The store already validates all three, so the change only moves values that were previously dropped into the record.

Checked and holds

  • startRun refuses a taskId with no podId before the run file is created (trace/store.js:99), and the supervisor calls startRun before the adapter spawns, so no adapter starts. The new test "a taskId without a podId is refused before the adapter is spawned" asserts spawned === false.
  • actor goes through validateActor (record/format.js:202 and the actorProblem checks above it): the type, id and owner shape, onBehalfOf restricted to user ids and roles, and the result frozen. Anything malformed throws before the run file is created.
  • The run's actor is used only for entries that name no actor (defaultActor, 'run' case). It does not change which actor a given entry carries when one is set.
  • No in-repo caller of superviseRun passes actor, taskId or podId yet (git grep over packages, backend and cli at this head: only the export and the definition). The values come from whichever seat wrapper calls it, the same party that already controls the run.
  • Redaction: entry data is still scrubbed with the run's secrets (writeLine, store.js:119). This PR does not touch scrubbing.

Non-blocking (name it, do not hold on it)

  • taskId and podId have no shape check in startRun. They are written verbatim to the envelope of every line (store.js:119 scrubs data only). In practice they are board ids, so this is hygiene, not an exposure. A future caller that passes a secret as taskId would get it into the hash-chained record unscrubbed. Suggest a token-shape check on both at the boundary, in a follow-up.
  • actor.owner and actor.onBehalfOf are caller-asserted. The record now says whatever the caller says about who a run is for. That is the intended attribution (the caller is the seat), but it is a claim, not a verified fact, and nothing in this PR says so. Worth one sentence in record-v1.md when the first real caller lands.

Tests (run at ba85bf9 in a temp worktree, removed after)

  • node --test test/supervisor.test.js: 23/23 pass.
  • npm test in packages/seats: 191/191 pass.
  • Reverted supervisor.js to ba85bf9^: the two new tests fail (21 pass, 2 fail), so they do guard the change.

Not checked here

  • CI at this head (not read).
  • Sable goes after this gate, per the ask.

Written by Rhea, a Commonly agent — https://commonly.me/v2/pods/6a8f6dc7a1dccf2e02f31015

GATE: PASS

@lilyshen0722 lilyshen0722 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sable code-leg gate at ba85bf9. The PR is stacked on feat/jellymoon-record, so I read its own commit, which touches two files: packages/seats/src/supervisor.js (+13/-2) and packages/seats/test/supervisor.test.js (+37).

What it does: superviseRun now accepts taskId, podId and actor and passes them to store.startRun. The run's own run.started entry and any emit that names no actor now record the run's actor, and every entry carries the task and pod. startRun already validates these: a taskId without a podId throws before anything is written.

Checked:

  • The startRun call fires before the adapter spawns. The new "a taskId without a podId is refused before the adapter is spawned" test confirms the refusal and that spawn never runs.
  • The actor fallback is in appendEntry through { runActor }, and the store's runActor is null when no actor is passed. Existing callers that pass no new fields are unchanged, since the new parameters are optional.
  • Clean worktree at ba85bf9, Node 22: supervisor.test.js 23/23. Full packages/seats suite, every test/*.test.js file: 191 of 191, matching Lily's container count.
  • Mutant: I reverted the startRun call to { runId, adapter, label }. Two tests fail: "the run's actor, task and pod reach the record…" and "a taskId without a podId is refused…". I restored the file and confirmed it with diff -q.

Non-blocking: the new test that checks byType['agent.message_chunk'].actor relies on the emit-side fallback. It passes, so the fallback is exercised, but a future change to appendEntry would surface there rather than in a direct unit test.

Written by Sable, a Commonly agent — https://commonly.me/v2/pods/6a692a1be833c668acdb84cf

GATE: PASS

@lilyshen0722
lilyshen0722 merged commit 20e459e into feat/jellymoon-record Oct 25, 2026
18 of 19 checks passed
lilyshen0722 added a commit that referenced this pull request Oct 27, 2026
The branch carried pre-merge copies of the record line (supervisor, record-v1,
grants, valet, trace) that reached main through #2739, #2701, #2736, #2763 and
#2766. Every conflict in a file touched on this side only by those commits takes
main's reviewed version: valet/env.js, valet/grant.js, record/format.js,
record-v1.md, supervisor.js and their tests, fake-provider, the seats README, the
seats-package test and the cli lock file. Main already carries every record kind
the ACP commits added (pod-member, session.opened, agent.retry, sandbox.applied,
egress.*, onBehalfOf).

Three files carry ACP additions main lacks and are resolved as unions:
- failures.js keeps refusal_retry_loop and provider_unavailable;
- index.js keeps main's process-group exports and adds the ACP, launch, client
  and provider-watch exports;
- trace/events.js keeps session.opened and the usage context fields, with main's
  newer credential.granted / credential.denied shapes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MDs9DRK3nqxjeAV7xL8uhg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant