---
name: framework-rot-audit
description: >
  Framework-internal rot/silo audit. Detects wiring/reference rot in a DevForgeAI
  install — artifacts DEFINED but never wired in (orphaned agents/hooks/skills =
  silos) and references that point at a NON-EXISTENT artifact (dangling) — and
  orchestrates the existing dead-code-detector for function-level dead code, then
  synthesizes a GROUNDED inventory report. Read-only: it never mutates the repo and
  never asserts a false orphan (mentioned-but-uninvoked is INCONCLUSIVE, not orphan).
  Use when the user runs /audit-rot, asks to audit the framework for orphans / dead
  code / silos / rot, or asks "what is defined but never used here". Do NOT use for
  structural orphans like backups/duplicates/dual-path size-drift (use /audit-orphans)
  or for a reference-sweep migration (use /migrate-refs).
metadata:
  author: DevForgeAI
  version: "1.0.0"
  category: quality-assurance
  agent-skills-spec-version: "1.0"
  last-updated: "2026-06-02"
  related-adr: ADR-116
allowed-tools:
  - Read
  - Glob
  - Grep
  - Write
  - AskUserQuestion
  - Task
  - Bash(devforgeai-validate:*)
model: opus
effort: Medium
---

# Framework Rot Audit

A **read-only** audit of a DevForgeAI install for wiring/reference rot. It complements
the existing detectors rather than duplicating them:

| Concern | Owner |
|---|---|
| **Wiring rot** — defined-but-unwired (orphan/silo) + dangling references | **this skill** (via `devforgeai-validate audit-wiring`) |
| Function-level dead code (unused functions) | `dead-code-detector` subagent (orchestrated in Phase 02) |
| Structural orphans (backups, duplicates, dual-path size-drift, skill-dirs) | `/audit-orphans` (NOT re-run here; cross-referenced) |
| Dual-path byte-drift | `Verify Mirror Sync` CI (NOT re-run here) |

**Core principle — never assert a false orphan.** A GROUNDED orphan exists only when an
artifact's name appears NOWHERE outside its own definition. A name that appears but in no
invocation form is INCONCLUSIVE ("mentioned, wiring unconfirmed"), never an orphan. Every
finding records the forms searched. See `.claude/rules/core/epistemic-integrity.md`.

**If ambiguous or conflicts detected: HALT and use AskUserQuestion.**

---

## Execution Model

This skill expands inline. After invocation, execute Phase 00 immediately; run the phases
in order. This is a **read-only audit** — there is no TDD phase-state machine, no
`phase-init`/`phase-record` CLI gates, and no spec-file/test-file writes. The deterministic
engine is `devforgeai-validate audit-wiring`; its JSON output (not LLM judgment) is the
source of every wiring finding.

**Behavioral admonitions** (Execution Model + Self-Check + Token-Optimization-Bias
prohibition): see `.claude/rules/core/anti-skip-behavior.md`. Honor every phase; do not skip
the deterministic engine and hand-roll grep instead — `audit-wiring` exists because naive
grep produces false orphans (a grep once called `coverage-analyzer` orphaned when it is
referenced in 8 files).

---

## Phase 00 — Initialization

EXECUTE: Resolve the project root (the directory containing `CLAUDE.md` + `.claude/`).
Confirm `devforgeai-validate` is available: `Bash(devforgeai-validate --help)`.
VERIFY: `.claude/` exists under the project root; the CLI help lists `audit-wiring`.
If the CLI is unavailable, HALT and AskUserQuestion (the engine is mandatory; do not
substitute hand-rolled grep).

---

## Phase 01 — Wiring audit (deterministic engine)

EXECUTE:
```
Bash(devforgeai-validate audit-wiring --project-root=<root> --format=json)
```
This runs six sub-audits (agents, hooks, skills, commands, code, retired) and emits one JSON
object with, per sub-audit, `orphans` (GROUNDED — defined, zero references), `dangling` (GROUNDED —
referenced, no definition), and `inconclusive` (mentioned, no invocation form). Capture the
JSON; do NOT re-derive any of these by hand.

The `code` sub-audit (ADR-120) builds a stdlib `ast` import graph over the Python source tree
(`--code-root`, default `src/claude/scripts`) and flags `.py` files imported by nothing that are
not entry points — at the same never-false-orphan bar (a module reached only dynamically is
INCONCLUSIVE, never a GROUNDED orphan). It auto-skips with a note when `--code-root` is absent
(e.g. a consumer install, which ships `.claude/` not `src/claude/`).
It also runs a dangling-import pass over `--code-root` and `tests/` files, emitting GROUNDED `{file, line, module}` entries for absolute imports pointing at repo-local modules that no longer exist.

The `retired` sub-audit (ADR-122) reads the hand-curated `data/retired_artifacts.json` registry
(tokens an ADR retired, e.g. `source-tree.md` → ADR-071) and flags OPERATIONAL references to them
as GROUNDED `dangling` — but ONLY an executable read-form (`open(`/`cat`/`source`/non-negated
`[ -f ]`) on a non-comment line of a `.py`/`.sh` file. A reference in prose/`.md`/`.json` (INCLUDING
a `Read(…)` instruction, indistinguishable from a migration EXAMPLE), a comment, a bare path, or a
negated `[ ! -f ]` assertion is `inconclusive` (surfaced with `cited_in`/`cited_count` for human
triage, never asserted) — the same never-false-positive bar (ADR-116 §Decision-3). It fails open
(absent/malformed registry → skipped, never exit 2), so it is consumer-safe.

**Consumer-install invocation (ADR-121).** The markdown/JSON reference corpus defaults to the
framework subdirs `.claude/{skills,commands,agents,rules}` × `.md,.py`. A consumer wires agents/
skills from their OWN files (app source, config, docs), which the default corpus never reads — so
run with the corpus broadened to include them:
```
Bash(devforgeai-validate audit-wiring --project-root=<root> --corpus-root . --corpus-suffixes .md,.py,.ts,.yaml,.json)
```
`--corpus-root` ADDS roots to the always-scanned framework subdirs (never replaces them, so a narrow
root cannot manufacture a false framework orphan); `--corpus-suffixes` widens the file types. Every
`orphans` verdict is GROUNDED only RELATIVE to the report's `corpus_roots`/`corpus_suffixes` (both
emitted in the JSON) — under the default corpus in a consumer repo, a wired-from-app artifact can
appear as an orphan, so widen the corpus before acting on a consumer orphan finding.

VERIFY: exit code is 0 (or 1 only if `--strict` was passed); stdout parses as JSON with an
`audits` object and a `grounded_finding_count`.

Optional flags to surface to the user: `--audits=agents,hooks` (subset), `--code-root=<path>`
(Python source tree for the `code` sub-audit — distinct from `--corpus-root`), `--retired-registry=<path>`
(retired-artifacts registry for the `retired` sub-audit; default bundled), `--corpus-root=<path>` +
`--corpus-suffixes=<csv>` (broaden the markdown/JSON reference search for consumer installs),
`--exclusions=<path>` (allowlist of by-design-unreferenced names), `--strict` (exit 1 on any
GROUNDED finding — for CI use).

---

## Phase 02 — Function-level dead code (orchestrated, optional)

EXECUTE: Delegate to the existing detector for unused functions (a DIFFERENT concern from
file-level wiring):
```
Task(subagent_type="dead-code-detector",
     prompt="Scan src/claude/scripts/ for unused functions; return the JSON findings contract.")
```
The detector owns Treelint-vs-grep fallback + entry-point exclusion + confidence scoring.
VERIFY: the subagent returns its `findings[]` JSON. If Treelint is unavailable/degraded in
the environment, accept the grep-fallback result and mark the relevant findings INCONCLUSIVE
(do NOT fabricate dead-code claims). This phase is skippable on user request (`--audits` with
no function scan) — record that it was skipped in the report.

---

## Phase 03 — Structural cross-reference (no re-run)

EXECUTE: Do NOT re-run structural orphan detection. State in the report that backups /
duplicates / dual-path size-drift / skill-dir structure are owned by `/audit-orphans`, and
dual-path byte-drift by the `Verify Mirror Sync` CI gate. Recommend the user run
`/audit-orphans` separately if a full structural sweep is wanted.

---

## Phase 04 — Synthesize the grounded inventory report

EXECUTE: Write `devforgeai/qa/rot-audit-<YYYY-MM-DD>.md` containing:
1. **GROUNDED findings** — every `orphan` and `dangling` from Phase 01 + every high-confidence
   `dead_code` from Phase 02. For each: artifact/ref, kind (orphan/dangling/dead-code), the
   `forms_searched` (or evidence), and a fix surface. These are facts.
2. **INCONCLUSIVE** — a SEPARATE section for `inconclusive` items ("mentioned but no invocation
   form found"). Label them "needs human confirmation"; NEVER present them as orphans.
3. **Scope note** — which audits ran, exclusions applied, and what is explicitly out of scope
   (structural → /audit-orphans; code-file unreferenced `.py` → the `code` sub-audit, ADR-120
   [`.js`/`.ts` still out of scope]; dual-path → CI).

VERIFY: the report file exists (Glob) and the GROUNDED count matches `grounded_finding_count`
from the engine. Present a short summary to the user (counts per category + the report path).

**Triage discipline (mirror the test-fix-can-hide-a-real-bug lesson):** before recommending
deletion of any GROUNDED orphan, advise a negative control — confirm the artifact is genuinely
unused (not invoked via a form the engine doesn't yet model). For confirmed by-design orphans,
add the name to the `audit_wiring_exclusions.txt` allowlist rather than deleting prematurely.

---

## Out of scope

Now SHIPPED (no longer out of scope): code-file (`.py`) unreferenced detection (the `code` sub-audit,
ADR-120); consumer-configurable markdown/JSON corpus (`--corpus-root`/`--corpus-suffixes`, ADR-121);
and the retired-artifact reference registry (the `retired` sub-audit, ADR-122).

Still out of scope: `.js`/`.ts` and cross-language code-rot (ADR-120 rejected — the never-false-orphan
invariant does not hold across arbitrary languages); auto-seeding the retired-artifact registry from
ADR prose (it is hand-curated — ADR-122); auto-detecting a consumer's wiring conventions / per-language
reference forms (ADR-121); and any auto-scheduled run. This skill is invoked on demand only.
