Skip to content

Record failed model.resolution for Pi sub-agents whose model fails the /reflect check - #70385

Merged
SivaKesava1 merged 6 commits into
mainfrom
copilot/fix-sub-agent-model-resolution
Oct 20, 2026
Merged

SivaKesava1 merged 6 commits into
mainfrom
copilot/fix-sub-agent-model-resolution

Conversation

Copilot AI commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

When a Pi sub-agent's model fails the /reflect liveness check, Pi aborts startup. However, the session's model.resolution still says the model resolved (exact). This was seen in run 39325995892, where mini-whoami is pinned to the retired copilot/gpt-5.4-mini. preparePiSubagents records the resolution before the liveness check runs, and the sub-agent loop never replaced that record when the check failed. The main-model path already handles this case.

Change (actions/setup/js/pi_models_json.cjs)

  • In the sub-agent loop, a ModelAliasResolutionError from validatePiModelAvailability now records failed for the role, then rethrows:
    { status: "failed", cause: error.reason, resolved_model: null, provider: error.provider || null }
    • It uses the same configured_model as the earlier record, so recordModelResolution replaces the exact record instead of adding a second one.
  • Unlike the main path, there is no !routingRequired guard. Sub-agent records are written in routed runs too, and run 39325995892 was routed.
  • Every role whose modelId matches the failing id is marked failed. The loop checks each model id only once (seen), so without this a second role on the same dead model would keep exact.
  • The main-model path and the shared resolver's exact meaning are unchanged.

Tests (pi_models_json.test.cjs)

  • New fixture: fixtures/awf-reflect-pi-routed-39325995892.json is the real sandbox/firewall/awf-reflect.json from that run's agent artifact, reformatted with jq .. Content is unchanged: models_fetch_complete: true, and 36 Copilot models with no gpt-5.4-mini.
  • Cases:
    • A dead pin is recorded as failed / model_unavailable, in routed and unrouted runs.
    • A live pin (claude-haiku-4.5) stays exact.
    • With one live and one dead sub-agent, only the dead one becomes failed, in both declaration orders. The order is set by the test (pinned readdirSync), because directory order depends on the filesystem.
    • Two sub-agents sharing a dead model are both failed.

Not yet done

…eflect availability

Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix sub-agent model resolution when failing liveness check Record failed model.resolution for Pi sub-agents whose model fails the /reflect check Oct 19, 2026
Copilot AI requested a review from SivaKesava1 October 19, 2026 23:21
@SivaKesava1
SivaKesava1 marked this pull request as ready for review October 19, 2026 23:38
Copilot AI max review requested due to automatic review settings October 19, 2026 23:38
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Ponytail Reviewer

Outcome: reported_no_action

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.

Generated from Ponytail Reviewer · gpt56 · 9.23 AIC · ⌖ 6.23 AIC · ⊞ 14.7K · ◷

Generated by Ponytail Reviewer for #70385

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

Design Decision Gate 🏗️

Outcome: reported_no_action

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.

Generated from Design Decision Gate 🏗️ · opus55 · 11.2 AIC · ⌖ 31.9 AIC · ◇ 15.6 AIC · ⊞ 1.9K · ◷

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel

Outcome: reported_no_action

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.

Generated from Test Quality Sentinel · sonnet50 · 13.3 AIC · ⌖ 15.1 AIC · ◇ 6.63 AIC · ⊞ 12.6K · ◷

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review summary

No actionable blocking issues found in the changed lines. The new failure-recording path consistently replaces prior sub-agent resolution records, handles routed and unrouted execution, and the added tests cover live, unavailable, mixed, and shared-model cases. Static review only; tests were not run.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt50 · 2.86 AIC · ⌖ 0.489 AIC · ⊞ 1.6K
Comment /review to run again

@SivaKesava1

Copy link
Copy Markdown
Collaborator

