Skip to content

vt-c-repo-audit

"Search & Destroy" deployment integrity audit. Scans for partition violations, context bleed, oversized files, LFS compliance, and hook security. Supports --fix for auto-correction.

Plugin: vt-base
Category: Governance
Command: /vt-c-repo-audit


/vt-c-repo-audit — Deployment Integrity Audit

The "Search & Destroy" skill from the Unified Repo Governance model.

Reference: docs/unified repo approach.md

Purpose

Scans the repository for governance violations that could compromise deployment integrity: - Context artifacts leaking into deployable paths - Oversized files not tracked by Git LFS - Sandbox code that should have been promoted or removed - Build bundles containing non-production files - Missing traceability references - Hook script tampering, unauthorized hooks, and dangerous patterns

When to Use

  • On demand: Run anytime to check governance compliance
  • Before deployment: /vt-d-5-finalize calls this automatically
  • In CI/CD: --ci-mode for non-interactive pipeline use
  • Self-correction: --fix to automatically resolve violations

Prerequisites

.repo-manifest.yaml must exist. If absent, report:

No .repo-manifest.yaml found. Governance is not active.
Run /vt-c-repo-init to initialize unified repo governance.

Invocation

/vt-c-repo-audit              # Interactive audit with full report
/vt-c-repo-audit --fix        # Auto-fix violations where possible
/vt-c-repo-audit --ci-mode    # Non-interactive, exit code 1 on critical failures

Execution Steps

Step 1: Load Manifest

Read .repo-manifest.yaml and parse: - partitions.deployable.paths — production code zones - partitions.context.paths — design/AI context zones - partitions.sandbox.paths — draft code zones - binary_rules — size thresholds and LFS extensions - traceability — SPEC reference requirements

Step 2: Context Bleed Check

Scan all files in deployable paths for context artifacts:

Context indicators in deployable paths (CRITICAL): - .md files with YAML frontmatter containing parent_prd:, personas:, status: draft - Files matching known context patterns: *-persona.md, *-research.md, PRD*.md, PID*.md - .design-state.yaml or .repo-manifest.yaml copies in src/ - Prototype HTML/CSS files in src/ (not component files)

Exceptions (allowed in deployable paths): - README.md, CHANGELOG.md, LICENSE.md - CLAUDE.md, AGENTS.md - Code comments referencing SPEC IDs - API documentation generated from code

Output per finding:

❌ CONTEXT BLEED: src/docs/user-research.md
   Context artifact found in deployable partition (src/)
   Fix: Move to docs/ or 02-Knowledge/

Step 3: File Size Check

Scan all files for oversized objects not tracked by LFS:

# Find files exceeding max_file_size_kb
find . -type f -size +{max_file_size_kb}k \
  -not -path "./.git/*" \
  -not -path "./node_modules/*"

For each oversized file: - If extension matches lfs_extensions but NOT in .gitattributes → CRITICAL - If in context paths → WARNING (large but won't deploy) - If in deployable paths → CRITICAL (will bloat production)

Output:

⚠ OVERSIZED: assets/hero-image.png (2.3 MB)
  Not tracked by Git LFS. Extension .png matches binary rules.
  Fix: git lfs track "*.png" && git add .gitattributes

Step 4: LFS Compliance Check

Verify .gitattributes covers all extensions in binary_rules.lfs_extensions:

For each extension in manifest:
  - Check .gitattributes contains matching LFS rule
  - Check any existing files with that extension are LFS-tracked

Output:

❌ LFS MISSING: .sketch files not in .gitattributes
   Manifest requires LFS tracking for .sketch
   Fix: echo "*.sketch filter=lfs diff=lfs merge=lfs -text" >> .gitattributes

Step 5: Build Bundle Audit

If dist/, build/, .next/, or out/ directories exist:

Scan build output for context artifacts:
  - .md files (except README/LICENSE)
  - .yaml/.yml files (except configs)
  - persona documents, research notes
  - prototype files, specs files
  - .design-state.yaml

Output:

❌ BUILD LEAK: dist/docs/PRD.md
   Context artifact found in build output
   Fix: Update build config to exclude docs/ from output

Step 6: Sandbox Status Check

Scan sandbox paths for stale code:

For files in sandbox paths:
  - Last modified > 30 days ago → STALE WARNING
  - Contains TODO/FIXME/HACK markers → NOTE
  - Duplicated in deployable paths → CONFLICT WARNING

Output:

⚠ STALE SANDBOX: 04-prototyp/components/Dashboard.tsx
  Last modified 45 days ago. Consider:
  - /vt-d-promote to move to production
  - Delete if no longer needed

Step 7: Traceability Check

Read traceability.require_spec_reference from manifest:

If true: Scan ALL commits to deployable paths (full enforcement).

If "new-only": Only scan commits AFTER traceability.cutoff_date:

git log --oneline --since="{cutoff_date}" --diff-filter=AM -- {deployable_paths}
Pre-governance commits are exempt. Include a note in output:
ℹ Traceability mode: new-only (adopted {adoption.adopted_at})
  {N} pre-governance commits exempt | Checking {M} post-governance commits

If false or absent: Skip traceability check entirely.

For each checked commit, verify message contains SPEC- prefix reference.

Also scan source files in deployable paths for SPEC- references in comments.

Output (full mode):

⚠ TRACEABILITY: 3 recent commits to src/ lack SPEC- references
  - abc1234 "Add login form"
  - def5678 "Fix button styling"
  - ghi9012 "Update API handler"
  Recommendation: Include SPEC-NNN in commit messages for traceability

Output (new-only mode):

⚠ TRACEABILITY (new-only, cutoff: 2026-02-13):
  15 pre-governance commits — exempt
  5 post-governance commits checked:
    3 reference SPEC- IDs ✅
    2 missing SPEC- references ⚠

  Note: Consider upgrading to require_spec_reference: true
  when all active features have SPEC IDs assigned.

Step 8: Hook Security Audit

Validates hook script integrity, location, permissions, and content against the trusted hooks manifest (configs/security/trusted-hooks.yaml).

Added by SPEC-053. Detects tampered, unauthorized, or dangerous hook scripts.

8a: Load Trusted Hooks Manifest

Read configs/security/trusted-hooks.yaml from the toolkit root (resolve via TOOLKIT_ROOT).

If the manifest file does not exist: - Record finding: [WARN] No trusted hooks manifest found; hook integrity cannot be verified - Continue with sub-steps 8e (network), 8f (permissions) and 8h (syntax) only — skip 8c (location), 8d (checksum), and 8g (unknown)

If the manifest exists: - Parse approved_locations list - Parse hooks map (keyed by relative path) - Continue with all sub-steps

8b: Discover Hook Scripts

Scan these directories for .sh files and other executables:

  1. plugins/*/scripts/ — all plugin script directories (relative to repo root)
  2. ~/.claude/hooks/ — user-global deployed hooks

For each discovered file: - If it is a symlink: resolve the target using readlink (macOS-compatible; follow chains until a real file is reached) - Record both the symlink path and the resolved target path - If the symlink is broken (target does not exist): record finding [CRITICAL] Broken hook symlink: <symlink> → <target> (target does not exist) - If the file is a directory: skip it silently

Build a set of all discovered scripts with their resolved paths.

8c: Location Validation

For each discovered script (using resolved paths for symlinks): - Check if the resolved path falls under one of the approved_locations entries from the manifest - For ~/.claude/hooks/ symlinks: validate that the resolved target is in an approved location (e.g., plugins/vt-base/scripts/)

Finding for unapproved location:

[CRITICAL] Hook script found in unapproved location: /tmp/.claude-hooks/exfil.sh

8d: Checksum Verification

For each hook entry in the manifest: 1. Check if the file exists on disk 2. If it exists: compute shasum -a 256 <file> and compare against the manifest's sha256 value 3. If the file does not exist on disk: record a WARN finding

Finding for checksum mismatch:

[CRITICAL] Hook checksum mismatch: block-writes.sh (expected abc123..., got def456...)

Finding for missing file:

[WARN] Manifest lists hook not found on disk: plugins/vt-base/scripts/deleted-hook.sh

8e: Network Call Detection

For each discovered script, scan for outbound network call patterns using grep -n:

Patterns to match: - Shell: curl, wget, nc (word boundary), netcat, ncat, socat - Python: python.*http, requests\., urllib, http\.client - Ruby: ruby.*net, Net::HTTP - Node: node.*http, fetch(, XMLHttpRequest

Scan ALL lines including comments (commented-out network calls can be uncommented and are still a risk indicator).

Finding for network call detected:

[CRITICAL] Hook contains network call pattern: secret-scanner.sh (line 42: curl)

If no network patterns are found in a script, no finding is reported for that script.

8f: Permission Audit

For each discovered script, check file permissions using macOS-compatible syntax:

stat -f '%Lp' <file>

Parse the octal permission value: - If the group-write bit is set (second digit is 2, 3, 6, or 7) → WARN - If the other-write bit is set (third digit is 2, 3, 6, or 7) → WARN - Acceptable permissions: 700, 750, 755 - Unacceptable permissions: 775, 776, 777, etc.

Finding for overly permissive:

[WARN] Hook has overly permissive permissions: block-writes.sh (777, expected 755 or stricter)

If a script has 755 or stricter permissions, no finding is reported.

8g: Unknown Hook Detection

Compare the set of discovered scripts against the manifest entries:

  • For scripts in plugins/*/scripts/: use the relative path from repo root as the key
  • For scripts in ~/.claude/hooks/ that are symlinks: validate via their resolved target path (the source script should be in the manifest)
  • For scripts in ~/.claude/hooks/ that are NOT symlinks (real files, not symlinks to plugin scripts): flag as unknown

Finding for unknown hook:

[CRITICAL] Unknown hook script not in manifest: ~/.claude/hooks/sneaky-hook.sh

8h: Syntax Check

For each script in the set built by 8b that is a shell script (a .sh extension, or a first line starting with #! naming bash or sh), run:

bash -n <resolved-target-path>

Use the resolved target for symlinks. Do not run a second find — the discovery set from 8b is the input. Skip non-shell executables silently. This sub-step needs no manifest, so it also runs when 8a found none.

Finding for a script that does not parse:

[CRITICAL] Hook script does not parse: plugins/vt-base/scripts/quality-gate.sh (bash -n: line 5: syntax error: unexpected end of file)

Never downgrade this to WARN — a hook that does not parse has the right location, a matching checksum and correct permissions, so 8c–8g all pass, and it fails only at runtime, at the worst moment, with an error that points at the hook mechanism rather than at the file. Functionally it is a missing hook. bash -n catches syntax only, not behaviour, and needs no external tool; shellcheck is deliberately not used — it is not installed everywhere and its style findings would turn an audit red while nothing is broken.

Step 8b: Gate Provenance (SPEC-189 FR-7)

Scan every gate artifact — .plan-gate.md, .review-gate.md, .test-gate.md, and the review_gate / plan_gate / test_gate blocks in specs/*/state.yaml.

FINDING — report as a normal audit finding:

a gate with status: PASS and a validator that is present but not agent, or with validators_run empty.

That is a PASS somebody's prescribed validator did not produce. It is the only actionable case, and it is actionable because the schema shipped before the artifact was written.

COUNTED LINE — never a finding:

N pre-schema gates (validator: unknown)

A gate with no validator key predates SPEC-189. validator absent reads as unknown, never as agent (FR-6), and EC-1 forbids retro-mutating existing artifacts — so a pre-schema gate is not something anyone can fix.

⚠ The counted line MUST NOT enter the severity roll-up at ### Step 9: Generate Report, and MUST NOT affect --ci-mode's exit status at ### Step 11. Folding it into the WARN aggregation turns every pre-schema artifact yellow forever — a control that reports a backlog it cannot act on, which trains its readers to skim past it and takes the real finding with it. That over-firing is exactly what this repo has a pattern doc about, and it is why Decision 3 made this a counted line rather than a finding.

Only status: PASS is scrutinised. An unverified FAIL misleads nobody; provenance matters where a verdict grants permission, not where it withholds it.

Step 9: Generate Report

╔══════════════════════════════════════════════════╗
║         REPO GOVERNANCE AUDIT REPORT             ║
╠══════════════════════════════════════════════════╣
║                                                  ║
║  Context Bleed:     ✅ PASS  (0 violations)      ║
║  File Size:         ⚠ WARN  (2 oversized files)  ║
║  LFS Compliance:    ✅ PASS  (all tracked)        ║
║  Build Bundle:      ✅ PASS  (clean)              ║
║  Sandbox Status:    ⚠ WARN  (1 stale file)       ║
║  Traceability:      ⚠ WARN  (3 commits)          ║
║  Hook Security:     ✅ PASS  (all hooks verified) ║
║                                                  ║
║  Overall: ⚠ WARNINGS (no critical failures)      ║
║                                                  ║
╚══════════════════════════════════════════════════╝

Hook Security traffic light logic: - ✅ PASS (green): All hooks pass all checks — checksums match, no unknown hooks, no network calls, permissions OK, every shell hook parses - ⚠ WARN (yellow): Only WARN-severity findings — permission issues, missing manifest, manifest lists hook not found on disk - ❌ FAIL (red): Any CRITICAL finding — checksum mismatch, unknown hook, unapproved location, network call pattern, broken symlink, parse failure (8h)

Step 10: Self-Correction (--fix mode)

If --fix flag is set, for each fixable violation:

  1. Context bleed: Move file to appropriate context path, update references
  2. Oversized files: Run git lfs track for matching extensions
  3. LFS missing: Add rules to .gitattributes
  4. Build leaks: Suggest build config changes (cannot auto-fix build tools)
  5. Hook permissions: Run chmod 755 <script> to fix overly permissive hooks
  6. Hook checksum mismatch: Cannot auto-fix — report "Manual review required: verify hook content is correct, then update manifest with shasum -a 256 <script>"
  7. Unknown hooks: Cannot auto-fix — report "Manual review required: add to configs/security/trusted-hooks.yaml or remove the script"
  8. Hook network calls: Cannot auto-fix — report "Manual review required: verify the network call is intentional"

Show summary of fixes applied:

Auto-fixed 3 violations:
  ✅ Moved src/research-notes.md → docs/research-notes.md
  ✅ Added *.sketch to .gitattributes (LFS tracking)
  ✅ Added *.psd to .gitattributes (LFS tracking)

Manual action needed:
  ⚠ Update build config to exclude docs/ from dist/
  ⚠ Add SPEC- references to 3 commit messages (cannot retrofix)

Step 11: CI Mode (--ci-mode)

When running in CI/CD pipeline: - No interactive prompts - Exit code 0 = PASS (no critical violations) - Exit code 1 = FAIL (critical violations found) - Warnings are logged but don't cause failure - Output is structured for CI log parsing

[REPO-AUDIT] Context Bleed: PASS
[REPO-AUDIT] File Size: WARN (2 files)
[REPO-AUDIT] LFS Compliance: PASS
[REPO-AUDIT] Build Bundle: PASS
[REPO-AUDIT] Sandbox Status: WARN (1 stale)
[REPO-AUDIT] Traceability: WARN (3 commits)
[REPO-AUDIT] Hook Security: PASS
[REPO-AUDIT] RESULT: PASS (0 critical, 7 warnings)