Repository navigation
Separate thread dispatch from subagent dispatch, and add DEV-STACK-08 - #163
Conversation
…d DEV-STACK-08 A subagent runs in the parent's own working directory: measured, and confirmed in codex-rs where apply_spawn_agent_runtime_overrides assigns the parent turn's cwd to the child on both spawn paths. The V1 tool description says the opposite, and only the V2 usage hint states the shared directory, so a V1 session is never told. The skills now say it in the bodies that are always read rather than behind a conditional link. Adds pabcd/references/dispatch-surfaces.md as the taxonomy owner, states the routing decision and its authority in loop/SKILL.md, pabcd/SKILL.md and the SessionStart PR affordance, rewrites DISPATCH-ISOLATION-01 so a shared tree forbids concurrent branch-level git work, and adds DEV-STACK-08 for owner-authorized lane-parallel stacks with cross-references from the five rules it qualifies.
…he V2 capability identity delegation.md's two trailing bullets become a detection rule plus labelled V1, V2 and thread-surface sections. spawn_agent is registered by both families and discriminates nothing; followup_task, send_message, interrupt_agent and list_agents are V2-only, send_input, close_agent and resume_agent are V1-only, and wait_agent proves nothing because V2 makes it optional. capabilities.ts declared V2 spawn as a tool called create_task. No such tool exists in either family. It is replaced by followup_task, which is a real V2-only signal, and detectSpawnSurface can now decide from a live tool list while keeping v2 as the no-evidence default and the exact CODEXCLAW_SPAWN_V1 override. Namespaced names are matched the way the spawn hook matches them: concatenated without punctuation, with . and _ accepted defensively. This corrects a declaration, not a behavior: resolveCapabilities is not wired into the spawn hook, where live detection remains isV2SpawnInput and isCollaborationToolName. The leaf-guard blocks injected into every spawned child now say the child shares the parent's working directory and must not run branch-level git commands.
…and widen namespace matching The final pre-push review caught the taxonomy overstating its own thesis: a create_thread task only gets its own checkout with environment: worktree, while environment: local shares the project checkout, and the goal/PABCD state is a codexclaw overlay keyed to the task rather than something create_thread provides. The claim that subagents of different threads cannot collide was false for two local threads on one checkout. Both are corrected wherever they appear, including the always-read bodies. The V2 wait row now records that wait_agent there is timeout-only with no targets argument, the second way V1-shaped code fails silently on V2. exposesTool also matches the multi_agent_v1 namespace. It accepts the double underscore form because this session's own catalog exposes multi_agent_v1__spawn_agent, which the review had ruled out from the hook-facing name alone; the hook-facing and model-facing renderings differ and detection reads the latter. The four role prompts gain the shared-tree sentence, since they are the canonical source and the hook injection is a second line of defence rather than the first.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1a5de4ac4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| |---|---|---| | ||
| | Working directory | the parent's, unchanged; never a copy | its own, with `environment: worktree`; the shared project checkout with `local` | | ||
| | Git branch and HEAD | the parent's | its own under `worktree`; shared under `local` | | ||
| | Edits visible to the parent | immediately, as the parent's own uncommitted changes | only through git | |
There was a problem hiding this comment.
Treat local-thread edits as shared-checkout changes
When a thread uses environment: local, the preceding rows correctly state that it shares the project checkout and HEAD, so its uncommitted edits are immediately visible and can collide with the parent's work. Saying thread edits are visible "only through git" can make coordinators assume an isolation boundary that does not exist; distinguish local threads from worktree threads here and apply the shared-tree safeguards to the former.
Useful? React with 👍 / 👎.
| before merging the tip: | ||
|
|
||
| ```sh | ||
| git merge-base --is-ancestor origin/<link-branch> <tip-commit> |
There was a problem hiding this comment.
Fetch current link heads before checking ancestry
When another actor has pushed a link branch since the last fetch, origin/<link-branch> remains a stale local remote-tracking ref, so this command can succeed even though the tip is not descended from the PR's actual current head. Merging on that false pass leaves the updated PR open and breaks the promised auto-close behavior; fetch the relevant refs or verify and fetch each current headRefOid before running the ancestry gate.
Useful? React with 👍 / 👎.
Problem
Two different mechanisms answer to the word "dispatch" in this codebase, and the skills never said which one a task needs.
A subagent (
spawn_agent) runs in the parent's own working directory: same path, same branch, same HEAD. A thread (create_thread) is a separate Codex task, and withenvironment: worktreeit gets its own checkout.Nothing told an agent that. On V1 the host tool description says the opposite, instructing the caller to have the child "edit files directly in its forked workspace"; only the V2 usage hint states the shared directory, so a V1 session is never told it by the runtime. The predictable failure is spawning N subagents for N branch lanes and putting N writers on one HEAD.
Evidence
Measured, not assumed. A probe subagent reported the parent's
pwd,git rev-parse --show-toplevel, branch and HEAD, and a file it created appeared as?? PROBE_SUBAGENT_A.tmpin the parent'sgit status. Incodex-rs(095da4b7e),apply_spawn_agent_runtime_overridesassignsconfig.cwd = turn_cwdand is called from bothmulti_agents/spawn.rs:109andmulti_agents_v2/spawn.rs:145; no worktree is created anywhere on the spawn path.fork_contextandfork_turnsfork conversation history, not the filesystem.The full record is in
devlog/_plan/260913_dispatch_taxonomy/001_evidence.md.Changes
The taxonomy. New
skills/pabcd/references/dispatch-surfaces.mdowns the choice between surfaces, with DISPATCH-SURFACE-01, DISPATCH-SHARED-TREE-01, DISPATCH-ROUTE-01 and DISPATCH-AUTHORITY-01.The rule lives where it gets read. An audit walked the real reading path for "merge seven PR lanes in parallel" and showed that a new reference is never opened on it:
cxc-loopforbids following links whose conditions do not obviously apply, andcxc-pabcd's delegation heading had already chosen "subagents" before any pointer was read. So the routing decision and its authority clause also sit in the always-read bodies ofcxc-loopandcxc-pabcd, in the SessionStart PR pointer, and instacked-prs.md. The heading became "Delegation model — choosing a surface".DEV-STACK-08. Lane-parallel stacks with an owner-authorized tip-only CI gate: lane grouping,
[skip ci]on non-tip heads, merge commits rather than squashes for a lane tip so every link reportsMERGED, thegit merge-base --is-ancestorinvariant that auto-close depends on, top-down merging for chained children, and wave ordering. DEV-STACK-01/03/04/07 gained cross-references so it cannot silently override "merge bottom-up" or "no top-only skips".A wrong capability identity.
capabilities.tsdeclared V2 spawn as a tool calledcreate_task. No such tool exists in either family: both registerspawn_agent, and V2 differs by namespace (collaboration) and by requiringtask_name. It is nowfollowup_task, a real V2-only signal, anddetectSpawnSurfacecan decide from a live tool list while keepingv2as the no-evidence default and the exactCODEXCLAW_SPAWN_V1override. This corrects a declaration; live spawn detection is stillisV2SpawnInput/isCollaborationToolNameand is untouched.The V1/V2 split.
delegation.md's two trailing bullets became a detection rule plus labelled V1, V2 and thread-surface tables, naming both silent failure modes of assuming V1 semantics on V2: the no-content mailbox, andwait_agenthaving notargetsargument there.What every child reads. The leaf-guard and scope blocks injected into every spawned child, and the four role prompts, now state that the child shares the parent's working directory and must not run branch-level git commands.
Review history worth knowing
Four independent review rounds ran on grok-4.6. One returned FAIL and forced the rule out of a skippable reference. The last caught the taxonomy overstating its own thesis — it claimed a thread always gets its own worktree, when
environment: localshares the project checkout, and claimed subagents of different threads cannot collide, which is false for twolocalthreads. Both are corrected.Validation
npm run gateexit 0.npm test: 3141 tests, 3137 pass, 1 fail. The failure isgui/router.test.tsunable to resolvereactbecause this worktree has no GUInode_modules; it fails identically on the unmodified base, so it is environmental rather than a regression. 11 new tests cover the surface detection, including the namespaced catalog forms and empty-list-is-no-evidence, plus two assertions pinning the new SessionStart clause.