Files
rune/scripts/check_gitignore_hygiene.py
T
Nick 834eece4d7 feat(runtime+facade): batch landing — receipt presentation, compaction, composer depth, themes, Sleight wiring, gitignore hygiene, taxonomy
- K27 receipt presentation: full decision+evidence+effect packages,
  expandable sections, UNKNOWN-never-success in every dimension state,
  spine verdicts via evidence.Evaluate (read-only import)
- 2.6-depth compaction: append-per-message (O(1) tail line, 198 bytes
  steady-state), compaction every 64 appends (512/channel hot), cool
  archives per memory-continuity contract, nothing deleted, exact-by-ID
  across compaction+restart+tamper, legacy checkpoints heal on load
- K27 composer depth: 3-line input per old TUI contracts (§2.3 history
  walk, placeholder, stash/restore), deterministic natural-language
  resolution (40+ pinned phrases), palette natural-tier + ctrl+p bind,
  Accessibility profile (keyboard-only/no-color/reduced-motion)
- K27 themes: 10 fixed Go theme tables (byte-exact from Textual 8.2.8,
  not guessed; nord/dracula-default/monokai/solarized/catppuccin/tokyo-
  night/flexoki/gruvbox), /theme command (mirrors _cmd_theme exactly),
  live-editor data shape (staged per-token preview overrides), base bg
  rgb(30,30,40), no-drift drift test anchoring Go to retiring Python
- Sleight live wiring: operator._context_broker registers seventh
  contributor 'continuity' (bucket 2, fail-closed); K16 allowlist
  amended (memory_continuity.working_set); matrices now run against
  the live road; 102 tests green
- .gitignore hygiene: directory-level traps fixed (internal/ re-includes
  .go/.json/.md only; root-anchored /index/ /journal/ /state/; fixtures
  + testdata re-included at any depth); 6-test frozen gate; unified_check
  member wired; real git add (no -f) proven on testmod/internal
- Receipts, compaction, composer, themes, sleight-wiring, hygiene all
  under ADMIT contracts (101+ contracts, 0 research-gate violations)
- main.go NOT landed (chat-live P1 wave owns it, still in flight)
2026-10-17 22:00:03 +01:00

166 lines
6.0 KiB
Python

#!/usr/bin/env python3
"""check_gitignore_hygiene.py — gitignore hygiene gate (wave-gitignore-hygiene).
RED when the .gitignore rules regress into the force-add era traps:
1. a probe source file under src/go/testmod/internal/ is ignored or
unaddable without -f (.go/.json/.md must be addable; nested
subpackage dirs too) — the trap behind the modegate binary
fabrication, the K9 sweep accident and the 102MB cache re-lands;
2. a probe binary under src/go/ IS addable (no binary intent may leak
through the re-include rules — binaries stay ignored at module
level AND inside subpackage dirs);
3. new files under the tracked source dirs memory/index/ and
memory/journal/ are swallowed by the cwd-local runtime dir rules
(index/ journal/ must be anchored to the repo root);
4. committed fixture stores (fixtures/, testdata/) lose addability
(pinned on real tracked files).
The gate builds a scratch module (src/go/testmod), probes it with real
git commands (check-ignore / status / add --dry-run), and removes the
scratch afterwards. Usage:
python scripts/check_gitignore_hygiene.py [--repo-root PATH]
Exit 0 = VERDICT: GREEN; 1 = RED with the violation list.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
SCRATCH = Path("src/go/testmod")
SCRATCH_FILES: dict[str, bytes] = {
"go.mod": b"module testmod\n\ngo 1.21\n",
"main.go": b"package main\n\nfunc main() {}\n",
# module-level compiled artifact: must stay ignored
"testmod-bin": b"\x7fELF-module-level-binary-probe",
"internal/probe.go": b"package internal\n",
"internal/probe.json": b"{}\n",
"internal/probe.md": b"# probe\n",
"internal/deep/nested.go": b"package deep\n",
# binary inside a subpackage dir: must stay ignored (no binary intent)
"internal/probe.bin": b"\x7fELF-internal-binary-probe",
# committed fixture store: must be addable at module level
"testdata/entry.txt": b"fixture input\n",
}
MUST_BE_ADDABLE = [
"src/go/testmod/go.mod",
"src/go/testmod/main.go",
"src/go/testmod/internal/probe.go",
"src/go/testmod/internal/probe.json",
"src/go/testmod/internal/probe.md",
"src/go/testmod/internal/deep/nested.go",
"src/go/testmod/testdata/entry.txt",
]
MUST_BE_IGNORED = [
"src/go/testmod/internal/probe.bin",
"src/go/testmod/testmod-bin",
]
# sweep pins: new files in these TRACKED source dirs must not be swallowed
# by the cwd-local runtime dir rules (index/ journal/).
MUST_BE_ADDABLE_HYPOTHETICAL = [
"memory/journal/__gitignore_probe__.jsonl",
"memory/index/__gitignore_probe__.json",
]
# fixture-store pins on real tracked files (fixtures/ + testdata/ stay
# fully addable, including non-source extensions like .jsonl/.txt).
MUST_BE_ADDABLE_TRACKED = [
"src/go/rune-entry/internal/entry/testdata/runtime/crash-replay/effects.jsonl",
"src/go/builder-bridge/fixtures/store_active/segments/observations.jsonl",
]
def git(root: Path, *args: str) -> subprocess.CompletedProcess:
return subprocess.run(["git", "-C", str(root), *args],
capture_output=True, text=True)
def make_scratch(root: Path) -> None:
for rel, content in SCRATCH_FILES.items():
p = root / SCRATCH / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_bytes(content)
def remove_scratch(root: Path) -> None:
subprocess.run(["rm", "-rf", str(root / SCRATCH)], check=False)
def ignored(root: Path, path: str) -> bool:
"""True when gitignore rules match the path (exit 0 = ignored)."""
return git(root, "check-ignore", "-q", "--", path).returncode == 0
def sweep_add_lines(root: Path, target: str) -> list[str]:
"""What `git add -n <target>` would stage — the wave-realistic form
(directory adds / status sweeps are where the old trap bit)."""
r = git(root, "add", "-n", "--", target)
return [l.strip() for l in r.stdout.splitlines() if l.startswith("add ")]
def collect_violations(root: Path) -> list[str]:
violations: list[str] = []
try:
make_scratch(root)
for path in MUST_BE_ADDABLE:
if ignored(root, path):
violations.append(f"source not addable without -f: {path}")
for path in MUST_BE_IGNORED:
if not ignored(root, path):
violations.append(f"binary intent leaked (addable): {path}")
status = git(root, "status", "--porcelain", "--", str(SCRATCH))
if SCRATCH.as_posix() not in status.stdout:
violations.append(
f"new module {SCRATCH}/ invisible to git status (dir pruned "
"by ignore rules — sweeps silently skip whole modules)")
added = sweep_add_lines(root, str(SCRATCH))
for path in MUST_BE_ADDABLE:
if f"add '{path}'" not in added:
violations.append(f"directory-form add skips source: {path}")
for path in MUST_BE_IGNORED:
if f"add '{path}'" in added:
violations.append(f"directory-form add sweeps binary: {path}")
for path in MUST_BE_ADDABLE_HYPOTHETICAL + MUST_BE_ADDABLE_TRACKED:
if ignored(root, path):
violations.append(
f"tracked source dir swallowed by runtime rule: {path}")
finally:
remove_scratch(root)
return violations
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description="gitignore hygiene gate")
ap.add_argument("--repo-root",
default=str(Path(__file__).resolve().parent.parent))
args = ap.parse_args(argv)
root = Path(args.repo_root).resolve()
violations = collect_violations(root)
if violations:
print("RED: gitignore hygiene violations:")
for v in violations:
print(f" - {v}")
print("VERDICT: RED")
return 1
print("GREEN: src/go sources addable without -f, binaries ignored, "
"memory/ source dirs + fixture stores unswallowed")
print("VERDICT: GREEN")
return 0
if __name__ == "__main__":
sys.exit(main())