Skip to content

feat: harden native subagent dispatch to standardization-ready — follow-up to #355 - #356

Merged
sergstack merged 2 commits into
mainfrom
codex/native-subagent-dispatch-standardization
Sep 2, 2026
Merged

feat: harden native subagent dispatch to standardization-ready — follow-up to #355#356
sergstack merged 2 commits into
mainfrom
codex/native-subagent-dispatch-standardization

Conversation

@sergstack

Copy link
Copy Markdown
Owner

Summary

Follow-up to #355 (merged as 0a7ee18). No new functionality — this closes the two standardization blockers from #355's residual risks using native runtime enforcement, not prompt text, runs 3 more observed multi-owner executions, adds measurement evidence, and delivers a decision memo.

Verdict: STANDARDIZE BOUNDED — conditional on a 4-item punch-list; else KEEP PILOT-ONLY until it lands. Full memo: docs/evidence/NATIVE_SUBAGENT_DISPATCH_STANDARDIZATION_2026-09-02.md.

Native enforcement audit (before any change)

Blocker Finding
Deterministic isolated workspace Agent isolation: "worktree" is real — probe child landed in a clean, locked .claude/worktrees/agent-<id> at a deterministic revision, not the stale parent branch.
Structural no-nesting on write path A general-purpose child can nest (probe spawned a sub-agent, got "PONG"). Only prompt text stopped it → insufficient. Plan/Explore are All tools except Agent, …, Write, Edit → structurally cannot nest or write. No built-in has Write-without-Agent; a tracked custom type is out of scope (.claude/agents gitignored).

→ Blocker 1 closable natively. Blocker 2 closable only by removing the write-capable child.

Changes (minimal)

  • PROJECT_CAPABILITIES.yaml — every executor: agent_type: Plan (was general-purpose for codex), write_capable: false (all), new workspace: isolated_worktree (all).
  • ai-os-orchestrator/SKILL.md — two structural invariants (no Agent tool ⇒ no nesting; no Write ⇒ patch-return + root applies); mandatory isolation: "worktree"; fail-closed spawn wording (omitting isolation/agent_type is an AES contract defect, not a silent default; a dispatch with no recorded workspace observation is not counted as executed).
  • AGENT_LOOP_PLAYBOOK.md bounds updated (hub-and-spoke "structurally enforced"; mandatory isolation; root is the only writer) + regenerated AIOS_05 bundle + provenance.
  • tests/test_project_capabilities_executor.py — DEF-001 regression (workspace == isolated_worktree for all), non-nesting (agent_type ∈ {Plan, Explore}), no write-capable child.
  • Pointers: docs/evidence/README.md, MASTER_STATUS.md, CURRENT_STATUS.md.

Not touched: AES record schema, ROUTING_RULES.md, registry role, root-only routing, authority/merge/deploy/production gates, child→child ban, framework ban. No runtime/DB/service, no .gitignore change.

3 more observed executions (all dispatched with isolation: "worktree")

Pilot What it proves Result
D — runtime failure Plan child asked to write → structurally cannot, refused honestly, no sed -i workaround → DEF-P D-001 → bounded recovery (patch-return, root applied + validated), no silent retry failure enters AES defect lifecycle
E — write path Plan child in isolated worktree returned a clean 2-file unified diff + validation commands; root applied + validated. subagent_tokens 41558, tool_uses 11, 93 s patch-return + return-to-root works
F — long multi-hop ai_os → thinking → analytics → codex → ai_os, 5 owner transitions, one execution_id exec-pilotF-2026-09-02, root-only routing, guard-gated repeat visit, Closure Review vs original goal. Pilot's own multi-owner verdict: STANDARDIZE BOUNDED WITH CONDITIONS routing integrity holds at length

Measurement evidence (nothing invented; gaps marked)

16 total dispatches (7 hardened) · isolated-worktree landing 7/7 = 100% · structural write refusal 1/1 · nested spawn by non-nesting executor 0 · silent retries 0 · 3 defects all handled · latency captured n=2 (93 s / 149 s) · telemetry captured opportunistically only · no cost owner assigned.

Risks

Closed (native, not prompt): DEF-001 workspace isolation; structural no-nesting (write-capable child removed).

Remaining: R1 unverified live spawn args — irreducible for a prompt-level controller; mitigated by fail-closed wording + registry-owned values, carried as a reviewed risk. R2 thin measurement base. R3 worktree isolates VCS state only. R7 no cost owner.

Strict STANDARDIZE BOUNDED criteria: 9/10 met (…#4 borderline)

deterministic isolated workspace ✔ · write child can't nest ✔ (removed) · runtime failure → AES defect ✔ · ≥6 executions ✔ (borderline — 6 reached bounded terminal state) · long multi-hop keeps one execution_id + provenance ✔ · root-only router ✔ · Closure Review on original goal ✔ · no silent retry / no inflation ✔ · bounded rollback ✔ · no authority expansion ✔

Punch-list for an unconditional STANDARDIZE BOUNDED

  1. Deterministic evidence linter (schemas/subagent_dispatch_evidence.schema.json + scripts/check_subagent_dispatch_evidence.py, wired to docs-safety.yml) — every recorded dispatch row carries agent_type + isolation: "worktree" + workspace observation. (Does not touch the AES schema.)
  2. Mandatory per-dispatch telemetry fields in that schema.
  3. ≥15 hardened dispatches across ≥3 owners incl. ≥1 deliberate failure, then calibrate the four continuation.guards thresholds.
  4. Named owner for the dispatch cost/latency budget.

Rollback

git revert the 2 commits. executor block is inert once the SKILL subsection reverts. No schema migration, no runtime store, no .gitignore change.

AES statuses

execution_state: completed · overall_delivery: pass · qa_status: pass (6 checks + provenance + pytest 216) · judge_verdict: not_run · authority_status: owner_review_pending · merge_status: open · production_status: not_applicable

🤖 Generated with Claude Code

Claude Code and others added 2 commits September 2, 2026 11:40
…structural no-nesting

Closes the two standardization blockers from PR #355's DEF-001 / residual risks,
using native runtime enforcement, not prompt text.

Blocker 1 (DEF-001, workspace isolation):
- Every executor gains workspace: isolated_worktree. The orchestrator MUST
  dispatch with Agent isolation: "worktree" — child works in a clean, locked
  git worktree at a deterministic revision, never the parent working tree.
  Audit: observed a probe child land in .claude/worktrees/agent-<id> on a clean
  HEAD, isolated from the parent branch.

Blocker 2 (structural no-nesting on the write path):
- Audit finding: a general-purpose child CAN spawn a nested sub-agent (probe
  did it). Plan/Explore agent types are defined "All tools except Agent, ...
  Write, Edit" -> structurally cannot nest and cannot write.
- Every executor.agent_type is now Plan (was general-purpose for codex).
  write_capable is false for all. An implementation slice returns a patch +
  validation commands; the ROOT applies and validates. Root is the only agent
  holding Agent + Write.

Files: PROJECT_CAPABILITIES.yaml (executor: +workspace, agent_type Plan,
write_capable false), ai-os-orchestrator/SKILL.md (structural invariants +
mandatory isolation + patch-return flow + bounded failure recovery),
AGENT_LOOP_PLAYBOOK.md bounds (+ regenerated AIOS_05 + provenance),
tests/test_project_capabilities_executor.py (DEF-001 regression + non-nesting
+ no-write-capable-child invariants).

Checks: 6 canonical scripts + provenance audit + pytest (216) pass.
Additional observed executions + measurement evidence + decision memo follow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rdict STANDARDIZE BOUNDED (conditional)

Native enforcement audit + Pilots D/E/F on the hardened dispatch:

- Pilot D (runtime failure): Plan child structurally lacks Write -> honest
  failure, no workaround -> DEF-P D-001 -> bounded recovery (patch-return,
  root applied + validated), no silent retry.
- Pilot E (write path): Plan child in isolated worktree returned a clean
  2-file unified diff + validation commands; root applied and validated.
  subagent_tokens 41558, tool_uses 11, duration 93s.
- Pilot F (long multi-hop): 5 owner transitions
  ai_os->thinking->analytics->codex->ai_os, one execution_id
  exec-pilotF-2026-09-02, root-only routing, guard-gated repeat, Closure
  Review. Pilot's own multi-owner verdict: STANDARDIZE BOUNDED WITH
  CONDITIONS.

Measurement evidence: 16 total dispatches (7 hardened), isolated-worktree
landing 7/7=100%, structural write refusal 1/1, nested spawn by non-nesting
executor 0, silent retries 0, latency captured n=2 (93s/149s), telemetry
captured opportunistically only.

Risks CLOSED (native, not prompt): DEF-001 workspace isolation (mandatory
isolation:"worktree", registry + test + fail-closed SKILL wording);
structural no-nesting (all executors Plan = no Agent tool; write-capable
child removed; patch-return + root applies).

Risks REMAINING: R1 unverified live spawn args (irreducible for a
prompt-level controller); R2 thin measurement base; R3 worktree isolates
VCS state only; R7 no cost owner.

SKILL.md gains fail-closed spawn wording. No AES-record schema change, no
ROUTING_RULES/registry-role/authority/gate change, no framework.

Recommendation: STANDARDIZE BOUNDED (conditional) on a 4-item punch-list
(fail-closed evidence linter, telemetry schema, >=15-dispatch sample, cost
owner); else KEEP PILOT-ONLY until it lands.

Checks: 6 canonical scripts + provenance audit + pytest (216) pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs-human-review Human review is required before merge label Sep 2, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Merge gate: protected paths changed. Auto-merge is disabled and owner review is required via CODEOWNERS.

@sergstack
sergstack merged commit 5a115c9 into main Sep 2, 2026
2 checks passed
@sergstack
sergstack deleted the codex/native-subagent-dispatch-standardization branch September 2, 2026 08:52
sergstack added a commit that referenced this pull request Sep 2, 2026
…ollow-up to #356 (#358)

* feat(orchestrator): commissioning — dispatch-evidence linter, telemetry contract, 16-dispatch sample, cost owner

Closes the STANDARDIZE BOUNDED commissioning punch-list (4/4). No architecture
change: no new router/state-machine/DB/framework, no AES-schema or
ROUTING_RULES change, no authority expansion, no child->child, no
write-capable children.

1. schemas/subagent_dispatch_evidence.schema.json + scripts/check_subagent_dispatch_evidence.py
   (wired into .github/workflows/docs-safety.yml as a blocking step) +
   tests/test_subagent_dispatch_evidence.py (13 tests). Every executed
   dispatch must have a machine-checkable record: agent_type (Plan/Explore
   only), isolation "worktree", workspace observation under
   .claude/worktrees/agent-, execution/owner linkage, cross-checked against
   PROJECT_CAPABILITIES.yaml.
2. Telemetry contract: duration_ms / subagent_tokens / tool_uses are required
   keys, each a number or the literal "not_captured" (never omitted or
   invented). Runtime emitted no usage footer for the 16 commissioning
   dispatches -> all recorded not_captured, honestly.
3. docs/evidence/subagent_dispatch_records_2026-09-02.json — 16 hardened
   dispatches across all 7 owner capabilities:
   - exec-commissionG: 6-hop long multi-hop + repeat-route guard (bare repeat
     refused; repeat with evidence_delta allowed);
   - exec-commissionH: 2 deliberate failures (capability-missing write;
     missing-input read) both honest + registered (DEF-commissionH-001/002),
     no silent retry, plus a patch-return recovery the root applied;
   - exec-commissionI: per-capability coverage;
   - C-PROBE-1: runtime re-verification (Plan has no Agent/Write;
     isolation:"worktree" = clean linked worktree; unchanged).
   Guard-calibration population recorded with a proposal; the four
   continuation.guards remain unset (schema unchanged).
4. Cost/latency owner = [AI OS], with a "when dispatch is justified vs inline
   root" rule, recorded in AGENT_LOOP_PLAYBOOK.md.

QA: exact no-nesting wording ("a Plan child cannot use the native Agent tool";
Bash-mediated external spawn = residual risk pending separate evidence);
runtime-upgrade re-verify bound added.

Status stays STANDARDIZE BOUNDED (conditional) pending the Judge review;
flips to STANDARDIZED BOUNDED only on judge_verdict: pass.

Checks: 6 canonical + provenance + dispatch-evidence linter + pytest (229) pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(commissioning): apply code-review findings to the dispatch-evidence linter

- acceptance gate now applies by filename match (subagent_dispatch_records*.json),
  not only when generated_for contains "commissioning" — a rename can no longer
  silently drop the >=15/owner/scenario enforcement;
- registry_executors() tolerates a capability with no executor block (degrades
  to {} -> clean FAIL message instead of an uncaught KeyError);
- missing jsonschema now raises a distinct EnvironmentError_ -> exit code 2
  ("CANNOT CHECK"), not a spurious validation FAIL (exit 1);
- check_file returns (problems, doc); main() no longer re-parses the file;
- tests: drop unused `import copy`; drop an absolute local path from a fixture;
  add coverage for the filename-based gate and the (problems, doc) return.

Checks: 6 canonical + provenance + dispatch-evidence linter + pytest (231) pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(commissioning): Judge round-1 revise — correct defect-status language

Judge (adversarial) round 1 returned `revise`: the memo/status claimed the two
deliberate-failure defects were "registered / in the AES defect lifecycle" with
no FAILURE_REGISTRY.md entry or AES record — status inflation — and def-/DEF-
casing did not match `defect_ref`.

- IDs normalised to def-commission-h-001 / def-commission-h-002 in the JSON
  defect_ref, JSON notes, and the memo;
- language corrected: the two induced failures are recorded in
  subagent_dispatch_records_2026-09-02.json (outcome:refused + defect_ref) as
  deliberate probes of the failure-handling path, NOT FAILURE_REGISTRY.md
  regression cases; FAILURE_REGISTRY.md and the AES schema stay unchanged;
- acceptance table row 3 reworded to match.

Round-2 Judge re-review result folded in next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat(orchestrator): Judge round-2 pass — flip native subagent dispatch to STANDARDIZED BOUNDED

Judge re-review (exec-commissionj J2, adversarial, Plan child in isolated
worktree, telemetry captured 41801 tok / 15 tools / 84062 ms): round-1 gap
closed, no regression on criteria 1/2/4/5/6. Verdict: pass.

- dispatch records: +J1 (round-1 revise) +J2 (round-2 pass) = 18 hardened
  dispatches across 7 owners; J2 is the first commissioning-campaign dispatch
  with a captured Agent usage footer;
- memo: Judge section filled from the observed result; counts 16 -> 18;
  telemetry capture status 1/18; §1 detail updated for the review fixes;
- status flipped conditional -> STANDARDIZED BOUNDED in CURRENT_STATUS.md,
  MASTER_STATUS.md, docs/evidence/README.md, and AGENT_LOOP_PLAYBOOK.md
  (bounded, pilot-scoped; NOT a default/unrestricted standard; general
  promotion + guard thresholds remain separate owner decisions).

judge_verdict: pass. No AES-schema / ROUTING_RULES / authority change; no
child->child; no write-capable children; rollback still a scoped git revert.

Checks: 6 canonical + provenance + dispatch-evidence linter + pytest (231) pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs(commissioning): sync linter docstring with the filename-based acceptance gate

Follow-up to the code-review fix in e2ba3c2: the module docstring still said the
acceptance gate keys only on `generated_for` containing "commissioning". It now
also applies by the `subagent_dispatch_records*.json` filename
(`acceptance_gate_applies`). Docstring only; no behaviour change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <sstegancev@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human-review Human review is required before merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant