Private
Public Access
0
0
Files
manual_slop/scripts/audit_branch_required_files.py
T
ed 8ec0a30bf4 feat(scripts): add audit_branch_required_files.py (Rule 4 CI gate)
Defense-in-depth check for the 2026-06-24 MCP regression: verifies that
the 2 MCP-config files (opencode.json + mcp_paths.toml) are present on
a tier-2 branch. If either is missing, the audit fails (exit 1) with
a clear diagnostic and the exact commands to restore the files.

The pre-commit hook (conductor/tier2/githooks/pre-commit, hardened in
eae75877) auto-unstages these files on commit, but does not prevent
the deletion from being in the commit's diff. The 2026-06-24 MCP
regression was exactly this: commit 6956676f deleted both files,
and the empty fix commit (2b7e2de1) was a no-op.

This audit catches that pattern 1 step earlier than the user noticing:
on push, on pre-merge, on manual review. It checks the branch's index
via 'git cat-file -e ref:file' (not the working tree) so it works in
CI without a checked-out working tree.

Usage:
  # Audit the current HEAD
  uv run python scripts/audit_branch_required_files.py

  # Audit a specific ref
  uv run python scripts/audit_branch_required_files.py --ref origin/tier2/foo

  # JSON output for CI integration
  uv run python scripts/audit_branch_required_files.py --json

The script's REQUIRED_FILES list has 2 entries (the actual MCP
regression targets), not 4. The 2 .opencode/agents/... files in
conductor/tier2/githooks/forbidden-files.txt are tier-2 sandbox-only
working tree files that are NEVER tracked in any branch (per commit
fab2e55b 'undo sandbox file leaks'); they live only in the tier-2
clone's working tree, copied there by setup_tier2_clone.ps1.

Exit codes:
  0 - all required files present
  1 - one or more required files missing (CI gate failure)
  2 - usage error

Verified:
- HEAD: OK (files restored by user commits 71b51674 + cb1b0c1c)
- master: OK (files exist on master)
- 6956676f: FAIL (correctly detects the MCP regression commit)
- --json output is valid JSON
- --help shows clean usage

CI integration (when the project gets CI):
  Add to .github/workflows/ci.yml (or equivalent):
    - name: Verify tier-2 required files
      run: uv run python scripts/audit_branch_required_files.py --strict

  Or as a per-PR check on tier-2 branches:
    - name: Verify required files on tier-2 PR
      if: startsWith(github.head_ref, 'tier2/')
      run: uv run python scripts/audit_branch_required_files.py --strict
2026-06-25 10:21:02 -04:00

151 lines
5.3 KiB
Python

#!/usr/bin/env python3
"""Tier 2 required-files audit.
Defense-in-depth check for the 2026-06-24 MCP regression: verifies that
the 2 MCP-config files (opencode.json + mcp_paths.toml) are present in
a tier-2 branch. If either is missing, the audit fails (exit 1) with
a clear diagnostic.
Context: setup_tier2_clone.ps1 modifies opencode.json and mcp_paths.toml
IN the clone (C:\\projects\\manual_slop_tier2\\), and copies the tier-2
agent prompt + slash command from conductor/tier2/ into .opencode/.
If a tier-2 commit accidentally captures any of these via `git add .`,
they leak into the main repo. The pre-commit hook
(conductor/tier2/githooks/pre-commit) auto-unstages them on commit
but does not prevent the deletions from appearing in commit history.
This audit is a defense-in-depth check: it can be run on any branch
(typically a tier-2 branch) to verify the 2 required files are present.
Run it in pre-merge, in a CI workflow, or manually before merging a
tier-2 branch to master.
Usage:
# Audit the current HEAD
uv run python scripts/audit_branch_required_files.py
# Audit a specific ref (branch, commit, tag)
uv run python scripts/audit_branch_required_files.py --ref origin/tier2/phase2_4_5_call_site_completion_20260621
# JSON output for CI integration
uv run python scripts/audit_branch_required_files.py --json
# Strict mode: exit 1 on any missing file (default; the script
# is informational by default but `--strict` is the CI-gate mode)
Exit codes:
0 - all required files present
1 - one or more required files missing (CI gate failure)
2 - usage error (bad args, git not available, ref not found)
The 2 required files (the actual MCP regression target from 2026-06-24):
1. opencode.json - the OpenCode config that setup_tier2_clone.ps1 overrides
2. mcp_paths.toml - the MCP allowed paths that setup_tier2_clone.ps1 clears
These are the 2 files that the 2026-06-24 MCP regression deleted from
the tier-2 branch's index. The pre-commit hook strips them from
tier-2 commits but does not prevent the deletion from being in the
commit's diff (the hook only unstages ADDITIONS).
The other 2 entries in conductor/tier2/githooks/forbidden-files.txt
(.opencode/agents/tier2-autonomous.md and
.opencode/commands/tier-2-auto-execute.md) are tier-2 sandbox-only
working tree files that are NEVER tracked in any branch (per commit
fab2e55b "undo sandbox file leaks"). They live only in the tier-2
clone's working tree, copied there by setup_tier2_clone.ps1 from
conductor/tier2/{agents,commands}/. They are not REQUIRED for the
audit.
CI integration (when the project gets CI):
Add to .github/workflows/ci.yml (or equivalent):
- name: Verify tier-2 required files
run: uv run python scripts/audit_branch_required_files.py --strict
# The `--strict` flag is the default behavior; explicit for clarity.
Or as a per-PR check on tier-2 branches:
- name: Verify required files on tier-2 PR
if: github.base_ref == 'master' && startsWith(github.head_ref, 'tier2/')
run: uv run python scripts/audit_branch_required_files.py --strict
Note: this script does NOT modify the working tree. It is read-only.
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from pathlib import Path
REQUIRED_FILES: tuple[str, ...] = (
"opencode.json",
"mcp_paths.toml",
)
def check_required_files(ref: str) -> list[str]:
missing: list[str] = []
for required in REQUIRED_FILES:
result = subprocess.run(
["git", "cat-file", "-e", f"{ref}:{required}"],
capture_output=True,
)
if result.returncode != 0:
missing.append(required)
return missing
def main() -> int:
parser = argparse.ArgumentParser(
description="Verify tier-2 sandbox-required files are present on a branch.",
)
parser.add_argument(
"--ref",
default="HEAD",
help="Git ref to check (default: HEAD). E.g. origin/tier2/phase2_4_5_call_site_completion_20260621",
)
parser.add_argument(
"--json",
action="store_true",
help="Emit JSON output for CI integration.",
)
parser.add_argument(
"--strict",
action="store_true",
default=True,
help="Exit 1 on any missing file (default; explicit for CI-gate clarity).",
)
args = parser.parse_args()
missing = check_required_files(args.ref)
if args.json:
result = {
"ref": args.ref,
"required": list(REQUIRED_FILES),
"missing": missing,
"ok": len(missing) == 0,
}
print(json.dumps(result, indent=2))
return 0 if result["ok"] else 1
if not missing:
print(f"OK: {args.ref} has all {len(REQUIRED_FILES)} required tier-2 files.")
for f in REQUIRED_FILES:
print(f" + {f}")
return 0
print(f"FAIL: {args.ref} is missing {len(missing)} required tier-2 file(s):", file=sys.stderr)
for f in missing:
print(f" - {f} (deleted or missing)", file=sys.stderr)
print("", file=sys.stderr)
print("This is a sandbox file leak. The 2026-06-24 MCP regression was caused", file=sys.stderr)
print("by `setup_tier2_clone.ps1` modifications to opencode.json + mcp_paths.toml", file=sys.stderr)
print("leaking into a tier-2 commit. To restore the missing files on this branch:", file=sys.stderr)
print(" git checkout master -- <missing-file>", file=sys.stderr)
print(" git commit -m 'fix: restore <missing-file> (deleted by tier2 sandbox)'", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())