Skip to content

refactor(codex): generate native role files for skill personas - #8384

Merged
waynesun09 merged 5 commits into
fullsend-ai:mainfrom
dhshah13:refactor/6970-codex-role-files
Oct 15, 2026
Merged

waynesun09 merged 5 commits into
fullsend-ai:mainfrom
dhshah13:refactor/6970-codex-role-files

Conversation

@dhshah13

@dhshah13 dhshah13 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

For an agent whose tools include Agent, with harness security on and a parent model Codex
resolves to collaboration V1, Bootstrap writes one TOML role file per skill persona, plus the
built-in default and explore roles, to $CODEX_HOME/agents/ and records their digests in the
runner-held set PR 1's spawn guard checks. Every other agent, and every run with security off, gets
the same files as today.

Inert until PR 6: config.toml keeps [agents] enabled = false, so Codex's multi-agent tools stay
off and no child loads a role file; config.toml, hooks.json and the launch command are byte-equal
to main (goldens).

Related Issue

Part of #6970. Third of the series that replaces the closed #7809 after ADR 0126 (#7827); builds on
#8122 and #8213.

Changes

  • internal/runtime/codex_personas.go: the gate (codexDelegates, codexParentResolvesV1 on the
    pinned CLI's bundled catalog), the catalog type and parser, the role list (codexRoles: skill
    personas plus default and explore, sorted) and the role TOML (name, description,
    developer_instructions; no model line in this PR: ADR 0126's per-persona model arrives
    with PR 6 of the codex runtime: run the review/retro sub-agent rosters through codex's native spawn_agent roles #6970 order, where delegation becomes reachable; default and explore never
    carry one). A child runs with its role's developer_instructions in place of the parent's, so
    every role opens with codexChildNote: pi's child contract and the chunked-read rule.
  • internal/runtime/pi_agent.go, pi_personas.go: piAgentDef keeps the raw frontmatter and
    piPersona carries it with Description and Body, the only pi change.
  • internal/runtime/codex_bootstrap.go: the catalog preflight (codex debug models --bundled,
    offline, like codex --version); the gate is decided before config.toml is rendered; when it
    passes, agents/ is created, one regular file per role is uploaded and RoleFiles records each
    digest. The chunked-read paragraph of codexNoSubagentNote is factored out as
    codexChunkedReadNote, shared with the role files; config.toml stays byte-equal (golden).
  • internal/runtime/codex_personas_test.go, codex_bootstrap_test.go: role rules (reserved,
    duplicate, description-less and body-less personas skipped with a warning; model, tools,
    permissionMode, background, maxTurns reported and never rendered), the catalog, the gate
    on and off (V2, unlisted and run-level-V2 parents off; a parent whose entry carries no version
    on), the three failure paths, and a planted or edited role file denying the spawn through the
    installed hook adapter.
  • internal/runtime/testdata/codex/models-bundled-0.159.3.json: the pinned CLI's catalog reduced
    to slug and multi_agent_version; regenerated on a CLI bump, never edited.
  • internal/runtime/codex_golden_test.go, testdata/codex/golden/roles/*.toml,
    launch-delegating.txt: four golden cases; the delegating launch differs from launch-hooks.txt
    in the FULLSEND_CODEX_SPAWN_DIGESTS value only, which a test pins.
  • docs/contributing/runtime-implementation.md was consulted and updated: the layout tree, the
    role-file row of the guard-anchor table, the "not wired" bullet, a re-check row for the catalog,
    the child note next to the chunked-read rule.

The gate is ADR 0126's Decision as written: a parent whose entry in the bundled catalog selects
v1, or carries no version, delegates; a V2 or unlisted parent runs in one context, with one
stderr line saying why.

Interpretations, for a maintainer to correct: a reserved (enabled, explorer, worker on
Codex), duplicate, description-less or body-less persona is a warning, never a Bootstrap error; a
tools: list naming only Task delegates, as Task already maps to spawn_agent; the parent
model is the one Run launches with, and one that does not translate to an OpenAI id is treated
as unlisted (Run reports it as today); hidden catalog entries count, so codex-auto-review
resolves to V1 as a parent.

Testing

  • make lint passes (staged first)
  • Tests added: TestCodexDelegates, TestDiscoverPersonas_CarriesDescriptionAndBody,
    TestCodexRole_*, TestCodexRoles_*, TestCodexDroppedFrontmatterKeys,
    TestCodexCatalog, TestCodexParentResolvesV1, TestCodexPreflightCatalog, TestCodexRolesDir,
    TestCodexRuntimeBootstrap_WritesRoleFilesForADelegatingAgent,
    TestCodexRuntimeBootstrap_NoRoleFilesWhenTheGateFails,
    TestCodexRuntimeBootstrap_ReportsRoleFileFailures, TestCodexBootstrap_PlantedRoleFileDeniesSpawn,
    TestCodexGolden_DelegatingLaunchDiffersOnlyInSpawnDigests and four TestCodexGolden cases; the
    existing pi and claude tests are unedited.
  • go test -race ./internal/runtime/ and make go-test green; go vet clean; docs build.
  • Base: origin/main at 29695351; Codex pin CODEX_VERSION=0.159.3.
  • Patch coverage (go tool cover -func): every new function 100% (codexDelegates, codexRoles,
    codexRole.toml, codexDroppedFrontmatterKeys, codexCatalog.has, codexCatalog.resolvesV1,
    parseCodexCatalog, codexParentResolvesV1, codexRolesDir) except codexPreflightCatalog 90%;
    Bootstrap 87%; touched parsePiAgent 100%, discoverPersonas 87%.

Checklist

  • Title follows Conventional Commits
  • Commits are signed off
  • I wrote this contribution and can explain every change

🤖 Generated with Claude Code

codexRoles turns the personas discoverPersonas registers, plus the
built-in default and explore roles, into agents/<name>.toml files with
name, description and developer_instructions (fullsend-ai#6970). A reserved,
duplicate, description-less or body-less persona is skipped with a
warning; model, tools, permissionMode, background and maxTurns are
reported and never rendered. piAgentDef keeps the raw frontmatter;
piPersona gains Description, Body and Frontmatter. Nothing calls this
yet.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
For an agent whose tools include Agent, with harness security on and a
parent model Codex resolves to collaboration V1 (its entry in the pinned
CLI's bundled catalog says v1 or carries no version; codex debug models
--bundled, read at Bootstrap), Bootstrap creates $CODEX_HOME/agents/,
uploads one regular file per role and records each digest in the
runner-held set, so the spawn guard admits the registered roles and
denies a planted or edited file. Every other agent, and every run with
security off, gets today's files. config.toml keeps [agents] enabled =
false until the dispatch note lands (fullsend-ai#6970).

Signed-off-by: Dharit Shah <dhshah@redhat.com>
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Generate guarded Codex role files for skill personas

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Generate native Codex roles for eligible agents from skill personas and built-in defaults.
• Record role-file digests so the existing spawn guard can reject altered or unregistered roles.
• Keep Codex multi-agent tools disabled until delegation is enabled in a later PR.
Diagram

graph TD
  B["Codex Bootstrap"] --> G{"Delegation gate"} --> P["Persona discovery"] --> R["Role TOML files"] --> D["Runner-held digests"] --> S["Spawn guard"]
  C["Bundled model catalog"] --> G
  G -->|gate fails| U["Existing files only"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Gate against an embedded catalog fixture
  • ➕ Avoids an additional sandbox command during Bootstrap.
  • ➖ Can drift from the installed CLI and incorrectly admit or omit roles after a version change.

Recommendation: Use the PR's runtime catalog preflight: it checks the binary that will run and keeps unsupported parents out of the role-generation path. Retain the pinned fixture as a test contract, not the source of runtime eligibility.

Files changed (13) +1133 / -3

Enhancement (4) +263 / -0
codex_bootstrap.goGate and upload Codex role files during Bootstrap +59/-0

Gate and upload Codex role files during Bootstrap

• Checks the pinned CLI's bundled catalog only for security-enabled agents with the Agent tool. For V1-resolving parents, creates the roles directory, uploads each rendered role, and records its digest in the runner-held set.

internal/runtime/codex_bootstrap.go

codex_personas.goRender validated personas as native Codex roles +191/-0

Render validated personas as native Codex roles

• Defines eligibility and bundled-catalog checks, builds sorted roles from discovered personas and two built-ins, and renders three-key TOML without model overrides. Skips invalid or reserved personas with warnings and reports unsupported frontmatter fields.

internal/runtime/codex_personas.go

pi_agent.goRetain raw agent frontmatter for persona diagnostics +5/-0

Retain raw agent frontmatter for persona diagnostics

• Stores the parsed agent definition's raw YAML frontmatter so Codex can identify fields omitted from role files.

internal/runtime/pi_agent.go

pi_personas.goCarry description and body through shared persona discovery +8/-0

Carry description and body through shared persona discovery

• Extends discovered personas with description, body, and raw frontmatter for Codex role generation while leaving the pi manifest shape unchanged.

internal/runtime/pi_personas.go

Tests (8) +861 / -0
codex_bootstrap_test.goTest role-file bootstrap and spawn-guard behavior +237/-0

Test role-file bootstrap and spawn-guard behavior

• Covers eligible and ineligible gates, catalog and upload failures, and recorded file digests. Exercises the installed hook adapter to verify that registered roles pass while planted or edited files deny a spawn.

internal/runtime/codex_bootstrap_test.go

codex_golden_test.goPin role output and delegating launch invariants +54/-0

Pin role output and delegating launch invariants

• Adds golden cases for three role files and a delegating launch command. Asserts that the launch differs from the existing hooks-enabled command only in its spawn-digest value.

internal/runtime/codex_golden_test.go

codex_personas_test.goCover role rendering, persona rules, and model gating +408/-0

Cover role rendering, persona rules, and model gating

• Tests eligibility, discovery metadata, TOML parsing, reserved and duplicate names, dropped frontmatter, and built-in roles. Pins catalog parsing and parent-model behavior, including unversioned and run-level model cases.

internal/runtime/codex_personas_test.go

launch-delegating.txtPin the delegating launch command +93/-0

Pin the delegating launch command

• Captures the launch command with role-file entries in FULLSEND_CODEX_SPAWN_DIGESTS; multi-agent tools remain disabled.

internal/runtime/testdata/codex/golden/launch-delegating.txt

correctness.tomlPin a skill-persona role file +7/-0

Pin a skill-persona role file

• Captures the correctness persona's description and developer instructions without a model or tool restriction.

internal/runtime/testdata/codex/golden/roles/correctness.toml

default.tomlPin the default role file +7/-0

Pin the default role file

• Captures instructions for an unnamed child task that relies on the parent's task message.

internal/runtime/testdata/codex/golden/roles/default.toml

explore.tomlPin the explore role file +7/-0

Pin the explore role file

• Captures read-only investigation instructions and makes clear that they are not an enforced child sandbox.

internal/runtime/testdata/codex/golden/roles/explore.toml

models-bundled-0.159.3.jsonFixture the pinned Codex bundled model catalog +48/-0

Fixture the pinned Codex bundled model catalog

• Records model slugs and collaboration versions from Codex 0.159.3 for gate tests, including V1, V2, and an unversioned entry.

internal/runtime/testdata/codex/models-bundled-0.159.3.json

Documentation (1) +9 / -3
runtime-implementation.mdDocument generated roles and their integrity boundary +9/-3

Document generated roles and their integrity boundary

• Adds role files to the sandbox layout and guard-anchor table. Clarifies that roles can be generated while multi-agent tools remain disabled, and records catalog assumptions to revisit on a Codex version bump.

docs/contributing/runtime-implementation.md

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 1:45 AM UTC · Ended 1:51 AM UTC

Commit: 049279a · View workflow run →

@qodo-code-review

qodo-code-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Informational

1. Raw frontmatter is assigned twice in a row ✓ Resolved
Description
parsePiAgent runs def.Frontmatter = front on two consecutive lines (pi_agent.go lines 82–83).
Nothing breaks at runtime, but a later reader may assume the second line was meant to assign some
other field (for example the body, or a copy of front) and spend time looking for a field that was
dropped.
Code

internal/runtime/pi_agent.go[R82-83]

+	def.Frontmatter = front
+	def.Frontmatter = front
Relevance

●●● Strong

Obvious copy-paste duplicate; trivial deterministic cleanup aligns with accepted maintainability and
correctness fixes.

PR-#764
PR-#8314

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Lines 82 and 83 are the same assignment, which looks like a copy/paste slip.

internal/runtime/pi_agent.go[78-83]

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

## Issue description
`parsePiAgent` assigns `def.Frontmatter = front` twice in a row.

## Fix Focus Areas
- internal/runtime/pi_agent.go[82-83]

## Recommended Fix
Delete one of the two identical `def.Frontmatter = front` lines.

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


Grey Divider

Context sources
✅ Compliance rules (platform): 89 rules
✅ Skills: 5 invoked
  writing-user-docs
  writing-adrs
  check-patch-coverage
  cutting-releases
  adr-corner
✅ Cross-repo context — repo relationships
  Explored: repo: fullsend-ai/agents (sha: e0166257) — View relationship
Review mode: Auto: 🧠 Deep: Substantial security-sensitive runtime logic spans multiple independent code paths.

Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread internal/runtime/pi_agent.go Outdated
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Site preview

Preview: https://857287f4-site.fullsend-ai.workers.dev

Commit: 683c8727200e873779d9c706bab16be14f437819

parsePiAgent assigned def.Frontmatter twice on consecutive lines.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:52 AM UTC · Completed 2:03 AM UTC

Commit: 6094f8b · View workflow run →

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

@codecov

codecov Bot commented Oct 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.11321% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/runtime/codex_bootstrap.go 93.54% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Oct 13, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Moderate risk: large change size and active churn on runtime bootstrap files are offset by zero protected or security-sensitive paths, default-disabled feature gating, and alignment with linked issue #6970.

Previous run

Risk Assessment: moderate (2/5)

Details

Moderate risk: large change size and active churn on runtime bootstrap files are offset by zero protected or security-sensitive paths, default-disabled feature gating, and alignment with linked issue #6970.

Previous run (2)

Risk Assessment: moderate (2/5)

Details

Moderate risk: large change size and active churn on runtime bootstrap files are offset by zero protected or security-sensitive paths, default-disabled feature gating, and alignment with linked issue #6970.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Looks good to me

Previous run

Looks good to me

Previous run (2)

Review

Findings

Medium

  • [architecture-fit] internal/runtime/codex_personas.go:38 — Role files never get a model line (codexRole.toml() comment: "No model line: the run-level default applies to every child"; PR body: "never a model line"). ADR 0126, Decision bullet 2, says a persona with its own subagents.<persona> entry carries that model in its role file, every other child runs agents.default_subagent_model, and Bootstrap checks each resolved child model against the bundled catalog. codexRoles takes no model input, and codex_personas_test.go asserts a role never carries a model line, which fixes the contradiction in place. Nothing in the head tree implements default_subagent_model or FULLSEND_CODEX_SUBAGENT_MODEL yet. The PR is inert ([agents] enabled = false), so no child runs with the wrong model today. The PR describes the omission as permanent, not deferred, and its "Interpretations" list does not cover it.
    Remediation: Either add an optional Model to codexRole and render it for personas with a subagents.<persona> entry (checked against the bundled catalog), here or in a named later PR before PR 6 enables [agents]; or record the departure from ADR 0126 in a new or superseding ADR. Reword the "never a model line" comment and test assertion either way. This needs a maintainer decision.

Low

  • [code-organization] internal/runtime/codex_bootstrap.go:165 — var catalog codexCatalog is declared at the outer scope of Bootstrap but only used inside the if codexDelegates(def, securityOn) block.
    Remediation: Declare it inside the block with catalog, err := codexPreflightCatalog(sandboxName).
  • [idiom] internal/runtime/codex_personas_test.go:174 — Expected paths are built with skill+"/sub-agents/default.md" and skill+"/sub-agents/Bad.md". discoverPersonas and sibling tests use filepath.Join.
    Remediation: Use filepath.Join(skill, "sub-agents", "default.md") and filepath.Join(skill, "sub-agents", "Bad.md").

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Oct 13, 2026
The toml comment and the TOML test read as if a role file never carries
a model line. ADR 0126's per-persona model arrives with the PR of fullsend-ai#6970
that makes delegation reachable; default and explore never carry one.

The expected persona paths in TestCodexRoles_DiscoverySkipIsAWarning are
joined with filepath, as discoverPersonas builds them.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:10 AM UTC · Completed 2:18 AM UTC

Commit: 8d8dd6a · View workflow run →

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

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Oct 13, 2026
@dhshah13 dhshah13 added the ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) label Oct 13, 2026
Comment thread internal/runtime/codex_personas.go Outdated
A native child runs with its role's developer_instructions in place of
the parent's, so nothing of codexNoSubagentNote reached it, including
the chunked-read rule for exec truncation (fullsend-ai#7831), and only the
built-in roles stated the child contract.

Every role, persona or built-in, now opens with codexChildNote: pi's
CHILD_SYSTEM_NOTE word for word, then codexChunkedReadNote, the
paragraph factored out of codexNoSubagentNote. The parent's note and
config.toml are byte-equal. The built-ins drop their own contract
clause.

Signed-off-by: Dharit Shah <dhshah@redhat.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 2:24 PM UTC · Completed 2:32 PM UTC

Commit: 683c872 · View workflow run →

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

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review October 14, 2026 14:32

Superseded by updated review

@waynesun09 waynesun09 left a comment

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.

LGTM. The child runtime note in 683c872 resolves the role-instructions finding; shared pi/Codex notes are tracked in #8403.

@waynesun09
waynesun09 added this pull request to the merge queue Oct 15, 2026
Merged via the queue into fullsend-ai:main with commit a797144 Oct 15, 2026
53 of 54 checks passed

This branch was successfully deployed

1 active and 1 inactive deployments
dev — 683c8727 Deployed Oct 14, 2026 by dhshah13 via behaviour #16591
site-preview — 683c8727 Deployed Oct 14, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ok-to-test Allow e2e CI to run after maintainer review (must be re-applied after each push) ready-for-merge All reviewers approved — ready to merge risk/moderate PR risk: moderate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants