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-finalizecalls this automatically - In CI/CD:
--ci-modefor non-interactive pipeline use - Self-correction:
--fixto 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:
ℹ 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:
plugins/*/scripts/— all plugin script directories (relative to repo root)~/.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:
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:
Finding for missing file:
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:
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:
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:
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:
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:
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: PASSand avalidatorthat is present but notagent, or withvalidators_runempty.
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:
- Context bleed: Move file to appropriate context path, update references
- Oversized files: Run
git lfs trackfor matching extensions - LFS missing: Add rules to
.gitattributes - Build leaks: Suggest build config changes (cannot auto-fix build tools)
- Hook permissions: Run
chmod 755 <script>to fix overly permissive hooks - Hook checksum mismatch: Cannot auto-fix — report "Manual review required: verify hook content is correct, then update manifest with
shasum -a 256 <script>" - Unknown hooks: Cannot auto-fix — report "Manual review required: add to
configs/security/trusted-hooks.yamlor remove the script" - 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)