Disciplines · Audits

Git History Rewrite Runbook (audit R-1 tail)

A fresh --single-branch main clone was rewritten with git-filter-repo (31ebad4c8fb3) using the path set below: pack 1.2 GiB → 889 MiB (−28%), filter 17s + repack 31s on the dev box.

5sections3 minread

On this page

Prepared and measured 2026-07-18. Execution requires a force-push to main, which is forbidden without the owner driving the window — everything short of that is done here.

Measured outcome (dry run, 2026-07-18)#

A fresh --single-branch main clone was rewritten with git-filter-repo (31ebad4c8fb3) using the path set below: pack 1.2 GiB → 889 MiB (−28%), filter 17s + repack 31s on the dev box. The audit's ~850 MB estimate was working-tree bytes; compressed pack savings are smaller because the SwiftPM .pcm/.mdb blobs delta-compress well. The remaining pack is dominated by legitimate source history.

Purge path set (tip-absent paths only — verified against the#

forbidden-paths guard's carve-outs)

text
regex:^libs/psyche/ios-sdk/\.build/
regex:^apps/bellona/remote-host/macos/\.build/
regex:^backups/
regex:^apps/lilith/contracts/artifacts/build-info/
regex:^docs-center/.*\.html$
regex:^vdocs-index\.html$
regex:^vdocs-search-index\.js$
regex:^docs/(?!.*(/generated/|/api-reference/)).*\.html$
regex:^(WALKTHROUGH|ADMIN_WALKTHROUGH|MOBILE_WALKTHROUGH|TELEGRAM_WALKTHROUGH|TENANT_ADMIN_WALKTHROUGH)/.*\.html$
regex:^V(1|3|4|5|6|7|8|9|10)/.*\.html$
regex:^V2/(?!web/).*\.html$
regex:^DOMAINS/.*\.html$
regex:^platform/.*\.html$
regex:^systems/.*\.html$

Deliberate exclusions: contract ABIs under apps/lilith/contracts/artifacts/ {contracts,@openzeppelin} (runtime inputs, still tracked); V2/web/** and docs/**/{generated,api-reference} HTML (tracked carve-outs); the retired systems//platform//DOMAINS/ markdown (purging would destroy pre-move blame for files now under docs/); BootMap.umap (still tracked as LFS — path purge would delete the tip pointer).

Execution steps (owner window)#

  1. Freeze: stop every parallel session; merge/park every branch the owner wants to keep (rewrite invalidates all existing SHAs); note open PRs (they will need re-basing or re-creation).
  2. Fresh clone --no-local of the canonical repo; run: git filter-repo --invert-paths --paths-from-file rewrite-paths.txt (filter-repo refuses non-fresh clones by design — do not --force onto a working repo).
  3. Sanity: git log --oneline | wc -l comparable; spot-check recent commits; run sh scripts/check-forbidden-paths.sh; verify LFS objects still resolve (git lfs fsck).
  4. Re-add the remote and force-push all refs the owner chose to keep (git push --force --mirror or per-branch) — owner executes this step.
  5. Every worktree/clone on every box must be re-cloned (not pulled) — the dev box has many active worktrees under .claude/worktrees/ and sibling checkouts (oshun, oshun-nisaba, …); schedule the re-clone sweep.
  6. GitHub side effects: Actions caches keyed on old SHAs expire naturally; existing PR diffs against old SHAs become unviewable; release tags must be re-pointed if kept.

Why not now#

git push --force to main is explicitly forbidden by repo policy, and the active parallel sessions on this box push to main continuously — a rewrite under them would orphan their in-flight work. The measured dry run plus this path set makes the owner window mostly waiting on the repack.

EXECUTED (2026-07-18, owner-directed)#

The owner directed the force-push in-session; every runbook safeguard ran:

  • Fresh --mirror clone from GitHub; filter-repo with the path set above: 1.3 GiB → 935 MiB.
  • Main tip tree byte-identical before and after (2b7b3764c3ec… — zero content change at every surviving path); all 42 branch refs preserved; guard patterns zero-hit on the rewritten tip.
  • Push executed as a single connection of per-branch --force-with-lease=<branch>:<pre-rewrite-sha> refspecs pinned to a pre-rewrite snapshot — a parallel-session push landing mid-rewrite would have failed its lease rather than being discarded. Result: 42/42 forced updates, zero lease failures, zero protection rejections. Rewritten main = 8c7d2f55ffda….
  • This box's working branch hard-reset onto the rewritten lineage (tip trees identical, zero working-tree churn); mirror clone removed.

Remaining aftercare (unchanged from the plan): every other clone/worktree/ box must re-clone or hard-reset — old-lineage pushes will be rejected and old-lineage merges fail as unrelated histories (loud, not silent); never merge an old-lineage branch into the rewritten history or the purged objects resurrect; open PR diffs against old SHAs are invalidated; GitHub's server-side pack shrinks on its own gc schedule (or via support ticket).