Skip to content

Separate thread dispatch from subagent dispatch, and add DEV-STACK-08 - #163

Merged
lidge-jun merged 14 commits into
devfrom
codex/dispatch-taxonomy-hardening
Sep 13, 2026
Merged

lidge-jun merged 14 commits into
devfrom
codex/dispatch-taxonomy-hardening

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

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 with environment: worktree it 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.tmp in the parent's git status. In codex-rs (095da4b7e), apply_spawn_agent_runtime_overrides assigns config.cwd = turn_cwd and is called from both multi_agents/spawn.rs:109 and multi_agents_v2/spawn.rs:145; no worktree is created anywhere on the spawn path. fork_context and fork_turns fork 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.md owns 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-loop forbids following links whose conditions do not obviously apply, and cxc-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 of cxc-loop and cxc-pabcd, in the SessionStart PR pointer, and in stacked-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 reports MERGED, the git merge-base --is-ancestor invariant 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.ts declared V2 spawn as a tool called create_task. No such tool exists in either family: both register spawn_agent, and V2 differs by namespace (collaboration) and by requiring task_name. It is now followup_task, a real V2-only signal, and detectSpawnSurface can decide from a live tool list while keeping v2 as the no-evidence default and the exact CODEXCLAW_SPAWN_V1 override. This corrects a declaration; live spawn detection is still isV2SpawnInput/isCollaborationToolName and 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, and wait_agent having no targets argument 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: local shares the project checkout, and claimed subagents of different threads cannot collide, which is false for two local threads. Both are corrected.

Validation

npm run gate exit 0. npm test: 3141 tests, 3137 pass, 1 fail. The failure is gui/router.test.ts unable to resolve react because this worktree has no GUI node_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.

…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.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-13T03:26:25.711574Z c1a5de4 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 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 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@lidge-jun
lidge-jun merged commit b93e0e8 into dev Sep 13, 2026
13 checks passed
@lidge-jun
lidge-jun deleted the codex/dispatch-taxonomy-hardening branch September 13, 2026 03:33
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