Seed an AI-legible project memory map that stays small

One file (AGENTS.md) that bootstraps the rest: rules, the file map to create,
what belongs in each file, the first-session interview order, and the list of
anti-patterns that killed the previous map (1,308-line guide, 63 task files,
narrative history inline, sessions skimming 56k tokens of context).

Separates four kinds of content by read frequency:
  rules/invariants  -> AGENTS.md          (every session)
  subsystem truth   -> docs/ai/<topic>.md (only the matching one)
  lookups           -> environment.md     (grepped, never read)
  retired decisions -> decisions.md       (only when re-opening one)

Caps are enforced, not promised: scripts/verify.sh checks doc budgets before
the build, so a commit that pads a capped file fails. Proven both ways — it
passes clean and exits 1 on an over-cap file.

scripts/scope-check.sh blocks edits outside the declared file list.
templates/ holds fill-in skeletons for the seven files a project must create.

CC0: copy it, rename it, sell it, no attribution needed.
This commit is contained in:
2026-10-30 06:51:19 -07:00
commit b5e3dbe614
14 changed files with 559 additions and 0 deletions
+57
View File
@@ -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
+81
View File
@@ -0,0 +1,81 @@
#!/usr/bin/env bash
# verify.sh — the one gate before committing: doc budgets + clean build + tests + scope check.
# Usage: ./scripts/verify.sh "path/changed1" "path/changed2" ...
#
# Why a CLEAN build? An incremental build skips recompiling, so a "0 warnings" report can be a lie
# — a nullable warning slipped past exactly that way once. Clean first, then build.
#
# CONFIGURE the three commands below for the project. Empty means "skip with a warning", so a
# docs-only repo works out of the box.
set -euo pipefail
CLEAN_CMD="" # e.g. 'dotnet clean App.csproj'
BUILD_CMD="" # e.g. 'dotnet build App.csproj'
TEST_CMD="" # e.g. 'dotnet test Tests/Tests.csproj'
WARN_PATTERN=": warning" # what a compiler warning looks like in BUILD_CMD output
KNOWN_FLAKY="" # regex of pre-existing flaky test names; anything else fails the gate
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
echo "══ 1/4 DOC BUDGETS ══"
# The map stays cheap to read. A commit that PADS a capped file instead of moving detail out
# fails here (see AGENTS.md -> Reading budget).
check_budget() {
local rel="$1" cap="$2" lines
[ -f "$ROOT/$rel" ] || { echo " ⚠ $rel not built yet — skipped"; return 0; }
lines=$(wc -l < "$ROOT/$rel")
if [ "$lines" -gt "$cap" ]; then
echo "✗ $rel is $lines lines (cap $cap)."
echo " Move the detail to docs/ai/<topic>.md, MyMistakes.md, decisions.md or git log."
return 1
fi
printf " ✓ %-12s %3s/%s lines\n" "$rel" "$lines" "$cap"
}
budget_failed=0
check_budget "AGENTS.md" 210 || budget_failed=1
check_budget "ai.md" 100 || budget_failed=1
check_budget "HANDOFF.md" 40 || budget_failed=1
check_budget "TASKS.md" 120 || budget_failed=1
[ "$budget_failed" -eq 0 ] || exit 1
echo "══ 2/4 CLEAN BUILD (expect 0 warnings) ══"
if [ -z "$BUILD_CMD" ]; then
echo "⚠ BUILD_CMD not configured — build step skipped."
else
[ -z "$CLEAN_CMD" ] || eval "$CLEAN_CMD" > /dev/null
OUT="$(eval "$BUILD_CMD" 2>&1)"
if echo "$OUT" | grep -qI "$WARN_PATTERN"; then
echo "✗ WARNINGS FOUND:"
echo "$OUT" | grep -i "$WARN_PATTERN" | sort -u
exit 1
fi
echo "$OUT" | tail -3
echo "✓ Build clean."
fi
echo "══ 3/4 TEST SUITE ══"
if [ -z "$TEST_CMD" ]; then
echo "⚠ TEST_CMD not configured — test step skipped."
else
set +e
RUN="$(eval "$TEST_CMD" 2>&1)"
EXIT=$?
set -e
echo "$RUN" | tail -5
if [ "$EXIT" -ne 0 ]; then
if [ -n "$KNOWN_FLAKY" ] && ! echo "$RUN" | grep -iE "fail" | grep -qvE "$KNOWN_FLAKY"; then
echo "⚠ Failures, but all match the known-flaky whitelist (OK)."
else
echo "✗ TEST FAILURES — investigate."
exit 1
fi
fi
fi
echo "══ 4/4 SCOPE CHECK ══"
"$ROOT/scripts/scope-check.sh" "$@"
echo ""
echo "✓ All gates passed — safe to commit."