Skip to content

feat(#6970)!: support native Codex review and retro children - #7809

Closed
dhshah13 wants to merge 2 commits into
fullsend-ai:mainfrom
dhshah13:codex/6970-native-subagents
Closed

dhshah13 wants to merge 2 commits into
fullsend-ai:mainfrom
dhshah13:codex/6970-native-subagents

Conversation

@dhshah13

@dhshah13 dhshah13 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fleet review and retro can now run on the Codex runtime with native child agents (spawn_agent / wait_agent / close_agent, collaboration V1) instead of doing every sub-agent's work in one context. Paired with the instruction changes in fullsend-ai/agents#1542.

Related Issue

Addresses #6970. Companion: fullsend-ai/agents#1542 (for fullsend-ai/agents#1500).

Changes

  • Roles. Bootstrap turns skill personas into Codex role files, plus default and explore, only for agents whose tools allow Agent (same rule as Claude Code and pi). Triage and prioritize keep the no-sub-agent path.
  • Models. The runner picks child models: persona mapping → FULLSEND_CODEX_SUBAGENT_MODEL → subagents.default → gpt-5.6-luna. Claude aliases, inherit and other providers are rejected.
  • Dispatch guard. A mandatory PreToolUse hook requires fresh context and depth one, and rejects model/effort overrides, resume and V2 dispatch. Codex caps open children at four.
  • Write protection. Codex starts under an inherited Landlock restriction, so the agent can't rewrite role files, config or hooks that Codex re-reads when each child starts.
  • Evidence. Child rollouts become separate redacted transcripts. Usage is reconciled per thread. Unclosed children or missing usage/transcripts fail the run (ErrIncompleteEvidence) without spending another retry.
  • Cost. Codex reports cost_unavailable instead of $0.
  • ADRs. Adds ADR 0126. Accepted ADR 0104 gains a two-line cross-reference only.

Moved out of this PR (they changed security behavior for every runtime and caused fleet regressions): the Codex security collector, the on_critical: halt gate and the shared tirith_check.py severity change. They are rebuilt with fixes on a follow-up branch for a separate issue. The Codex canary quoting bug on main has its own PR.

BREAKING CHANGE:

  • Codex runs need Linux Landlock ABI ≥ 3 (kernel 6.2+), including runs without children.
  • During a Codex run, new top-level $HOME entries other than .cache, .cargo, .config/git, .local and .npm can't be created.
  • Skill sub-agent personas need a description.
  • Metrics consumers must handle cost_unavailable.

Testing

  • make lint passes
  • Tests added/updated for new or modified logic

Unit tests pass for runtime, CLI, security and harness. The Landlock and dispatch-guard tests pass in both Linux sandbox images. About 99% of added Go lines are covered locally.

Live, local, gpt-5.6-luna at medium effort, Codex CLI 0.157.0 (pinned) and 0.158.0 (latest, #7747):

Run 0.157.0 0.158.0
Codex only: review ✅ 8 children, 97 responses ✅ 8 children, 106 responses
Codex only: retro ✅ 4 children, 52 responses ✅ 4 children, 59 responses
Fullsend: review ✅ 8 children, 98 responses ✅ 8 children, 106 responses
Fullsend: retro ✅ 4 children, 49 responses ✅ 4 children, 63 responses
Fullsend: triage ✅ no children ✅ no children
Fullsend: code ✅ fix committed; Go module download, npm install, tests pass ✅ same
Fullsend: fix ✅ fix committed; go test passes ✅ same

Review/retro audits check the selected roster, fresh context, at most 4 open children, every child completed and closed before output, one transcript per thread, reconciled usage and schema validation.

Security hooks cost turns but never stopped a run. Every code/fix run lost one command to CANARY_LEAKED, caused by the canary quoting bug on main; with that fix applied the same code run had none. Tirith analysis_incomplete also blocked 1–3 nested shell commands per code/fix run, which is existing hook behavior.

Review/retro use synthetic forge fixtures. Triage/code/fix ran against a disposable private repo with post-scripts off. Not covered: live forge posting, CI Workload Identity, GitLab runner kernels, V2 parents and mixed-model rosters.

Checklist

  • PR title follows Conventional Commits (correct type, ! for breaking changes)
  • Commits are signed off (DCO) — human and human-directed agent sessions only
  • I wrote this contribution myself and can explain all changes in it

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

E2E tests did not run

E2E tests run automatically for org/repo members and collaborators on pull requests.

For other contributors, a maintainer with write access must add the ok-to-test label after the latest push.

See E2E testing guide for details.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:38 PM UTC · Completed 8:06 PM UTC

Commit: 5d081a0 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5 · Effort: high · Cost: $17.76

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Site preview

Preview: https://dbea793b-site.fullsend-ai.workers.dev

Commit: 4b6c863c47bbec5218fa0db3ce9c8d15a0b3f3a6

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.60000% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/runtime/codex_personas.go 98.19% 1 Missing and 1 partial ⚠️
internal/runtime/codex_transcript.go 99.50% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@dhshah13 dhshah13 added the ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) label Sep 28, 2026
@github-actions github-actions Bot removed the ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) label Sep 28, 2026
@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Sep 28, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Large change size and elevated git churn across touched runtime files are counterbalanced by an established contributor, zero protected or dependency modifications, a solid 0.31 test ratio, and close alignment with tracked feature issue #6970, preserving the moderate (2) risk rating anchored to the prior review.

Previous run

Risk Assessment: moderate (2/5)

Details

Large change size and high git churn across touched runtime files are balanced by an established author, zero protected or dependency file modifications, strong test ratio, and close alignment with the tracked feature issue; unchanged from the prior review (score 2) since Tier 1 signals (author, protected/dependency-path counts, issue alignment) are the same.

Previous run (2)

Risk Assessment: moderate (2/5)

Details

Large change size and high git churn across runtime files are balanced by an established author, zero protected or dependency file modifications, solid test coverage, and close alignment with the tracked feature issue.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review

Findings

Low

  • [logic-error] internal/runtime/codex_run.go:514 — The agents_states switch has no default case, so an unlisted status leaves the prior children[id] value unchanged. Known agent-thread values handled are completed/pending_init/running/interrupted/errored/not_found. The parent prompt (codexSubagentNote, codex_personas.go:208) also names shutdown as a finished-child status alongside completed/errored/not_found, and that token is absent from the switch. A child that never reports completed stays false from spawn and still fails the run (fail-closed), so most unknown-status paths are safe. The residual gap: spawn + wait(completed, nonempty message) + a later unlisted status (e.g. shutdown) does not retract completeness, so close_agent can still record closure for a child whose actual terminal state was never recognized.
    Remediation: Add a default case that sets children[id]=false for unknown agents_states.status values, or explicitly handle "shutdown" alongside the other incomplete terminals. Add a regression test covering spawn + completed nonempty wait + a later shutdown (or other unlisted) status.

  • [fail-open] internal/runtime/codex_personas.go:22 — codexDispatchMatcher is a closed-world exact alternation: spawn_agent|resume_agent|multi_agent_v1resume_agent|collaborationspawn_agent. The comment documents Codex's flat_tool_name convention (namespace+name, no separator): V1 resume is multi_agent_v1resume_agent, V2 spawn is collaborationspawn_agent — but the equivalent V2 resume form, collaborationresume_agent, is absent from the matcher. Tests pin the observed names on CLI 0.157.0/0.158.0 and do not include this form, so it is unobserved on the pinned CLIs rather than a demonstrated live gap. If a V2 parent ever emitted that concatenated resume name, Codex's PreToolUse hook lookup would not match it and the mandatory dispatch guard would never run for it (fail-open at the hook-dispatch layer), though native max_depth=1 and other guards remain as residual protection.
    Remediation: Add collaborationresume_agent to codexDispatchMatcher if the pinned CLI can emit it, or document explicitly that V2 has no resume tool on 0.157/0.158. Extend the dispatch-policy test's observed-name list accordingly.

All four high/medium findings from the prior review of an earlier commit in this PR were not observed in the current diff: internal/cli/run_security.go's fail-open iteration-boundary halt, internal/runtime/codex_hook/fullsend-codex-dispatch.py's non-blocking uncaught-exception path, internal/cli/run.go's scanOutputFiles "security"-directory skip tied to that halt, and docs/cli/agent.md's stale --subagent resolution note are not present in this diff's file list or have been superseded — the PR body states the security collector, on_critical: halt gate, and shared tirith_check.py severity change were deliberately moved to a follow-up branch, and this was confirmed against the current 62-file change list (neither internal/cli/run_security.go nor internal/security/hooks/tirith_check.py appear in it, and internal/cli/run_security.go does not exist on the base branch either). The prior low-severity codex_run.go logic-error finding is re-affirmed above at reduced confidence (downgraded from medium to low after re-verification found the fail-closed default path is broader than originally described) with a new companion codex_personas.go dispatch-matcher gap identified in this review round. The docs/ADRs/0126 Landlock-ABI-floor documentation gap raised in the prior review was checked against the current diff and found already covered by existing troubleshooting text in docs/runtimes/codex.md.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run