Verification at ef4e7c2090:

  • End to end: smoke-pi-routed on this branch, run 39342976530. As expected, Pi refuses to start on the dead mini-whoami pin (fatal: Pi model "gpt-5.4-mini" is not advertised …). The session now records:

    • mini-whoami: {configured_model: copilot/gpt-5.4-mini, resolved_model: null, provider: copilot, status: failed, cause: model_unavailable}
    • haiku-whoami: {configured_model: copilot/claude-haiku-4.5, resolved_model: claude-haiku-4.5, status: exact} (unchanged)

    The smoke's U3 check now fails with the cause (FAIL U3 role mini-whoami status is failed (cause model_unavailable)). Before this PR it passed on a false exact record (run 39325995892). The routed run still wrote the sub-agent records, as required. The smoke stays red until its pin is replaced (tracked in [model-inventory] Model alias inventory - action required - 2026-10-19 #70367).

  • Tests: at this commit, locally, pi_models_json.test.cjs, pi_subagent_config.test.cjs and resolve_model_alias.test.cjs pass, including the 7 sub-agent cases. The fixture is the unmodified reflect capture with no credentials. The diff is 3 files. PR checks pass (JS shards 1–4, typecheck, lint).

  • One non-blocking note: when a role has no declaredModel, the fallback configured_model is ${nativeProvider}/${modelId}, where modelId is the main model's id, not failed.modelId. In practice this can't trigger: a sub-agent without its own model shares the main model, which is checked first and skipped through seen. Records are replaced by engine and role, so it can't misreplace either. For consistency with pi_subagent_config.cjs, which records agent.declaredModel || parentModel, it could use failed.declaredModel || configuredModel.

Ready for review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Required end-to-end smoke evidence confirming session records and U3 failure reporting remains pending.

No open issues 🎉

What changed in this PR

Corrects Pi sub-agent model-resolution reporting when /reflect rejects a model, addressing #70384.

Changes:

  • Marks every role sharing the unavailable model as failed before rethrowing.
  • Adds regression coverage for routed/unrouted runs, live models, declaration order, and shared models.
  • Adds the recorded /reflect fixture from run 39325995892.
File Description
actions/​setup/​js/​pi_models_json.test.cjs Tests sub-agent resolution records across availability scenarios.
actions/​setup/​js/​pi_models_json.cjs Replaces successful resolution records when availability validation fails.
actions/​setup/​js/​fixtures/​awf-reflect-pi-routed-39325995892.json Supplies the recorded model catalogue for regression tests.

Review effort: Max

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /tdd and /codebase-design on the two changed hunks in pi_models_json.cjs and the new sub-agent test suite. The fix is correctly scoped (replaces the stale exact record via the existing engine+role dedupe in recordModelResolution, and correctly omits the !routingRequired guard that the main-model path has, since routed sub-agent records should still be written). One test-coverage gap and one maintainability concern below; both are fixable without redesigning the change.

📋 Key Themes & Highlights

Key Themes

  • Untested fallback branch: the failed.declaredModel || ${nativeProvider}/${modelId}`` fallback in pi_models_json.cjs:545 (used when a sub-agent inherits the parent model) has no covering test — all four new cases pin `declaredModel` explicitly.
  • Re-derived value instead of reused value: that same fallback reconstructs a string (${nativeProvider}/${modelId}) that preparePiSubagents already computed once as requested/parentModel, but never attached to the returned agent object. Reusing it instead of re-deriving it would remove a source of future drift.

Positive Highlights

  • ✅ Correctly reuses the existing engine+role replace-not-append semantics in recordModelResolution, so the model.resolution “one event per role” contract (T-UAS-074 in the unified-session spec) still holds.
  • ✅ Deliberately diverges from the main-model path by omitting the !routingRequired guard, with a clear rationale in the PR description and inline comment, and the routed case (run 39325995892) is exactly the real-world scenario it fixes.
  • ✅ "Mark every sub-agent sharing the dead model failed" and "only the dead one, not the live one" are both tested, closing the gap the PR description calls out explicitly.
  • ✅ Good provenance: the recorded /reflect fixture and run links give concrete evidence for the pinned-model-retirement scenario.

Note on scope

The PR description flags the end-to-end smoke-pi-routed run as not yet done. That's a reasonable gap to leave for a follow-up since the unit tests already exercise the exact recorded fixture from the failing run, but it's worth confirming before merge per the author's own checklist.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 139.8 AIC · ⌖ 15.1 AIC · ⊞ 10.2K
Comment /matt to run again

Comment thread actions/setup/js/pi_models_json.cjs Outdated
Comment thread actions/setup/js/pi_models_json.test.cjs
Co-authored-by: SivaKesava1 <11771739+SivaKesava1@users.noreply.github.com>

Copilot AI commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Verification at ef4e7c2090:

  • End to end: smoke-pi-routed on this branch, run 39342976530. As expected, Pi refuses to start on th...

Done in 12ef285. The fallback is now failed.declaredModel || parentModel, and parentModel is the same value passed to preparePiSubagents. I didn't add a test because a sub-agent without its own model: can't reach this path. A code comment explains why: it shares the main model, which is checked first and skipped via seen. The PR description now includes run 39342976530. The branch already had all of main.

make fmt and make lint passed. The 3 Pi test files pass (183 tests). make agent-finish stopped at test-all on 5 tests in other files. Four of them fail the same way with this change stashed: 2 in copilot_sdk_mcp_config (symlink permissions in the sandbox) and 2 in work_queue_control_performance (30s timeouts). The fifth, awf_reflect_summary, passes when run alone. Because of that stop, the steps after test-all didn't run.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes.
  2. Review (actions/setup/js/pi_models_json.cjs:545): [/codebase-design] The fallback failed.declaredModel || \\${nativeProvider}/\${modelId}` duplicates the main-path shape (${nativeProvider}/${modelId} built from the *parent's resolved model, not the failing sub-agent's). When a sub-agent *inherits* the parent model (declaredModelundefined) and that inherited model is the one that fails, this correctly reconstructsparentModel. But preparePiSubagentsalready computed and used exactly this string asrequested(seepi_subagent_config.cjs:64) — it just never stored it on the returned agent object. Re-deriving it here from two different-but-related variables (nativeProvider, modelId) is fragile: if either ever diverges from how parentModelwas built when sub-agents were prepared,configured_model` would silently point at the wrong value for inherited-model sub-agents. - Record failed model.resolution for Pi sub-agents whose model fails the /reflect check #70385 (comment)
  3. Review (actions/setup/js/pi_models_json.test.cjs:756): [/tdd] All four new cases pin declaredModel explicitly on the dying sub-agent. There is no case for a sub-agent that inherits the parent model (no model: frontmatter) where that inherited model is the one that fails /reflect. The pi_models_json.cjs:545 fallback (failed.declaredModel || \${nativeProvider}/${modelId}``) is exactly the branch this would exercise, and it is currently untested. - Record failed model.resolution for Pi sub-agents whose model fails the /reflect check #70385 (comment)

Push the necessary fixes, reply to each listed review thread and resolve it when addressed. Ignore feedback already answered or resolved. Use the pr-finisher skill and stop when only human review or CI remains; do not trigger CI.

Sous-chef state: f98fb736bd61b24beaebe622171e52e768ab0e9ecd8581ff966470699c020b18

Generated by 👨‍🍳 PR Sous Chef · pi · haiku45 · 12.4 AIC · ⌖ 7.06 AIC · ⊞ 1.1K · ◷
Comment /souschef to run again

…-model-resolution

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes....

Merged latest main using make merge-main (d0e4c77) and pushed both review fixes in f330cc4. Replied to each linked thread. Build, all 344 workflow recompilations, formatting, repository lint, final impacted-test gate, and 185 focused tests pass; secret scan is clean. CodeQL found no alerts; a separate read-only review found no significant issues after automated review hit an unavailable model. Thread resolution is not supported by the exposed tools and local gh has no credentials, so both addressed threads still need manual resolution. No CI was triggered; CI on the new HEAD remains unverified.

Copilot AI requested a review from gh-aw-bot October 20, 2026 01:36
@SivaKesava1
SivaKesava1 dismissed github-actions[bot]’s stale review October 20, 2026 06:20

Both points are fixed in f330cc4: preparePiSubagents keeps the requested model on each sub-agent and the failed record reuses it, and tests cover sub-agents that inherit a live or an unavailable parent model. Verified locally at f330cc4 (Pi tests pass); review threads resolved.

@SivaKesava1
SivaKesava1 merged commit e81ab38 into main Oct 20, 2026
3 of 4 checks passed
@SivaKesava1
SivaKesava1 deleted the copilot/fix-sub-agent-model-resolution branch October 20, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pi sub-agent model.resolution stays 'exact' when the model fails the /reflect liveness check

4 participants