# G14: the history rewrite, exact plan and dry-run result (4 October 2026, night) Internal. Extends `docs/fud-fixes.md` section 5 (step 4) with the exact commands, what the dry run showed, what breaks, and the order for the morning. Nothing here has touched the real repository: the dry run ran on a throwaway mirror clone under the session scratchpad and nothing was pushed. The owner is not named in this file; "the first name" and "the login" stand for the values the script reads from the history itself. ## 1. What the history holds today (counts from the real repository, 4 October 2026, 22:30 UTC) | Item | Count | Where | |---|---|---| | Commits | 363 on all branches | | | Commits stamped `+0100` (author or committer) | 291 of 363 | the UK or Irish summer offset; 72 are `+0000` | | Commits authored with the personal name | 40 (31 on the old GitHub noreply address, 9 on the personal address) | the commits before the 3 October identity rule | | Commits as the standing login `igneum-labs` | 323 | | | The intake key | 6 tracked files, 8 commits (`78df757` to `4c9810f`) | `packaging/mac/packaged-config.sh`, `infra/gpu-bench/upload.sh`, `proving/windows-wsl2/prove-block.sh`, `prove-shard.sh`, `proto-cuda/windows-miner/upload-log.bat`, `proto-cuda/windows-app/upload-log.bat` | | The dl token | 1 tracked file, 1 commit (`c47ff03`) | `docs/plans/morning-2026-10-04.md` | | The `.next` rotations of both | 0 files, 0 commits | `~/.config/igneum/log-intake-key.next`, `dl-token.next` (4 October 19:25) are not in the tree | | The relay key and token (current and old) | 0 files, 0 commits | | | The review files | `docs/fud-ledger.md` (36 commits from `39c20b7`), `docs/fud-fixes.md` (6 from `e7545d5`), `docs/review/` (4 from `5ab296c`), `site/ledger.html` (5 from `0ec11be`) | tracked, not ignored | | Tracked files carrying the first name (case-insensitive) | 71 at HEAD; 93 commits touch such content; 10 commit messages carry it | `CLAUDE.md`, the agent file, plans, packaging, the app's WSL paths, the Chrome profile rule | | The surname | 4 files at HEAD | | | The other businesses' names, the registrar, the database id, home paths | [other-business] 6, [other-business] 5, [other-business] 4, godaddy 7, soft-voice 3, `/Users/` 22, quantum 4 | identity terms are rewritten by the history pass below; providers and paths are the public-export scrub's job (`tools/ci/forbidden-strings.txt`), not this pass | ## 2. The rewrite, exactly Tool: `git-filter-repo` 2.47.0 (not installed on the Mac; the dry run used a pip install into the scratchpad, `python3 -m pip install --target git-filter-repo`, run as `python3 /git_filter_repo.py`). It refuses to run on anything but a fresh clone, which is the safety the plan relies on. The script is `dryrun.sh` in the scratchpad (`rewrite/`); it reads every value from the history and from `~/.config/igneum` at run time and writes the replacement files with mode 0600, then deletes them. The one invocation, with the files it writes: ``` git clone --mirror clone && cd clone python3 git_filter_repo.py --force \ --invert-paths --path docs/fud-ledger.md --path docs/fud-fixes.md --path docs/review --path site/ledger.html \ --replace-text replace.txt \ --replace-message messages.txt \ --mailmap mailmap \ --commit-callback ' for attr in ("author_date", "committer_date"): d = getattr(commit, attr); parts = d.split(b" ") if len(parts) == 2 and parts[1] != b"+0000": setattr(commit, attr, parts[0] + b" +0000") ' ``` | File | Lines (values never written in this plan) | |---|---| | `replace.txt` (blob text) | `literal:==>***INTAKE-KEY-REMOVED***`; `literal:
==>***DL-TOKEN-REMOVED***`; the two personal `Name ` strings to the standing login string; the personal email and the old noreply address to `[removed]`; `regex:\bFirst's\b==>the project lead's`; `regex:\bFirst\s+Last\b==>the project lead`; `regex:\bFirst\b==>the project lead`; `regex:\bLast\b==>[removed]`; `regex:(?i)(?[user]` (the lowercase user-name form in Windows and WSL paths, added after dry run 1 left 20 lines); `regex:(?i)\b\b==>[second-owner-login]`; `regex:(?i)\b([other-business]\|[other-business]\|[other-business]\|[other-business]\|[other-business])\b==>[other-business]` | | `messages.txt` (commit messages) | the first-name rules and the second-login rule | | `mailmap` | both personal identities to `igneum-labs <337424239+[removed]>` | The date callback keeps the instant and rewrites the offset to `+0000`, so no commit moves in time; only the `+0100` fingerprint goes. `--invert-paths` drops the four internal files from every commit, which empties the commits that touched nothing else; filter-repo prunes those. ## 3. The dry run (two passes on the mirror clone, 4 October 2026, 22:35 to 22:55 UTC) | Check | Before | After pass 2 | |---|---|---| | Commits | 364 in the mirror (363 plus the in-progress branch head) | 312: the 52 commits that only touched the dropped files are gone | | Author and committer identities | 3 | 1: the standing login on all 312 | | Timezone offsets (author and committer, 624 stamps) | 291 x 2 `+0100` | 624 `+0000` | | `git log -S` | 8 commits | 0 | | `git log -S
` | 1 commit | 0 | | Commits touching the four dropped files | 51 | 0 | | Identity grep over every blob in the history (first name outside the login, surname, second login, personal addresses, the other businesses; case-insensitive) | thousands of lines | 0 lines | | Identity grep over commit metadata (names, addresses, subjects, bodies) | | 0 lines | | `CLAUDE.md` line 4 after the pass | the full name | "the project lead's project, started 3 October 2026" | | Runtime | | 2 min 58 s for the filter, 3 min 15 s with the greps | Pass 1 (case-sensitive name rules only) left 20 blob lines and 2 message lines: the lowercase user-name form in `C:\Users\` and WSL paths in `app/igneum-app/src/jobrun.rs`, `prover.rs`, `docs/plans/shard-test-pc2.md`, `packaging/README-ship.md`, `packaging/ota/publish-jobs.sh`, `relay/playbooks/shard-test.ps1` and the Chrome-profile line of `CLAUDE.md`. The `(?i)(? <337424239+@...> <337424239+igneum-labs@...>`) and one more replace rule (`igneum-labs` to the new login) go into the same pass | the owner (rename), then the script | | `CLAUDE.md` as a public file (section 5 step 2 of `docs/fud-fixes.md`: the registrar, the database id, the browser-profile section, the tooling links) | The pass replaces names; it does not rewrite paragraphs. The scrubbed `CLAUDE.md` of step 2 replaces the file in every commit with `--path-rename` or a blob callback once it exists | Claude, after the owner approves the public text | | The second owner login is still an organisation owner | GitHub setting (decision e: one anonymous owner) | the owner | | Providers, hosts, home paths, machine names | the public-export scrub (`tools/ci/forbidden-strings.txt`, `igneum-public/tools/sync.sh`); the private repository keeps them until the public date | the export | ## 4. What breaks when the rewrite is applied for real | What | Why | Recovery | |---|---|---| | Every worktree of the main checkout (16 today: `igneum-wt-appui`, `bughunt`, `buildjob`, `devfee`, `eff`, `finality`, `latency`, `perf`, `redteam`, `release`, `reliability`, `ship`, `site`, `wallet`, `testnet`, plus two under the scratchpad) | Their HEADs point at old commit ids that no longer exist in the rewritten history; `git status` still works on the old objects, `git pull` and `git rebase` do not | Each agent commits and pushes its branch before the freeze; after the rewrite every branch is re-created from the rewritten refs: `git worktree remove`, `git worktree add ../igneum-wt- ` | | Agents' branches (15 local, 11 on origin) | Rewritten with everything else (the mirror clone carries every ref), so the branch names survive with new ids; an agent that keeps an old local branch will have diverged from its rewritten twin by every commit | No agent commits during the freeze; after it, every agent re-creates its worktree, never merges an old-id branch into a new one | | Open pull requests, if any | Their base and head ids vanish | None open today (the project merges by hand); check `gh pr list` before the freeze | | The Vercel GitHub integration (`igneum` project, deploys on push to master) | The integration links by repository id, not by commit, so it survives a force-push; the first push of the rewritten `master` triggers one deploy of the same site (the public tree is unchanged by the pass except the dropped `site/ledger.html`, already a 307 redirect) | Watch the deploy; nothing to relink. If the repository is re-created instead (section 5, option B), the integration is re-linked once in the Vercel project settings | | The `windows-ci` and `ci` workflows | Run on the rewritten push like any push; the DL_TOKEN secret is a repository setting and survives | Re-set the secrets if the repository is re-created | | Old commit ids in documents (`docs/bench-log.md`, plans, the ledger) and in the public export | They name commits that will not exist; filter-repo writes `commit-map` (old id to new id) in `.git/filter-repo/` and rewrites ids it finds in commit messages, not in files | Keep `commit-map` with the private notes; the bench log keeps its short ids as historical labels (the public export already strips the history) | | GitHub's copies of the old objects | A force-push does not delete them from GitHub's object store; cached PR views, old commit URLs and forks keep serving them until GitHub runs a garbage collection, which support can be asked to do | Option B below removes the question | | The fork worktrees under `vendor/` | Separate repositories (`vendor/` is gitignored); untouched | Nothing | | The intake key and the dl token | Removing them from the history does not revoke them; every shipped package and every installed app carries the current key | Rotate first (the `.next` values exist since 4 October 19:25): new key in `relay/` and in `packaging/mac/packaged-config.sh`, repackage, republish; the old key keeps working for installed apps until they update, then dies | ## 5. The order of operations for the morning 1. Rotate the secrets: switch the relay and the intake to `log-intake-key.next`, the downloads folder to `dl-token.next`, repackage the Mac and Windows apps with the new values, publish, confirm an upload lands under the new key. Then the old values in the history are dead values. 2. The owner renames the login `igneum-labs` (GitHub settings; the noreply id 337424239 stays), confirms the second login is no longer an organisation owner, and approves the public `CLAUDE.md` text (section 5 step 2). 3. Freeze: every agent commits and pushes its branch, then stops; `gh pr list` must be empty; `git worktree list` is recorded. 4. Mirror clone, run the pass (section 2) with the two extra lines from step 2 and the scrubbed `CLAUDE.md` blob; the greps of section 3 must all read 0; keep `commit-map`. 5. Choose A or B. A: `git push --mirror` from the clone to the existing repository, then ask GitHub support to purge the unreachable objects. B (the route `docs/fud-fixes.md` step 4 prefers): create a fresh repository under the organisation, push the rewritten refs there, re-link Vercel and re-set the two secrets, archive the old repository private. B leaves no old object anywhere. 6. Re-clone the main checkout from the new history; every agent re-creates its worktree from its rewritten branch. 7. `TZ=UTC` on every path that commits: the agents' shells, the ship scripts, the relay; and `git config --global` cannot set a timezone, so the rule is in the environment. The CI identity grep and `git log --format='%ad' --date=raw | grep -c +0100` become the daily check (0 is the goal). 8. The public export (`igneum-network/spec`) is unaffected: it carries no history from this repository. ## 6. What waits for the owner | Decision | Options | |---|---| | The new login name | any handle without a name | | A or B in step 5 | B recommended | | The public `CLAUDE.md` text | section 5 step 2 of `docs/fud-fixes.md` | | The day | after step 1; before the public date in every case |