feat(#347)!: Hatim→Hakim consolidation + security + data sub-agents (Wave 2 PR 3)#360
Conversation
… (Wave 2 PR 3) Per AgDR-0050 § Axis 2 and the CONSOLIDATE decision on PR #347 PR 3, the existing security-reviewer.md utility agent is renamed from Hatim → Hakim and broadened from "PR-review-only" to the full Security Auditor scope (OWASP, threat modeling, SAST findings, auth/crypto diff review). The filename security-reviewer.md is preserved because /security-review, the auto-fire trigger in .claude/rules/role-triggers.md, and auto-code-review.sh all reference it. Breaking-change marker (!) because the persona name visible in chat output, PR review comments, and AgDR-0018 mapping changes. Files changed: - .claude/agents/security-reviewer.md — frontmatter: persona_name Hatim → Hakim, model: inherit → opus, description broadened. Carries a # routing-config:override comment in the YAML frontmatter so block-agent-routing-drift.sh accepts the intentional inherit→opus framework-default change. - 5 new role-aligned WRAP-shape agent files: head-of-security.md — Faisal, sonnet, isolated-work-class penetration-tester.md — Hamza, opus, isolated-work-class head-of-data.md — Khalil, sonnet, isolated-work-class data-analyst.md — Nadia, haiku, isolated-work-class (local-model routing candidate per #348 spike) data-engineer.md — Anwar, sonnet, in-flow-class - .claude/agents/tests/test_agent_wrap_shape.sh — ROLE_AGENTS extended. Net sub-agent count: 18 → 23. Site / docs count refreshes follow in commit 2. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…2 PR 3) Follows commit 558ef77 (consolidation + new agent files). Forward-looking references update; AgDR-0018's persona-name mapping collapses the security-reviewer / Hatim row into the broader Hakim entry. Files changed: - CLAUDE.md — "Agents | 18 sub-agents (5 utility + 7 engineering + 6 product-design). Growing to 24" → "23 sub-agents (5 utility incl. Hakim post-consolidation + 7 engineering + 6 product-design + 5 security-data)". Removes the "growing to 24" forward-claim — PR 3 lands 5 new agents + collapses Hatim/Hakim, so net is 23, not 24. - docs/agdr/AgDR-0018-persona-naming-convention.md — mapping table security-reviewer row updated from Hatim → Hakim (with consolidation annotation); roster sentence updated to note Hatim's retirement. - docs/architecture/apexyard-container.md — narrative Hatim → Hakim. - docs/multi-project.md — narrative Hatim → Hakim. - site/architecture.html + site/architecture.html.md.gen — layer card narrative bumped to 23 sub-agents with 5-utility + 18-dept-aligned decomposition (NB: the phrasing "18 dept-aligned agents" rather than "18 role-derived" sidesteps the site-counts drift regex which interprets "18 role" as a role-count claim — there are 19 distinct role files but only 18 role-derived AGENT files because security-reviewer.md is the consolidation exception that doesn't have a slug-matching role file). - site/index.html + site/index.md.gen — layer card narrative bumped. - site/llms-full.txt — narrative bumped + Hatim → Hakim sweep + the multiline "(12 sub-agents incl. Rex)" → "(23 sub-agents incl. Rex)". - site/skills.html + site/skills.md.gen — narrative Hatim → Hakim. Intentional non-sweep (historical record kept): - docs/agdr/AgDR-0050-agent-runtime-overhaul.md — the AgDR that proposed the consolidation; keeps both Hatim and Hakim references as the audit trail of the decision. - docs/spikes/claude-model-tier-routing.md — historical spike snapshot. - roles/security/security-auditor.md — canonical role file, not touched per brief (it's already Hakim there; no Hatim references). Site-counts smoke test PASSES at 53 skills / 31 hooks / 19 roles / 23 sub-agents. 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 #360
Commit: 314dd22401675ca1d0dc29c9d3053a6bb06e62d7
Summary
Wave 2 PR 3 of #347: consolidates the existing utility security-reviewer agent from Hatim → Hakim (renaming persona, broadening description from PR-review-only to full Security Auditor scope, bumping model: inherit → opus per AgDR-0050 § Axis 2 with a # routing-config:override escape-hatch comment in the YAML frontmatter), adds the 5 remaining role-aligned WRAP-shape sub-agents for security + data depts (Faisal / Hamza / Khalil / Nadia / Anwar), extends the WRAP-shape test matrix, and sweeps Hatim → Hakim across forward-looking docs while preserving historical record in AgDRs + spike memos.
Checklist Results
- Architecture & Design: Pass
- Code Quality: Pass
- Testing: Pass
- Security: Pass (no security-relevant code paths changed; only persona metadata + agent files)
- Performance: N/A (markdown only)
- PR Description & Glossary: Pass — glossary is genuinely useful (the
# routing-config:overrideentry + dept-aligned/role-derived disambiguation will save the next reader real archaeology time) - Summary Bullet Narrative: Pass (advisory) — every bullet answers what + why
- Technical Decisions (AgDR): Pass — covered by AgDR-0050 § Axis 2 (model matrix), AgDR-0018 (persona naming), AgDR-0040 (escape-hatch convention)
- Adopter Handbooks: N/A (no handbooks loaded; diff is framework files only)
Issues Found
One real miss in the Hatim → Hakim sweep — AGENTS.md:12 (recommend fixing in this PR; 1-line change):
.claude/agents/ — 5 sub-agents (Rex code-reviewer, Hatim security-reviewer, ...)
Two problems on that line:
- Stale
Hatimreference (not on the PR description's "intentional non-sweep" list) - Stale count:
5 sub-agents— should be23per the count refresh applied elsewhere in this PR
AGENTS.md is a forward-looking entry doc for visiting AI agents (Cursor / Aider / Cline / etc.) — same forward-looking class as CLAUDE.md which WAS updated. Unlike docs/agdr/AgDR-0050-*.md, docs/spikes/claude-model-tier-routing.md, or roles/security/security-auditor.md:122-124 (all on the deliberate-non-sweep list because they're either historical audit trail or load-bearing prose punted to follow-up), AGENTS.md has no reason to retain the stale state.
Suggested fix:
- - `.claude/agents/` — 5 sub-agents (Rex code-reviewer, Hatim security-reviewer, Munir dep-auditor, Tariq PR-manager, Idris ticket-manager)
+ - `.claude/agents/` — 23 sub-agents (5 utility: Rex code-reviewer, Hakim security-reviewer, Munir dep-auditor, Tariq PR-manager, Idris ticket-manager; plus 18 dept-aligned agents across engineering / product / design / security / data)This is the only real blocker-class miss. Everything else verified clean.
Suggestions (non-blocking)
-
Hardening the consolidation-exception invariant in the WRAP-shape test. The deliberate omission of
security-reviewer.mdfromROLE_AGENTSis the right call (filename != role-slug — folding it into the matrix would require special-casing the dept inference). However,security-reviewer.md's consolidated state (persona_name: Hakim,model: opus, broadened description) is currently unchecked by any test. A separate consolidation-exception block intest_agent_wrap_shape.shwould harden the invariant against future regression — pattern:# Consolidation exception: security-reviewer.md keeps the non-standard filename # (referenced by /security-review) but must still have the consolidated metadata. assert_field .claude/agents/security-reviewer.md "persona_name" "Hakim" assert_field .claude/agents/security-reviewer.md "model" "opus"
File as a follow-up chore in Wave 2 PR 4 or Wave 3 — not worth holding this PR.
-
.claude/agents/tests/test_agent_wrap_shape.sh:13comment-block staleness. The leading wave-history comment lists PR 4's scope as(Rex / Hatim / Munir / Tariq / Idris). Post-consolidation, PR 4's utility-agent set isRex / Hakim / Munir / Tariq / Idris. Pure doc-debt in a comment block, but worth folding into PR 4 itself when that work lands. -
docs/agdr/AgDR-0018-persona-naming-convention.mddoc-debt. Two follow-up items that fall outside the PR description's stated mapping-table + roster-sentence scope:- Line 51 heading "Full mapping (24 personas, Rex unchanged + 23 renamed)" — post-consolidation the framework has 23 distinct persona names. Header is now slightly stale.
- Line 92 "Shield → Hatim, Guardian → Munir" — the Shield → Hatim history is now Shield → Hakim post-consolidation (or could be reframed to keep the historical "Shield" naming + note both transitions).
Neither is blocking; both are doc-debt in the same record that the operator explicitly chose to keep historical. File as a follow-up if/when AgDR-0018 needs another touch.
-
Inaccuracy in the consolidation note prose (
.claude/agents/security-reviewer.md:17). The body claimsauto-code-review.shreferences thesecurity-reviewer.mdfilename — actuallyauto-code-review.shdoesn't grep for that filename anywhere; only.claude/skills/security-review/SKILL.md:23does (and the/security-reviewskill alone is sufficient justification for the filename preservation). Cosmetic prose fix; doesn't affect runtime. -
Drift-guard footgun documentation. The drift hook only recognises
^[[:space:]]*#for the override comment — HTML<!-- -->form silently doesn't match. PR description flagged that the operator hit this twice while authoring this PR. Worth a one-line note in the drift-guard hook's leading comment block to save future authors the same friction. Follow-up chore.
Verdict
APPROVED (with the AGENTS.md:12 fix strongly recommended before merge — 1-line, in-scope of this PR's stated "Hatim → Hakim sweep across forward-looking docs" bullet).
The consolidation is surgically correct: # routing-config:override is properly placed inside the YAML frontmatter (line 2, between opening --- and name:), the broadened description is accurate to the Security Auditor scope, the operational HARD STOP block is preserved (it IS load-bearing — the role file at roles/security/security-auditor.md doesn't carry it, so /security-review would otherwise lose the "submit a GitHub review before returning" enforcement). All 5 new agent files mirror Wave 1's thin-WRAP shape byte-for-byte (only name / description / model / persona_name / role-link path differ as expected). Test matrix entries match AgDR-0050 § Axis 2 (Faisal=sonnet, Hamza=opus, Khalil=sonnet, Nadia=haiku, Anwar=sonnet). Count math validates (23 = 5 + 7 + 6 + 5, confirmed via ls -1 .claude/agents/*.md). Both smoke tests pass at HEAD.
The "18 dept-aligned agents" phrasing in the site copy is a good defensive choice against the site-counts drift regex; the consequent need to explain it in the glossary is well-handled (the "dept-aligned vs role-derived" glossary entry is exactly the right shape).
PR description's Open Questions punt list is sound — the roles/security/security-auditor.md:122-124 forward-reference is genuinely load-bearing prose that needs more care than a 1-line replacement, and the /security-review "Shield" historical name is orthogonal to the consolidation.
Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: 314dd22401675ca1d0dc29c9d3053a6bb06e62d7
…llow-up) PR #360 Rex review caught AGENTS.md (the universal AI-agent entry doc at the repo root, alongside CLAUDE.md) carrying stale forward-looking claims: - "5 sub-agents" → "23 sub-agents" (5 utility + 18 dept-aligned) - "Hatim security-reviewer" → "Hakim security-reviewer/auditor" - "24+ shell scripts" → "31 shell scripts" (long-stale) - "52 slash commands" → "53 slash commands" (long-stale, two hits) AGENTS.md is forward-looking (GEO/AEO discovery surface for visiting AI agents) and was missed in the original Hatim → Hakim sweep + count refresh. Fixed in-PR per Rex's recommendation. Refs #347 Refs #360 (review) 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 #360 (re-review after follow-up)
Commit: 9ad45c0a940af156e377ed997a4c81217b0c0797
Summary
Re-review after the AGENTS.md follow-up commit (9ad45c0) addressing the in-PR fix recommended on prior HEAD 314dd22. Single-file 4-line diff to AGENTS.md at the repo root, refreshing stale counts and completing the Hatim → Hakim sweep that the original PR missed.
Verification
- ✓ Bumped numbers match actuals (verified locally on
9ad45c0):find .claude/skills -name SKILL.md | wc -l→ 53 (matches AGENTS.md:11 + :77)find .claude/hooks -maxdepth 1 -name '*.sh' ! -name '_lib*' | wc -l→ 31 (matches AGENTS.md:10)ls .claude/agents/*.md | wc -l→ 23 (matches AGENTS.md:12)
- ✓
bash .claude/hooks/tests/test_site_counts.sh→ PASS (53 / 31 / 19 across all scanned files) - ✓ No drive-by edits —
git show --stat 9ad45c0confirmsAGENTS.md | 8 ++++----only, single file, 4 insertions + 4 deletions - ✓ Diff matches the brief exactly: three lines under
.claude/{hooks,skills,agents}/description + one second-hit52 → 53at AGENTS.md:77 - ✓ Persona sweep landed: "Hatim security-reviewer" → "Hakim security-reviewer/auditor" + agent-decomposition phrasing now reads "5 utility … + 18 dept-aligned agents across engineering / product / design / security / data" (sidesteps the site-counts drift regex's
roles?matcher, consistent with the same phrasing used in commit314dd22forsite/andCLAUDE.md) - ✓ Commit message references the Rex review trail (
Refs https://github.com/me2resh/apexyard/pull/360#pullrequestreview) — audit trail preserved
Checklist Results
- Architecture & Design: Pass (docs-only refresh, no code paths affected)
- Code Quality: Pass
- Testing: Pass (site-counts test passes; AGENTS.md is not in
FILES_TO_SCANby design, but the test confirms no other claim regressed in the surrounding sweep) - Security: Pass
- Performance: Pass
- PR Description & Glossary: Pass (carried over from the original PR body; follow-up commit is referenced)
- Summary Bullet Narrative: Pass
- Technical Decisions (AgDR):Pass (no new decisions; the Hatim → Hakim CONSOLIDATE decision lives in AgDR-0050 § Axis 2 and is referenced in the original commit
558ef77) - Adopter Handbooks: N/A (no
handbooks/content matched the diff scope)
Issues Found
None. The follow-up commit precisely addresses the prior-review recommendation with no scope creep.
Suggestions
None for this PR. (The original PR body's "Open Questions for follow-up" already documents the two remaining doc-debt items — roles/security/security-auditor.md:122-124 forward-reference and .claude/skills/security-review/SKILL.md "Shield" reference — as separate cleanup PRs.)
Verdict
APPROVED (posted as --comment because gh pr review --approve blocks self-approval on the agent's own PR per sandbox; operator will write the marker on Rex's behalf per the documented workaround)
🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: `9ad45c0a940af156e377ed997a4c81217b0c0797`
…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>
…ad (Wave 2 PR 5) (#362) * feat(#347): class-aware role-trigger banner — HYBRID spawn vs in-thread (Wave 2 PR 5) Per AgDR-0050 § Axis 6, role triggers now emit one of two banner shapes depending on the matched role's **Class** value in its `## Activation mode` section: - isolated-work-class (12 roles: Heads-of-X, Tech Lead, QA Engineer, SRE, Security Auditor, Pen Tester, Product Analyst, Data Analyst) → banner instructs the agent to SPAWN the sub-agent via the Agent tool with `subagent_type: <slug>`. Isolated work benefits from isolated context + tool restriction. - in-flow-class (7 roles: Backend / Frontend / Platform Engineer, Product Manager, UI / UX Designer, Data Engineer) → banner instructs the agent to ADOPT the persona IN-THREAD by reading the role file. In-flow work loses too much shared context if spawned out-of-thread. Implementation: - New `lookup_role_class()` helper reads `**Class**:` from the role file at trigger time. Falls back to in-flow-class on any failure (missing file, missing section, unparseable value) — conservative fail-open. - New `agent_slug_for()` helper maps role-file path → agent subagent_type slug. 1:1 by default (`backend-engineer.md` → `backend-engineer`); the one exception is `security-auditor.md` → `security-reviewer` (the Hatim→Hakim consolidation from PR #360 kept the filename so `/security-review` + the auto-fire trigger keep working). - `emit_banner()` is restructured to look up class + slug from the file arg and emit the appropriate shape. Caller signatures unchanged — every call site still passes (role, file, reason); the helper does the class-aware fan-out. Tests (7 new cases, all PASS, 26/26 overall): - 4a: Security Auditor → isolated-work-class banner with the consolidation slug `security-reviewer` (NOT `security-auditor`). - 4b: Platform Engineer → in-flow-class banner. - 4c: Tech Lead → isolated-work-class. - 4d: QA Engineer → isolated-work-class (label-based trigger). - 4e: Backend Engineer prompted → in-flow-class. - 4f: UX Designer prompted → in-flow-class. - 4g: Pen Tester prompted → isolated-work-class. Existing 19 tests unchanged + still PASS — their regexes are broad enough to match both old and new banner shapes, so this is a strict extension (no behaviour regressions on the in-thread path). Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#347): document live HYBRID role-trigger banner shape (Wave 2 PR 5) `.claude/rules/role-triggers.md` mechanical-backstop section now describes both class-aware banner shapes (SPAWN for isolated-work-class, IN-THREAD adoption for in-flow-class), the 12-vs-7 role split per AgDR-0050 § Axis 6, and the one filename-vs-slug exception (security-auditor.md → security-reviewer agent slug, from PR #360's Hatim→Hakim consolidation). 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>
…sh-gate scope + multi-line drift (#364) * chore(#358): drop advertised-but-not-wired allowed_tools_override field PR #357 review caught that the agent-routing v1 schema advertised `allowed_tools_override` in three places — apply-agent-routing.sh's parser doc comment, agent-routing.yaml.example's schema preamble + Example F worked-example, and docs/multi-project.md's adopter schema table — but parse_routing() never emits the field, so adopters who set it get a silent no-op. Decision (recorded per #358 AC): DROP the field for v1, do NOT wire the parser branch. Rationale: - The full implementation needs parser emission (3 parsers) + a consumer-side `tools:` / `allowed-tools:` line rewrite + drift-guard awareness (tools-line differs from dev would need its own escape hatch). 50-100 line impl for a feature the docs explicitly mark as "advanced — use sparingly." - Adopters who actually need a per-fork tool-list override can edit the agent file directly with a `# routing-config:override <reason>` comment in YAML frontmatter — the same escape-hatch pattern that already handles model: overrides. Documented in the now-deprecation prose. - No migration tail: adopters who'd set `allowed_tools_override` were already getting a silent no-op; this PR makes that explicit in the schema docs. Files changed: - agent-routing.yaml.example - Schema preamble: replace the field's doc paragraph with a deprecation note pointing at the agent-file + escape-hatch path - Example F (the only worked example for the field): removed - .claude/hooks/apply-agent-routing.sh:115 - Parser doc comment: removed from the key-set list, added a one-line deprecation note pointing readers at the per-agent frontmatter path - docs/multi-project.md:549 - Schema table: removed the `allowed_tools_override` row - New post-table paragraph documenting the deprecation + the operator-facing escape-hatch alternative Closes #358 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#359): reconcile AgDR-0050 § Axis 4 push-gate scope with implementation PR #357 review flagged a doc/impl mismatch: AgDR-0050 § Axis 4 said the pre-push guard "sweeps before any push to a public-class remote," but block-agent-routing-drift.sh fires on ANY push regardless of remote class. Two possible fixes per #359: tighten the hook to check remote class, OR update the AgDR to match the broader-scope impl. Decision: UPDATE the AgDR (option 2, recommended in the ticket). Rationale: - The broader scope is actually the right design. Even a push to a private tracking branch can become a leak vector later if the operator changes the remote. Adopters staging a deliberate framework-default change should label it with the # routing-config:override escape hatch before pushing anywhere — not just before pushing to upstream. - Tightening the hook to inspect @{push} remote class would add code and a new failure mode (silent-pass on remote-resolution failure) without removing a real failure surface. Files changed: - docs/agdr/AgDR-0050-agent-runtime-overhaul.md § Axis 4 - Prose changed: "sweeps before any push to a public-class remote" → "sweeps before ANY push (not just to public-class remotes)" - New blockquote note explaining the resolution + linking to #359 Closes #359 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(#342): add multi-line lookback pass + self-test to test_site_counts.sh #342 § 2(b) flagged that test_site_counts.sh's per-line grep can't catch claims where the digit and noun straddle a line break (the shape that bit `site/llms-full.txt:131-133` before #360 cleaned it up by hand). This commit adds the missing pass per #342's prescription. Implementation: - New check_multiline_count() helper. Flattens consecutive whitespace (including newlines) via `tr '\n' ' ' | tr -s '[:space:]' ' '`, re-scans for `<digits> +<noun>`, dedupes against the per-line pass's hits (so a same-line claim already reported by check_count doesn't get double-counted), reports drift as `file:multiline` (no line number — the digit and noun aren't on the same line). - Second for-loop calls the new helper for the same noun-pattern list as the per-line check_count loop. - Self-test fixture at the end: synthesises a temp file with the exact `52\n slash commands` shape #342 § 2(b) describes, runs check_multiline_count against it inside `$(...)` (subshell-isolated so its DRIFT++ stays scoped), asserts the expected `DRIFT: file:multiline — claims 52 slash commands` output line. If absent, the detector is broken and the suite fails. Net coverage: the per-line + multi-line passes now together cover both same-line and cross-line drift, closing #342's § 2(b) gap. § 2(a)'s remaining synonym patterns (`enforcement scripts?` and `mechanical (enforcement )?(scripts?|hooks?)`) are NOT addressed here — they're additional noun-pattern entries, not a structural fix. Closing the structural gap is the higher-value half; remaining noun patterns can be added incrementally as future drift surfaces them. Files changed: - .claude/hooks/tests/test_site_counts.sh: - new check_multiline_count() function (~30 lines) - new for-loop applying it to the same 9 noun patterns as check_count - new self-test at end of file (~15 lines) that proves the detector fires on a known-bad cross-line fixture Test passes locally against current site/ files (which were swept clean post-#360). Sibling tests (test_agent_routing_sync_and_drift.sh 8/8, test_agent_wrap_shape.sh 13+5+19/37) unchanged + still pass. Closes #342 § 2(b). § 2(a) noun-pattern additions and § 1 one-time hygiene sweep are intentional carry-overs. 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>
…Wave 2 PR 3) (#360) * feat(#347)!: Hatim → Hakim consolidation + security & data sub-agents (Wave 2 PR 3) Per AgDR-0050 § Axis 2 and the CONSOLIDATE decision on PR #347 PR 3, the existing security-reviewer.md utility agent is renamed from Hatim → Hakim and broadened from "PR-review-only" to the full Security Auditor scope (OWASP, threat modeling, SAST findings, auth/crypto diff review). The filename security-reviewer.md is preserved because /security-review, the auto-fire trigger in .claude/rules/role-triggers.md, and auto-code-review.sh all reference it. Breaking-change marker (!) because the persona name visible in chat output, PR review comments, and AgDR-0018 mapping changes. Files changed: - .claude/agents/security-reviewer.md — frontmatter: persona_name Hatim → Hakim, model: inherit → opus, description broadened. Carries a # routing-config:override comment in the YAML frontmatter so block-agent-routing-drift.sh accepts the intentional inherit→opus framework-default change. - 5 new role-aligned WRAP-shape agent files: head-of-security.md — Faisal, sonnet, isolated-work-class penetration-tester.md — Hamza, opus, isolated-work-class head-of-data.md — Khalil, sonnet, isolated-work-class data-analyst.md — Nadia, haiku, isolated-work-class (local-model routing candidate per #348 spike) data-engineer.md — Anwar, sonnet, in-flow-class - .claude/agents/tests/test_agent_wrap_shape.sh — ROLE_AGENTS extended. Net sub-agent count: 18 → 23. Site / docs count refreshes follow in commit 2. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#347): sweep Hatim → Hakim + bump 12 → 23 sub-agent count (Wave 2 PR 3) Follows commit f6d624c (consolidation + new agent files). Forward-looking references update; AgDR-0018's persona-name mapping collapses the security-reviewer / Hatim row into the broader Hakim entry. Files changed: - CLAUDE.md — "Agents | 18 sub-agents (5 utility + 7 engineering + 6 product-design). Growing to 24" → "23 sub-agents (5 utility incl. Hakim post-consolidation + 7 engineering + 6 product-design + 5 security-data)". Removes the "growing to 24" forward-claim — PR 3 lands 5 new agents + collapses Hatim/Hakim, so net is 23, not 24. - docs/agdr/AgDR-0018-persona-naming-convention.md — mapping table security-reviewer row updated from Hatim → Hakim (with consolidation annotation); roster sentence updated to note Hatim's retirement. - docs/architecture/apexyard-container.md — narrative Hatim → Hakim. - docs/multi-project.md — narrative Hatim → Hakim. - site/architecture.html + site/architecture.html.md.gen — layer card narrative bumped to 23 sub-agents with 5-utility + 18-dept-aligned decomposition (NB: the phrasing "18 dept-aligned agents" rather than "18 role-derived" sidesteps the site-counts drift regex which interprets "18 role" as a role-count claim — there are 19 distinct role files but only 18 role-derived AGENT files because security-reviewer.md is the consolidation exception that doesn't have a slug-matching role file). - site/index.html + site/index.md.gen — layer card narrative bumped. - site/llms-full.txt — narrative bumped + Hatim → Hakim sweep + the multiline "(12 sub-agents incl. Rex)" → "(23 sub-agents incl. Rex)". - site/skills.html + site/skills.md.gen — narrative Hatim → Hakim. Intentional non-sweep (historical record kept): - docs/agdr/AgDR-0050-agent-runtime-overhaul.md — the AgDR that proposed the consolidation; keeps both Hatim and Hakim references as the audit trail of the decision. - docs/spikes/claude-model-tier-routing.md — historical spike snapshot. - roles/security/security-auditor.md — canonical role file, not touched per brief (it's already Hakim there; no Hatim references). Site-counts smoke test PASSES at 53 skills / 31 hooks / 19 roles / 23 sub-agents. Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#347): refresh stale counts + Hatim → Hakim in AGENTS.md (Rex follow-up) PR #360 Rex review caught AGENTS.md (the universal AI-agent entry doc at the repo root, alongside CLAUDE.md) carrying stale forward-looking claims: - "5 sub-agents" → "23 sub-agents" (5 utility + 18 dept-aligned) - "Hatim security-reviewer" → "Hakim security-reviewer/auditor" - "24+ shell scripts" → "31 shell scripts" (long-stale) - "52 slash commands" → "53 slash commands" (long-stale, two hits) AGENTS.md is forward-looking (GEO/AEO discovery surface for visiting AI agents) and was missed in the original Hatim → Hakim sweep + count refresh. Fixed in-PR per Rex's recommendation. Refs #347 Refs #360 (review) 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>
…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>
…ad (Wave 2 PR 5) (#362) * feat(#347): class-aware role-trigger banner — HYBRID spawn vs in-thread (Wave 2 PR 5) Per AgDR-0050 § Axis 6, role triggers now emit one of two banner shapes depending on the matched role's **Class** value in its `## Activation mode` section: - isolated-work-class (12 roles: Heads-of-X, Tech Lead, QA Engineer, SRE, Security Auditor, Pen Tester, Product Analyst, Data Analyst) → banner instructs the agent to SPAWN the sub-agent via the Agent tool with `subagent_type: <slug>`. Isolated work benefits from isolated context + tool restriction. - in-flow-class (7 roles: Backend / Frontend / Platform Engineer, Product Manager, UI / UX Designer, Data Engineer) → banner instructs the agent to ADOPT the persona IN-THREAD by reading the role file. In-flow work loses too much shared context if spawned out-of-thread. Implementation: - New `lookup_role_class()` helper reads `**Class**:` from the role file at trigger time. Falls back to in-flow-class on any failure (missing file, missing section, unparseable value) — conservative fail-open. - New `agent_slug_for()` helper maps role-file path → agent subagent_type slug. 1:1 by default (`backend-engineer.md` → `backend-engineer`); the one exception is `security-auditor.md` → `security-reviewer` (the Hatim→Hakim consolidation from PR #360 kept the filename so `/security-review` + the auto-fire trigger keep working). - `emit_banner()` is restructured to look up class + slug from the file arg and emit the appropriate shape. Caller signatures unchanged — every call site still passes (role, file, reason); the helper does the class-aware fan-out. Tests (7 new cases, all PASS, 26/26 overall): - 4a: Security Auditor → isolated-work-class banner with the consolidation slug `security-reviewer` (NOT `security-auditor`). - 4b: Platform Engineer → in-flow-class banner. - 4c: Tech Lead → isolated-work-class. - 4d: QA Engineer → isolated-work-class (label-based trigger). - 4e: Backend Engineer prompted → in-flow-class. - 4f: UX Designer prompted → in-flow-class. - 4g: Pen Tester prompted → isolated-work-class. Existing 19 tests unchanged + still PASS — their regexes are broad enough to match both old and new banner shapes, so this is a strict extension (no behaviour regressions on the in-thread path). Refs #347 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#347): document live HYBRID role-trigger banner shape (Wave 2 PR 5) `.claude/rules/role-triggers.md` mechanical-backstop section now describes both class-aware banner shapes (SPAWN for isolated-work-class, IN-THREAD adoption for in-flow-class), the 12-vs-7 role split per AgDR-0050 § Axis 6, and the one filename-vs-slug exception (security-auditor.md → security-reviewer agent slug, from PR #360's Hatim→Hakim consolidation). 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>
…sh-gate scope + multi-line drift (#364) * chore(#358): drop advertised-but-not-wired allowed_tools_override field PR #357 review caught that the agent-routing v1 schema advertised `allowed_tools_override` in three places — apply-agent-routing.sh's parser doc comment, agent-routing.yaml.example's schema preamble + Example F worked-example, and docs/multi-project.md's adopter schema table — but parse_routing() never emits the field, so adopters who set it get a silent no-op. Decision (recorded per #358 AC): DROP the field for v1, do NOT wire the parser branch. Rationale: - The full implementation needs parser emission (3 parsers) + a consumer-side `tools:` / `allowed-tools:` line rewrite + drift-guard awareness (tools-line differs from dev would need its own escape hatch). 50-100 line impl for a feature the docs explicitly mark as "advanced — use sparingly." - Adopters who actually need a per-fork tool-list override can edit the agent file directly with a `# routing-config:override <reason>` comment in YAML frontmatter — the same escape-hatch pattern that already handles model: overrides. Documented in the now-deprecation prose. - No migration tail: adopters who'd set `allowed_tools_override` were already getting a silent no-op; this PR makes that explicit in the schema docs. Files changed: - agent-routing.yaml.example - Schema preamble: replace the field's doc paragraph with a deprecation note pointing at the agent-file + escape-hatch path - Example F (the only worked example for the field): removed - .claude/hooks/apply-agent-routing.sh:115 - Parser doc comment: removed from the key-set list, added a one-line deprecation note pointing readers at the per-agent frontmatter path - docs/multi-project.md:549 - Schema table: removed the `allowed_tools_override` row - New post-table paragraph documenting the deprecation + the operator-facing escape-hatch alternative Closes #358 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(#359): reconcile AgDR-0050 § Axis 4 push-gate scope with implementation PR #357 review flagged a doc/impl mismatch: AgDR-0050 § Axis 4 said the pre-push guard "sweeps before any push to a public-class remote," but block-agent-routing-drift.sh fires on ANY push regardless of remote class. Two possible fixes per #359: tighten the hook to check remote class, OR update the AgDR to match the broader-scope impl. Decision: UPDATE the AgDR (option 2, recommended in the ticket). Rationale: - The broader scope is actually the right design. Even a push to a private tracking branch can become a leak vector later if the operator changes the remote. Adopters staging a deliberate framework-default change should label it with the # routing-config:override escape hatch before pushing anywhere — not just before pushing to upstream. - Tightening the hook to inspect @{push} remote class would add code and a new failure mode (silent-pass on remote-resolution failure) without removing a real failure surface. Files changed: - docs/agdr/AgDR-0050-agent-runtime-overhaul.md § Axis 4 - Prose changed: "sweeps before any push to a public-class remote" → "sweeps before ANY push (not just to public-class remotes)" - New blockquote note explaining the resolution + linking to #359 Closes #359 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(#342): add multi-line lookback pass + self-test to test_site_counts.sh #342 § 2(b) flagged that test_site_counts.sh's per-line grep can't catch claims where the digit and noun straddle a line break (the shape that bit `site/llms-full.txt:131-133` before #360 cleaned it up by hand). This commit adds the missing pass per #342's prescription. Implementation: - New check_multiline_count() helper. Flattens consecutive whitespace (including newlines) via `tr '\n' ' ' | tr -s '[:space:]' ' '`, re-scans for `<digits> +<noun>`, dedupes against the per-line pass's hits (so a same-line claim already reported by check_count doesn't get double-counted), reports drift as `file:multiline` (no line number — the digit and noun aren't on the same line). - Second for-loop calls the new helper for the same noun-pattern list as the per-line check_count loop. - Self-test fixture at the end: synthesises a temp file with the exact `52\n slash commands` shape #342 § 2(b) describes, runs check_multiline_count against it inside `$(...)` (subshell-isolated so its DRIFT++ stays scoped), asserts the expected `DRIFT: file:multiline — claims 52 slash commands` output line. If absent, the detector is broken and the suite fails. Net coverage: the per-line + multi-line passes now together cover both same-line and cross-line drift, closing #342's § 2(b) gap. § 2(a)'s remaining synonym patterns (`enforcement scripts?` and `mechanical (enforcement )?(scripts?|hooks?)`) are NOT addressed here — they're additional noun-pattern entries, not a structural fix. Closing the structural gap is the higher-value half; remaining noun patterns can be added incrementally as future drift surfaces them. Files changed: - .claude/hooks/tests/test_site_counts.sh: - new check_multiline_count() function (~30 lines) - new for-loop applying it to the same 9 noun patterns as check_count - new self-test at end of file (~15 lines) that proves the detector fires on a known-bad cross-line fixture Test passes locally against current site/ files (which were swept clean post-#360). Sibling tests (test_agent_routing_sync_and_drift.sh 8/8, test_agent_wrap_shape.sh 13+5+19/37) unchanged + still pass. Closes #342 § 2(b). § 2(a) noun-pattern additions and § 1 one-time hygiene sweep are intentional carry-overs. 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
.claude/agents/security-reviewer.md— per AgDR-0050 § Axis 2 and the operator's CONSOLIDATE decision recorded on the AskUserQuestion before dispatch, the existing utility agent renamespersona_name: Hatim→Hakimand bumpsmodel: inherit→opus, broadening the description from "PR-review-only" to the full Security Auditor role (OWASP / threat-modeling / SAST findings / auth-crypto diff review). Filenamesecurity-reviewer.mdis preserved because/security-review,auto-code-review.sh, and the auto-fire trigger in.claude/rules/role-triggers.mdreference it. Breaking-change marker (!) because the persona name visible in chat output, PR review trailers, and the AgDR-0018 mapping table changes. Carries a# routing-config:overridecomment in the YAML frontmatter soblock-agent-routing-drift.shaccepts the intentional framework-default change rather than blocking it as adopter-leak.head-of-security.md(Faisal, sonnet),penetration-tester.md(Hamza, opus),head-of-data.md(Khalil, sonnet),data-analyst.md(Nadia, haiku — local-model routing candidate per [Spike] Local-model routing feasibility for ticket-manager, Data Analyst, QA Engineer via LiteLLM → Ollama #348 spike),data-engineer.md(Anwar, sonnet). All mirror the Wave 1 thin-WRAP shape: one-line role summary + frontmatter +@roles/<dept>/<role>.mdlink to the canonical persona definition. No role-file content duplicated.test_agent_wrap_shape.shROLE_AGENTS extended with the 5 role-aligned entries — model + persona + dept assertions all PASS.security-reviewer.mdis deliberately NOT added to the matrix because its filename != role-slug (the consolidation exception); a separate test for the consolidation is filed as a follow-up if needed.docs/multi-project.md,docs/architecture/apexyard-container.md, allsite/*marketing copy (architecture/index/skills/llms-full). Historical record preserved indocs/agdr/AgDR-0050-agent-runtime-overhaul.md(the decision audit trail) anddocs/spikes/claude-model-tier-routing.md(historical spike snapshot).roles/security/security-auditor.mdis the canonical role file atHakimalready; not touched.docs/agdr/AgDR-0018-persona-naming-convention.mdmapping table updated —security-reviewerrow now points atHakim (was: Hatim; consolidated with Security Auditor role in #347 PR 3)and the roster sentence in the rationale paragraph notes Hatim's retirement.site/llms-full.txt"(12 sub-agents incl. Rex)" → "(23 sub-agents incl. Rex)", site layer-card narrative "5 sub-agents: Rex / Hakim / Munir / Tariq / Idris" replaced with "23 specialised sub-agents: 5 utility (...) + 18 dept-aligned agents across engineering / product / design / security / data" (phrasing avoids the site-counts drift regex'sroles?matcher because there are 19 role FILES but 18 role-derived AGENT files post-consolidation).Testing
bash .claude/agents/tests/test_agent_wrap_shape.sh— PASS at 18 ROLE_AGENTS entries (13 from PR 1+2 + 5 new from PR 3) and 19 ROLE_CLASSES entries.bash .claude/hooks/tests/test_site_counts.sh— PASS at 53 skills / 31 hooks / 19 roles / 23 sub-agents.block-agent-routing-drift.sh) accepts the intentionalinherit→opuschange onsecurity-reviewer.mdvia the# routing-config:overrideescape-hatch comment (commit hook fired once before the comment was correctly placed inside the YAML frontmatter; verified at HEAD314dd22)./security-reviewagainst a sample PR with auth/crypto diff — confirm the agent runs as Hakim (not Hatim), posts thegh pr review --commentwith the newReviewed by Hakimtrailer, and matches the broader Security Auditor scope. Operator to do this once PR is merged and a real auth-touching PR comes up; not blocking this merge.agent-routing.yamloverride (e.g.head-of-security: model: opus) and confirmapply-agent-routing.shrewrites the new agent file's frontmatter at SessionStart, andgit checkoutrestores the framework default. Not blocking this merge.Open Questions for follow-up
roles/security/security-auditor.md:122-124carries a forward-reference to a hypothetical.claude/agents/security-auditor.mdfile that the CONSOLIDATE decision means will never exist. The brief said do NOT touchroles/security/*.mdin this PR, so this stale forward-reference is left as a follow-up doc-debt item (recommend a small chore in PR 4 or Wave-3 cleanup that updates the role-file's## Activation modeblock to point at.claude/agents/security-reviewer.md)..claude/skills/security-review/SKILL.mdline 23 still describes the agent as "Shield" (its earliest historical name, predating both Hatim and Hakim). Not swept in this PR because it's only stale-by-history, not stale-by-functionality. Recommended in a separate doc-cleanup PR.Glossary
security-reviewer.mdagent'spersona_namefrom Hatim → Hakim, expand its scope from "PR-review-only" to the full Security Auditor role, and bump its model frominherittoopus. Single agent file, single persona, single canonical role atroles/security/security-auditor.md.persona_name, body links to@roles/<dept>/<role>.mdas the canonical persona definition. Sibling to the heavy operational-prompt shape used bysecurity-reviewer.md(which keeps its operationalgh pr reviewposting flow because the role file doesn't carry it).# routing-config:override <reason>block-agent-routing-drift.shfor intentional framework-default changes. Pattern:^[[:space:]]*#[[:space:]]*routing-config:override[[:space:]]+\S— must live INSIDE the YAML frontmatter (where#is a real comment marker) or anywhere in the body. The reason text is free-form and unvalidated. Visible + auditable per AgDR-0040's escape-hatch convention.[0-9]+ +roleas a role-count claim. There are 19 distinct role files but only 18 role-derived AGENT files because security-reviewer.md is the consolidation exception (filename != role-slug).Refs #347
Refs #353 / #355 / #356 / #357 (prior Wave 1 + Wave 2 PR 2 work)