Skip to content

Keep child work on Subagent routes - #65

Merged
SaladDay merged 11 commits into
mainfrom
codex/subagent-visibility
Sep 23, 2026
Merged

SaladDay merged 11 commits into
mainfrom
codex/subagent-visibility

Conversation

@SaladDay

@SaladDay SaladDay commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Child (Subagent) work now appears only where the official service shows it. Session-level Turn reads and the Session event stream carry only the root work. Child Turns stay available through the Subagent routes, using the official agent_id projection. The pinned baseline is unchanged (SDK 3.13.0 / d7c41ef / agents=v1).

Behavior

  • Session Turns: GET /v1/agents/sessions/{id}/turns list, retrieve and the after cursor are root-only. A child Turn ID returns the same 404 body as a missing Turn. Child Turns stay reachable through subagents/{id}/turns and their Items routes.
  • Session stream: the creation and GET streams no longer carry agent.session.turn.* or Item events for child Turns. The official stream carries neither; the plan's belief that child Item events were already absent turned out to be wrong. agent.session.subagent.* events and root coordination Items are unchanged. Creation-stream settlement is unaffected.
  • Child Turn agent_id: the Session's Agent ID (the same value as Subagent.parent_agent_id), with subagent_id set. The TS client accepts both this shape and the older one.
  • Subagent list: {object: "list", data, first_id, last_id, has_more}, with null IDs on an empty page.
  • Child Item and child-Turn Item lists: limit 0 and values above 100 clamp. The Subagent and Subagent Turn lists still reject values outside 1–100, as the official service does.
  • Core Web: the timeline stays root-only, and it also hides child Items streamed by older Cores.
  • Unchanged: child history ownership, cancellation, cold continuation, Subagent reads, tenant isolation. No child data is deleted and there are no schema changes (new root-only queries; the old view is left in place). Documented Core extensions are untouched; this batch removes only undocumented mixed Session Turn pages and child events on the Session stream, which official-shaped clients would misread as root work.

Evidence

Campaign scan 3 findings SAT-01/02/07/08/09: the first official Subagent evidence, from 2 owned Sessions and 2 child Turns, all deleted. Recorded in contracts/agents-api/subagents.md, list-query-semantics.md and operation-evidence.md (register U; rows 12–14 and 20–25).

Validation

  • Live acceptance through real Core, the daemon, the native harnesses and real models (environment: none, multi_agent.enabled, a delegated child task) on all three harnesses: Codex with Kimi K3, MiniMax Code with MiniMax-M2.7, and Claude SDK with Kimi K3. A1–A5 failed on baseline main for every harness and passed on the candidate for every harness. Tenant B gets 404 on all seven routes. An interrupted Codex attempt, caused by model-connection reconnects, is kept as failed evidence. 4 of 6 Turns were used; cleanup and secret scans passed.
  • Real-PostgreSQL public test covering A1–A5, the stream contents and settlement. It is stable at -count=100 after the review fix. Store unit tests cover nested agent_id and tenant isolation. TS client and Web unit tests pass.
  • Server gate on this head: all make check targets, Web typecheck, core-doctor, unit tests and the build pass. The Playwright browser cases were not run on the server (no Google Chrome; the user approved the skip). make sqlc-generate and make openapi output is byte-identical.
  • Independent blind review by a fresh Claude Code subagent (the user-approved replacement for GPT-6 Astra): code correct and no boundary regressions. Its blocker, a flaky stream-drain test, and its follow-ups (Web filtering against older Cores, evidence labels, docs) are fixed in the last commits and verified by tests and the gate.

Deferred

  • Cursor error semantics (SAT-04).
  • 404 message text (SAT-06).
  • Official double subagent.created (not copied).
  • Nested-child agent_id (unobserved officially).
  • Child history input Item (SAT-12, unknown).

No full protocol compatibility is claimed.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

Session Turn list and retrieve now read root Turns through a new root-only
query; the public_execution_turns view stays in the schema unchanged. A
Subagent Turn ID on the Session routes, including as a list cursor, returns
the same not found error as a missing Turn (SAT-07).

Child Turns no longer record agent.session.turn.* lifecycle events on the
Session event log (SAT-09). The Session log has no internal reader other than
the public GET and creation streams, and creation-stream settlement reads root
Turns only, so no internal signal is needed.

