Skip to content

feat(#347)!: Hatim→Hakim consolidation + security + data sub-agents (Wave 2 PR 3)#360

Merged
atlas-apex merged 3 commits into
devfrom
feature/GH-347-promote-security-data-agents-wave-2-pr-3
May 21, 2026
Merged

feat(#347)!: Hatim→Hakim consolidation + security + data sub-agents (Wave 2 PR 3)#360
atlas-apex merged 3 commits into
devfrom
feature/GH-347-promote-security-data-agents-wave-2-pr-3

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • Hatim → Hakim consolidation in .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 renames persona_name: HatimHakim and bumps model: inheritopus, broadening the description from "PR-review-only" to the full Security Auditor role (OWASP / threat-modeling / SAST findings / auth-crypto diff review). Filename security-reviewer.md is preserved because /security-review, auto-code-review.sh, and the auto-fire trigger in .claude/rules/role-triggers.md reference 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:override comment in the YAML frontmatter so block-agent-routing-drift.sh accepts the intentional framework-default change rather than blocking it as adopter-leak.
  • 5 new role-aligned WRAP-shape agent files completing the AgDR-0050 § Axis 2 promotion for Security + Data depts — 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>.md link to the canonical persona definition. No role-file content duplicated.
  • test_agent_wrap_shape.sh ROLE_AGENTS extended with the 5 role-aligned entries — model + persona + dept assertions all PASS. security-reviewer.md is 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.
  • Hatim → Hakim sweep across forward-looking docs — CLAUDE.md persona table cell, docs/multi-project.md, docs/architecture/apexyard-container.md, all site/* marketing copy (architecture/index/skills/llms-full). Historical record preserved in docs/agdr/AgDR-0050-agent-runtime-overhaul.md (the decision audit trail) and docs/spikes/claude-model-tier-routing.md (historical spike snapshot). roles/security/security-auditor.md is the canonical role file at Hakim already; not touched.
  • docs/agdr/AgDR-0018-persona-naming-convention.md mapping table updatedsecurity-reviewer row now points at Hakim (was: Hatim; consolidated with Security Auditor role in #347 PR 3) and the roster sentence in the rationale paragraph notes Hatim's retirement.
  • Sub-agent count claims refreshed — CLAUDE.md "18 sub-agents" → "23 sub-agents", 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's roles? 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.
  • Drift guard (block-agent-routing-drift.sh) accepts the intentional inherit→opus change on security-reviewer.md via the # routing-config:override escape-hatch comment (commit hook fired once before the comment was correctly placed inside the YAML frontmatter; verified at HEAD 314dd22).
  • Manual smoke: invoke /security-review against a sample PR with auth/crypto diff — confirm the agent runs as Hakim (not Hatim), posts the gh pr review --comment with the new Reviewed by Hakim trailer, 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.
  • Manual smoke: apply an agent-routing.yaml override (e.g. head-of-security: model: opus) and confirm apply-agent-routing.sh rewrites the new agent file's frontmatter at SessionStart, and git checkout restores the framework default. Not blocking this merge.

Open Questions for follow-up

  • roles/security/security-auditor.md:122-124 carries a forward-reference to a hypothetical .claude/agents/security-auditor.md file that the CONSOLIDATE decision means will never exist. The brief said do NOT touch roles/security/*.md in 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 mode block to point at .claude/agents/security-reviewer.md).
  • .claude/skills/security-review/SKILL.md line 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

Term Definition
Consolidation (Hatim → Hakim) The operator decision recorded for PR #347 PR 3: rename the security-reviewer.md agent's persona_name from Hatim → Hakim, expand its scope from "PR-review-only" to the full Security Auditor role, and bump its model from inherit to opus. Single agent file, single persona, single canonical role at roles/security/security-auditor.md.
WRAP-shape agent file The thin runtime-wrapper convention introduced in #355: 10-16 lines, one-line role summary in the body, frontmatter owns model + tool restriction + persona_name, body links to @roles/<dept>/<role>.md as the canonical persona definition. Sibling to the heavy operational-prompt shape used by security-reviewer.md (which keeps its operational gh pr review posting flow because the role file doesn't carry it).
# routing-config:override <reason> The escape-hatch comment recognised by block-agent-routing-drift.sh for 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.
isolated-work-class vs in-flow-class activation Per AgDR-0050 § Axis 6's HYBRID role-trigger split: isolated-work-class roles spawn the sub-agent in a separate context (suitable for multi-step deliberation — Head-of-X roles, Security Auditor, Penetration Tester, Data Analyst); in-flow-class roles adopt the persona in the main thread (suitable for tightly-coupled work alongside other roles — Backend / Frontend / Platform engineers, UI / UX designers, Data Engineer).
dept-aligned agents vs role-derived agents Effectively synonymous; "dept-aligned" is the phrasing used in the site copy to sidestep the site-counts drift regex which interprets [0-9]+ +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 (filename != role-slug).

Refs #347
Refs #353 / #355 / #356 / #357 (prior Wave 1 + Wave 2 PR 2 work)

me2resh and others added 2 commits May 21, 2026 01:53
… (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 atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review: PR #360

Commit: 314dd22401675ca1d0dc29c9d3053a6bb06e62d7

Summary

Wave 2 PR 3 of #347: consolidates the existing utility security-reviewer agent from HatimHakim (renaming persona, broadening description from PR-review-only to full Security Auditor scope, bumping model: inheritopus 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 HatimHakim 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:override entry + 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:

  1. Stale Hatim reference (not on the PR description's "intentional non-sweep" list)
  2. Stale count: 5 sub-agents — should be 23 per 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)

  1. Hardening the consolidation-exception invariant in the WRAP-shape test. The deliberate omission of security-reviewer.md from ROLE_AGENTS is 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 in test_agent_wrap_shape.sh would 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.

  2. .claude/agents/tests/test_agent_wrap_shape.sh:13 comment-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 is Rex / Hakim / Munir / Tariq / Idris. Pure doc-debt in a comment block, but worth folding into PR 4 itself when that work lands.

  3. docs/agdr/AgDR-0018-persona-naming-convention.md doc-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.

  4. Inaccuracy in the consolidation note prose (.claude/agents/security-reviewer.md:17). The body claims auto-code-review.sh references the security-reviewer.md filename — actually auto-code-review.sh doesn't grep for that filename anywhere; only .claude/skills/security-review/SKILL.md:23 does (and the /security-review skill alone is sufficient justification for the filename preservation). Cosmetic prose fix; doesn't affect runtime.

  5. 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 atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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 -l53 (matches AGENTS.md:11 + :77)
    • find .claude/hooks -maxdepth 1 -name '*.sh' ! -name '_lib*' | wc -l31 (matches AGENTS.md:10)
    • ls .claude/agents/*.md | wc -l23 (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 9ad45c0 confirms AGENTS.md | 8 ++++---- only, single file, 4 insertions + 4 deletions
  • ✓ Diff matches the brief exactly: three lines under .claude/{hooks,skills,agents}/ description + one second-hit 52 → 53 at 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 commit 314dd22 for site/ and CLAUDE.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_SCAN by 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`

@atlas-apex atlas-apex merged commit 41de535 into dev May 21, 2026
4 checks passed
@atlas-apex atlas-apex deleted the feature/GH-347-promote-security-data-agents-wave-2-pr-3 branch May 21, 2026 01:22
atlas-apex added a commit that referenced this pull request May 21, 2026
…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>
atlas-apex added a commit that referenced this pull request May 21, 2026
…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>
atlas-apex added a commit that referenced this pull request May 21, 2026
…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>
This was referenced May 24, 2026
me2resh added a commit that referenced this pull request Jun 5, 2026
…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>
me2resh added a commit that referenced this pull request Jun 5, 2026
…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>
me2resh added a commit that referenced this pull request Jun 5, 2026
…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>
me2resh added a commit that referenced this pull request Jun 5, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants