Skip to content

2026-04-28: The Clean Stage

Pencil sketch on a dark void — a bare stage lit by one sharp line, three immaculate boxes below it labeled committed and gitignored, the lower half marked no third state, a soft teal halo at the dividing line

Run git status in ~/.sanctum. The terminal scrolls, and keeps scrolling. 1,827 untracked items sat in that one repo at the start of the cleanup — logs, metrics, build artefacts, and, buried in the noise, two files that made us stop cold. One was a voipms-secrets.sh. The other was an api_key.py that had ridden in from a vendored library. Neither belonged in git. Both had been sitting there for weeks, a single git add -A away from a public mirror.

That backlog didn’t announce itself. The 2026-04-27 colima→OrbStack and pressure-valve phase-4 commits closed two long-running runtime gaps, and in doing so they surfaced a deeper one: every tracked sanctum repo had been quietly accumulating untracked files for weeks. Most were rolling state that should have been gitignored from the start. Some were real new content that needed triage. And a handful were the kind of thing Windu loses sleep over — secrets that should never be in git at all.

You could clean each repo by hand. That is the wrong answer, because a hand-cleaned repo drifts again by month two. The right answer is a mechanism that catches drift on day one and never gets bored of the job.

If it’s not in git, it’s either gitignored or it doesn’t exist. No third state.

Untracked-but-not-ignored is a bug, not a tolerated background condition. It is the same shape as the pressure-valve trilogy redux: a thing that completes successfully (“untracked file added”) but doesn’t change the underlying signal (“no one knows about it”), repeated forever until it buries a secret in plain sight.

Three layers enforce the doctrine. None of them trusts you to remember.

~/.sanctum/templates/sanctum-gitignore.template is the single source of truth for sanctum-wide ignore patterns: OS noise, editor state, Python build/runtime, pnpm/node_modules, Rust target trees, manual snapshots (*.bak, *.pre-*, *.stale-*), rolling runtime state (logs, metrics, audit), local databases, secrets (keys, env, credentials/, secrets/, .ssh, .aws), apple-platform local state. The block lives between two markers (sanctum-gitignore-managed begin/end) so per-repo additions remain intact below.

~/.sanctum/scripts/sanctum-gitignore-sync.py propagates the canonical block into every tracked sanctum repo’s .gitignore. Re-running the sync replaces the block in-place — no duplication, no drift between repos. After today’s first sync, the block is identical across all seven tracked repos. Jocasta would call it a filing system; we call it the thing that keeps git status honest.

The template stops new drift. It does nothing about what already slipped through, so we gave the haus a night watchman. ~/.sanctum/scripts/sanctum-drift-sentinel.py runs daily at 06:30 ET via com.sanctum.git-drift-sentinel. It walks every tracked sanctum repo, lists git ls-files --others --exclude-standard, and classifies each untracked path:

ClassTriggered byOutcome
sensitivebasename matches secret patterns (.env*, *.key, id_rsa, *api_key*, *secret*, credentials/, secrets/, .aws/, .ssh/) — but excluding library paths (node_modules/, site-packages/, vendor/, target/, .venv/, .cache/, .tox/)RED alert in ~/.sanctum/alerts.json; exit 2
automatches well-known auto-ignore patterns (*.bak, *.stale-*, *.pre-*, node_modules/, __pycache__/, .DS_Store, **/target/, staging/) — already covered by template, listed for awarenesslogged, no alert
revieweverything else — real new content the human must triageyellow alert; exit 1

State at ~/.sanctum/state/git-drift.json. It is brevity-gated the way Tommy would insist: a clean run prints one OK line and writes no alert, so a quiet morning stays quiet. Drift produces a structured report.

The sensitive classifier is intentionally narrow at the basename level, and that narrowness is the whole art. fastapi/security/api_key.py is a vendored library file, not your secret — the LIBRARY_PATH exclusion catches it. A top-level api_key.txt is your secret — the SENSITIVE_BASENAME regex catches it. Both calls are right, and a classifier that cried wolf on every library path would teach you to ignore the one time it mattered.

~/.sanctum/hooks/pre-commit is the canonical hook. ~/.sanctum/scripts/sanctum-hooks-install.sh symlinks it into every tracked sanctum repo’s .git/hooks/ (worktree-aware via git rev-parse --git-path hooks). Per-repo custom hooks aren’t overwritten — the installer skips with a warning.

Behavior on every commit:

  • Lists untracked files (git ls-files --others --exclude-standard).
  • Sensitive matches → refuse with exit 1. Bypassable via --no-verify for emergencies; the bypass is intentional friction, not a back door.
  • Other untracked → warn with the file list, but allow the commit. The warning is the prompt: “decide what these are.”
  • Library paths excluded as in the sentinel.
  • Portable bash 3.x (macOS default ships without mapfile; the hook uses a read-loop instead).

We tested it in a sandbox repo before trusting it anywhere real: .env refused with rc=1; newfile.txt warned with rc=0 and the commit proceeded. The hook does not moralize. It just stands at the door.

Seven repos now share the canonical gitignore block at the top of their .gitignore:

  • ~/.sanctum
  • ~/.openclaw
  • ~/.openclaw/skills
  • ~/Projects/sanctum-rs
  • ~/Documents/Claude_Code/sanctum-docs
  • ~/Projects/openclaw-skills
  • ~/Projects/yoda-voice-agent

The ~/Projects/yoda-voice-agent repo dropped from 10,782 untracked files to 1 after the template covered .tts-venv/. The other six show only real triage candidates — content that genuinely needs a human verdict.

That is the clean stage the title promises, and it borrows the same instinct as the capacity doctrine: each repo now answers for exactly its own air. The noise is swept off, so the one file that still needs your judgment is the only thing left standing in the light.

  • Triage the surfaced backlog. The first sentinel run flagged 309 files in ~/.sanctum, 9 in openclaw-skills, 1 in sanctum-rs, 1 in yoda-voice-agent, and 1 sensitive (voipms-secrets.sh). Each needs a verdict: commit, ignore, or rm. The sentinel will keep flagging them until you decide.
  • Wire morning-briefing integration. Today the alert lands in alerts.json; tomorrow it should fold into Yoda’s dawn briefing so quiet days stay quiet and drift days surface as one terse line.
  • Watchdog manifest. Add git-drift-sentinel to the watchdog’s expected service list so it self-heals if the LaunchAgent fails to load after a reboot.