Child Turns on the Subagent routes carry the Session's Agent ID as agent_id
and keep subagent_id (SAT-08). Nested children follow the same rule.
Child Items recorded agent.session.turn.item.* and content events on the
Session event log with the child Turn ID. The official parent stream carries
neither child Turn nor child Item events (SAT-09, S1 and S2 frames), and the
scan's "child Item events absent" reading missed this path. Child history stays
readable through the Subagent Item routes; the stored output index is kept.
The Subagent list now returns object "list" with first_id and last_id (null
on an empty page), built with listBounds like the Turn and Item lists
(SAT-01). Subagent Item and Subagent Turn Item lists clamp limit 0 to 1 and
values above 100 to 100, like Session Items (SAT-02); the Subagent list and
Subagent Turn list keep rejecting out-of-range limits, as the official service
does. OpenAPI is regenerated, including the root-only Session Turn and child
agent_id descriptions from the previous commit.
One real-PostgreSQL HTTP test covers A1-A5 together: root-only Session Turn
list/retrieve/cursor with child IDs answering like missing ones, the Session
Agent ID on direct and nested child Turns, the Subagent list envelope and empty
page, child Item limit clamping versus Subagent/Subagent Turn rejection, tenant
B 404s, and creation/GET streams that carry root coordination and
subagent.created but no child Turn or Item events while the creation stream
still settles on the root idle.
A child Turn carries the Session's Agent ID as agent_id and names the child in
subagent_id (SAT-08). The Turn projection no longer requires subagent_id to
equal agent_id; it requires a nonempty agent_id and a nonempty or null
subagent_id. The earlier child-owned agent_id and child Turn stream snapshots
from older Core releases are still accepted.
Core no longer lists or streams Subagent Turns for a Session, so the Web
timeline and trace show root work only. The Web never presented child Turns
as Subagent work; it has no Subagent view and does not need the Subagent
routes. If an earlier Core still lists or streams child Turns, the durable
loader skips them (keeping its cursor) and live snapshots ignore them, so the
durable and live timelines agree.
Inspection now runs A1-A5 as named visibility checks, records each result per
phase and fails only after all of them, so a baseline run lists every
difference: root-only Session Turn list/retrieve/cursor (child IDs answer like
missing ones, also for the foreign project and through the pinned SDK), the
Session Agent ID on child Turns, the list envelope with null IDs on an empty
page, child Item limit clamping versus Subagent/Subagent Turn rejection, and a
Session stream without child Turn or Item events that carries
subagent.created. Creation now uses the streaming create as the observed
stream; later input observes the GET stream opened before submission.

Unknown list keys are now expected to be ignored, matching the merged list
query tolerance batch; the script previously expected 400.
subagents.md gains the A1-A5 matrix with official request IDs, the consumer
audit behind removing child Turn and Item events, the unchanged scope and the
acceptance boundary. List query semantics now clamp Subagent Item lists and
record the sampled Subagent/Subagent Turn rejections. Operation evidence adds
register U and updates rows 13-14 and 20-25. CONTRIBUTING, the history/events
contract and the coverage ledger no longer describe mixed Session Turn pages or
child Turn events.
…ty test

The GET stream polls independently of the creation stream, so stopping it as
soon as the creation stream settled could drop events not yet drained (about
one run in ten). The test now stops it only after the stream delivered the
idle that follows the root Turn's completion, with a bounded timeout.
An earlier Core still streams child Item and text events. Their Turns are
filtered out of the timeline, so these Items appeared under unassociated
Items in the trace. The Web now records Subagent Turn IDs from the durable
Turn list and from streamed child Turn snapshots, per Session and Core
connection, and hides Items of those Turns from the Session view. Current Core
sends no child work to the Session, so nothing changes there.
The kept Subagent and Subagent Turn limit rejections (R12, R13, R16, R17) are
SAT-03, not SAT-02. Record the current base, drop the stale child-delta note
from the events.stream row, rewrite the EVT-03 usage sentence, note that older
Cores send the child ID as a child Turn's agent_id, and state that documented
Core extensions are unaffected; only undocumented mixed Session Turn pages and
child Session events were removed.
@SaladDay
SaladDay merged commit 39f111c into main Sep 23, 2026
1 check passed
@SaladDay
SaladDay deleted the codex/subagent-visibility branch October 7, 2026 06:38
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