8ec0a30bf4
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 ineae75877) 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: commit6956676fdeleted 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 commitfab2e55b'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 commits71b51674+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
151 lines
5.3 KiB
Python
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())
|