Claude Skill

audit

Full-sweep quality audit of .claude/ config — cross-references, permissions, inventory drift, model tiers, docs freshness. Scope tokens select what to audit; --upgrade applies docs-sourced improvements; --adversarial runs foundry:challenger + Codex adversarial review; --efficienc

LLM Mart · 0 points · 24 views 30 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Borda-AI-Rig-plugins_cc_foundry_skills_audit-39e3a48.zip · 110 KB
borda/ai-rig 27 4 forks Apache-2.0 Updated 2d ago
Part of borda/ai-rig — 82 skills

Install

skills CLI npx skills add https://github.com/Borda/AI-Rig/tree/main/plugins/cc_foundry/skills/audit
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install borda-ai-rig@llmmart
Git git clone https://github.com/Borda/AI-Rig.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole borda/ai-rig collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Files (ai-rig)
  • modes
    • adversarial.md 10.9 KB
      # Adversarial Mode — foundry:audit
      
      Triggered by `/audit --adversarial` (alias: `--challenge`). Read+executed by `/audit` when `--adversarial` flag present.
      
      ## Mode: adversarial (alias: --challenge)
      
      **Trigger**: `/audit [<scope>...] --adversarial`
      
      Adversarial review of all agents + skills in scope. Runs parallel with or after standard per-file audit (Step 3). Surfaces issues curator pass misses: subtle logic flaws, inconsistent claims, NOT-for gaps, scope leakage, cross-file contradictions, security vulnerabilities in bin/ executables.
      
      **Spawn wave cap (overrides "same response"/"parallel with" phrasing below when combined count is high)**: caps are per model tier (`CAP_OPUS`/`CAP_SONNET`, constants block; canonical table in claude-config.md §Parallel Spawn Ceilings), not one shared total — "parallel" between phases means logically independent, not necessarily launched in one burst.
      
      - **Opus pool** (`CAP_OPUS`, default 5): Steps 3-4's curator batches + Phase A's curator batches + Phase A-prime's curator batches all draw from this one pool, since all three spawn `foundry:curator`. Before launching, sum every opus-tier batch about to be spawned across all currently-due phases; if that sum exceeds `CAP_OPUS`, split into ordered waves of `WAVE_STEP` (default 5) — Step 3/4 first (core audit value), then Phase A, then Phase A-prime. Wait for a wave to return before opening the next.
      - **Sonnet pool** (`CAP_SONNET`, default 8): Step 4's docs-freshness web-explorer + Phase D's qa-specialist batches draw from this pool, separately from the opus pool above — the two may run concurrently.
      - **Phase B** (Codex bridge call) isn't a Claude `Agent()` spawn and draws from neither pool — it fits any wave with room, governed by Codex-side capacity instead (see codex-rig `specialist-orchestration.md` if `bridge` routes through it).
      
      Below a pool's cap, launch that pool's phases together as written below.
      
      **Phase A — Challenger sweep** (parallel with Phase B):
      
      For each file in scope (Step 2 inventory; default all agents + skills if no explicit scope), spawn **foundry:curator** (config-file adversarial review — `foundry:challenger` NOT-for excludes config-file review, routes to `foundry:curator`):
      
      > "Adversarially challenge this agent/skill. Do NOT accept claims at face value. Find: (1) unstated assumptions failing in edge cases, (2) NOT-for coverage gaps — tasks this agent wrongly accepts because exclusions are incomplete, (3) conflicting instructions producing non-deterministic or contradictory behavior, (4) workflow steps routing to the wrong sub-agent for the stated goal, (5) implicit scope contradicting explicit NOT-for lines. Report every finding with specific evidence from the file."
      >
      > Write full findings to `<RUN_DIR>/challenger-<file-slug>.md` where `<file-slug>` = `<plugin>-<skill-dir-name>` for skills or `<plugin>-<agent-name>` for agents (e.g. `foundry-audit`, `oss-review`, `foundry-curator`); `.claude/` files prefix `local`. Never use bare `challenger-SKILL.md`. Return ONLY: `{"status":"done","file":"<path>","findings":N,"severity":{"security":N,"critical":N,"high":N,"medium":N,"low":N},"confidence":0.N}`
      
      Use `ADVERSARIAL_BATCH_SIZE` (default 2) for grouping — smaller batches than Step 3 (`BATCH_SIZE_MIN`) to maximise per-file attention depth. Same plugin-aware batching algorithm; substitute `ADVERSARIAL_BATCH_SIZE` for `BATCH_SIZE_MIN`.
      
      **Phase A-prime — Unconstrained curator pass** (parallel with Phase A and Phase B):
      
      Resolve checklist path first — spawned curator runs in a fresh shell, can't expand orchestrator variables, so resolved value interpolates as a literal string into the prompt:
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
      printf "CURATOR_PROMPT_PATH=%s/curator-prompt.md\n" "$AUDIT_TPL"
      ```
      
      For each file in scope, spawn **foundry:curator** with no scope constraint — replace `<CURATOR_PROMPT_PATH>` with the literal value printed above:
      
      > "Audit this file. Run `cat "<CURATOR_PROMPT_PATH>"` via the Bash tool, use as baseline checklist — apply all those checks. Then go beyond: report ANY additional issue outside the explicit checklist. Look especially for: execution continuing after a confirmed failure path with no `exit 1`; incomplete specs leaving an agent uncertain at a branch point; undocumented implicit dependencies (env vars, files, network) not declared in inputs; workflow logic self-consistent but silently producing wrong results on a valid non-happy-path input. No scope constraint — senior-engineer judgment applies."
      >
      > Write full findings to `<RUN_DIR>/deep-curator-<file-slug>.md` using same `<file-slug>` convention as Phase A. Return ONLY: `{"status":"done","file":"<path>","findings":N,"severity":{"security":N,"critical":N,"high":N,"medium":N,"low":N},"confidence":0.N}`
      
      Use `ADVERSARIAL_BATCH_SIZE` grouping. Phase C deduplicates Phase A-prime findings against `summary.jsonl` from Steps 3–6 in SAME RUN_DIR only — not against prior runs. Adversarial-only mode (no same-run standard audit): all Phase A-prime findings carried forward.
      
      **Phase B — Codex adversarial pass** (parallel with Phase A):
      
      ```bash
      CODEX_STATUS=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_bridge.py" --status 2>/dev/null || echo "absent")  # timeout: 5000
      [ "$CODEX_STATUS" = "available" ] && CODEX_AVAILABLE=true || CODEX_AVAILABLE=""  # timeout: 5000
      _SHARED=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_shared_path.py" foundry skills/_shared 2>/dev/null || echo "plugins/cc_foundry/skills/_shared")  # timeout: 5000
      [ -f "$_SHARED/codex-prepass.md" ] || { printf "⚠ WARNING: codex-prepass.md not found at $_SHARED — skipping codex pre-pass\n"; CODEX_AVAILABLE=""; }
      [ -n "$CODEX_AVAILABLE" ] && cat "$_SHARED/codex-prepass.md"
      ```
      
      `$CODEX_AVAILABLE` non-empty: apply the codex-prepass.md instructions above, run Codex pass on all in-scope files. Focus Codex on: cross-file inconsistencies, circular dispatch chains, agent description ambiguities causing routing failures, workflow steps assuming capabilities declared tools don't provide. Else: `echo "⚠ bridge@borda-ai-rig is ${CODEX_STATUS} — skipping codex adversarial pass"`.
      
      Codex writes per-file findings to `<RUN_DIR>/codex-adversarial-<file-slug>.md`, same `<file-slug>` convention as Phase A. Return compact JSON envelope per file.
      
      For state-changing workflows, include unchanged downstream consumers and the next ordinary user action in supplied context. Compare producer postconditions with consumer preconditions; challenge whether all local checks could pass while the promised result fails. Record concrete counterexamples or unexecuted probe requests, positive cases, coverage gaps for identity, ownership, destination, lifetime, supported resume/retry behavior. Preserve read-only audit authority; no production mutation implied by a probe request.
      
      **Phase D — Security & Vulnerability Review** (parallel with Phases A, A-prime, B):
      
      Scope resolution — map audit scope tokens to plugin dirs, collect all bin/ scripts:
      
      - Default (full sweep): all `plugins/*/bin/*.py` and `plugins/*/bin/*.sh`
      - Named scope (e.g. `foundry`, `oss`, `codemap`): `plugins/<name>/bin/*.py` and `plugins/<name>/bin/*.sh`
      - `--local` mode: same paths from `plugins/` source tree; non-local: same paths under `~/.claude/plugins/cache/borda-ai-rig/`
      - Zero bin/ scripts for scope: skip Phase D, note in report
      
      Per-plugin security sweep — for each plugin in scope with bin/ scripts, spawn **foundry:qa-specialist**:
      
      > "Security and vulnerability review of all bin/ scripts in `plugins/<name>/bin/`. You are a black-box security reviewer — focus on executable surface only, not callers. Review every `.py` and `.sh` file in the directory. For Python scripts, check OWASP Top 10 applicability: (1) injection — `subprocess` calls with `shell=True` or string-concatenated command args; (2) path traversal — unvalidated file paths from argv; (3) insecure deserialization — `pickle.load`, `yaml.load` without `Loader`; (4) hardcoded secrets — API keys, tokens, passwords in source; (5) uncontrolled resource consumption — unbounded loops or file reads without size check. For shell scripts: (1) unquoted variable expansion in command positions; (2) `eval` with external input; (3) `rm -rf` with unvalidated variable path; (4) hardcoded credentials. Write full findings per file to `<RUN_DIR>/security-<plugin-name>.md`. Return ONLY: `{\"status\":\"done\",\"file\":\"<path>\",\"scripts_reviewed\":N,\"findings\":N,\"severity\":{\"critical\":N,\"high\":N,\"medium\":N,\"low\":N},\"confidence\":0.N}`"
      
      Use `BATCH_SIZE_MIN` (not `ADVERSARIAL_BATCH_SIZE`) for Phase D — bin/ scripts shorter, security checks deterministic; BATCH_SIZE_MIN fine here. Plugin with ≤5 bin/ scripts: one foundry:qa-specialist spawn covers all; larger sets batch by 5.
      
      Phase D runs parallel with Phases A, A-prime, and B — same async launch pattern.
      
      **Phase C — Aggregate and deduplicate**:
      
      Spawn **foundry:curator** consolidator to merge Phase A + Phase A-prime + Phase B + Phase D findings. Cross-reference against standard audit `summary.jsonl` (same RUN_DIR). Surface only findings NOT already in standard audit — adversarial adds signal, not noise.
      
      Adversarial-only mode (`--adversarial` flag without preceding standard audit in same RUN_DIR): no `summary.jsonl` exists in this RUN_DIR. Skip dedup entirely — surface all adversarial findings without overlap filtering. Do NOT dedup against prior audit runs in `.reports/audit/`; prior runs may have unresolved findings still needing fixing.
      
      Write deduplicated findings to `<RUN_DIR>/adversarial-aggregate.md` and `<RUN_DIR>/adversarial-summary.jsonl` (same JSONL format as Step 5). Return: `{"status":"done","new_findings":N,"overlapping":N,"severity":{"security":N,"critical":N,"high":N,"medium":N,"low":N}}`
      
      **Report format**:
      
      ```markdown
      ## Adversarial Audit — <date> — <scope>
      
      | File | Challenger | Deep-curator | Codex | Security | New Findings | Top Issue |
      |------|-----------|--------------|-------|----------|--------------|-----------|
      | agents/curator.md | 3 | 1 | 1 | 0 | 2 | NOT-for gap: accepts task X |
      ```
      
      Adversarial findings feed into standard fix pipeline (Steps 7–10) when user picks fix level from follow-up gate.
      
      **Adversarial-only runs** (no standard audit in same RUN_DIR): run the run-directory setup block from `SKILL.md` Step 3 (`export CSID` → `make_run_dir.py` → empty-path guard → `echo` → sentinel write) before Phase A, then skip Steps 3–6; run only Phases A–D above; skip Phase C dedup; report all adversarial findings. Standard findings from prior `.reports/audit/` runs NOT consulted for dedup — user may not have fixed them.
      
      **Flag aliases**: `--adversarial` and `--challenge` are identical — either triggers this mode.
      
    • efficiency.md 22.5 KB
      # Efficiency Mode — foundry:audit
      
      <!-- file: efficiency.md — consumers: audit/SKILL.md -->
      
      Triggered by `/audit --efficiency`. Read+executed by `/audit` when `--efficiency` flag present.
      
      ## Mode: efficiency
      
      **Trigger**: `/audit [<scope>...] --efficiency`
      
      Sweeps agents and skills for cost inefficiency signals. Does NOT run standard per-file quality audit (Steps 3–6) — efficiency-only analysis. Generates prioritized cost-reduction plan with estimated savings. Mode produces heuristic estimates only — no live token-cost baseline measured, no post-fix delta computed. Savings figures are directional guidance.
      
      **Scope resolution**: same as standard audit. No scope = all agents + skills across plugins + `.claude/`. Named scope = union of resolved file sets. Fragment files (`*/modes/*`, `*/templates/*`, `*/_shared/*`): skip checks 1–3 and 6 (no model frontmatter); run checks 5 (token bloat) and 7 (bin/ extraction) only.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir" 2>/dev/null || RUN_DIR=""
      if [ -z "$RUN_DIR" ] || [ ! -d "$RUN_DIR" ]; then
        RUN_DIR=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/make_run_dir.py" .reports/audit)  # timeout: 5000
        [ -z "$RUN_DIR" ] && { printf "! BREAKING: make_run_dir.py returned empty path\n"; exit 1; }
        mkdir -p "${TMPDIR:-/tmp}/audit-state-${CSID}"
        echo "$RUN_DIR" > "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir"
      fi
      echo "Run dir: $RUN_DIR"
      ```
      
      This RUN_DIR replaces Step 3 setup (skipped in efficiency mode) — unless a prior mode in the same combined invocation (e.g. `--adversarial --efficiency`) already minted one; then it is reused, never clobbered.
      
      **Phase A — Per-file efficiency audit** (parallel foundry:curator spawns, same batching rule as Step 3 (`EFFECTIVE_BATCH`) — efficiency skips Step 3, so recompute the value here):
      
      Spawn **foundry:curator** per file with efficiency-specific prompt:
      
      > Audit `<file>` for cost and efficiency signals only. Do NOT run general quality checks. Check:
      >
      > 1. **Model tier**: `model:` declared? If `model: opus` or `model: opusplan`, does task genuinely require reasoning depth — adversarial multi-step analysis, architectural design, complex implementation? Flag if task primarily: template fill, pattern matching, structured summarization, orchestrator-only dispatch, or single-pass structured write. **Performance-safety sub-check (mandatory before any downgrade recommendation)**: does agent produce quality-sensitive output? Signals: public-facing text (contributor replies, blog posts), security analysis (OWASP, exploit reasoning), adversarial reasoning, complex multi-file code design, creative original content. Any signal present → add `performance_risk: medium|high` to finding, require empirical validation note — do NOT recommend downgrade as P1/P2 without this caveat. A lower-cost model degrading output quality is not an efficiency gain.
      > 2. **Effort level**: `effort: xhigh` declared? Flag if agent read-only, single-pass, or executes a fixed decision tree — xhigh planning budget has nowhere to spend. Exception: `xhigh` on sonnet acceptable only for these agent roles: adversarial reviewer (challenger, qa-specialist), multi-file code designer (sw-engineer, solution-architect), public-facing content writer (creator, shepherd). All other roles: flag regardless of quality-sensitivity claim.
      > 3. **Missing model declaration**: no `model:` → session model inherited; flag with recommended tier (opus/sonnet/haiku based on role complexity). Applies to skill and agent files alike. `disable-model-invocation: true` is **not** an exemption: blocks only Claude-automatic invocation, subagent preloading, scheduled dispatch — skill still user-invocable via `/skill-name`, still runs a model, still inherits session model when it declares none.
      >
      > <!-- Item 4 (dead model spec) removed: `disable-model-invocation: true` blocks only Claude-automatic invocation, subagent preloading, and scheduled dispatch — user-typed `/skill-name` still runs and `model:` still governs it. Numbering gap at 4 is intentional; items 5–9 keep their numbers because E8/E9 are named identifiers referenced across this file and the envelope schema. -->
      >
      > 5. **Token bloat**: identify inline reference blocks >40 lines loading unconditionally but applying only to a subset of invocations (e.g. ML-specific patterns for non-ML projects, hook authoring for non-hook tasks, domain CI blocks always loaded). Flag with estimated line count + suggested gate/extraction. A block "applies only to a subset" if its heading keyword doesn't appear in the skill's `description:` field or is scoped by a conditional the skill rarely enters. Curator must verify subset-applicability before flagging — do not flag always-on decision tables or check indexes.
      > 6. **Tool grant scope**: agent files — does `tools:` include `*` or tools unused in the agent's workflow prose? Skill files — does `allowed-tools:` include `*` or tools unused in the skill workflow body? Skills with `disable-model-invocation: true` have no model execution, tool grants irrelevant — skip.
      > 7. **Bin/ extraction candidates**: scan fenced code blocks of any language (bash, python, sh, perl, etc.) for self-contained patterns appearing 3+ times in this file with only constant differences (variable names, path segments, string literals). Self-contained means: block produces output to stdout, has no shell function definitions, reads no caller shell state beyond `$HOME`, `$ARGUMENTS`, `$RUN_DIR`, `$AUDIT_TPL`. Flag each candidate: block language, purpose, occurrence count, suggested `bin/<script-name>.sh` or `bin/<script-name>.py`. Skip: blocks defining bash functions, blocks mutating shell state used in later blocks. 7b. **Prose compression candidates**: scan inline fenced code blocks and bin/ call-site descriptions. Flag when: (a) inline block — `tokens(block) > tokens(equivalent prose/table/schema)` at identical precision; (b) bin/ call-site — `tokens(call-site description) >= tokens(prose equivalent)`. Exempt: examples, templates, blocks carrying exact executable syntax. Severity: low. Fix: replace with prose/table/schema (Case 1) or delete script and replace call-site with prose (Case 2). Ref: `bin-authoring-guide.md §Prose over Code (Token Compression)`.
      >
      > <!-- GUARD-RAILS: never flag as E8/E9 — these look verbose but are load-bearing -->
      >
      > <!-- (a) Structural protocols: convergence limits, iteration caps, loop bounds, health monitoring constants (HARD_CUTOFF, EXTENSION), security gates (adversarial pre-apply validation, AskUserQuestion before destructive ops), NON_AUTO_FIXABLE bypass lists, confidence block requirements, task hygiene protocols, JSON envelope contracts, file-based handoff requirements, batch-size guards, Fix Action Hierarchy multi-step rules, post-fix verification protocols, reversibility checks, <antipatterns-to-flag> curator rules. -->
      >
      > <!-- (b) High-stakes path reinforcement: 3+ restatements on irreversible operations, destructive edits, security boundaries are expected and exempt — any section whose heading or surrounding context names git push, settings.json mutation, external messages, force operations, or dropping data. -->
      >
      > <!-- (c) Domain-specific constraints: any instruction not inferable from agent/skill role alone — escalation protocols, output format specs (field names, file naming), cross-plugin coordination rules, permission model constraints. -->
      >
      > 8. **Instruction complexity (E8)**: instructions disproportionately complex relative to operation governed? Flag when: (a) same behavioral constraint restated 3+ times with different wording in same logical section — exempt: high-stakes path reinforcement (guard-rail (b) above); (b) conditional logic nested >3 levels with no meaningful behavioral difference between branches; (c) >100-word preamble preceding single atomic action with no new constraints added; (d) step with ≥5 sub-bullets where ≥3 restate same rule — exempt: Fix Action Hierarchy and structural-protocol lists (guard-rail (a) above). Never flag guard-rail taxonomy items above. Severity: medium. Fix: consolidate redundant restatements to one canonical statement; flatten trivially equivalent branches. For each E8 finding in the report file include columns: `check` (E8), `location` (line range), `issue` (one-line), `impact` (estimated lines reduced), `suggested simplification`.
      > 9. **Behavioral noise (E9)**: do instructions describe behavior any capable frontier model performs by default, or contradict each other? Flag when: (a) instruction verbatim duplicates documented default Claude Code behavior already enforced by `CLAUDE.md` or `settings.json` — verifiable by grep, not opinion; (b) abstract directive with no observable behavioral constraint ("be thorough", "ensure quality") — identical behavior present or absent; (c) two instructions in same file that cannot both be satisfied — contradiction is noise; (d) "IMPORTANT: do X" where X explicitly stated in an adjacent Bash block or step header in same file — same-file structural evidence required. Omit (a) and (d) when no file-internal or CLAUDE.md/settings.json evidence exists — do not flag on inference. Never flag guard-rail taxonomy items above, domain-specific constraints, or format contracts. Severity: low. Fix: remove or replace with concrete behavioral constraint. For each E9 finding in the report file include columns: `check` (E9), `location` (line range), `issue` (one-line), `impact` (estimated lines reduced), `suggested simplification`. Write findings to `<RUN_DIR>/efficiency-<file-slug>.md` where `<file-slug>` = `<plugin>-<skill-dir-name>` for skills (e.g. `foundry-audit`, `oss-review`) or `<plugin>-<agent-name>` for agents (e.g. `foundry-curator`). `.claude/` files prefix with `local` (e.g. `local-audit`). Never use bare `efficiency-SKILL.md` — all skills share that basename.
      >
      > Return ONLY: `{"status":"done","file":"<path>","issues":N,"severity":{"critical":N,"high":N,"medium":N,"low":N},"top_issue":"<one-line>","cheapest_viable_model":"<model or unchanged>","e8_complexity":N,"e9_noise":N,"confidence":0.N}`
      
      **Phase B — System-wide spawn pattern + duplication scan** (parallel with Phase A):
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _SCAN_DIR="plugins/" || _SCAN_DIR=".claude/"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/scan_efficiency_signals.py" --scan-dir "$_SCAN_DIR"  # timeout: 60000
      ```
      
      **Phase B2 — Code block purpose-grouping + extraction feasibility (Check 33, parallel with Phase A+B)**:
      
      Scope: per plugin — compare blocks within same plugin only (cross-plugin overlap also captured here). `--efficiency` active → **skip Check 17** — Phase B2 subsumes it: DUPLICATE clusters (max-sim ≥ 0.90) are the Check 17 findings at higher resolution.
      
      **Primary signal: functional purpose, not syntactic similarity.** Syntactic line-intersection is blind to conditional-inversion and variable renaming — two blocks implementing same logic written differently have low syntactic overlap but identical purpose. Group by purpose first; syntactic overlap only as secondary confirmation and DUPLICATE label.
      
      <!-- policy-sibling: plugins/cc_foundry/skills/audit/templates/checks-skills.md (Check 33b Phase 2 — same Gate/Score spec) -->
      
      Spawn **foundry:curator** per plugin with this prompt:
      
      > Enumerate every fenced code block (```` ```bash ````, ```` ```python ````, ```` ```sh ````, etc.) in all `.md` files under `plugins/<name>/` — including `modes/`, `templates/`, `_shared/` subdirs. Assign each a block ID: `<plugin-abbrev>-<skill-slug>-B<n>` (e.g. `fnd-audit-B3`, `fnd-audit-modes-efficiency-B2`). Record: ID, source file, start line, language, line count, total lines across cluster.
      >
      > **Step 1 — Purpose statements**: for each block, write a one-sentence purpose statement describing what the block does functionally (not how) — e.g. "resolves `_shared/` path from plugin cache", "detects codex plugin availability", "sets LOCAL_MODE-aware glob vars", "emits boilerplate-duplication counts". Same wording of different goal = different cluster. Different wording of same goal = same cluster.
      >
      > **Step 2 — Purpose clusters**: group blocks with equivalent purpose into clusters. Primary grouping. Singletons omitted. Assign cluster ID `C<n>`.
      >
      > **Step 2a — Computational equivalence gate (mandatory before finalizing any cluster)**: purpose-statement wording similarity necessary but not sufficient — two blocks that *sound* alike can compute different things. Before finalizing a cluster (and before proposing any "site B bypasses/duplicates site A" relationship), verify: (a) same output destination *namespace* — `.reports/audit/*` and `.reports/research/*` are different namespaces even when both blocks "write a report file"; (b) same input parameters/env vars consumed; (c) same side-effect semantics (write vs read vs delete). Members target structurally different destinations or inputs despite similar wording → split into separate clusters, do not merge. Any auto-fix proposal renaming/redirecting one site's output to match another's requires this equivalence evidence stated in cluster row; omit auto-fix suggestion (report cluster only) when evidence inconclusive.
      >
      > **Step 3 — Syntactic similarity (secondary)**: for each cluster, normalize each member block: strip `#` comment lines → collapse whitespace → replace path segments / slugs / numeric literals with `<STR>` → **replace ALL concrete argument/parameter values** (flag values after `--flag`, option strings, RHS of variable assignments `FOO="val"`) with `<ARG>`; keep structural tokens. Compute `sim(A,B) = 2 × |lines(A_norm) ∩ lines(B_norm)| / (|A| + |B|)`. Record max-sim within cluster. Mark cluster **DUPLICATE** if max-sim ≥ 0.90 (blocks are near-identical, not just same-purpose).
      >
      > **Table 1 — Purpose clusters**:
      >
      > ```
      > | Cluster | Block IDs | Files | Lang | Lines each | Total lines | Est. tokens/call | Purpose | Max-sim | Duplicate? |
      > ```
      >
      > (Total lines = sum of Lines each across all instances; Est. tokens/call = (lines_per_instance − 1) × ~4 — tokens saved per calling-skill invocation when block extracted to bin/)
      >
      > **Table 2 — Extraction scoring**: for each cluster, apply gate then score:
      >
      > - **ParamSlots**: count of distinct `<ARG>` placeholder slots after normalization = how many CLI parameters the extracted script would need.
      > - **Tokens**: estimated token count of one block instance.
      > - **Never-extract list — checked first, before the gate.** A cluster matching any of these isn't a candidate at all; record in Table 1 with reason, omit from Table 2:
      >   - Replacement costs more than the block. A one-command block (`rm -f .temp/state/skill-contract.md`) becomes a longer invocation line — extraction is a net token loss.
      >   - Block marked `# audit-skip` on its first line, or prose beside it declares the duplication intentional.
      >   - Block's only purpose is echoing values for the model to read, no computation behind them.
      >   - One-off glue under ~10 lines, single file only.
      >   - Body carries model-substituted placeholders (`<changed_files>`, `<TARGET>`) rather than shell variables — a template, not executable code.
      >   - Block is an `Agent(...)` prompt, an output template, or a prompt string assigned to a shell variable.
      >   - Block is a propagated copy owned by another plugin's MANIFEST entry — extract at the canonical, never at a copy.
      >   - Shape load-bearing for a permission hook: `IFS= read -r VAR < "${TMPDIR:-/tmp}/…-${CSID}"` sentinel reads pass `sentinel-read-allow.js` by shape, and a `$(python …)` replacement reintroduces the prompt they exist to avoid.
      > - **Gate** = `G1:P/F · G2:P/F · G3:P/F` — all must pass or Verdict = HOLD:
      >   - G1 (Size OR execution cost): block > 100 tokens (payload cost), **OR** block launches an external interpreter process (subprocess/heredoc/`-c` invocation of python/node/perl/ruby/etc) **and** occurs ≥3× in cluster (execution cost — N forked interpreters is real overhead even when each instance is token-small; a tiny `python -c "..."` one-liner repeated 84× must not gate out on size alone)
      >   - G2 (Independence): no branch on prior LLM decision that cannot become explicit arg
      >   - G3 (Identity): has computational meaning outside orchestration prose (high env-var coupling = G3 fail)
      > - **Score** = sum of applicable positive-dimension weights when gate passes:
      >   - Testable (deterministic I/O, writable pytest/shellcheck test) +2
      >   - Reuse (same logic in 2+ .md files) +2
      >   - Token drain (block > 300 tokens) +2
      >   - Process overhead (external interpreter launched ≥3× in cluster — extraction collapses N process forks into 1 script invocation) +2
      >   - Lintable (shellcheck/ruff directly applicable) +1
      >   - Run frequency (executes >1× per skill invocation) +1
      >   - Standalone debuggable (runnable with no SKILL.md context) +1
      > - **Verdict**: HOLD (any gate fail) · LOW (0–1) · MEDIUM (2–3) · HIGH (≥4)
      >
      > ```
      > | Cluster | ParamSlots | Tokens | Gate | Score | Verdict | Differs-by | Recommended extraction |
      > ```
      >
      > **Differs-by**: list the concrete `<ARG>` slot values that vary across cluster instances — these become named CLI parameters in the extracted script signature. Recommendation = concrete: e.g. `Extract → bin/find-plugin.sh <plugin-name>; N call sites become $(find-plugin.sh codex)`.
      >
      > **Severity**: DUPLICATE cluster (max-sim ≥ 0.90) → **high** regardless of gate/score; HIGH verdict → **medium**; MEDIUM verdict → **low**; LOW or HOLD → Table 2 only (not a finding). Python blocks with HIGH verdict → medium + note approval-prompt impact.
      >
      > Write to `<RUN_DIR>/efficiency-check33-<plugin>.md`. Return ONLY: `{"status":"done","file":"<path>","clusters":N,"findings":N,"severity":{"high":N,"medium":N,"low":N},"confidence":0.N}`
      
      **Phase C — Aggregate, score, and plan** (after A+B+B2 complete):
      
      Spawn **foundry:curator** consolidator to merge all findings:
      
      > Read all per-file reports from `<RUN_DIR>/efficiency-*.md` and all Check 33 reports from `<RUN_DIR>/efficiency-check33-*.md`. Also read Phase B bash output passed as context. Deduplicate findings by `(file, finding_type)` pair — Phase A curator and Phase B bash both scan for the missing-model condition; prefer Phase A curator finding (has file context) over Phase B bash line (no context) when both report same file+condition.
      >
      > Produce a cost-reduction report with these sections:
      >
      > 1. **Cheapest Viable Model table** — one row per agent/skill with cost issue: `| file | current model+effort | minimum viable | rationale | estimated saving |`; saving = opus→sonnet: LARGE, opusplan→sonnet: LARGE, xhigh→high: MEDIUM, xhigh→medium: MEDIUM (heuristic tiers — not measured against live run costs)
      > 2. **Unbounded Spawn Patterns** — list files with uncapped per-item agent dispatch; recommended cap + batch strategy
      > 3. **Token Bloat Hotspots** — top 5 files by redundant inline content; section name, line count, suggested action
      > 4. **Boilerplate Duplication + Bin/ Extraction Candidates** — pattern name × occurrence count × total redundant lines × extraction target; for each bin/ candidate from Phase A+B: block purpose, occurrence count, suggested `bin/<script-name>.sh`, estimated line reduction. Merge with Check 33 (Phase B2) similarity clusters: include Table 1 and Table 2 per plugin inline in this section, sorted by feasibility HIGH→LOW
      > 5. **Missing Model Declarations** — list files inheriting session model; recommended tier per file
      > 6. **Prioritized Improvement Plan** — P1 (critical: correctness/highest cost), P2 (high: model downgrades), P3 (medium: hygiene/dedup), P4 (low: compression); each item: file + exact change + estimated saving + `performance_risk: low|medium|high`. Items with `performance_risk: high` auto-downgrade to P-HOLD (do not apply without empirical benchmarking). Items with `performance_risk: medium` stay in plan, carry a `⚠ validate first` marker.
      > 7. **Estimated Combined Savings** — rough directional reduction for most common workflows if all P1+P2 applied; savings are heuristic estimates only — no live cost measurement performed; directional guidance, not engineering targets
      > 8. **Instruction Quality Issues** — table of E8/E9 findings aggregated from per-file envelopes (`e8_complexity` + `e9_noise` fields) and detailed findings in `efficiency-*.md` reports: `| file | check | location | issue | impact | suggested simplification |`; sorted by estimated noise reduction (high→low). NON_AUTO_FIXABLE — E8 findings contribute to `medium` count; E9 findings contribute to `low` count; both enter the follow-up gate under option (c) "Fix ALL" as a separate "instruction-quality" AskUserQuestion category. Do not auto-fix instruction-quality findings via options (a) or (b). Omit section entirely when both E8 and E9 totals are zero. Write full report to `<RUN_DIR>/efficiency-report.md`. End report with `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements).
      >
      > Return ONLY: `{"status":"done","file":"<RUN_DIR>/efficiency-report.md","critical":N,"high":N,"medium":N,"low":N,"total_issues":N,"clusters":N,"extract_count":N,"recommended_count":N,"e8_complexity":N,"e9_noise":N,"top_saving":"<description>","confidence":0.N}`
      
      **Report format** (terminal summary):
      
      ```
      verdict: EFFICIENCY_ISSUES · critical: N · high: N · medium: N · low: N
      code-blocks: clusters: N · HIGH: N · MEDIUM: N
      instruction-quality: complexity: N · noise: N
      confidence: 0.N
      → <RUN_DIR>/efficiency-report.md
      
      Critical: [list each]
      Estimated savings (P1+P2): ~X%
      ```
      
      Omit `code-blocks:` line when no clusters found (all HOLD verdicts or no Check 33 data available). Omit `instruction-quality:` line when both complexity and noise zero.
      
      Efficiency findings feed into standard fix pipeline (Steps 7–10). **Step 8 override**: model-tier mismatch findings NOT subject to Step 8's "report-only" bypass — user opted into auto-fix by invoking `--efficiency`. Fix agents apply model-tier changes.
      
      **Extraction routing**: when Phase C envelope `extract_count > 0` (HIGH or MEDIUM verdict clusters), follow-up gate replaces option (d) with a `/distill executables` choice — user selects from gate; do NOT auto-run. Gate substitution logic in main SKILL.md follow-up gate section.
      
      **Post-extraction orphan check**: after `/distill executables` completes, run `python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_orphaned_bin.py"` — must exit 0. New orphan introduced (bin/ script created without consumer rewire) = HIGH finding; abort extraction phase, require wire-in before commit.
      
      **Flag aliases**: `--efficiency` only (no alias).
      
    • fix.md 15 KB
      <!-- file: fix.md — consumers: audit/SKILL.md -->
      
      # Mode: fix (Steps 8–10)
      
      Loaded by `audit/SKILL.md` Step 7 only when user picks a fix option (a–c) from follow-up gate. Runs fix-dispatch → codex cross-file → re-audit convergence loop inline, then returns to SKILL.md Step 11 (Final report).
      
      State (`$RUN_DIR`, `$AUDIT_TPL`, `LOCAL_MODE`, `summary.jsonl`) re-derived from persisted state files exactly as in SKILL.md Pre-flight — Claude Code spawns fresh shell per `Bash()` call.
      
      ## Step 8: Delegate fixes to subagents
      
      > **HARD RULE — No inline fixes**: Orchestrator MUST NOT apply any fix directly via Edit or Write — not even single-line edits. Every fix at every severity level goes through sub-agent. Not optional. Spawning overhead always lower than context cost of 40+ inline Edit calls in a `fix all` run.
      
      **Fix Action Hierarchy** — before any fix:
      
      1. **Reason** — finding correct? Flagged content genuinely wrong or wrong place? Misidentified → discard, don't act.
      2. **Relocate** — correct content, wrong location → move, not remove.
      3. **Consolidate** — redundant with nearby content → merge into one clearer location.
      4. **Minimize** — too long but valid → compress (tighten wording, remove restatements).
      5. **Remove** — only if none above apply. Never remove solely because flagged as verbose.
      
      Apply hierarchy to every fix at all severity levels.
      
      **Dependency classification — before any dispatch**
      
      Classify each finding from `summary.jsonl` before spawning fix agents to avoid stale-read conflicts (agent reads file A, concurrent agent modifies file A, first agent's assumptions wrong).
      
      **Parallel-safe** (ALL four must hold — apply after same-file coalescing in criterion 4):
      
      1. Fix writes only to the file that contains the finding
      2. Finding category is in `PARALLEL_SAFE_CATEGORIES`: `{hardcoded-path, missing-confidence-block, typo, heading-hierarchy, duplicate-lines, broken-bash-fence, stale-version-ref, missing-frontmatter-field, verbose-bash-block}` — any category not in this list defaults to sequential
      3. No other finding in this batch writes to a file this fix reads from
      4. **Coalesce first**: group all same-file findings into a single agent prompt before applying criteria 1–3; the coalesced group is classified as one unit (prevents multiple agents racing on the same file)
      
      Parallel-safe examples: typos, hardcoded `/Users/` paths (replacement `~/`), missing `## Confidence` block (template known), broken bash fence, heading hierarchy jump, duplicated lines within one file, verbose-bash-block (compress multi-line `if/fi` to `&&`/`||` one-liners, join sequential assignments, remove WHAT/HOW comments — see curator.md §Code Block Authoring step 7).
      
      **Sequential (cross-file dependent)**: any finding where the fix must read another file to determine correct replacement, OR where another concurrent fix writes to a file this fix reads from.
      
      Sequential examples: broken cross-reference (must verify target name on current disk), inventory drift (must read MEMORY.md), README sync (must read agent/skill source files).
      
      **Two-phase dispatch**:
      
      - **Phase 1 — Parallel basket**: issue ALL parallel-safe fix spawns in a single response. Each agent touches only its own file with self-contained changes.
      
      - **Phase 2 — Sequential basket**: after Phase 1 complete, spawn **foundry:curator** mini-agent to re-read files modified in Phase 1 that are dependency inputs for Phase 2 fixes — orchestrator must NOT inline-read modified files (orchestration contract: see SKILL.md `## Pre-flight checks` orchestration rule). Mini-agent returns updated finding refs (refreshed line numbers, moot findings dropped). Then dispatch Phase 2 fixes via category→dependency table:
      
        | Category | Reads from | Serialization rule |
        | -- | -- | -- |
        | broken-cross-ref | target agent/skill file | serialize after any fix that renames/moves target |
        | inventory-drift | MEMORY.md | serialize after any fix that adds/removes agents or skills |
        | README-sync | agent/skill source files | serialize after any fix that modifies an agent/skill file |
        | orphaned-ref | disk agent/skill inventory | serialize after any fix that adds/removes agents or skills |
      
        Groups with no dependency on other groups' outputs run in parallel; groups depending on prior output serialize in order.
      
      Narrate phase boundaries: `"Phase 1: N parallel-safe fixes launched"` → `"Phase 1 complete — mini-agent re-reading modified files"` → `"Phase 2: N sequential fixes starting"`.
      
      **Adversarial pre-apply validation gate** — each proposed fix must clear two-agent gate before spawning fix agent:
      
      1. Spawn **foundry:challenger** with finding text, file path, proposed fix — challenge: "Is this finding real? Is fix appropriate? Does it risk removing load-bearing behavioral content (runtime gates, behavioral invariants, execution constraints, `notes` checkpoints)?"
      2. Spawn **foundry:curator** same context — validate: "Fix correct per Fix Action Hierarchy? Preserves behavioral integrity? Could silently remove load-bearing content even if appearing redundant or verbose?"
      3. Both spawns in parallel per file. Each writes verdict to `<RUN_DIR>/gate-<file-basename>-<finding-id>.md`; returns only: `{"verdict":"approved"|"blocked","reason":"<one-line>","file":"<path>"}`
      4. **Either** returns `blocked` → skip fix agent; add to `blocked_findings` with reason; surface `⚠ GATE-BLOCKED — needs human review: <reason>`
      5. **Both** `approved` → proceed to fix agent
      
      Gate applies at every severity level. Skip only for inline-exception cases (settings.json, CLAUDE.md, dead loops, model tier).
      
      **Trivial-finding fast path** (see `agent-spawn-protocol.md` §Delegation cost discipline): batch parallel-safe findings by file adjacency, prefer `bridge:implement` when `bridge@borda-ai-rig` available. Each call must state exact finding, target paths, current evidence, permitted edits, required result, stop condition, verification command. Bridge absent/disabled → use normal per-file-type agent with `model: haiku`. Reserve full adversarial gate + top-tier agents for CRITICAL/HIGH or cross-file-dependent findings.
      
      Fix agent by file type:
      
      - **`.claude/agents/*.md` and `.claude/skills/*/SKILL.md`** → spawn **foundry:curator** — domain expertise in config quality, has `Write`/`Edit` tools
      - **Code files** (`.py`, `.js`, `.ts`, etc.) → spawn **foundry:sw-engineer**
      
      **Phase 4 delegation rule**: edits touching >3 files → delegate to `foundry:sw-engineer` — pass findings list + target file paths; returns compact status JSON.
      
      Spawn one agent per affected file, batch all findings per file into single prompt. Issue **all spawns in a single response** for parallelism.
      
      Each subagent prompt: instruct agent to run `cat "$AUDIT_TPL/fix-prompt.md"` via the Bash tool, then fill `<file path>` and findings list.
      
      **Preferred orchestration pattern — audit-fix sub-agent**
      
      <!-- loads: audit-fix-prompt.md -->
      
      After gate fires (Step 7): finding count > 10 or user picked option (a) "Fix auto-fixable" or (c) "Fix ALL" → use audit-fix sub-agent pattern below (handles Steps 8–10 in isolation); otherwise inline batched pattern at end of this step.
      
      **Gate authority**: sub-agent path → orchestrator Step 7 gate **skipped** — sub-agent runs own gate internally, authoritative. Inline batched path (≤10 findings) → orchestrator Step 7 gate authoritative, no sub-agent gate. Never double-gate.
      
      **Gate failure fallback**: if sub-agent returns `blocked_findings: []` with `fixed > 0` and `failed == 0` but no `gate-<file>.md` files appear in `<RUN_DIR>`, surface: `⚠ GATE-SKIPPED — sub-agent did not perform adversarial gate; review fixes manually before merging.` Missing `gate-<file>.md` expected — not a failure — for any file the sub-agent recorded under `fast_path` in `fix-summary.md` (mechanical, single-file, no CRITICAL/HIGH — see `audit-fix-prompt.md` §Gate fast path). Fire the warning only when a fixed file is absent from BOTH the gate files and `fast_path`.
      
      ```bash
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
      cat "$AUDIT_TPL/audit-fix-prompt.md"
      ```
      
      Spawn dedicated **audit-fix** sub-agent — use full prompt loaded above, pass `<RUN_DIR>` and `$AUDIT_TPL` as context values substituted into prompt. Orchestrator reads only compact JSON envelope returned; does NOT read `fix-summary.md` unless `re_audit_clean: false`, `failed > 0`, or `residual_criticals > 0`.
      
      Finding count ≤ 10 and user picked option (b) "Fix SECURITY + CRITICAL + HIGH" → inline batched pattern (one fix-agent per file, all parallel) acceptable; no dedicated sub-agent.
      
      **Findings that bypass fix-agent delegation:**
      
      **NON_AUTO_FIXABLE** (authoritative set — referenced by bypass list and option (d)): Check 3 (settings.json missing permission), Check 5 critical/high (permission safety), Check 19 (model tier mismatch), CLAUDE.md contradiction, dead loop in follow-up chains.
      
      Default (options a–b): report only — no Edit or Write tool calls for NON_AUTO_FIXABLE findings.
      
      - **settings.json permission missing** (Check 3, 5): report only — structural JSON edits risky to delegate
      - **CLAUDE.md contradiction**: raise to user — do not auto-fix (CLAUDE.md takes precedence)
      - **Dead loop**: flag for user review — human judgment needed on which link to break
      - **Model tier mismatch** (Check 19): report only — assignments may be intentional for cost/latency trade-offs; user decides
      
      **"Fix ALL" option (c)**:
      
      1. **Upfront decision collection** — before any fixes run: group all NON_AUTO_FIXABLE findings by category (settings.json / model-tier / CLAUDE.md-conflict / dead-loop — max 4 categories). For each category call `AskUserQuestion` (one call per category, honoring `communication.md` 4-question-per-call cap): list up to 4 representative findings; >4 in category → note "and N more follow same pattern". Options: (a) Apply same resolution to all in category · (b) Review each individually · (c) Skip category. Hard cap: max 4 `AskUserQuestion` calls total; overflow findings listed in final report. "Apply same resolution" valid for uniform findings; non-uniform findings force option (b).
      
      2. **Single integrated fix pass** — after all decisions collected, run auto-fixable + user-resolved NON_AUTO_FIXABLE in one combined loop, same Phase 1 parallel / Phase 2 sequential dispatch. Low findings included. No mid-run checkpoint — all decisions already made upfront.
      
      Apply NON_AUTO_FIXABLE fixes only on explicit user selection per category; never auto-apply.
      
      After subagents complete, collect results and proceed to Step 10.
      
      **Low findings** (nits): fix only when `fix all` passed — otherwise collect in final report for optional manual cleanup.
      
      ## Step 9: Codex cross-file check
      
      After Step 8 fix agents complete, before foundry:curator re-audit:
      
      ```bash
      CODEX_STATUS=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_bridge.py" --status 2>/dev/null || echo "absent")
      [ "$CODEX_STATUS" = "available" ] && CODEX_AVAILABLE=true || CODEX_AVAILABLE=""  # timeout: 5000
      _SHARED=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_shared_path.py" foundry skills/_shared 2>/dev/null || echo "plugins/cc_foundry/skills/_shared")  # timeout: 5000
      [ -f "$_SHARED/codex-prepass.md" ] || { printf "⚠ WARNING: codex-prepass.md not found at $_SHARED — skipping codex pre-pass\n"; CODEX_AVAILABLE=""; }
      [ -n "$CODEX_AVAILABLE" ] && cat "$_SHARED/codex-prepass.md"
      ```
      
      `$CODEX_AVAILABLE` non-empty: follow codex-prepass.md instructions above, applied to combined diff of Step 8 fixes. Otherwise: `echo "⚠ codex plugin not available — skipping codex pass"`
      
      Treat findings as additional issues entering Step 10 re-audit scope. Skip if Step 8 touched only 1 file.
      
      ## Step 10: Re-audit modified files + confidence check
      
      For every file changed in Step 8, spawn **foundry:curator** to confirm fix resolved finding and no new issues introduced. Write full re-audit findings to `<RUN_DIR>/<file-basename>-reaudit.md`; end the full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements); return ONLY compact JSON envelope: `{"status":"done","file":"<RUN_DIR>/<file-basename>-reaudit.md","findings":N,"severity":{"security":N,"critical":N,"high":N,"medium":N,"low":N},"confidence":0.N,"summary":"<filename>: fix confirmed, N residual findings"}`
      
      ```bash
      # Replace BROKEN_NAME and FIXED_FILE with the actual values from the finding
      grep -n "BROKEN_NAME" FIXED_FILE
      ```
      
      **Confidence re-run**: parse confidence scores from Step 3 and Step 10 summaries. **Score < 0.80**: Step 5b already ran a three-pass remediation (double-reasoning, docs consultation, Codex adversarial) — if Step 10 re-audit still scores < 0.80 after Step 5b, flag with ⚠, include gap in final report. Recurring low-confidence gaps (same gap, same file, multiple runs) → candidate for foundry:curator `<antipatterns-to-flag>` or agent instructions.
      
      **Convergence loop**: re-audit surfaces new fixable findings within gate-selected severity threshold → loop back to Step 8. Repeat until:
      
      - Zero fixable findings remain → mark fix pass complete, or
      - Hard limit: **5 total fix passes** (including initial Step 8) — still not converged → surface all remaining fixable findings with `⚠ CONVERGENCE LIMIT` warning; **do not re-enter Step 8; omit fix options from follow-up gate when convergence limit reached**.
      
      Track pass count via `$RUN_DIR/fix-passes.txt` (persists across bash calls — shell state doesn't). At each Step 10 entry: `IFS= read -r PASS_COUNT < "$RUN_DIR/fix-passes.txt" 2>/dev/null || PASS_COUNT=0; PASS_COUNT=$((PASS_COUNT + 1)); echo "$PASS_COUNT" > "$RUN_DIR/fix-passes.txt"`. If `$PASS_COUNT >= 5`, stop loop immediately — do not re-enter Step 8 regardless of remaining findings. Never suppress findings to clean counter.
      
      Audit-fix sub-agent (when used) must apply this loop internally — instruct it to keep spawning fix agents and re-audit agents until clean or 5-pass limit.
      
      **Cross-file re-validation**: after per-file re-audit, re-run Step 4 checks sensitive to modified files:
      
      - Check 1 (inventory drift) — if any agent or skill file modified
      - Check 2 (README vs disk) — if any agent or skill added, renamed, or deleted
      - Check 14a (structural tag symmetry) — if any agent or skill file modified
      - Check 14b (code fence symmetry) — if any agent or skill file modified
      - Check 17 (cross-file content duplication) — if 2+ files modified
      - Check 25 (implicit agent references) — if any agent or skill file modified
      - Check 27 (cross-plugin shared-file ref integrity) — if any skill file modified
      
      Write findings to `<RUN_DIR>/crossfile-revalidation-pass<N>.md`, N = current pass count. Include new findings in convergence loop input for next Step 8 iteration.
      
      After the convergence loop completes (clean or 5-pass limit), return to `audit/SKILL.md` Step 11 (Final report).
      
    • steps-4-5-7.md 17.4 KB
      <!-- file: steps-4-5-7.md — consumers: SKILL.md (Steps 4–5b and Step 7 pointers) -->
      
      ## Step 4: System-wide checks
      
      > **Full implementation instructions** are split across 5 scope files in `$AUDIT_TPL/` (resolved in Pre-flight). Read only the file(s) for the active scope at the start of this step — do not read all 5 files unless running a full sweep.
      >
      > | Scope | File(s) to read |
      > | -- | -- |
      > | `setup` | `checks-setup.md` (Checks 1–11, 39) + `checks-install.md` (I1–I3) + `checks-security.md` (Check 37) |
      > | `plugin` | `checks-setup.md` (Checks 7, 8 only) |
      > | `plugins` | `checks-setup.md` (7, 8) + `checks-agents.md` + `checks-skills.md` + `checks-shared.md` (14a, 14b, 15, 17, 12, 13, 25, 26, 29, 41, 45) + checks 32, 32d, 33, 38, 40 + `checks-install.md` (R1–R5 — LOCAL_MODE) + `checks-security.md` (35, 36, 37) |
      > | `plugins <name>` | same as `plugins` — scoped to one plugin directory |
      > | `agents` | `checks-agents.md` (19, 20) + `checks-shared.md` (run only: 14a, 14b, 15, 17, 12, 13, 25, 26, 29, 41, 45) + `checks-skills.md` (22, 40 only) + `checks-security.md` (35, 36) |
      > | `skills` | `checks-skills.md` (22–24, 27, 28, 30, 31, 32, 33, 38, 40) + `checks-shared.md` (run only: 14a, 14b, 15, 17, 12, 13, 25, 26, 29, 41, 45) + `checks-security.md` (35–37) |
      > | `rules` | `checks-shared.md` (run only: 18, 12, 13, 29, 41, 45) + `checks-skills.md` (32c only) |
      > | `communication` | `checks-shared.md` (run only: 15, 16, 12, 13, 29, 45) |
      > | No scope (full) | all 5 files |
      
      **Delegation for full-sweep runs**: for full-sweep (no scope), spawn dedicated `foundry:curator` per scope group, passing template file path and RUN_DIR: agents-checks (reads `checks-agents.md` + relevant `checks-shared.md`), skills-checks (reads `checks-skills.md` + relevant `checks-shared.md`), shared-checks (reads `checks-shared.md`), setup-checks (reads `checks-setup.md` + `checks-install.md`), security-checks (reads `checks-security.md` — security findings land in separate Security Findings section of report). Each writes findings to `<RUN_DIR>/system-checks-<scope>.md`, returns only JSON envelope. Orchestrator does NOT read template files — passes path to spawned agent only.
      
      Run checks below. Native tools first (Glob, Grep, Read); Bash only for pipeline ops native tools can't do.
      
      **Agent roster consistency policy**: evaluate agent system as capability set, not just files. For every overlap in checks 20 or 17, explicit judgment:
      
      - **keep** when both roles own meaningfully different acceptance criteria
      - **sharpen** when both roles justified but descriptions/handoffs too fuzzy
      - **merge/prune** when roles differ mostly by tone or examples, not decision surface
      
      Don't leave overlap findings as vague "potential duplication." Audit must state which outcome applies and why.
      
      **Context discipline for Step 4**: write all check findings to `$RUN_DIR/system-checks.md` (Write tool after checks complete), not main context. Keep one-line status per check in context:
      
      - `✓ Check N — <one-line result>` (pass)
      - `⚠ Check N — N findings` (issues)
      
      **Scope filter**: when `$SCOPE` is set, run only checks listed for that scope; skip all others silently.
      
      > Checks 42, 43 (and 14a/14b/14c/14d/14e, 32d, cli-flag-drift, R3) run in Step 1b on every invocation regardless of scope — never list them here; SKILL.md:198 forbids prose re-derivation.
      
      - `agents` — Checks 14a, 14b, 15, 16, 19, 20, 17, 12, 13, 25, 22, 26, 29, 35, 36, 40, 41 (files: `.claude/agents/*.md` + `plugins/*/agents/*.md`)
      - `skills` — Checks 14a, 14b, 15, 16, 17, 12, 23, 22, 13, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 38, 40, 41 (files: `.claude/skills/*/SKILL.md` + `plugins/*/skills/*/SKILL.md`)
      - `rules` — Checks 18, 12, 13, 29, 32c, 41 (32d skipped — no plugin bin/ in rules scope)
      - `communication` — Checks 15, 16, 12, 13, 29
      - `setup` — Checks 1, 2, 3, 4, 5, 9, 10, 11, 7, 6, 8, 30, 37, 39, I1, I2, I3 (Step 3: one foundry:curator spawn for `setup` SKILL.md only; I1–I3 read `~/.claude/`)
      - `plugin` — Checks 7, 8 (Step 3: one foundry:curator spawn for `${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/skills/setup/SKILL.md` only)
      - `plugins` — Checks 7, 8, 14, 14b, 15, 16, 19, 20, 17, 12, 13, 25, 22, 26, 23, 24, 27, 28, 29, 30, 31, 32, 32d, 33, 35, 36, 37, 38, 39, 40, 41, R1, R2, R3, R4, R5 (files: all `plugins/*/agents/*.md` + `plugins/*/skills/*/SKILL.md`; Step 3: foundry:curator batches for all plugin agents + skills + each plugin's setup SKILL.md; 32d, R1–R5 always LOCAL_MODE — skip in non-local)
      - `plugins <name>` or `<plugin-name>` (tier 2) — same check list as `plugins`, scoped to `plugins/<name>/` only
      - `<agent-name>` (tier 3) — Checks 14, 14b, 15, 16, 19, 20, 17, 12, 13, 25, 22, 26, 29, 35, 36, 40, 41 (one file only; no cross-plugin Checks 7/8)
      - `<skill-name>` (tier 3) — Checks 14, 14b, 15, 16, 17, 12, 23, 22, 13, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33a, 35, 36, 37, 38, 40, 41 (one file only)
      - Multiple scope tokens — union of check lists for all resolved scope types; de-duplicate; run each check once against union file set
      - No scope argument — run all checks
      
      ### Check summary
      
      <!-- loads: checks-index.md -->
      
      <!-- loads: checks-security.md -->
      
      ```bash
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
      cat "$AUDIT_TPL/checks-index.md" "$AUDIT_TPL/checks-security.md"
      ```
      
      Full check index (Checks 1–41, I1–I3, R1–R5 with severity, scope, notes) and security checks (35–37) loaded above.
      
      ### Claude Code docs freshness (within Step 4)
      
      > Replace `<RUN_DIR>` with the actual run-directory path (sentinel `${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir`) before dispatching — the spawned agent gets a fresh shell and cannot expand orchestrator variables.
      
      ```text
      Agent(subagent_type="foundry:web-explorer", prompt="Fetch current Claude Code docs (https://code.claude.com/docs/en/). If that URL returns 404 or redirects, navigate from https://code.claude.com homepage to find the documentation section. If docs are entirely unavailable, return {\"status\":\"unavailable\",\"findings\":0}. Check: hook event names + type field vs documented schema (deprecated decision:/reason: fields); agent frontmatter fields + model values; skill frontmatter fields; new features passing genuine-value filter → Upgrade Proposals table (max 5, classify config or capability). Write full findings to <RUN_DIR>/docs-freshness.md using the Write tool. End your full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Return ONLY: {\"status\":\"done\",\"file\":\"<RUN_DIR>/docs-freshness.md\",\"findings\":N,\"deprecated\":N,\"new_features\":N,\"confidence\":0.N,\"summary\":\"N findings, N deprecated, N new features\"}")
      ```
      
      <!-- URLs fetched live by web-explorer at runtime; graceful degradation: if any 404, instruct navigation from code.claude.com homepage. -->
      
      Severity: deprecated/invalid = **high**; deprecated frontmatter field = **medium**; new feature not used = **Upgrade Proposals** (not LOW).
      
      After checks complete: collect `⚠` lines, write full details to `$RUN_DIR/system-checks.md`, include only summary table in context.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r _RUN_DIR < "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir" 2>/dev/null || _RUN_DIR=""
      IFS= read -r _KEEP < "${TMPDIR:-/tmp}/audit-state-${CSID}/keep-items" 2>/dev/null || _KEEP=""
      _PRESERVE="run-dir=$_RUN_DIR, static-findings=${TMPDIR:-/tmp}/audit-state-${CSID}/static-findings.jsonl, finding-files=$_RUN_DIR/*.md"
      [ -n "$_KEEP" ] && _PRESERVE="$_PRESERVE; user-keep: $_KEEP"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/write_skill_contract.py" "foundry:audit" "aggregate (after parallel curator+system-checks fan-out)" "$_RUN_DIR" "$_PRESERVE" "consolidate findings → aggregate.md + summary.jsonl → Step 7 report"  # timeout: 5000
      ```
      
      ## Step 5: Aggregate and classify findings
      
      **Delegate aggregation** to consolidator agent to avoid flooding main context. Resolve the two absolute paths the consolidator must read — it runs in a fresh shell, can't expand orchestrator variables, so these are interpolated as literal strings into the prompt below:
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
      IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir" 2>/dev/null || RUN_DIR=""
      printf "RUN_DIR=%s\nSTATIC_FINDINGS_PATH=%s\nSEVERITY_TABLE_PATH=%s\n" "$RUN_DIR" "${TMPDIR:-/tmp}/audit-state-${CSID}/static-findings.jsonl" "$(dirname "$AUDIT_TPL")/severity-table.md"
      ```
      
      Replace `<RUN_DIR>`, `<STATIC_FINDINGS_PATH>`, and `<SEVERITY_TABLE_PATH>` in the prompt below with the literal values printed above. Spawn **foundry:curator** consolidator:
      
      > "Read all finding files in `<RUN_DIR>/` (\*.md files from Steps 3–4, including `docs-freshness.md` if present) AND the deterministic Layer-1 results at `<STATIC_FINDINGS_PATH>` (Step 1b — one JSON object per check; each `\"status\":\"fail\"` object's `lines` array is a set of already-verified mechanical findings, severity per the check's known level: fence/mode-dispatch=high, tag/README-drift/bash-persistence/spawn-vars/shared-drift=medium, orphaned-bin/routing=medium). Run `cat "<SEVERITY_TABLE_PATH>"` via the Bash tool and apply its severity classification. Antipatterns that indicate severity under-classification are also in that file. Group all findings by severity (security, critical, high, medium, low). Apply the one-finding-per-issue rule: when a single location has multiple distinct problems at different severities, emit one finding entry per problem. Write the aggregated severity table to `<RUN_DIR>/aggregate.md` using the Write tool. End your aggregate.md file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Also write `<RUN_DIR>/summary.jsonl` — one compact JSON object per line, one line per finding: `{"file":"<basename>","sev":"security|critical|high|medium|low","id":"H1","line":"<line number or null>","category":"<category>","one_line":"<finding description>"}`. This file is what the orchestrator will read; aggregate.md is for human review only. Return ONLY a compact JSON envelope on your final line — nothing else after it: `{\"status\":\"done\",\"file\":\"<RUN_DIR>/aggregate.md\",\"findings\":N,\"severity\":{\"security\":N,\"critical\":N,\"high\":N,\"medium\":N,\"low\":N},\"confidence\":0.N,\"summary\":\"N findings total: S security, C critical, H high, M medium, L low\"}`"
      
      Main context receives only that one-liner. Orchestrator MUST NOT read `aggregate.md` in full — 200–600 lines, overflows context on large audits. Use `$RUN_DIR/summary.jsonl` for all dispatch decisions, Steps 7 and 8.
      
      ## Step 5b: Low-confidence remediation
      
      Parse confidence scores from each file's `## Confidence` block in `<RUN_DIR>/<slug>.md` output files (use Glob + Read — batch envelopes carry aggregate confidence, not per-file scores; individual file reports are the authoritative source). For each slug where `Score` < **0.80**, run three parallel passes:
      
      **Fan-out ceiling**: when MORE THAN 8 slugs score \<0.80, do NOT run per-slug passes (4 spawns × N is unbounded) — instead spawn ONE consolidated **foundry:curator** re-run covering all low-confidence slugs (batched prompt listing every slug + its `Gaps:` block, one combined `<RUN_DIR>/lowconf-batch-rerun.md`) plus one Codex pass over same batch when available; run pass B (docs check) only for slugs whose gaps explicitly cite schema/docs uncertainty. Systematically low confidence signals a rubric or curator problem, not N independent file problems — remediate once, not N×4 times.
      
      **Health monitoring** (CLAUDE.md §6): apply the honest protocol in `$_FS/agent-spawn-protocol.md` — passes A–C return on completion; read each pass's output file afterwards. On empty/missing output: mark `timed_out`, surface with ⏱ in final report.
      
      **A — Double-reasoning pass** (curator re-run with gaps called out):
      
      Spawn **foundry:curator** with the prior report and its `Gaps:` block:
      
      > "Re-audit `<original-source-file>` targeting these specific gaps from the prior pass: `<Gaps block content>`. Address each gap explicitly — do not repeat prior findings verbatim; focus on what was uncertain. Write updated findings to `<RUN_DIR>/<slug>-rerun.md`. End your full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Return ONLY: `{\"status\":\"done\",\"file\":\"<path>\",\"findings\":N,\"confidence\":0.N,\"summary\":\"...\"}`"
      
      **B — Docs consultation** (verify findings against current Claude Code schema):
      
      Spawn **foundry:web-explorer**:
      
      > "Fetch current Claude Code docs for `[agent|skill|hook]` schema — navigate from `https://code.claude.com/docs/en/` to the `[sub-agents|skills|hooks]` page. Verify that findings about frontmatter fields or documented behavior in `<RUN_DIR>/<slug>-rerun.md` are accurate against current docs. List any corrections. Write to `<RUN_DIR>/docs-recheck-<slug>.md`. End your full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Return ONLY: `{\"status\":\"done\",\"file\":\"<path>\",\"corrections\":N,\"confidence\":0.N}`"
      
      **C — Codex adversarial pass** (requires `bridge@borda-ai-rig`):
      
      ```bash
      CODEX_STATUS=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_bridge.py" --status 2>/dev/null || echo "absent")
      [ "$CODEX_STATUS" = "available" ] && CODEX_AVAILABLE=true || CODEX_AVAILABLE=false  # timeout: 5000
      ```
      
      If `CODEX_AVAILABLE=true`, resolve every placeholder in the following review brief, then pass that entire rendered brief to `Skill(skill="bridge:review", args="...")`:
      
      > "Adversarial review of low-confidence findings for `<original-source-file>`. Prior foundry:curator pass scored confidence=<N> — gaps: `<Gaps>`. Challenge each finding: real? severity correct? missed issues? Read source file + prior report `<RUN_DIR>/<slug>-rerun.md`. Write to `<RUN_DIR>/codex-recheck-<slug>.md`. End your full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Return ONLY: `{\"status\":\"done\",\"file\":\"<path>\",\"findings\":N,\"confidence\":0.N}`"
      
      If `CODEX_AVAILABLE=false`: log `[Step 5b] bridge@borda-ai-rig is ${CODEX_STATUS} — adversarial pass skipped.` Include note in final report `## Confidence` section.
      
      **After all three passes complete** for a slug: spawn **foundry:curator** mini-consolidator to merge `<slug>-rerun.md`, `docs-recheck-<slug>.md`, `codex-recheck-<slug>.md`, append `### Low-Confidence Remediation — <slug>` section to `aggregate.md` with reconciled findings (promoted corrections, confirmed findings, refuted findings). Update `summary.jsonl` with any net-new findings.
      
      **Skip Step 5b entirely** when no Step 3 files scored below 0.80.
      
      Returns to SKILL.md Step 6 (cross-validate critical findings) after Step 5b completes or is skipped.
      
      ## Step 7: Report findings
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r _RUN_DIR < "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir" 2>/dev/null || _RUN_DIR=""
      IFS= read -r _KEEP < "${TMPDIR:-/tmp}/audit-state-${CSID}/keep-items" 2>/dev/null || _KEEP=""
      _PRESERVE="run-dir=$_RUN_DIR, aggregate=$_RUN_DIR/aggregate.md, summary=$_RUN_DIR/summary.jsonl"
      [ -n "$_KEEP" ] && _PRESERVE="$_PRESERVE; user-keep: $_KEEP"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/write_skill_contract.py" "foundry:audit" "report (after aggregate complete)" "$_RUN_DIR" "$_PRESERVE" "emit report → follow-up gate → optional fix mode (Steps 8-10) → Step 11"  # timeout: 5000
      ```
      
      Before emitting, read current `$RUN_DIR/summary.jsonl` (may have been updated by Step 5b with net-new promoted findings), recompute severity totals. Then emit report (omit Upgrade Proposals if none passed genuine-value filter):
      
      ```markdown
      ## Audit Report
      
      ### Findings by Severity
      #### Security (N) | Critical (N) | High (N) | Medium (N) | Low (N)
      | File | Line | Issue | Category |
      |---|---|---|---|
      | agents/foo.md | 42 | References `bar-agent` which does not exist on disk | broken cross-ref |
      
      ### Summary
      - Total: N (S security, C critical, H high, M medium, L low)
      - Fix via follow-up gate: (a) Fix auto-fixable (Recommended) · (b) Fix SECURITY + CRITICAL + HIGH · (c) fix ALL incl. systemic
      
      ### Upgrade Proposals (N — pick `/audit --upgrade` from gate to apply)
      | # | Feature | Type | Rationale |
      |---|---------|------|-----------|
      ```
      
      After report → fire **Follow-up gate**, with question header `audit`. The hook requires `aggregate.md`, valid `summary.jsonl`, and current parent-visible `Audit Report`, exact `Total: N`, and every finding's `one_line`. A zero-finding summary is valid when its aggregate exists. Missing/unreadable delivery blocks only this fix transition; diagnostic/recovery questions use a different header and remain available. Do not require Step 11's final report before Step 7. If user picks fix option (a–c), proceed inline to fix mode (Steps 8–10, loaded from `modes/fix.md`). Otherwise skip to Step 11.
      
      Returns to SKILL.md Steps 8–10 (fix dispatch, gated) / Step 11 (final report).
      
    • upgrade.md 4.6 KB
      # Upgrade Mode — foundry:audit
      
      Triggered by `/audit --upgrade`. Read+executed by `/audit` when `--upgrade` flag present.
      
      ## Mode: upgrade
      
      **Trigger**: `/audit --upgrade`
      
      **Purpose**: apply documented Claude Code improvements that passed genuine-value filter. Config changes applied + correctness-checked immediately. Capability changes A/B tested via mini calibrate pipeline — accepted only if Δrecall ≥ 0 and ΔF1 ≥ 0.
      
      **Task tracking**: TaskCreate "Fetch upgrade proposals", "Apply config proposals", "A/B test capability proposals". Mark in_progress/completed throughout.
      
      ### Phase 1: Gate check
      
      Verify no unresolved blocking findings before applying anything.
      
      Critical/high issues from recent `/audit` run: stop, print "⚠ Resolve critical/high findings first (run `/audit` and pick fix level from gate), then re-run `/audit --upgrade`."
      
      ### Phase 2: Fetch and classify proposals
      
      **Always spawn fresh foundry:web-explorer** — no context from prior audit runs, cached docs, or memory. Every upgrade run fetches live docs.
      
      Run **Claude Code docs freshness** check from Step 4 of main audit workflow: spawn foundry:web-explorer, validate current config against latest docs, apply genuine-value filter, produce Upgrade Proposals table. Cap 5 total (max 3 capability, any number config).
      
      **RTK hook alignment** — also run Check 10 from main audit workflow (inline, no subagent):
      
      - `rtk` not installed or `.claude/hooks/rtk-rewrite.js` absent: skip silently.
      - Otherwise: run `rtk --help`, extract `RTK_PREFIXES` from hook, compare, add findings as **config proposals**:
        - Invalid prefix (not valid RTK subcommand) → config proposal: remove from `RTK_PREFIXES`; severity **high**
        - Filterable RTK command absent from hook → config proposal: add to `RTK_PREFIXES`; severity **medium**
      
      Include alongside docs-based proposals in same Upgrade Proposals table.
      
      No proposals pass filter: print "✓ No upgrade proposals — current setup is current." and stop.
      
      ### Phase 3: Apply config proposals
      
      Mark "Apply config proposals" in_progress. Each **config** proposal, in sequence:
      
      1. Apply change (Edit/Write tool) (inline — exempt from fix.md §No inline fixes, which scopes to Steps 8–10)
      2. Correctness check:
         ```bash
         jq empty .claude/settings.json && echo "✓ valid JSON" || echo "✗ invalid JSON" # timeout: 5000
         node --check .claude/hooks/*.js 2>&1 | grep -v '^$' || true # timeout: 5000
         ```
      3. Accept (✓) if check passes; revert + mark rejected (✗) with reason if fails
      
      Mark "Apply config proposals" completed.
      
      ### Phase 4: A/B test capability proposals
      
      Mark "A/B test capability proposals" in_progress. Each **capability** proposal (max 3), in sequence:
      
      **Step a — Baseline calibration**: Load the calibrate pipeline template via `cat` (not the Read tool — `Bash(cat:*)` grant is version-proof):
      
      ```bash
      CALIB_TPL=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" calibrate templates 2>/dev/null || echo "plugins/cc_foundry/skills/calibrate/templates")  # timeout: 5000
      cat "$CALIB_TPL/pipeline-prompt.md"  # timeout: 5000
      ```
      
      Spawn `general-purpose` subagent with that template, target agent name, domain, N=3, MODE=fast, AB_MODE=false. Capture `recall_before` and `f1_before` from returned JSON.
      
      **Step b — Apply change**: Edit target agent file per proposal spec.
      
      **Step c — Post calibration**: Spawn same pipeline subagent, identical params. Capture `recall_after` and `f1_after`.
      
      **Step d — Decision**:
      
      - `Δrecall = recall_after − recall_before`
      - `ΔF1 = f1_after − f1_before`
      - **Accept** (✓) if Δrecall ≥ 0 AND ΔF1 ≥ 0 → keep change
      - **Revert** (✗) if either delta negative → restore file, record deltas
      
      Mark "A/B test capability proposals" completed.
      
      ### Phase 5: Report and sync
      
      ```markdown
      ## Upgrade Complete — <date>
      
      ### Gate
      [clean / issues found and stopped]
      
      ### Config Changes
      | # | Feature | Target | Result | Notes |
      |---|---------|--------|--------|-------|
      | 1 | ... | hooks/task-log.js | ✓ accepted | jq valid |
      
      ### Capability Changes
      | # | Feature | Target | Δrecall | ΔF1 | Result |
      |---|---------|--------|---------|-----|--------|
      | 1 | ... | agents/curator.md | +0.04 | +0.02 | ✓ accepted |
      | 2 | ... | agents/sw-engineer.md | −0.02 | +0.01 | ✗ reverted |
      
      ### Next Steps
      - `/foundry:setup` — propagate accepted changes to ~/.claude/
      - `/audit` — confirm clean baseline after upgrades
      - Reverted items: run `/calibrate <agent> full` for deeper A/B signal (N=10 vs N=3 used here)
      ```
      
      Propose `/foundry:setup` after upgrade completes — no auto-execute. Print: `` → Run `/foundry:setup` to propagate accepted changes to ~/.claude/ ``
      
  • templates
    • audit-fix-prompt.md 6.9 KB
      <!-- file: audit-fix-prompt.md — consumers: audit/modes/fix.md Step 8 -->
      
      <!-- Canonical multi-file fix orchestration. NOT derived from fix-prompt.md (per-file only). Keep both in sync when changing shared audit-fix behavior. -->
      
      Read `<RUN_DIR>/summary.jsonl` — the findings list (one JSON object per line). Run `cat "$AUDIT_TPL/fix-prompt.md"` via the Bash tool for the per-file fix prompt template. **Dependency classification**: (1) Coalesce all same-file findings into one group first. (2) Classify each coalesced group: parallel-safe = category in PARALLEL_SAFE_CATEGORIES {hardcoded-path, missing-confidence-block, typo, heading-hierarchy, duplicate-lines, broken-bash-fence, stale-version-ref, missing-frontmatter-field} AND fix writes only to its own file AND no other group writes to a file this group reads. All others sequential. **Model tiering (gate + fix calls)**: tier on whether the task PRODUCES a judgment or merely APPLIES one. Use `haiku` or `bridge:implement` for applying an exact supplied replacement or confirming mechanical facts (grep counts, numeric equality, file existence, table-row presence); every bridge brief must include the exact finding, target path, current evidence, permitted edit, expected result, stop condition, and verification command. Use `sonnet` for fixes needing judgment about placement or phrasing. Use `opus` for adversarial gates on CRITICAL/HIGH findings, and any task needing empirical probing or cross-file authority determination. Once a gate returns a verbatim replacement string, every downstream call is transcription; a gate that must derive correct wording from history, runtime behavior, or conflicting sources earns `opus` regardless of the resulting diff size. **Gate fast path** (mandated by `_shared/agent-spawn-protocol.md` §Delegation cost discipline: the two-agent gate on a mechanical, unambiguous, single-file substitution is "cost without signal"): a file qualifies for the fast path only when ALL of its approved findings are in PARALLEL_SAFE_CATEGORIES AND the coalesced group is single-file by the classification above (writes only its own file, reads no file another group writes) AND no finding on that file is CRITICAL or HIGH. Fast-path files skip the pre-apply gate and go straight to their fix agent under the Model tiering rule above; record them under `fast_path` in the completion summary below. Every other file — any CRITICAL or HIGH finding, any cross-file dependency, any category outside PARALLEL_SAFE_CATEGORIES — takes the full gate below unchanged (findings where a wrong fix has real cost keep the full gate regardless of how small the diff looks). **Adversarial pre-apply gate**: for each unique file that didn't qualify for the fast path, spawn **foundry:challenger** AND **foundry:curator** in parallel — challenge/validate each finding batch: "Is each finding real? Is the fix appropriate? Does any fix risk removing load-bearing behavioral or structural/display content (e.g. CommonMark fence nesting, template rendering, visual output structure)?" Each writes verdict to `<RUN_DIR>/gate-<file-basename>.md`; return `{"verdict":"approved"|"blocked","reason":"<one-line>","file":"<path>"}`. Either returns `blocked` → mark findings for that file as blocked (add to `blocked_findings` list with reason); skip fix agent. Proceed to fix agent only if both return `approved`. Issue all gate spawns in a single response (parallel). After gate verdicts received: **Phase 1 — Parallel basket**: issue all parallel-safe approved fix spawns in a single response. Wait for all to complete. **Phase 2 — Sequential basket**: re-read the files modified in Phase 1 that are dependency inputs for Phase 2 fixes. Bounded direct read — narrows the "do NOT inline-read — orchestration contract" rule rather than dropping it: run `wc -l` over the dependency-input set first; at ≤10 files AND ≤2000 lines total, read them yourself with the Read tool, because this prompt already runs inside a disposable fix sub-agent whose context ends with the pipeline, so paying a ~120K-token spawn to avoid ~5–10K tokens of reads is inverted. Above either bound, keep the original contract: spawn a foundry:curator mini-agent to do the re-read, and do not inline-read. Use category→dependency rules: broken-cross-ref reads target agent/skill file; inventory-drift reads MEMORY.md; README-sync reads agent/skill source files. Serialize groups where group A output is group B input; parallel within independent groups. For each file that passed the gate or qualified for the fast path, spawn one fix agent (foundry:curator for .md files, foundry:sw-engineer for .js/.py files) with all approved findings batched into a single prompt — pass `model:` per the Model tiering rule above (parallel-safe/mechanical findings → `haiku`, override the agent's own opus-class default; placement/phrasing judgment → `sonnet`; never spend a fix-spawn on `opus`, reserved for the gate). After all fix agents complete: **Step R1 — curator re-audit**: spawn foundry:curator re-audit agents in batches of up to 5 changed files per spawn (BATCH_SIZE_MIN-aligned) to confirm fixes held — each spawn re-reads every file in its batch and returns per-file verdicts in one envelope: `{"files":[{"file":"<path>","held":true|false,"note":"<one-line>"}]}`. Every changed file is still re-audited; only spawn packaging changes. **Step R2 — challenger residual scan**: for every file where a CRITICAL finding was fixed, spawn foundry:challenger in batches of up to 2 files per spawn (ADVERSARIAL_BATCH_SIZE-aligned — adversarial passes use smaller batches for deeper per-file attention, so keep this batch below R1's) with prompt: "Fresh adversarial read of each of `<file list>`. No context from prior audit — read each file yourself before judging it. Find any CRITICAL issues present NOW. Return ONLY: {"files":\[{"file":"<path>","new_criticals":N,"findings":\[{"id":"<slug>","summary":"<one-line>"}\]}\]}". Issue all challenger spawns in single response (parallel). Write one result file per scanned file to `<RUN_DIR>/residual-<file-basename>.md`. Collect total `new_criticals` by summing every per-file entry across all results. Set `re_audit_clean: true` only if: curator confirms all fixes held AND total new_criticals = 0. Otherwise `re_audit_clean: false`. Write a completion summary to `<RUN_DIR>/fix-summary.md`:
      
      - findings_total: N
      - fixed: N
      - blocked: N (gate-rejected; listed in blocked_findings)
      - fast_path: N (files that skipped the pre-apply gate under the fast-path rule; each listed with its file path)
      - failed: N
      - residual_criticals: N (new criticals from post-fix challenger scan; 0 when no CRITICAL findings were fixed)
      - re_audit_clean: true|false (true = fixes held AND residual_criticals = 0)
      - blocked_findings: [{id, file, reason}, ...] Return ONLY: {"status":"done","file":"\<RUN_DIR>/fix-summary.md","fixed":N,"blocked":N,"failed":N,"residual_criticals":N,"re_audit_clean":true|false,"confidence":0.N}
      
    • checks-agents.md 5.8 KB
      # Agent Checks — 8, 13
      
      ## Check 19 — Model tier appropriateness
      
      Three capability tiers:
      
      | Tier | Model | Example agents |
      | -- | -- | -- |
      | Plan-gated | `opusplan` | solution-architect, oss:shepherd, curator |
      | Implementation | `opus` | sw-engineer, research:scientist, perf-optimizer |
      | Diagnostics / writing | `sonnet` | web-explorer, doc-scribe, research:data-steward, oss:cicd-steward, qa-specialist |
      | High-freq diagnostics | `haiku` | linting-expert |
      
      Extract declared models:
      
      ```bash
      printf "%-30s %s\n" "AGENT" "MODEL"
      for f in .claude/agents/*.md; do # timeout: 5000
          name=$(basename "$f" .md)
          model=$(awk '/^---$/{c++; if(c==2)exit} c==1 && /^model:/{sub(/^model: /,""); print}' "$f")
          printf "%-30s %s\n" "$name" "${model:-(inherit)}"
      done
      ```
      
      Use model reasoning. Classify each agent by tier from `<role>`, `description`, workflow body. Cross-ref vs declared model:
      
      - `focused-execution` + `opus`/`opusplan` → **medium** (potential overkill)
      - `deep-reasoning` + `sonnet` → **high** (likely underpowered)
      - **Orchestration signal**: workflow body contains `Spawn`, `Agent tool`, or explicit sub-agent delegation → classify `deep-reasoning` regardless of description — `sonnet` on orchestrating agent → **high**
      - `plan-gated` + `sonnet` → **high**
      - `focused-execution` + `haiku` → **not a finding**
      
      **Important**: CLAUDE.md `## Agent Teams` specifies models for team-mode spawn — NOT a mandate for agent frontmatter. Don't flag frontmatter models as violations for differing from CLAUDE.md team-mode spec.
      
      **Report only** — never auto-fix. Model assignments may be intentional trade-offs.
      
      ## Check 20 — Agent description routing alignment
      
      Canonical roster-consistency check. Three routing sub-checks + one decision check. All **report-only**.
      
      Extract all agent descriptions:
      
      ```bash
      printf "%-25s %s\n" "AGENT" "DESCRIPTION"
      for f in .claude/agents/*.md; do # timeout: 5000
          name=$(basename "$f" .md)
          desc=$(awk '/^---$/{c++; if(c==2)exit} c==1 && /^description:/{sub(/^description: /,""); print}' "$f")
          printf "%-25s %s\n" "$name" "$desc"
      done
      ```
      
      ### Apply model reasoning:
      
      **20a — Overlap analysis**: per agent pair, assess domain overlap. Flag pairs where descriptions don't disambiguate → **medium** per ambiguous pair.
      
      **20b — NOT-for clause coverage**: per high-overlap pair from 20a, check at least one agent has "NOT for" exclusion referencing other or its domain. Missing disambiguation → **medium**.
      
      **20c — Trigger phrase specificity**: per agent, check description's first clause states exclusive domain. Vague opener → **low**.
      
      **20d — Keep / sharpen / merge-prune decision**: Per overlap pair from 20a, explicit roster judgment:
      
      - **keep** — both agents own distinct acceptance criteria or review surfaces
      - **sharpen** — both stay, but one/both descriptions / NOT-for clauses / handoff notes need tightening
      - **merge-prune** — pair differs mostly by tone, examples, or tool list — not decision surface
      
      ### Decision rules:
      
      - Different tools alone → no separate role justified
      - Different examples alone → no separate role justified
      - Distinct acceptance criteria, escalation paths, or review surfaces → separate roles justified
      - Two agents swappable on realistic task with no material output difference → **merge-prune** candidate unless another file makes boundary explicit
      
      Every Check 20 finding must include: overlapping pair, shared surface, remaining distinct surface (if any), decision (`keep`, `sharpen`, `merge-prune`), concrete fix path.
      
      Fix reference: run `/calibrate routing` to verify description overlap translates to actual routing confusion.
      
      ## Check 46 — Roster boundary alignment
      
      Holistic roster-level analysis. Subsumes former `/distill review` mode. Run as part of `foundry:audit agents`.
      
      **46a — Per-pair overlap scan**: for every agent pair, compute scope overlap from descriptions + NOT-for clauses. Default threshold: **>50%** shared scope → flag. With `--eager`: threshold drops to **>30%**; any single shared named capability also flags as boundary issue.
      
      ```bash
      # Extract all agent descriptions for model reasoning
      printf "%-25s %s\n" "AGENT" "DESCRIPTION"
      for f in .claude/agents/*.md plugins/*/agents/*.md 2>/dev/null; do  # timeout: 5000
          [ -f "$f" ] || continue
          name=$(basename "$f" .md)
          desc=$(awk '/^---$/{c++; if(c==2)exit} c==1 && /^description:/{sub(/^[^:]*: /,""); print}' "$f")
          printf "%-25s %s\n" "$name" "$desc"
      done
      ```
      
      Use model reasoning to score each pair: `overlap_pct` = fraction of one agent's scope covered by the other. Flag pairs exceeding threshold.
      
      **46b — Coverage gap detection**: scan agent descriptions for task domains with no clear owner. Coverage gap = realistic task type where no agent's TRIGGER applies and no NOT-for exclusion explains the gap.
      
      Examples of coverage gap signals:
      
      - "Who handles X?" produces no confident agent → gap
      - Two agents exclude a domain ("NOT for Y") but no agent includes it → gap
      
      **46c — Sharpen Boundary section** (always include when ≥1 overlap pair found; required when `--eager`):
      
      ```markdown
      ### Sharpen Boundary
      
      | Pair | Shared capability | Recommended split |
      |------|------------------|-------------------|
      | agent-a / agent-b | <specific capability> | <which agent owns it; what NOT-for to add to the other> |
      ```
      
      **Report format** (report only — no auto-fix):
      
      ```markdown
      ## Check 46 — Roster Boundary Alignment
      
      ### Overlap Findings (threshold: >50% [or >30% with --eager])
      - **agent-a / agent-b** — overlap: ~N% — <shared domain> — decision: keep|sharpen|merge-prune
      
      ### Coverage Gaps
      - <task domain>: no clear owner — <which agent is closest; what's missing>
      
      ### Sharpen Boundary
      | Pair | Shared capability | Recommended split |
      ```
      
      Severity: **medium** per overlap pair above threshold; **low** per coverage gap; **medium** per missing Sharpen entry when `--eager`.
      
    • checks-index.md 9.7 KB
      <!-- file: checks-index.md — consumers: audit/SKILL.md Step 4 -->
      
      <!-- Quick-reference index only. Full check implementations in `audit/templates/checks-*.md`. -->
      
      | # | Name | Severity | Scope | Notes |
      | -- | -- | -- | -- | -- |
      | 1 | Inventory drift (MEMORY.md vs disk) | medium | setup | Agents + skills on disk vs MEMORY.md roster |
      | 2 | README vs disk | medium | setup | Agent/skill table rows in README vs disk |
      | 3 | settings.json permissions | medium | setup | Bash commands in skills vs allow list |
      | 4 | permissions-guide.md drift | medium | setup | Every allow entry needs a guide row, and vice versa |
      | 5 | Permission safety audit | critical/high | setup | Allow entries must be non-destructive, reversible, local-only |
      | 6 | Stale settings.json allow entries | low | setup | Allow entries with no usage in any .claude/ file |
      | 7 | codex plugin integration | medium | setup | Plugin installed and enabled; dispatches work |
      | 8 | foundry plugin correctness | critical/high/med | setup | 8a manifest, 8b symlinks, 8c hook scripts, 8d hooks.json, 8e dry-run validate, 8f perms drift, 8g init skill placement |
      | 9 | Agent color drift | medium | setup | statusline COLOR_MAP vs agent frontmatter color: |
      | 10 | RTK hook alignment | high/medium | setup | RTK_PREFIXES vs installed RTK subcommands - skip if rtk absent |
      | 11 | Memory health | low | setup | 11a duplicate rules, 11b stale version pins, 11c absorbed feedback files |
      | I1 | Plugin cache intact | high | setup | foundry in ~/.claude/plugins/installed_plugins.json; installPath exists |
      | I2 | Settings merge complete | medium | setup | statusLine, permissions.allow, enabledPlugins.codex in ~/.claude/settings.json |
      | I3 | Link health (conditional) | high | setup | Symlinks in ~/.claude/rules/ and ~/.claude/TEAM_PROTOCOL.md resolve; fix: /foundry:setup |
      | 12 | File length | medium | all | Agents ≈300 lines, skills ≈600 lines, rules ≈200 lines; report only — fix = remove content, never collapse lines |
      | 13 | Heading hierarchy continuity | medium | all | Heading level jumps >1 (e.g. ## to ####) |
      | 14 | Orphaned follow-up references | medium | agents/skills | Skill-name refs in SKILL.md vs disk inventory |
      | 15 | Hardcoded user paths | high | agents/skills | /Users/ and /home/ in config files + settings.json |
      | 16 | Example value vs. token cost | low | agents/skills | Inline examples: high-value vs. low-value (prose restatement) |
      | 17 | Cross-file code block inventory | low | agents/skills | Block count table across all .md files (skills, modes, \_shared, templates, agents, rules); flag files with ≥10 blocks for `--efficiency` run. NxN similarity analysis moved to `--efficiency` Phase B2 (expensive) |
      | 18 | Rules integrity | high/medium | rules | 18a inventory, 18b frontmatter, 18c redundancy, 18d cross-ref integrity |
      | 19 | Model tier + effort appropriateness | medium/high | agents | Tier policy: fable/opusplan/opus/sonnet/haiku; effort low/medium/high/xhigh/max — report only. Flag `effort:` on a haiku-tier agent (Haiku 4.5 ignores it) and any tier downgrade with no A/B evidence |
      | 20 | Agent description routing | medium/low | agents | 20a overlap pairs, 20b NOT-for coverage, 20c trigger specificity, 20d keep/sharpen/prune |
      | 22 | Calibration coverage gap | medium/low | agents/skills | Unregistered calibratable skills/agents; stale domain table entries |
      | 23 | Bash misuse / native tool substitution | medium | agents/skills | cat/grep/find/echo>/sed replaceable by native tools; 23a python inline; 23b `# timeout: N` without `timeout S` shell prefix or Python `subprocess.*` without `timeout=`; 23c `eval "$(...)"` for multi-value bin/ output — use §Script Output Routing (TMPDIR files) instead; 23d shell variable used across `Bash()` calls — var dies at shell boundary, write to TMPDIR file |
      | 24 | Skill sequence compatibility | high/medium | skills | 24a target skill not on disk; 24b argument absent from argument-hint; scans skills, agents, READMEs |
      | 25 | Implicit agent references | high | agents/skills | subagent_type without plugin prefix; exempt: built-in types |
      | 26 | Symbol and shortcut consistency | medium/low | agents/skills | 26a same-concept emoji conflict, 26b slash notation mixed, 26c body contradicts legend |
      | 27 | Cross-plugin shared-file ref integrity | critical/high/med | skills | 27a absent from foundry/\_shared/; 27b catch-22 (fallback needs foundry); 27c plugin-local \_shared/ unmounted |
      | 28 | Cross-plugin agent dispatch fallback | high/medium | skills | 28a no fallback for cross-plugin dispatch; 28b fallback present but incomplete |
      | 29 | LLM context minimality | medium/low | agents/skills/rules | Within-file repetition, prose inflation, obvious-consequence restatement — report only; 29a trigger-inverse restatement; 29b hedged/non-actionable directives |
      | 30 | Config token overhead | medium/low | setup | 30a CLAUDE.md + global + rules/ > 100 KB; 30b single rules file > 10 KB |
      | 31 | Tool-body consistency | medium | skills | Skill `allowed-tools` must include every tool the workflow body invokes; see `checks-skills.md` for full spec |
      | 32 | Dead file detection | medium/low | skills/rules | 32a mode files in `*/modes/` not referenced from parent SKILL.md; 32b template files in `*/templates/` not referenced; 32c rule files whose `paths:` globs match no project files; 32d orphaned bin/ scripts not referenced by any plugin .md file (LOCAL_MODE only) |
      | 33 | Code block similarity + extraction feasibility | medium/low | skills | `--efficiency` only — Table 1: pairwise similarity per plugin; Table 2: rigidity + extraction feasibility + pos/neg impact. See `modes/efficiency.md` Phase B2 |
      | R1 | Computed path resolution (local + installed duality) | high/medium/info | plugins (LOCAL_MODE only) | R1-FAIL: file exists locally but absent from installed cache; R1-WARN: installed-only file; R1-INFO: plugin not installed. Root cause guard for silent-deletion class of bugs |
      | R2 | Grep-visible referencing (orphan-risk detection) | medium | plugins (LOCAL_MODE only) | Basename of indirect-load .md file (modes/, templates/, \_shared/) not literal in any consumer .md — deletion-prone; fix: add `# loads: <basename>` comment |
      | R3 | bin/ script existence at local + installed | high | plugins (LOCAL_MODE only) | R3-FAIL: script referenced but missing locally; R3-WARN: script local but absent from installed cache |
      | R4 | bin/ Python test coverage | medium | plugins (LOCAL_MODE only) | Every `bin/*.py` has matching non-empty `tests/test_<basename>.py` with ≥1 `def test_` function that is not a pure `pass`/`...` stub |
      | 46 | Roster boundary alignment | medium/low | agents | 46a per-pair overlap >50% (>30% with --eager), 46b coverage gaps (task domain with no clear owner), 46c Sharpen Boundary section when --eager |
      | 35 | $ARGUMENTS shell injection | security/high | agents/skills | Bash blocks interpolating argument text or env-var user input without handing it to a bin/ parser; `security` when an interpreter runs the text, `high` when only a shell construct guards it |
      | 36 | eval-unsafe bin/ output | security | agents/skills | Python bin/ scripts producing shell assignments for eval without `shlex.quote` |
      | 37 | Hardcoded secrets in config | security | all | API keys, tokens, passwords literal in any plugin `.md`, `settings.json`, or hook `.js` |
      | 38 | AskUserQuestion cap violation | high | skills | Skill branch path with >4 `AskUserQuestion` calls — exceeds `communication.md:86` hard limit |
      | 39 | Plugin version freeze | medium | setup | `plugin.json` version unchanged vs HEAD despite modified plugin files |
      | 40 | Health monitoring gap | high/medium | agents/skills | Skill calling `Agent(subagent_type=…)` without sentinel, liveness probe, or hard cutoff (high); or still prescribing a fixed-interval poll (medium — spawns are background, nothing sleeps) |
      | R5 | Consumer→template orphan | medium | plugins (LOCAL_MODE only) | `<!-- loads: X -->` or `# loads: X` comment points to non-existent template file — reverse of R2 |
      | 41 | LLM-first formatting | low/medium | all .md excl. README | 41a list-marker uniformity (`-` only); 41b numbering-intent clarity (`1.`=steps, `(a)`=choices); 41c table-vs-prose preference (3+ items × 2+ attrs → table); 41d legacy phase/step numbering (`Phase A.5`/`Step 1.5` instead of canonical `1b`) |
      | 42 | CLI flag drift vs. spawn-prompt vars (number collision) | medium/critical | plugins | Two unrelated checks both claim `42`: index/Step-1b use it for CLI flag drift (`check_cli_flag_drift.py`, SKILL.md flags vs argparse, medium); `checks-skills.md` separately defines `Check 42` as "Unexpanded variables in agent spawn prompts" (`check_spawn_prompt_vars.py`, critical). Pre-existing, predates Check 45 — not resolved here; needs a deliberate renumber pass (touches severity tables + dispatch lists in `steps-4-5-7.md`), not a drive-by fix |
      | 43 | Shell variable persistence across Bash calls | critical | plugins/skills | `check_bash_persistence.py` — `$VAR` referenced in bash block N, assigned only in earlier block M — each Bash call is a fresh shell, silently expands to empty string |
      | 44 | Sub-check naming symmetry vs. TMPDIR sentinel scoping (number collision) | low/high | agents/skills/rules | Two unrelated checks both claim `44`: `checks-shared.md` defines it as "Sub-check naming symmetry" (`Check Nb` without `Check Na`, low); `checks-skills.md` separately defines it as "TMPDIR sentinel session scoping" (bare `/tmp` sentinel names collide across sessions, critical). Pre-existing, predates Check 45 — not resolved here |
      | 45 | Policy-sibling marker symmetry | high/medium | all .md | 45-BROKEN: `<!-- policy-sibling: ... -->` lists a path not on disk; 45-ASYMMETRIC: listed sibling has no marker pointing back — reference-graph completeness for policies restated (not cross-ref'd) across files |
      
    • checks-install.md 11.4 KB
      # Install Checks — I1, I2, I3
      
      Checks validate post-install state in `~/.claude/`. Operate on home dir, not project `.claude/`. Run via `/foundry:audit setup` (or `/audit setup` after `foundry:setup link`).
      
      ## Check I1 — Plugin cache intact
      
      Verify foundry plugin installed, cache dir accessible.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_install_state.py" --check I1  # timeout: 10000
      ```
      
      **Severity**: missing/broken cache → **high** (plugin non-functional).
      
      ## Check I2 — Settings merge complete
      
      Verify `foundry:setup` ran: `~/.claude/settings.json` has required entries, no stale hooks block.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_install_state.py" --check I2  # timeout: 10000
      ```
      
      **Severity**: missing entry or stale hooks block → **medium** per sub-check (non-blocking, degrades functionality). Fix: re-run `/foundry:setup` — idempotent.
      
      ## Check I3 — Link health (conditional)
      
      Two distinct expectations, both asserted here:
      
      - `~/.claude/agents/` and `~/.claude/skills/` must contain **zero** foundry symlinks. Both namespaces dispatch from the plugin (`foundry:sw-engineer`, `/foundry:audit`); a skills link additionally registers a user-level skill shadowing Claude Code's bundled skill of the same name. `/foundry:setup` Step 10 Phase 1 purges both.
      - `~/.claude/rules/` and `~/.claude/TEAM_PROTOCOL.md` **are** symlinked, so checked for staleness — they break silently when a version upgrade moves the cache path.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_install_state.py" --check I3  # timeout: 10000
      ```
      
      **Severity**: both → **high**. A foundry symlink under `agents/` or `skills/` shadows namespaced dispatch; a broken `rules/` symlink silently drops a rule file. Fix for either: re-run `/foundry:setup` (no `link` subcommand exists — `argument-hint` is `[--approve]`).
      
      ## Check R1 — Computed path resolution (local + installed duality)
      
      Root cause guard for `adversarial.md` / `upgrade.md` silent-deletion bug class. Skill `.md` files construct paths via variable substitution (`$AUDIT_TPL/../modes/upgrade.md`, `$_FS/task-hygiene.md`, `${CLAUDE_PLUGIN_ROOT:-plugins/<x>}/bin/<script>`). Those paths exist as literal strings only if target filename is grep-visible. File existing locally but never copied to installed plugin cache silently fails for users who install plugin.
      
      **What it checks**: for every computed-path reference in `plugins/*/skills/*/SKILL.md`, `plugins/*/skills/*/modes/*.md`, `plugins/*/agents/*.md` — verify resolved target exists both locally (`plugins/<plugin>/...`) and in installed cache (`~/.claude/plugins/cache/borda-ai-rig/<plugin>/*/<path>`). Skip if `LOCAL_MODE != true` (no plugin source tree to scan).
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check R1: Computed path resolution (local + installed duality) ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check R1 skipped in non-local mode (no plugin source tree)\n"
      else
          python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_routing_links.py" \
              --plugins-dir plugins \
              --installed-plugins-json ~/.claude/plugins/installed_plugins.json \
              --check R1  # timeout: 15000
      fi
      ```
      
      **Severity**:
      
      - `R1-FAIL` (file exists locally but absent from installed cache) → **high** — users who install plugin get broken dispatch at runtime; likely file added locally but plugin not re-installed
      - `R1-WARN` (file exists in installed cache but absent locally) → **medium** — stale installed copy; breaks after plugin update
      - `R1-INFO` (plugin not installed) → **low/info** — cannot verify installed state; note in report only
      
      Fix: re-install plugin with `claude plugin install <plugin>@borda-ai-rig` to sync installed state with local source tree. WARN: restore missing local file or remove reference.
      
      ## Check R2 — Grep-visible referencing (orphan-risk detection)
      
      Structural guard: for every `.md` file in `plugins/*/skills/*/modes/`, `plugins/*/skills/*/templates/`, `plugins/*/skills/_shared/` — verify its **basename** appears as literal string in ≥1 consumer `.md` file in same plugin.
      
      **Scope**: `modes/`, `templates/`, `_shared/` only. SKILL.md and agent `.md` files covered by Check 32a (checks-skills.md); R2 complementary — covers subdirectories 32a doesn't walk.
      
      **Why**: grep-based dead-file checks (Check 32a, 32b) and agent zero-hit analysis search for filename. File loaded only via computed path (e.g. `$AUDIT_TPL/../modes/adversarial.md`) has zero literal-basename hits → grep tools conclude unreferenced → deletion risk.
      
      Skip if `LOCAL_MODE != true`.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check R2: Grep-visible referencing (orphan-risk detection) ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check R2 skipped in non-local mode (no plugin source tree)\n"
      else
          python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_routing_links.py" \
              --plugins-dir plugins \
              --check R2  # timeout: 15000
      fi
      ```
      
      **Severity**: `R2-ORPHAN-RISK` → **medium** — file is grep-invisible; any automated or agent-assisted dead-file sweep will incorrectly flag as unreferenced and may delete it.
      
      Fix: add comment in consumer `SKILL.md` making basename a literal string, e.g.:
      
      ```
      # loads: adversarial.md  (via $AUDIT_TPL/../modes/adversarial.md)
      ```
      
      This single-line comment costs ~5 tokens, permanently protects file from grep-based false-positive orphan detection.
      
      ## Check R3 — bin/ script reference integrity (reverse of Check 32d)
      
      Check 32d walks `bin/` scripts, flags those unreferenced by any `.md` file (orphaned scripts). R3 is the reverse: for every `${CLAUDE_PLUGIN_ROOT:-plugins/<x>}/bin/<script>` reference in any plugin `.md` file, verify script exists locally — catches typos, deleted scripts, refactor leftovers leaving dangling references.
      
      Skip if `LOCAL_MODE != true`.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check R3: bin/ script existence (local + installed) ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check R3 skipped in non-local mode (no plugin source tree)\n"
      else
          python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_routing_links.py" \
              --plugins-dir plugins \
              --installed-plugins-json ~/.claude/plugins/installed_plugins.json \
              --check R3  # timeout: 15000
      fi
      ```
      
      **Severity**:
      
      - `R3-FAIL` (script referenced but missing locally) → **high** — skill dispatch fails immediately at `python ...` call site
      - `R3-WARN` (script exists locally but absent from installed cache) → **high** — same as R1-FAIL but for bin/ scripts; users get broken skill at runtime after install
      
      Fix: create missing script locally (FAIL) or re-install plugin to sync (WARN).
      
      > **Convenience shortcut**: run all three checks together:
      >
      > ```bash
      > python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_routing_links.py" \
      >     --plugins-dir plugins \
      >     --installed-plugins-json ~/.claude/plugins/installed_plugins.json  # timeout: 20000
      > ```
      >
      > Omitting `--check` runs R1, R2, R3 in one pass.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | R1-FAIL — local-only file | resolved file exists locally but absent from installed cache | high | no — re-install plugin |
      | R1-WARN — installed-only file | file exists in cache but missing locally | medium | no — restore local file or remove ref |
      | R1-INFO — plugin not installed | cannot verify installed state | info | n/a |
      | R2-ORPHAN-RISK — grep-invisible | basename not literal in any consumer .md file | medium | no — add `# loads: <basename>` comment |
      | R3-FAIL — bin/ script missing locally | script referenced but local file absent | high | no — create script |
      | R3-WARN — bin/ script missing from cache | script local but absent from installed cache | high | no — re-install plugin |
      
      ## Check R4 — bin/ Python test coverage
      
      For every `plugins/<plugin>/bin/<script>.py`, verify corresponding `plugins/<plugin>/tests/test_<script>.py` exists and non-empty. Skip if `LOCAL_MODE != true`.
      
      **Implementation note**: `check_orphaned_bin.py` lacks `--check-tests`; until that flag is added, run the check inline:
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_bin_test_coverage.py" $( [ "$LOCAL_MODE" = "true" ] && echo "--local" )  # timeout: 30000
      ```
      
      **Severity**:
      
      - `R4-FAIL` (any sub-check) → **medium** — plugin policy violation; new bin/ scripts must ship with tests containing real assertions
      
      Fix: create `tests/test_<basename>.py` with at minimum one test class covering script's public API.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | R4-FAIL — missing test file | `bin/*.py` with no matching `tests/test_*.py` | medium | no — write tests |
      | R4-FAIL — empty test file | `tests/test_*.py` exists but has zero size | medium | no — populate test file |
      | R4-FAIL — no test functions | test file non-empty but has zero `def test_` functions | medium | no — write tests |
      | R4-FAIL — stub tests only | all `def test_` functions body is only `pass` or `...` | medium | no — implement assertions |
      
      Note: the `_*.py` exclusion (e.g. `_schema.py`) is intentional only for pure type-definition modules with no runnable logic. Files with `__name__ == "__main__"` guards must have tests regardless of leading underscore. Auditor verifies exclusion appropriate per file when `_FAIL` reports absent.
      
      ## Check R5 — Consumer→template orphan (reverse of R2)
      
      Check R2 verifies every template file has its basename visible in a consumer `.md` file. R5 is the reverse: for every `<!-- loads: X -->` or `# loads: X` comment in any `.md` file, verify `X` exists on disk (locally or in installed cache).
      
      Catches deleted or renamed templates where consumer `<!-- loads: -->` comment wasn't updated — silent runtime failure when audit tries to `Read $AUDIT_TPL/X`.
      
      Skip if `LOCAL_MODE != true`.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check R5: Consumer→template orphan ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check R5 skipped in non-local mode\n"
      else
          grep -rn "loads:\s\+\([a-zA-Z0-9_-]\+\.md\)" plugins/ \
              --include="*.md" -o 2>/dev/null \
          | sed 's/.*loads:[[:space:]]*//' \
          | sort -u \
          | while IFS= read -r target; do
              found=$(find plugins -name "$target" 2>/dev/null | head -1)
              if [ -z "$found" ]; then
                  printf "R5-FAIL: loads: %s — file not found in plugins/ tree\n" "$target"
              fi
          done  # timeout: 10000
      fi
      ```
      
      **Severity**: medium — audit itself may crash at runtime trying to read missing template; silent breakage for users. Fix: restore missing template file or remove/update stale `<!-- loads: -->` comment in consumer.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | R5-FAIL — missing template | `loads: X` comment but `X` not found on disk | medium | no — restore file or remove dead comment |
      
    • checks-security.md 7.8 KB
      <!-- file: checks-security.md — consumers: audit/SKILL.md Step 4 -->
      
      <!-- Quick-reference: Check 35 ($ARGUMENTS injection), Check 36 (eval-unsafe output), Check 37 (hardcoded secrets). -->
      
      <!-- security findings appear in a dedicated Security Findings section of the audit report, before functional findings. -->
      
      ## Check 35 — $ARGUMENTS shell injection risk security
      
      Bash blocks in any SKILL.md that interpolate `$ARGUMENTS`, `$SCAN_ARGS`, or `$SCAN_QUERY` (or any unvalidated env var representing user-supplied input) directly into shell string without sanitization.
      
      `$ARGUMENTS` is not an environment variable in the Bash tool environment — `env` contains no such entry. Argument text reaches the command as a literal written into the source before that source runs. **No shell-level construct sanitizes it**, because every one of them runs after the text is already parsed as program source. The patterns below differ on how much of the blob the shell must interpret before something safer takes over, and how the construct fails when interpretation goes wrong.
      
      **Least exposed** (satisfies Check 35):
      
      - Passing the whole blob as one positional arg to a Python bin/ script (`python ... "$ARGUMENTS"`), which parses it in Python. The shell still must get the quoting right for the argument to arrive as a single argv element — a bare `"` at a word boundary splits it into extra slots, and anything after a closing quote is read as shell source. This buys: parsing logic itself is no longer shell — no `BASH_REMATCH`/`match` divergence, no re-splitting on the value's own spaces, failure is a Python-side error rather than a silently-empty capture.
      - Whether the receiving script then uses `shlex` is the script's own business — don't assume it does. Repo examples that genuinely do: `cc_oss/bin/parse-skill-flags.py`, `cc_oss/bin/parse-resolve-args.py`, `codemap-py/bin/parse_scan_args.py`. `cc_foundry/bin/extract-keep-flag.py` regex-searches the raw blob instead — sufficient for its one quoted flag but not shlex.
      
      **Conditionally acceptable** — each bullet names the one variable the block must assign; read the bullet, not the block, to decide which. `$ARGUMENTS` itself never satisfies this tier — it's a literal written into the source, not a name a block can assign — so a bare `$ARGUMENTS` use reaches step 3. An alias (`$SCAN_ARGS`, `$SCAN_QUERY`) that no block assigns reads as empty, so the guard passes over nothing and the skill silently proceeds with no arguments: a finding, not a pass.
      
      - `shlex.split(os.environ.get("ARGUMENTS", ""))` — Python-side splitting. The block must put `ARGUMENTS` in the child's environment itself (`ARGUMENTS="..." python ...`); the Bash tool environment does not carry it, so without that the script reads the empty string.
      - `EXEC_ARGS="${ARGUMENTS#prefix}"`, then every later use of `EXEC_ARGS` either double-quoted (`[ -d "$EXEC_ARGS" ]`) or passed through `shlex.quote`. The named variable is `EXEC_ARGS`; the assignment alone is not handling. One unquoted `$EXEC_ARGS` in interpolation position drops the bullet to step 3.
      
      **Never counts as handling** (does not satisfy Check 35):
      
      - `[[ "$ARGUMENTS" =~ ^safe-pattern$ ]]` guard before use. Text carrying a quote or a newline breaks the block at parse time, so the guard never executes and every step below it in that block is skipped. Also can't capture portably: `[[ =~ ]]` sets `BASH_REMATCH` under bash but `match` under zsh, so `${BASH_REMATCH[1]}` silently yields the empty string there and the guarded value is lost rather than rejected.
      - `[[ "$ARGUMENTS" == *"--flag"* ]]` substring tests. A substring match fires on the flag name appearing anywhere, including inside a quoted value the user passed to a different flag.
      
      **Unsafe patterns** (flag as security):
      
      - `eval "cmd $ARGUMENTS"` or `bash -c "... $ARGUMENTS ..."` (direct shell eval)
      - `python -c "... $ARGUMENTS ..."` (inline python with injected argument)
      - Unquoted `$ARGUMENTS` in heredoc expansion position
      
      Scan all `*/SKILL.md` and `*/skills/*/SKILL.md` files in scope. For each bash code block containing `$ARGUMENTS` (or an env-var alias like `$SCAN_ARGS`, `$SCAN_QUERY`), decide in this order and stop at the first match:
      
      1. A **Least exposed** pattern appears in the same or a preceding line of that block → pass.
      2. A **Conditionally acceptable** pattern appears in full — the variable that bullet names is assigned in this block, **and** the bullet's own condition on its later uses holds → pass. The assignment must be visible in the block; an inherited value doesn't count, because shell state doesn't survive between Bash calls.
      3. Otherwise → flag. This includes a block whose only handling is a `[[ ]]` test or a substring comparison, a bare `$ARGUMENTS` use with no bin/ script behind it, and a **Conditionally acceptable** pattern whose named variable is never assigned or whose condition fails.
      
      ```bash
      printf "=== Check 35: \$ARGUMENTS shell injection risk ===\n"
      ```
      
      **Severity**: two tiers, decided by whether an interpreter receives the argument text as source.
      
      - `security` — an **Unsafe pattern** above: `eval`, `bash -c`, `python -c`, or unquoted expansion in heredoc position. The text runs.
      - `high` — a block that reaches step 3 with only a shell construct handling the blob: a `[[ =~ ]]` guard, a `case`, or a substring comparison. Nothing executes the text, so this isn't an injection vector; it's a guard that can't be relied on — `[[ =~ ]]` captures into `match` under zsh, and a quote or newline in the blob breaks the block at parse time, skipping every step below it.
      
      Fix, both tiers: hand the whole blob to a bin/ script as one positional argument and parse it there (`bin/parse-skill-flags.py`, shipped by every plugin, for boolean and value flags; codemap-py's `bin/parse_scan_args.py` for a shlex-based reference).
      
      ## Check 36 — eval-unsafe bin/ output security
      
      Python bin/ scripts producing shell variable assignments for `eval $()` in a calling SKILL.md must quote all dynamic values with `shlex.quote`. Unquoted values allow injection via crafted env var content.
      
      **Safe pattern (required for eval-consumed output)**:
      
      ```python
      import shlex
      print(f"VAR={shlex.quote(value)}")
      ```
      
      **Unsafe pattern (flag)**:
      
      ```python
      print(f"VAR={value}")  # unquoted — injection risk if value contains shell metacharacters
      ```
      
      Scan: for each Python script in `plugins/*/bin/` producing lines of form `VAR=value` (detectable via `print(f"VAR=` or `print("VAR=` in source), verify `shlex.quote` wraps value. Flag scripts with bare `print(f"... = {` patterns in assignment position without `shlex.quote`.
      
      Exempt scripts producing only JSON output (no shell assignment format).
      
      ```bash
      printf "=== Check 36: eval-unsafe bin/ output ===\n"
      grep -rn 'print(f"[A-Z_]*=' plugins/*/bin/*.py 2>/dev/null \
        | grep -v 'shlex.quote' \
        | grep -v '\.json' \
        | while IFS= read -r hit; do
            printf "R36-WARN: potential eval-unsafe output: %s\n" "$hit"
          done  # timeout: 5000
      ```
      
      **Severity**: `security` — exploitable only when calling SKILL.md passes crafted env var through eval-consumed bin/ script. Fix: wrap dynamic values in `shlex.quote()` before printing assignment strings.
      
      ## Check 37 — Hardcoded secrets in config security
      
      Any hardcoded API key, token, password, or bearer credential in plugin `.md` files, `settings.json`, or hook `.js` files.
      
      ```bash
      printf "=== Check 37: Hardcoded secrets in config ===\n"
      grep -rniE '(api[-_]?key|token|secret|password|bearer)\s*[=:]\s*["'"'"'][a-zA-Z0-9+/=_-]{16,}["'"'"']' \
          plugins/ .claude/settings.json .claude/hooks/*.js 2>/dev/null \
          | grep -v '# example\|# placeholder\|YOUR_\|<your\|XXXXXX\|example.com'  # timeout: 5000
      ```
      
      Any hit not an example/placeholder pattern → `security` finding.
      
      **Severity**: `security` — immediate secret rotation required. Fix: remove secret from config; use env var reference (`$MY_API_KEY`) or system keychain; never commit secrets to plugin files.
      
    • checks-setup.md 11.3 KB
      # Setup Checks — 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
      
      ## Check 1 — Inventory drift (MEMORY.md vs disk)
      
      Use Glob (`agents/*.md`, path `.claude/`) to list agent files; extract basenames, sort, write to `/tmp/agents_disk.txt` via Bash:
      
      ```bash
      ls .claude/agents/*.md 2>/dev/null | xargs -n1 basename 2>/dev/null | sed 's/\.md$//' | sort >/tmp/agents_disk.txt || true # timeout: 5000
      ```
      
      Read `- Agents:` and `- Skills:` roster lines from MEMORY.md content injected in conversation context (auto-memory at session start). Don't Grep file path — MEMORY.md not under `.claude/` but in Claude Code's auto-memory system. Repeat with Glob (`skills/*/`, path `.claude/`) for skills on disk — write to `/tmp/skills_disk.txt`.
      
      **macOS caution**: BSD grep treats args starting with `-` as option flags. Building bash comparison from MEMORY.md roster via grep: use `grep -E 'Agents:'` (no leading `- `) or `grep -- '- Agents:'`, not `grep '- Agents:'` — latter exits 2 on macOS, silently produces empty result. Safest: use Read tool (not grep) for MEMORY.md.
      
      ## Check 2 — README vs disk
      
      Use Grep tool (pattern `^\| \*\*`, file `README.md`, output mode `content`) to extract agent/skill table rows.
      
      ## Check 3 — settings.json permissions
      
      Use Grep tool (pattern `gh |python -m|ruff|mypy|pytest`, glob `skills/*/SKILL.md`, path `.claude/`, output mode `content`) to collect bash commands used in skills.
      
      ## Check 4 — permissions-guide.md drift
      
      Every allow entry must appear in guide, and vice versa.
      
      ```bash
      if [ "${JQ_AVAILABLE:-false}" = "false" ] || ! command -v jq &>/dev/null; then  # timeout: 5000
          printf "⚠ SKIPPED: Check 4 — jq not available\n"
      elif [ ! -f ".claude/settings.json" ]; then
          printf "⚠ SKIPPED: Check 4 — .claude/settings.json not found\n"
      elif [ ! -f ".claude/permissions-guide.md" ]; then
          printf "⚠ SKIPPED: Check 4 — .claude/permissions-guide.md not found\n"
      else
          jq -r '.permissions.allow[]' .claude/settings.json 2>/dev/null | \  # timeout: 5000
          while IFS= read -r perm; do
              grep -qF "\`$perm\`" .claude/permissions-guide.md 2>/dev/null \
                  || printf "⚠ MISSING from guide: %s\n" "$perm"
          done
      
          grep '^| `' .claude/permissions-guide.md 2>/dev/null | awk -F'`' '{print $2}' | \  # timeout: 5000
          while IFS= read -r perm; do
              jq -e --arg p "$perm" '(.permissions.allow // []) + (.permissions.deny // []) | contains([$p])' .claude/settings.json > /dev/null 2>&1 \  # timeout: 5000
              || printf "⚠ ORPHANED in guide: %s\n" "$perm"
          done
      fi
      ```
      
      ## Check 5 — Permission safety audit
      
      Every `allow` entry must be non-destructive, reversible, local-only.
      
      Read `.claude/settings.json` with Read tool, extract `permissions.allow` list. For each entry, use model reasoning to evaluate against three criteria:
      
      - **Non-destructive**: no permanent delete/overwrite (no `rm -rf`, `git push --force`, `DROP TABLE`)
      - **Reversible**: effect undoable without data loss (local file edits, test runs, read-only queries)
      - **Local-only**: no effect outside working directory, no external data transmission
      
      Flag destructive patterns as **critical** (auto-approved destructive commands always = breaking safety failure). Flag external-state mutations as **high**, raise to user — some (e.g. `gh release create`) may be intentional but must be explicitly acknowledged.
      
      ## Check 6 — Stale settings.json allow entries
      
      ```bash
      if [ "${JQ_AVAILABLE:-false}" = "false" ] || ! command -v jq &>/dev/null; then # timeout: 5000
          printf "⚠ SKIPPED: Check 6 — jq not available\n"
      elif [ ! -f ".claude/settings.json" ]; then
          printf "⚠ SKIPPED: Check 6 — .claude/settings.json not found\n"
      else
          printf "=== Check 6: Stale allow entries ===\n"
          jq -r '.permissions.allow[]' .claude/settings.json 2>/dev/null | while IFS= read -r entry; do # timeout: 5000
              cmd=$(echo "$entry" | sed 's/^[A-Za-z]*(\(.*\))$/\1/' | sed 's/^"\(.*\)"$/\1/')
              hits=$(grep -rl "$cmd" .claude/agents/ .claude/skills/ .claude/rules/ .claude/hooks/ .claude/CLAUDE.md 2>/dev/null | wc -l | tr -d ' ') # timeout: 5000
              if [ "$hits" -eq 0 ]; then
                  printf "⚠ STALE allow: %s — no usage found in .claude/ files\n" "$entry"
              fi
          done
          printf "✓: Check 6 scan complete\n"
      fi
      ```
      
      **Severity**: **low** per stale entry. Fix: remove stale entry from `settings.json` (report only — `settings.json` never auto-edited per audit policy).
      
      **Important**: some allow entries intentionally grant broad patterns (e.g. `Bash(mkdir -p .reports/audit/*)`) not appearing verbatim in config files — exercised at runtime. Flag only entries whose command fragment appears nowhere in any `.claude/` file.
      
      ## Check 7 — bridge-to-Codex plugin integration check
      
      Skip when `bridge@borda-ai-rig` is absent.
      
      ```bash
      CODEX_STATUS=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_bridge.py" --status 2>/dev/null || echo "absent") # timeout: 5000
      if [ "$CODEX_STATUS" = "absent" ]; then
          printf "⚠ SKIPPED: Check 7 — bridge@borda-ai-rig not installed\n"
      elif [ "$CODEX_STATUS" = "disabled" ]; then
          printf "⚠ WARN: Check 7 — bridge@borda-ai-rig installed but DISABLED\n"
          printf "  Fix: run \`claude plugin enable bridge@borda-ai-rig\` then \`/reload-plugins\`\n"
      else
          printf "✓ OK: Check 7 — bridge@borda-ai-rig present and enabled\n"
      fi
      ```
      
      - Plugin installed but **disabled** → **medium** (fix: `claude plugin enable bridge@borda-ai-rig` + `/reload-plugins`)
      - Plugin present but dispatches fail → **high** (verify with `/calibrate skills`)
      
      ## Check 8 — foundry plugin correctness
      
      Verify repo's `foundry` plugin structure at `plugins/cc_foundry/`. Skip if not found.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_plugin_layout.py" --plugin-dir plugins/cc_foundry --expect-name foundry  # timeout: 60000
      ```
      
      **Severity**: manifest missing/invalid JSON → **critical**; broken symlink, hooks.json invalid, hooks.json references missing file, or `claude plugin validate` fails → **high**; .js plugin file is symlink (not real file) → **medium**; 8f settings.json entries missing from permissions-allow.json → **medium** (plugin users won't get them; sole direction that sets exit 1); permissions-allow.json entries missing from settings.json → **low**; setup-foundry SKILL.md missing → **high**; missing required keyword coverage → **medium**. **Report only** — never auto-fix.
      
      ## Check 9 — Agent color drift (statusline COLOR_MAP vs frontmatter)
      
      ```bash
      for f in .claude/agents/*.md; do # timeout: 5000
          name=$(basename "$f" .md)
          color=$(awk '/^---$/{c++; if(c==2)exit} c==1 && /^color:/{sub(/^color: */,""); print}' "$f")
          [ -n "$color" ] && printf "%s: %s\n" "$name" "$color"
      done
      ```
      
      Use model reasoning to cross-reference each extracted color name against `COLOR_MAP` keys in `.claude/hooks/statusline.js`. Flag:
      
      - Color in agent frontmatter but **not a key in `COLOR_MAP`** → **medium** (agent appears uncolored)
      - Color in `COLOR_MAP` not declared by any agent → **low** (dead mapping, no functional impact)
      
      ## Check 10 — RTK hook alignment
      
      Verify prefix list in `.claude/hooks/rtk-rewrite.js` (`RTK_PREFIXES` array) consistent with commands installed RTK binary supports.
      
      Skip if RTK not installed (`rtk --version` fails) or `.claude/hooks/rtk-rewrite.js` not found.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_rtk_alignment.py"  # timeout: 30000
      ```
      
      Severity: invalid prefix entries = **high**; missing filterable commands = **medium**. **Report only** — never auto-fix.
      
      ## Check 11 — Memory health (MEMORY.md noise accumulation)
      
      MEMORY.md has 200-line truncation limit. Three sub-checks:
      
      **Check 11a — Duplicate with CLAUDE.md**: Read both MEMORY.md and CLAUDE.md. For each MEMORY.md section, check if same rule or directive exists verbatim or near-verbatim in CLAUDE.md. Flag duplicates **low**.
      
      **11b — Stale version pins**:
      
      ```bash
      MEMORY_FILE="$HOME/.claude/projects/$(git rev-parse --show-toplevel | sed 's|[/.]|-|g')/memory/MEMORY.md" # timeout: 3000
      if [ -f "$MEMORY_FILE" ]; then
          grep -nE '(v[0-9]+\.[0-9]+\.[0-9]+|as of [A-Z][a-z]+ 20[0-9]{2})' "$MEMORY_FILE" || echo "no stale pins found" # timeout: 5000
      else
          printf "⚠ SKIPPED: Check 11b — MEMORY.md not found at derived path: %s\n" "$MEMORY_FILE"
      fi
      ```
      
      **11c — Absorbed feedback files**:
      
      ```bash
      MEMORY_DIR="$HOME/.claude/projects/$(git rev-parse --show-toplevel | sed 's|[/.]|-|g')/memory" # timeout: 3000
      if [ -d "$MEMORY_DIR" ]; then
          ls "$MEMORY_DIR"/feedback_*.md 2>/dev/null || echo "no feedback files" # timeout: 5000
      else
          printf "⚠ SKIPPED: Check 11c — memory dir not found: %s\n" "$MEMORY_DIR"
      fi
      ```
      
      All three sub-checks produce only **low** findings — auto-fixed when user picks "Fix all" from follow-up gate. Fix: remove duplicate section, drop version pin, delete absorbed feedback file.
      
      ## Check 34 — Config token overhead
      
      Rules files in `.claude/rules/` load **entirely at session start**, regardless of relevance. Agents and skills lazy-loaded (zero cost until invoked). Measures always-loaded byte count, flags oversized components.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/measure_config_size.py" --mode overhead  # timeout: 10000
      ```
      
      Severity: > 100 KB total or > 10 KB single file = **medium**; 50–100 KB total or 5–10 KB single file = **low**. **Report only** — fix = split or remove content from rules files; never auto-collapse.
      
      Note: `agents/` and `skills/` lazy-loaded — never flag for token overhead.
      
      Note: thresholds were calibrated against a total that counted the global `CLAUDE.md` twice. Total now lower for the same tree, so a threshold can only fire later, never sooner — recalibrate downward if 50 KB stops discriminating.
      
      ## Check 39 — Plugin version freeze
      
      `plugins/CLAUDE.md` versioning policy requires bumping `plugin.json` version in every commit that modifies plugin files. A frozen version misrepresents what changed and defeats changelog reconstruction.
      
      Skip if `LOCAL_MODE != true` (no git history accessible).
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check 39: Plugin version freeze ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check 39 skipped in non-local mode\n"
      else
          for plugin_dir in plugins/*/; do
              plugin_name=$(basename "$plugin_dir")
              plugin_json="$plugin_dir.claude-plugin/plugin.json"
              [ -f "$plugin_json" ] || continue
              disk_ver=$(python -c "import json; print(json.load(open('$plugin_json'))['version'])" 2>/dev/null)
              head_ver=$(git show HEAD:"$plugin_json" 2>/dev/null | python -c "import sys,json; print(json.load(sys.stdin)['version'])" 2>/dev/null)
              [ -z "$head_ver" ] && continue  # new plugin, no HEAD yet
              if [ "$disk_ver" = "$head_ver" ]; then
                  changed=$(git diff --name-only HEAD -- "$plugin_dir" 2>/dev/null | wc -l | tr -d ' ')
                  if [ "$changed" -gt 0 ]; then
                      printf "C39-MEDIUM: plugin '%s' has %s modified file(s) but version unchanged (%s)\n" \
                          "$plugin_name" "$changed" "$disk_ver"
                  fi
              fi
          done  # timeout: 10000
      fi
      ```
      
      **Severity**: medium — commit mislabeling; no runtime breakage, but release notes and changelog reconstruction are unreliable. Fix: bump `plugin.json` patch or minor version per `plugins/CLAUDE.md` versioning policy before committing.
      
    • checks-shared.md 33.6 KB
      # Shared Checks (all scopes) — 17, 4, 5, 9, 16, 15
      
      ## Check 12 — File length (context budget risk)
      
      Thresholds: agents > 300 lines (~4 k tokens) · skill SKILL.md > 600 lines (~8 k tokens) · rules > 200 lines (~2.5 k tokens).
      
      > **`bin/` scripts are exempt** — executables run via subprocess, never loaded into LLM context; size irrelevant to token budget. Check 12 applies to `.md` config files only.
      
      > **Line count = human-readable proxy; token count = true measure.** Thresholds guide human review — not actual context budget. Short sentences + short lines preferred: easier to read AND cheaper per logical unit. Collapsing multiple short lines into one long line does NOT reduce token cost and destroys readability. Fix = remove or distill content. Collapsing lines is not a fix.
      
      ```bash
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/measure_config_size.py" --mode inventory  # timeout: 10000
      ```
      
      **Severity**: **medium** — report only, never auto-fix. When flagging, remind fixer: only content removal or distillation counts; collapsing lines not acceptable.
      
      ## Check 13 — Markdown heading hierarchy continuity
      
      ````bash
      printf "=== Check 13: Heading hierarchy continuity ===\n"
      violations=0
      for f in .claude/agents/*.md .claude/skills/*/SKILL.md .claude/rules/*.md; do # timeout: 5000
          [ -f "$f" ] || continue
          awk -v file="$f" '
          /^```/ { in_code = !in_code; next }
          in_code { next }
          /^#+ / {
            n = 0; s = $0
            while (substr(s,1,1) == "#") { n++; s = substr(s,2) }
            if (prev > 0 && n > prev + 1) {
              printf "  ⚠ HEADING JUMP: %s:%d — h%d followed by h%d (skipped h%d)\n", \
                file, NR, prev, n, prev+1
              found++
            }
            prev = n
          }
          END { exit (found > 0) ? 1 : 0 }
        ' "$f" || violations=$((violations + 1))
      done
      if [ "$violations" -eq 0 ]; then
          printf "✓: Check 13 — no heading hierarchy violations found\n"
      fi
      ````
      
      **Severity**: **medium** — heading jumps impair navigation. Fix: insert missing intermediate heading level, or demote/promote offending heading. **Report only** — never auto-fix.
      
      ## Check 14a — Structural tag symmetry
      
      Checks two failure modes: (1) empty blocks — `<tag></tag>` with only whitespace between open and close; (2) unbalanced tags — open count differs from close count. Both leave files structurally broken.
      
      Scan all agent and skill files via deterministic bin/ script:
      
      ```bash
      printf "=== Check 14a: Structural tag symmetry ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_tag_symmetry.py" \
          .claude/agents/*.md .claude/skills/*/SKILL.md  # timeout: 10000
      ```
      
      **Severity**: **medium** — gate-level; must fix before audit passes.
      
      - **Empty block**: **Auto-fix: YES** — remove empty open+close tag pair entirely; no content to lose.
      - **Unbalanced tag**: **Auto-fix: NO** — missing open or close tag requires manual inspection to determine intended structure.
      
      > Root cause: prior fix moved or removed block content but left container tags (empty block); or copy-paste error dropped closing tag (unbalanced). Empty `<constants>` most common empty-block case.
      
      ## Check 14b — Code fence symmetry
      
      Detects two failure modes: (1) unclosed fence — opening ```` ``` ```` or ```` ```lang ```` with no matching closing ```` ``` ````; (2) bad nesting — inner fence uses same or more backticks as outer (outer must use ` ` or more to contain inner ```` ``` ````).
      
      Scan all agent and skill files via deterministic bin/ script:
      
      ```bash
      printf "=== Check 14b: Code fence symmetry ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_fence_symmetry.py" \
          .claude/agents/*.md .claude/skills/*/SKILL.md  # timeout: 10000
      ```
      
      **Severity**: **high** — unclosed fence corrupts all subsequent code blocks in the file; Claude misparses the rest of the file.
      
      - **Unclosed fence**: **Auto-fix: YES** — add missing closing ```` ``` ```` at end of block; confirm content boundary by reading surrounding context.
      - **Bad nesting**: **Auto-fix: YES** — promote outer fence to ` ` ` `; or demote inner if outer is intentionally 3-backtick.
      - **Timeout comment on closing fence** (```` ``` # timeout: N ````): **Auto-fix: YES** — move comment to last command inside block; change closing line to plain ```` ``` ````.
      
      > Root cause: timeout annotation placed on closing fence delimiter instead of inside block (most common); or copy-paste lost a closing ```` ``` ````.
      
      ## Check 14c — README drift
      
      Detects README facts drifted from disk: (1) a literal `Current version: `X.Y.Z\`\` marker not matching the plugin's `plugin.json` version; (2) a `.py`/`.sh` script named on a README line mentioning `bin/` (or as an explicit `plugins/<plugin>/bin/<name>` path) existing nowhere in the plugin. Arbitrary version-shaped strings (release examples, historical benchmark tags) ignored — only the explicit marker checked.
      
      Scan all plugins via deterministic bin/ script:
      
      ```bash
      printf "=== Check 14c: README drift ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_readme_drift.py" \
          --scan-dir plugins  # timeout: 10000
      ```
      
      **Severity**: **medium** — user-facing wrong facts; gate-level.
      
      - **Version marker drift**: **Auto-fix: YES** — update the marker to the current `plugin.json` version.
      - **Stale bin/ reference**: **Auto-fix: NO** — resolve to the current script name (often a sh→py migration) by inspecting the actual `bin/` directory.
      
      > Root cause: operational constants and inventories duplicated into README prose by hand; README-sync is convention-only and demonstrably fails. Also enforced pre-commit (per-file on `README.md`).
      
      ## Check 14d — Mode dispatch integrity
      
      Detects dangling mode-dispatch references: a SKILL.md line routing control to a named section (e.g. `go to "Mode: Lessons Distillation"` or `skip to **Mode: X**`) with no matching `## Mode: X` header in the same file — the half-done-rename bug class where the header was renamed but a dispatch line still points at the old name. Scan all plugins via deterministic bin/ script:
      
      ```bash
      printf "=== Check 14d: Mode dispatch integrity ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_mode_dispatch.py" \
          --scan-dir plugins  # timeout: 10000
      ```
      
      **Severity**: **high** — dangling dispatch sends the agent to a section that does not exist; the mode silently never runs.
      
      - **Dangling dispatch**: **Auto-fix: NO** — resolve to the intended header name (restore the renamed header or update the dispatch line to match); manual inspection determines which side is stale.
      
      > Root cause: a `## Mode: <Name>` header renamed without updating every `go to`/`skip to`/`see` dispatch line that references it (or vice versa).
      
      ## Check 14e — Cross-plugin shared-file drift
      
      Detects byte-level drift in files that must be identical across plugins because each plugin ships its own copy of a shared mechanism (a plugin can't depend on another being installed). The canonical copy lives in one plugin; others must track it byte-for-byte. Source of truth is the `MANIFEST` in the script (currently the `agent-router.js` fallback hook: foundry canonical → oss/develop/research copies). Files that legitimately vary per plugin (e.g. `agent-resolution.md` fallback tables, per-plugin `rules/quality-gates.md`) intentionally NOT in the manifest.
      
      Scan via deterministic bin/ script:
      
      ```bash
      printf "=== Check 14e: Cross-plugin shared-file drift ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/propagate_shared.py"  # timeout: 10000
      ```
      
      **Severity**: **high** — a stale copy means one plugin runs old fallback logic; behaviour diverges silently by plugin.
      
      - **Drifted copy**: **Auto-fix: YES** — run `propagate_shared.py --apply` to overwrite copies with the canonical.
      
      > Root cause: the root `Makefile` does not propagate cross-plugin shared files; a canonical edit was not mirrored into the consuming plugins. Also enforced pre-commit.
      
      ## Check 14f — Unmanaged codemap index-guard copy
      
      Detects a hand-written codemap index path in a file that neither a `MANIFEST` entry nor the guard registry covers. The guard ("is codemap-py installed, and does an index exist for this project?") was hand-copied across four plugins with nothing linking the copies, so each drifted alone and one path fix cost ten edits. Two shapes permitted: consume the provider CLI (`codemap-py query`, `codemap_resolve.py` — such a consumer never spells the path, so it never trips this check), or one canonical copy propagated byte-identical. Inline bash in agent/skill prose is a fragment `MANIFEST` can't propagate, so those copies are named in the script's `REGISTRY` with a reason and held to two invariants: index dir anchored to a project-root variable (never CWD), project name the raw basename (never sanitized).
      
      ```bash
      printf "=== Check 14f: Unmanaged codemap index-guard copy ===\n"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_codemap_guard.py"  # timeout: 10000
      ```
      
      **Severity**: **high** — an unmanaged copy drifts silently; a subdir-anchored or sanitized-name copy reports `no_index` while an index exists, and the agent falls back to Grep with no error.
      
      - **Unregistered copy**: **Auto-fix: NO** — pick a shape first (provider CLI preferred), then add the `MANIFEST` or `REGISTRY` entry.
      - **Invariant violation**: **Auto-fix: NO** — restore root anchoring / raw basename at the offending line.
      - **Stale registry entry**: **Auto-fix: YES** — drop the entry; the file no longer holds a guard.
      
      > Root cause: no structural link between copies of a mechanism duplicated by policy. `--list` prints the full inventory with each copy's shape. Also enforced pre-commit.
      
      ## Check 15 — Hardcoded user paths
      
      Use Grep tool (pattern `/Users/|/home/`, glob `{agents/*.md,skills/*/SKILL.md}`, path `.claude/`, output mode `content`) to flag non-portable paths in agent and skill files. Run second Grep on `.claude/settings.json` with same pattern to catch absolute hook paths.
      
      **Important**: run on every file regardless of prior critical/high findings — path portability orthogonal to other severity classes, must not deprioritize.
      
      Also grep for bare `plugins/<name>/` prefix as primary path in skill/agent bodies — source-tree paths working during authoring but breaking post-install. See Check C32 for full scan.
      
      ## Check 16 — Example value vs. token cost
      
      First, detect whether project has local context files:
      
      ```bash
      for f in AGENTS.md CONTRIBUTING.md .claude/CLAUDE.md; do # timeout: 5000
          [ -f "$f" ] && printf "✓ found: %s\n" "$f"
      done
      ```
      
      Scan agent and skill files for inline examples:
      
      ````bash
      for f in .claude/agents/*.md .claude/skills/*/SKILL.md; do # timeout: 5000
          count=$(grep -cE '^```|^## Example|^### Example' "$f" 2>/dev/null || true)
          lines=$(wc -l <"$f" | tr -d ' ')
          [ "$count" -gt 0 ] && printf "%s: %d example blocks, %d total lines\n" "$f" "$count" "$lines"
      done
      ````
      
      Classify each example block via model reasoning:
      
      - **High-value**: non-obvious pattern, nuanced judgment, or output-format spec prose can't convey → keep
      - **Low-value**: restates prose, trivial, or superseded by project-local docs → **low** finding: suggest removing or replacing with pointer to local doc
      
      Report per-file: `N examples total, K high-value, M low-value (est. ~X tokens wasted)`.
      
      ## Check 17 — Cross-file code block inventory
      
      Block count across all .md files in scope. NxN similarity analysis is expensive — runs in `--efficiency` mode only (Phase B2), which subsumes this check. When `--efficiency` active, skip Check 17.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/list_audit_files.py" $( [ "$LOCAL_MODE" = "true" ] && echo "--local" )  # timeout: 30000
      ```
      
      Flag files with block count ≥ 10 as extraction candidates — recommend `--efficiency` run for full NxN analysis.
      
      > **A `0` in the BLOCKS column is a correct result, not a filtering bug** — `_shared/agent-resolution.md` is the standing example. Those rows are the regression test for the `grep -c` fallback defect fixed in the block above: with `|| echo 0`, a zero-match file captured `"0\n0"` and aborted the arithmetic, so the check emitted nothing at all. Keep zero rows in the output — their presence is the evidence the count path still works.
      
      For 17a (step-level prose overlap, ≥40% consecutive steps): flag pair, name canonical owner; route to Check 20 `merge-prune` if no clear owner.
      
      | Sub-check | Algorithm | Threshold | Severity | Output |
      | -- | -- | -- | -- | -- |
      | 17a — step overlap | consecutive step fraction | ≥40% steps | medium | findings list only |
      | 17b — block duplicate | NxN similarity (moved) | run `--efficiency` for full analysis | — | Phase B2 in efficiency.md |
      
      ## Check C32 — Hardcoded source-tree paths (install-path regression)
      
      Plugin skill and agent files must not contain bare `plugins/<name>/` paths as primary references. Resolve in source tree but break post-install where `plugins/` absent. Install-path resolution pattern (cache + fallback) mandatory.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check C32: Hardcoded source-tree paths ===\n"
      # C32 inherently scans plugin source tree — LOCAL mode only
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓ [Check C32/shared] Skipped in non-local mode (no plugin source tree)\n"
      else
          grep -rn ' plugins/[a-z]' plugins/*/skills/*/SKILL.md plugins/*/agents/*.md 2>/dev/null |
            grep -v '^Binary' |
            grep -v '^\s*#' |
            grep -v '&& .*plugins/' |
            grep -v ':-.*plugins/' |
            grep -v '"plugins/' | grep -v "'plugins/" | while IFS= read -r hit; do
              printf "! BREAKING C32: %s\n" "$hit"
              printf "  fix: replace with installed-path resolution:\n"
              printf "        VAR=\"\$(ls -td ~/.claude/plugins/cache/borda-ai-rig/<plugin>/*/skills/_shared 2>/dev/null | head -1)\"\n"
              printf "        [ -z \"\$VAR\" ] && VAR=\"plugins/<plugin>/skills/_shared\"\n"
          done
          printf "✓: Check C32 scan complete\n"
      fi  # timeout: 5000
      ```
      
      Severity: **high** — skill silently fails for any user who installed via marketplace (primary install path).
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | C32 — source-tree primary path | `plugins/<name>/` not in comment or fallback | high | no |
      
      > **Related**: Check 15 covers `/Users/` and `/home/` hardcoded paths. C32 covers `plugins/` source-tree paths.
      
      ## Check 18 — Rules integrity and efficiency
      
      Four sub-checks covering `.claude/rules/`. Skip if `rules/` directory absent or empty.
      
      **18a — Inventory vs MEMORY.md**:
      
      ```bash
      ls .claude/rules/*.md 2>/dev/null | xargs -I{} basename {} .md | sort # timeout: 5000
      ```
      
      Rules on disk absent from MEMORY.md → **medium**. Rules in MEMORY.md absent on disk → **medium**.
      
      **18b — Frontmatter completeness**:
      
      ```bash
      for f in .claude/rules/*.md; do # timeout: 5000
          desc=$(awk '/^---$/{c++; if(c==2)exit} c==1 && /^description:/{found=1} END{print found+0}' "$f")
          [ "$desc" -eq 0 ] && printf "MISSING description: %s\n" "$f"
      done
      ```
      
      Missing `description:` → **high**. Malformed `paths:` → **high**.
      
      **18c — Redundancy check**: Per rule file, identify 2–3 most specific directive phrases. Grep verbatim in `.claude/CLAUDE.md` and `.claude/agents/*.md`. Exact phrase in ≥2 locations outside rule file → **medium** (distillation incomplete).
      
      ```bash
      grep -l "Never switch to NumPy" .claude/agents/*.md .claude/CLAUDE.md 2>/dev/null # timeout: 5000
      grep -l "never git add" .claude/agents/*.md .claude/CLAUDE.md 2>/dev/null         # timeout: 5000
      ```
      
      **18d — Cross-reference integrity**: Grep agent files, skill files, CLAUDE.md for `.claude/rules/<name>.md` patterns. Verify each referenced filename exists on disk → missing → **high**.
      
      ```bash
      grep -rh '\.claude/rules/[a-z_-]*\.md' .claude/agents/ .claude/skills/ .claude/CLAUDE.md 2>/dev/null |
      grep -o 'rules/[a-z_-]*\.md' | sort -u # timeout: 5000
      ```
      
      Severity: 18b = **high**; 18a/18c/18d = **medium**.
      
      ## Check 25 — Implicit agent references (missing plugin prefix)
      
      All agent dispatch calls must use fully-qualified plugin-prefixed form (`foundry:sw-engineer`, `oss:shepherd`, etc.). Bare names like `sw-engineer` ambiguous: rely on `~/.claude/agents/` symlinks present, break if symlinks stale, missing, or pointing to wrong plugin.
      
      Scan agent files, skill files, CLAUDE.md for `subagent_type=` patterns:
      
      ```bash
      printf "=== Check 25: Implicit agent references ===\n"
      grep -rn 'subagent_type=' .claude/agents/ .claude/skills/ .claude/CLAUDE.md 2>/dev/null |
      grep -v '^Binary' |
      grep 'subagent_type="[a-z]' |
      grep -v '"[a-z][a-z-]*:[a-z]' |
      grep -v '"general-purpose"\|"Explore"\|"Plan"\|"claude-code-guide"\|"statusline-setup"' || true  # timeout: 5000
      ```
      
      Exempt built-in types (no plugin prefix required): `general-purpose`, `Explore`, `Plan`, `claude-code-guide`, `statusline-setup`.
      
      Every non-exempt bare name = **high** finding:
      
      ```text
      [high] Implicit agent reference: subagent_type="<name>" in <file>
      fix: use fully-qualified form, e.g. subagent_type="foundry:<name>"
      ```
      
      **Report only** — no auto-fix; correct prefix depends on which plugin owns agent.
      
      > **Related**: Check 28 (in `checks-skills.md`) covers cross-plugin fallback coverage — dispatched agent exists but no fallback when that plugin absent. Check 25 and Check 28 address different failure modes; run both.
      
      ## Check 29 — LLM context minimality (verbosity)
      
      Every token in agent, skill, rule file = inference cost on every invocation. Each file must be semantically minimal — all information retained, zero redundant wording. **Scan targets**: `.claude/agents/*.md`, `.claude/skills/*/SKILL.md`, `.claude/rules/*.md`.
      
      Via model reasoning, apply four criteria per file:
      
      **1 — Within-file repetition**: same rule or instruction in two sections. Sub-bullet fully restates parent with no additive content. Workflow step re-explains constraint already defined in preamble or `<notes>`.
      
      **2 — Prose inflation**: filler preambles ("Note that", "It is important to", "Please be aware", "Keep in mind") — flag phrase; substantive content survives without it. Unconditional rule hedged with "might", "could potentially", "in some cases" where rule absolute. Opening sentence paraphrases heading with no additive content.
      
      **3 — Restatement of obvious consequence**: "Do X" immediately followed by "Failing to do X causes Y" where Y self-evident from X alone.
      
      **4 — Information gap test (mandatory before flagging any candidate)**: "If removed, can reader reconstruct from remaining content?" YES = safe to flag. NO = not a finding — content load-bearing even if verbose. Always skip: code blocks, inline examples (Check 16), cross-reference tables, numbered lists where order carries meaning.
      
      Per finding: location (section heading + approx line range) · pattern type (repetition / prose-inflation / obvious-consequence) · estimated token savings (small \<20 / medium 20–80 / large >80) · proposed shorter form or "remove entirely".
      
      **Severity**: **medium** — total savings >= medium across >= 2 distinct locations. **low** — isolated small savings only. **Report only** — never auto-fix; minimization risks removing load-bearing nuance.
      
      **29a — Trigger-inverse restatement**: TRIGGER/SKIP or fires-when/skip-when adjacent pairs where second item is pure logical negation of first. Example: "TRIGGER when X" immediately followed by "SKIP when not X" — second adds zero information.
      
      Via model reasoning: extract TRIGGER and SKIP bullet lists from agent and skill files. For each TRIGGER condition, check adjacent SKIP section for negation complement (same subject, negated predicate). Flag pair if second is reconstructable from first by negation alone.
      
      Information gap test (mandatory): "If SKIP bullet removed, can reader infer from TRIGGER + closed-world assumption?" YES = flag. NO = retain (SKIP carries additive context — e.g. different agent type, overlapping domain clarification).
      
      Per finding: file · TRIGGER line · SKIP line · one-line reason second is pure negation. **Severity**: **low** — report only; never auto-remove (negation-form SKIP bullets may carry implicit scope narrowing not obvious from trigger alone).
      
      **29b — Non-actionable / hedged absolute directive**: directive using "consider", "may", "might", "should ideally", "try to", "where possible" where surrounding context makes rule absolute (no conditionality intended). Also: step missing verb+object+condition triad — subject-only or object-only instructions with no triggering condition.
      
      Via model reasoning per file: scan workflow steps and rule bullets. Flag where:
      
      - Hedging word present + no conditional clause justifying it (absolute rule weakened by hedge)
      - Step body is object-only ("error handling", "edge cases") with no verb or condition
      
      Information gap test (mandatory): "Does hedge word change correct behavior?" YES (hedge load-bearing) = skip. NO = flag as prose-inflation variant.
      
      Per finding: file · section · hedged phrase → proposed imperative form. **Severity**: **low** — report only.
      
      ## Check 26 — Symbol and shortcut consistency
      
      Three sub-checks for within-file consistency of emoji symbols, slash-command notation, legend alignment.
      
      **26a — Emoji/symbol consistency within files**
      
      Per agent or skill file, extract lines with emoji and annotated concept label. Group by concept. Flag concepts with 2+ distinct emoji in same file.
      
      ````bash
      printf "=== Check 26a: Emoji/symbol consistency ===\n"
      for f in .claude/agents/*.md .claude/skills/*/SKILL.md; do # timeout: 5000
          [ -f "$f" ] || continue
          awk '/^```/{skip=!skip} !skip && /[🔴🟡🟢🔵⛔✅❌⚠️💭▶️🔗🔹🔸🚫]/{print FILENAME": "NR": "$0}' "$f" 2>/dev/null
      done
      ````
      
      Via model reasoning, identify concept labels (e.g., "closed", "open", "active focus", "merged") appearing with two+ distinct symbols in same file. Example: file marks branch 🔴 (closed) in one section and ⛔ closed in another = violation.
      
      Flag: `[medium] Inconsistent symbol for "<concept>" in <file>: <symbol-A> (line N) vs <symbol-B> (line M)`
      
      **26b — Slash command notation consistency**
      
      Directive references to other skills (e.g., "run → /audit") must use `/name` form. Prose mentions (e.g., "the audit skill") may omit slash. Flag files mixing `` `/name` `` and `` `name` `` in same directive context.
      
      ```bash
      printf "=== Check 26b: Slash command notation ===\n"
      for f in .claude/agents/*.md .claude/skills/*/SKILL.md; do # timeout: 5000
          [ -f "$f" ] || continue
          grep -n '→ `/\?[a-z][a-z:-]*`\|run `/\?[a-z][a-z:-]*`\|suggest.*`/\?[a-z][a-z:-]*`' "$f" 2>/dev/null
      done
      ```
      
      Via model reasoning: same skill referenced with both `/name` and bare `name` in directive context in same file → **low** finding.
      
      **26c — Legend ↔ body symbol alignment**
      
      When file defines legend (any line matching `Legend:` followed by symbol/concept pairs), every body use of concept must match legend symbol exactly.
      
      ```bash
      printf "=== Check 26c: Legend/key alignment ===\n"
      grep -n 'Legend:\|^Key:' .claude/agents/*.md .claude/skills/*/SKILL.md 2>/dev/null || true # timeout: 5000
      ```
      
      Via model reasoning: extract (symbol, concept) pairs from legend. Per concept, scan file body outside code fences for different symbol. Flag: `Legend defines <concept> as <symbol-A> but body uses <symbol-B> at line N`.
      
      **Report only** — never auto-fix; symbol choices may be intentional or constrained by existing docs.
      
      | Sub-check | Severity | Auto-fix |
      | -- | -- | -- |
      | 26a — same concept, different symbols | medium | no |
      | 26b — directive notation mixed `/name` vs `name` | low | no |
      | 26c — body symbol contradicts legend | medium | no |
      
      ## Check 41 — LLM-first formatting conventions
      
      Config files consumed primarily by LLM at inference time. Formatting inconsistencies force LLM to resolve ambiguity before parsing content — wasted tokens, degraded reliability. **Principle**: compact + robust + minimal variation. One canonical form per pattern type per file.
      
      **Scan targets**: all `*.md` files under `.claude/` and `plugins/`, excluding any file named `README.md`. Each sub-check block below re-derives that file list inline — a shared assignment in its own block wouldn't survive into the next Bash call (Check 43), and `mapfile` is a bash builtin absent from zsh (Check 45's note).
      
      Via model reasoning, apply four sub-checks per file:
      
      **41a — List marker uniformity**: scan all unordered list lines outside code fences. Collect distinct markers used (`-`, `*`, `+`). More than one distinct marker in same file = finding. Mixed markers = ambiguous parse order for LLM; `-` is canonical.
      
      ````bash
      printf "=== Check 41a: List marker uniformity ===\n"
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          markers=$(awk '/^```/{skip=!skip} !skip && /^[[:space:]]*[*+] /{print $1}' "$f" | sort -u | tr '\n' ' ')
          [ -n "$markers" ] && echo "$f: uses markers: $markers"
      done < <(find .claude plugins -name "*.md" ! -name "README.md" 2>/dev/null | sort)
      ````
      
      Via model reasoning: for each file printing markers, confirm multiple distinct markers present outside code fences. Flag files with `*` or `+` alongside `-`.
      
      **41b — Numbering intent clarity**: two numbering registers must not be mixed in same document context:
      
      - `1.` `2.` `3.` — sequential steps (implies ordering + dependency)
      - `(a)` `(b)` `(c)` — choices / alternatives (implies selection, no ordering)
      
      Violations to flag:
      
      - `1.` `2.` used for choices inside AskUserQuestion option blocks or "choose one" lists
      - `(a)` `(b)` used for sequential workflow sub-steps where ordering matters
      
      ```bash
      printf "=== Check 41b: Numbering intent ===\n"
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          grep -n 'AskUserQuestion' "$f" 2>/dev/null | grep -q '.' && grep -B5 -A5 'AskUserQuestion' "$f" | grep -n '^\s*[0-9]\.' 2>/dev/null | head -5 && echo "  ^^^ $f (numbered options in AskUserQuestion block)"
      done < <(find .claude plugins -name "*.md" ! -name "README.md" 2>/dev/null | sort)
      ```
      
      Via model reasoning: around each AskUserQuestion block, check whether options use `1.`/`2.` (violation) or `(a)`/`(b)` (compliant). In workflow steps, verify numbered sub-items (`1.`, `2.`) represent sequential actions, not option choices.
      
      **41c — Table vs nested prose**: when content has 3+ list items each with 2+ fixed-schema attributes, a Markdown table is more compact and structurally clearer for LLM parse than nested prose bullets.
      
      Via model reasoning per file: identify nested bullet blocks where each top-level bullet has ≥2 sub-bullets with consistent attribute structure across items (e.g., every item has "Input:", "Output:", "When:"). If block has ≥3 top-level items with ≥2 uniform sub-attributes → flag as table candidate.
      
      Exception: skip when attributes vary per item (non-uniform schema — prose correct).
      
      **41d — Legacy mixed/decimal phase-step numbering**: canonical sub-step convention (precedent: `oss:review`'s `Step 3a`–`3e`, `oss:audit`'s `Step 5b`, Check 44 below) is **number-primary, letter-secondary, no separator** — `1b`, `3a`, `41a`. A `### Phase`/`Step`/`Check`/`Mode`/`Section` header using a decimal or letter-primary variant instead (`1.5`, `A.5`, `5.b`) is a legacy/inconsistent form that predates or bypassed that convention — same intent, wrong register, and outside Check 44's scope (whose regex only matches literal `Check N<letter>`, not general workflow headers).
      
      ```bash
      printf "=== Check 41d: Legacy phase/step numbering ===\n"
      found41d=0
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          hdr=$(grep -nE '^#{1,6}[[:space:]]+(Phase|Step|Check|Mode|Section)[[:space:]]+[A-Za-z]*[0-9]+\.[0-9A-Za-z]+' "$f" 2>/dev/null)
          [ -n "$hdr" ] && { echo "$hdr" | sed "s|^|$f:|"; found41d=1; }
      done < <(find .claude plugins -name "*.md" ! -name "README.md" 2>/dev/null | sort)
      [ "$found41d" -eq 0 ] && printf "✓: Check 41d — no legacy decimal/mixed phase-step headers\n"
      ```
      
      Via model reasoning: for each flagged header, find the file's own established sibling convention (existing `<N><letter>` sub-steps in the same file) and propose the matching rename. Before renaming, grep the same file (and, for cross-plugin shared files, every consumer) for every other mention of the flagged token — header and every prose cross-reference get renamed together in one pass, never left half-updated.
      
      **Severity**: P3 — report only. Never auto-fix; reformatting risks layout regression in rendered contexts. Flag only clear violations with concrete line references.
      
      | Sub-check | Severity | Auto-fix |
      | -- | -- | -- |
      | 41a — mixed list markers | low | no |
      | 41b — numbering register mismatch | medium | no |
      | 41c — nested prose where table fits | low | no |
      | 41d — legacy phase/step numbering | low | no |
      
      ## Check 44 — Sub-check naming symmetry
      
      Sub-check letter suffixes must be contiguous starting at `a`. A file containing `Check Nb` or `Check Nc` without `Check Na` is an orphan — the `a` variant was never created or was removed, leaving a misleading gap. Same applies to any letter sequence gap (e.g., `a`, `b`, `d` missing `c`).
      
      ```bash
      printf "=== Check 44: Sub-check naming symmetry ===\n"
      found=0
      while IFS= read -r f <&3; do  # timeout: 5000
          [ -f "$f" ] || continue
          # skip CLAUDE.md — cross-references check numbers, not defining sub-checks
          [ "$(basename "$f")" = "CLAUDE.md" ] && continue
          # -w: BSD grep whole-word (macOS — \b unsupported in ERE)
          while IFS= read -r entry; do
              num=$(printf '%s' "$entry" | grep -oE '[0-9]+')
              letter=$(printf '%s' "$entry" | grep -oE '[a-z]$')
              if ! grep -qw "Check ${num}a" "$f"; then
                  printf "⚠ 44: %s — Check %s%s exists without Check %sa\n" "$f" "$num" "$letter" "$num"
                  found=1
              fi
          done < <(grep -oE 'Check [0-9]+[b-z]' "$f" 2>/dev/null | sort -u)
      done 3< <(find .claude plugins -name "*.md" ! -name "README.md" 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 44 — sub-check naming symmetric across all files\n"
      ```
      
      **Severity**: low — gap in sub-check labeling. No runtime impact; misleads readers into expecting a missing variant.
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 44 — orphan letter suffix | `Check Nb` in file without `Check Na` | low | no — rename or add missing variant |
      
      ## Check 45 — Policy-sibling marker symmetry (reference-graph completeness)
      
      Some policies (safety rules, scoping rules, format conventions) are **restated in prose** across multiple files instead of cross-referenced, because each consumer needs the rule inline in its own reading context. A restated copy has no structural link back to its siblings, so refining the policy in one location can silently leave others stating a stale version — grep-for-violations doesn't catch a file that correctly states an *old* rule. `plugins/CLAUDE.md §Policy Duplication Marker` requires a `<!-- policy-sibling: path1, path2, ... -->` comment in every copy, listing every other file stating the same policy. This check verifies that declared graph is complete and symmetric — it doesn't (can't, mechanically) verify the restated *content* itself stays in sync; that judgment call is `foundry:curator`'s reference-graph trace (see curator `<workflow>` step on policy edits).
      
      Two failure modes:
      
      - **45-BROKEN**: marker lists a sibling path that doesn't exist on disk (stale — file renamed/deleted, marker not updated)
      - **45-ASYMMETRIC**: file A's marker lists file B as a sibling, but B has no `policy-sibling` marker pointing back — B was never updated to know it's part of the group (exactly how `git-commit.md` was missed before this check existed)
      
      ```bash
      printf "=== Check 45: Policy-sibling marker symmetry ===\n"
      OUT=""
      for f in $(grep -rl "<!-- policy-sibling:" .claude plugins --include="*.md" 2>/dev/null); do  # timeout: 10000
          siblings=$(grep -o '<!-- policy-sibling:[^—]*' "$f" | head -1 | sed 's/<!-- policy-sibling://')
          for sib in $(printf '%s' "$siblings" | tr ',' '\n' | awk '{print $1}' | grep -E '/.*\.md$'); do
              if [ ! -f "$sib" ]; then
                  OUT="${OUT}⚠ 45-BROKEN: $f — policy-sibling lists missing file: $sib\n"
                  continue
              fi
              grep -q "<!-- policy-sibling:" "$sib" || OUT="${OUT}⚠ 45-ASYMMETRIC: $f — declares sibling $sib, but that file has no policy-sibling marker back\n"
          done
      done
      if [ -n "$OUT" ]; then printf "$OUT"; else printf "✓: Check 45 — no unsynced policy-sibling markers found\n"; fi
      ```
      
      Extraction anchors on the literal `<!-- policy-sibling:` prefix (not a bare grep for the word) so prose documenting the convention — like this file's own explanation above, or an example snippet — never self-matches; the sibling-token filter (`/.*\.md$`) additionally drops non-path fragments (placeholder text, trailing rationale words) surviving the comma split. No array syntax (`read -ra`, `mapfile`) — Claude Code's Bash tool runs under the user's login shell, `zsh` on macOS by default, and zsh's `read` doesn't support bash's `-a` flag; plain `for x in $(...)` word-splitting is portable to both.
      
      **Severity**:
      
      - `45-BROKEN` — **high** — sibling reference points nowhere; anyone following it to propagate a fix finds nothing
      - `45-ASYMMETRIC` — **medium** — one-directional link; the group is incomplete, next refinement likely repeats the git-commit.md miss
      
      Fix: add the missing `policy-sibling` marker to the un-listed file (45-ASYMMETRIC), or correct/remove the stale path (45-BROKEN). Both directions must resolve — A→B requires B→A.
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 45-BROKEN — dangling sibling path | listed path does not exist on disk | high | no — fix or remove path |
      | 45-ASYMMETRIC — one-directional link | B listed by A but B has no marker back | medium | no — add reciprocal marker to B |
      
    • checks-skills.md 72.5 KB
      # Skill Checks — 22, 23, 24, 27, 28, 30, 31
      
      ## Scan-root derivation — prepend to every bash block in this file
      
      Each fenced block below runs as its own Bash tool call in a **fresh shell**, so no variable set in one block survives into the next (`audit/SKILL.md` §State re-derivation; Check 43 in this file flags the same pattern). Each block therefore re-reads `LOCAL_MODE` and re-derives its own scan root.
      
      Roots are **plain directory paths**, never glob patterns held in variables: the tool shell may be `zsh`, which — unlike bash — performs neither word-splitting nor filename generation on an unquoted `$VAR`. A var-held glob such as `for f in $_SKILL_GLOB` iterates once over the literal pattern string and matches nothing. Enumerate with `find` piped into `while IFS= read -r`, which behaves identically under both shells.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      ```
      
      `find "$_ROOT" -path "*/skills/*/SKILL.md"` enumerates skills; `find "$_ROOT" -path "*/agents/*.md"` enumerates agents **including nested subdirectories** (a `*/agents/*.md` glob silently misses `agents/<parent>/<file>.md`); `find "$_ROOT" -path "*/rules/*.md"` enumerates rules.
      
      ## Check 22 — Calibration coverage gap
      
      **Step 1 — Read calibrate domain table**: Load calibrate `skills.md` via `cat` (not the Read tool — `Bash(cat:*)` grant is version-proof), extract registered target list under `### Domain table`. Build registered-targets set.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      CALIB_MODES=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" calibrate modes $([ "$LOCAL_MODE" = "true" ] && echo --local) 2>/dev/null || echo "plugins/cc_foundry/skills/calibrate/modes")  # timeout: 5000
      cat "$CALIB_MODES/skills.md"  # timeout: 5000
      ```
      
      **Step 2 — Scan all skill modes on disk**: Use Glob (`skills/*/SKILL.md`, path `.claude/`) and Glob (`skills/*/modes/*.md`, path `.claude/`) to enumerate every skill and mode file. Extract mode names from `argument-hint:` frontmatter and `## Mode:` / `### Mode:` headings.
      
      **Step 3 — Validate registered targets exist on disk**: For each registered target, verify matching skill/mode file exists. Registered target with no matching file → **medium** (calibrate fails at runtime).
      
      **Step 4 — Identify unregistered calibratable candidates** (model reasoning):
      
      Mode is calibratable when ALL three signals present:
      
      1. **Deterministic structured output**: findings list, completeness checklist, structured table, or machine-readable verdict
      2. **Synthetic input feasible**: testable without external services
      3. **Ground truth constructable**: known issues injectable and scorable
      
      → Unregistered mode matching all three: **low** (add to `calibrate/modes/skills.md` domain table)
      
      **Step 5 — Read agents domain table**: Load calibrate `agents.md` via `cat` (not the Read tool — `Bash(cat:*)` grant is version-proof), extract all agent names from `### Domain table`. Build registered-agent-names set.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      CALIB_MODES=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" calibrate modes $([ "$LOCAL_MODE" = "true" ] && echo --local) 2>/dev/null || echo "plugins/cc_foundry/skills/calibrate/modes")  # timeout: 5000
      cat "$CALIB_MODES/agents.md"  # timeout: 5000
      ```
      
      **Step 6 — Scan all agent files on disk**: (When `LOCAL_MODE=false`, skip `plugins/*/agents/*.md` — scan `.claude/agents/` only.) Use Glob (`plugins/*/agents/*.md`, path project root) for plugin agent files; Glob (`agents/*.md`, path `.claude/`) for directly installed agents. Derive qualified name per file: `plugins/<plugin>/agents/<name>.md` → `<plugin>:<name>`; `.claude/agents/<name>.md` → `<name>`. Build full discovered-agent set.
      
      **Step 7 — Validate registered agents exist on disk**: For each registered agent in domain table, verify it resolves to discovered file. Bare name (e.g. `sw-engineer`) matches `foundry:sw-engineer` when no `.claude/agents/sw-engineer.md` exists — apply model reasoning to resolve bare names against plugin-qualified discoveries. Registered agent with no matching file → **medium** (stale entry causes calibrate to fail at runtime; remove from domain table or correct prefix).
      
      **Step 8 — Identify unregistered agents**: For each discovered agent not in domain table, apply same three-signal calibratability test from Step 4. → Unregistered calibratable agent: **low** (add to `calibrate/modes/agents.md` domain table with appropriate domain string).
      
      ## Check 23 — Bash command misuse / native tool substitution
      
      ```bash
      printf "=== Check 23: Bash misuse candidates ===\n"
      grep -rn '\bcat \|`cat ' .claude/agents/ .claude/skills/ .claude/rules/ 2>/dev/null |
      grep -v '^Binary' | grep -v '# ' &&
      printf "  hint: replace cat with Read tool\n" || true
      grep -rn '\bgrep \|\brg \b' .claude/agents/ .claude/skills/ .claude/rules/ 2>/dev/null |
      grep -v '^Binary' | grep -v '# .*grep\|Grep tool\|Use Grep' &&
      printf "  hint: replace grep/rg with Grep tool\n" || true
      grep -rn '\bfind \b.*-name\|\bls \b.*\*' .claude/agents/ .claude/skills/ .claude/rules/ 2>/dev/null |
      grep -v '^Binary' | grep -v '# .*Glob\|Use Glob\|Glob tool' &&
      printf "  hint: replace find/ls with Glob tool\n" || true
      grep -rn 'echo .* >\|tee ' .claude/agents/ .claude/skills/ .claude/rules/ 2>/dev/null |
      grep -v '^Binary' | grep -v '# .*Write tool\|Use Write' &&
      printf "  hint: replace echo-redirect/tee with Write tool\n" || true
      grep -rn '\bsed \b\|\bawk \b' .claude/agents/ .claude/skills/ .claude/rules/ 2>/dev/null |
      grep -v '^Binary' | grep -v '# .*Edit tool\|Use Edit\|awk.*{print\|awk.*BEGIN' &&
      printf "  hint: replace sed/awk text-substitution with Edit tool\n" || true
      printf "✓: Check 23 scan complete\n"
      ```
      
      After scan, apply model reasoning to each match — exclude cases where shell command genuinely necessary. Flag only where native tool is direct drop-in.
      
      | Shell command | Preferred native tool | Severity |
      | -- | -- | -- |
      | `cat <file>` | Read tool | medium |
      | `grep`/`rg` for content search | Grep tool | medium |
      | `find`/`ls` for file listing | Glob tool | medium |
      | `echo … >` / `tee` to write a file | Write tool | medium |
      | `sed`/`awk` for text substitution | Edit tool | medium |
      
      ### Sub-check 23a — python inline policy (CLAUDE.md / MEMORY.md violation)
      
      `Bash(python:*)` in allow list, covers bare `python script.py`. But `python -c "..."` does NOT match `Bash(python:*)` — Claude Code's permission matcher tokenizes the full prefix, so `python -c` needs a separate `Bash(python -c:*)` entry (intentionally absent). Any `python -c` in a skill body pauses for a permission prompt mid-workflow; user deny = phase fails. Enforcement mechanism for the inline-Python antipattern, not a coverage gap.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 23a: python inline policy ===\n"
      find "$_ROOT" -path "*/skills/*/SKILL.md" -exec grep -Hn 'python -c\b' {} + 2>/dev/null |
        grep -v '^Binary' | grep -v '^\s*#' &&
      printf "  hint: python not in allow list by design — move logic to bin/*.py or use native tools (Read/Write/Edit/Bash with jq)\n" || true
      printf "=== Check 23a: heredoc python policy ===\n"
      find "$_ROOT" -path "*/skills/*/SKILL.md" -exec grep -Hn "python << '\|python <<\"" {} + 2>/dev/null |
        grep -v '^Binary' | grep -v '^\s*#' &&
      printf "  hint: CLAUDE.md bans heredoc python; use bin/*.py instead\n" || true
      printf "✓: Check 23a scan complete\n"  # timeout: 5000
      ```
      
      Severity: **high** — permission prompt mid-workflow blocks automation; user deny = skill phase fails.
      
      | Sub-check | Pattern | Severity |
      | -- | -- | -- |
      | 23a — python -c inline | `python -c` in skill body | high |
      | 23a — python heredoc | `python << '` in skill body | high |
      
      **Report only** — never auto-fix; some Bash invocations in example/illustration code blocks intentional.
      
      ### Sub-check 23b — `# timeout:` annotation without shell enforcement
      
      `# timeout: N` on a bash line is a hint to Claude Code's Bash tool — no effect when the command runs outside the tool (bin/ scripts, CI, direct shell). Hard enforcement needs `timeout S <cmd>` prefix (bash) or `--timeout S` via argparse passed to every blocking call (Python subprocess). See `bin-authoring-guide.md §Timeout Policy` for patterns and ms→s conversion table.
      
      Rules:
      
      - **Bash call sites**: line with `# timeout: N` must have `timeout S` shell prefix (S = N ÷ 1000); no internal fallback exists.
      - **Python call sites**: shell `timeout S` wrapper optional — timeout enforced internally via `--timeout` argparse parameter whose `default=` must equal N ÷ 1000 (from the calling site's `# timeout: N` annotation).
      - **Python `bin/` scripts with subprocess**: must expose `--timeout SECS` and pass it to every `subprocess.*` call.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_skill_contracts.py" --check 23b --root "$_ROOT"  # timeout: 30000
      ```
      
      After scan, apply model reasoning — exclude lines inside illustration/example code blocks (marked `# ✗`, surrounded by explanatory prose, or not reachable as actual tool-call commands). Flag only live executable lines. Severity: **medium** — bash comment-only timeout silently ignored at runtime; Python script missing `--timeout` default has no internal enforcement.
      
      | Sub-check | Pattern | Severity |
      | -- | -- | -- |
      | 23b — bash comment-only timeout | `# timeout: N` without `timeout S` shell prefix (non-python invocations) | medium |
      | 23b — subprocess no timeout= | `subprocess.*` call without `timeout=` in `bin/*.py` | medium |
      | 23b — missing --timeout default | Python `bin/` script uses subprocess but no `--timeout` argparse arg | medium |
      
      **Report only** — flag for human review; timeout default values must match `# timeout: N` at call site (N ÷ 1000).
      
      ### Sub-check 23c — `eval` for multi-value data output
      
      Skill uses `eval "$(...)"` or `eval "$(python ...)"` to capture data values from a bin/ script, rather than writing to TMPDIR files. Distinct from shell-setup eval (health_sentinel.py, ssh-agent) — those exempt.
      
      ```bash
      # timeout: 10000
      printf "=== Check 23c: eval for data output ===\n"
      grep -rn 'eval.*"\$.*python\|eval.*"\$.*bin/' \
          plugins/*/skills/*/SKILL.md .claude/skills/*/SKILL.md 2>/dev/null |
        grep -v 'health_sentinel\|ssh-agent\|direnv\|rbenv\|pyenv\|nvm\|# shell-setup\|parse-skill-flags\|derive_codemap_target\|git_slugs' |
        grep -v '^\s*#' | head -20
      ```
      
      False-positive exemption: eval whose stdout is `VAR=val` shell assignments for the calling shell — argument parsing (`parse-skill-flags.py`), target derivation (`derive_codemap_target.py`), slug helpers (`git_slugs.sh`), health monitoring (`health_sentinel.py`). Finding = eval used to capture a script's **data output** into variables, where a TMPDIR file is the correct channel. Cross-block persistence is Sub-check 23d, not this one.
      
      **Sub-check 23d** — shell variable used for state across separate Bash tool calls. **Not grep-detectable** (requires cross-block analysis of Bash call boundaries, which are runtime not lexical). Flag during curator per-file review only: when auditing a skill, scan for `VAR=$(...)` pattern in one fenced block and `"$VAR"` or `[ -z "$VAR" ]` in a later fenced block with no `cat "${TMPDIR:-/tmp}/...-${CSID}"` supplying `VAR` between them.
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 23c — eval for data output | `eval "$(python ...)` without `health_sentinel` context | medium | no — replace with TMPDIR-file pattern per `bin-authoring-guide.md §Script Output Routing` |
      | 23d — cross-call shell var | `$VAR` in block N, set in earlier block M, no TMPDIR bridge | medium | no — curator flag only |
      
      **Report only** — never auto-fix; replacement requires understanding the script's full output contract.
      
      ## Check 24 — Skill sequence compatibility
      
      Skill `<notes>` and `<workflow>` sections often document multi-skill chains (e.g., `→ /audit`, `suggested next: /brainstorm breakdown <file>`). Check verifies documented sequences internally consistent:
      
      - **24a (target existence)**: every skill referenced in documented chain exists on disk — root skills under `.claude/skills/<name>/`, plugin skills under `plugins/<plugin>/skills/<skill>/`
      - **24b (argument plausibility)**: when suggestion includes explicit argument (e.g., `→ /audit fix`), that argument must appear as substring in target skill's `argument-hint:` frontmatter (case-insensitive)
      
      **Step 1 — Extract sequence references**:
      
      Scan three sources for documented chains:
      
      1. **Skill files**: Grep (pattern `→.*` + backtick + `/[a-z]|suggest.*` + backtick + `/[a-z]|run.*after.*` + backtick + `/[a-z]`, glob `skills/*/SKILL.md`, path `.claude/`, output mode `content`)
      2. **Agent files**: same Grep on `agents/*.md` (path `.claude/`)
      3. **README files**: Grep same pattern in `README.md` (project root), `plugins/*/README.md`, `.claude/README.md` — README sequence tables are canonical workflow chain documentation; must be consistent with what is installed
      
      Filter out:
      
      - Lines starting with `#` (comments)
      - Lines containing `e.g.` or `for example` (illustrative, not directive)
      - Lines whose surrounding context describes what skill does rather than "run next" directive
      
      Collect all unique (source-file, skill-reference, trailing-argument) triples. README-sourced sequences held to same validity standard as skill-sourced ones: broken README sequence = **high** (user-facing workflow documentation).
      
      **Step 2 — Resolve each reference (Check 24a)**:
      
      | Reference form | Resolution |
      | -- | -- |
      | `/name` | Glob `.claude/skills/name/SKILL.md` — must exist |
      | `/plugin:name` | Glob `plugins/plugin/skills/name/SKILL.md` — must exist; if no `plugins/` dir, note "installed plugin — cannot verify statically" and skip |
      
      Missing target → **[high]**: `Sequence reference /<name> in <file> resolves to no installed skill`
      
      **Step 3 — Argument plausibility (Check 24b)**:
      
      For references with trailing argument token (e.g., `--adversarial` in `/audit --adversarial`, `breakdown` in `/brainstorm breakdown`):
      
      1. Read target skill's frontmatter `argument-hint:` (Glob-resolved path, first 5 lines)
      2. If argument token does NOT appear as case-insensitive substring of `argument-hint` → **[medium]**: `Sequence argument '<arg>' absent from /<name> argument-hint: '<hint>'`
      
      **Step 4 — Cycle detection (Check 24c)**:
      
      Build directed graph from (source-file, skill-reference) pairs collected in Step 1. Walk all paths from each node; flag back-edges (skill A → skill B → … → skill A).
      
      → Any cycle found: **[high] 24c**: `Cycle: <A> → <B> → … → <A>` — document full cycle path; do not auto-fix; resolution requires removing or redirecting one chain edge.
      
      **Report only** — no auto-fix; sequence intent requires human judgment.
      
      | Sub-check | Severity | Auto-fix |
      | -- | -- | -- |
      | 24a — target skill not on disk | high | no |
      | 24b — argument absent from argument-hint | medium | no |
      | 24c — directed cycle in follow-up chain | high | no |
      
      ## Check 27 — Cross-plugin shared-file reference integrity
      
      **Policy — every plugin's `_shared` is its own** (`plugins/CLAUDE.md` §Self-Contained \_shared). A plugin must resolve `skills/_shared` through its **own** resolver and read only files it **ships itself**. Two forbidden shapes:
      
      - **Global path** — `$HOME/.claude/skills/_shared/...` or bare `.claude/skills/_shared/...`. No such path exists any more: `/foundry:setup` symlinks only `rules/*.md` and `TEAM_PROTOCOL.md`, and purges any leftover `~/.claude/skills/` link. A dir with `SKILL.md` there would register as a user-level skill and shadow Claude Code's bundled skill of that name.
      - **Sibling reach-in** — resolving another plugin's tree (`resolve_shared_path.py foundry` from a non-foundry plugin, `dev_shared_resolve.py --foundry`, `$_FOUNDRY_SHARED`, `$_FOUNDRY_BIN`, or a literal `plugins/cc_<other>/` path). Content genuinely needed by two plugins is **duplicated**, not borrowed: add a `MANIFEST` entry in `bin/propagate_shared.py` so the copies stay byte-identical.
      
      **Sanctioned exception — optional-provider contract**: `resolve_shared_path.py codemap-py claude-skills/_shared` inside a `codemap-gates.md` / `codemap-context.md` wrapper is **not** a finding when the block also gates on `command -v codemap-py` and prints a fallback line on absence. The provider's contract has no value without the provider, and a frozen copy would drift against the installed CLI. Flag it as 27b only if the gate or fallback is missing, or if the resolved plugin is anything other than an optional provider (`plugins/CLAUDE.md` §Self-Contained `_shared`).
      
      **Special antipattern — foundry-dependency catch-22**: a borrowed file that describes how to degrade *without* foundry (e.g. `agent-resolution.md` listing `general-purpose` substitutes) is **critical** — the instructions for surviving foundry's absence are reachable only when foundry is present.
      
      **Step 1 — Global `_shared` paths** (any plugin, foundry included):
      
      ```bash
      grep -rn '\.claude/skills/_shared/' plugins/*/skills/ plugins/*/*.md 2>/dev/null  # timeout: 5000
      ```
      
      - Any match → **[high] 27a**: `<plugin>/<file>: reads _shared via global .claude/skills/ path — resolve own plugin's skills/_shared instead`
      
      **Step 2 — Sibling reach-in**:
      
      ```bash
      grep -rn 'resolve_shared_path\.py" [a-z-]*\|--foundry\|_FOUNDRY_SHARED\|_FOUNDRY_BIN\|plugins/cc_[a-z]*}\?/\(bin\|skills/_shared\)' plugins/*/skills/ 2>/dev/null | grep -v '/audit/templates/checks-skills\.md:' | awk -F/ '{ own=$2; l=$0; keep=0; while (match(l,/plugins\/cc_[a-z]*/)) { t=substr(l,RSTART+8,RLENGTH-8); if (t!="cc_" && t!=own) keep=1; l=substr(l,RSTART+RLENGTH) }; l=$0; while (match(l,/resolve_shared_path\.py" [a-z-]*/)) { t=substr(l,RSTART+24,RLENGTH-24); if ("cc_" t!=own && t!="codemap-py") keep=1; if (t=="codemap-py" && $0 !~ /command -v codemap-py/) keep=1; l=substr(l,RSTART+RLENGTH) }; if (own!="cc_foundry" && $0 ~ /_FOUNDRY_|--foundry/) keep=1; if (keep) print }'  # timeout: 5000
      ```
      
      The `}\?` admits the `${CLAUDE_PLUGIN_ROOT:-plugins/cc_<x>}/bin/…` idiom as well as a bare `plugins/cc_<x>/bin/` literal; the resolver pattern requires the double-quoted path the repo idiom always uses (an unquoted `resolve_shared_path.py foundry` in prose is not a call). The `awk` drops rows whose every `plugins/cc_<x>` token is the file's own plugin and whose every resolver token is its own name or `codemap-py` (both loops walk the whole line — a second, foreign token after an own-plugin one is still caught); `_FOUNDRY_*` / `--foundry` count as reach-ins only outside `cc_foundry`, so foundry's own skills stay in scope for every other shape; a `plugins/cc_` token with no plugin letters after it (`cc_<x>`, `cc_*`) names no sibling and is ignored (`t!="cc_"` — the token keeps its `cc_` prefix); a `codemap-py` resolver call is dropped only when `command -v codemap-py` gates it on the same line, so an ungated provider read still surfaces for the sanctioned-exception judgement; this template is excluded from its own scan because its prose quotes every forbidden shape — ~410 raw rows become the handful worth judging (POSIX awk, no GNU back-references).
      
      Ignore a plugin's own resolver call (`resolve_shared_path.py" oss` inside `cc_oss`) and its own bare-path fallback (e.g. `plugins/cc_oss/...` inside `cc_oss`) — that is the sanctioned last-resort tier — and the gated `codemap-py` provider read under the sanctioned exception above. Every plugin now ships `resolve_shared_path.py`, so a reach-in can name any sibling, not only foundry; the pattern catches the plugin token, the auditor judges it. Everything else:
      
      - Match → **[high] 27b**: `<plugin>/<skill>: reads <file> from cc_<other>'s _shared or bin — ship a copy in this plugin's own tree and add a propagate_shared.py MANIFEST entry`
      
      **Step 3 — Catch-22 upgrade**:
      
      For each 27b match, inspect the borrowed filename and surrounding context for degraded-mode signals (keywords: `fallback`, `without foundry`, `agent-resolution`, `general-purpose`, `not installed`).
      
      - Match → upgrade to **[critical] 27c**: `<plugin>/<skill>: fallback file <name> is only reachable via foundry — catch-22`
      - No match → keep as **[high] 27b**
      
      **Step 4 — Orphaned own-plugin \_shared files**:
      
      ```bash
      for f in plugins/*/skills/_shared/*; do
          plugin=$(echo "$f" | cut -d/ -f2)
          fname=$(basename "$f")
          grep -rq "$fname" "plugins/$plugin/" 2>/dev/null || echo "unreferenced: $f"
      done
      ```
      
      Own-plugin `_shared/` IS reachable at runtime (each plugin's resolver finds it), so presence there is correct — but a file no consumer names is dead weight a grep-based sweep will eventually delete (see `plugins/CLAUDE.md` §Shared File Authoring Rule).
      
      - Unreferenced → **[low] 27d**: `<plugin>: _shared/<file> named by no consumer — add a `# loads:` reference or delete`
      - Exists in plugin-local `_shared/` but not referenced → **[low]**: unreachable dead file; suggest removal
      
      **Report only** — no auto-fix; resolution requires deciding whether to inline content or move file to `foundry/_shared/`.
      
      | Sub-check | Severity | Auto-fix |
      | -- | -- | -- |
      | 27a — file absent from foundry's \_shared/ | high | no |
      | 27b — catch-22 (fallback file needs foundry to reach) | critical | no |
      | 27c — plugin-local \_shared/ file referenced but not mounted | medium | no |
      
      ## Check 28 — Cross-plugin agent dispatch fallback
      
      Skills dispatching agents via `Agent(subagent_type="<plugin>:<name>", ...)` depend on that plugin being installed. When dispatched agent belongs to different plugin from skill's own plugin, and no fallback declared for absent-plugin case, skill fails at runtime. **Exempt**: `general-purpose` (built-in, always available); `codex:*` agents (conditional dispatch tracked by Check 7).
      
      **Step 1 — Map skills to owning plugin:**
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      # needs plugin source tree — .claude/skills/ has no plugin-prefixed structure
      if [ "$LOCAL_MODE" != "true" ]; then
          echo "[Check 28 Step 1] Skipped in non-local mode (no plugin source tree)"
      else
          for f in plugins/*/skills/*/SKILL.md; do
              plugin=$(echo "$f" | cut -d/ -f2)
              skill=$(echo "$f" | cut -d/ -f4)
              echo "$plugin $skill $f"
          done
      fi
      ```
      
      **Step 2 — Collect cross-plugin dispatches per skill:**
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      if [ "$LOCAL_MODE" != "true" ]; then
          echo "[Check 28 Step 2] Skipped in non-local mode (no plugin source tree)"
      else
          grep -rn 'subagent_type' plugins/*/skills/*/SKILL.md 2>/dev/null | grep -v '^Binary'  # timeout: 5000
      fi
      ```
      
      For each match: extract `(skill_file, dispatched_plugin, dispatched_agent)`. Dispatch is **cross-plugin** when `dispatched_plugin ≠ owning_plugin`. Build map: `skill_file → [cross-plugin agents]`.
      
      Skip: any `general-purpose` dispatch and any `codex:*` dispatch.
      
      **Step 3 — Verify fallback coverage:**
      
      For each skill with one or more cross-plugin dispatches, read skill file and search for fallback declaration. Valid fallback is any of:
      
      - Section heading matching `Agent Resolution`, `Fallback`, or `Plugin Check` (case-insensitive)
      - Sentence containing cross-plugin agent name AND word from `{fallback, not installed, substitute, general-purpose, unavailable}` within 5 lines of each other
      - Conditional dispatch block: `if not installed` or `plugin list.*grep.*<plugin>` followed by alternative
      
      No fallback found → **[high] Check 28a**: `<plugin>/<skill>: dispatches <cross-plugin-agent> with no fallback for missing plugin`
      
      **Step 4 — Completeness check:**
      
      For each skill where fallback section exists: verify every cross-plugin agent dispatched by that skill is named within fallback block (bare name OR fully-qualified `plugin:name` form). Agent covered when name appears in fallback block.
      
      Partially covered → **[medium] Check 28b**: `<plugin>/<skill>: fallback section present but does not cover <agent>`
      
      **Report only** — fixing requires adding Agent Resolution section with fallback substitutes for each cross-plugin dependency; pattern in `develop:plan` (Agent Resolution table with `foundry agent | Fallback | Model | Role description prefix`) is reference implementation.
      
      > **Related**: Check 25 (in `checks-shared.md`) covers bare-name dispatch (missing plugin prefix). Check 25 and Check 28 address different failure modes — run both.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 28a | no fallback for cross-plugin dispatch | high | no |
      | 28b | fallback present but agent not covered | medium | no |
      
      ### Sub-check 28c — Cross-plugin prose references without availability guard
      
      Skills may reference other plugins' skills in `<notes>`, follow-up chains, and prose documentation without runtime dispatch (no `Agent(subagent_type=...)` call). These prose references shown to users as runnable next-steps; if referenced plugin absent, command fails silently.
      
      **Step — Scan for unguarded prose cross-plugin references**:
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check 28c: Cross-plugin prose refs ===\n"
      # needs plugin source tree
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check 28c skipped in non-local mode (no plugin source tree)\n"
      else
          for f in plugins/*/skills/*/SKILL.md; do
            [ -f "$f" ] || continue
            skill_dir=$(echo "$f" | cut -d/ -f2)
            plugin_json="plugins/$skill_dir/.claude-plugin/plugin.json"
            [ -f "$plugin_json" ] || plugin_json="plugins/$skill_dir/.codex-plugin/plugin.json"
            # routing prefix is plugin.json `name` (cc_foundry -> foundry); author.name precedes it, so parse JSON, never grep
            routing_prefix=$(python -c "import json,sys; print(json.load(open(sys.argv[1]))['name'])" "$plugin_json" 2>/dev/null)
            # unreadable manifest must not silently drop the file from scan — fall back to no self-filter
            [ -n "$routing_prefix" ] || routing_prefix="__no_self_filter__"
            # match backtick-wrapped or plain /plugin:skill refs
            matches=$(grep -nE '`/[a-z]+:[a-z]|/oss:|/develop:|/research:|/codemap:|/codemap-py:|/foundry:' "$f" 2>/dev/null |
              grep -v "subagent_type\|#.*requires\|requires.*plugin\|plugin.*installed\|if.*plugin" |
              grep -v "/${routing_prefix}:" || true)
            if [ -n "$matches" ]; then
              echo "$matches" | while IFS= read -r line; do
                printf "⚠ 28c: %s — cross-plugin ref without availability guard: %s\n" "$f" "$line"
                printf "  fix: add '(requires <plugin> plugin)' inline, or wrap in availability check\n"
              done
            fi
          done
          printf "✓: Check 28c scan complete\n"
      fi  # timeout: 5000
      ```
      
      Severity: **medium** — user sees broken command in follow-up gate or documentation prose.
      
      Fix: append `` (requires `<plugin>` plugin) `` immediately after cross-plugin skill reference, or restructure as conditional.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 28a | no fallback for cross-plugin dispatch | high | no |
      | 28b | fallback present but agent not covered | medium | no |
      | 28c | prose cross-plugin ref without availability guard | medium | no |
      
      ## Check 30 — Plugin skill bash operational correctness
      
      Four static-grep patterns catching silent failures in skill SKILL.md bash blocks. Run across both `.claude/skills/` and `plugins/*/skills/` — bugs appear in any skill.
      
      ### 30a — Pipe exit code capture (PIPESTATUS)
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 30a: Pipe exit code capture ===\n"
      # tail/head always exit 0 — $? after them ≠ upstream cmd's exit
      find "$_ROOT" -path "*/skills/*" -name "*.md" -exec grep -Hn '| tail\b\|| head\b' {} + 2>/dev/null |
        grep -v 'PIPESTATUS\|pipefail\|#.*tail\|#.*head' |
        grep -v '^Binary' &&
      printf "  hint: use \${PIPESTATUS[0]} or set -o pipefail; \$? captures tail/head exit (always 0)\n" || true
      printf "✓: Check 30a scan complete\n"  # timeout: 5000
      ```
      
      Severity: **critical** — gate commands appear to pass on genuine failure; `$?` after `cmd | tail -N` = tail's exit code (0), not cmd's.
      
      Fix pattern: `cmd 2>&1 | tail -N; EXIT=${PIPESTATUS[0]}`
      
      ### 30b — SKIP variable guard missing
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 30b: SKIP variable guard ===\n"
      # anchor at word start — FORTIFY_SKIP_VARIANT=1 is a printed inter-block signal, not a guard variable; self-excluded because this template quotes the pattern (precedent: Check 27 at line 246). Two -e patterns, not (^|…) — `^` inside an ERE group is not portable to BSD grep.
      find "$_ROOT" -path "*/skills/*" -name "*.md" ! -name "checks-skills.md" -exec grep -Hn -E -e '^SKIP_[A-Z_]*=1' -e '[^A-Za-z0-9_]SKIP_[A-Z_]*=1' {} + 2>/dev/null |
        grep -v '^Binary' | grep -v '#' | while IFS= read -r match; do
          file=$(echo "$match" | cut -d: -f1)
          grep -q '\[ "\${SKIP_' "$file" 2>/dev/null ||
            printf "⚠ SKIP guard missing: %s — SKIP variable set but no conditional guard found\n" "$file"
      done
      printf "✓: Check 30b scan complete\n"  # timeout: 5000
      ```
      
      Severity: **critical** — `SKIP_RUFF=1` set by tool detection, but `$RUNNER ruff check` runs unconditionally; detection is cosmetic.
      
      Fix pattern: `[ "${SKIP_RUFF:-0}" -ne 1 ] && $RUNNER ruff check ...`
      
      ### 30c — Agent filename convention mismatch (model reasoning)
      
      Cannot be caught by grep alone — requires reading spawn prompt and consolidator read pattern in same file.
      
      Flag when skill file:
      
      1. Spawns agents with prompt instructing them to write findings to file named with plugin-prefixed format (e.g. `foundry:sw-engineer.md`)
      2. AND consolidator reads files using bare-name format (e.g. `sw-engineer.md`)
      
      These never match → all agent findings silently dropped.
      
      Severity: **high**
      
      Fix: standardize to bare agent name in both spawn prompt and consolidator read pattern (e.g. `sw-engineer.md`).
      
      ### 30d — TEST_CMD used with pytest-specific flags without PYTEST_CMD split
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 30d: TEST_CMD/PYTEST_CMD split ===\n"
      find "$_ROOT" -path "*/skills/*" -name "*.md" -exec grep -Hn \
        '\$TEST_CMD.*--tb\b\|\$TEST_CMD.*--co\b\|\$TEST_CMD.*::\|\$TEST_CMD.*--cov\b\|\$TEST_CMD.*--doctest' {} + 2>/dev/null |
        grep -v 'PYTEST_CMD\|#' | grep -v '^Binary' &&
      printf "  hint: derive PYTEST_CMD for pytest-specific flags; TEST_CMD=tox or make won't accept --tb/--co/::/--cov\n" || true
      printf "✓: Check 30d scan complete\n"  # timeout: 5000
      ```
      
      Severity: **high** — skill fails silently on tox/make projects when pytest-specific flags appended to TEST_CMD.
      
      Fix: after detecting TEST_CMD, derive `PYTEST_CMD` for targeted runs: `tox` → `PYTEST_CMD="uv run pytest"`; `make test` → `PYTEST_CMD="uv run pytest"`.
      
      **Report only** — no auto-fix; resolution requires understanding each skill's runner detection block.
      
      ### 30e — Heredoc python in skill bodies
      
      Heredoc python blocks (`python << 'EOF'`) banned by CLAUDE.md. Distinct from 23a (targets `python -c` one-liners); 30e catches multi-line heredoc forms that bypass one-liner size limit.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 30e: Heredoc python ===\n"
      find "$_ROOT" -path "*/skills/*" -name "*.md" -exec grep -Hn "python <<\|python << '" {} + 2>/dev/null |
        grep -v '^Binary' | grep -v '^\s*#' &&
      printf "  hint: CLAUDE.md bans python heredoc; use bin/*.py script instead\n" || true
      printf "✓: Check 30e scan complete\n"  # timeout: 5000
      ```
      
      Severity: **high** — heredoc triggers permission prompt; user deny = workflow block; violates CLAUDE.md §Pre-Authorized Operations.
      
      ### 30f — Missing exit on confirmed failure path
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 30f: Missing exit on confirmed failure ===\n"
      find "$_ROOT" -path "*/skills/*" -name "*.md" -exec grep -Hn \
        'GENUINE.FAILURE\|all retries failed\|failed.*abort\|error.*critical\|cannot continue' {} + 2>/dev/null |
        grep -v '^Binary' | grep -v '^\s*#' | while IFS= read -r match; do
          file=$(echo "$match" | cut -d: -f1)
          line=$(echo "$match" | cut -d: -f2)
          context=$(awk "NR>=$line && NR<=$((line+3))" "$file" 2>/dev/null)
          echo "$context" | grep -q 'exit [1-9]' ||
            printf "⚠ missing exit: %s:%s — failure detected but execution continues\n" "$file" "$line"
        done
      printf "✓: Check 30f scan complete\n"  # timeout: 5000
      ```
      
      Severity: **high** — workflow continues past a detected failure; downstream steps produce misleading output or incorrect partial results.
      
      Fix pattern: add `exit 1` (or appropriate non-zero exit) immediately after the failure is confirmed; do NOT continue to next step.
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 30a — pipe exit code | `\| tail` / `\| head` without PIPESTATUS | critical | no |
      | 30b — SKIP guard missing | `SKIP_X=1` with no `[ "${SKIP_X:-0}" ]` guard | critical | no |
      | 30c — filename mismatch | spawn filename ≠ consolidator filename (model reasoning) | high | no |
      | 30d — TEST_CMD+pytest flags | `$TEST_CMD --tb` / `--co` / `::` / `--cov` without PYTEST_CMD | high | no |
      | 30e — heredoc python | `python <<` in skill body | high | no |
      | 30f — missing exit | confirmed failure with no `exit 1` within 3 lines | high | no |
      
      ## Check 31 — Skill tool call vs allowed-tools consistency
      
      For each SKILL.md, verify every **gating or dispatch tool** called in workflow body is declared in `allowed-tools:` frontmatter. Runtime enforces `allowed-tools` — undeclared tool calls blocked silently; entire workflow phase fails with no error message.
      
      **High-risk tools** (absence breaks entire workflow phases, not just individual steps):
      
      | Tool | Consequence if absent from `allowed-tools` |
      | -- | -- |
      | `Skill` | Follow-up gate never dispatches target skill; user's selection silently dropped |
      | `AskUserQuestion` | Skill falls back to prose questions (violates communication.md; interactive gates broken) |
      | `Agent` | Sub-agent spawns blocked; orchestration phase fails silently |
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      found=0
      # process substitution, not pipe — piped while runs in subshell, found wouldn't survive
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          skill_name=$(basename "$(dirname "$f")")
          allowed=$(awk '/^---$/{c++} c==1{print} c==2{exit}' "$f" 2>/dev/null | grep '^allowed-tools:' | sed 's/allowed-tools:[[:space:]]*//')
          [ -z "$allowed" ] && continue
          body=$(awk '/^---$/{c++} c>=2{print}' "$f" 2>/dev/null)
          for tool in Skill AskUserQuestion Agent; do
              if echo "$body" | grep -qE "\b${tool}\(" 2>/dev/null; then
                  if ! echo "$allowed" | grep -qw "$tool"; then
                      printf "! BREAKING skills/%s: body calls %s() but '%s' absent from allowed-tools\n" "$skill_name" "$tool" "$tool"
                      printf "  fix: add '%s' to allowed-tools: in %s\n" "$tool" "$f"
                      found=1
                  fi
              fi
          done
      done < <(find "$_ROOT" -path "*/skills/*/SKILL.md" 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 31 — all gating tool calls covered by allowed-tools\n"  # timeout: 5000
      ```
      
      Severity: **critical** — blocked gating tool = entire workflow phase silently broken at runtime.
      
      Auto-fix: append missing tool name to `allowed-tools:` frontmatter line.
      
      | Sub-check | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | 31 — tool-body mismatch | body calls `Skill()`, `AskUserQuestion()`, or `Agent()` but tool absent from `allowed-tools` | critical | yes — add to frontmatter |
      
      ### Sub-check 31a — Skill frontmatter completeness
      
      Verify required frontmatter fields present in every SKILL.md. Missing fields cause undocumented default behavior or miscategorized routing.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 31a: Frontmatter completeness ===\n"
      found=0
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          skill=$(basename "$(dirname "$f")")
          fm=$(awk '/^---$/{c++} c==1{print} c==2{exit}' "$f" 2>/dev/null)
          echo "$fm" | grep -q '^effort:' || {
              printf "⚠ 31a: %s — missing effort: field (required; no default)\n" "$skill"
              found=1
          }
          echo "$fm" | grep -q '^when_to_use:' && {
              printf "⚠ 31a: %s — when_to_use: present (deprecated; merge content into description: then remove)\n" "$skill"
              found=1
          }
      done < <(find "$_ROOT" -path "*/skills/*/SKILL.md" 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 31a — frontmatter complete across all skills\n"
      ```
      
      Severity: **medium** for `effort:` (no default documented); **low** for `when_to_use:` (deprecated field).
      
      | Sub-check | Field | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- | -- |
      | 31 — tool-body mismatch | `allowed-tools` | body calls Skill/AskUserQuestion/Agent, not in frontmatter | critical | yes |
      | 31a — effort missing | `effort:` | always required | medium | yes |
      | 31a — when_to_use present | `when_to_use:` | deprecated — any presence flagged (merge into `description:`, then strip) | low | no |
      
      ## Check C35 — Background agent health monitoring compliance (CLAUDE.md §6)
      
      CLAUDE.md §6 requires every skill spawning background agents to implement: (1) launch sentinel creation, (2) 5-min file-activity poll, (3) 15-min hard cutoff. Absence = stalled agents silently drop findings.
      
      **Step 1 — Find skills that spawn agents**. Every spawn is a background spawn now: `Agent()` never blocks and there is no `run_in_background` parameter, so matching that dead token would make this check silently N/A everywhere.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check C35: Agent health monitoring ===\n"
      mkdir -p "${TMPDIR:-/tmp}/audit-state-${CSID}"
      find "$_ROOT" -path "*/skills/*/SKILL.md" -exec grep -l 'Agent(subagent_type' {} + 2>/dev/null |
        sort > "${TMPDIR:-/tmp}/audit-state-${CSID}/c35-bg-skills"
      if [ ! -s "${TMPDIR:-/tmp}/audit-state-${CSID}/c35-bg-skills" ]; then
          printf "✓: No agent spawns found — C35 N/A\n"
      else
          cat "${TMPDIR:-/tmp}/audit-state-${CSID}/c35-bg-skills"
      fi  # timeout: 5000
      ```
      
      **Step 2 — For each skill found, verify §8 protocol elements**:
      
      Step 1's list is re-read from the state file — a variable set in Step 1's block is gone by the time this block runs (fresh shell per Bash call).
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      while IFS= read -r f; do  # timeout: 5000
          [ -f "$f" ] || continue
          skill=$(basename "$(dirname "$f")")
          if grep -q 'agent-spawn-protocol' "$f" 2>/dev/null; then
              printf "✓ C35: %s — references agent-spawn-protocol.md\n" "$skill"
              continue
          fi
          # grep -c prints 0 AND exits 1 on no match — || echo 0 double-fires, "0\n0" breaks numeric tests below
          has_sentinel=$(grep -c 'LAUNCH_AT\|touch /tmp/' "$f" 2>/dev/null) || has_sentinel=0
          has_probe=$(grep -c 'find.*-newer.*-type f.*wc -l\|completion notification' "$f" 2>/dev/null) || has_probe=0
          has_cutoff=$(grep -c 'HARD_CUTOFF\|timed.out\|15 min\|900' "$f" 2>/dev/null) || has_cutoff=0
          # MONITOR_INTERVAL is now a defect, not evidence: nothing sleeps, so an interval has no clock to run on
          has_interval=$(grep -c 'MONITOR_INTERVAL\|every 5 min\|poll every' "$f" 2>/dev/null) || has_interval=0
          [ "$has_sentinel" -eq 0 ] && printf "⚠ C35a: %s — no launch sentinel (CLAUDE.md §6)\n" "$skill"
          [ "$has_probe" -eq 0 ]   && printf "⚠ C35b: %s — no liveness probe and no completion-notification handling (CLAUDE.md §6)\n" "$skill"
          [ "$has_cutoff" -eq 0 ]  && printf "⚠ C35c: %s — no 15-min hard cutoff (CLAUDE.md §6)\n" "$skill"
          [ "$has_interval" -gt 0 ] && printf "⚠ C35d: %s — prescribes a fixed-interval poll; spawns are background, the turn ends and resumes on notification\n" "$skill"
      done < "${TMPDIR:-/tmp}/audit-state-${CSID}/c35-bg-skills"
      ```
      
      Severity: **high** for C35a/b/c — stalled agents drop findings with no user-visible signal. **medium** for C35d — a dead interval wastes turns rather than losing findings. Fix: reference `$_FOUNDRY_SHARED/agent-spawn-protocol.md` (preferred) or inline the elements in the skill.
      
      | Sub-check | Pattern | Severity | Auto-fix |
      | -- | -- | -- | -- |
      | C35a — no launch sentinel | no `touch /tmp/<sentinel>` after spawn | high | no |
      | C35b — no liveness probe | no `find -newer` probe, no completion-notification handling | high | no |
      | C35c — no hard cutoff | no `HARD_CUTOFF` / 15-min signal | high | no |
      | C35d — fixed-interval poll | `MONITOR_INTERVAL`, "every 5 min", "poll every" — no clock exists to run it | medium | no |
      
      ## Check 32 — Dead file detection
      
      Surfaces skill subdirectory files and rule files that exist on disk but are never loaded at runtime — accumulated from past iterations where references were removed but files were not.
      
      ### Sub-check 32a — Dead mode files
      
      Mode files in `*/skills/*/modes/` that are not referenced from the parent skill's `SKILL.md` are never executed. They create maintenance confusion and may contain outdated logic that silently diverges from the live mode.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 32a: Dead mode files ===\n"
      found=0
      while IFS= read -r mode_file; do  # timeout: 5000
          skill_dir="$(dirname "$(dirname "$mode_file")")"
          [ -f "$skill_dir/SKILL.md" ] || continue
          mode_name=$(basename "$mode_file")
          hit=0
          # dispatch can be multi-level: a mode may be loaded from a sibling mode file, not only SKILL.md
          while IFS= read -r consumer; do
              # a shared file's own `<!-- file: -->` header names it — self-match is not a reference
              [ "$consumer" = "$mode_file" ] && continue
              /usr/bin/grep -qF "$mode_name" "$consumer" 2>/dev/null && { hit=1; break; }
          done < <(find "$skill_dir" \( -name "SKILL.md" -o -path "*/modes/*.md" \) 2>/dev/null | sort)
          if [ "$hit" -eq 0 ]; then
              printf "⚠ 32a: %s — not referenced in %s/SKILL.md or any sibling modes/*.md\n" "$mode_file" "$skill_dir"
              found=1
          fi
      done < <(find "$_ROOT" -path "*/skills/*/modes/*.md" 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 32a — all mode files referenced in SKILL.md or a sibling mode file\n"
      ```
      
      Severity: **medium** — dead mode file = unreachable code; may diverge silently from live workflow. Auto-fix: delete the file, or add a reference in SKILL.md if omission was accidental.
      
      ### Sub-check 32b — Dead template files
      
      Template files in `*/skills/*/templates/` not referenced from the parent `SKILL.md` are never injected into prompts.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 32b: Dead template files ===\n"
      found=0
      while IFS= read -r tpl_file; do  # timeout: 5000
          skill_dir="$(dirname "$(dirname "$tpl_file")")"
          [ -f "$skill_dir/SKILL.md" ] || continue
          tpl_name=$(basename "$tpl_file")
          hit=0
          # templates are routinely loaded from a mode file, not SKILL.md (audit/modes/steps-4-5-7.md loads every checks-*.md)
          while IFS= read -r consumer; do
              [ "$consumer" = "$tpl_file" ] && continue
              /usr/bin/grep -qF "$tpl_name" "$consumer" 2>/dev/null && { hit=1; break; }
          done < <(find "$skill_dir" \( -name "SKILL.md" -o -path "*/modes/*.md" \) 2>/dev/null | sort)
          if [ "$hit" -eq 0 ]; then
              printf "⚠ 32b: %s — not referenced in %s/SKILL.md or any sibling modes/*.md\n" "$tpl_file" "$skill_dir"
              found=1
          fi
      done < <(find "$_ROOT" -path "*/skills/*/templates/*" -type f 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 32b — all template files referenced in SKILL.md or a mode file\n"
      ```
      
      Severity: **low** — templates may be referenced indirectly via agent spawn prompts that mention the filename inline; human review required before deletion. Auto-fix: delete if confirmed unused; no auto-delete.
      
      ### Sub-check 32c — Dead rule files (paths: matches no project files)
      
      Rule files with `paths:` frontmatter that match no existing project files are never applied. Rules without `paths:` (global rules) are always active — skip those.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      [ "$LOCAL_MODE" = "true" ] && _ROOT="plugins" || _ROOT=".claude"
      printf "=== Check 32c: Dead rule files ===\n"
      found=0
      while IFS= read -r rule_file; do  # timeout: 5000
          [ -f "$rule_file" ] || continue
          paths_block=$(awk '/^---$/{c++} c==1{print} c==2{exit}' "$rule_file" 2>/dev/null | awk '/^paths:/{p=1;next} p && /^[^ ]/{p=0} p{print}')
          [ -z "$paths_block" ] && continue  # no paths: — global rule, always active, skip
          matched=0
          while IFS= read -r pat_line; do
              pat=$(echo "$pat_line" | sed "s/^ *- *'//;s/'$//;s/^ *- *//")
              [ -z "$pat" ] && continue
              found_file=$(find . -path "./$pat" -not -path "./.git/*" 2>/dev/null | head -1)
              [ -n "$found_file" ] && { matched=1; break; }
          done <<< "$paths_block"
          if [ "$matched" -eq 0 ]; then
              printf "⚠ 32c: %s — paths: patterns match no project files (rule never applied)\n" "$rule_file"
              found=1
          fi
      done < <(find "$_ROOT" -path "*/rules/*.md" 2>/dev/null | sort)
      [ "$found" -eq 0 ] && printf "✓: Check 32c — all scoped rules match at least one project file\n"
      ```
      
      Severity: **medium** — rule with non-matching paths is never applied; may represent outdated scope (e.g., `src/**/*.py` when project no longer has Python files). Note: false positives possible if project files are in a non-standard location or generated at runtime. Human review before deletion. Auto-fix: remove `paths:` to make the rule global, or delete the file if rule is obsolete.
      
      ### Sub-check 32d — Orphaned bin/ scripts
      
      `bin/` scripts existing in the plugin source tree but not referenced by any `.md` file in that plugin (SKILL.md, agents, rules, modes, templates, \_shared) are unreachable at runtime. Common cause: script authored as scaffolding but never wired into its caller SKILL.md.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check 32d: Orphaned bin/ scripts ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check 32d skipped in non-local mode (no plugin source tree)\n"
      else
          python "${CLAUDE_PLUGIN_ROOT}/bin/check_orphaned_bin.py" --plugins-dir plugins  # timeout: 10000
      fi
      ```
      
      Severity: **high** — orphaned script = either dead code or incomplete extraction; both are runtime gaps, not stylistic noise. An extraction that creates a bin/ script without wiring it into the caller leaves the inline twin active and the new script unreachable. Auto-fix: no — each category requires human judgment: (a) zero plausible consumer: confirm no in-progress branch before deleting; (b) cross-plugin consumer: search other plugins for the basename — if found, add `<!-- file: <basename> — consumers: <plugin> skills/<name> -->` doc header; (c) extraction started but wire-in skipped: identify the correct consumer SKILL.md and replace inline twin with bin/ invocation.
      
      > Note: search covers the entire plugins tree — cross-plugin callers are found correctly. False negatives possible only if the caller references the script by a dynamic path or alias that does not include the basename.
      
      | Sub-check | Target | Condition | Severity | Auto-fix |
      | -- | -- | -- | -- | -- |
      | 32a — dead mode file | `*/modes/*.md` | file exists but not referenced in parent SKILL.md | medium | delete file or add reference |
      | 32b — dead template file | `*/templates/*` | file exists but not referenced in parent SKILL.md | low | human review — may be indirect ref |
      | 32c — dead rule file | `*/rules/*.md` | `paths:` set but matches no project files | medium | remove `paths:` or delete file |
      | 32d — orphaned bin/ script | `plugins/*/bin/*.py`, `*.sh` | script not referenced in any plugin .md file | high | yes — see severity guidance above |
      | 32e — bin/ script cross-similarity | `plugins/*/bin/*.py` | ≥ 2 scripts with structural similarity ≥ 0.8 after normalization | medium | no — semantic review required |
      
      ### Sub-check 32e — Cross-similarity between bin/ scripts
      
      bin/ scripts sharing structural patterns (≥ 0.8 similarity after normalizing identifiers and string literals) across or within plugins are merge candidates — one parametrized script replaces both, reducing duplicated maintenance surface. Common case: per-plugin helpers differing only in a path constant, plugin name, or threshold.
      
      Skip in non-local mode (no source tree). Skip when fewer than 2 non-private bin/ Python scripts exist.
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      printf "=== Check 32e: bin/ script cross-similarity ===\n"
      if [ "$LOCAL_MODE" != "true" ]; then
          printf "✓: Check 32e skipped in non-local mode (no plugin source tree)\n"
      else
          # newline-delimited scalar, not array — mapfile has no zsh equivalent (see checks-shared Check 45)
          _C32E_SCRIPTS=$(find plugins -path '*/bin/*.py' -not -name '_*.py' 2>/dev/null | sort)  # timeout: 5000
          _C32E_N=$(printf '%s' "$_C32E_SCRIPTS" | grep -c '^')
          if [ "$_C32E_N" -lt 2 ]; then
              printf "✓: Check 32e — fewer than 2 bin/ scripts, skip\n"
          else
              printf "⚙ Check 32e — %d bin/ scripts found; delegating similarity analysis to foundry:curator\n" "$_C32E_N"
          fi
      fi
      ```
      
      **Delegation prompt** (when `_C32E_N ≥ 2`): spawn foundry:curator with the `_C32E_SCRIPTS` file list and this instruction:
      
      > "Scan these bin/ Python scripts for cross-similarity. For each pair: strip docstrings and inline comments → normalize variable names to `<VAR>` → normalize string literals and path constants to `<STR>` → compare AST-level structure. Report pairs with structural similarity ≥ 0.8. For each candidate pair: script names, plugin origin, similarity score, what differs (constant, path, plugin name, threshold), lines saved by merge, and suggested parametrization (e.g. `--plugin-root` flag, `--output-dir` arg, or move shared logic to `_shared/` helper). Skip pairs serving clearly different semantic roles despite structural overlap. Skip pairs marked `# audit-skip: resilience-replication` in their module docstring (intentional per-plugin independence)."
      
      Severity: **medium** — near-duplicate scripts = duplicated maintenance; a bug fix in one copy likely missed in the other. Auto-fix: no — merge requires semantic review of both scripts and all callers; confirm no behavioral difference before merging.
      
      ## Check 32f — Mode-file body shadowed in SKILL.md (extraction integrity)
      
      Detects the "extraction done but inline twin survived" topology: `modes/<name>.md` is referenced from `SKILL.md` AND its body is also present inline in `SKILL.md`. Complements Check 32a (which catches unreferenced mode files — the inverse polarity).
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/check_skill_contracts.py" --check 32f $( [ "$LOCAL_MODE" = "true" ] && echo "--local" )  # timeout: 30000
      ```
      
      Severity: **medium** — inline twin diverges silently from canonical mode file on every future edit. Auto-fix: delete inline block from SKILL.md; replace with bash+read pattern matching other modes.
      
      ## Check 32g — Self-confessed manual sync markers
      
      Detects explicit sync instructions in plugin `.md` files (`SYNC:`, `lock-step`, `keep both`, `duplicated from`, `mirror this in`, `keep.*copies.*sync`). Each marker = author admitted manual sync required = extraction not yet done.
      
      ```bash
      printf "=== Check 32g: self-confessed sync markers ===\n"
      _C32G_HITS=$(grep -rn \
          -e 'SYNC:' \
          -e 'lock-step' \
          -e 'lockstep' \
          -e 'keep both' \
          -e 'keep.*copies.*sync' \
          -e 'duplicated from' \
          -e 'mirror this in' \
          plugins/*/agents/*.md plugins/*/skills/*/SKILL.md plugins/*/skills/*/modes/*.md \
          plugins/*/rules/*.md 2>/dev/null | grep -v '# audit-skip:')
      if [ -n "$_C32G_HITS" ]; then
          echo "$_C32G_HITS" | while IFS= read -r hit; do
              printf "⚠ 32g [medium] %s — self-confessed manual sync; extract canonical source or delete duplicate\n" "$hit"
          done
      else
          printf "✓: Check 32g — no sync markers found\n"
      fi  # timeout: 10000
      ```
      
      Severity: **medium** — self-confessed sync = guaranteed future drift. Auto-fix: run `/distill memory` or extract to `modes/` + replace inline block with bash+read pattern.
      
      ## Check 33 — Code block duplication (NxN similarity matrix)
      
      <!-- policy-sibling: plugins/cc_foundry/skills/audit/modes/efficiency.md (Phase B2 Table 2 — same Gate/Score spec) -->
      
      Full-spectrum detection of duplicate or near-duplicate fenced code blocks across all .md files (SKILL.md, agents, rules, templates, modes) — any language (bash, python, sh, perl, ruby, js, etc.). Produces NxN pairwise similarity matrix to surface extraction candidates: 33a within-file (same block 3+ times — bin/ script or helper function candidate); 33b cross-file NxN (same block in 3+ .md files — shared bin/ script candidate).
      
      **Check 33a — Within-file repetition**: delegate to Phase A foundry:curator (has full file context). Curator prompt must include:
      
      > "Extract every fenced code block (any language marker — ```` ```bash ````, ```` ```python ````, ```` ```sh ````, ```` ```perl ````, ```` ```ruby ````, ```` ```js ````, etc.) from this file. For each pair of blocks, compute normalized similarity: strip comments → normalize variable names to `<VAR>` → normalize string literals to `<STR>` → compare structure. Report any pair with similarity ≥ 0.8 that appears 3+ times (within this file) as a 33a finding. For each candidate: block language, purpose, occurrence count, similarity score, what differs between instances, and suggested extraction (bash function defined once in pre-flight, or `bin/<name>.py` — bin/ scripts are Python only). Context saving estimate: (block_lines − 1) × occurrence_count. Skip: blocks marked `# audit-skip: resilience-replication` (first line of block) or prose annotation matching 'intentional resilience replication'."
      
      **33b — Cross-file NxN** — two phases: bash quick scan identifies known hotspots; curator NxN delegation runs when clusters found. Scope: all .md files in plugin tree (SKILL.md, agents, rules, templates, modes).
      
      **Phase 1 — Bash quick scan** (known duplication hotspots):
      
      ```bash
      export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
      IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
      if [ "$LOCAL_MODE" = "true" ]; then
          _C33_DIR="plugins/"
      else
          # latest foundry version dir — skip other cached versions
          _C33_DIR=$(ls -td ~/.claude/plugins/cache/borda-ai-rig/foundry/*/ 2>/dev/null | head -1)
          _C33_DIR="${_C33_DIR:-.claude/}"
      fi
      printf "=== Check 33b: scope=%s files=%d ===\n" "$_C33_DIR" \
          "$(find "$_C33_DIR" -name '*.md' -type f 2>/dev/null | wc -l | tr -d ' ')"
      
      printf "=== Check 33b Phase 1: Cross-file code block quick scan ===\n"
      
      MODE_DISPATCH=$(grep -rl 'find.*plugins/cache.*-path.*modes/' "$_C33_DIR" --include="*.md" 2>/dev/null | wc -l | tr -d ' ')
      [ "${MODE_DISPATCH:-0}" -ge 3 ] && printf "⚠ 33b: bash mode-dispatch pattern in %s files — bin/ extraction candidate: resolve_skill_mode.py <mode>\n" "$MODE_DISPATCH"
      
      SHARED_RES=$(grep -rl '=\$(find.*plugins/cache.*_shared\|=\$(ls -td.*plugins/cache' "$_C33_DIR" --include="*.md" 2>/dev/null | wc -l | tr -d ' ')
      [ "${SHARED_RES:-0}" -ge 3 ] && printf "⚠ 33b: bash _shared resolution pattern in %s files (variants may be inconsistent) — bin/ extraction candidate\n" "$SHARED_RES"
      
      PY_HEREDOC=$(grep -rl 'python -c' "$_C33_DIR" --include="*.md" 2>/dev/null | wc -l | tr -d ' ')
      [ "${PY_HEREDOC:-0}" -ge 3 ] && printf "⚠ 33b: python -c one-liner in %s files — evaluate if any cluster repeats\n" "$PY_HEREDOC"
      
      # audit-skip: resilience-replication — unsupported-flag-check is intentional per-plugin
      FLAG_CHECK=$(grep -rl 'Unknown flag' "$_C33_DIR" --include="*.md" 2>/dev/null | wc -l | tr -d ' ')
      [ "${FLAG_CHECK:-0}" -ge 3 ] && printf "ℹ 33b: unsupported-flag-check boilerplate in %s files — known intentional per-plugin resilience\n" "$FLAG_CHECK"
      
      echo "--- Code block language distribution across all .md files (Phase 2 trigger signals) ---"
      NEEDS_CURATOR_NXN=false
      for lang in bash python sh perl ruby node js; do
        count=$(grep -rl "^\`\`\`${lang}" "$_C33_DIR" --include="*.md" 2>/dev/null | wc -l | tr -d ' ')
        [ "${count:-0}" -ge 5 ] && { echo "  ${lang}:
    • curator-prompt.md 3.3 KB
      Each invocation, ask curator to check:
      
      - **Purpose and logical coherence**: role clearly defined? Scope right — not too broad, not too narrow? New user know when to reach for it vs similar one?
      
      - **Structural completeness** (tag/fence symmetry handled by deterministic bin/ checkers — do not restate): required sections present, step numbering sequential, **no orphaned empty blocks** — any structural tag (`<constants>`, `<notes>`, `<calibration>`, `<inputs>`, `<not-for>`, `<role>`, `<initialization>`, `<antipatterns-to-flag>`) containing only whitespace = dead markup, remove; flag **medium** (gate-level); auto-fix safe (no content to lose)
      
      - **Cross-reference validity**: every agent/skill name mentioned must exist on disk. Cross-reference against Step 2 inventory. Name not in Step 2 inventory = **broken cross-reference** (critical). No conditional language ("if X doesn't exist") — by Step 3, inventory known. If inventory not collected (e.g., running in isolation), flag: "unverified reference — requires disk inventory check." **Antipattern to flag**: writing "potentially missing" / "likely doesn't exist" / "if this agent doesn't exist" / "pending verification" / "should be checked against inventory" when Step 2 ran. These phrases = agent not using inventory. Name in workflow, absent from Step 2 list = confirmed broken cross-reference — report critical, not conditional. Conditional language acceptable only when Step 2 genuinely not run.
      
      - **Verbosity and duplication**: bloated steps, repeated instructions, copy-paste between files. **Token count is verbosity metric, not line count**:
      
        - prefer breaking long lines into shorter ones for clarity (line breaks help model processing)
        - flag splits that add words, padding, or prose beyond minimal formatting overhead (newline, list marker)
        - N+1 backtick outer fence when inner content has N-backtick fences = correct CommonMark nesting — not formatting overhead; do not flag as non-standard
      
      - **Edit quality gate** — self-challenge every addition, edit, deletion:
      
        - best approach: simpler path exists → flag it; no unnecessary complexity or speculative abstractions
        - no side effects: cross-refs still resolve, existing callers unaffected, no behavior regression
        - complete and clean: no gaps, no dead instructions, no orphaned cross-refs, no leftover stubs
        - verified: every claim backed by code/disk evidence — no hypothesis stated as fact
      
      - **Content freshness**: outdated model names or tool/CLI names in config text — agent file names model that no longer exists, or uses deprecated CLI flag
      
      - **Example value vs. token cost**: for each inline example (code block or `## Example` section), judge whether it earns tokens — demonstrates non-obvious pattern or nuanced judgment call prose alone cannot convey? Flag examples that restate surrounding prose in code, illustrate obvious/trivial cases, or better served by project-local `AGENTS.md`. If project has own `AGENTS.md` or `CONTRIBUTING.md`, generic examples in agent files less justified. **Scope constraint**: report only findings within above checklist. No out-of-scope findings (e.g., "no error handling described," "missing inputs section for a skill") unless that specific check in list. Extra findings = noise — dilute precision, distract from confirmed issues.
      
    • fix-prompt.md 1.3 KB
      Fix issues in `<file path>`. Listed fixes only — no other changes.
      
      \<for each finding in this file, one bullet per fix>
      
      - [SEVERITY] <specific fix description> Fix: \<what to change, with context to locate it>
      
      Fix type reference:
      
      - Broken cross-reference "foo" → replace with correct name (verify exists on disk)
      - Inventory drift → update line to match disk state exactly
      - Hardcoded path → replace `$HOME/path` with `.claude/path` or `~/path`
      - Missing Confidence block → add `End your response with a ## Confidence block per CLAUDE.md output standards.` before closing `</workflow>` tag
      - Broken bash block → fix syntax per description (add missing opening fence, fix 4-backtick closer, unescape angle brackets)
      - Missing variable declaration → prepend `VAR="$(command)"` as first line of affected bash block
      - Stale cross-reference → replace `<old-name>` with `<correct-name>`
      - Duplicate section → remove listed lines verbatim
      
      No comments, docstrings, improvements beyond listed fixes.
      
      Fix Action Hierarchy — work through in order before acting:
      
      1. Reason: finding correct? If not, discard.
      2. Relocate: correct content, wrong place? Move.
      3. Consolidate: duplicates nearby? Merge.
      4. Minimize: too long but valid? Compress.
      5. Remove: only if none above apply.
      
    • report-template.md 3.7 KB
      Output complete audit summary. List each audited file by name in `### Files Audited` — from Step 2 inventory; counts alone insufficient.
      
      ```markdown
      ---
      Audit — .claude/ config
      Date:     [YYYY-MM-DD]
      Scope:    [N agents, N skills, N rules, N hooks]
      Focus:    [config quality audit — agents / skills / routing / all]
      Agents:   foundry:curator, foundry:challenger (adversarial mode only)
      Outcome:  CLEAN | NEEDS_ATTENTION | BLOCKED
      Findings: [N] security · [N] critical · [N] high · [N] medium · [N] low
      Confidence: [aggregate score from agent Confidence blocks]
      Next steps: /foundry:setup (sync clean config) | fix findings → re-run /foundry:audit
      Path:       → .reports/audit/<timestamp>/report.md
      ---
      
      ## Audit Complete — .claude/ config
      
      ### Files Audited
      - **Agents** (N): name-1, name-2, ...
      - **Skills** (N): name-1, name-2, ...
      - **Rules** (N): name-1, name-2, ...
      - **Hooks** (N): file-1.js, file-2.js, ...
      - **Settings**: settings.json
      - **Communication** (if in scope): communication.md, quality-gates.md, TEAM_PROTOCOL.md, file-handoff-protocol.md
      
      ### Unscanned Plugins
      
      > Omit section entirely when the `unscanned-plugins` sentinel is empty (`--local`/`plugins` scope only — see SKILL.md Step 2 coverage reconciliation).
      
      ⚠ UNSCANNED: codemap-py — no files matched scope globs
      
      **A non-empty section here means the sweep above is incomplete for that plugin** — its findings count of zero is not "clean", it is "not checked". Re-run after confirming the plugin's `.claude-plugin/plugin.json` `skills`/`agents` fields resolve correctly.
      
      ### Security Findings
      
      > Omit section entirely when no security findings present.
      
      | Severity | ID | File | Finding | Fix |
      |---|---|---|---|---|
      | `security/critical` | C37-1 | `hooks/foo.js` | Hardcoded API key on line 12 | Remove; use env var `$MY_KEY` |
      | `security/high` | C35-1 | `skills/bar/SKILL.md` | Unquoted `$ARGUMENTS` in bash block | Route via parse_scan_args.py |
      
      **Required action before any `/foundry:setup` or merge**: resolve all security findings — they are not deferred to "fix all" queue.
      
      ### Findings
      | Severity | Found | Fixed | Remaining |
      |---|---|---|---|
      | security | N | N | 0 |
      | critical | N | N | 0 |
      | high | N | N | 0 |
      | medium | N | N | 0 |
      | low | N | N ("Fix all" only) | N |
      
      **Fix convergence**: Converged in N pass(es) — 0 fixable findings remain.
      ```
      
      Or if limit hit:
      
      ```markdown
      **Fix convergence**: ⚠ CONVERGENCE LIMIT reached (5 passes) — N fixable findings remain (see Remaining section).
      ```
      
      (Omit fix convergence line when user picked "skip" from gate — only shown when fix option chosen.)
      
      ```markdown
      ### Fixes Applied
      
      | File | Change |
      | --- | --- |
      | agents/foo.md | Replaced broken ref `old-agent` → `correct-agent` |
      
      ### Remaining (low/nits — auto-fixed only with 'fix all'; otherwise manual review optional)
      
      - [low findings that were not auto-fixed]
      - [any infinite loops flagged for user decision]
      ```
      
      ### Code Block Similarity
      
      Include when `$RUN_DIR/similarity-check33.md` exists (`--efficiency` mode only). Read file and embed both tables verbatim — do NOT summarize. Label:
      
      ```markdown
      #### Purpose-based similarity clusters — Check 33 / --efficiency only
      <Table 1 from similarity-check33.md verbatim>
      <Table 2 from similarity-check33.md verbatim>
      ```
      
      Omit section if `similarity-check33.md` absent (efficiency not active or no clusters found).
      
      ```markdown
      ### Agent Confidence
      
      | File | Score | Label | Gaps |
      | --- | --- | --- | --- |
      | agents/foo.md | 0.92 | high | — |
      | skills/bar/SKILL.md | 0.64 | ⚠ low | no runtime data for bash validation |
      
      Low-confidence files re-audited: N | Still uncertain after retry: N (see gaps above)
      
      ### Next Step
      
      Run `/foundry:setup` to propagate clean config to ~/.claude/
      
      ```
      
  • severity-table.md 3.7 KB
    Severity order: **security** → **critical** → **high** → **medium** → **low** → **nit**
    
    `Weight` is the score each finding contributes in the Adversarial Convergence Loop (`quality-gates.md`), which decides when a review → fix cycle has converged, plateaued, or gone backwards.
    
    | Severity | Weight | Examples |
    | -- | -- | -- |
    | **security** | 20 | Shell injection where argument text reaches an interpreter as source — `eval "cmd $ARGUMENTS"`, `bash -c "… $ARGUMENTS …"`, `python -c "… $ARGUMENTS …"`, unquoted `$ARGUMENTS` in heredoc expansion position — hardcoded secrets (API keys, tokens, passwords) in config files, eval-unsafe bin/ output (unquoted shell assignments), `subprocess` with `shell=True` or string-concatenated command args, path traversal via unvalidated argv, `eval` with external input in shell scripts, unquoted variable expansion in command position, `pickle.load`/`yaml.load` without safe loader on external data |
    | **critical** | 10 | Broken cross-reference (agent/skill not exist on disk), MEMORY.md inventory wrong, relative path silently fall back to wrong dir |
    | **high** | 6 | Argument text handled only by a shell construct — a `[[ =~ ]]` guard, a `case`, or a substring comparison — with no `bin/` parser behind it: no interpreter runs the text, but the guard is unreliable (zsh sets `match`, not `BASH_REMATCH`) and a quote or newline in the blob breaks the block at parse time, skipping every step below it. Dead loop in follow-up chain, missing settings.json permission for tool in use, broken code example (undefined variable, wrong command syntax), agent/skill instruction directly contradicts `.claude/CLAUDE.md` directive, deprecated/invalid hook event name or type in use, tool declared in `tools:`/`allowed-tools:` needed but absent causing silent failures, `deep-reasoning` or `plan-gated` agent declared on `sonnet` (underpowered for tier) |
    | **medium** | 4 | Duplication across files, stale model name, README row missing for existing skill, hardcoded `/Users/<name>/` path, undocumented modes in inputs, deprecated frontmatter field or settings key, permissions-guide.md missing row for allow entry or has orphaned row, declared tool not referenced anywhere in workflow (unnecessary permission surface), `focused-execution` agent declared on `opus`/`opusplan` (overkill for tier) |
    | **low** | 2 | Verbosity, minor formatting, incomplete follow-up chain, outdated version pin with "autoupdate" note, agent/skill omits CLAUDE.md principle but no contradiction, 💡 new CC feature not yet used, inline example restates prose or superseded by `AGENTS.md`/`CONTRIBUTING.md` |
    | **nit** | 1 | Wording preference, a synonym swap, a comment that reads slightly better another way — anything whose fix changes no behaviour and no reader's decision |
    
    ## Antipatterns (severity under-classification — common calibration failures)
    
    - Any injection vector, secret leak, or eval-with-external-input → always **security**, not **critical**
    
    - `subprocess(shell=True)` with any external input source → always **security**, not **high**
    
    - Argument text guarded only by `[[ =~ ]]`, `case`, or a substring test → **high**, not **security** and not **medium** — no interpreter receives the text, so the injection tier does not apply, but the guard is unreliable and its block breaks on a quoted blob
    
    - Hardcoded credentials (even "test" or "example" tokens with real format) → always **security**
    
    - MEMORY.md inventory drift → always **critical**, not "out of sync" (medium) — stale roster fails at runtime
    
    - `deep-reasoning` agent on `sonnet` → **high**, not "possibly underpowered" (medium) — tier table is authority
    
    - Direct CLAUDE.md contradiction → **high**, not "best practice concern" (medium) — governance hierarchy applies
    
  • SKILL.md 47.7 KB
    ---
    name: audit
    description: Full-sweep quality audit of .claude/ config — cross-references, permissions, inventory drift, model tiers, docs freshness. Scope tokens select what to audit; --upgrade applies docs-sourced improvements; --adversarial runs foundry:challenger + Codex adversarial review; --efficiency sweeps model tiers, token bloat, spawn patterns, boilerplate duplication, and bin/ extraction candidates (extraction performed separately via /distill executables). Fix level chosen via always-fire follow-up gate after report.
    argument-hint: '[<scope>...] [--local] [--upgrade | --adversarial | --efficiency] [--skip-gate] [--keep "<items>"]'
    disable-model-invocation: true
    allowed-tools: Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, Skill, TaskCreate, TaskUpdate, TaskList, AskUserQuestion
    effort: high
    ---
    
    <objective>
    
    Full-sweep audit of `.claude/` config + all `plugins/*/` files: agents, skills, rules, settings.json, hooks. Spawns `foundry:curator` per-file, aggregates system-wide for cross-file issues — infinite loops, inventory drift, missing permissions, interop breaks. Reports findings; fix level from follow-up gate.
    
    </objective>
    
    <inputs>
    
    - **$ARGUMENTS**: optional — parse `--flags` first, then resolve remaining tokens as scope
    
      **Flags** (order independent, any combination with scope):
    
      - `--local` — audit source tree (`plugins/*/`) not user setup (`.claude/` + installed cache); plugin-dev workflows where local edits not yet installed; sets `LOCAL_MODE=true`
      - `--upgrade` — fetch latest Claude Code docs, filter new features by genuine value, apply: **config** changes (apply + correctness check), **capability** changes (calibrate before → apply → calibrate after → accept if Δrecall ≥ 0 and ΔF1 ≥ 0). Skip to **Mode: upgrade**. Mutually exclusive with `--adversarial` and `--efficiency` — error if combined with either.
      - `--adversarial` (alias: `--challenge`) — adversarial review of all agents + skills in scope using `foundry:challenger` (Phase A) + Codex adversarial pass (Phase B); surfaces issues beyond standard per-file audit; see **Mode: adversarial**. Mutually exclusive with `--upgrade` only; combinable with `--efficiency`.
      - `--efficiency` — cost and efficiency sweep: model tier validation, token bloat detection, unbounded spawn patterns, cross-file boilerplate duplication, missing model declarations, bin/ extraction candidates (Check 33). Generates prioritized cost-reduction plan with estimated savings. Detection only — run `/distill executables` to act on extraction candidates. Skip to **Mode: efficiency**. Mutually exclusive with `--upgrade` only; combinable with `--adversarial`.
      - `--skip-gate` — suppress follow-up gate (for automation pipelines)
      - `--fast` — widen fan-out from `MAX_BATCHES` toward that phase's model-tier ceiling (`CAP_OPUS`/`CAP_SONNET`, see `<constants>` and claude-config.md §Parallel Spawn Ceilings), trading tokens for wall-clock. **Not free**: each extra agent costs ~120,851 tok of fixed overhead regardless of how little work it does. Curator-spawning phases (opus tier) have little headroom above `MAX_BATCHES=4` toward `CAP_OPUS=5`; Phase D's qa-specialist (sonnet tier) has more, toward `CAP_SONNET=8`. Use when latency matters more than cost; omit by default. Combinable with every other flag.
    
      **Legacy positional tokens** (`fix`, `upgrade`, `adversarial`, `challenge`, `ab`, `apply`, `fast`, `full`) — **hard error**: print migration hint and stop. Example: "`fix medium` removed — run `/audit` and pick fix level from gate, or pass `--upgrade` / `--adversarial` as flags."
    
      **Scope tokens** (positional, space-separated — resolve each token before Step 2):
    
      - No scope: full sweep — sources per `--local`: **without `--local`** covers `.claude/agents/`, `.claude/skills/`, `.claude/rules/`, hooks, settings, `~/.claude/plugins/cache/` installed; **with `--local`** covers `plugins/*/agents/`, `plugins/*/skills/` + `.claude/` secondary
      - `agents` — restrict sweep to agent files only
      - `skills` — restrict sweep to skill files only
      - `rules` — restrict sweep to rule files only
      - `communication` — restrict sweep to communication governance files: `rules/communication.md`, `rules/quality-gates.md`, `TEAM_PROTOCOL.md`, `skills/_shared/file-handoff-protocol.md`
      - `setup` — restrict to system-config files: `settings.json`, `permissions-guide.md`, hooks, `MEMORY.md`, `README.md`, plugin integration, post-install user state (Checks 1–11, 30, I1, I2, I3); Step 3: `setup` SKILL.md only (one foundry:curator spawn); Checks I1–I3 read `~/.claude/` not `.claude/`
      - `plugin` — plugin integration only: codex plugin (Check 7), foundry plugin + init validation (Check 8, including 8g); Step 3: `setup` SKILL.md only (one foundry:curator spawn)
      - `plugins` — full audit of all plugins: per-file audit of every `plugins/*/agents/*.md` and `plugins/*/skills/*/SKILL.md` + integration checks (7, 8) per plugin
      - `plugins <name>` — same as `plugins` scoped to one plugin: `plugins/<name>/agents/*.md` + `plugins/<name>/skills/*/SKILL.md` + integration checks; `<name>` must match dir under `plugins/` (e.g. `plugins foundry`, `plugins oss`, `plugins research`)
      - `<plugin-name>` — **tier 2 shorthand**: bare plugin dir name (e.g. `oss`, `foundry`, `research`, `develop`, `codemap`) auto-resolved when token matches dir under `plugins/`; equivalent to `plugins <name>`; no `plugins` prefix needed
      - `<agent-name>` — **tier 3**: name matches `plugins/*/agents/<name>.md` or `.claude/agents/<name>.md`; runs agent checks only (Checks 14a, 14b, 15, 19, 20, 17, 12, 13, 25, 22, 26, 29); one file in Step 3
      - `<skill-name>` — **tier 3**: name matches `plugins/*/skills/<name>/SKILL.md` or `.claude/skills/<name>/SKILL.md`; runs skill checks only (Checks 14a, 14b, 15, 17, 12, 23, 22, 13, 24, 25, 26, 27, 28, 29); one file in Step 3
      - Multiple scope tokens — space-separated, any combo; scope = union of resolved file sets: `agents skills`, `oss research`, `shepherd curator`, `review resolve`; check list = union (de-duplicated)
    
      **Scope token resolution** (each remaining token after flag-strip, resolved before Step 2): (1) reserved keywords (`agents`, `skills`, `rules`, `communication`, `setup`, `plugin`, `plugins`) → use as-is; (2) matches dir under `plugins/<token>/` → tier 2; (3) matches agent file in `plugins/*/agents/<token>.md` or `.claude/agents/<token>.md` → tier 3 agent; (4) matches skill dir `plugins/*/skills/<token>/` or `.claude/skills/<token>/` → tier 3 skill; (5) no match → error and stop
    
      **Valid combinations**: scope tokens + flags mix freely: `foundry --local`, `foundry --adversarial`, `agents skills --local`, `oss research --adversarial`, `foundry --efficiency`, `plugins --efficiency`, `foundry --adversarial --efficiency`, `plugins --local --adversarial --efficiency`. `--upgrade` mutually exclusive with `--adversarial` and `--efficiency` — error if combined with either. `--local` compatible with all. When `--adversarial` and `--efficiency` both present: run adversarial Phases A–C then efficiency Phases A–C sequentially; merge findings; single follow-up gate.
    
    </inputs>
    
    <constants>
    
    ```text
    BATCH_SIZE_MIN=5       # minimum files per batch; ensures curator gets sufficient context per spawn
    MAX_BATCHES=4          # total batch cap; EFFECTIVE_BATCH = max(BATCH_SIZE_MIN, ceil(total / MAX_BATCHES))
    ADVERSARIAL_BATCH_SIZE=2  # adversarial phases (A, A-prime) use smaller batches for deeper per-file attention
    AGENT_CALL_BUDGET=55   # target tool-calls per spawned agent; above ~60 agents stall mid-task without returning an envelope
    CAP_OPUS=5             # per claude-config.md §Parallel Spawn Ceilings — foundry:curator, foundry:challenger (opus/opusplan tier)
    CAP_SONNET=8           # per claude-config.md §Parallel Spawn Ceilings — foundry:qa-specialist (Phase D) (sonnet tier)
    WAVE_STEP=5            # growth toward a tier's ceiling happens this much at a time, never a sudden jump to the cap
    ```
    
    <!-- Fan-out buys WALL-CLOCK, not tokens: ~120K tok fixed cost per agent spawned;
         agents past ~60 calls stall without returning an envelope. Default to the
         fewest batches keeping each agent near AGENT_CALL_BUDGET; --fast only when
         latency outweighs cost, and only up to that phase's own model-tier ceiling
         (CAP_OPUS/CAP_SONNET) — --fast has no headroom on opus-tier phases already
         at or near CAP_OPUS. When adversarial/efficiency phases stack onto Steps
         3-4, each tier's spawns wave toward its own ceiling WAVE_STEP at a time;
         different tiers draw from separate pools and may run concurrently. -->
    
    </constants>
    
    <compaction>
    
    - Key boundary 1: after Steps 3+4 fan-out (curator spawns + system-wide checks complete), before Step 5 aggregate.
    - Key boundary 2: after Step 5 aggregate (aggregate.md + summary.jsonl written), before Step 7 report.
    - Preserve at boundary 1: RUN_DIR, per-batch finding file paths, static-findings.jsonl path.
    - Preserve at boundary 2: RUN_DIR, aggregate.md path, summary.jsonl path, finding counts.
    
    </compaction>
    
    <workflow>
    
    **Task hygiene**: load and follow the protocol below.
    
    ```bash
    # loads: compaction-contract.md
    # audit-skip: resilience-replication
    python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/load_shared_doc.py" foundry skills/_shared task-hygiene.md  # timeout: 5000
    ```
    
    **Orchestration contract**: orchestrator is thin coordinator — issues Glob/Grep for inventory, spawns agents, reads JSON envelopes, aggregates findings. Must NOT read agent/skill/rule file bodies directly. Inline read of non-template file = protocol violation; causes context overflow at scale.
    
    **Task tracking**: TaskCreate for each major phase; mark status live:
    
    - Phase 1: setup + collect (Pre-flight + Steps 1–2) → in_progress on start, completed when file list ready
    - Phase 2: per-file audit (Step 3) → in_progress on agent launch, completed when all reports received
    - Phase 3: system-wide checks (Step 4) → in_progress on start, completed when all checks done
    - **Phases 2 and 3 launch simultaneously** — mark both in_progress same update; independent, must not serialize
    - Phase 4: aggregate + fix (Steps 5–10) → in_progress, completed when fixes land; **do NOT mark completed until EITHER: (a) follow-up gate fires (Step 7) AND fixes applied or user chose skip; OR (b) `--skip-gate` active — gate suppressed, complete after Step 5 aggregation; Step 5 aggregation alone does NOT complete Phase 4 in normal mode**
    - Phase 5: write final report (Step 11a) → in_progress, completed only once `$RUN_DIR/report.md` exists on disk — assembling the report in context does not complete this phase
    - Phase 6: print report header (Step 11b) → `blockedBy` Phase 5; mark completed immediately BEFORE emitting the header block (task-lifecycle.md §TaskUpdate before long output), then print
    - On loop retry or scope change → new task; do not reuse completed task
    
    Surface progress at milestones: after system-wide checks ("✓ Checks 1-20 complete, N findings so far — spawning per-file audits"), after agent reports ("Agent reports received — N medium, N low findings"), before each fix batch ("Fixing N medium findings in parallel").
    
    ## Pre-flight checks
    
    **Context budget**: full audit (12+ agents, 14+ skills, 12 system checks) runs close to context limits. File-based handoff mandatory — every sub-agent writes full output to file, returns only compact JSON envelope. Sub-agent echoing findings to context = compaction before audit done.
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/audit_preflight.py" --arguments "$ARGUMENTS"  # timeout: 20000
    ```
    
    If `.claude/` missing, abort immediately. Missing `jq` is warning — audit continues with Check 4 skipped.
    
    **State re-derivation across Bash blocks** — Claude Code spawns a fresh shell per Bash() call; variables set in pre-flight are LOST in Steps 2–11. Every Bash block in subsequent steps that uses `LOCAL_MODE` or `AUDIT_TPL` must re-read them from the persisted state files:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    ```
    
    Place these three lines at the top of every Bash block in Steps 2–11 that references either variable.
    
    **Unsupported flag check** — after extracting supported flags (`--local`, `--upgrade`, `--adversarial`, `--efficiency`, `--skip-gate`, `--keep`), scan `$ARGUMENTS` for remaining `--<token>` tokens. Found → print `` ! Unknown flag(s): `--<token>`. Supported: `--local`, `--upgrade`, `--adversarial`, `--efficiency`, `--skip-gate`, `--keep`. `` then invoke `AskUserQuestion` — (a) **Abort** (stop, re-invoke with correct flags) · (b) **Continue ignoring** (skip unknown flags, proceed). On Abort: stop.
    
    ## Step 1: Run pre-commit (if configured)
    
    ```bash
    preflight_ok()   { local f=".temp/state/preflight/$1.ok"; [ -f "$f" ] && [ $(($(date +%s) - $(cat "$f"))) -lt 14400 ]; }
    preflight_pass() { mkdir -p .temp/state/preflight; date +%s >".temp/state/preflight/$1.ok"; }
    
    if (preflight_ok pre-commit || { command -v pre-commit &>/dev/null && preflight_pass pre-commit; }) &&
    [ -f .pre-commit-config.yaml ]; then
        timeout 600 pre-commit run --all-files # timeout: 600000
    fi
    ```
    
    Files auto-corrected by pre-commit hooks are clean before structural audit. Note modified files — include in audit scope even if not originally targeted.
    
    If pre-commit not configured, skip silently.
    
    ## Step 1b: Layer-1 deterministic static pass
    
    Run the zero-LLM checker driver — the same deterministic checkers pre-commit enforces, aggregated into one reproducible findings file. These results are **authoritative** for their check classes: Steps 3–4 (LLM curator + judgment checks) must NOT re-derive them in prose — treat them as already-verified and spend model tokens only on judgment.
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    STATIC_SCOPE=$( [ "$LOCAL_MODE" = true ] && echo plugins || echo .claude )
    python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/audit_static.py" --scan-dir "$STATIC_SCOPE" \
        --jsonl "${TMPDIR:-/tmp}/audit-state-${CSID}/static-findings.jsonl"  # timeout: 120000
    ```
    
    Covered deterministically by the driver (map to legacy check IDs — do NOT re-run these as prose): **14a** tag symmetry · **14b** fence symmetry · **14c** README drift · **14d** mode-dispatch integrity · **14e** cross-plugin shared-file drift · **43** bash-variable persistence · **42** spawn-prompt `$VAR` (checks-skills.md) · **32d** orphaned bin/ scripts · **cli-flag-drift** SKILL.md flags vs argparse (`check_cli_flag_drift.py`, checks-index 42) · **R3** bin/computed-path reference integrity. Whole-repo checks (orphaned-bin, routing-links, shared-drift) always scope to `plugins/` regardless of `STATIC_SCOPE`; driver most complete in `--local` mode. Step 5 merges `static-findings.jsonl` into aggregate.
    
    > **Layer-1 recall is benchmarked** — `tests/test_audit_static.py` plants a known defect per scope-aware class and asserts the driver catches every one (100% mechanical recall), so this pass is trusted, not assumed.
    
    ## Step 1c: Layer-3 recurrence signal (attention weighting)
    
    Read a compact git-churn signal so audit attention follows *measured* churn — files and change-classes that keep being re-fixed are where the next defect most likely hides. Report the dominant recurring-fix class, not only point findings.
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/audit_churn.py" --limit 300 --path plugins \
        > "${TMPDIR:-/tmp}/audit-state-${CSID}/churn-signal.json" 2>/dev/null  # timeout: 20000
    ```
    
    Use `churn-signal.json` (`commit_types`, `top_churn`, `recurring_hint`) to: (1) prioritize per-file audits toward the most-churned files first in Step 3 batching; (2) add a **Recurring fix class** line to the Step 7 report naming the dominant theme (e.g. "version-bump churn — candidate for automation"). Zero-LLM, best-effort; skip silently if git is unavailable.
    
    ## Step 2: Collect all config files
    
    Enumerate everything in scope with built-in tools. Run all Glob calls in parallel.
    
    **Plugin layout resolution** (`--local`/`plugins` scope — multi-plugin source tree, mandatory before the fixed-pattern Globs below): don't assume every plugin uses `skills/`+`agents/` — a plugin can override the convention in its own manifest (e.g. `plugins/codemap-py/.claude-plugin/plugin.json` declares `"skills": "./claude-skills/"`, not `./skills/`; a fixed `*/skills/*/SKILL.md` glob silently never matches). Resolve each plugin's real dir names first:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    if [ "$LOCAL_MODE" = "true" ]; then
      for pj in plugins/*/.claude-plugin/plugin.json plugins/*/.codex-plugin/plugin.json; do
        [ -f "$pj" ] || continue
        plugin_name=$(basename "$(dirname "$(dirname "$pj")")")
        python3 -c "
    import json
    d = json.load(open('$pj'))
    skills = (d.get('skills') or './skills/').strip('./')
    agents = (d.get('agents') or './agents/').strip('./')
    print(f'PLUGIN_LAYOUT: $plugin_name | skills={skills} | agents={agents}')
    " 2>/dev/null || echo "PLUGIN_LAYOUT: $plugin_name | skills=skills | agents=agents"
      done
    fi
    ```
    
    For every `PLUGIN_LAYOUT:` line, use its `skills=`/`agents=` value (not hardcoded `skills`/`agents`) when Globbing that plugin below — e.g. `codemap-py` prints `skills=claude-skills`, so Glob `claude-skills/*/SKILL.md` under `plugins/codemap-py/`, not `skills/*/SKILL.md`. Plugins with no manifest override resolve to the same `skills`/`agents` names the fixed patterns already assume — no behavior change. **`codex-skills/`-style dirs for non-Claude-Code runtimes intentionally excluded** — only the manifest-declared path is authoritative; a heuristic `*skill*` match would also sweep in skill dirs for a different agent runtime, producing false positives against Claude Code's frontmatter schema.
    
    **Source selection by `LOCAL_MODE`**:
    
    - **`LOCAL_MODE=false` (default — user setup)**: `.claude/` primary; `plugins/` skipped. Installed/active config only.
    - **`LOCAL_MODE=true` (--local — project source)**: `plugins/` primary; `.claude/` secondary for rules/hooks/settings only.
    
    **Without `--local` (`LOCAL_MODE=false`)**:
    
    - **Agents**: Glob tool, pattern `agents/*.md`, path `.claude/`
    - **Skills**: Glob tool, pattern `skills/*/SKILL.md`, path `.claude/`
    - **Rules**: Glob tool, pattern `rules/*.md`, path `.claude/`
    - **Communication**: Read tool on `rules/communication.md`, `rules/quality-gates.md`, `TEAM_PROTOCOL.md`, `skills/_shared/file-handoff-protocol.md`
    - **Settings**: Read tool on `.claude/settings.json`
    - **Hooks**: Glob tool, pattern `hooks/*`, path `.claude/`
    
    **With `--local` (`LOCAL_MODE=true`)**:
    
    - **Agents (source — primary)**: for each `PLUGIN_LAYOUT:` line above, Glob tool pattern `<agents-dir>/*.md`, path `plugins/<plugin_name>/`
    - **Skills (source — primary)**: for each `PLUGIN_LAYOUT:` line above, Glob tool pattern `<skills-dir>/*/SKILL.md`, path `plugins/<plugin_name>/`
    - **Agents (project-local — secondary)**: Glob tool, pattern `agents/*.md`, path `.claude/`
    - **Skills (project-local — secondary)**: Glob tool, pattern `skills/*/SKILL.md`, path `.claude/`
    - **Rules / Settings / Hooks**: same as without `--local` (`.claude/`)
    
    Merge into single flat inventory. When `LOCAL_MODE=true` and same logical name in both `plugins/` and `.claude/`, prefer plugin source — skip `.claude/` duplicate. Record full paths — Step 3 cross-reference checks depend on current inventory. If MEMORY.md not updated since last agent/skill added/removed, run live disk scan, not cached roster. Stale inventory = primary cause of false-negative cross-reference findings.
    
    **Coverage reconciliation** (bare `plugins` scope, or default full sweep with `--local` — mandatory, not optional; **NOT** `plugins <name>` / tier-2 single-plugin scope, see narrowed check below): a plugin contributing zero files to the inventory is invisible to every downstream check and must never pass as a silent clean sweep — same failure shape as a check that always no-ops: absence of findings misread as absence of problems. Base set for this comparison is **every directory under `plugins/`**, not just ones that produced a `PLUGIN_LAYOUT:` line above — a plugin with a missing or malformed `plugin.json` contributes no `PLUGIN_LAYOUT:` line either, so comparing against that subset would make it invisible again:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    mkdir -p "${TMPDIR:-/tmp}/audit-state-${CSID}"
    find plugins -mindepth 1 -maxdepth 1 -type d ! -name ".*" 2>/dev/null | sed 's|plugins/||' | sort > "${TMPDIR:-/tmp}/audit-state-${CSID}/all-plugins"
    ```
    
    After inventory built (model-context comparison — inventory already in context, not re-read from disk): compare every name in `all-plugins` against plugin names actually present in inventory. For each plugin with zero inventory entries, print and persist in the **same** bash call (re-export `CSID` here too — a later call starts a fresh shell and an unexported `$CSID` silently writes to the wrong sentinel path):
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    : > "${TMPDIR:-/tmp}/audit-state-${CSID}/unscanned-plugins"
    # for each zero-contribution plugin found above:
    printf "⚠ UNSCANNED: %s — no files matched scope globs\n" "<plugin>" | tee -a "${TMPDIR:-/tmp}/audit-state-${CSID}/unscanned-plugins"
    ```
    
    Step 11 reads this sentinel and surfaces it as a top-level report section — a zero-coverage plugin must never produce a green summary.
    
    **Narrowed check for `plugins <name>` / tier-2 single-plugin scope**: the all-plugins comparison above does NOT apply — every other plugin under `plugins/` is out of scope by design, and reporting it `UNSCANNED` would be a false positive on every such run. Instead, verify only that the one requested plugin contributed at least one file to the inventory; if it contributed zero, print/persist the same `⚠ UNSCANNED: <name> — no files matched scope globs` line for that single plugin only.
    
    **Scope filtering for Step 2** (applies on top of `LOCAL_MODE`):
    
    - `agents` scope — collect agents from active source (`.claude/agents/` or `plugins/*/agents/` per `LOCAL_MODE`); skip skills, rules, hooks
    - `skills` scope — collect skills from active source; skip agents, rules, hooks
    - `plugins` scope — always reads `plugins/*/agents/*.md` + `plugins/*/skills/*/SKILL.md` regardless of `LOCAL_MODE`; forces `LOCAL_MODE=true`
    - `plugins <name>` or `<plugin-name>` (tier 2) scope — collect `plugins/<name>/agents/*.md` + `plugins/<name>/skills/*/SKILL.md` only; forces `LOCAL_MODE=true`; also force `LOCAL_MODE=true` when any scope token matches `plugins` keyword or matches a `plugins/<name>/` directory even without explicit `--local` flag
    - `<agent-name>` (tier 3) scope — single matching agent file; `LOCAL_MODE=false`: `.claude/agents/<name>.md`; `LOCAL_MODE=true`: `plugins/*/agents/<name>.md` first, then `.claude/agents/<name>.md`
    - `<skill-name>` (tier 3) scope — single matching skill file; same `LOCAL_MODE` resolution as agent above
    - Multiple scope tokens — union of all resolved file sets
    - `setup`/`plugin` (bare) scope — no agent/skill collection from plugins; see setup/plugin notes below
    - Full sweep (no scope) — collect per `LOCAL_MODE` source selection above
    
    **Setup scope**: when `$SCOPE` is `setup`, also collect `${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/skills/setup/SKILL.md` for Step 3 foundry:curator spawn — only per-file spawn in setup scope. Checks I1–I3 (from `checks-install.md`) run in Step 4 against `~/.claude/` to validate post-install user state.
    
    **`plugins <name>` scope**: verify `plugins/<name>/` exists — abort `! BREAKING: plugins/<name>/ not found` if absent. Collect `plugins/<name>/skills/setup/SKILL.md` for Step 3 plus all agents/skills in that plugin. **`plugins` (no name)**: iterate every subdir under `plugins/` — do not filter by literal `agents/`/`skills/` dir presence; PLUGIN_LAYOUT resolution above already covers plugins declaring a non-standard skills/agents path via their own manifest.
    
    ## Step 3: Per-file audit via foundry:curator
    
    **Context management** — 12+ agents and 14+ skills: accumulating full foundry:curator responses in context causes overflow before aggregation. Use file-based findings to keep main context lean.
    
    **Hard rule — no pre-reading**: Never call Read on agent/skill file before spawning foundry:curator. Spawned agent does the reading. Orchestrator reads only returned JSON envelope. Pre-reading 41 KB files into main context = defeats delegation + causes context overflow at scale.
    
    **Batching rule**: Always apply the grouping algorithm. Compute `EFFECTIVE_BATCH = max(BATCH_SIZE_MIN, ceil(total_files / MAX_BATCHES))` before grouping — caps total batches at `MAX_BATCHES` while guaranteeing `BATCH_SIZE_MIN` files per batch for adequate curator context. Group files into batches up to `EFFECTIVE_BATCH`. Never spawn one agent per file. Total files ≤ `EFFECTIVE_BATCH` → one batch, all files. `--fast` passed: recompute with `min(CAP_OPUS, <desired wider batch count>)` in place of `MAX_BATCHES` — Step 3 spawns `foundry:curator` (opus tier), so this phase can never exceed `CAP_OPUS`.
    
    **Spawn-count gate — apply before spawning anything**: every agent costs ~120,851 tok just to exist, ~73 tool-calls' worth of work (see `<constants>`). **Spawn fewest agents keeping each near `AGENT_CALL_BUDGET`**, not the most the cap allows. Two mandatory consequences:
    
    - **Gate on Layer-1 signal.** Step 1b already ran ~10 deterministic checkers at zero LLM cost, authoritative for their classes. A file with no Layer-1 finding and no judgment-bearing content (no `Agent()` dispatch, no cross-file contract, no model/tool declaration) does **not** need a curator — audit by static pass alone, say so in report. Spawning a curator to re-confirm a clean mechanical result is this skill's most common waste.
    - **State the trade in the report.** Record agents spawned + reason for that count. `--fast` widened fan-out → say it bought wall-clock at ~120K tok per extra agent — never present the fast path as free.
    
    **Budget line — include verbatim in every spawn prompt**: `If you approach your budget, stop cleanly and return the envelope with "partial": true and an accurate account of what you finished.` Measured: agents at ≤57 calls returned a clean envelope 5/5; agents at 87–142 calls stalled mid-task 3/5 and returned a progress fragment instead, forcing the orchestrator to reconstruct their state from disk.
    
    **Grouping algorithm**: (1) sort by plugin origin (`plugins/<name>/` prefix); (2) assign each plugin's files to batches, fill to `EFFECTIVE_BATCH` before next — keeps same-plugin files together; (3) remaining files (`.claude/` and mixed) fill open slots. Plugin-first, not strictly ordered — unconnected files assigned randomly to reach `EFFECTIVE_BATCH`.
    
    **Layer-2 — judgment by domain, not by file (plugin scope)**: scope `plugins`, `plugins <name>`, or tier-2 plugin name → override batch cap, group **all of a plugin's files into ONE holistic batch** (one `foundry:curator` per plugin), even past `EFFECTIVE_BATCH`. Whole-plugin context lets the curator catch cross-file breaks per-file batching misses — tool-grant mismatches (agent frontmatter vs skill dispatch), inter-skill contract splits (a constant clamped differently in two files), dead dispatch paths, version/description drift. Curator prompt for a holistic batch must say: "You have this plugin's ENTIRE file set — review it as one system: check every `Agent(subagent_type=...)` dispatch targets an agent whose frontmatter grants the needed tools, shared constants/contracts agree across files, no skill references a removed mode/file." Mechanical checks already done in Step 1b — spend this holistic pass on cross-file judgment only. (Very large plugins may still split, but keep agents + their dispatching skills in the same batch. This override deliberately accepts stall risk above the `AGENT_CALL_BUDGET` guidance in the Spawn-count gate below — the mitigation is the per-spawn budget line already required there ("stop cleanly, return `partial: true`"), not a hard file-count cap. A holistic batch that returns `partial: true` gets a second, narrower spawn covering only the files it didn't finish — never a full retry of the whole plugin.)
    
    For workflow-bearing targets, that holistic pass also traces the entrypoint through state changes, unchanged consumers, next ordinary user action. Record each producer's guaranteed postcondition against the next consumer's required precondition. Ask whether every local check can pass while the intended user outcome fails; distinguish value equality from identity, ownership, authority, destination, lifetime. Question accepted design choices and tests that merely assert the chosen operation. Use a source-backed counterexample or request a bounded parent-owned probe for the highest-impact unproven handoff, including supported resume/retry states and a valid positive case. Keep unexecuted probes and missing consumer context explicit — zero findings on inspected files doesn't establish end-to-end safety. Stay within the audit's existing scope and permissions.
    
    **Scope-restricted runs**: fewer than `EFFECTIVE_BATCH` files → one batch, ALL files in scope (single foundry:curator spawn). Read only relevant template file(s) for active scope, not all 4.
    
    Set up the run directory once before spawning any agents:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    
    RUN_DIR=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/make_run_dir.py" .reports/audit)  # timeout: 5000
    [ -z "$RUN_DIR" ] && { printf "! BREAKING: make_run_dir.py returned empty path — check Python availability and write permission on .reports/\n"; exit 1; }
    echo "Run dir: $RUN_DIR"
    echo "$RUN_DIR" > "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir"
    cat "$AUDIT_TPL/curator-prompt.md"
    ```
    
    Spawn **foundry:curator** agents in batches of up to `EFFECTIVE_BATCH` (grouping algorithm above) — or one batch if scope ≤ `EFFECTIVE_BATCH`. Each spawn prompt must:
    
    1. Include the curator-prompt.md content loaded above
    2. Include the disk inventory from Step 2 (agent/skill list for cross-reference validation)
    3. End with:
    
    > "Write your FULL findings (all severity levels) to `<RUN_DIR>/<file-slug>.md` using the Write tool — where `<file-slug>` is a unique identifier combining plugin prefix and filename (e.g. `foundry-shepherd.md`, `oss-analyse-SKILL.md`, `develop-fix-SKILL.md`) to avoid collisions between cross-plugin files sharing the same basename. End your full findings file with a `## Confidence` block per quality-gates.md format (Score, Gaps, Refinements). Then return to the caller ONLY a compact JSON envelope on your final line — nothing else after it: `{\"status\":\"done\",\"file\":\"<RUN_DIR>/<file-slug>.md\",\"findings\":N,\"severity\":{\"security\":N,\"critical\":N,\"high\":N,\"medium\":N,\"low\":N},\"confidence\":0.N,\"summary\":\"<filename>: N critical, N high, N medium, N low\"}`"
    
    Replace `<RUN_DIR>` with actual path, `<file-slug>` with plugin-prefixed unique slug (e.g. `foundry-shepherd`, `oss-analyse-SKILL`, `develop-fix-SKILL`). Slug chars: `[a-zA-Z0-9-]` only — no slashes, spaces, or dots. Plugin dir names carry a `cc_` prefix on disk (`plugins/cc_foundry/`, `plugins/cc_oss/`) — strip it for the slug's plugin segment, matching the examples above (`foundry-shepherd`, never `cc_foundry-shepherd`).
    
    **Critical context discipline**: response body = JSON envelope, final line only. No other text, summaries, findings. All content to file.
    
    > Template file = canonical per-file audit criteria. Disk inventory and RUN_DIR path = runtime values injected per spawn.
    
    After spawns complete: short summaries in context; use to identify files with findings. Full content in run-dir files.
    
    **Health monitoring** (CLAUDE.md §6): apply the honest protocol in `$_FS/agent-spawn-protocol.md` — these curator batches return on completion; after each returns, read its `$RUN_DIR` output file. On empty/missing output: mark `timed_out`, surface with ⏱ in final report. Never omit timed-out agents.
    
    ## Steps 4–5b: System-wide checks, aggregate, low-confidence remediation
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_MODES=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit modes $( [ "$LOCAL_MODE" = true ] && echo "--local" )) || { printf "! BREAKING: audit/modes not found — run /foundry:setup first\n"; exit 1; }  # timeout: 5000
    cat "$AUDIT_MODES/steps-4-5-7.md"
    ```
    
    > loads: modes/steps-4-5-7.md (§Step 4–5b and §Step 7)
    
    Execute §Step 4–5b loaded above — system-wide checks (scope-dispatched), aggregate + classify findings, low-confidence remediation (conditional, skipped when no file scored \<0.80). State on disk in `summary.jsonl`, `$RUN_DIR`, `$AUDIT_TPL`. Returns here to Step 6.
    
    ## Step 6: Cross-validate critical findings
    
    ```bash
    _SHARED=$(python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_shared_path.py" foundry skills/_shared 2>/dev/null || echo "plugins/cc_foundry/skills/_shared")  # timeout: 5000
    SKIP_CROSS_VAL=false
    [ -f "$_SHARED/cross-validation-protocol.md" ] || { printf "⚠ WARNING: cross-validation-protocol.md not found at $_SHARED — skipping cross-validation\n"; SKIP_CROSS_VAL=true; }
    [ "$SKIP_CROSS_VAL" = false ] && cat "$_SHARED/cross-validation-protocol.md"
    ```
    
    If `$SKIP_CROSS_VAL` = false: follow the cross-validation protocol loaded above.
    
    **Skill-specific**: the verifier agent is always **foundry:curator**.
    
    ## Step 7: Report findings
    
    Execute §Step 7 of `steps-4-5-7.md` (loaded above in Steps 4–5b) — emits report, fires follow-up gate; on fix pick continues to Steps 8–10 (`modes/fix.md`); otherwise skip to Step 11.
    
    ## Steps 8–10: Fix dispatch, codex cross-file check, re-audit (mode: fix)
    
    Runs **only** when the user picked a fix option (a–c) from the Step 7 follow-up gate. Resolve the modes dir, load `fix.md`, then execute it inline — it carries Step 8 (delegate fixes to subagents), Step 9 (codex cross-file check), and Step 10 (re-audit + convergence loop), then returns here to Step 11:
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/load_mode.py" audit modes fix.md $( [ "$LOCAL_MODE" = true ] && echo "--local" )  # timeout: 5000
    ```
    
    > loads: modes/fix.md Execute Steps 8–10 loaded above inline (state on disk in `summary.jsonl`, `$RUN_DIR`, `$AUDIT_TPL`). On convergence (clean or 5-pass limit), continue to Step 11 below. If no fix option was picked, skip directly to Step 11.
    
    ## Step 11: Final report
    
    <!-- loads: report-template.md -->
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    IFS= read -r RUN_DIR < "${TMPDIR:-/tmp}/audit-state-${CSID}/run-dir" 2>/dev/null || RUN_DIR=""
    cat "$AUDIT_TPL/report-template.md"
    ```
    
    **Step 11a — assemble and persist** — assemble the complete audit report following the template and instructions loaded above, then **Write it to `$RUN_DIR/report.md`** using the Write tool. Set the template's `Path:` field to that same real path. This Write is mandatory, not optional: the terminal step below reads the header back from this file, and the report is the run's only durable artifact once the session ends. Skipping it leaves `Path:` pointing at a file that was never created.
    
    **Unscanned-plugin surfacing** (`--local`/`plugins` scope only): before assembling, read `${TMPDIR:-/tmp}/audit-state-${CSID}/unscanned-plugins` (sentinel from Step 2 coverage reconciliation). Non-empty → include an `### Unscanned Plugins` section in the report immediately after `### Files Audited`, listing every `⚠ UNSCANNED: <plugin>` line verbatim; this makes zero-coverage plugins visible in the durable artifact, not just the transient terminal output. Empty or missing → omit the section.
    
    **Step 11b — terminal output** — per quality-gates.md universal rule: read the `---` header block from the top of `$RUN_DIR/report.md` (all fields from opening `---` up to and including closing `---`) and render as a two-column Markdown table (`Field | Value`, one row per key, file order) as the FIRST content of the reply — never print the raw `---`-delimited block. Then print `→ $RUN_DIR/report.md`. Then executive summary. Omit the `╔═╗` Re:Anchor box (communication.md exempts quality-gates `---` report headers — the table IS the reply header).
    
    **Completion marker** — on successful completion, write `$RUN_DIR/result.jsonl` with one JSONL line summarising the run (severity totals, scope, pass count). On any abort/error path before completion, leave `result.jsonl` absent — the TTL cleanup hook (artifact-lifecycle.md) intentionally skips run directories without `result.jsonl`, preserving incomplete runs for post-mortem debugging. To force cleanup of a known-bad incomplete run, write `{"status":"incomplete","reason":"<one-line>"}` to `result.jsonl` so TTL can age it out.
    
    ```bash
    rm -f .temp/state/skill-contract.md  # clear contract — skill complete (compaction-contract.md §Lifecycle)  # timeout: 5000
    ```
    
    ## Mode: upgrade
    
    **Trigger**: `/audit --upgrade`
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    cat "$AUDIT_TPL/../modes/upgrade.md"
    ```
    
    Execute the mode loaded above.
    
    ## Mode: adversarial (alias: --challenge)
    
    **Trigger**: `/audit [<scope>...] --adversarial`
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    cat "$AUDIT_TPL/../modes/adversarial.md"
    ```
    
    Execute the mode loaded above.
    
    ## Mode: efficiency
    
    **Trigger**: `/audit [<scope>...] --efficiency`
    
    ```bash
    export CSID="${CLAUDE_CODE_SESSION_ID:-$PPID}"
    IFS= read -r LOCAL_MODE < "${TMPDIR:-/tmp}/audit-state-${CSID}/local-mode" 2>/dev/null || LOCAL_MODE="false"
    AUDIT_TPL=$(cat "${TMPDIR:-/tmp}/audit-state-${CSID}/audit-tpl" 2>/dev/null || python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_foundry}/bin/resolve_skill_subdir.py" audit templates $( [ "$LOCAL_MODE" = true ] && echo "--local" ))
    cat "$AUDIT_TPL/../modes/efficiency.md"
    ```
    
    Execute the mode loaded above.
    
    ## Combined-run output isolation
    
    When `--adversarial` and `--efficiency` both run in the same invocation: each mode writes finding files to its own subdirectory to prevent overwrite — `$RUN_DIR/adversarial/` for adversarial output, `$RUN_DIR/efficiency/` for efficiency output. Step 5 consolidator reads both subdirs. Follow-up gate fires once after both complete, merged finding counts.
    
    ## Follow-up gate
    
    **Always fires** unless `--skip-gate` passed (programmatic callers). Call `AskUserQuestion` — do NOT write options as plain text first. Map options directly into tool-call arguments.
    
    When user picks fix option (a–c): run Steps 8–10 inline via `modes/fix.md` (state on disk in `summary.jsonl`); no recursive `/audit` call.
    
    **HARD RULE — Fixed option labels**: Use exactly the labels below verbatim. Do NOT rewrite, paraphrase, or substitute finding-specific alternatives. Finding context must NOT influence option labels. This rule has been violated repeatedly in past runs; enforce strictly.
    
    **Mandatory options (always present, never omit)**:
    
    - (a) label: `Fix auto-fixable (Recommended)` — auto-fix critical, high, medium, and low findings (skip NON_AUTO_FIXABLE systemic issues)
    - (c) label: `Fix ALL` — collect all NON_AUTO_FIXABLE decisions upfront (max 4 `AskUserQuestion` calls), then run one integrated fix pass covering auto-fixable + resolved systemic items + lows; most thorough option
    
    **Conditional options (include when condition met)**:
    
    - (b) label: `Fix SECURITY + CRITICAL + HIGH` — include only when security, critical, or high findings present; omit otherwise to stay within 4-option cap
    - (d) label: `Skip` — always include; when `--efficiency` ran and `extract_count > 0` (HIGH or MEDIUM), replace with `` `Run /distill executables (N HIGH, M MEDIUM candidates)` `` instead
    
    **Option slot budget** (`AskUserQuestion` hard cap = 4 options; "Other" always auto-appended as 5th free-text slot):
    
    - Typical: (a) + (b) + (c) + (d) = 4 ✓
    
    - No sec/crit/high findings: (a) + (c) + (d) = 3 ✓ — slot freed; do NOT fill with custom finding-specific option
    
    - Efficiency override active: (a) + (b) + (c) + distill = 4 ✓ — (d) Skip replaced by distill label
    
    - question: "What next?" (include counts, e.g. "2 critical, 4 high, 3 medium, 1 low. What next?")
    
    After completing `--upgrade`, `--adversarial`, or `--efficiency`: also fire this gate. For (d) Skip: remove the mode just run from the "for other modes" list. When `--adversarial --efficiency` combined: fire gate once after both complete with merged finding counts; remove both from (d) hint. Efficiency distill override: selecting distill option invokes `/distill executables` → `foundry:sw-engineer` extraction then `/audit --efficiency` re-run to confirm savings.
    
    </workflow>
    
    <notes>
    
    - **`!` Breaking findings**: when skill or agent completely non-functional (check 7, broken cross-refs, invalid hook events), prefix finding with `!`, state impact + fix in one place — don't bury in table row. Surfaces as **`! BREAKING`** in bash output and as prominent callout in final report. **`! BREAKING` findings require user acknowledgment before audit proceeds past that check**: call `AskUserQuestion` — state what's broken and impact; user must explicitly confirm awareness before continuing. One question per distinct breaking finding; group only when logically one atomic issue. Batch up to 4 questions into a single `AskUserQuestion` call (communication.md per-call cap) — N breaking findings become `ceil(N/4)` calls, never N. Batching changes only call count; every finding still gets its own question and acknowledgment. Prose acknowledgment in response body does NOT count — `AskUserQuestion` mandatory.
    
    - **settings.json is hands-off**: missing permissions always reported, never auto-edited — structural JSON edits risk breaking Claude Code config loading
    
    - **Dead loops need human judgment**: cycle in follow-up chains may be intentional (e.g. refactor → review → fix → refactor) — flag + explain, don't auto-remove
    
    - **Convergence loop replaces cycle cap**: fix loop runs until zero fixable findings or 5-pass hard limit — see `modes/fix.md` Step 10 for full protocol
    
    - **Relationship to curator**: `foundry:curator` = single-file reactive audit; `/audit` = system-wide sweep running foundry:curator at scale + cross-file checks
    
    - **Paths must be portable**: `.claude/` for project-relative, `~/` or `$HOME/` for home — never literal `/Users/<name>/` or `/home/<name>/` (anti-examples only); applies to ALL config files incl. `settings.json`
    
    - **Bash error logging**: if bash block in Pre-flight or Step 4 fails unexpectedly, append JSONL line to `.notes/logs/audit-errors.jsonl` (`{"ts":"<ISO>","check":"<N>","error":"<message>"}`) for post-mortem — never swallow errors silently.
    
    - **Parallel execution rule**: after Step 2, launch Steps 3 and 4 in same response — all foundry:curator spawns AND system-wide bash checks issued together. Do NOT run Step 3 then Step 4. Aggregation (Step 5) waits for both. Docs-freshness web-explorer (within Step 4, sonnet tier) launches in same parallel batch. Bash checks in Step 4 aren't Agent() spawns, so they never count against any tier ceiling — only Step 3's curator batches (opus tier, `CAP_OPUS`) and the web-explorer spawn (sonnet tier, `CAP_SONNET`) do.
    
    - **Global spawn wave cap**: `CAP_OPUS`/`CAP_SONNET` (constants block; canonical table in claude-config.md §Parallel Spawn Ceilings) bound Agent() calls of that model tier in flight at once, summed across every step/phase due in the same response — not just within one step's own batch count. Applies whenever `--adversarial` and/or `--efficiency` stack extra fan-out onto Steps 3-4 (see `modes/adversarial.md` §Spawn wave cap for the ordering). Growing toward a tier's ceiling happens in `WAVE_STEP`-sized waves, never one sudden burst — different tiers are separate pools, so an opus-tier wave and a sonnet-tier wave may run concurrently without summing against one shared number.
    
    - **Token cost**: Step 3 (foundry:curator spawns) most expensive. For quick structural scan needing only cross-reference + inventory validation, Step 4 system-wide checks often sufficient. Run `/audit agents` or `/audit skills` to scope, or skip Step 3 for fast pass when per-file quality trusted.
    
    - **Routing calibration complement**: to test whether skill trigger descriptions fire correctly (trigger accuracy, A/B testing), use `/foundry:calibrate routing`. `/audit` checks structural quality; `/foundry:calibrate routing` validates right skill selected by Claude Code dispatcher.
    
    - Follow-up chains:
    
      - Audit clean → pick `/foundry:setup` from gate to propagate verified config to `~/.claude/`
      - Audit found structural issues → review flagged files manually before syncing; pick fix level from gate
      - Audit found many low items → pick "Fix all" from gate, or run `/develop:refactor` (requires `develop` plugin) for targeted cleanup
      - After fixing agent instructions (from audit gate) → `/foundry:calibrate <agent>` to verify fix improved recall and confidence calibration
      - Audit Check 20 found description overlap → `/foundry:calibrate routing` to verify behavioral routing impact; update descriptions for confused pairs based on routing report
      - Audit surfaced upgrade proposals → run `/audit --upgrade` separately to apply (not a gate option — use Other slot or run the command directly after gate)
      - `/audit --upgrade` reverted capability change → run `/foundry:calibrate <agent> --full` for deeper signal (N=10 vs N=3 used in upgrade mode)
      - Audit Check 22 found unregistered calibratable mode → update `calibrate/modes/skills.md` domain table and run `/foundry:calibrate skills` to verify new target works
      - Audit Check 22 found stale domain table entry → remove from `calibrate/modes/skills.md`
      - `/audit --efficiency` found model over-provisioning → apply P1+P2 changes from efficiency report → re-run `/audit --efficiency` to confirm savings estimate; run `/foundry:calibrate routing --fast` to verify no routing regression from model changes
      - `/audit --efficiency` found bin/ extraction candidates (HIGH or MEDIUM verdict) → select `Run /distill executables` from follow-up gate to perform extraction; then re-run `/audit --efficiency` to confirm `extract_count == 0`
    
    </notes>
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related