feat(#347): promote utility agents to per-agent model: frontmatter (Wave 2 PR 4)#361
Conversation
…(Wave 2 PR 4) Per AgDR-0050 § Axis 2, the framework's 5 utility sub-agents drop the default model: inherit (which pays the parent thread's rate) for explicit per-agent baselines optimised for the work each does. Hakim was promoted in PR 3 (#360) as part of the Hatim→Hakim consolidation; this commit finishes the remaining 4. Frontmatter changes (each carries the # routing-config:override comment inside the YAML frontmatter so block-agent-routing-drift.sh accepts the intentional inherit→<model> framework-default change): Rex (code-reviewer) inherit → opus AgDR-0050 § Axis 2 line 50 (PR diff review + handbook reasoning depth) Munir (dependency-auditor) inherit → sonnet AgDR-0050 § Axis 2 line 63 (pattern-matching across package files) Tariq (pr-manager) inherit → sonnet AgDR-0050 § Axis 2 line 64 (tool-call-heavy + PR body narrative quality) Idris (ticket-manager) inherit → sonnet AgDR-0050 § Axis 2 line 65 (schema-conforming output + interactive interview) Local-routing candidate per #348 spike. No body / role-file / count changes — the 23-agent count is unchanged, just 4 model values + 4 YAML comments. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…PR 4 Locks in the post-PR-4 utility-agent baselines so a future regression (e.g. someone reverts a model: line to inherit, or strips the escape-hatch comment) trips a test rather than silently slipping through. test_agent_wrap_shape.sh: - Added UTILITY_AGENTS array (mirror of ROLE_AGENTS, format "<slug>:<model>:<persona>"). Covers all 5 utility agents including Hakim (already promoted to opus in PR 3). - New "Invariant 4b" block asserts each utility agent has (a) explicit model: matching AgDR-0050 § Axis 2, (b) correct persona_name, and (c) a # routing-config:override comment in YAML frontmatter (the drift-guard escape hatch required for any inherit → <model> change). - Awk regex uses POSIX [[:space:]] / [^[:space:]] rather than \S — \S isn't portable across awk implementations (bug found while extending this test; macOS BSD awk silently treated \S as a literal). test_agent_routing_sync_and_drift.sh: - Added a ticket-manager fixture to make_fork (model: sonnet, escape-hatch comment, persona Idris). The fixture proves the routing-sync mechanism doesn't distinguish utility vs role-derived agents — same hook, same rewrite, same snapshot path. - New CASE 8 exercises a utility-agent override (ticket-manager sonnet → opus). Asserts: agent frontmatter flips to opus, qa-engineer is untouched (single-override scope), the framework-defaults snapshot records the sonnet baseline (not the opus override). Both tests PASS at the new HEAD. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
atlas-apex
left a comment
There was a problem hiding this comment.
Code Review: PR #361
Commit: 637f2d9649434b8b99dd88cd1de99a088a41d8f3
Summary
Wave 2 PR 4 of #347. Promotes the remaining 4 utility sub-agents off model: inherit to explicit per-agent baselines per AgDR-0050 § Axis 2, completing the 5/5 utility-agent matrix (Hakim landed in PR #360). Two commits: the 4 frontmatter edits + 2 test extensions (UTILITY_AGENTS array with Invariant 4b block + CASE 8 in the routing smoke test). Strict scope: 6 files, +131 / −5.
Checklist Results
- Architecture & Design: PASS
- Code Quality: PASS
- Testing: PASS — both extended test suites green at HEAD (18 wrap-shape PASS lines + 8/8 routing cases)
- Security: N/A (no auth / secrets / crypto / user-data surface)
- Performance: N/A
- PR Description & Glossary: PASS — every bullet narrative; Glossary has 4 well-explained terms (UTILITY_AGENTS array, escape-hatch comment, Axis 2 baseline vs adopter override, POSIX awk vs
\S) - Summary Bullet Narrative: PASS — each bullet answers what + why, ~3-5 sentences
- Technical Decisions (AgDR): N/A — AgDR-0050 is cited 9 times in the PR body and governs this entire 5-PR series; the model assignments execute its § Axis 2 matrix rather than making new decisions
- Adopter Handbooks: N/A — diff is
.mdfrontmatter +.shtest scripts; no language handbooks fire, no findings againstarchitecture/orgeneral/always-load handbooks
Issues Found
None.
Verifications performed
-
AgDR-0050 § Axis 2 line numbers — confirmed against
docs/agdr/AgDR-0050-agent-runtime-overhaul.mdlines 50, 63, 64, 65. Exact text from each line:- L50:
| Code Reviewer (Rex) | opus | PR diff review + handbook reasoning (existing utility) |→ Rex inherit → opus - L63:
| Dependency Auditor (Munir, utility) | sonnet | Pattern-matching across package files |→ Munir inherit → sonnet - L64:
| PR Manager (Tariq, utility) | sonnet | Tool-call-heavy + narrative-quality PR bodies |→ Tariq inherit → sonnet - L65:
| Ticket Manager (Idris, utility) | sonnet | Schema-conforming output + interactive interview |→ Idris inherit → sonnet
All 4 frontmatter model values match the cited lines exactly.
- L50:
-
Escape-hatch comment shape — ran the live drift-guard regex (
^[[:space:]]*#[[:space:]]*routing-config:override[[:space:]]+[^[:space:]]) against each of the 4 modified agent files inside its YAML frontmatter; all 4 match with non-empty reason text. The comment is on line 2 (inside---/---) in each file — first thing the drift guard sees, no parser dependency on ordering. Each comment names the persona, AgDR line number, and rationale. -
UTILITY_AGENTSarray completeness — array covers 5 entries (Rex, Hakim, Munir, Tariq, Idris) with model + persona triples matching AgDR-0050. Hakim's inclusion (already promoted in PR #360) is the right call — locking in the post-Wave-2 baseline so a future revert toinheriton any of the 5 trips this test, not just the 4 promoted today. Test ran at HEAD: 5/5 utility-agent PASS lines printed, plus 13 ROLE_AGENTS and 19 ROLE_CLASSES PASS. -
POSIX awk fix — confirmed on macOS BSD awk:
echo "abc def" | awk '/\S/ { print "matched-S" } /[^[:space:]]/ { print "matched-POSIX" }'prints onlymatched-POSIX. The fix is correct. Scanned the broader hook/agent test suite for any other\S/\W/\DPerl-style classes in awk blocks — zero remaining. No follow-up needed; the gotcha is contained to this PR's new awk block. -
CASE 8 tightness — the case asserts FOUR independent properties:
after_tm = opus(override applied)after_qa = haiku(single-override scope — unrelated agent untouched)- Banner reports
applied 1 agent-routing override(exactly 1, not 0 / not 2+) - Framework-defaults snapshot records
"ticket-manager":"sonnet"(pre-override baseline preserved, not the post-override opus)
All four are load-bearing for the "utility override mechanism doesn't drift or lose baseline" invariant. Theqa-engineer = haikuassertion is the load-bearing single-override-scope check — well-chosen, since it's the most-different model (haiku vs opus) and would catch any wholesale-rewrite bug.
-
Scope discipline —
gh pr diff --name-onlyreturns exactly 6 files matching the PR description's claim. No drive-bys to CLAUDE.md / AGENTS.md / site / docs. -
AGENTS.md refresh — grepped: claims "23 sub-agents" (post-Hakim consolidation), no "inherit" references. Live count of
model:lines: 6 opus + 15 sonnet + 2 haiku = 23. Matches. No refresh needed. -
Drift guard pre-commit cleanliness —
git log 0af3288..HEAD --format='%H %s'shows two clean commits with no revert-and-restage churn. Diff-stat per commit: 0961572 = 4 agent files only; 637f2d9 = 2 test files only. Both passed the drift guard on first try. -
CI —
gh pr checks 361: all checks pass (verify-ticket-id, lychee, markdownlint, shellcheck, site-counts-drift).
Suggestions
None blocking. Two non-blocking observations for context:
- nit: the comment on
test_agent_wrap_shape.shline 253 still mentions\Sto explain the gotcha. That's fine and informative — leaving it as-is preserves the institutional memory for whoever next reaches for\Sin an awk block. No action. - nit: AgDR-0050 § Axis 2's matrix header says "24-entry default matrix" (line 112) but post-Hatim→Hakim consolidation the count is 23. This is pre-existing doc debt from PR #360, not introduced by this PR — flagged in the PR-360 follow-up list ("AgDR-0018 lines 51+92 doc-debt") and intentionally out-of-scope here. No action for this PR.
Verdict
APPROVED
The PR executes Wave 2 PR 4 of #347 to-spec: 4 frontmatter promotions matching the AgDR-0050 § Axis 2 baselines on the cited lines, drift-guard escape-hatch comments shaped correctly, two-test extension that mechanically locks in both the post-PR-4 invariants and the utility-vs-role-agent equivalence in the routing mechanism. POSIX awk fix is a clean side-fix contained to the new code (no other \S regex regressions in the suite). Scope discipline is exemplary — 6 files, both commits clean through the drift guard, no drive-bys.
The operator should write the approval marker on Rex's behalf per sandbox restriction:
echo 637f2d9649434b8b99dd88cd1de99a088a41d8f3 > .claude/session/reviews/361-rex.approved
Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: 637f2d9649434b8b99dd88cd1de99a088a41d8f3
…ave 2 PR 4) (#361) * feat(#347): promote 4 utility agents to per-agent model: frontmatter (Wave 2 PR 4) Per AgDR-0050 § Axis 2, the framework's 5 utility sub-agents drop the default model: inherit (which pays the parent thread's rate) for explicit per-agent baselines optimised for the work each does. Hakim was promoted in PR 3 (#360) as part of the Hatim→Hakim consolidation; this commit finishes the remaining 4. Frontmatter changes (each carries the # routing-config:override comment inside the YAML frontmatter so block-agent-routing-drift.sh accepts the intentional inherit→<model> framework-default change): Rex (code-reviewer) inherit → opus AgDR-0050 § Axis 2 line 50 (PR diff review + handbook reasoning depth) Munir (dependency-auditor) inherit → sonnet AgDR-0050 § Axis 2 line 63 (pattern-matching across package files) Tariq (pr-manager) inherit → sonnet AgDR-0050 § Axis 2 line 64 (tool-call-heavy + PR body narrative quality) Idris (ticket-manager) inherit → sonnet AgDR-0050 § Axis 2 line 65 (schema-conforming output + interactive interview) Local-routing candidate per #348 spike. No body / role-file / count changes — the 23-agent count is unchanged, just 4 model values + 4 YAML comments. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(#347): cover utility-agent invariants + routing case for Wave 2 PR 4 Locks in the post-PR-4 utility-agent baselines so a future regression (e.g. someone reverts a model: line to inherit, or strips the escape-hatch comment) trips a test rather than silently slipping through. test_agent_wrap_shape.sh: - Added UTILITY_AGENTS array (mirror of ROLE_AGENTS, format "<slug>:<model>:<persona>"). Covers all 5 utility agents including Hakim (already promoted to opus in PR 3). - New "Invariant 4b" block asserts each utility agent has (a) explicit model: matching AgDR-0050 § Axis 2, (b) correct persona_name, and (c) a # routing-config:override comment in YAML frontmatter (the drift-guard escape hatch required for any inherit → <model> change). - Awk regex uses POSIX [[:space:]] / [^[:space:]] rather than \S — \S isn't portable across awk implementations (bug found while extending this test; macOS BSD awk silently treated \S as a literal). test_agent_routing_sync_and_drift.sh: - Added a ticket-manager fixture to make_fork (model: sonnet, escape-hatch comment, persona Idris). The fixture proves the routing-sync mechanism doesn't distinguish utility vs role-derived agents — same hook, same rewrite, same snapshot path. - New CASE 8 exercises a utility-agent override (ticket-manager sonnet → opus). Asserts: agent frontmatter flips to opus, qa-engineer is untouched (single-override scope), the framework-defaults snapshot records the sonnet baseline (not the opus override). Both tests PASS at the new HEAD. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
model: inheritto explicit AgDR-0050 § Axis 2 baselines: Rex (code-reviewer.md) inherit → opus per Axis 2 line 50 (PR diff review + handbook reasoning depth); Munir (dependency-auditor.md) inherit → sonnet per line 63 (pattern-matching across package files); Tariq (pr-manager.md) inherit → sonnet per line 64 (tool-call-heavy + PR body narrative quality); Idris (ticket-manager.md) inherit → sonnet per line 65 (schema-conforming output + interactive interview). Hakim was already promoted to opus in PR feat(#347)!: Hatim→Hakim consolidation + security + data sub-agents (Wave 2 PR 3) #360 as part of the Hatim→Hakim consolidation, completing the 5/5 utility-agent matrix.# routing-config:overrideescape-hatch comment inside the YAML frontmatter soblock-agent-routing-drift.shaccepts the intentional inherit→ framework-default change rather than blocking it as adopter-leak. The comment names the agent, the AgDR line number, and the rationale — visible + auditable per AgDR-0040's escape-hatch convention. Each commit landed cleanly through the drift guard on the first attempt (no PR-3-style "wrong comment style" retry).test_agent_wrap_shape.shextended with aUTILITY_AGENTSarray and a new "Invariant 4b" block that asserts each utility agent has (a) explicitmodel:matching Axis 2, (b) correctpersona_name, and (c) the escape-hatch comment in YAML frontmatter. Locks in the post-PR-4 baselines so a future revert-to-inherit trips the test instead of silently slipping. Bonus fix: the new awk uses POSIX[[:space:]]/[^[:space:]]instead of\S—\Sisn't portable across awk implementations (macOS BSD awk silently treated it as a literal, masking the regex bug). Other awk blocks in the test file already use POSIX classes, so this is a one-time gotcha that's now closed.test_agent_routing_sync_and_drift.shextended with a ticket-manager fixture inmake_fork+ new CASE 8 that exercises a utility-agent override (ticket-manager sonnet → opus). Proves the routing-sync mechanism doesn't distinguish utility from role-derived agents — same hook, same rewrite, same snapshot path. 8/8 cases PASS.Testing
bash .claude/agents/tests/test_agent_wrap_shape.sh— PASS at 13 ROLE_AGENTS + 5 UTILITY_AGENTS + 19 ROLE_CLASSES.bash .claude/hooks/tests/test_agent_routing_sync_and_drift.sh— PASS at 8/8 cases (case 8 new this PR).0961572model changes +637f2d9test extensions) without blocking — verifies the escape-hatch comment placement is correct on all 4 utility files.agent-routing.yamloverride (e.g.ticket-manager: model: opus) and confirm SessionStart sync rewrites.claude/agents/ticket-manager.md'smodel:toopus;git checkoutthen restoressonnet. Not blocking.Glossary
ROLE_AGENTSintest_agent_wrap_shape.sh. Format"<slug>:<expected_model>:<expected_persona_name>". Locks in AgDR-0050 § Axis 2 baselines for the 5 utility agents (Rex, Hakim, Munir, Tariq, Idris). Different fromROLE_AGENTSbecause utility agents don't have a canonicalroles/<dept>/<slug>.mdfile — they're framework primitives, not role-derived personas.# routing-config:override <reason>escape hatchblock-agent-routing-drift.sh(regex^[[:space:]]*#[[:space:]]*routing-config:override[[:space:]]+\S). Used for intentional framework-default changes — e.g. this PR's 4 inherit→ bumps. The comment ships with the PR and makes the deliberate change visible. HTML<!-- -->form does NOT match — only the#-prefixed YAML comment form does (footgun from PR #360 is now documented in the test).agent-routing.yamlin their private portfolio repo (or gitignored single-fork). The SessionStart sync hook (apply-agent-routing.sh, from #357) rewrites the agent file'smodel:line at session start; the drift guard prevents that rewrite from leaking into a commit.\Sawkand other POSIX-strict implementations don't support\S(the Perl-style non-whitespace shorthand). They silently treat\Sas a literal "S" character. The new test code uses[^[:space:]]instead — same semantics, portable across all awk implementations.Refs #347 (4/5 PRs now landed; PR 5 — role-trigger integration — is the last)
Refs #348 (Idris is one of three candidates for the local-routing feasibility spike)
Note for the next reviewer
This PR keeps strictly to scope: 4 frontmatter promotions + 2 test extensions. No drive-by cleanups, no doc rewrites, no CLAUDE.md / site / AGENTS.md changes (agent count is unchanged — only model values change). Rex's 5 non-blocking suggestions on PR #360 (consolidation-exception test, stale Hatim in test file comment, AgDR-0018 lines 51+92 doc-debt, prose inaccuracy at security-reviewer.md:17, HTML-comment drift-guard footgun docs) are intentionally NOT included in this PR — they're tracked as follow-ups for a Wave-3 cleanup PR or discrete chore tickets.