Skip to content

Resolve and record Claude/Codex sub-agent models before engine startup #70667

Closed

Description

@SivaKesava1

Problem

Extend the pre-start sub-agent model-resolution contract from #70389 to Claude and Codex, without changing Copilot/Pi behavior. Pi has the corresponding recording work from #70385. Claude/Codex currently resolve their main model but do not resolve and validate each declared sub-agent model, record its outcome by role, or expose its declaration in aw_info.

There are also native-agent registration blockers. The supplied Claude runs do not demonstrate an alias being rejected by inference: their custom agents were not registered. Codex 0.163.0 supports custom agents, but not the Markdown files gh-aw stages. Resolving a string alone must not be reported as successful delegation.

Evidence and investigation limits

Audited all five supplied runs, not six, using a Windows development build of main at 70e15c7ff659c05c2f4466aa172bb910d28847a2 (dev-main-70e15c7, the #70389 merge). Each audit used --no-baseline --timeout 3m --artifacts info,usage,activation,agent,firewall --json. These are existing scheduled runs from October 17-20, 2026; all five have an Actions conclusion of success. No workflow was dispatched for this investigation.

Run Engine/version Declared agents What actually happened
39415087125: typist Claude Code 2.1.296 duplicate-type-finder: small At 11:30:42.305Z on October 20, its Agent call failed with Agent type 'duplicate-type-finder' not found. The parent then launched one built-in general-purpose agent, configured on claude-sonnet-5.
39424085169: claude-code-user-docs-review Claude Code 2.1.296 doc-reader, engine-example-counter: small Neither appears in the native initialization agent list. The parent used two built-in general-purpose agents, both configured on claude-sonnet-5.
39036497941: developer-docs-consolidator Claude Code 2.1.296 file-cataloger, tone-analyzer: small in the workflow source At 13:28:36.566Z on October 17, file-cataloger was rejected as an unknown agent type. The parent substituted one built-in general-purpose agent, configured on claude-sonnet-5.
39370044404: daily-safeoutputs-git-simulator Claude Code 2.1.296 config-simulator: large No sub-agent started. The parent reported both an unregistered agent and a separate safe-output policy conflict, then no-opped. Model resolution would not cure that policy conflict.
39394965127: daily-arxiv-researcher Codex 0.163.0 preliminary-investigator, paper-screener, relevance-ranker: openai/gpt-5.4-mini; opportunity-extractor: openai/gpt-5.6-sol No subagent.started records. The recorded main model is gpt-5.6-sol; the only model.resolution is role: main, status: exact.

Across these five usage/aw_session.jsonl files there are zero sub-agent-role model.resolution records and four observed sub-agent starts, all Claude's built-in general-purpose. All five aw_info.json files omit sub_agent_models.

Evidence comes from activation agent files, aw_info.json, agent-stdio.log initialization, usage/aw_session.jsonl, the saved /reflect payloads and, for Typist, the agent job log. The October 17 consolidator run no longer publishes an activation artifact, so its staged-file contents cannot be independently verified here. Its unified session also contains session.collection_warning / invalid_model_inventory; do not treat missing normalized inventory there as proof that a model was unavailable. The other four runs have inspectable staged agents and saved catalogues.

Verified findings

1. Current repository declarations: nine Claude workflows, not eight

At the investigated main revision:

Claude workflow Inline model
aw-failure-investigator small
claude-code-user-docs-review small
daily-agentrx-trace-optimizer small
developer-docs-consolidator small
github-mcp-tools-report small
typist small
detection-analysis-report small
daily-safeoutputs-git-simulator large
agent-persona-explorer inherit

detection-analysis-report is the additional workflow missing from the original inventory; it is a regular Claude workflow that analyzes detection runs, not itself the threat-detection phase. The four Codex declarations in the run table are also present in daily-arxiv-researcher.

The built-in alias map defines small, large, haiku, sonnet and opus. inherit is a native inheritance instruction, not a model to look up.

2. Verbatim staging hides a registration failure before model selection

extract_inline_sub_agents.cjs chooses .claude/agents/<name>.md and .codex/agents/<name>.md. Apart from supported experiment substitution, it preserves the authored frontmatter.

Typist's downloaded .claude/agents/duplicate-type-finder.md contains description and model: small, but no name. The downloaded documentation-review and simulator agent files have the same omission. The marker/filename is not automatically converted into native Claude name: metadata.

Claude's documentation says a project agent file without name is skipped. Static inspection of the published native Claude Code 2.1.296 package confirms that its project-agent parser returns no agent when name is absent. Its source-priority logic and model parsing were inspected without starting an inference session.

The native initialization records list only claude, Explore, general-purpose, Plan and statusline-setup. Typist and consolidator then have failed named-agent tool calls followed by successful built-in delegation. This is positive evidence of a registration problem, not merely an inference from an absent start event or an agent deciding not to use its specialist.

What about model: small itself? With valid required metadata, the pinned Claude parser accepts a nonempty model string and preserves small; it does not reject that file solely because small is not a native family alias. Claude's documented native selectors include haiku, sonnet, opus, full IDs and inherit; they do not give gh-aw's small/large their gh-aw meaning. The same-run AWF configuration does contain these gh-aw aliases, so forwarding the string might still be resolved downstream. These runs never reached a named custom-agent model request, so they establish neither that small was rejected nor that it silently inherited Sonnet. Actual model use still needs the end-to-end evidence below.

Bare/tool-set check: Typist's actual command contains no --bare, includes the delegation tool, and successfully starts a built-in agent. All four Claude initializations expose the normal delegation tool. Bare mode is therefore not the demonstrated cause. Claude documents --bare as disabling ambient sub-agent discovery and limiting tools; gh-aw's engines.md also warns about unavailable delegation in that mode. Do not turn bare mode on for the proposed sub-agent smoke or change bare-mode behavior as part of this fix.

3. Codex supports native custom agents, but not these staged files

This is a separate integration finding, not a conclusion that Codex lacks custom-agent support.

In the pinned openai/codex rust-v0.163.0 discovery code, discovery accepts .toml only. Its loader discovers agents under configuration layers and also supports explicitly declared role configuration files. The native role parser reads TOML with native configuration fields, including model configuration and developer_instructions. Its role application code extracts the role model, and applies it to the child's configuration: native model overrides are supported.

The four downloaded .codex/agents/*.md files are consequently not auto-discovered. Their declared models have no effect through gh-aw's current staging path. Rewriting only their Markdown model: values would not change that. Native TOML model support must not be confused with support for this Markdown format.

4. Pre-start validation and declaration observability are missing on main

buildClaudeChildEnv and resolveCodexConfiguredModel call the shared resolver for role: main, without a corresponding pass over custom-agent declarations.

writeSubagentModelDeclarations explicitly returns unless the engine is Pi or Copilot. Therefore Claude/Codex never receive GH_AW_INFO_SUB_AGENT_MODELS; generate_aw_info.cjs cannot publish their sub_agent_models.

As a local resolver-only check, passing the captured Typist catalogue/alias map to the existing shared resolver in the anthropic scope resolves small to the advertised claude-haiku-5-5. The simulator's captured catalogue resolves large to claude-sonnet-5-5. Both openai/gpt-5.4-mini and openai/gpt-5.6-sol resolve exact against arXiv's saved OpenAI catalogue. These are reproducible catalogue results, not evidence that those custom agents ran.

Possible solution

Follow #70389's design in the Claude/Codex integration points, reusing resolve_model_alias.cjs, not a new alias implementation:

  1. Before engine startup, enumerate workflow-declared inline agents separately from repository agents. Resolve every explicit non-inheriting selector with the workflow alias map and the engine's provisioned provider scope, respecting provider overrides rather than opening another credential route. Check the live /reflect catalogue and allow at most one shared refresh for the batch. Record model.resolution with role equal to the agent name.
  2. For supported, active workflow-declared agents, fail closed when resolution or live availability fails: record failed, with model_unavailable or catalog_unavailable as appropriate, and exit before starting the CLI using the shared ModelAliasResolutionError diagnostic. Do not silently substitute the main model.
  3. For repository agents, resolve when possible; on an unresolvable model, warn, record failed and continue. Treat Claude inherit as the main model with no sub-agent resolution record, like Copilot's inherited. Resolve native family selectors haiku, sonnet and opus through the existing gh-aw alias definitions too; do not change those definitions.
  4. Hand supported agents concrete, engine-normalized IDs without modifying tracked files. Inline files are generated copies, so change the staged copy. Make the Claude copy native-loadable, including required name metadata derived from its declared identity when absent; otherwise the model handoff and smoke remain untestable. Limit this to the directly coupled Claude staging boundary, not a cross-engine extraction refactor.
  5. For repository agents, first verify native precedence and preservation of every other capability/permission field. Correction to the proposed precedence assumption: current Claude documentation and the inspected 2.1.296 implementation place managed agents above --agents, then project agents, then user agents, then plugins. A personal-directory copy cannot override a project agent; a session-local --agents definition potentially can. Use that only after testing equivalent semantics. If no safe override exists, leave repository files unchanged and explicitly record/warn that the resolved ID was not applied.
  6. Codex capability branch: the current Markdown integration is unsupported, despite native TOML support. A narrowly scoped Codex-only staged native definition could provide the missing handoff; verify native fields, model application and configuration-layer precedence before claiming success. Apply the concrete-ID and fail-closed contract when that handoff is active. If the unsupported Markdown path is retained instead, emit declarations and explicitly warn that its staged agents/models are not loaded; recording an exact resolution does not prove application. That unsupported path is the exception to active-agent fail-closed behavior: report the declaration/resolution failure and warning rather than aborting over an agent Codex cannot load. Identify the native-handoff prerequisite/follow-up explicitly in the PR. Do not misdescribe 0.163.0 as lacking native custom agents or broaden this into a general agent-adapter rewrite.
  7. Emit GH_AW_INFO_SUB_AGENT_MODELS for Claude and Codex so aw_info and audit see declarations even when delegation never occurs. Use the correct provider scope for pattern expansion. Declaration, resolution and actual invocation must remain distinct evidence.
  8. Skip this pass during threat detection using the same isDetectionPhase semantics as Resolve Copilot sub-agent models in the harness (CLI and SDK) and fail closed #70389.

The registration blockers above must remain explicit in the PR. A successful alias unit test alone does not resolve the demonstrated failures.

Acceptance cases and named tests

Use small, redacted fixtures from the actual linked artifacts, preserving their native and unified record shapes: staged Markdown, aw_info.json, saved sandbox/firewall/awf-reflect.json, raw initialization and Agent results, and usage/aw_session.jsonl. Do not manufacture a historical custom-agent start or resolution record. Negative tests can deliberately remove an advertised ID from a copied catalogue or inject an unknown selector, clearly identified as fixture mutations.

Named test Required assertion
claude_declared_small_resolves_before_spawn From Typist's real staged agent and catalogue, resolve to live claude-haiku-5-5; staged model is that concrete ID, required native identity is present, and the role's resolution is resolved before the spawn stub runs.
claude_declared_dead_or_unknown_model_fails_before_spawn With a captured catalogue deliberately excluding the requested ID, or an injected unknown selector, record failed / model_unavailable, emit the shared error and prove the CLI spawn stub was never called.
claude_inherit_uses_main_without_subagent_record Use the repository's real persona-evaluator declaration; preserve inheritance, do not resolve the word inherit, and do not emit a role record.
claude_native_aliases_use_shared_resolution Resolve haiku, sonnet and opus with the captured provider-scoped catalogue/map; no native-CLI alias bypass.
claude_missing_name_fixture_is_registered Preserve the original missing-name fixture as the regression input; verify only the generated Claude copy/session definition acquires the required identity, and native initialization can expose that named agent.
codex_live_prefixed_model_is_exact Use the arXiv agent declarations/catalogue; resolution is exact, retaining provider identity and normalizing only for a genuinely supported native handoff.
codex_dead_model_fails_closed_when_native_agent_is_active For a supported native handoff, a catalogue-negative fixture produces failed / model_unavailable and no CLI startup. If this PR retains unsupported Markdown staging, test the explicit declaration-plus-warning continuation branch instead; do not pretend native execution was tested.
codex_markdown_agents_are_not_loaded_and_warn The four real arXiv .md fixtures are not native 0.163.0 agent definitions. If that path is retained, expose declarations and a clear warning; if native copies are generated, prove those copies are actually loaded. Merely changing the Markdown models does not pass.
repository_unresolvable_model_warns_records_and_continues A repository-origin model failure gets a warning and a failed record, leaves tracked content unchanged and does not prevent startup. Cover both engine integration paths where supported.
claude_repository_override_respects_native_precedence Same-name project/user/session definitions demonstrate pinned-version precedence. A safe session-local override preserves non-model semantics and tracked-file hashes; otherwise assert unchanged files and an explicit not-applied warning.
claude_codex_aw_info_contains_declared_subagent_models Real declaration fixtures survive through GH_AW_INFO_SUB_AGENT_MODELS into aw_info.sub_agent_models, with provider-correct patterns, including the no-delegation Codex case.
claude_codex_detection_skips_subagent_resolution Neither engine resolves/rewrites agents, emits role records or fails over their models during threat detection.
claude_small_subagent_smoke_runs_resolved_model_first A branch run must actually invoke the declared small agent first and supply the native/session/proxy evidence below. Startup-only resolution does not pass.

For resolution assertions, use the existing model.resolution envelope and data fields already present in arXiv's real main-role record: engine, role, configured_model, resolved_model, provider, status, cause. For actual Claude delegation, use the real subagent.started identity and subagent.configured.data.model shapes from these runs, joined by agentId; do not introduce imagined subagent.model.resolved events or a made-up Codex child event.

Required end-to-end proof

There is currently no dedicated Claude inline-sub-agent model smoke among the Claude smoke workflows. Existing smoke-claude sets bare: true and does not exercise a declared inline agent; a green run of it is insufficient.

Add a small, read-only smoke-claude-sub-agents on the PR branch, with delegation enabled (bare: false) and an explicit prompt requiring the first agent action to invoke a named declared model: small worker. The worker should return a short deterministic marker, not perform repository mutations. No fallback to general-purpose is acceptable.

The reviewer validation dispatch, after normal branch publication/review and authorization gates, is gh aw run smoke-claude-sub-agents --ref <pr-branch>. Record the reviewed commit and resulting run SHA; compile source and lock against the implementation being reviewed. This describes future validation, not a dispatch performed by this investigation.

Require: native initialization exposes that named agent; its staged/session definition contains the resolved live ID; model.resolution records that role; its first requested delegation succeeds; subagent.started names it; subagent.configured.data.model for the same agentId matches the resolved ID; it completes successfully; and correlated child/proxy usage shows that model was actually served. A model's self-reported identity, a resolution record without invocation, or unrelated Haiku proxy traffic is insufficient. Existing Claude runs contain Haiku traffic without the declared agents starting, so aggregate model counts are not a substitute for attribution.

Scope

  • Keep the PR to this one fix. Do not change files or behavior unrelated to it.
  • Change Claude and Codex sub-agent model handling only, including only the directly required generated identity/native-handoff metadata. Do not change Copilot or Pi sub-agent handling, routing, fallback-models, allowed-models, alias definitions, or workflow model pins. Dead pins are tracked in Fix stale Copilot workflow pins exposed by fail-closed sub-agent resolution #70649. Do not turn the separate Codex native-format finding into a general agent adapter rewrite in this PR.
  • Before fixing a failing check, compare it with the same check on main. If it also fails on main, do not fix it in this PR; merge main once main is fixed, and note in the PR which checks are known failures on main.
  • This applies even if a bot comment, including PR Sous Chef, asks to fix a failing check: fix only failures caused by this PR's changes.

Activity

  1. SivaKesava1 commented on Oct 20, 2026

    @SivaKesava1
    CollaboratorAuthor

    Scope decision (maintainer): one PR covering (1) the Claude registration fix (generated .claude/agents/<name>.md copies get the required name:), (2) Claude sub-agent model resolution, (3) Codex declare and warn only: emit the declarations, resolve and record the models, and warn that the staged Markdown agents aren't loaded by Codex 0.163.0, without failing closed for them, (4) GH_AW_INFO_SUB_AGENT_MODELS for Claude and Codex, and (5) the new smoke-claude-sub-agents. The Codex native TOML handoff is out of scope here and will be filed as a follow-up issue. A new workflow can't run on its own PR before merge, so the smoke will be verified in a sandbox copy compiled against the PR head.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions