igneum/docs/plans/history-rewrite.md
igneum-labs 09c2634d2c Pre-public scrub, second pass (7 October 2026, 20:0x UK, main's rulings 2 and 4): the public tree names igneum-labs only; the public ledger generated from the full ledger
Ruling 2: every tracked text mention of the login's pre-rename spelling becomes igneum-labs (29 files); the commit identity in the scripts becomes igneum-labs <337424239+igneum-labs@users.noreply.github.com> (the same noreply id); the three scripts that hand the stored gh keyring name to gh (register.sh, fetch-ci-artifacts.sh, ship-app.mjs) read it from ~/.config/igneum/gh-user (never in the repository), default igneum-labs; fresh-repo.sh reads the login to rewrite from the encoded list's row 10; the old spelling joins the founder-strings list, so no tracked file may spell it; CLAUDE.md's GitHub paragraph rewritten. The red watcher's self-test fixtures use a neutral author.

Ruling 4: docs/ledger-public.md, one row per ledger item (id, the claim in one line, status, what was done in one line, the evidence link or the evidence in words), generated by tools/ledger/export-public.mjs from docs/fud-ledger.md: no round or status-update sections, no commit ids, no time of day, no lane, agent or branch names. 190 items. The gate runs its self-test (a fixture with a commit id, a time, a lane and a branch name comes out clean; --check fails on drift) and its --check on every merge; the founder check reads the output like every tracked file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-07 18:56:08 +00:00

121 lines
12 KiB
Markdown

# 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 | the other business 6, the earlier entity 5, the earlier 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 <dir> git-filter-repo`, run as `python3 <dir>/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 <repo> 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>==>***INTAKE-KEY-REMOVED***`; `literal:<dl token>==>***DL-TOKEN-REMOVED***`; the two personal `Name <email>` 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)(?<!igneum-)\bfirst\b==>[user]` (the lowercase user-name form in Windows and WSL paths, added after dry run 1 left 20 lines); `regex:(?i)\b<second login>\b==>[second-owner-login]`; `regex:(?i)\b(the other business\|the earlier entity\|the earlier business\|another brand\|another brand)\b==>[other-business]` |
| `messages.txt` (commit messages) | the first-name rules and the second-login rule |
| `mailmap` | both personal identities to `igneum-labs <337424239+igneum-labs@users.noreply.github.com>` |
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<intake key>` | 8 commits | 0 |
| `git log -S<dl token>` | 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\<first>` 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)(?<!igneum-)` rule closed them in pass 2.
What the pass does NOT do, by design, and must be done by hand or by the owner:
| Gap | Why | Who |
|---|---|---|
| The standing login `igneum-labs` carries the first name inside it, in every commit's author line and in every file that names the login | A login is a GitHub setting, not a text rule: renaming it is one setting, the numeric noreply id stays, then one more mailmap line (`<new> <337424239+<new>@...> <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-<name> <branch>` |
| 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 |