Review

Findings

Medium

  • [logic-error] internal/runtime/codex_run.go:518 — Native child completeness updates from agents_states only for the enumerated statuses completed/pending_init/running/interrupted/errored/not_found. Any other status (including the tool-level failed status that this same stream uses for spawn_agent item.Status) is ignored, so a child already marked complete stays complete. close_agent then records closure from that stale true, and collectCodexUsage will not emit "did not deliver a completed result". A later wait/close that reports an unrecognized terminal state can therefore certify a finished child incorrectly.
    Remediation: Treat unknown agents_states.status values as incomplete (default children[id]=false) rather than leaving the prior value unchanged. Add a regression test where spawn + wait(completed, message) + a later wait/close reporting status=failed or an unlisted status fails the run and does not set closedChildren.

Low

  • [scope-creep] internal/security/hooks/tirith_check.py:87 — check_command's return signature now reports maximum blocking severity, and main() emits metadata.severity on Tirith blocks. This file is shared by every sandboxed runtime; Claude/pi block-vs-allow decisions are unchanged, and the added value is consumed only by the Codex adapter. This file is unchanged since the prior review (not touched by the latest 3 commits) — severity anchored from the prior review. The PR body now explicitly acknowledges this as "a deliberate dependency of the requested security follow-up... visible for maintainer review", which mitigates but does not eliminate the shared-hook scope question.
    Remediation: Consider moving Tirith severity tracking into an independent hook-fix PR, or explicitly broaden the linked issue (codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970) to cover the shared-hook change, for clearer attribution.
  • [cross-repo-coordination] docs/ADRs/0126-runner-owned-codex-child-policy.md:85 — The ADR states Codex requires Python and Linux Landlock ABI 3+ inside its OpenShell sandbox, including for runs without children. This PR's new "Release coordination" section (and the corresponding docs/runtimes/codex.md update) states the runtime, sandbox image, and companion fullsend-ai/agents revision are released together via the existing same-version-tag workflow, and that existing deployments retain their prior pins until that combination is available — substantially addressing the prior staged-rollout gap. The residual risk is purely operational: there is no in-code capability probe or soft-fail flag if an operator manually pulls the new sandbox image without realizing the Landlock ABI floor moved, and the ADR's Consequences section states the requirement unconditionally with no documented escape hatch.
    Remediation: Optional — add a one-line pointer in docs/runtimes/codex.md's sandbox-image bullet noting that upgrading CODEX_VERSION also raises the minimum Landlock ABI floor, so operators bumping the pin get an explicit heads-up rather than discovering it via a fail-closed error.
  • [sub-agent-failure] N/A — The challenger adversarial-review pass was skipped: only ~634s remained in the review's time budget after the dimension sub-agents completed (checkpoint threshold is 600s, and the challenger takes 2.5-6 minutes on any PR). Using the pre-challenger finding set without adversarial adjudication.

All four high/medium findings from the prior review were verified against the current head and are not observed in the current diff: the sandbox-findings fail-open gap (run_security.go), the dispatch-hook exception fail-open (fullsend-codex-dispatch.py), the scanOutputFiles "security"-directory skip (run.go), and the missing V1/V2 model guard note (codex_personas.go) each now have code changes addressing them, confirmed by re-reading the current source rather than the PR description. The prior collaboration-stream 1 MiB truncation finding (codex_run.go/codex_progress.go) is also not observed in the current diff — the parser now sizes to 8 MiB and fails closed on overflow, backed by new regression tests. A new, distinct completeness gap was found in the same area (see the medium finding above).


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (2)

Review

Findings

High

  • [fail-open] internal/cli/run_security.go:39 — Iteration-boundary halt still keys on sandbox findings.jsonl under the agent-writable workspace (/sandbox/workspace/.security/), which Codex Landlock does not protect (only ConfigDir and /sandbox/codex-policy). A successful download with a missing findings.jsonl writes an empty snapshot and returns nil without checkSecurityFindings, so collectSandboxSecurityFindings leaving securityErr nil lets validation and the reminting post-script proceed. Tirith/adapter log_finding still writes unhashed JSONL; VerifyChain skips hash checks when hash and prev_hash are empty, so replacing the log with unhashed non-critical rows yields Valid=true and CriticalFindings=0. New tests cover save failures and non-regular downloads but not this missing-file-is-treated-as-clean path (TestSecurityFindings_UnverifiableAuditIsNotClean explicitly expects a missing audit to succeed). Production code here is unchanged since the prior review.
    Remediation: Do not treat a missing sandbox audit as clean. Require a runner-owned empty marker written before the agent starts, fail closed when the downloaded log is absent, truncated relative to that marker, or contains unhashed entries now that halt depends on it, and append via a path the agent cannot unlink or rewrite.
  • [fail-open] internal/runtime/codex_hook/fullsend-codex-dispatch.py:29 — The mandatory native-child PreToolUse guard remains a raw python -c script, not the Codex adapter. Codex treats exits other than 0 or 2 (and exit 2 with empty stderr) as Failed, which does not block. The script only maps ValueError/TypeError/KeyError/IndexError to a blocking reason; any other exception (OSError, MemoryError) is uncaught and becomes traceback/exit 1. Reasons are printed rather than written via os.write(2), unlike the adapter's block(). The adapter exists specifically to convert unexpected failures into a blocking exit 2 with bytes on fd 2; this policy hook does not. Production dispatch script is unchanged since the prior review.
    Remediation: Wrap the dispatch script in a BaseException handler that always os.write(2, reason) and sys.exit(2), matching fullsend-codex-hook.py's block(). Do not rely on print() to deliver blocking stderr.

Medium

  • [logic-error] internal/runtime/codex_run.go:503 — Native child completeness is still derived only from spawn_agent/send_input item.completed collab events in the 1 MiB codex exec --json stream. parseCodexStream (codex_progress.go:561) discards isPrefix lines without failing. wait/close only update IDs already in that map, and collectCodexUsage only requires completeness/closure for expectedChildren — so a skipped spawn line leaves rollout-backed children unenforced, while a skipped later wait/close can mark a finished child incomplete. Rollout scanners were raised to 256 MiB for command records; the live collab path was not. Production code is unchanged since the prior review; the new boundary tests (codex_run_boundary_test.go, codex_usage_boundary_test.go, codex_transcript_integrity_test.go) cover digest/download/usage-fold failures but do not add a >1 MiB spawn_agent/wait completed item that still records the child.
    Remediation: Do not treat skipped stream lines as a successful empty collab history. Fail closed on truncated collab lines, raise the collab parse limit, and/or take expected child IDs from downloaded rollout session_meta rather than only stream receiver_thread_ids. Add a regression where a >1 MiB spawn_agent/wait completed item still records the child.
  • [secret-exposure] internal/cli/run.go:4991 — scanOutputFiles now returns a critical count used to halt and gate the post-script, but still SkipDirs on every directory whose basename is security. Skipping the runner's own audit dirs (outputDir/security, iteration-N/security) is intended; the defect is that any agent-created directory named security under extracted output (e.g. iteration-N/output/security/) is also skipped, so secrets there are neither redacted nor counted toward halt. This basename skip predates the PR; the new halt makes the hole more material. Production walk is unchanged since the prior review.
    Remediation: Skip only runner audit paths (filepath.Join(outputDir, "security") and iteration-*/security) by relative path, and continue walking agent output including directories the agent itself names security.
  • [scope-creep] internal/cli/run.go:2521 — PR feat(#6970)!: support native Codex review and retro children #7809 adds runner-wide iteration-boundary security escalation (collectSandboxSecurityFindings), which runs whenever h.SecurityEnabled() — not only for Codex — skips the post-loop validation sweep when securityErr is set, and gates the post-script via runErr for every runtime. Issue codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 is Codex native children; the halt is a documented breaking change but still expands blast radius beyond the Codex-only issue to all sandboxed runtimes.
    Remediation: Extract runner-level security escalation into a dedicated PR/issue with its own design evaluation, or explicitly re-scope codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 so the global halt is an authorized breaking change rather than an incidental bundle.
  • [stale-doc] docs/cli/agent.md:470 — --subagent flag documentation still states that clearing an entry resolves like an unmentioned persona (its frontmatter model, then subagents.default) and only links Pi documentation. With native Codex children, persona model resolution does not read Claude frontmatter models; it resolves via FULLSEND_CODEX_SUBAGENT_MODEL, then subagents.default, then gpt-5.6-luna. Resolution is now runtime-dependent, and this file was not updated by the PR.
    Remediation: Note in docs/cli/agent.md that persona model resolution depends on the runtime and add a cross-reference link to docs/runtimes/codex.md alongside the Pi reference.
  • [missing-guardrail] internal/runtime/codex_personas.go:141 — codexPersonaModel (via translateCodexModel) rejects Claude aliases, inherit, and non-OpenAI provider prefixes, but performs no check against GPT-6/V2-collaboration-API model IDs. docs/contributing/runtime-implementation.md and docs/runtimes/codex.md both state GPT-6/V2 models are not claimed supported for native review/retro children (different lifecycle, no close/depth contract validated), yet an operator can still configure e.g. subagents.security: openai/gpt-6-sol and it passes validation with no warning, silently running an unvalidated collaboration API in a security-relevant child-dispatch path. Confirmed unchanged since the prior review.
    Remediation: Either enforce the V1-only constraint in codexPersonaModel (reject known V2/GPT-6 model IDs or maintain an explicit allowlist of validated model families) or emit a clear warning, so the documented "not supported" claim is backed by actual guardrails rather than prose alone.

Low

  • [scope-creep] internal/security/hooks/tirith_check.py:87 — check_command's return signature now reports maximum blocking severity, and main() emits metadata.severity on Tirith blocks. The file is shared by every sandboxed runtime, but Claude/pi block-vs-allow decisions are unchanged (still exit 1 + decision=block); the added value is consumed by the Codex adapter to avoid classifying every Tirith block as critical. This is an additive metadata field, not a reclassification of parser errors or exit statuses for other runtimes.
    Remediation: Consider moving Tirith severity tracking into an independent hook-fix PR, or explicitly broaden the linked issue to cover the shared-hook change, for clearer attribution even though the behavioral risk is low.
  • [incomplete-doc] docs/guides/dev/tracing.md:113 — Root run and agent span end-attribute summaries still list fullsend.cost_usd without mentioning fullsend.cost_unavailable (emitted, with cost_usd omitted, when dollar cost is unavailable, e.g. on Codex). docs/guides/infrastructure/distributed-tracing.md was updated in this PR, and this page already defers to that reference for the full attribute list, which limits the practical impact.
    Remediation: Update docs/guides/dev/tracing.md to document fullsend.cost_unavailable/omitted fullsend.cost_usd, matching the distributed-tracing reference.
  • [cross-repo-coordination] docs/ADRs/0126-runner-owned-codex-child-policy.md:62 — The ADR states Codex requires Python and Linux Landlock ABI 3+ inside its OpenShell sandbox, including for runs without children. This is enforced and fails closed by this runner independently of the companion fullsend-ai/agents PRs (Prioritize resolving protected-path false positives (refs #1392, #1068) #1501, Review agent: validate documented CLI workflows against code behavior #1542), which affect review/retro instructions rather than bootstrap — so the earlier concern about those PRs' merge state breaking existing deployments does not hold. There is, however, no staged-enablement flag for an existing experimental codex image that predates ABI 3 support.
    Remediation: Document the sandbox-image capability dependency (Python + Landlock ABI 3+) and any sequencing needed for existing experimental codex users, independent of the companion agents-repo PRs.
  • [api-shape] internal/cli/run.go:514 — runAgent is wrapped as runAgentWithRuntimeResolver with an extra resolveRuntime parameter so run_evidence_test.go can inject a backend. That diverges from the package-level XxxFn test-seam convention in docs/contributing/go-code.md, but parameter injection is a valid seam and avoids mutable package state.
    Remediation: Prefer var resolveBackendFromFn = resolveBackendFrom with a doc comment and t.Cleanup restore if matching the repo convention is required.
  • [api-shape] internal/cli/run.go:4963 — scanOutputFiles returns (int, error) with an anonymous integer that is the critical-finding count; the doc comment does not say so.
    Remediation: Name the result (criticalFindings int, err error) or document the integer in the comment.
  • [code-organization] internal/runtime/codex_personas.go:122 — codexPersona composite literals use unkeyed positional arguments for four string fields, unlike keyed initialization used for nearby types.
    Remediation: Use keyed fields, e.g. codexPersona{Name: "default", Description: "...", Model: defaultModel, Instructions: codexChildInstructions}.
  • [error-handling-idiom] internal/runtime/codex_transcript_integrity_test.go:223 — assert.True(t, errors.Is(err, os.ErrNotExist), ...) wraps errors.Is inside assert.True instead of using assert.ErrorIs(t, err, os.ErrNotExist, ...), diverging from the convention used elsewhere in the repository and earlier in this same file. assert.ErrorIs formats the error chain on failure for clearer diagnostics.
    Remediation: Replace with assert.ErrorIs(t, err, os.ErrNotExist, "do not start another remote transfer when no byte budget remains").

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR
Previous run (3)

Review

Findings

High

  • [fail-open] internal/cli/run_security.go:39 — Iteration-boundary halt keys on sandbox findings.jsonl under the agent-writable workspace (/sandbox/workspace/.security/), which Codex Landlock does not protect (only ConfigDir and /sandbox/codex-policy). A successful download with a missing findings.jsonl writes an empty snapshot and returns nil without checkSecurityFindings. Tirith log_finding writes unhashed JSONL; VerifyChain (trace.go) skips hash checks when hash and prev_hash are empty, so replacing the log with unhashed non-critical rows yields Valid=true and CriticalFindings=0. Either path suppresses critical hook findings and lets validation and the reminting post-script proceed, because collectSandboxSecurityFindings returning nil leaves securityErr nil.
    Remediation: Do not treat a missing sandbox audit as clean. Require a runner-owned empty marker written before the agent starts, fail closed when the downloaded log is absent, truncated relative to that marker, or contains unhashed entries now that halt depends on it, and append via a path the agent cannot unlink or rewrite.
  • [fail-open] internal/runtime/codex_hook/fullsend-codex-dispatch.py:29 — The mandatory native-child PreToolUse guard is a raw python -c script, not the Codex adapter. Codex treats exits other than 0 or 2 (and exit 2 with empty stderr) as Failed, which does not block. The script only maps ValueError/TypeError/KeyError/IndexError to exit 2; any other exception (OSError, MemoryError) becomes an uncaught traceback/exit 1. Reasons are printed via print() rather than os.write(2), unlike adapter block(). The adapter exists specifically to convert unexpected failures into a blocking exit 2; this policy hook does not.
    Remediation: Wrap the dispatch script in a BaseException handler that always os.write(2, reason) and sys.exit(2), matching fullsend-codex-hook.py block(). Do not rely on print() to deliver blocking stderr.

Medium

  • [logic-error] internal/runtime/codex_run.go:503 — Native child completeness is derived only from spawn_agent/send_input item.completed collab events in the 1 MiB codex exec --json stream. parseCodexStream (codex_progress.go) discards isPrefix lines without failing. wait/close only update IDs already in that map, and collectCodexUsage only requires completeness/closure for expectedChildren—so a skipped spawn line leaves rollout-backed children unenforced, while a skipped later wait/close can mark a finished child incomplete. Rollout scanners were raised to 256 MiB for command records; the live collab path was not.
    Remediation: Do not treat skipped stream lines as a successful empty collab history. Fail closed on truncated collab lines, raise the collab parse limit, and/or take expected child IDs from downloaded rollout session_meta rather than only stream receiver_thread_ids. Add a regression where a >1 MiB spawn_agent/wait completed item still records the child.
  • [api-contract] docs/runtimes/codex.md:99 — Documented Codex child model precedence does not match codexPersonas. The numbered list (and docs/reference/config-reference.md) implies agents[].subagents.default beats FULLSEND_CODEX_SUBAGENT_MODEL for the default role. Implementation applies subagents.default first, then overwrites defaultModel with the env var; resolve("default") always returns that env-derived default. TestCodexBootstrapPersonaModels case "environment overrides configured default" locks this in.
    Remediation: Document the implemented order: named persona subagents. wins; otherwise FULLSEND_CODEX_SUBAGENT_MODEL; otherwise subagents.default; otherwise gpt-5.6-luna. Call out that subagents.default does not beat the env var for the default role. Align docs/reference/config-reference.md.
  • [secret-exposure] internal/cli/run.go:4991 — scanOutputFiles now returns a critical count used to halt and gate the post-script, but still SkipDir on every directory whose basename is "security". That skips the intended host and iteration audit dirs and also any agent-created directory named security under extracted output (e.g. iteration-N/output/security/), so secrets there are neither redacted nor counted toward halt. This basename skip predates the PR; the new halt makes the hole more material.
    Remediation: Skip only runner audit paths (filepath.Join(outputDir, "security") and iteration-*/security) by relative path, and continue walking agent output including directories the agent names security.
  • [scope-creep] internal/cli/run.go:2521 — PR feat(#6970)!: support native Codex review and retro children #7809 adds runner-wide iteration-boundary security escalation (collectSandboxSecurityFindings), skips the post-loop validation sweep when securityErr is set, and gates the post-script via runErr for every runtime. Issue codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 is Codex native children; the halt is a documented breaking change (feat codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 ! and docs/cli/run.md Security escalation) but still expands blast radius beyond the Codex-only issue.
    Remediation: Extract runner-level security escalation into a dedicated PR/issue with its own design evaluation, or explicitly re-scope codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 so the global halt is an authorized breaking change rather than an incidental bundle.
  • [scope-creep] internal/security/hooks/tirith_check.py:87 — check_command's return signature now reports maximum blocking severity, changing how parser errors, exit statuses, and block actions are classified for every sandboxed runtime. That is motivated by Codex child review/halt wiring but lands in the shared Tirith adapter.
    Remediation: Move Tirith severity tracking into an independent hook-fix PR, or explicitly broaden the linked issue to cover the shared-hook change.
  • [stale-doc] docs/cli/agent.md:470 — --subagent still says clearing an entry resolves like an unmentioned persona (frontmatter model, then subagents.default) and only links Pi. Codex ignores Claude frontmatter model and resolves via FULLSEND_CODEX_SUBAGENT_MODEL, then subagents.default, then gpt-5.6-luna. The flag write semantics are unchanged; the resolution text is now runtime-dependent.
    Remediation: Note that persona model resolution depends on the runtime and link docs/runtimes/codex.md alongside the Pi section.
  • [incomplete-doc] docs/guides/dev/tracing.md:113 — Root run and agent span end-attribute lists still show fullsend.cost_usd unconditionally. This PR omits fullsend.cost_usd and sets fullsend.cost_unavailable when CostUnavailable is set (Codex). docs/guides/infrastructure/distributed-tracing.md is updated; this developer tracing page is not.
    Remediation: Document that fullsend.cost_unavailable is emitted and fullsend.cost_usd omitted when dollar cost is unavailable, matching the distributed-tracing schema.
  • [backward-compatibility] internal/runtime/codex_personas.go:141 — codexPersonaModel rejects Claude aliases, inherit, and non-OpenAI-shaped ids, but not GPT-6/V2 collaboration models. ADR 0126 and docs/runtimes/codex.md say GPT-6 entries select V2, which is not claimed supported for review/retro close/depth. An operator can still set subagents.security: openai/gpt-6-sol and pass validation. Default remains gpt-5.6-luna (V1); this is an unenforced documented restriction, not a silent default to V2.
    Remediation: Allow-list validated V1 model ids in codexPersonaModel, or preflight the model's collaboration API and fail closed on V2.
  • [cross-repo-coordination] docs/ADRs/0126-runner-owned-codex-child-policy.md:65 — Every runtime: codex run now requires Python, Landlock ABI 3+, and the native dispatch PreToolUse guard, including runs with no children (ADR 0126 consequences). There is no flag waiting on agents Prioritize resolving protected-path false positives (refs #1392, #1068) #1501/Review agent: validate documented CLI workflows against code behavior #1542. Companion instructions teach spawn shapes; Landlock/Python apply even if those PRs have not merged.
    Remediation: Stage rollout (same release/tag as companion PRs, or a config flag) so existing runtime: codex repos are not broken by an asynchronous merge, or confirm none pin runtime: codex in production.

Low

  • [fail-open] internal/runtime/codex_hook/fullsend-codex-dispatch.py:21 — Fresh-context enforcement is exclusive on key presence: fork_turns="none" with fork_context omitted is allowed (the intended V2 path, covered by TestCodexLandlockEnforcesInheritedProtection "fresh V2"). A bypass exists only if a V1 schema ignores unknown fork_turns and still inherits parent context. That V1 behavior is not evidenced in this repo.
    Remediation: If V1 is confirmed to ignore fork_turns, require the active schema's freshness field (fork_context is False, and/or fork_turns=="none") and deny when that field is missing rather than treating fork_turns presence as proof of V2.
  • [unauthorized-change] internal/runtime/codex_run.go:580 — The PR body says it incorporates unmerged upstream PR fix(#7725): keep codex hook block at exit 2 with closed stderr #7726 (real-stderr delivery, exit-status regressions, adapter severity). That work is explicitly listed, so it is not hidden, but reviewers of feat(#6970)!: support native Codex review and retro children #7809 still have to separate those lines from the codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 feature.
    Remediation: If fix(#7725): keep codex hook block at exit 2 with closed stderr #7726 is still unmerged, wait and rebase or isolate those commits so reviewers can attribute them independently.
  • [api-shape] internal/cli/run.go:514 — runAgent is wrapped as runAgentWithRuntimeResolver with an extra resolveRuntime parameter so run_evidence_test.go can inject a backend. That diverges from the package-level XxxFn test-seam convention in docs/contributing/go-code.md, but parameter injection is a valid seam and avoids mutable package state.
    Remediation: Prefer var resolveBackendFromFn = resolveBackendFrom with a doc comment and t.Cleanup restore if matching the repo convention is required.
  • [api-shape] internal/cli/run.go:4963 — scanOutputFiles returns (int, error) with an anonymous integer that is the critical-finding count; the doc comment does not say so.
    Remediation: Name the result (criticalFindings int, err error) or document the integer in the comment.
  • [code-organization] internal/runtime/codex_personas.go:122 — codexPersona composite literals use unkeyed positional arguments for four string fields, unlike keyed initialization used for nearby types.
    Remediation: Use keyed fields, e.g. codexPersona{Name: "default", Description: "...", Model: defaultModel, Instructions: codexChildInstructions}.

Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 8:22 PM UTC · Completed 8:52 PM UTC

Commit: 7856414 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5 · Effort: high · Cost: $12.23

fullsend-ai-review[bot]

This comment was marked as outdated.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 9:37 PM UTC · Ended 9:44 PM UTC

Commit: 8e78100 · View workflow run →

@dhshah13

Copy link
Copy Markdown
Contributor Author

Additional file-level and summary findings addressed in 0dfd9f9, with validation recorded in 8e78100:

The output walker now exempts only the runner's exact audit locations. Agent-created security directories below extracted output, including nested ones, are scanned and redacted. The regression places synthetic credential-shaped content in those paths and verifies both critical counts and redaction, while preserving the runner audit bytes.

Updated the flag documentation to state that clearing retains the existing write semantics but model fallback depends on the runtime. It now links both Pi and Codex, including Codex's environment/default fallback and its treatment of Claude frontmatter.

Updated the developer tracing lists to match the emitted schema: when dollar cost is unavailable, fullsend.cost_unavailable is present and fullsend.cost_usd is omitted. Unknown cost is not reported as a known zero-dollar run.

This dependency is no longer unmerged: PR 7726 merged as 86e8bf5b8bf95508c51a53de6ed353583a791bb7 on 2026-09-28, and that commit is an ancestor of this branch. Its origin and the integration remain attributed; no separate wait on an unmerged dependency is needed.

All eight selected local native/Fullsend workflow audits passed for the source revisions documented in the PR. Final-head CI, authorized admin e2e and human approval remain separate gates.

@dhshah13
dhshah13 marked this pull request as ready for review September 28, 2026 21:43
@dhshah13
dhshah13 requested a review from a team as a code owner September 28, 2026 21:43
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 9:46 PM UTC · Ended 10:23 PM UTC

Commit: 8e78100 · View workflow run →

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Support native Codex children with protected policy and complete evidence

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Enable native Codex review and retro children with runner-selected roles and bounded delegation.
• Require complete child, transcript, usage, and trusted security evidence before accepting a run.
• Halt Codex runs on critical findings before validation, retries, or post-scripts.
Diagram

graph TD
  CLI["Run CLI"] --> Boot["Codex bootstrap"] --> Guard["Protected launcher"] --> Root["Root agent"] --> Child["Native children"] --> Evidence["Evidence collection"] --> CLI
  Root --> Audit["Trusted audit"] --> CLI
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Image-owned immutable Codex policy
  • ➕ Could simplify per-run filesystem protection.
  • ➖ Requires coordinated image updates for dynamic roles and hook plans.
  • ➖ Still needs authenticated child evidence and a trusted scanner audit.

Recommendation: Retain runner-owned policy for dynamic persona selection and trusted per-iteration evidence. An image-owned policy could simplify protection, but cannot replace the role and evidence contracts; coordinate rollout with the compatible image and companion instructions.

Files changed (78) +7180 / -430

Enhancement (19) +2569 / -230
run.goGate iterations on trusted evidence +115/-51

Gate iterations on trusted evidence

• Retains artifacts on incomplete evidence and checks Codex security before validation or retries. Propagates unavailable cost.

internal/cli/run.go

run_security.goSeparate trusted and legacy findings +137/-0

Separate trusted and legacy findings

• Verifies finalized runtime audits, applies escalation, and keeps other runtimes' logs diagnostic.

internal/cli/run_security.go

codex_audit.goManage protected audit sessions +279/-0

Manage protected audit sessions

• Starts and seals a separate collector and exports its current-iteration findings.

internal/runtime/codex_audit.go

codex_bootstrap.goProvision native roles and hooks +55/-36

Provision native roles and hooks

• Installs personas and named skills, initializes native state, and records protected policy hashes.

internal/runtime/codex_bootstrap.go

fullsend-codex-audit.pyImplement trusted scanner collector +488/-0

Implement trusted scanner collector

• Runs scanners outside agent write authority and seals bounded request and finding records.

internal/runtime/codex_hook/fullsend-codex-audit.py

fullsend-codex-dispatch.pyEnforce V1 child dispatch +49/-0

Enforce V1 child dispatch

• Blocks unsupported roles, inherited context, grandchildren, resumption, V2 arguments, and overrides.

internal/runtime/codex_hook/fullsend-codex-dispatch.py

fullsend-codex-hook.pyRoute scans to trusted collector +73/-2

Route scans to trusted collector

• Adds the audit client and preserves verified Tirith blocking severity.

internal/runtime/codex_hook/fullsend-codex-hook.py

fullsend-codex-landlock.pyRestrict Codex policy writes +139/-0

Restrict Codex policy writes

• Applies inherited Landlock protection before agent environment loading and verifies authority files.

internal/runtime/codex_hook/fullsend-codex-landlock.py

codex_integrity.goHold role and audit authority +5/-0

Hold role and audit authority

• Records Python, role hashes, and installed hook groups outside the sandbox.

internal/runtime/codex_integrity.go

codex_landlock.goBuild protected launch policy +59/-0

Build protected launch policy

• Defines authority files and allowed native state for inherited Landlock restrictions.

internal/runtime/codex_landlock.go

codex_personas.goRegister native Codex personas +195/-0

Register native Codex personas

• Generates named, default, and Explore roles with runner-selected models and lifecycle instructions.

internal/runtime/codex_personas.go

codex_run.goEnforce complete native execution +209/-61

Enforce complete native execution

• Launches under protection, tracks child completion and closure, reconciles usage, and fails missing evidence.

internal/runtime/codex_run.go

codex_skills.goInstall skills by declared name +77/-0

Install skills by declared name

• Retains nested skills and rejects unsafe or conflicting names.

internal/runtime/codex_skills.go

codex_transcript.goAuthenticate native transcripts +335/-67

Authenticate native transcripts

• Keeps expected identities and digests, downloads privately, and publishes validated redacted files atomically.

internal/runtime/codex_transcript.go

codex_usage.goReconcile response usage +297/-0

Reconcile response usage

• Validates and deduplicates per-thread deltas, attributes models, and separates token categories.

internal/runtime/codex_usage.go

event.goExpose result cost availability +1/-0

Expose result cost availability

• Adds an availability flag to normalized result events.

internal/runtime/event.go

renderer.goRender unavailable cost +5/-1

Render unavailable cost

• Displays unavailable instead of a misleading zero-dollar Codex result.

internal/runtime/renderer.go

runtime.goAdd evidence capability contracts +36/-6

Add evidence capability contracts

• Defines incomplete-evidence errors, trusted audit collection, trace IDs, and cost-aware metrics.

internal/runtime/runtime.go

trace.goCount verified critical findings +15/-6

Count verified critical findings

• Exposes critical counts after complete audit-chain verification.

internal/security/trace.go

Bug fix (3) +80 / -38
codex_progress.goPreserve collaboration lifecycle +19/-7

Preserve collaboration lifecycle

• Fails oversized live records instead of dropping child evidence; marks dollar cost unavailable.

internal/runtime/codex_progress.go

codex_redact.goValidate large rollout records +10/-3

Validate large rollout records

• Accepts JSONL records within the existing artifact bound and rejects overflow.

internal/runtime/codex_redact.go

tirith_check.pyPreserve Tirith blocking severity +51/-28

Preserve Tirith blocking severity

• Reports structured severity while classifying malformed verdicts and failures conservatively.

internal/security/hooks/tirith_check.py

Refactor (1) +4 / -0
pi_personas.goShare persona discovery +4/-0

Share persona discovery

• Exposes discovered persona metadata for native Codex role registration.

internal/runtime/pi_personas.go

Tests (36) +3967 / -67
cost_availability_test.goTest cost availability +64/-0

Test cost availability

• Checks aggregate and telemetry flags without treating known zero as unavailable.

internal/cli/cost_availability_test.go

run_audit_test.goTest trusted-audit failures +175/-0

Test trusted-audit failures

• Covers missing evidence, escalation, invalid publication, and persistence failures.

internal/cli/run_audit_test.go

run_evidence_test.goTest runner failure gates +313/-0

Test runner failure gates

• Checks artifact retention and stopping behavior for evidence and security failures.

internal/cli/run_evidence_test.go

run_security_test.goTest audit integrity rejection +60/-0

Test audit integrity rejection

• Ensures unverifiable findings cannot be accepted as clean evidence.

internal/cli/run_security_test.go

scan_output_telemetry_test.goTest output scan failures +71/-1

Test output scan failures

• Covers agent-named security directories, unreadable files, and failed audit persistence.

internal/cli/scan_output_telemetry_test.go

codex_audit_collector_test.goTest collector authority boundaries +426/-0

Test collector authority boundaries

• Covers worker cleanup, concurrency, tampering, disconnected clients, and incomplete scans.

internal/runtime/codex_audit_collector_test.go

codex_audit_test.goTest audit lifecycle +256/-0

Test audit lifecycle

• Checks finalization, cancellation, stale identity, cleanup, and incomplete-evidence handling.

internal/runtime/codex_audit_test.go

codex_bootstrap_test.goUpdate bootstrap coverage +39/-11

Update bootstrap coverage

• Checks native-child instructions and revised provisioning and failure paths.

internal/runtime/codex_bootstrap_test.go

codex_collection_test.goTest evidence completeness +538/-0

Test evidence completeness

• Covers missing root or child results, failed dispatch, interruption, and partial transfer.

internal/runtime/codex_collection_test.go

codex_config_test.goUpdate hook configuration tests +3/-3

Update hook configuration tests

• Adjusts expected commands for collector-backed hooks.

internal/runtime/codex_config_test.go

codex_dispatch_test.goTest fail-closed dispatch +40/-0

Test fail-closed dispatch

• Verifies unexpected hook failures block through raw stderr.

internal/runtime/codex_dispatch_test.go

codex_hook_test.goTest adapter severity +106/-0

Test adapter severity

• Checks verified Tirith classifications and retention of unclassified critical findings.

internal/runtime/codex_hook_test.go

codex_landlock_test.goTest dispatch and Landlock policy +126/-0

Test dispatch and Landlock policy

• Exercises forbidden dispatch and inherited filesystem protection where supported.

internal/runtime/codex_landlock_test.go

codex_personas_test.goTest persona model resolution +152/-0

Test persona model resolution

• Covers precedence, invalid overrides, duplicate names, and unsafe files.

internal/runtime/codex_personas_test.go

codex_progress_test.goTest large collaboration events +33/-3

Test large collaboration events

• Checks valid large events and rejects overflow without retaining false success.

internal/runtime/codex_progress_test.go

codex_redact_test.goAdjust stream redaction test +1/-1

Adjust stream redaction test

• Updates redacted stream expectations for the revised Codex run.

internal/runtime/codex_redact_test.go

codex_rollout_limits_test.goTest rollout bounds +135/-0

Test rollout bounds

• Covers large command records and artifact-limit failures.

internal/runtime/codex_rollout_limits_test.go

codex_run_boundary_test.goTest protected run failures +124/-0

Test protected run failures

• Checks mandatory hook consistency and retention of known usage on collection failure.

internal/runtime/codex_run_boundary_test.go

codex_run_test.goTest protected launcher flags +53/-6

Test protected launcher flags

• Checks policy guards and separation of helper paths from launcher and authentication paths.

internal/runtime/codex_run_test.go

codex_skills_test.goTest skill installation +121/-0

Test skill installation

• Covers nesting, duplicate declarations, symlinks, names, and upload failures.

internal/runtime/codex_skills_test.go

codex_subagent_usage_test.goTest response-level child accounting +334/-0

Test response-level child accounting

• Exercises recorded fixtures, attribution, deduplication, malformed usage, and overflow.

internal/runtime/codex_subagent_usage_test.go

codex_transcript_integrity_test.goTest transcript integrity +223/-0

Test transcript integrity

• Covers changed files, bounds, staging, publication failures, and safe artifact retention.

internal/runtime/codex_transcript_integrity_test.go

codex_transcript_test.goTest required thread transcripts +146/-17

Test required thread transcripts

• Checks root and child completeness, naming, downloads, and extraction errors.

internal/runtime/codex_transcript_test.go

codex_usage_boundary_test.goTest incomplete usage boundaries +152/-0

Test incomplete usage boundaries

• Checks conflicting identities, missing counters, read failures, and overflow.

internal/runtime/codex_usage_boundary_test.go

cost_availability_test.goTest runtime cost accounting +60/-0

Test runtime cost accounting

• Checks unavailable cost alongside parent and child token aggregation.

internal/runtime/cost_availability_test.go

renderer_test.goTest cost rendering +9/-0

Test cost rendering

• Checks the unavailable-cost terminal display.

internal/runtime/renderer_test.go

default.jsonlCapture 0.157 default-child usage +3/-0

Capture 0.157 default-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0157/default.jsonl

probe-alpha.jsonlCapture 0.157 alpha-child usage +3/-0

Capture 0.157 alpha-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0157/probe-alpha.jsonl

probe-beta.jsonlCapture 0.157 beta-child usage +3/-0

Capture 0.157 beta-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0157/probe-beta.jsonl

root.jsonlCapture 0.157 root usage +8/-0

Capture 0.157 root usage

• Provides parent records for root and child reconciliation.

internal/runtime/testdata/codex/native-subagents/0157/root.jsonl

default.jsonlCapture 0.158 default-child usage +3/-0

Capture 0.158 default-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0158/default.jsonl

probe-alpha.jsonlCapture 0.158 alpha-child usage +3/-0

Capture 0.158 alpha-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0158/probe-alpha.jsonl

probe-beta.jsonlCapture 0.158 beta-child usage +3/-0

Capture 0.158 beta-child usage

• Provides reduced native response and identity records.

internal/runtime/testdata/codex/native-subagents/0158/probe-beta.jsonl

root.jsonlCapture 0.158 root usage +8/-0

Capture 0.158 root usage

• Provides parent records for root and child reconciliation.

internal/runtime/testdata/codex/native-subagents/0158/root.jsonl

tirith_check_test.pyTest Tirith severity handling +90/-25

Test Tirith severity handling

• Covers structured findings, unexpected exits, and malformed verdicts.

internal/security/hooks/tirith_check_test.py

trace_test.goTest critical audit counts +83/-0

Test critical audit counts

• Checks severity counting and withholding trusted counts on invalid chains.

internal/security/trace_test.go

Documentation (18) +552 / -92
0104-per-persona-model-resolution.mdLink Codex persona policy +3/-0

Link Codex persona policy

• Points to the Codex-specific model and dispatch decision.

docs/ADRs/0104-per-persona-model-resolution.md

0126-runner-owned-codex-child-policy.mdRecord native-child authority policy +94/-0

Record native-child authority policy

• Defines role resolution, write protection, delegation, trusted auditing, and coordinated release requirements.

docs/ADRs/0126-runner-owned-codex-child-policy.md

architecture.mdDescribe protected Codex children +6/-0

Describe protected Codex children

• Adds bounded native delegation and thread-based evidence to the architecture overview.

docs/architecture.md

agent.mdClarify Codex model overrides +5/-3

Clarify Codex model overrides

• Distinguishes Codex subagent fallback from pi frontmatter behavior.

docs/cli/agent.md

run.mdDocument Codex escalation and metrics +35/-16

Document Codex escalation and metrics

• Explains audit gates, separate findings, child accounting, and unavailable cost.

docs/cli/run.md

runtime-implementation.mdDocument native-child implementation +193/-25

Document native-child implementation

• Describes authority and evidence flows, trusted-audit capability, and local validation scope.

docs/contributing/runtime-implementation.md

tracing.mdExplain unavailable-cost spans +7/-4

Explain unavailable-cost spans

• Documents the availability attribute and omission of unreported dollar cost.

docs/guides/dev/tracing.md

choosing-a-runtime.mdQualify experimental Codex support +2/-2

Qualify experimental Codex support

• Adds native review and retro support alongside its validated profile and limitations.

docs/guides/getting-started/choosing-a-runtime.md

distributed-tracing.mdDescribe cost availability in telemetry +13/-8

Describe cost availability in telemetry

• Explains Codex cost attributes and aggregate availability.

docs/guides/infrastructure/distributed-tracing.md

running-agents-locally.mdAdd local Codex review guidance +9/-0

Add local Codex review guidance

• Provides model and effort settings and recommends disabling posting during validation.

docs/guides/user/running-agents-locally.md

agent-architecture.mdReference fresh-context delegation +1/-1

Reference fresh-context delegation

• Links the stateless-child discussion to the Codex policy decision.

docs/problems/agent-architecture.md

security-threat-model.mdQualify hook immutability +1/-1

Qualify hook immutability

• Distinguishes Codex's protected policy from writable scripts in other runtimes.

docs/problems/security-threat-model.md

config-reference.mdSpecify Codex model precedence +9/-0

Specify Codex model precedence

• Documents overrides, explicit OpenAI IDs, and invalid persona mappings.

docs/reference/config-reference.md

harness-reference.mdDefine critical escalation +3/-1

Define critical escalation

• Separates known findings from scanner failure mode and describes the Codex halt boundary.

docs/reference/harness-reference.md

runtimes.mdUpdate runtime capabilities +7/-6

Update runtime capabilities

• Lists native Codex children, companion requirements, and unavailable dollar cost.

docs/runtimes.md

codex.mdDocument native Codex workflows +106/-20

Document native Codex workflows

• Explains roles, V1 lifecycle, evidence, security escalation, and validation limitations.

docs/runtimes/codex.md

pi_subagents.goClarify pi reasoning limitations +3/-5

Clarify pi reasoning limitations

• Updates usage documentation to distinguish pi from Codex per-model reasoning tokens.

internal/runtime/pi_subagents.go

README.mdDocument fixture provenance +55/-0

Document fixture provenance

• Records reductions, source hashes, expected token totals, and invocation-count semantics.

internal/runtime/testdata/codex/native-subagents/README.md

Other (1) +8 / -3
codex_config.goEnable native agents and hooks +8/-3

Enable native agents and hooks

• Configures multi-agent operation, non-login shells, state storage, and collector-backed hooks.

internal/runtime/codex_config.go

@qodo-code-review

qodo-code-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 🔗 Cross-repo conflicts (0) 📜 Skill insights (1)

Grey Divider


Action required

1. Critical output findings arrive after validation ✗ Dismissed
Description
runAgentWithRuntimeResolver first scans extracted output and escalates its critical findings in
the post-loop scanOutputFiles call. If output from an earlier iteration contains a critical
finding, validation and any resulting inference retries have already run before that finding is
checked.
Code

internal/cli/run.go[R2684-2689]

+		critical, scanErr := scanOutputFiles(runDir, securityTraceID, printer)
+		if _, trustedAudit := rt.(agentruntime.SecurityAuditCollector); trustedAudit {
+			if scanErr != nil {
+				securityErr = errors.Join(securityErr, securityAuditError(h, fmt.Errorf("output scan error: %w", scanErr), printer))
			}
-		}
-
-		findingsJSONL := filepath.Join(runDir, "security", "findings.jsonl")
-		if _, statErr := os.Stat(findingsJSONL); statErr == nil {
-			cv, verifyErr := security.VerifyChain(findingsJSONL)
-			if verifyErr != nil {
-				printer.StepWarn("Audit log verification error: " + verifyErr.Error())
-			} else if !cv.Valid {
-				printer.StepFail(fmt.Sprintf("Audit log integrity check FAILED: %s", cv.BrokenMsg))
-				return fmt.Errorf("audit log integrity check failed: %s", cv.BrokenMsg)
-			} else if cv.Entries > 0 {
-				printer.StepDone(fmt.Sprintf("Audit log integrity verified (%d entries)", cv.Entries))
+			auditErr := checkSecurityFindings(h, filepath.Join(runDir, "security", "findings.jsonl"), printer)
Relevance

●●● Strong

Security failures are expected to halt before downstream validation or retries; similar sequencing
fixes were accepted.

PR-#1780
PR-#239
PR-#2085

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The output pipeline can produce critical findings, and scanOutputFiles counts them. Its new
escalation is after the inline validation script, retry decision, and post-loop validation sweep.

internal/security/redactor.go[88-116]
internal/cli/run.go[5071-5090]
internal/cli/run.go[2607-2689]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Host-output critical findings are escalated only after the validation loop has finished.
## Fix Focus Areas
- internal/cli/run.go[2684-2695]
- internal/cli/run.go[2530-2538]
## Recommended Fix
Scan and verify each iteration's extracted output before its validation script or retry decision. Halt on a critical finding under the configured policy, while retaining the final artifact-redaction pass.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Incomplete child evidence still reaches validation ✓ Resolved
Description
runAgentWithRuntimeResolver accumulates ErrIncompleteEvidence in the run-wide evidenceErr but
continues to validation and retries even though that error is returned at the end. When rt.Run or
transcript extraction reports missing usage or child closure, the runner can run the validation
script and spend another inference budget, including child runs, without clearing the failure.
Code

internal/cli/run.go[R2425-2429]

+		if runErr != nil {
+			// Preserve partial output and transcripts below, but a schema
+			// pass cannot replace required runtime evidence.
+			evidenceErr = errors.Join(evidenceErr, fmt.Errorf("running agent (iteration %d): %w", iteration, runErr))
+		}
Relevance

●●● Strong

Runtime failures bypassing validation or requiring immediate evidence preservation closely match
accepted runner fixes.

PR-#1682
PR-#6938
PR-#279

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Codex returns ErrIncompleteEvidence when usage or child-closure checks fail, including when a
child was not closed. The CLI joins that error into evidenceErr, which is never cleared, while the
retry path does not check it; the post-loop validation sweep is guarded by securityErr rather than
evidenceErr, and the accumulated evidence error is returned at the end.

internal/runtime/codex_run.go[564-608]
internal/cli/run.go[2410-2429]
internal/cli/run.go[2607-2658]
internal/cli/run.go[2425-2429]
internal/cli/run.go[2727-2727]
internal/runtime/codex_run.go[564-567]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The runner retains an incomplete-evidence error for the whole run but still permits validation and retries. Because the retained error is returned at the end, those retries consume inference without allowing the run to succeed.

## Fix Focus Areas
- internal/cli/run.go[2425-2429]
- internal/cli/run.go[2512-2514]
- internal/cli/run.go[2654-2658]
- internal/cli/run.go[2727-2727]

## Recommended Fix
Preserve artifact and audit collection, then stop the iteration loop before validation or another inference run once `evidenceErr` is set. Also prevent the post-loop validation sweep from validating that output. If retries for incomplete evidence are intended instead, track evidence errors per iteration and fail only when the accepted iteration's evidence is incomplete.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. The new decision record breaks the template ✓ Resolved
Description
ADR 0126 inserts a ## Release coordination section between ## Decision and ## Consequences,
although the ADR template has no section in that position. This places the publication requirements
outside the template’s prescribed section structure, leaving a later editor to decide whether they
belong to the decision or its consequences.
Code

docs/ADRs/0126-runner-owned-codex-child-policy.md[72]

+## Release coordination
Relevance

●● Moderate

ADR structure matters, but precedent is mixed on whether additional sections are accepted or
rejected.

PR-#6972
PR-#2743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new ADR contains a Release coordination heading between Decision and Consequences; the
repository template proceeds from Decision directly to Consequences.

docs/ADRs/0126-runner-owned-codex-child-policy.md[72-88]
docs/ADRs/0000-adr-template.md[32-41]
Skill: writing-adrs

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new ADR adds a section absent from the required ADR template.

## Fix Focus Areas
- docs/ADRs/0126-runner-owned-codex-child-policy.md[72-85]

## Recommended Fix
Move the release-coordination requirements into an appropriate template section or a linked release document, then retain only the template’s section headings.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. An accepted decision edit lacks a callout 📘 Rule violation § Compliance
Description
The PR adds a Codex-policy cross-reference to the already Accepted ADR 0104, but the PR description
does not identify ADR 0104 or summarize that edit. The description discusses the new ADR 0126, so a
reviewer of the accepted record is not explicitly told that the older record also changed.
Code

docs/ADRs/0104-per-persona-model-resolution.md[R20-21]

+Codex-specific model precedence and protected native role dispatch are defined
+in [ADR 0126](0126-runner-owned-codex-child-policy.md).
Relevance

●●● Strong

Accepted ADR cross-reference edits are routinely corrected or annotated, supporting explicit
PR-description callouts.

PR-#5244
PR-#5798

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ADR 0104 is marked Accepted and gains a new cross-reference. The supplied PR description mentions
ADR 0126 but does not call out the edit to ADR 0104.

Rule 1062059: Call out edits to accepted ADRs in PR descriptions
docs/ADRs/0104-per-persona-model-resolution.md[17-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR description does not identify the edit to accepted ADR 0104.

## Fix Focus Areas
- docs/ADRs/0104-per-persona-model-resolution.md[20-21]

## Recommended Fix
Add a PR-description note naming ADR 0104 and explaining that its Codex-specific policy now cross-references ADR 0126.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Codex users are sent to older instructions ✓ Resolved
Description
The Codex row in choosing-a-runtime.md directs readers to agents PR #1501 for native review and
retro instructions instead of the companion PR #1542. Users following the newly updated
runtime-selection guide reach the earlier work, while the Codex runtime documentation identifies
#1542 as the required companion.
Code

docs/guides/getting-started/choosing-a-runtime.md[19]

+| `codex` | Experimental | [Codex](https://github.com/openai/codex) — OpenAI models only; local API-key validation, CI credential path not yet exercised | Opt-in pilots when you want GPT. Native review/retro require the companion work in [agents #1501](https://github.com/fullsend-ai/agents/pull/1501); the verified profile uses `openai/gpt-5.6-luna` with medium effort. Select an OpenAI model explicitly because the fleet harnesses request `opus`; see [Codex requirements and limits](../../runtimes/codex.md#native-children). |
Relevance

●●● Strong

Recent documentation reviews accept corrections for stale, inaccurate, or inconsistent
cross-references.

PR-#6897
PR-#6036

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed guide names agents #1501, whereas the updated Codex documentation names agents #1542 as
the companion instructions for native children.

Rule 2748504: Update docs/ references when changing user-facing behavior
docs/guides/getting-started/choosing-a-runtime.md[19-19]
docs/runtimes/codex.md[89-95]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The updated runtime-selection guide links to the earlier agents PR rather than the companion instructions identified by the Codex runtime documentation.

## Fix Focus Areas
- docs/guides/getting-started/choosing-a-runtime.md[19-19]

## Recommended Fix
Replace the agents #1501 link with agents #1542 and keep the description consistent with the Codex runtime page.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. A denied tool call halts the whole run ✗ Dismissed
Description
run_script defaults every hook block to severity = "critical" and lowers it only for structured
Tirith verdicts. As a result, ordinary blocks from tool_allowlist_pretool.py, ssrf_pretool.py or
canary_pretool.py are recorded as critical codex_pretool_block findings. With security enabled
and the default on_critical: halt, the iteration-boundary check breaks the loop, which skips
validation, retries and the post-script, even though the hook already stopped the action.
Code

internal/runtime/codex_hook/fullsend-codex-hook.py[488]

+    severity = "critical"
Relevance

●●● Strong

Recent Codex hook reviews accepted fixes for incorrect block delivery and security enforcement
behavior.

PR-#7726
PR-#6952

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Other hooks send {"decision":"block","reason":...} with no metadata, so the severity stays
critical. The collector records these findings, VerifyChain counts them as critical, and
securityEscalationError halts the run. run.go then breaks before validation and skips the
post-loop sweep.

internal/runtime/codex_hook/fullsend-codex-hook.py[488-510]
internal/security/hooks/tool_allowlist_pretool.py[238-243]
internal/cli/run_security.go[120-129]
internal/cli/run.go[2532-2537]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Every hook block other than a structured Tirith verdict is recorded as a critical finding. Under the default halt policy, a successfully denied tool call therefore aborts the whole Codex run.

## Fix Focus Areas
- internal/runtime/codex_hook/fullsend-codex-hook.py[488-510]
- internal/runtime/codex_hook/fullsend-codex-hook.py[614-620]

## Recommended Fix
- Default a successful structured block (`decision == "block"` with a string reason) to `high`, or to a per-hook severity.
- Keep `critical` for hook crashes, unexpected exit codes and malformed output.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

7. Below-threshold Tirith blocks escalate to critical 🐞 Bug ≡ Correctness
Description
When no individual finding meets TIRITH_FAIL_ON but Tirith's top-level action is block,
check_command now logs and returns critical (previously high). The Codex adapter trusts this
structured severity, so findings the operator deliberately set below the blocking threshold trigger
the default critical halt.
Code

internal/security/hooks/tirith_check.py[R239-240]

+        log_finding("tirith_action_block", "critical", reason, "block")
+        return True, reason, "critical"
Relevance

●●● Strong

Tirith top-level block actions and fail-closed severity handling have established accepted
precedents.

PR-#2085
PR-#239

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
block_severity is set only for findings that pass the threshold check. The action == "block"
branch runs only when none did, yet it returns critical. run_script accepts the declared
severity from Tirith, and securityEscalationError halts on any critical finding.

internal/security/hooks/tirith_check.py[210-240]
internal/runtime/codex_hook/fullsend-codex-hook.py[499-509]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `action == "block"` fallback in `check_command` reports `critical` even though no finding met the threshold.

## Fix Focus Areas
- internal/security/hooks/tirith_check.py[233-240]

## Recommended Fix
- On the `action == "block"` path, return the highest severity among the findings Tirith reported, or `high` as before.
- Keep `critical` for malformed or unrecognized output and scanner failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Codex validation steps stay in prose 📜 Skill insight ✧ Quality
Description
The new Codex review and retro instructions in running-agents-locally.md give actions such as
adding flags, keeping the key in an environment file, and using --no-post-script as prose rather
than numbered steps. A reader working through this added validation procedure must extract the
sequence from paragraphs instead of following an ordered list.
Code

docs/guides/user/running-agents-locally.md[R232-235]

+To exercise review with Codex, use the companion native-child instructions and add
+`--runtime codex --model openai/gpt-5.6-luna --effort medium --env-file fullsend-openai.env`
+to the review command above. Keep the key in the runner's env file. Use
+`--no-post-script` while validating so the run saves its output without posting a
Relevance

● Weak

Recent reviews rejected converting similar procedural prose or numbered headings into ordered lists.

PR-#7411
PR-#6972
PR-#6540

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed passage contains imperative procedural instructions in paragraphs, without an ordered
list.

docs/guides/user/running-agents-locally.md[232-239]
Skill: writing-user-docs

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The added Codex validation procedure presents its actions as prose rather than ordered steps.

## Fix Focus Areas
- docs/guides/user/running-agents-locally.md[232-239]

## Recommended Fix
Convert the review and retro validation actions into numbered steps, putting each action before its explanation.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 72 rules
✅ Cross-repo context — repo relationships
  ⚠️ Failed to retrieve: fullsend-ai/agents
  ⚠️ Failed to retrieve: fullsend-ai/mlflow-assessments
Review mode: 🧠 Deep: This is a highly dense, security-sensitive cross-cutting change spanning native subagent dispatch, sandboxing, transcript integrity, usage accounting, CLI boundaries, hooks, and many independent logic paths where redundant review can catch subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread docs/guides/getting-started/choosing-a-runtime.md Outdated
Comment on lines +20 to +21
Codex-specific model precedence and protected native role dispatch are defined
in [ADR 0126](0126-runner-owned-codex-child-policy.md).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

5. An accepted decision edit lacks a callout 📘 Rule violation § Compliance

The PR adds a Codex-policy cross-reference to the already Accepted ADR 0104, but the PR description
does not identify ADR 0104 or summarize that edit. The description discusses the new ADR 0126, so a
reviewer of the accepted record is not explicitly told that the older record also changed.
Agent Prompt
## Issue description
The PR description does not identify the edit to accepted ADR 0104.

## Fix Focus Areas
- docs/ADRs/0104-per-persona-model-resolution.md[20-21]

## Recommended Fix
Add a PR-description note naming ADR 0104 and explaining that its Codex-specific policy now cross-references ADR 0126.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The PR description now calls out the ADR 0104 cross-reference.

Comment thread docs/ADRs/0126-runner-owned-codex-child-policy.md Outdated
Comment thread internal/cli/run.go
Comment thread internal/cli/run.go Outdated
Comment thread internal/runtime/codex_hook/fullsend-codex-hook.py Outdated
Comment thread internal/security/hooks/tirith_check.py Outdated
Comment on lines +239 to +240
log_finding("tirith_action_block", "critical", reason, "block")
return True, reason, "critical"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Informational

7. Below-threshold tirith blocks escalate to critical 🐞 Bug ≡ Correctness

When no individual finding meets TIRITH_FAIL_ON but Tirith's top-level action is block,
check_command now logs and returns critical (previously high). The Codex adapter trusts this
structured severity, so findings the operator deliberately set below the blocking threshold trigger
the default critical halt.
Agent Prompt
## Issue description
The `action == "block"` fallback in `check_command` reports `critical` even though no finding met the threshold.

## Fix Focus Areas
- internal/security/hooks/tirith_check.py[233-240]

## Recommended Fix
- On the `action == "block"` path, return the highest severity among the findings Tirith reported, or `high` as before.
- Keep `critical` for malformed or unrecognized output and scanner failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

tirith_check.py is unchanged from main in this PR. The severity change, with this fix, moved to the security follow-up.

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Sep 28, 2026
fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:46 PM UTC · Completed 10:22 PM UTC

Commit: 8e78100 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5 · Effort: high · Cost: $21.42

Review and retro can now delegate to Codex native children
(spawn_agent / wait_agent / close_agent, collaboration V1) instead of
running every sub-agent in the root context.

- Bootstrap registers skill personas plus default and explore roles as
  Codex role files, only for agents whose tools allow Agent (as on
  Claude and pi). Child models come from the runner: a persona mapping,
  FULLSEND_CODEX_SUBAGENT_MODEL, subagents.default, then gpt-5.6-luna.
- A mandatory PreToolUse dispatch guard enforces fresh context, depth
  one, no call-site model or effort override, no resume and no V2
  dispatch. Codex caps open children at four.
- Codex starts under an inherited Landlock write restriction so the
  agent cannot rewrite role files, config, hooks or the auth helper
  that Codex re-reads when each child starts. Policy hooks run on a
  system Python outside the agent's write roots.
- Child rollouts are collected and redacted as separate transcripts.
  Parent and child usage is reconciled per thread, and a run whose
  children did not complete and close, or whose usage or transcripts
  are missing, fails with ErrIncompleteEvidence instead of retrying.
- Codex reports dollar cost as unavailable instead of zero.

BREAKING CHANGE: Codex runs require a system python3 3.11+ outside
/sandbox and /tmp and Linux Landlock ABI 3+ (kernel 6.2+) in the
sandbox, including runs without children. During a Codex run, new
top-level $HOME entries other than the ones Bootstrap pre-creates
(.cache, .cargo, .config/git, .local, .npm) cannot be created. Skill
sub-agent personas need a non-empty description. Codex child models
must be OpenAI model IDs available in the pinned CLI; Claude aliases,
inherit and other providers are rejected. Consumers of run metrics
must treat cost_unavailable instead of reading a zero cost.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
Add ADR 0126 (runner-owned policy for native Codex children) and
cross-reference it from the Accepted ADR 0104. Describe the Codex child
roster, model precedence, dispatch limits, Landlock and Python
requirements, cost_unavailable and the validated profile (Codex CLI
0.157.0 and 0.158.0, gpt-5.6-luna, medium effort) in the runtime,
CLI, config and tracing docs.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
@dhshah13
dhshah13 force-pushed the codex/6970-native-subagents branch from 8e78100 to 4b6c863 Compare September 29, 2026 02:20
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:22 AM UTC · Completed 2:48 AM UTC

Commit: 4b6c863 · View workflow run →

Runtime: pi · Model: sonnet → claude-sonnet-5 · Effort: high · Cost: $14.28

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

continue
}
if _, exists := children[id]; exists {
switch state.Status {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] logic-error

The agents_states switch at codex_run.go:514 has no default case, so an unlisted status leaves the prior children[id] value unchanged. Known agent-thread values handled are completed/pending_init/running/interrupted/errored/not_found. The parent prompt (codexSubagentNote, codex_personas.go:208) also names "shutdown" as a finished-child status alongside completed/errored/not_found, and that token is absent from the switch. A child that never reports completed stays false from spawn and still fails the run (fail-closed), so most unknown-status paths are safe. The residual gap: spawn + wait(completed, nonempty message) + a later unlisted status (e.g. shutdown) does not retract completeness, so close_agent can still record closure for a child whose actual terminal state was never recognized.

Suggested fix: Add a default case that sets children[id]=false for unknown agents_states.status values, or explicitly handle "shutdown" alongside the other incomplete terminals. Add a regression test covering spawn + completed nonempty wait + a later shutdown (or other unlisted) status.

// flat_tool_name): V1 resume is multi_agent_v1resume_agent and V2 spawn is
// collaborationspawn_agent. The plain names cover providers without
// namespaced tools.
const codexDispatchMatcher = "spawn_agent|resume_agent|multi_agent_v1resume_agent|collaborationspawn_agent"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] fail-open

codexDispatchMatcher is a closed-world exact alternation: spawn_agent|resume_agent|multi_agent_v1resume_agent|collaborationspawn_agent. The comment documents Codex's flat_tool_name convention (namespace+name, no separator): V1 resume is multi_agent_v1resume_agent, V2 spawn is collaborationspawn_agent — but the equivalent V2 resume form, collaborationresume_agent, is absent from the matcher. Tests pin the observed names on CLI 0.157.0/0.158.0 and do not include this form, so it is unobserved on the pinned CLIs, not a demonstrated live gap. If a V2 parent ever emitted that concatenated resume name, Codex's PreToolUse hook lookup would not match it and the mandatory dispatch guard would never run for it (fail-open at the hook-dispatch layer), though native max_depth=1 and other guards remain as residual protection.

Suggested fix: Add collaborationresume_agent to codexDispatchMatcher if the pinned CLI can emit it, or document explicitly that V2 has no resume tool on 0.157/0.158. Extend the dispatch-policy test's observed-name list accordingly.

@rh-hemartin

Copy link
Copy Markdown
Member

Hello! Do not submit ADRs and implementation at the same time, we still need to accept the ADR and if we reject it the way it is you just have wasted a lot of tokens for the implementation. Make this a draft and open a new PR with the ADR alone, where we can debate if it is worth an ADR or if we accept it/reject it.


Fullsend does not calculate inference cost from token counts or maintain a
model-price table. Each runtime reports a USD cost value and fullsend
model-price table. When a runtime reports a USD cost value, fullsend

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are you changing this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not needed for #6970: Codex has never reported dollar cost (ADR 0099), and this replaced the recorded $0 with an unavailable flag. Dropping it.

@rh-hemartin

Copy link
Copy Markdown
Member

I feel this is handling too many things at once, any way you can split this? I bet there is work here that can be deferred to a followup PR.

@dhshah13
dhshah13 marked this pull request as draft September 29, 2026 12:58
@dhshah13

Copy link
Copy Markdown
Contributor Author

Converted to draft; ADR 0126 is alone in #7827. Once it's decided, this splits into child usage accounting, then roles plus the dispatch guard, then the docs flip. Landlock, cost_unavailable, runner-side completion checks and transcript hardening are out of #6970.

@dhshah13

Copy link
Copy Markdown
Contributor Author

Closing in favour of small PRs after ADR 0126 (#7827) is decided; the split is in the comment above.

This branch was successfully deployed

1 active deployment
site-preview — 4b6c863c Deployed Sep 29, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants