# Tier 2 Sandbox File Leak Prevention — Spec

**Track:** `tier2_leak_prevention_20260620`
**Created:** 2026-06-20
**Type:** fix (recovery + defense-in-depth)
**Scope:** 5 new files, 1 modified file, 4 commits

## Background

On 2026-06-19, commit `00e5a3f2` ("chore(env): pre-existing tier2 setup files") was pushed to `origin/master`. The commit contained 9 file changes:

| Status | File | Notes |
|--------|------|-------|
| ADDED | `.opencode/agents/tier2-autonomous.md` | tier-2 SANDBOX agent (canonical source: `conductor/tier2/agents/tier2-autonomous.md`) |
| ADDED | `.opencode/commands/tier-2-auto-execute.md` | tier-2 SANDBOX command (canonical source: `conductor/tier2/commands/tier-2-auto-execute.md`) |
| MODIFIED | `opencode.json` | tier-2 sandbox overrode MCP path → `manual_slop_tier2`, default_agent → `tier2-autonomous`, model → `minimax-coding-plan/MiniMax-M3` |
| MODIFIED | `mcp_paths.toml` | tier-2 sandbox cleared `extra_dirs` to `[]` |
| MODIFIED | `project_history.toml` | timestamp update only (out of scope) |
| ADDED | `scripts/tier2/artifacts/.../*.py` | 4 throwaway scripts (out of scope; legitimately tier-2 working artifacts) |

The commit message ("pre-existing tier2 setup files") was misleading. The actual root cause: `setup_tier2_clone.ps1` legitimately modifies these files **in the clone** (`C:\projects\manual_slop_tier2\`), but the modifications leaked into the **main repo** via an accidental `git add .` in the tier-2 clone. The canonical sources live at `conductor/tier2/*` (per `setup_tier2_clone.ps1:48-49`); the main repo should NEVER see the sandbox's local config drift.

## What the user asked for

1. **Selective revert** of the offending files: `./opencode/*`, `mcp_paths.toml`, `opencode.json`. Leave the 4 throwaway scripts and `project_history.toml` timestamp at HEAD per the user's explicit list.
2. **A way to make sure tier-2 autonomous never commits those files** — explicitly NOT via gitignore.

## Design

### Layer 1 (existing): OpenCode permission system
The tier-2-autonomous agent profile denies direct edits to the forbidden files. This was already in place but the deny rules didn't cover the auto-modifications done by `setup_tier2_clone.ps1` (the script itself writes the files, not the agent directly).

### Layer 2 (this track): pre-commit hook at the commit boundary
`conductor/tier2/githooks/pre-commit`:
- Reads `conductor/tier2/githooks/forbidden-files.txt` (substring patterns, one per line)
- For each staged file, checks if any pattern is a substring of the path
- Auto-unstages matching files via `git rm --cached --force`
- Always exits 0 (removes the leak rather than blocking the commit, since tier-2 cannot run `git restore --staged` per the sandbox permission rules)
- Hook source lives at `conductor/tier2/githooks/pre-commit`; config lives alongside as `conductor/tier2/githooks/forbidden-files.txt`

### Layer 3 (this track): working-tree audit
`scripts/audit_tier2_leaks.py`:
- Default mode (informational, exit 0): scans working tree for forbidden files
- `--strict` mode (CI gate, exit 1 if leaks): catches anything the hook missed (manual edits, ops mistakes)
- `--json` mode: machine-readable output for CI integration
- Skips `tests/`, `conductor/`, `node_modules/`, `.git/`, etc.
- Reports only `untracked` and `modified` files (tracked-and-clean files are legitimate)

### Hook installation
`scripts/tier2/setup_tier2_clone.ps1` step 4 (Install git hooks) is updated to copy the new `pre-commit` hook into the clone's `.git/hooks/` directory alongside the existing `pre-push` and `post-checkout` hooks. The forbidden-files.txt config is already committed to the clone (as part of the canonical `conductor/tier2/*` source), so the hook can find it via the project root.

## Forbidden patterns (substring matches)

```
.opencode/agents/tier2-autonomous    # sandbox agent, NOT the interactive tier2-tech-lead
.opencode/commands/tier-2-auto-execute  # sandbox slash command
opencode.json       # MCP path / default_agent / model override
mcp_paths.toml      # extra_dirs cleared in clone
```

Patterns are SPECIFIC (not prefix-based) so they do not match the legitimate interactive tier-2 tech-lead prompt at `.opencode/agents/tier2-tech-lead.md`.

## Tests

- `tests/test_tier2_pre_commit_hook.py` (12 tests): pre-commit hook behavior
- `tests/test_audit_tier2_leaks.py` (13 tests): audit script behavior

All 25 tests pass.

## Files changed

| Status | File |
|--------|------|
| NEW | `conductor/tier2/githooks/pre-commit` |
| NEW | `conductor/tier2/githooks/forbidden-files.txt` |
| NEW | `scripts/audit_tier2_leaks.py` |
| NEW | `tests/test_tier2_pre_commit_hook.py` |
| NEW | `tests/test_audit_tier2_leaks.py` |
| MODIFIED | `scripts/tier2/setup_tier2_clone.ps1` |

## Out of scope

- Wiring `audit_tier2_leaks.py --strict` into CI (deferred to a follow-up track)
- Rebasing stale tier-2 branches on the new master tip (user action required; see `TRACK_COMPLETION_tier2_leak_prevention_20260620.md` §Next Steps)
- The 4 throwaway scripts in `scripts/tier2/artifacts/.../*.py` (legitimate tier-2 working artifacts per the tier-2 convention)
- The `project_history.toml` timestamp update (harmless side effect)
