You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As a team running Pair inside Codex I wantpair-loop to fan out to fresh Codex subagents — explicit context packets in, schema-validated results out, audit and checkpoints on disk, resumable after a kill So that the same Pair delivery process runs context-safely in Codex as a first-tier in-harness realization, instead of degrading to one card per invocation
Where: a Codex CLI/app/IDE session in a Pair-enabled repository, against GitHub Projects, with the audit trail on disk under the working area.
Refined: Story is detailed, estimated, and ready for development
In Progress: Story is actively being developed
Done: Story delivered and accepted
Assumptions
Resolved during an unattended refinement (2026-08-22) from ADR-017, ADR-016, the ADL 2026-07-11-agent-execution-layer, #250/#451's already-Refined acceptance criteria, and a direct capability probe of the Codex CLI installed on this machine (codex-cli 0.149.0). Each is a defensible default, not a product ruling — overturn one and the AC it backs changes.
Codex does expose a fan-out primitive, and the draft's central unknown is now answered by evidence, not by assumption. Probed on codex-cli 0.149.0: feature multi_agent is stable and on by default, and the binary carries the v1 multi-agent tool handlers spawn_agent, wait (wait_agent), send_input, resume_agent, close_agent; a v2 toolset (spawn, wait, list_agents, send_message, followup_task, interrupt_agent) sits behind multi_agent_v2 (stable, default off), configurable through features.multi_agent_v2.* (max_concurrent_threads_per_session, min/max/default_wait_timeout_ms, tool_namespace, wait_agent_enabled, subagent_developer_instructions). Hooks subagent_start/subagent_stop exist; a separate enable_fanout feature is removed. This makes Codex a legitimate tier-1 in-harness realization — exactly the slot pair-cli execution adapter: run a card on any engine (pi, opencode, claude -p) — portable fan-out, amends ADR-017 #451's AC11 already reserves for «Codex subagents per Codex workflow orchestration for Pair skills and pair-loop #441».
The same evidence proves the surface is volatile, which is why AC1/AC2 are written as a runtime capability probe with fail-closed degradation rather than a version check: two tool namespaces coexist, one is default-off, and a third fan-out mechanism has already been removed. The adapter must never conclude "Codex, therefore subagents".
Role definitions are explicit packets; profiles are an optional accelerator. Codex does read .codex/agents and an [agents] config block, but the story's own Objective forbids assuming that a named profile binds consistently across Codex surfaces. The minimum viable adapter therefore passes role instructions + the skill reference in each spawn request; a profile, if present, is an optimization the adapter must be able to run without.
Distribution is unchanged, and the draft's .codex/skills phrasing is corrected. Pair writes no .codex/ tree: skills reach Codex through the .agents/skills/ symlink target declared in apps/pair-cli/config.json plus the root AGENTS.md bridge. (Codex can also read .codex/skills; Pair simply does not use it.) Out of scope either way — recorded so the AC does not encode a path Pair never writes.
Touches packages/knowledge-hub (dataset skill + KB convention) and apps/website (docs); both listed explicitly Low. The table covers every deployable, so neither the file-absent Medium default nor the unlisted-service conservative High applies
Change/diff risk
yellow
story scope
A new harness-realization branch in the pair-loop skill + a new KB skill-convention (capability probe / realization cascade) + Codex role packets + result-contract wiring + docs + a decision record. Additive; no data schema, no migration, no break to a published surface. Not folded by change-risk.dataset-mirror-pairs: the diff reaches beyond one dataset↔mirror pair (KB convention, dataset skill, website docs, adoption)
Business impact
red
subdomain class
Collaborative Workflow is Core — this story executes delivery orchestration, the product's central capability and epic #212's payoff. Same dimension that puts #250 and #451 at red
Security relevance
red
path heuristic
Unattended fan-out of subagents with repository write access, under a harness whose sandbox/approval posture (approval_policy, sandbox_mode, project trust, --dangerously-bypass-approvals-and-sandbox) is itself an authorization control. Same profile as #250, #451 and #217. Unaffected by tier-resolution.default-artifact-downgrade, which never dilutes this dimension
Coupling balance
yellow
scoped /pair-capability-map-contexts (see below)
Raised above #250/#451's "not assessed", deliberately: this story introduces a relationship whose far side carries an observed High volatility (two coexisting Codex multi-agent tool namespaces, one default-off, a third mechanism already removed). Unbalanced-but-mitigated by the anti-corruption layer AC1/AC4 mandate. Does not change the tier
Tier = max(assessed) = risk:red. tier-resolution.default-artifact-downgrade cannot fire: it requires zero red dimensions and this diff carries two (Business impact, Security relevance), both dimensions the override explicitly refuses to dilute. Gate checks for 🔴: lint + type + build + unit + integration/E2E; extended checklist; explicit approval.
Cost = yellow: the LLM calls signal — each iteration fans out N agent runs — baselines 🟠 orange and drops one class under the bounded-vs-unbounded heuristic, because every ceiling is hard and declared: max_parallelism (tech/automation.md), the harness's own max_concurrent_threads_per_session, the mandatory stop predicate and the max-iterations backstop, plus bounded waits (AC10). Identical signal and identical reasoning to #250 and #219 — this story adds a realization of the same fan-out, not a second loop (that stays #250's, and the headless outer loop stays #451's, which is why #451 sits one class higher at orange).
Confidence: high — classified from a fully declared scope against the current adoption files, with the harness capability probed rather than assumed. Deterministic on re-run against this body and these adoption inputs.
Subdomain and Context Analysis
Scoped to what this story touches (/pair-capability-map-subdomains + /pair-capability-map-contexts, story scope — never all). No catalog file was created or updated: every touched entry already exists with the same classification, so this run recorded a delta of zero.
The adapter executes delivery orchestration — the way-of-working mechanics this subdomain owns. It carries no merit logic; it is the thing that runs the lane
not rated (catalog carries no rating; the context map's Volatility column is intentionally empty)
Unbalanced + volatile → mitigated. High distance already demands a contract, and the strength is one; what makes it unbalanced is the volatility on the far side. Mitigation recorded and mandated by AC1/AC4: an anti-corruption layer — tool names, namespaces and config keys kept as data, resolved by a runtime capability probe, with fail-closed degradation to tier-2/tier-3. Carried as a row in Technical Risks (D38)
Development Collaboration → pair-loop / implement-batch lane definition (same repo)
Conformist — consumes the phase sequence and the parameters exactly as declared, adds none (D18)
none
low
balanced
Development Collaboration → Knowledge Standards (tech/automation.md, ADR-017, the new convention)
Weak — read-only for policy; one decision record written
none
low
balanced
Development Collaboration → Integration & Process Standardization (AGENTS.md, .agents/skills/, integrations/codex.mdx)
Weak — documentation and an already-published distribution target, unchanged by this story
none
low
balanced
Coupling balance for the matrix: yellow — see the first row. This is the one place this story departs from #250/#451's "not assessed": those cards had no rated volatility on either side, whereas here the far side's churn is directly observed. It does not move the tier.
Ubiquitous-language terms this story introduces — to be registered in product/subdomain/collaborative-workflow.context.mdat implementation time (this refinement writes to the PM tool only): harness realization, capability probe, context packet, role packet, result contract, terminal phase outcome.
Acceptance Criteria
Functional Requirements
AC1 — The realization is probed, never assumed, and always announced
Given a Codex session Whenpair-loop starts Then it resolves its fan-out realization by probing the tools actually exposed in that session (the v1 handles spawn_agent/wait_agent/send_input/resume_agent/close_agent, or the v2 namespace when multi_agent_v2 is enabled), and prints which realization won, which primitive it bound to, and why — before dispatching anything And the probe never infers availability from the product name or a version string alone And the tool names, namespaces and config keys it probes are held as data (one structure), never as conditionals scattered through the skill — a Codex rename must be a data edit
AC2 — No primitive ⇒ degrade in the declared order, never a silent in-context loop
Given the probe finds no usable fan-out primitive (feature off, surface without it, unknown namespace) When the run continues Then it degrades in preference order — (2) external driver (#451) when available, else (3) degraded one-card path: exactly one eligible card driven to its gate, audit + checkpoint written, stop, continue-token reported And under no condition does the orchestrator iterate multiple cards inside one context (ADR-017 §3, architectural invariant) And the degradation and its reason are recorded in the audit
AC3 — Every phase runs in a fresh subagent, fed one explicit context packet
Given an eligible card and a phase to run (implement / PR / review / fix) When the phase is dispatched Then it runs in a freshly spawned Codex subagent whose input is an explicit context packet: role instructions, the Pair skill to invoke, the card's id/title/branch/base/worktree, any scope note, and the return schema And the packet contains no other card's content and no material the role must not see And the orchestrator retains only the compact schema-validated result — never the subagent's transcript
AC4 — Role instructions travel in the spawn request; profiles are optional
Given the adapter spawns a role When it builds the request Then role instructions and the skill reference are passed explicitly in that request And Given no .codex/agents profile and no [agents] config exist Then the adapter works unchanged — it never depends on a named profile binding, on any Codex surface
AC5 — Reviewer blindness survives the port
Given the review phase When its packet is built Then the packet excludes .pair/working/** — checkpoint, handoff and working logs — preserving the same blindness invariant the Claude pair-reviewer agent carries And a packet that would carry that material is rejected before spawn, not merely discouraged in prose
AC6 — One result contract for both harnesses, fail-closed
Given a dispatched phase completes When the orchestrator reads its outcome Then the return value is validated against the same JSON-Schema result contract the Claude workflow uses (ADR-016 / the workflow's step schemas), realized through Codex's structured-result mechanism — no Codex-specific format is invented And Given the return is absent, unparseable, or schema-invalid Then the phase counts as failed, the reason is recorded, and that card stops advancing — a missing result is never read as success
Givenmax_parallelism from tech/automation.md and the harness's own concurrency ceiling (Codex max_concurrent_threads_per_session) When the batch is composed Then the adapter runs min(dependency-allowed, max_parallelism, harness-ceiling) and prints the effective cap and which limit bound it And it never exceeds max_parallelism, and it introduces no policy parameter of its own — eligibility, dependency analysis, mutex sets and the stop predicate all stay pair-loop's (D18)
AC8 — Audit and checkpoint on disk; the orchestrator never accumulates
Given any dispatch or outcome When it happens Then it is appended to the audit file under the working area (working_path from pair.config.json, default .pair/working/), and per-card resumable state is written through /pair-capability-checkpoint And the audit is never held in context, and .pair/working/ is never loaded as ambient context (D14) And Given the audit cannot be written Then the unattended run fails loudly — an unaudited unattended run is not an acceptable degraded mode
AC9 — Resume re-dispatches only what is unfinished
Given a Codex run killed, cancelled or context-compacted mid-batch When it is re-invoked on the same scope Then it reconstructs state from the audit + checkpoints, re-dispatches only phases not recorded complete, and re-runs no completed card And it opens no second PR for a story that already carries one — an in-flight story re-enters through the existing-PR path
AC10 — Cancellation, timeout, dead agent and partial batch each have a declared representation
Given a subagent that is cancelled, exceeds its wait timeout, dies, or never started When the orchestrator collects results Then each maps to a declared terminal phase outcome recorded per card (cancelled / timed-out / died / not-started), distinct from failed-validation And every wait is bounded by a timeout — an unbounded wait is not permitted And the surviving siblings' results are still collected and audited: a partial batch is reported, never discarded And an unrecognisable outcome is fail-closed to failed (never assumed successful)
AC11 — Claude Code behaviour is unchanged
Given a Claude Code session Whenpair-loop runs Then it binds the existing workflow realization and behaves exactly as before And this story requires no change to .claude/workflows/*, .claude/agents/* or their dataset mirrors — a regression surface, not a refactor target
AC12 — The realization rule is recorded, once, consistently with #451
Given this story lands When the decision is recorded Then a record extends ADR-017 §4's Realization rule to name the Codex in-harness realization as a tier-1 fan-out realization, using the same three-tier ordering #451 records — (1) in-harness, (2) external driver, (3) degraded one-card + continue-token And whichever of #441 / #451 lands second amends the existing record rather than restating a second, divergent framing And ADR-017 §1 (pair-next frozen atom), §2 (implement-batch as engine) and §6 (tech/automation.md as policy home) are left intact And the ADL 2026-07-11-agent-execution-layer's statement that other assistants «do not have» subagent primitives is corrected for Codex, with the probe evidence
AC13 — Distribution is untouched
Givenpair install / pair update When they run Then skills continue to reach Codex through the existing .agents/skills/ symlink target plus the root AGENTS.md And this story adds no new distribution target and writes no .codex/ tree; the mirror guards stay green
AC14 — Every path is tested against mocked harness primitives
Given CI with no Codex binary When the suite runs Then conformance and smoke tests exercise, against mocked spawn/wait primitives: probe-hit (v1) · probe-hit (v2 namespace) · probe-miss → tier-2 → tier-3 · packet contains exactly one card · reviewer packet rejects a working-path entry · schema-valid return · absent/invalid return ⇒ failed · timeout · cancellation · dead agent · partial-batch collection · cap arithmetic including 1 and 0 · resume skipping completed phases
Business Rules
Zero merit logic (D18): the adapter selects nothing, classifies nothing and judges nothing. Eligibility, dependency analysis, mutex sets, the stop predicate and auto-advance stay pair-loop's. Grep-verifiable.
Context isolation is architectural (ADR-017 §3): a fresh subagent per card/phase, audit on disk. An in-context multi-card loop is a defect, not a degraded mode.
The harness surface is data, behind an anti-corruption layer: tool names, namespaces, config keys and their probes live in one structure. A Codex rename is a data edit, not a refactor.
Fail-closed everywhere: no probe, no schema-valid return, no recognisable terminal outcome, no writable audit ⇒ stop or degrade — never proceed on an assumption.
Merge stays the human/policy gate: the adapter never merges, in any mode.
Claude is a regression surface: this story ports a realization; it does not reshape the one that works.
Edge Cases and Error Handling
Case
Expected behaviour
multi_agent disabled, or a surface without the tools
Probe miss → tier-2, else tier-3; reason audited (AC2)
multi_agent_v2 enabled with a custom tool_namespace
Resolved from the probed namespace, not a hard-coded name (AC1)
Codex renames or removes a multi-agent tool
Probe miss, clean degradation; the fix is a data edit (BR4)
Subagent returns prose instead of the schema
Phase failed, card stops advancing (AC6)
Wait exceeds max_wait_timeout_ms
timed-out terminal outcome; siblings still collected (AC10)
Subagent killed by the harness / session compacted mid-batch
died; re-invocation resumes from audit + checkpoints (AC9, AC10)
Harness ceiling below max_parallelism
Effective cap is the harness's; the binding limit is printed (AC7)
HALT on the policy read, before any card is touched (#250 AC7 — unchanged here)
No eligible card
Clean "nothing eligible" exit; nothing is spawned
Audit path unwritable
Loud failure, no unattended run (AC8)
A packet would carry .pair/working/** into the review role
Rejected before spawn (AC5)
Story already carries a PR
Continued on it; never a second PR (AC9)
Nested spawn attempted from inside a subagent
Not attempted: dispatch is the orchestrator's alone, matching the review-dispatch-required signal ADR-018 already defines
Definition of Done Checklist
Development Completion
All acceptance criteria implemented and verified
pair-loop's Realization section gains the Codex in-harness branch, with the capability probe and the degradation cascade — one skill, two realizations, no forked process logic
A KB skill-convention for harness capability probing / realization cascade (the gap the convention set does not cover today), so the next harness reuses it instead of re-deriving it
Role packets for implementer / PR / reviewer / fixer, carrying the role instructions explicitly (AC4) and the blindness rule (AC5)
Result-contract wiring reuses ADR-016's generated schemas — no new contract format (AC6)
Audit + checkpoint + resume paths implemented against the existing working-area convention (AC8, AC9)
Ubiquitous-language terms registered in product/subdomain/collaborative-workflow.context.md
Decision record extending ADR-017 §4 and correcting the ADL 2026-07-11-agent-execution-layer (AC12)
Documentation updated from the canonical Pair artifacts: apps/websiteintegrations/codex.mdx (the Codex realization, its limits, and what to enable) and the pair-loop docs page's non-Claude note
Code review completed and approved
Quality Assurance
🔴 tier gate set green: lint + type + build + unit + integration/E2E
Automated tests: the full AC14 list, against mocked spawn/wait primitives
Regression: Claude Code path byte-unchanged; workflow/agent mirror guards green (AC11, AC13)
Grep-verifiable: the adapter contains no eligibility/classification/stop criteria of its own (D18, BR1)
Manual dogfood: one Codex run over a narrow perimeter of this repo's own eligible backlog, leaving a readable audit
Deployment and Release
No infrastructure change, no migration, no feature flag required — stated explicitly rather than omitted (🔴 extended depth)
No new distribution target; pair install / pair update unchanged (AC13)
The shipped default keeps unattended merge off — inherited from tech/automation.md, not re-decided here
Story Sizing and Sprint Readiness
Refined Story Points
Final Story Points: 8 (XL) Confidence Level: Medium Sizing Justification: five distinct deliverables — the capability probe + realization cascade (and the KB convention that generalizes it), the role-packet layer with its blindness rule, the result-contract wiring, the audit/checkpoint/resume path, and the terminal-outcome mapping for cancellation/timeout/death/partial batches — over a lane whose Claude realization is a 1451-line workflow. The draft's XL was tentative with the runtime contract unknown; the probe (Assumption 1) removes the unknown and confirms 8 at Medium. It is not larger than 8: no data migration, no infrastructure, no new contract format, and the per-card lane is consumed rather than rebuilt.
Sprint Capacity Validation
Sprint Fit Assessment: fits a sprint only once #250 is merged — this story realizes pair-loop's lane in a second harness, and against an unmerged lane definition it will not. Total Effort Assessment: fits — Yes (conditional on the dependency above)
Story Splitting Recommendations
Not required, but available if #250 slips or the card proves too wide in flight:
Codex workflow orchestration for Pair skills and pair-loop #441-A — probe, packets and one card (5): the capability probe + realization cascade + the KB convention, role packets with the blindness rule, the result-contract wiring, audit/checkpoint, and a single-card Codex dispatch. Covers AC1–AC6, AC8, AC11–AC13. Independently valuable: a Codex user gets context-safe fresh-subagent execution of one card, which is already strictly better than tier-3.
Codex workflow orchestration for Pair skills and pair-loop #441-B — fan-out, resume and failure modes (5): bounded parallelism against the harness ceiling, resume that skips completed phases, and the terminal-outcome mapping for cancellation/timeout/death/partial batches. Depends on A. Covers AC7, AC9, AC10, and the remainder of AC14.
Split rationale: A delivers the invariant (context isolation in Codex) without the concurrency surface; B adds throughput and the failure taxonomy. Neither half re-implements the other's logic.
Shared Components: the pair-loop skill, tech/automation.md (read-only), the working-area convention, ADR-016's contract generator, the checkpoint template.
External Dependencies
Third-party Integrations: the Codex CLI/app/IDE multi-agent tool surface — the one genuinely external dependency, and the reason AC1/AC2 are a probe rather than a version check. GitHub Projects and the code host are reached through the existing adapters; nothing new. Infrastructure Requirements: none.
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: mocked-primitive conformance tests for the full AC14 list (probe hit v1 / hit v2 / miss, packet isolation, blindness rejection, schema-valid and schema-invalid returns, timeout, cancellation, death, partial-batch collection, cap arithmetic, resume). Plus a control comparison: the same fixture card driven through the Claude workflow realization and through the Codex realization must produce the same per-card outcomes and an equivalent audit — a divergence localises the bug in the new realization, not in the lane. Test Data Requirements: a fixture board with eligible/ineligible/untagged cards, a probe stub in each of its three states, and return-value fixtures for every terminal outcome. Environment Requirements: CI needs no Codex binary (primitives are mocked). One manual dogfood run in a real Codex session over a narrow perimeter of this repo's backlog.
User Validation
Success Metrics: a Codex session advances a batch of eligible cards to their gates unattended, with an audit a human can read to reconstruct every dispatch, outcome and stop — and with the orchestrator's context flat across the batch. Rollback Plan: the realization cascade is the rollback — disabling the probe (or the harness feature) drops the run to tier-2/tier-3 with no code change, and Claude Code is untouched throughout.
Notes and Additional Context
Refined 2026-08-22, unattended, following /pair-process-refine-story. The draft carried seven open questions; all seven are resolved above and none required a product ruling:
Which Codex surfaces/versions expose spawn_agent/wait_agent reliably? → Assumption 1 + Assumption 2: probed on codex-cli 0.149.0; answered as a runtime probe, not a version matrix, because the surface is demonstrably in motion.
Can a subagent receive a scoped skill + packet without inheriting private material? → AC3 + AC5: the packet is explicit and allow-listed, and a review packet carrying .pair/working/** is rejected before spawn.
Explicit packets, .codex/agents profiles, or both? → Assumption 4 / AC4: explicit packets are normative, profiles an optional accelerator — the story's own Objective forbids depending on profile binding.
What is the exact machine-readable handoff contract? → Assumption 3 / AC6: the one Pair already has (ADR-016 JSON-Schema return contract). No new format.
Which state belongs to audit / checkpoint / per-phase result? → AC6 + AC8: per-phase result = schema-validated return value (in memory, compact); resumable per-card state = /pair-capability-checkpoint; everything else = the append-only audit file.
How are cancellation, timeout, dead agent and partial batch represented? → AC10: four declared terminal outcomes, bounded waits, partial batches reported rather than discarded, unrecognisable outcomes fail-closed.
Two factual corrections to the draft, both recorded rather than silently applied:
The draft's «.codex/skills symlink distribution mechanism» is not what Pair does — skills reach Codex via .agents/skills/ + AGENTS.md (Assumption 5, AC13).
The draft's premise that Codex «has no Pair adapter» while «exposing subagent primitives» is now evidence-backed rather than assumed, and it contradicts the ADL 2026-07-11-agent-execution-layer, which states that assistants such as Codex do not have subagent primitives. AC12 requires that correction to be recorded.
Capability-probe evidence (2026-08-22, codex-cli 0.149.0, local install): codex features list reports multi_agentstable/on, multi_agent_v2stable/off, enable_fanoutremoved; the binary carries core/src/tools/handlers/multi_agents/{spawn,wait,send_input,resume_agent,close_agent}.rs and multi_agents_v2/{spawn,wait,list_agents,send_message,followup_task,interrupt_agent}.rs, the config keys features.multi_agent_v2.{max_concurrent_threads_per_session,min_wait_timeout_ms,max_wait_timeout_ms,default_wait_timeout_ms,tool_namespace,wait_agent_enabled,subagent_developer_instructions} and agents.max_concurrent_threads_per_session, the hooks subagent_start/subagent_stop, and canonical subagent task paths (/root/task1/task_3). codex exec offers --json (JSONL events), --output-schema <FILE> and -o/--output-last-message — the structured-result mechanism AC6 binds to. This is a snapshot of one version on one machine, which is precisely why AC1 mandates a probe.
Design: not required — the runtime contract was validated during this refinement (Assumptions 1–2) and the remaining shape is fixed by ADR-017 §3/§4, ADR-016 and #451's three-tier ordering.
Technical Strategy: add a second realization to pair-loop, not a second engine. The skill keeps one lane definition and one policy source; what varies is how a phase is dispatched. A small resolution cascade — probe the harness, bind the strongest available realization, announce it, degrade fail-closed — sits behind an anti-corruption layer that holds every harness-specific name as data. Each dispatch is one fresh subagent, one explicit packet in, one schema-validated value out; everything durable lands on disk.
Key Components:
Component
Nature
Harness capability probe + realization cascade
New KB skill-convention + its use in the pair-loop skill
Data — one structure, replaceable without touching logic
Role packets (implementer / PR / reviewer / fixer)
Prompt-side contract, with the reviewer's allow-list
Result-contract binding
Reuse of ADR-016's generated JSON Schemas
Audit + checkpoint + resume
Existing working-area convention and /pair-capability-checkpoint
Terminal-outcome mapping
Cancelled / timed-out / died / not-started / failed-validation
Decision record extending ADR-017 §4
Adoption
integrations/codex.mdx + pair-loop docs
apps/website
Data Flow: policy (tech/automation.md) → pair-loop selection + dependency analysis (unchanged, #250) → realization probe → per-card/per-phase context packet → Codex spawn_agent → bounded wait_agent → schema-validated result → audit + checkpoint → next decision. Only the compact result crosses back into the orchestrator.
Integration Points: the Codex multi-agent tool surface; the Pair skills invoked inside each subagent (never modified); tech/automation.md (read-only); the PM tool and code host through existing adapters; per-card worktrees (existing invariant).
A Codex release silently disables the realization, or worse, changes its semantics under the same names
High
The anti-corruption layer: surface names/keys as data, a runtime probe as the only source of availability (AC1), fail-closed degradation to tier-2/tier-3 (AC2), and mocked primitives in CI so the suite does not depend on the vendor
The orchestrator drifts into "a second process engine"
Divergent behaviour between harnesses, duplicated logic
Medium
BR1 + AC7: no policy parameter of its own, grep-verifiable; one lane definition shared with the Claude realization
A model-driven orchestrator loses determinism that the Claude workflow gets from JS
Non-reproducible batches, unreadable audits
Medium
Every decision is schema-validated and audited (AC6, AC8); the control comparison against the Claude realization is a DoD item
Reviewer blindness silently lost in the port
An independent review that reads the author's own handoff
Medium
AC5 makes it a pre-spawn rejection, with a dedicated test — not a prose instruction
Unbounded wait_agent on a dead subagent
A run that hangs forever unattended
Medium
AC10: every wait bounded; timed-out/died are declared outcomes; siblings still collected
Autonomous fan-out with repository write access under a permissive sandbox/approval posture
Prolonged privileged execution
Medium
Inherited ceilings only — max_parallelism, the stop predicate, the iteration backstop — plus merge never automatic (BR6) and the fail-closed audit (AC8)
Hard prerequisite, stated; the A/B split is the fallback if it slips
Spike Requirements
Required Spikes: none. The spike the draft implied — validate the Codex orchestration primitives — was executed during this refinement; its evidence is in Notes and its volatility is carried as AC1's probe rather than as a research task.
Task Breakdown
T-1: KB skill-convention — harness realization cascade and capability probing
T-2: Harness surface map as data (anti-corruption layer)
Summary: Add the missing convention that tells any Pair skill how to discover what its harness can do and which realization to bind.
Type: Documentation / Standard
Description: The skill-conventions/ set covers resolution cascade, graceful degradation and template resolution, but nothing about harness capability. That gap is why the Codex realization would otherwise be hand-rolled inside one skill. This task writes the convention first so T-2/T-3 implement a stated rule rather than inventing one, and so the next harness reuses it.
Acceptance Criteria:
Primary deliverable: a new convention file under .pair/knowledge/guidelines/technical-standards/ai-development/skill-conventions/, mirrored into the dataset
Integration requirement: referenced from graceful-degradation.md and resolution-cascade.md rather than duplicating them
Verification method: mirror-guard test green; the convention is cited by the pair-loop skill
Implementation Approach:
Technical Design: one convention file, generic and portable — no Codex specifics (those are data, T-2)
Files to Modify/Create: .pair/knowledge/guidelines/technical-standards/ai-development/skill-conventions/harness-realization.md (+ dataset mirror), .../skill-conventions/README.md index row
Technical Standards: skill-conventions README scope note (generic and portable)
Dependencies:
Technical: none
Tasks: none — this is the root task
Implementation Steps:
Write the convention: probe → bind → announce → degrade, with the three tiers named
Add the index row and the cross-references
Mirror into packages/knowledge-hub/dataset/
Testing Strategy:
Unit Tests: n/a
Integration Tests: mirror-guard equality
T-2: Harness surface map as data (anti-corruption layer)
Summary: Hold every Codex-specific name — tool handles, namespaces, config keys — in one replaceable structure.
Type: Feature Implementation
Description: The far side of this integration is volatile by observation (v1 and v2 namespaces coexist, v2 is default-off, enable_fanout was removed). BR4 requires that a rename be a data edit. This task creates the single structure the probe reads: for each known realization, the spawn/wait/cancel handles, the namespace, and the config keys that gate and bound them.
Acceptance Criteria:
Primary deliverable: one declarative surface map covering the v1 handles (spawn_agent, wait_agent, send_input, resume_agent, close_agent) and the v2 namespace, plus the concurrency/timeout config keys
Quality standard: zero harness names outside this structure — grep-verifiable
Integration requirement: consumed by T-3's probe only; carries no behaviour of its own
Verification method: a test asserts no harness tool name appears in the logic files
Summary: The cascade itself: probe the session, bind the strongest available realization, print what won and why, degrade tier-2 → tier-3 when nothing binds.
Type: Feature Implementation
Description: Adds a second realization to pair-loop, not a second engine — one lane definition, one policy source, a varying dispatch mechanism. Availability comes only from the runtime probe, never from a product name or version. Under a probe miss the run degrades to #451's external driver when available, else to the degraded one-card path with a continue-token; it never iterates multiple cards in one context.
Acceptance Criteria:
Primary deliverable: the Codex in-harness realization branch plus the resolution cascade in the pair-loop skill
Quality standard: the bound realization, the primitive it bound to, and the reason are announced before any dispatch, and audited
Integration requirement: the Claude workflow realization is selected unchanged on Claude Code
Summary: Build the explicit packet each freshly spawned subagent receives — one card, one role, one skill, one return schema.
Type: Feature Implementation
Description: Each phase (implement / PR / review / fix) is dispatched into a fresh subagent whose entire input is an explicit packet: role instructions, the Pair skill to invoke, the card's id/title/branch/base/worktree, any scope note, and the return schema. Role instructions travel in the request: .codex/agents profiles may exist but the adapter must run with none present. The packet must carry no other card's content, and the orchestrator retains only the compact result.
Acceptance Criteria:
Primary deliverable: packet builders for the four roles, mirroring the .claude/agents/* role definitions without forking their content
Quality standard: single-card isolation — a packet references exactly one card
Integration requirement: works with zero profiles configured (AC4)
Verification method: packet-isolation and no-profile tests (T-11)
Implementation Approach:
Technical Design: role text derived from the existing agent definitions; packet assembled per dispatch
Files to Modify/Create: the pair-loop skill's Codex realization (+ dataset mirror)
Technical Standards: ADR-017 §3; ADR-018 (dispatch is the orchestrator's, never nested)
Dependencies:
Technical: the existing .claude/agents/*.md role definitions (read, not modified)
Tasks: T-3
Implementation Steps:
Extract the four role instruction sets
Define the packet shape and the single-card assertion
Wire packet → spawn request
Testing Strategy:
Unit Tests: packet shape, single-card isolation, no-profile path
Integration Tests: a dispatch round-trip against the mocked primitive
T-5: Reviewer blindness enforced as a pre-spawn rejection
Summary: Make the independent review's blindness a mechanical check, not a sentence in a prompt.
Type: Feature Implementation
Description: The Claude pair-reviewer carries "you MUST NOT read anything under .pair/working/". Porting that as prose would lose it silently. The review packet is allow-listed, and a packet that would carry checkpoint/handoff/working-log material is rejected before the spawn call, with the offending entry named.
Acceptance Criteria:
Primary deliverable: an allow-list check on the review packet with a pre-spawn rejection path
Quality standard: the rejection names the offending entry
Integration requirement: the same invariant the Claude reviewer enforces, expressed once
Verification method: a dedicated failing-packet test (T-11)
Implementation Approach:
Files to Modify/Create: the review packet builder from T-4
Technical Standards: D14 (.pair/working/ never ambient); the reviewer blindness invariant
Dependencies:
Tasks: T-4
Implementation Steps:
Define the review allow-list
Reject and report before spawn
Add the negative test
Testing Strategy:
Unit Tests: allow-list accept/reject
Integration Tests: no spawn occurs on rejection
T-6: Result-contract binding to the ADR-016 schemas, fail-closed on invalid returns
Summary: One contract for both harnesses — reuse the JSON-Schema return contract Pair already generates.
Type: Feature Implementation
Description: The machine-readable handoff contract is not invented here: it is ADR-016's *.contract.json plus the workflow's step schemas. Codex realizes it through its structured-result mechanism. An absent, unparseable or schema-invalid return counts as a failed phase, is recorded, and stops that card advancing — a missing result is never read as success.
Acceptance Criteria:
Primary deliverable: schema binding per phase plus validation of every return value
Quality standard: no Codex-specific result format exists anywhere in the diff
Integration requirement: the schemas are the same artifacts the Claude workflow consumes
Verification method: schema-valid and schema-invalid return tests (T-11)
Implementation Approach:
Files to Modify/Create: the Codex realization's dispatch/collect path
Summary: Three ceilings, one effective cap, and always say which one bound it.
Type: Feature Implementation
Description:max_parallelism comes from tech/automation.md; the harness contributes its own concurrency ceiling (max_concurrent_threads_per_session); the dependency/mutex analysis stays #250's. The adapter takes the minimum, prints the effective cap and the binding limit, and introduces no policy parameter of its own.
Acceptance Criteria:
Primary deliverable: cap arithmetic with the binding-limit report
Quality standard: never exceeds max_parallelism; grep-verifiably owns no policy knob
Integration requirement: reads the policy through pair-loop's existing resolution, not a second reader
Verification method: cap arithmetic tests including 1 and 0 (T-11)
Implementation Approach:
Files to Modify/Create: the Codex realization's batch composition
Technical Standards: ADR-017 §6; D18
Dependencies:
Tasks: T-3
Implementation Steps:
Read the harness ceiling from the surface map
Compute and announce the effective cap
Handle 0 as a no-dispatch outcome
Testing Strategy:
Unit Tests: cap arithmetic across the three sources
Integration Tests: no spawn when the cap is 0
T-8: Audit and checkpoint persistence, fail-loud when the audit is unwritable
Summary: Everything durable goes to disk; the orchestrator keeps nothing but compact results.
Type: Feature Implementation
Description: Every dispatch and outcome is appended to the audit under the working area (working_path from pair.config.json, default .pair/working/); per-card resumable state is written through /pair-capability-checkpoint. .pair/working/ is never loaded as ambient context. If the audit cannot be written, the unattended run fails loudly — an unaudited unattended run is not an acceptable degraded mode.
Acceptance Criteria:
Primary deliverable: audit append + checkpoint write on every dispatch/outcome
Quality standard: no card content accumulates in the orchestrator
Integration requirement: reuses /pair-capability-checkpoint and the working-area convention — no third store
Verification method: audit-content and unwritable-audit tests (T-11)
Implementation Approach:
Files to Modify/Create: the Codex realization's dispatch/collect path
Summary: Name every way a subagent can end, bound every wait, and never throw away the siblings.
Type: Feature Implementation
Description: Cancellation, timeout, death and never-started each map to a declared terminal outcome recorded per card, distinct from failed-validation. Every wait is bounded by a timeout — an unbounded wait would hang an unattended run forever. When one subagent ends badly, the surviving siblings' results are still collected and audited: the partial batch is reported, never discarded. Anything unrecognisable is fail-closed to failed.
Acceptance Criteria:
Primary deliverable: the terminal-outcome taxonomy plus bounded waits and partial-batch collection
Quality standard: no code path can wait unbounded
Integration requirement: outcomes feed pair-loop's existing per-card decision, adding no new decision
Summary: A killed or compacted Codex session picks up where it stopped, without redoing work or opening a second PR.
Type: Feature Implementation
Description: On re-invocation over the same scope, state is reconstructed from the audit and the checkpoints; only phases not recorded complete are re-dispatched. A story already carrying a PR re-enters through the existing-PR path — never a second PR.
Acceptance Criteria:
Primary deliverable: resume reconstruction plus the completed-phase skip
Quality standard: no completed card is re-run
Integration requirement: one PR per story preserved across resumes
Verification method: resume test asserting the skip and the single PR (T-11)
Implementation Approach:
Files to Modify/Create: the Codex realization's entry path
Summary: Prove every path without a Codex binary in CI.
Type: Testing
Description: Implements AC14's full list against mocked spawn/wait primitives: probe-hit (v1), probe-hit (v2 namespace), probe-miss → tier-2 → tier-3, single-card packet isolation, reviewer-packet rejection, schema-valid return, absent/invalid return ⇒ failed, timeout, cancellation, dead agent, partial-batch collection, cap arithmetic including 1 and 0, and resume skipping completed phases. Also carries the control comparison: the same fixture card through the Claude realization and through the Codex realization must yield the same per-card outcomes and an equivalent audit.
Acceptance Criteria:
Primary deliverable: the conformance/smoke suite plus the probe stub in its three states
Quality standard: CI needs no Codex binary and no network
Integration requirement: runs inside the repo's existing test setup
Verification method: 🔴 tier gate set green
Implementation Approach:
Files to Modify/Create: test files alongside the realization; fixture board and return-value fixtures
Technical Standards: the project testing strategy
Dependencies:
Tasks: T-3, T-4, T-5, T-6, T-7, T-8, T-9, T-10
Implementation Steps:
Build the mocked primitive and the three-state probe stub
Cover each AC14 case
Add the Claude-vs-Codex control comparison
Testing Strategy:
Unit Tests: the AC14 list
Integration Tests: the control comparison
T-12: Claude-path regression and distribution guard
Summary: Prove that nothing the Claude path or the installer already does has moved.
Type: Testing / Configuration
Description: AC11 and AC13 are guarantees of absence, so they need assertions: no change to .claude/workflows/*, .claude/agents/* or their dataset mirrors; no .codex/ tree written; skills still reach Codex through the .agents/skills/ symlink target plus the root AGENTS.md; mirror guards green.
Acceptance Criteria:
Primary deliverable: regression assertions for the Claude realization and the distribution targets
Quality standard: the guards fail loudly if a future change writes a .codex/ target
Summary: Record the Codex in-harness realization once, in the same three tiers #451 records.
Type: Documentation / Decision
Description: Extends ADR-017 §4's Realization rule to name Codex subagents as a tier-1 fan-out realization, using the ordering #451 records — (1) in-harness, (2) external driver, (3) degraded one-card + continue-token. Whichever of #441/#451 lands second amends that record rather than writing a divergent second framing. §1, §2 and §6 stay intact. It also corrects the ADL 2026-07-11-agent-execution-layer, which states that assistants such as Codex do not have subagent primitives, citing the probe evidence in this story's Notes.
Acceptance Criteria:
Primary deliverable: one decision record (via /pair-capability-record-decision) plus the ADL correction
Quality standard: no second, divergent three-tier framing exists in adoption
Summary: Tell a Codex user what they now get, what to enable, and where it stops.
Type: Documentation
Description: Updates apps/websiteintegrations/codex.mdx with the Codex realization, what must be enabled for it (the multi-agent feature and its concurrency/timeout knobs), and the degradation behaviour when it is not; adds the non-Claude note to the pair-loop docs page. Registers the terms this story introduces — harness realization, capability probe, context packet, role packet, result contract, terminal phase outcome — in product/subdomain/collaborative-workflow.context.md.
Acceptance Criteria:
Primary deliverable: updated integrations/codex.mdx and pair-loop docs page; six terms registered
Quality standard: written from the canonical Pair artifacts, never restating them
Integration requirement: docs e2e/critical-path checks stay green
Verification method: docs tests green; the context map carries the terms
Implementation Approach:
Files to Modify/Create: apps/website/content/docs/integrations/codex.mdx, the pair-loop docs page, .pair/adoption/product/subdomain/collaborative-workflow.context.md
Story Statement
As a team running Pair inside Codex
I want
pair-loopto fan out to fresh Codex subagents — explicit context packets in, schema-validated results out, audit and checkpoints on disk, resumable after a killSo that the same Pair delivery process runs context-safely in Codex as a first-tier in-harness realization, instead of degrading to one card per invocation
Where: a Codex CLI/app/IDE session in a Pair-enabled repository, against GitHub Projects, with the audit trail on disk under the working area.
Epic Context
Parent Epic: Supervised automation — zero logic, consumes tags #212
Status: Refined
Priority: P1 (Should-Have)
Status Workflow
Assumptions
Resolved during an unattended refinement (2026-08-22) from ADR-017, ADR-016, the ADL
2026-07-11-agent-execution-layer, #250/#451's already-Refined acceptance criteria, and a direct capability probe of the Codex CLI installed on this machine (codex-cli 0.149.0). Each is a defensible default, not a product ruling — overturn one and the AC it backs changes.codex-cli 0.149.0: featuremulti_agentis stable and on by default, and the binary carries the v1 multi-agent tool handlersspawn_agent,wait(wait_agent),send_input,resume_agent,close_agent; a v2 toolset (spawn,wait,list_agents,send_message,followup_task,interrupt_agent) sits behindmulti_agent_v2(stable, default off), configurable throughfeatures.multi_agent_v2.*(max_concurrent_threads_per_session,min/max/default_wait_timeout_ms,tool_namespace,wait_agent_enabled,subagent_developer_instructions). Hookssubagent_start/subagent_stopexist; a separateenable_fanoutfeature is removed. This makes Codex a legitimate tier-1 in-harness realization — exactly the slot pair-cli execution adapter: run a card on any engine (pi, opencode, claude -p) — portable fan-out, amends ADR-017 #451's AC11 already reserves for «Codex subagents per Codex workflow orchestration for Pair skills and pair-loop #441»..claude/workflows/pair-contracts/ensure-contract.mjs, the workflow'sSTEP_SCHEMA/PR_SCHEMA/PROBE_SCHEMA/REVIEW_SCHEMA). Codex realizes it through its structured-result mechanism (codex exec --output-schema, or the spawn tool's declared return shape). Human-readable resumable state stays/pair-capability-checkpoint(.pair/working/checkpoints/<story-id>.md, pair-capability-checkpoint — resumable state (write/resume, handoff pattern) #254). No third store — the same ruling pair-loop — unattended loop engine: pair-next selection, dependency analysis, mutex-safe parallel batch via implement-batch (#219), automation policy + stop predicate #250's Assumption 7 made for the degraded path..codex/agentsand an[agents]config block, but the story's own Objective forbids assuming that a named profile binds consistently across Codex surfaces. The minimum viable adapter therefore passes role instructions + the skill reference in each spawn request; a profile, if present, is an optimization the adapter must be able to run without..codex/skillsphrasing is corrected. Pair writes no.codex/tree: skills reach Codex through the.agents/skills/symlink target declared inapps/pair-cli/config.jsonplus the rootAGENTS.mdbridge. (Codex can also read.codex/skills; Pair simply does not use it.) Out of scope either way — recorded so the AC does not encode a path Pair never writes.Design: not required. The draft demanded the Codex runtime contract be validated before the story could be sized; that validation happened in this refinement (Assumption 1) and the remaining shape is fixed by ADR-017 §3/§4, ADR-016 and pair-cli execution adapter: run a card on any engine (pi, opencode, claude -p) — portable fan-out, amends ADR-017 #451's three-tier ordering. What is left is engineering, not design.Classification
risk:red· cost: yellow (computed, not projected —tech/risk-matrix.mddeclaresActive: risk)Matrix — per dimension
tech/risk-matrix.md)packages/knowledge-hub(dataset skill + KB convention) andapps/website(docs); both listed explicitlyLow. The table covers every deployable, so neither the file-absent Medium default nor the unlisted-service conservative High appliespair-loopskill + a new KB skill-convention (capability probe / realization cascade) + Codex role packets + result-contract wiring + docs + a decision record. Additive; no data schema, no migration, no break to a published surface. Not folded bychange-risk.dataset-mirror-pairs: the diff reaches beyond one dataset↔mirror pair (KB convention, dataset skill, website docs, adoption)approval_policy,sandbox_mode, project trust,--dangerously-bypass-approvals-and-sandbox) is itself an authorization control. Same profile as #250, #451 and #217. Unaffected bytier-resolution.default-artifact-downgrade, which never dilutes this dimension/pair-capability-map-contexts(see below)Tier = max(assessed) = risk:red.
tier-resolution.default-artifact-downgradecannot fire: it requires zero red dimensions and this diff carries two (Business impact, Security relevance), both dimensions the override explicitly refuses to dilute. Gate checks for 🔴: lint + type + build + unit + integration/E2E; extended checklist; explicit approval.Cost = yellow: the
LLM callssignal — each iteration fans out N agent runs — baselines 🟠 orange and drops one class under the bounded-vs-unbounded heuristic, because every ceiling is hard and declared:max_parallelism(tech/automation.md), the harness's ownmax_concurrent_threads_per_session, the mandatory stop predicate and the max-iterations backstop, plus bounded waits (AC10). Identical signal and identical reasoning to #250 and #219 — this story adds a realization of the same fan-out, not a second loop (that stays #250's, and the headless outer loop stays #451's, which is why #451 sits one class higher at orange).Confidence: high — classified from a fully declared scope against the current adoption files, with the harness capability probed rather than assumed. Deterministic on re-run against this body and these adoption inputs.
Subdomain and Context Analysis
Scoped to what this story touches (
/pair-capability-map-subdomains+/pair-capability-map-contexts, story scope — neverall). No catalog file was created or updated: every touched entry already exists with the same classification, so this run recorded a delta of zero.Subdomains touched
Bounded-context relationships (this story's)
spawn_agent/wait_agent,features.multi_agent*)enable_fanoutremoved)pair-loop/implement-batchlane definition (same repo)tech/automation.md, ADR-017, the new convention)AGENTS.md,.agents/skills/,integrations/codex.mdx)Coupling balance for the matrix: yellow — see the first row. This is the one place this story departs from #250/#451's "not assessed": those cards had no rated volatility on either side, whereas here the far side's churn is directly observed. It does not move the tier.
Ubiquitous-language terms this story introduces — to be registered in
product/subdomain/collaborative-workflow.context.mdat implementation time (this refinement writes to the PM tool only): harness realization, capability probe, context packet, role packet, result contract, terminal phase outcome.Acceptance Criteria
Functional Requirements
AC1 — The realization is probed, never assumed, and always announced
Given a Codex session
When
pair-loopstartsThen it resolves its fan-out realization by probing the tools actually exposed in that session (the v1 handles
spawn_agent/wait_agent/send_input/resume_agent/close_agent, or the v2 namespace whenmulti_agent_v2is enabled), and prints which realization won, which primitive it bound to, and why — before dispatching anythingAnd the probe never infers availability from the product name or a version string alone
And the tool names, namespaces and config keys it probes are held as data (one structure), never as conditionals scattered through the skill — a Codex rename must be a data edit
AC2 — No primitive ⇒ degrade in the declared order, never a silent in-context loop
Given the probe finds no usable fan-out primitive (feature off, surface without it, unknown namespace)
When the run continues
Then it degrades in preference order — (2) external driver (#451) when available, else (3) degraded one-card path: exactly one eligible card driven to its gate, audit + checkpoint written, stop, continue-token reported
And under no condition does the orchestrator iterate multiple cards inside one context (ADR-017 §3, architectural invariant)
And the degradation and its reason are recorded in the audit
AC3 — Every phase runs in a fresh subagent, fed one explicit context packet
Given an eligible card and a phase to run (implement / PR / review / fix)
When the phase is dispatched
Then it runs in a freshly spawned Codex subagent whose input is an explicit context packet: role instructions, the Pair skill to invoke, the card's
id/title/branch/base/worktree, any scope note, and the return schemaAnd the packet contains no other card's content and no material the role must not see
And the orchestrator retains only the compact schema-validated result — never the subagent's transcript
AC4 — Role instructions travel in the spawn request; profiles are optional
Given the adapter spawns a role
When it builds the request
Then role instructions and the skill reference are passed explicitly in that request
And Given no
.codex/agentsprofile and no[agents]config existThen the adapter works unchanged — it never depends on a named profile binding, on any Codex surface
AC5 — Reviewer blindness survives the port
Given the review phase
When its packet is built
Then the packet excludes
.pair/working/**— checkpoint, handoff and working logs — preserving the same blindness invariant the Claudepair-revieweragent carriesAnd a packet that would carry that material is rejected before spawn, not merely discouraged in prose
AC6 — One result contract for both harnesses, fail-closed
Given a dispatched phase completes
When the orchestrator reads its outcome
Then the return value is validated against the same JSON-Schema result contract the Claude workflow uses (ADR-016 / the workflow's step schemas), realized through Codex's structured-result mechanism — no Codex-specific format is invented
And Given the return is absent, unparseable, or schema-invalid
Then the phase counts as failed, the reason is recorded, and that card stops advancing — a missing result is never read as success
AC7 — Parallelism = min(dependency-allowed,
max_parallelism, harness ceiling)Given
max_parallelismfromtech/automation.mdand the harness's own concurrency ceiling (Codexmax_concurrent_threads_per_session)When the batch is composed
Then the adapter runs
min(dependency-allowed, max_parallelism, harness-ceiling)and prints the effective cap and which limit bound itAnd it never exceeds
max_parallelism, and it introduces no policy parameter of its own — eligibility, dependency analysis, mutex sets and the stop predicate all staypair-loop's (D18)AC8 — Audit and checkpoint on disk; the orchestrator never accumulates
Given any dispatch or outcome
When it happens
Then it is appended to the audit file under the working area (
working_pathfrompair.config.json, default.pair/working/), and per-card resumable state is written through/pair-capability-checkpointAnd the audit is never held in context, and
.pair/working/is never loaded as ambient context (D14)And Given the audit cannot be written
Then the unattended run fails loudly — an unaudited unattended run is not an acceptable degraded mode
AC9 — Resume re-dispatches only what is unfinished
Given a Codex run killed, cancelled or context-compacted mid-batch
When it is re-invoked on the same scope
Then it reconstructs state from the audit + checkpoints, re-dispatches only phases not recorded complete, and re-runs no completed card
And it opens no second PR for a story that already carries one — an in-flight story re-enters through the existing-PR path
AC10 — Cancellation, timeout, dead agent and partial batch each have a declared representation
Given a subagent that is cancelled, exceeds its wait timeout, dies, or never started
When the orchestrator collects results
Then each maps to a declared terminal phase outcome recorded per card (
cancelled/timed-out/died/not-started), distinct fromfailed-validationAnd every wait is bounded by a timeout — an unbounded wait is not permitted
And the surviving siblings' results are still collected and audited: a partial batch is reported, never discarded
And an unrecognisable outcome is fail-closed to failed (never assumed successful)
AC11 — Claude Code behaviour is unchanged
Given a Claude Code session
When
pair-looprunsThen it binds the existing workflow realization and behaves exactly as before
And this story requires no change to
.claude/workflows/*,.claude/agents/*or their dataset mirrors — a regression surface, not a refactor targetAC12 — The realization rule is recorded, once, consistently with #451
Given this story lands
When the decision is recorded
Then a record extends ADR-017 §4's Realization rule to name the Codex in-harness realization as a tier-1 fan-out realization, using the same three-tier ordering #451 records — (1) in-harness, (2) external driver, (3) degraded one-card + continue-token
And whichever of #441 / #451 lands second amends the existing record rather than restating a second, divergent framing
And ADR-017 §1 (
pair-nextfrozen atom), §2 (implement-batchas engine) and §6 (tech/automation.mdas policy home) are left intactAnd the ADL
2026-07-11-agent-execution-layer's statement that other assistants «do not have» subagent primitives is corrected for Codex, with the probe evidenceAC13 — Distribution is untouched
Given
pair install/pair updateWhen they run
Then skills continue to reach Codex through the existing
.agents/skills/symlink target plus the rootAGENTS.mdAnd this story adds no new distribution target and writes no
.codex/tree; the mirror guards stay greenAC14 — Every path is tested against mocked harness primitives
Given CI with no Codex binary
When the suite runs
Then conformance and smoke tests exercise, against mocked spawn/wait primitives: probe-hit (v1) · probe-hit (v2 namespace) · probe-miss → tier-2 → tier-3 · packet contains exactly one card · reviewer packet rejects a working-path entry · schema-valid return · absent/invalid return ⇒ failed · timeout · cancellation · dead agent · partial-batch collection · cap arithmetic including
1and0· resume skipping completed phasesBusiness Rules
pair-loop's. Grep-verifiable.Edge Cases and Error Handling
multi_agentdisabled, or a surface without the toolsmulti_agent_v2enabled with a customtool_namespacemax_wait_timeout_mstimed-outterminal outcome; siblings still collected (AC10)died; re-invocation resumes from audit + checkpoints (AC9, AC10)max_parallelismmax_parallelismmalformed (0, negative, unknown tier).pair/working/**into the review rolereview-dispatch-requiredsignal ADR-018 already definesDefinition of Done Checklist
Development Completion
pair-loop's Realization section gains the Codex in-harness branch, with the capability probe and the degradation cascade — one skill, two realizations, no forked process logicproduct/subdomain/collaborative-workflow.context.md2026-07-11-agent-execution-layer(AC12)apps/websiteintegrations/codex.mdx(the Codex realization, its limits, and what to enable) and thepair-loopdocs page's non-Claude noteQuality Assurance
Deployment and Release
pair install/pair updateunchanged (AC13)tech/automation.md, not re-decided hereStory Sizing and Sprint Readiness
Refined Story Points
Final Story Points: 8 (XL)
Confidence Level: Medium
Sizing Justification: five distinct deliverables — the capability probe + realization cascade (and the KB convention that generalizes it), the role-packet layer with its blindness rule, the result-contract wiring, the audit/checkpoint/resume path, and the terminal-outcome mapping for cancellation/timeout/death/partial batches — over a lane whose Claude realization is a 1451-line workflow. The draft's XL was tentative with the runtime contract unknown; the probe (Assumption 1) removes the unknown and confirms 8 at Medium. It is not larger than 8: no data migration, no infrastructure, no new contract format, and the per-card lane is consumed rather than rebuilt.
Sprint Capacity Validation
Sprint Fit Assessment: fits a sprint only once #250 is merged — this story realizes
pair-loop's lane in a second harness, and against an unmerged lane definition it will not.Total Effort Assessment: fits — Yes (conditional on the dependency above)
Story Splitting Recommendations
Not required, but available if #250 slips or the card proves too wide in flight:
Split rationale: A delivers the invariant (context isolation in Codex) without the concurrency surface; B adds throughput and the failure taxonomy. Neither half re-implements the other's logic.
Dependencies and Coordination
Story Dependencies
Prerequisite Stories:
pair-loop: hard prerequisite. This story adds a realization of its lane; it must be merged, not merely PR-ready, before implementation starts.implement-batch, merged — the phase sequence and its return contract), pair-capability-checkpoint — resumable state (write/resume, handoff pattern) #254 (/pair-capability-checkpoint), pair-capability-classify + tag projection + risk-matrix delta #233 (classification tags), pair-capability-publish-pr — gate + PR + tag propagation + git-workflow/conditional sections #255 (publish-pr), pair-process-implement composes checkpoint + publish-pr via subagent (resume from checkpoint) #256 (implement).Related, not blocking:
Dependent Stories: none declared. Epic Dependencies:
pair-next(#204), classification (#208),publish-pr(#206) — all shipped.Shared Components: the
pair-loopskill,tech/automation.md(read-only), the working-area convention, ADR-016's contract generator, the checkpoint template.External Dependencies
Third-party Integrations: the Codex CLI/app/IDE multi-agent tool surface — the one genuinely external dependency, and the reason AC1/AC2 are a probe rather than a version check. GitHub Projects and the code host are reached through the existing adapters; nothing new.
Infrastructure Requirements: none.
Validation and Testing Strategy
Acceptance Testing Approach
Testing Methods: mocked-primitive conformance tests for the full AC14 list (probe hit v1 / hit v2 / miss, packet isolation, blindness rejection, schema-valid and schema-invalid returns, timeout, cancellation, death, partial-batch collection, cap arithmetic, resume). Plus a control comparison: the same fixture card driven through the Claude workflow realization and through the Codex realization must produce the same per-card outcomes and an equivalent audit — a divergence localises the bug in the new realization, not in the lane.
Test Data Requirements: a fixture board with eligible/ineligible/untagged cards, a probe stub in each of its three states, and return-value fixtures for every terminal outcome.
Environment Requirements: CI needs no Codex binary (primitives are mocked). One manual dogfood run in a real Codex session over a narrow perimeter of this repo's backlog.
User Validation
Success Metrics: a Codex session advances a batch of eligible cards to their gates unattended, with an audit a human can read to reconstruct every dispatch, outcome and stop — and with the orchestrator's context flat across the batch.
Rollback Plan: the realization cascade is the rollback — disabling the probe (or the harness feature) drops the run to tier-2/tier-3 with no code change, and Claude Code is untouched throughout.
Notes and Additional Context
Refined 2026-08-22, unattended, following
/pair-process-refine-story. The draft carried seven open questions; all seven are resolved above and none required a product ruling:spawn_agent/wait_agentreliably? → Assumption 1 + Assumption 2: probed oncodex-cli 0.149.0; answered as a runtime probe, not a version matrix, because the surface is demonstrably in motion..pair/working/**is rejected before spawn..codex/agentsprofiles, or both? → Assumption 4 / AC4: explicit packets are normative, profiles an optional accelerator — the story's own Objective forbids depending on profile binding./pair-capability-checkpoint; everything else = the append-only audit file.Two factual corrections to the draft, both recorded rather than silently applied:
.codex/skillssymlink distribution mechanism» is not what Pair does — skills reach Codex via.agents/skills/+AGENTS.md(Assumption 5, AC13).2026-07-11-agent-execution-layer, which states that assistants such as Codex do not have subagent primitives. AC12 requires that correction to be recorded.Capability-probe evidence (2026-08-22,
codex-cli 0.149.0, local install):codex features listreportsmulti_agentstable/on,multi_agent_v2stable/off,enable_fanoutremoved; the binary carriescore/src/tools/handlers/multi_agents/{spawn,wait,send_input,resume_agent,close_agent}.rsandmulti_agents_v2/{spawn,wait,list_agents,send_message,followup_task,interrupt_agent}.rs, the config keysfeatures.multi_agent_v2.{max_concurrent_threads_per_session,min_wait_timeout_ms,max_wait_timeout_ms,default_wait_timeout_ms,tool_namespace,wait_agent_enabled,subagent_developer_instructions}andagents.max_concurrent_threads_per_session, the hookssubagent_start/subagent_stop, and canonical subagent task paths (/root/task1/task_3).codex execoffers--json(JSONL events),--output-schema <FILE>and-o/--output-last-message— the structured-result mechanism AC6 binds to. This is a snapshot of one version on one machine, which is precisely why AC1 mandates a probe.Documentation Links: ADR-017 §3/§4/§6 · ADR-016 · ADR-018 (nested-dispatch prohibition) · ADL
2026-07-11-agent-execution-layer· ADL2026-08-13-the-agent-execution-layer-ships· quality model §3.1/§3.2/§3.3/§5 (D10, D18, D21, D22) · working-area convention (D14) ·graceful-degradationandresolution-cascadeskill conventions · #250, #451, #450, #219.Technical Analysis
Implementation Approach
Design: not required — the runtime contract was validated during this refinement (Assumptions 1–2) and the remaining shape is fixed by ADR-017 §3/§4, ADR-016 and #451's three-tier ordering.
Technical Strategy: add a second realization to
pair-loop, not a second engine. The skill keeps one lane definition and one policy source; what varies is how a phase is dispatched. A small resolution cascade — probe the harness, bind the strongest available realization, announce it, degrade fail-closed — sits behind an anti-corruption layer that holds every harness-specific name as data. Each dispatch is one fresh subagent, one explicit packet in, one schema-validated value out; everything durable lands on disk.Key Components:
pair-loopskill/pair-capability-checkpointintegrations/codex.mdx+pair-loopdocsapps/websiteData Flow: policy (
tech/automation.md) →pair-loopselection + dependency analysis (unchanged, #250) → realization probe → per-card/per-phase context packet → Codexspawn_agent→ boundedwait_agent→ schema-validated result → audit + checkpoint → next decision. Only the compact result crosses back into the orchestrator.Integration Points: the Codex multi-agent tool surface; the Pair skills invoked inside each subagent (never modified);
tech/automation.md(read-only); the PM tool and code host through existing adapters; per-card worktrees (existing invariant).Technical Risks and Mitigation
enable_fanoutalready removedwait_agenton a dead subagenttimed-out/diedare declared outcomes; siblings still collectedmax_parallelism, the stop predicate, the iteration backstop — plus merge never automatic (BR6) and the fail-closed audit (AC8)Spike Requirements
Required Spikes: none. The spike the draft implied — validate the Codex orchestration primitives — was executed during this refinement; its evidence is in Notes and its volatility is carried as AC1's probe rather than as a research task.
Task Breakdown
pair-loop— probe, bind, announce, degrade fail-closedmin(dependency-allowed, max_parallelism, harness ceiling)Dependency Graph
AC Coverage
T-1: KB skill-convention — harness realization cascade and capability probing
Priority: P0 | Estimated Hours: 4h | Bounded Context: Knowledge Standards
Summary: Add the missing convention that tells any Pair skill how to discover what its harness can do and which realization to bind.
Type: Documentation / Standard
Description: The
skill-conventions/set covers resolution cascade, graceful degradation and template resolution, but nothing about harness capability. That gap is why the Codex realization would otherwise be hand-rolled inside one skill. This task writes the convention first so T-2/T-3 implement a stated rule rather than inventing one, and so the next harness reuses it.Acceptance Criteria:
.pair/knowledge/guidelines/technical-standards/ai-development/skill-conventions/, mirrored into the datasetgraceful-degradation.mdandresolution-cascade.mdrather than duplicating thempair-loopskillImplementation Approach:
.pair/knowledge/guidelines/technical-standards/ai-development/skill-conventions/harness-realization.md(+ dataset mirror),.../skill-conventions/README.mdindex rowDependencies:
Implementation Steps:
packages/knowledge-hub/dataset/Testing Strategy:
T-2: Harness surface map as data (anti-corruption layer)
Priority: P0 | Estimated Hours: 4h | Bounded Context: Development Collaboration
Summary: Hold every Codex-specific name — tool handles, namespaces, config keys — in one replaceable structure.
Type: Feature Implementation
Description: The far side of this integration is volatile by observation (v1 and v2 namespaces coexist, v2 is default-off,
enable_fanoutwas removed). BR4 requires that a rename be a data edit. This task creates the single structure the probe reads: for each known realization, the spawn/wait/cancel handles, the namespace, and the config keys that gate and bound them.Acceptance Criteria:
spawn_agent,wait_agent,send_input,resume_agent,close_agent) and the v2 namespace, plus the concurrency/timeout config keysImplementation Approach:
pair-loopskill's realization data (+ dataset mirror)Dependencies:
Implementation Steps:
Testing Strategy:
T-3: Realization resolution in
pair-loop— probe, bind, announce, degrade fail-closedPriority: P0 | Estimated Hours: 8h | Bounded Context: Development Collaboration
Summary: The cascade itself: probe the session, bind the strongest available realization, print what won and why, degrade tier-2 → tier-3 when nothing binds.
Type: Feature Implementation
Description: Adds a second realization to
pair-loop, not a second engine — one lane definition, one policy source, a varying dispatch mechanism. Availability comes only from the runtime probe, never from a product name or version. Under a probe miss the run degrades to #451's external driver when available, else to the degraded one-card path with a continue-token; it never iterates multiple cards in one context.Acceptance Criteria:
pair-loopskillImplementation Approach:
pair-loopskill (+ dataset mirror)Dependencies:
Implementation Steps:
Testing Strategy:
T-4: Context packets and role packets for the four phases
Priority: P0 | Estimated Hours: 8h | Bounded Context: Development Collaboration
Summary: Build the explicit packet each freshly spawned subagent receives — one card, one role, one skill, one return schema.
Type: Feature Implementation
Description: Each phase (implement / PR / review / fix) is dispatched into a fresh subagent whose entire input is an explicit packet: role instructions, the Pair skill to invoke, the card's
id/title/branch/base/worktree, any scope note, and the return schema. Role instructions travel in the request:.codex/agentsprofiles may exist but the adapter must run with none present. The packet must carry no other card's content, and the orchestrator retains only the compact result.Acceptance Criteria:
.claude/agents/*role definitions without forking their contentImplementation Approach:
pair-loopskill's Codex realization (+ dataset mirror)Dependencies:
.claude/agents/*.mdrole definitions (read, not modified)Implementation Steps:
Testing Strategy:
T-5: Reviewer blindness enforced as a pre-spawn rejection
Priority: P0 | Estimated Hours: 3h | Bounded Context: Development Collaboration
Summary: Make the independent review's blindness a mechanical check, not a sentence in a prompt.
Type: Feature Implementation
Description: The Claude
pair-reviewercarries "you MUST NOT read anything under.pair/working/". Porting that as prose would lose it silently. The review packet is allow-listed, and a packet that would carry checkpoint/handoff/working-log material is rejected before the spawn call, with the offending entry named.Acceptance Criteria:
Implementation Approach:
.pair/working/never ambient); the reviewer blindness invariantDependencies:
Implementation Steps:
Testing Strategy:
T-6: Result-contract binding to the ADR-016 schemas, fail-closed on invalid returns
Priority: P0 | Estimated Hours: 6h | Bounded Context: Development Collaboration
Summary: One contract for both harnesses — reuse the JSON-Schema return contract Pair already generates.
Type: Feature Implementation
Description: The machine-readable handoff contract is not invented here: it is ADR-016's
*.contract.jsonplus the workflow's step schemas. Codex realizes it through its structured-result mechanism. An absent, unparseable or schema-invalid return counts as a failed phase, is recorded, and stops that card advancing — a missing result is never read as success.Acceptance Criteria:
Implementation Approach:
ensure-contract.mjscache-by-hashDependencies:
Implementation Steps:
Testing Strategy:
T-7: Bounded parallelism —
min(dependency-allowed, max_parallelism, harness ceiling)Priority: P1 | Estimated Hours: 4h | Bounded Context: Development Collaboration
Summary: Three ceilings, one effective cap, and always say which one bound it.
Type: Feature Implementation
Description:
max_parallelismcomes fromtech/automation.md; the harness contributes its own concurrency ceiling (max_concurrent_threads_per_session); the dependency/mutex analysis stays #250's. The adapter takes the minimum, prints the effective cap and the binding limit, and introduces no policy parameter of its own.Acceptance Criteria:
max_parallelism; grep-verifiably owns no policy knobpair-loop's existing resolution, not a second reader1and0(T-11)Implementation Approach:
Dependencies:
Implementation Steps:
0as a no-dispatch outcomeTesting Strategy:
0T-8: Audit and checkpoint persistence, fail-loud when the audit is unwritable
Priority: P0 | Estimated Hours: 5h | Bounded Context: Development Collaboration
Summary: Everything durable goes to disk; the orchestrator keeps nothing but compact results.
Type: Feature Implementation
Description: Every dispatch and outcome is appended to the audit under the working area (
working_pathfrompair.config.json, default.pair/working/); per-card resumable state is written through/pair-capability-checkpoint..pair/working/is never loaded as ambient context. If the audit cannot be written, the unattended run fails loudly — an unaudited unattended run is not an acceptable degraded mode.Acceptance Criteria:
/pair-capability-checkpointand the working-area convention — no third storeImplementation Approach:
Dependencies:
/pair-capability-checkpoint(pair-capability-checkpoint — resumable state (write/resume, handoff pattern) #254, shipped)Implementation Steps:
Testing Strategy:
T-9: Terminal phase outcomes, bounded waits and partial-batch collection
Priority: P0 | Estimated Hours: 6h | Bounded Context: Development Collaboration
Summary: Name every way a subagent can end, bound every wait, and never throw away the siblings.
Type: Feature Implementation
Description: Cancellation, timeout, death and never-started each map to a declared terminal outcome recorded per card, distinct from
failed-validation. Every wait is bounded by a timeout — an unbounded wait would hang an unattended run forever. When one subagent ends badly, the surviving siblings' results are still collected and audited: the partial batch is reported, never discarded. Anything unrecognisable is fail-closed to failed.Acceptance Criteria:
pair-loop's existing per-card decision, adding no new decisionImplementation Approach:
Dependencies:
Implementation Steps:
Testing Strategy:
T-10: Resume — reconstruct from audit + checkpoints, re-dispatch only what is unfinished
Priority: P1 | Estimated Hours: 6h | Bounded Context: Development Collaboration
Summary: A killed or compacted Codex session picks up where it stopped, without redoing work or opening a second PR.
Type: Feature Implementation
Description: On re-invocation over the same scope, state is reconstructed from the audit and the checkpoints; only phases not recorded complete are re-dispatched. A story already carrying a PR re-enters through the existing-PR path — never a second PR.
Acceptance Criteria:
Implementation Approach:
Dependencies:
Implementation Steps:
Testing Strategy:
T-11: Conformance and smoke suite against mocked harness primitives
Priority: P0 | Estimated Hours: 8h | Bounded Context: Development Collaboration
Summary: Prove every path without a Codex binary in CI.
Type: Testing
Description: Implements AC14's full list against mocked spawn/wait primitives: probe-hit (v1), probe-hit (v2 namespace), probe-miss → tier-2 → tier-3, single-card packet isolation, reviewer-packet rejection, schema-valid return, absent/invalid return ⇒ failed, timeout, cancellation, dead agent, partial-batch collection, cap arithmetic including
1and0, and resume skipping completed phases. Also carries the control comparison: the same fixture card through the Claude realization and through the Codex realization must yield the same per-card outcomes and an equivalent audit.Acceptance Criteria:
Implementation Approach:
Dependencies:
Implementation Steps:
Testing Strategy:
T-12: Claude-path regression and distribution guard
Priority: P0 | Estimated Hours: 3h | Bounded Context: Integration & Process Standardization
Summary: Prove that nothing the Claude path or the installer already does has moved.
Type: Testing / Configuration
Description: AC11 and AC13 are guarantees of absence, so they need assertions: no change to
.claude/workflows/*,.claude/agents/*or their dataset mirrors; no.codex/tree written; skills still reach Codex through the.agents/skills/symlink target plus the rootAGENTS.md; mirror guards green.Acceptance Criteria:
.codex/targetmirror-guard/workflow-mirrorImplementation Approach:
packages/knowledge-hubapps/pair-cli/config.jsonregistriesDependencies:
Implementation Steps:
Testing Strategy:
T-13: Decision record extending ADR-017 §4 and correcting the agent-execution-layer ADL
Priority: P0 | Estimated Hours: 3h | Bounded Context: Knowledge Standards
Summary: Record the Codex in-harness realization once, in the same three tiers #451 records.
Type: Documentation / Decision
Description: Extends ADR-017 §4's Realization rule to name Codex subagents as a tier-1 fan-out realization, using the ordering #451 records — (1) in-harness, (2) external driver, (3) degraded one-card + continue-token. Whichever of #441/#451 lands second amends that record rather than writing a divergent second framing. §1, §2 and §6 stay intact. It also corrects the ADL
2026-07-11-agent-execution-layer, which states that assistants such as Codex do not have subagent primitives, citing the probe evidence in this story's Notes.Acceptance Criteria:
/pair-capability-record-decision) plus the ADL correctionImplementation Approach:
.pair/adoption/tech/adr/or.pair/adoption/decision-log/; the ADL fileDependencies:
Implementation Steps:
Testing Strategy:
T-14: Documentation and ubiquitous-language registration
Priority: P1 | Estimated Hours: 4h | Bounded Context: Knowledge Standards
Summary: Tell a Codex user what they now get, what to enable, and where it stops.
Type: Documentation
Description: Updates
apps/websiteintegrations/codex.mdxwith the Codex realization, what must be enabled for it (the multi-agent feature and its concurrency/timeout knobs), and the degradation behaviour when it is not; adds the non-Claude note to thepair-loopdocs page. Registers the terms this story introduces — harness realization, capability probe, context packet, role packet, result contract, terminal phase outcome — inproduct/subdomain/collaborative-workflow.context.md.Acceptance Criteria:
integrations/codex.mdxandpair-loopdocs page; six terms registeredImplementation Approach:
apps/website/content/docs/integrations/codex.mdx, thepair-loopdocs page,.pair/adoption/product/subdomain/collaborative-workflow.context.mdDependencies:
Implementation Steps:
pair-loopnon-Claude noteTesting Strategy: