Scope Lock: pre-task declaration, git history scan, pre-commit audit script
This commit is contained in:
@@ -64,6 +64,38 @@ at a problem.
|
||||
- **No-Fluff Mode is available on request** — unpadded, ruthless review that
|
||||
argues rather than reassures (see `ai.md`). Optional, never the default.
|
||||
|
||||
## Scope Lock
|
||||
|
||||
BEFORE editing any file, declare the exact file list for the task. Every file you
|
||||
touch must be either in that list OR you must state the specific dependency that
|
||||
requires it (e.g. "method X's signature changed, callers must update"). No "while
|
||||
I'm here" edits. No refactoring. No style tweaks. If you spot a problem in a file
|
||||
you're already editing, note it in HANDOFF.md as a follow-up — do not fix it in
|
||||
this change.
|
||||
|
||||
> *The commit `d2114c7` touched 11 files across 4 layers because the AI decided
|
||||
> to refactor the world. This rule exists because of that incident.*
|
||||
|
||||
### Before editing a file — git history scan
|
||||
|
||||
```
|
||||
git log --oneline -5 -- <file>
|
||||
```
|
||||
|
||||
If the file hasn't been touched in many commits and the current task doesn't
|
||||
directly require changing it, that is a red flag — stop and justify the edit or
|
||||
don't make it. A stable file touched by an unrelated task is a bug, not a feature.
|
||||
|
||||
### Pre-commit audit
|
||||
|
||||
Before every commit, run `scripts/scope-check.sh` with the declared file list.
|
||||
If any file appears in `git diff` but not in the declared scope, either justify
|
||||
it or revert the change. The script enforces what the rule demands.
|
||||
|
||||
```bash
|
||||
./scripts/scope-check.sh "Models/Scene.cs" "ViewModels/MainViewModel.cs" "MainWindow.xaml"
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
From WSL, ALWAYS use the Windows dotnet host — Linux `dotnet` re-downloads the
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
# HANDOFF — Session State
|
||||
|
||||
## Branch
|
||||
**`main`** @ `89ab83b`, committed LOCALLY, working tree clean. **NOT pushed — user rule: never
|
||||
**`main`** @ `67baac6`, committed LOCALLY, working tree clean. **NOT pushed — user rule: never
|
||||
push without explicit instruction (2026-08-24).** No feature branches pre-1.0: all work lands on
|
||||
`main` per work unit.
|
||||
|
||||
@@ -23,7 +23,10 @@ push without explicit instruction (2026-08-24).** No feature branches pre-1.0: a
|
||||
- `HotkeyConfigDialog.xaml` + `.cs` — click-to-capture, Unbind per row, Reset/Save/Cancel
|
||||
- `ViewModels/MainViewModel.cs` — loads bindings, `OpenHotkeyConfigCommand`, `HotkeyBindingsChanged` callback
|
||||
- `MainWindow.xaml.cs` — passes bindings to manager, re-attaches on dialog save
|
||||
- Tests: `HotkeyConfigTests` (round-trip, display string, storage serialization)
|
||||
- Tests: `HotkeyConfigTests` (round-trip, display string, storage serialization)
|
||||
- **Thumbnail strip hotkey labels** (`d4aa5e1`): each scene shows its current binding (e.g. "Starting F1").
|
||||
- **Pull-out tab renamed** to "Stream Settings"; **right-click Start Stream** → Change Account / Logout context menu (`67baac6`).
|
||||
- **Scope Lock rules** (`AGENTS.md` + `scripts/scope-check.sh`): pre-task scope declaration, git history scan before editing, pre-commit audit script that validates `git diff` against declared file list. Born from the `d2114c7` incident.
|
||||
- Suite: 231 total, 230 pass — only failure is the known pre-existing `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`. Build 0 warnings.
|
||||
|
||||
## ⚠️ Landmines
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env bash
|
||||
# scope-check.sh — Validate that git diff only touches declared files.
|
||||
# Usage: ./scripts/scope-check.sh "file1.cs" "file2.cs" ...
|
||||
# Exit 0 if all changed files are in scope, 1 if any are outside.
|
||||
#
|
||||
# Checks staged, unstaged, and untracked changes against the allowed list.
|
||||
# Supports glob patterns in the allowed list (e.g. "ytLive.Tests/*").
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 \"file1.cs\" \"file2.cs\" ..."
|
||||
echo " Pass the declared file list as arguments."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Collect allowed patterns into an array
|
||||
allowed=("$@")
|
||||
|
||||
# Gather all changed files: working tree vs HEAD (tracked) + staged + untracked
|
||||
changed=$({
|
||||
git diff --name-only HEAD 2>/dev/null || true
|
||||
git diff --name-only --cached 2>/dev/null || true
|
||||
git ls-files --others --exclude-standard 2>/dev/null || true
|
||||
} | sort -u)
|
||||
|
||||
if [ -z "$changed" ]; then
|
||||
echo "✓ No changes detected."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
violations=()
|
||||
while IFS= read -r file; do
|
||||
matched=false
|
||||
for pattern in "${allowed[@]}"; do
|
||||
if [[ "$file" == $pattern ]]; then
|
||||
matched=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$matched" = false ]; then
|
||||
violations+=("$file")
|
||||
fi
|
||||
done <<< "$changed"
|
||||
|
||||
if [ ${#violations[@]} -eq 0 ]; then
|
||||
echo "✓ Scope check passed — all changed files are in scope."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✗ SCOPE VIOLATION — the following files are outside the declared scope:"
|
||||
for f in "${violations[@]}"; do
|
||||
echo " - $f"
|
||||
done
|
||||
echo ""
|
||||
echo "Either justify the edit or revert the change."
|
||||
exit 1
|
||||
Reference in New Issue
Block a user