Repository navigation
Register Claude inline sub-agents and resolve Claude/Codex sub-agent models before engine startup - #70671
Conversation
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.
|
|
✅ PR Code Quality Reviewer completed the code quality review.
|
|
✅ Ponytail Reviewer completed successfully! Ponytail ReviewerOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
|
✅ Test Quality Sentinel completed test quality analysis. Test Quality SentinelOutcome: The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible. Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.
|
🏗️ Design Decision Gate — ADR RequiredThis PR adds 150 new lines of business logic (more than the 100-line threshold, mostly in 📄 Draft ADR committed: The draft records this decision: resolve and record every Claude and Codex sub-agent model before the engine CLI starts. How strict it is depends on who owns the agent:
It also covers adding the missing
📋 What to do next
Once the PR body links an ADR, this gate re-runs and checks that the implementation matches the decision. ❓ Why ADRs Matter
ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you. 📋 Michael Nygard ADR Format ReferenceAn ADR must contain these four sections to be considered complete:
ADRs are stored in
|
There was a problem hiding this comment.
Verdict: comment
No actionable blocking issues were identified in the changed lines. The review was static; repository tests and builds were not run.
🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 7.65 AIC · ⌖ 0.55 AIC · ⊞ 1.6K
Comment /review to run again
| const name = typeof frontmatter.name === "string" && frontmatter.name.trim() ? frontmatter.name.trim() : null; | ||
| agents.push({ fileName: entry.name, path: agentPath, name, ...(typeof frontmatter.model === "string" ? { model: frontmatter.model.trim() } : {}) }); | ||
| } catch (error) { | ||
| logger(`warning: cannot read custom agent ${agentPath}: ${getErrorMessage(error)}`); |
There was a problem hiding this comment.
Malformed workflow-declared agent files are silently dropped here, so the fail-closed path never runs. listMarkdownAgentFiles logs the parse/read error and returns no entry; applyClaudeSubagentModels then reports success and Claude starts with the unresolved staged agent, reproducing the registration/model failure this change is intended to prevent. Treat parse failures under the workflow staging directory as fatal (while retaining lenient handling for repository agents), and add a regression test.
There was a problem hiding this comment.
Fixed in 1f97c55. A staged workflow sub-agent file that can't be read or parsed (or a non-ENOENT read error on the staging directory) now stops Claude before it starts. listMarkdownAgentFiles takes an onInvalid callback, and applyClaudeSubagentModels returns ok: false. Repository agent files are still only warned about. Regressions are in engine_subagent_models.test.cjs (staged: fails closed; repository: lenient) and claude_harness.test.cjs (exit 1, no spawn).
| const resolvedNote = model !== configuredModel ? ` (resolved to '${model}')` : ""; | ||
| if (liveCheck && !isModelAvailableInReflectData(model, reflectData, providers)) { | ||
| const initialReason = classifyUnlistedModel(reflectData, providers); | ||
| const refreshed = await refreshOnce(); |
There was a problem hiding this comment.
The /reflect refresh promise can reject, and this await refreshOnce() is outside the surrounding resolution try/catch. Because the promise is memoized, one transient fetch failure aborts the entire batch; the Claude harness does not catch applyClaudeSubagentModels, so the run exits via an uncaught rejection without recording the remaining agents or emitting the intended catalog-unavailable diagnostic. Catch refresh failures inside refreshOnce and return null so the existing failure classification remains reliable.
There was a problem hiding this comment.
Fixed in 1f97c55. refreshOnce now catches refresh failures, including synchronous throws from refetchReflectData. It logs a warning and returns null, so the batch still resolves and records every agent, and an incomplete catalogue is classified as catalog_unavailable. A regression test checks that a rejected refresh is attempted once and both agents are recorded as catalog_unavailable.
| env, | ||
| logger, | ||
| }); | ||
| if (failures.some(failure => failure.fatal)) return { ok: false, resolutions }; |
There was a problem hiding this comment.
This makes an incomplete /reflect catalog a hard startup failure for every fatal Claude workflow agent, even when the provider may still serve the configured model. catalog_unavailable covers discovery being incomplete or absent, not evidence that the model is invalid; exiting before the workflow starts introduces a new availability dependency and can turn transient discovery failures into total run outages. Keep catalog-unavailable non-fatal (or make this policy explicit), and reserve fail-closed behavior for a complete catalog proving model_unavailable.
There was a problem hiding this comment.
Keeping this as is, on purpose. For a workflow-declared Claude sub-agent, catalog_unavailable fails closed. That's the same policy #70389 uses for Copilot sub-agents and #70320 uses for the Copilot SDK main model: gh-aw doesn't start an agent whose declared model it can't confirm. A transient failure gets one shared refresh first. Repository agents and Codex agents only record and warn. The policy is now documented in the PR body under "catalog_unavailable policy (intentional)".
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /tdd, /codebase-design, and /grill-with-docs (triage via pr-triage returned new_feature). Overall this is a well-tested, carefully scoped change (623 touched JS tests, new Go declaration tests, a realistic smoke workflow built from real failure fixtures). No blocking correctness issues found; comments below are refinement suggestions.
📋 Key Themes & Highlights
Key Themes
- Duplicated/hard-to-verify branch logic in
classifyUnlistedModel's two call sites inengine_subagent_models.cjs— the short-circuit ternary deserves a one-line comment explaining the invariant it relies on. - No surrounding try/catch around the new
applyClaudeSubagentModelscall inclaude_harness.cjs; worth confirming the shared resolver truly never throws, matching the documented "never thrown" contract, or add a safety net consistent with the adjacentfetchAWFReflect/buildClaudeChildEnvtry/catch. - Single string-equality guard (
engineId.toLowerCase() === "claude") is the only thing preventing the regression this PR fixes (Agent type '<name>' not found); the lone call site (interpolate_prompt.cjs) does pass the expected literal, but it's worth a defensive test for unexpected casings/aliases given how costly a silent skip would be. - Provider-string duplication between the new
claudeCodexSubagentProviderhelper and the existingllmProviderAliasesmap inllm_provider.go— aprovider == "copilot"branch looks unreachable given the normalizedLLMProvidertype returned upstream. - Docs gap:
inline-sub-agents.md's "Runtime behavior" section doesn't describe the new fail-closed-for-Claude / declare-and-warn-for-Codex model resolution semantics, which is a meaningful behavior change for anyone declaring amodel:on an inline sub-agent.
Positive Highlights
- ✅ Fail-closed vs. declare-and-warn split between Claude and Codex is clearly reasoned about and documented in code comments and the PR body.
- ✅ Extensive fixture-driven tests sourced from real run failures (Typist 39415087125, arXiv 39394965127), with each fixture mutation explicitly labelled.
- ✅ New
smoke-claude-sub-agents.mdworkflow is a genuinely useful end-to-end check (native init, delegation, proxy-served model) rather than a superficial smoke test. - ✅
providerScopedSubagentPatternscorrectly special-cases Claude'sinheritas "no patterns", matching the semantic that it isn't a real model.
Note: the diff is large (38 files, ~3.5k additions); review focused on the pr-triage-identified high-impact files (engine_subagent_models.cjs, claude_harness.cjs, codex_harness.cjs, extract_inline_sub_agents.cjs, compiler_yaml_step_lifecycle.go, inline-sub-agents.md) rather than the 14 mechanically-updated lock files or the fixture data.
@copilot please address the review comments above.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 143.4 AIC · ⌖ 15.5 AIC · ⊞ 10.2K
Comment /matt to run again
|
|
||
| module.exports = { | ||
| applyClaudeSubagentModels, | ||
| declareCodexSubagentModels, |
There was a problem hiding this comment.
[/codebase-design] classifyUnlistedModel is computed before refreshOnce() as initialReason, then the post-refresh branch re-derives it with a terse ternary: initialReason === "model_unavailable" || !refreshed ? initialReason : classifyUnlistedModel(refreshed, providers). This is hard to verify at a glance without tracing both call sites.
💡 Suggested simplification
const reason = !refreshed ? initialReason : classifyUnlistedModel(refreshed, providers);If initialReason === "model_unavailable" always implies the refreshed classification would also be model_unavailable (since a complete catalog without the model can't become incomplete), make that invariant explicit with a short comment so the short-circuit isn't mistaken for dead code or an optimization-only branch.
@copilot please address this.
There was a problem hiding this comment.
Fixed in 1f97c55. The line is now initialReason === "catalog_unavailable" && refreshed ? classifyUnlistedModel(refreshed, providers) : initialReason. Behaviour is unchanged. A comment states the invariant: a complete catalogue without the model already proves model_unavailable, so only an incomplete one is reclassified from refreshed data.
| @@ -559,6 +560,23 @@ async function main() { | |||
| process.exit(0); | |||
| } | |||
|
|
|||
| // Sub-agent models are resolved on routed runs too, but a pre-existing noop must not | |||
There was a problem hiding this comment.
[/diagnosing-bugs] applyClaudeSubagentModels runs after fetchAWFReflect() and the pre-flight noop/safeInitialArgs checks, but there's no unit/integration test asserting the exact ordering relative to removeClaudePlugin(pluginDir) cleanup on a mid-stream failure of resolveClaudePromptFileArgs (the earlier try/catch at line ~517). If applyClaudeSubagentModels throws instead of returning { ok: false } (e.g. an unexpected error inside resolveEngineSubagentModels's outer try/catch), this call site has no surrounding try/catch, so pluginDir cleanup would be skipped on an unhandled rejection.
💡 Suggested check
Confirm resolveEngineSubagentModels truly never throws (the PR description says "never thrown"), or wrap this call in a try/catch that calls removeClaudePlugin(pluginDir) before re-throwing, matching the pattern used for fetchAWFReflect/buildClaudeChildEnv just above.
@copilot please address this.
There was a problem hiding this comment.
Fixed in 1f97c55. The call is now wrapped in try/catch. An unexpected throw logs fatal: unexpected error while resolving Claude sub-agent models: …, then goes through the existing ok: false path: removeClaudePlugin(pluginDir), then process.exit(1). A harness test injects a throw and checks the exit code, that nothing was spawned, the message, and that cleanup ran.
| @@ -383,7 +402,8 @@ function writeInlineSubAgents(content, workspaceDir, agentsBaseDir, engineId) { | |||
|
|
|||
| for (const agent of agents) { | |||
There was a problem hiding this comment.
[/tdd] ensureClaudeSubAgentName is only applied when engineId.toLowerCase() === "claude", string-compared against the raw engineId parameter. If an upstream caller ever passes a differently-cased or aliased Claude engine id (e.g. "Claude-Code"), this silently skips the name injection and reintroduces the exact Agent type '<name>' not found bug this PR fixes, with no test coverage for that mismatch.
💡 Suggested test
it('still ensures name for an unexpected engineId casing or alias', () => {
// document whatever ids this function is actually called with across the codebase
});Worth confirming (via grep) every writeInlineSubAgents call site passes exactly the literal "claude" string, since this is the only guard against regressing the bug.
@copilot please address this.
There was a problem hiding this comment.
Fixed in 1f97c55. writeInlineSubAgents now normalises the engine id once (trimmed and lower-cased) and uses it for both the target directory and the name: injection, so every id that stages into .claude/agents gets the name. The only production caller is interpolate_prompt.cjs, which passes GH_AW_ENGINE_ID; the compiler emits that as the canonical claude. A test covers Claude and CLAUDE.
| if provider == string(LLMProviderGitHub) || provider == "copilot" { | ||
| return "github-copilot" | ||
| } | ||
| return provider |
There was a problem hiding this comment.
[/codebase-design] claudeCodexSubagentProvider collapses LLMProviderGitHub and the literal "copilot" string into "github-copilot", duplicating the normalization already expressed via llmProviderAliases in llm_provider.go (which maps copilot/github/github-copilot/github_models all to LLMProviderGitHub). Since resolveEngineLLMProviderFromModel already returns a normalized LLMProvider, the provider == "copilot" branch here looks unreachable (it would always be the already-normalized LLMProviderGitHub string "github"), which is subtly different from the alias map's source strings.
💡 Suggested check
Add a short comment explaining why the "copilot" string branch exists here if it's reachable, or remove it if resolveEngineLLMProviderFromModel never returns that literal. Consider having this function call into the shared llmProviderProfileFor/alias logic instead of re-deriving the github-copilot provider-id string, to avoid the two call paths drifting apart.
@copilot please address this.
There was a problem hiding this comment.
Removed in 1f97c55. resolveEngineLLMProviderFromModel always returns a normalised LLMProvider, because normalizeLLMProvider and fallbackModelProvider both map copilot through llmProviderAliases. So the literal "copilot" branch was unreachable. A comment now explains the github → github-copilot mapping. The existing "claude on the copilot provider" declaration test still passes.
There was a problem hiding this comment.
🔵 Needs a closer look
Production startup changes still lack live end-to-end validation, and blocking correctness feedback remains unresolved.
6 open issues
Missing safe-outputs enables unintended issue creation · New Sub-agent named main overwrites main-model records · New Bare plugins use unresolved inline agent definitions · New Frontmatter edits can generate invalid YAML · New Provider-qualified aliases are not expanded · New Inherited model declarations are matched literally · New
What changed in this PR
Extends gh-aw’s pre-start sub-agent model resolution to Claude and Codex, following the Copilot contract, and fixes Claude inline-agent registration.
Changes:
- Adds Claude agent names and resolves declared models before startup.
- Records provider-scoped declarations in
aw_info. - Adds artifact-based tests and a Claude delegation smoke workflow.
| File | Description |
|---|---|
pkg/workflow/compiler_yaml_step_lifecycle.go |
Emits provider-scoped declarations. |
pkg/workflow/claude_codex_subagent_declarations_test.go |
Tests compiled declarations. |
actions/setup/js/smoke_claude_sub_agents.test.cjs |
Tests smoke evidence assertions. |
actions/setup/js/generate_aw_info.test.cjs |
Tests declaration export. |
actions/setup/js/fixtures/claude-codex-sub-agents/typist-39415087125/.claude/agents/duplicate-type-finder.md |
Preserves Claude registration fixture. |
actions/setup/js/fixtures/claude-codex-sub-agents/arxiv-39394965127/.codex/agents/relevance-ranker.md |
Adds recorded ranker definition. |
actions/setup/js/fixtures/claude-codex-sub-agents/arxiv-39394965127/.codex/agents/preliminary-investigator.md |
Adds recorded investigator definition. |
actions/setup/js/fixtures/claude-codex-sub-agents/arxiv-39394965127/.codex/agents/paper-screener.md |
Adds recorded screener definition. |
actions/setup/js/fixtures/claude-codex-sub-agents/arxiv-39394965127/.codex/agents/opportunity-extractor.md |
Adds recorded extractor definition. |
actions/setup/js/fixtures/claude_codex_subagent_runs.cjs |
Supplies recorded events and provenance. |
actions/setup/js/fixtures/awf-reflect-codex-arxiv-39394965127.json |
Supplies OpenAI catalogue evidence. |
actions/setup/js/fixtures/awf-reflect-claude-typist-39415087125.json |
Supplies Anthropic catalogue evidence. |
actions/setup/js/extract_inline_sub_agents.test.cjs |
Tests Claude name staging. |
actions/setup/js/extract_inline_sub_agents.cjs |
Adds missing Claude names. |
actions/setup/js/engine_subagent_models.test.cjs |
Tests resolution and failure behavior. |
actions/setup/js/engine_subagent_models.cjs |
Implements shared sub-agent resolution. |
actions/setup/js/codex_harness.test.cjs |
Tests Codex pre-start declarations. |
actions/setup/js/codex_harness.cjs |
Records declarations before spawn. |
actions/setup/js/claude_harness.test.cjs |
Tests Claude startup enforcement. |
actions/setup/js/claude_harness.cjs |
Applies resolution before spawn. |
.github/workflows/typist.lock.yml |
Adds worker metadata. |
.github/workflows/spec-enforcer.lock.yml |
Adds worker metadata. |
.github/workflows/smoke-claude-sub-agents.md |
Adds delegation smoke and assertions. |
.github/workflows/smoke-claude-sub-agents.lock.yml |
Adds compiled smoke workflow. |
.github/workflows/purelock.lock.yml |
Adds worker metadata. |
.github/workflows/github-mcp-tools-report.lock.yml |
Adds worker metadata. |
.github/workflows/developer-docs-consolidator.lock.yml |
Adds worker metadata. |
.github/workflows/detection-analysis-report.lock.yml |
Adds worker metadata. |
.github/workflows/daily-safeoutputs-git-simulator.lock.yml |
Adds worker metadata. |
.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml |
Adds worker metadata. |
.github/workflows/daily-arxiv-researcher.lock.yml |
Adds Codex declarations. |
.github/workflows/daily-agentrx-trace-optimizer.lock.yml |
Adds worker metadata. |
.github/workflows/claude-code-user-docs-review.lock.yml |
Adds worker metadata. |
.github/workflows/aw-failure-investigator.lock.yml |
Adds worker metadata. |
.github/workflows/api-consumption-report.lock.yml |
Adds worker metadata. |
.github/workflows/agent-persona-explorer.lock.yml |
Adds inheritance declaration. |
.github/workflows/ab-testing-advisor.lock.yml |
Adds worker metadata. |
.changeset/claude-codex-subagent-model-resolution.md |
Documents runtime behavior changes. |
Review effort: Max
| sandbox: | ||
| agent: | ||
| id: awf | ||
| post-steps: |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and this smoke workflow safe-outputs change isn't one of them. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. The smoke workflow is now read-only:
- Disabled: the default
create-issue, failure and failed-jobs issue reporting, issue creation for missing-tool, missing-data and report-incomplete, and threat detection. - It finishes with a
noopthat doesn't publish (report-as-issue: false).
The regenerated lock grants no issues: write and has no create_issue. smoke_claude_sub_agents.test.cjs checks this.
| try { | ||
| result = await resolveEngineModel({ | ||
| engine, | ||
| role: agent.name, |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and reserving the main role for sub-agent records isn't one of them. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. main is now a reserved sub-agent name for Claude and Codex:
- A workflow-declared
mainstops the run with a message asking to rename the agent. - A repository agent named
mainlogs a warning and is skipped.
New tests check that both the main and worker model.resolution records survive, and that the harness stops before spawning when a declared main is present.
| const existing = fs.existsSync(agent.workspacePath) ? fs.readFileSync(agent.workspacePath, "utf8") : null; | ||
| if (existing !== content) { | ||
| fs.mkdirSync(path.dirname(agent.workspacePath), { recursive: true }); | ||
| fs.writeFileSync(agent.workspacePath, content); |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and the bare-mode plugin copy isn't one of them. The PR also lists bare-mode behaviour as out of scope. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. With bare: true, the plugin's agents link is replaced by a run-local copy of the staged definitions, with resolved models written in. The staged and repository files are not changed.
claude_harness.test.cjs reads the definition through the real --plugin-dir/agents path, and engine_subagent_models.test.cjs uses the real claudeBareCapabilities.
| if (/^name[ \t]*:/m.test(frontmatter)) return content; | ||
| const start = content.indexOf("\n") + 1; | ||
| return content.slice(0, start) + nameLine + "\n" + content.slice(start); |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and parser-based name: detection and flow-mapping insertion aren't among them. If such a file did come out unparseable, it now fails closed before Claude starts (1f97c55) rather than being skipped. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. ensureClaudeSubAgentName now uses the YAML parser:
- An existing
namekey is detected, including a quoted"name":. - For flow-mapping frontmatter, the name goes inside the
{...}, so the YAML stays valid.
Staging tests cover a quoted key, {description: ..., model: small} and { }.
| patterns := expandSubagentModelPatterns(agent.Model, data, provider) | ||
| if engineID == "claude" || engineID == "codex" { | ||
| patterns = providerScopedSubagentPatterns(agent.Model, patterns, provider) | ||
| } |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and expanding provider-qualified aliases in declarations isn't one of them. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. Claude and Codex declaration patterns now strip a provider prefix that matches the engine's provider before alias expansion. For example, anthropic/fast with fast: [anthropic/*haiku*] expands to the Haiku pattern.
Prefixes for a different provider are left as they are. Declaration tests cover anthropic/fast, anthropic/small, copilot/small, openai/small, and a mismatched prefix.
| if strings.EqualFold(strings.TrimSpace(model), "inherit") { | ||
| return scoped |
There was a problem hiding this comment.
Not changed in this round. The maintainer limited the review to items 1–4 in #70671 (comment), and matching inherited declarations in the audit consumer isn't one of them. It's left for the maintainer to schedule.
There was a problem hiding this comment.
Fixed in f55f661. declaredModelMatches now matches inherit and inherited declarations against the parent model. That model comes from the main agent's observed models (requested and served); if none were observed, it falls back to the model in aw_info.
As a result, a successful inherited invocation is no longer reported as "Declared Sub-agent Model Not Observed". TestDeclaredInheritedSubagentModelMatchesParentModel covers this.
…refresh failures, guard harness call site Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
…, bare plugin agents, YAML name key, provider-prefixed aliases, inherited audit match Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
`safe-outputs.create-issue: false` is valid frontmatter: it disables the handler and stops default issue creation. smoke-claude-sub-agents (#70671) uses it to stay read-only. The allowed-labels rule passed the value to policyMapping, which accepts only a mapping, so the repository lint test TestRepositoryAgenticWorkflows/smoke-claude-sub-agents.lock.yml fails on main ("AST policy lint requires a mapping at safe-outputs.create-issue"). A boolean handler has no allowed-labels to check, so skip it. Other non-mapping values (strings, numbers) are still reported as malformed. Co-authored-by: Siva Kakarla <SivaKesava1@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>



Claude Code skipped every inline sub-agent gh-aw staged, because the generated agent file had no
name:. Typist failed withAgent type 'duplicate-type-finder' not foundand fell back togeneral-purpose. Claude and Codex also resolved only the main model: declared sub-agent models were never checked or recorded, and never reachedaw_info. This PR applies the Copilot pre-start contract from #70389 to Claude, and a declare-and-warn version of it to Codex. Copilot and Pi are unchanged.Claude: registration
extract_inline_sub_agents.cjsaddsname: "<block-name>"to the generated.claude/agents/<name>.mdcopy when the author left it out. This applies to Claude only; other engines' staging is unchanged.Claude: model resolution (
engine_subagent_models.cjs, called fromclaude_harness.cjsbefore spawn)resolve_model_alias.cjs, scoped to the engine's provider. The result is checked against the live/reflectcatalogue, with at most one shared refresh per batch.model.resolutionrecord, withroleset to the agent name.failedwithmodel_unavailableorcatalog_unavailable, emits the sharedModelAliasResolutionErrorand exits before the CLI starts. On success it writes the concrete ID into the generated workspace copy only.--agentsoverride has not been verified to preserve the agent's other settings. Instead, a warning says the resolved ID was not applied.inheritkeeps the main model and gets no record.haiku,sonnetandopusresolve through gh-aw's existing alias definitions, which are unchanged.Codex: declare and warn only (
codex_harness.cjs).codex/agents/*.mdfiles are resolved and recorded.aw_infodeclarationswriteSubagentModelDeclarationsnow emitsGH_AW_INFO_SUB_AGENT_MODELSfor Claude and Codex, with patterns limited to the engine's provider. Claudeinheritgets no patterns.Threat detection
isDetectionPhaseis true.smoke-claude-sub-agents(new, read-only,workflow_dispatch,bare: false)model: smallworkermarker-worker, which returns a fixed marker.model.resolutionrecord;nameand the resolved ID in the generated definition;Agentcall succeeds;subagent.startednames the worker, and nogeneral-purposefallback ran;subagent.configured.data.modelmatches the resolved ID;actions/*steps, like the other Claude smoke workflows.Tests
/reflectcatalogue, the initialization record, the failedAgentcall and session records.Out of scope / follow-ups
fallback-models,allowed-modelsand bare-mode behaviour.