From db179ad98afd56621bf3a3d06934549045b8a258 Mon Sep 17 00:00:00 2001 From: boban Date: Mon, 15 Jun 2026 19:30:23 +0200 Subject: [PATCH] docs(spec): de-Claude repo + git-history rewrite runbook (beta-prep #2) Deferred runbook for the final beta-prep step: purge CLAUDE.md, rules.md, handoff.md, docs/session-handoff.md and docs/superpowers from all history, strip Co-Authored-By trailers, keep the files locally + gitignored, then a gated force-push. Execute at beta-cut time only, behind explicit confirmation. Co-Authored-By: Claude Opus 4.8 (1M context) --- ...-06-15-de-claude-history-rewrite-design.md | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-15-de-claude-history-rewrite-design.md diff --git a/docs/superpowers/specs/2026-06-15-de-claude-history-rewrite-design.md b/docs/superpowers/specs/2026-06-15-de-claude-history-rewrite-design.md new file mode 100644 index 0000000..08e176b --- /dev/null +++ b/docs/superpowers/specs/2026-06-15-de-claude-history-rewrite-design.md @@ -0,0 +1,189 @@ +# de-Claude the repo + git history (beta-prep #2) — Design / Runbook + +**Date:** 2026-06-15 · **Branch:** `feat/v1-foundation` · **Status:** approved, execution DEFERRED to +beta/staging cut · Beta-prep #2 (the final, irreversible roadmap item) + +Strip every trace of AI-assisted development from the repository **and from its entire git history**, +so the codebase that ships to beta/staging looks like an ordinary, human-authored product repo. This +is a **history rewrite + force-push** — irreversible, and it rewrites the shared branch. It runs +**once, last**, after all content is complete and tagged, and **only on the operator's explicit go** +for the force-push. + +> **Timing:** the operator scoped this to beta/staging time ("in der dev Variante ist das nicht so +> schlimm … später wenn es in beta oder staging muss das weg sein"). During dev the AI traces stay. +> This document is the prepared runbook; it is **not** executed now. + +## Operator-approved decisions +- **File scope — purge ALL internal process docs from every commit:** `CLAUDE.md`, `rules.md`, + `handoff.md`, `docs/session-handoff.md`, and the whole `docs/superpowers/` tree. Product code, + `README.md`, `CHANGELOG.md`, `install.sh`, `docker/`, etc. stay. +- **Keep on disk, gitignore:** after purging from git, the files remain on disk (dev keeps using + `rules.md`/`CLAUDE.md` as references); they are added to `.gitignore` so they never re-enter git. +- **Message scrub — trailers only:** strip the 167 `Co-Authored-By: Claude …` trailer lines. Keep all + descriptive subjects/bodies (they describe real changes). The 23 "Codex" / 19 "spec" message + mentions are **left as-is** (operator's call). Commits that only added a now-purged doc become empty + and are pruned automatically. + +## Ground truth (scanned 2026-06-15) +- **170 commits, single author** `boban ` — no "Claude" author/committer identity + to fix. +- **167/170** commits carry a `Co-Authored-By: Claude …` trailer; **0** "Generated with Claude Code" + lines. +- Tracked artifacts to purge: `CLAUDE.md`, `rules.md`, `handoff.md`, `docs/session-handoff.md`, + `docs/superpowers/**` (16 spec/plan files). +- **No** `.claude/`, `.cursor/`, `AGENTS.md`, `.mcp.json` anywhere in history. +- **One branch** (`feat/v1-foundation`) + tags `v0.1.0 … v0.9.2`. No `main`. +- `git-filter-repo` is **not** installed; no `pip3`; `python3` 3.13 is present → use the single-file + script via `python3` (no sudo needed). + +## Tool: git-filter-repo (single-file, no install) +`git filter-repo` is the modern, git-recommended history-rewrite tool (correct tag rewriting, empty- +commit pruning, ref-safe — unlike the deprecated `filter-branch`). It is one self-contained Python +script. Fetch it once into the repo-adjacent tooling dir and run via `python3` — no `pip`, no `sudo`: + +```bash +# from a machine with internet; the script is pure Python, ~250 KB, MIT-licensed +curl -fsSL https://raw.githubusercontent.com/newren/git-filter-repo/main/git-filter-repo \ + -o /tmp/git-filter-repo && chmod +x /tmp/git-filter-repo +python3 /tmp/git-filter-repo --version # sanity +``` +(Alternative if preferred and sudo is available interactively: `sudo apt-get install git-filter-repo`.) + +## Runbook (execute top-to-bottom AT BETA TIME) + +### Phase 0 — preconditions (abort if any fails) +- This is the **last** operation. All feature work merged, all releases tagged, `CHANGELOG` current. +- **Working tree clean** — `git status --short` empty. (In particular the parallel timing-fix + refinement to `app/Livewire/Auth/ForgotPassword.php` must already be committed.) +- Full suite green; app boots (R12). +- No open Gitea PRs against the branch (a force-push invalidates them). Solo repo → expected none. + +### Phase 1 — backup (the safety net for an irreversible op) +```bash +TS=$(date +%Y%m%d-%H%M%S) +# 1a. complete mirror of all refs (commits, branches, tags) — the real recovery artifact +git bundle create "$HOME/clusev-pre-declaude-$TS.bundle" --all +# 1b. on-disk copies of the to-be-kept files (filter-repo will delete them from the work tree) +mkdir -p "$HOME/clusev-keep-$TS" +cp -a CLAUDE.md rules.md handoff.md docs/session-handoff.md "$HOME/clusev-keep-$TS/" +mkdir -p "$HOME/clusev-keep-$TS/superpowers" && cp -a docs/superpowers/. "$HOME/clusev-keep-$TS/superpowers/" +# verify the bundle is restorable BEFORE proceeding +git bundle verify "$HOME/clusev-pre-declaude-$TS.bundle" +``` +Do **not** delete these backups until Phase 6 confirms a fresh clone of the rewritten remote builds. + +### Phase 2 — rewrite history (in-place, --force; backups exist) +```bash +python3 /tmp/git-filter-repo \ + --path CLAUDE.md \ + --path rules.md \ + --path handoff.md \ + --path docs/session-handoff.md \ + --path docs/superpowers \ + --invert-paths \ + --message-callback ' + import re + return re.sub(rb"(?im)^\s*Co-authored-by:\s*Claude.*\n?", b"", message).rstrip() + b"\n" + ' \ + --force +``` +- `--invert-paths` deletes the listed paths from **every** commit; commits that become empty are + pruned. Tags pointing at rewritten commits are rewritten automatically. +- The `--message-callback` strips any `Co-authored-by: Claude …` line (case-insensitive) and tidies the + trailing blank line. Bodies/subjects are otherwise untouched. +- filter-repo **removes the `origin` remote** afterward (a deliberate guard) and `reset --hard`s the + work tree to the rewritten HEAD — so `CLAUDE.md` etc. now vanish from disk too (restored in Phase 4). + +### Phase 3 — verify the rewrite +```bash +git log --all -i --grep='co-authored-by: claude' --oneline | wc -l # expect 0 +for f in CLAUDE.md rules.md handoff.md docs/session-handoff.md; do \ + echo -n "$f in history: "; git log --all --oneline -- "$f" | wc -l; done # expect 0 each +git log --all --oneline -- docs/superpowers | wc -l # expect 0 +git rev-list --count HEAD # < 170 (empty doc-only commits pruned) +git log -1 --format='%an <%ae>' # still boban — author identity unchanged +``` + +### Phase 4 — restore kept files on disk + gitignore them +```bash +# Phase 1 copied these in by basename, so $KEEP holds: CLAUDE.md, rules.md, handoff.md, +# session-handoff.md, and superpowers/. Restore to their repo paths: +KEEP="$HOME/clusev-keep-$TS" +cp -a "$KEEP/CLAUDE.md" "$KEEP/rules.md" "$KEEP/handoff.md" . +mkdir -p docs && cp -a "$KEEP/session-handoff.md" docs/session-handoff.md +mkdir -p docs/superpowers && cp -a "$KEEP/superpowers/." docs/superpowers/ +``` +Append to `.gitignore` (idempotently): +``` +# Internal AI-assisted-dev docs — kept locally, never committed (de-Claude'd for beta) +/CLAUDE.md +/rules.md +/handoff.md +/docs/session-handoff.md +/docs/superpowers/ +``` +Commit just the ignore change (and any tidy), **without** a Co-Authored-By trailer: +```bash +git add .gitignore && git commit -m "chore: ignore internal development docs" +git status --short # clean — the kept files are now invisible to git +``` + +### Phase 5 — re-add origin, rebuild, GATED force-push +```bash +git remote add origin https://git.bave.dev/boban/clusev.git # filter-repo dropped it +``` +Rebuild + run the suite on the rewritten tree (R12 — app boots, tests green) before pushing. + +**>>> EXPLICIT-CONFIRMATION GATE <<<** — the force-push is irreversible and rewrites the shared +branch/tags. Do not run it without the operator's clear, in-the-moment "yes, force-push". Token is +read inline from `~/.env.gitea` and **all** output is sanitized (never echo the token): +```bash +TOKEN=$(grep -E '^GIT_ACCESS_TOKEN=' "$HOME/.env.gitea" | head -1 | cut -d= -f2- | tr -d '\r\n') +URL="https://boban:${TOKEN}@git.bave.dev/boban/clusev.git" +# branch — plain --force to the explicit URL (filter-repo dropped origin and the local +# origin/* tracking ref is stale from earlier explicit-URL pushes, so --force-with-lease +# has no reliable lease to check here; the Phase-1 bundle is the safety net instead) +git push "$URL" --force feat/v1-foundation 2>&1 | sed -E 's#https://[^@]*@#https://@#g; s/[0-9a-f]{40,}//g' +# tags moved to new commits — force-update them too +git push "$URL" --force --tags 2>&1 | sed -E 's#https://[^@]*@#https://@#g; s/[0-9a-f]{40,}//g' +unset TOKEN URL +``` +(Enumerate any additional remote branches first with `git ls-remote --heads "$URL"` — currently only +`feat/v1-foundation` exists. Do **not** use `--mirror`: it would prune refs unexpectedly.) + +### Phase 6 — post-checks, then clean up +- Fresh-clone check: clone the remote into a temp dir, confirm it builds and contains **no** + `Co-Authored-By: Claude`, no `CLAUDE.md`/`rules.md`/`handoff.md`/`docs/superpowers`. +- Gitea: releases follow the force-updated tags; old commit SHAs in any links/PRs break (expected, + solo repo). +- Only **after** the fresh clone verifies: the Phase-1 backups (`bundle` + keep dir) may be archived + or removed. + +## Going forward (post-execution rule) +Once de-Claude has run, **stop appending the `Co-Authored-By: Claude` trailer** to new commits, and do +not re-add the gitignored docs to git — otherwise the traces re-enter the history this rewrite cleaned. +(This overrides the default commit-footer convention, per the operator's beta requirement.) + +## Recovery (if something goes wrong) +The remote is force-pushed, but the Phase-1 `--all` bundle holds the complete pre-rewrite state: +```bash +git clone "$HOME/clusev-pre-declaude-.bundle" clusev-restored # full history back +# or, in-place: git fetch "$HOME/…bundle" '+refs/*:refs/*' then reset/force-push the old refs +``` + +## Security +- Token read only at push time from `~/.env.gitea`; never echoed/committed; push output sanitized. +- The rewrite never touches `.env*` (gitignored, not in history) or any secret. +- filter-repo's `origin`-drop + `--force` requirement are guards; the mandatory pre-backup is the + recovery path for the one irreversible step (the force-push). + +## Notes / self-reference +- This spec lives under `docs/superpowers/` — the rewrite purges it from git but Phase 4 restores it on + disk (gitignored), so it survives as the executed-runbook reference. +- 170 commits is small; filter-repo completes in seconds. `filter-branch` is a zero-dependency + fallback but slower and deprecated — prefer filter-repo. + +## Out of scope +- Re-writing commit-message bodies beyond the Co-Authored-By trailer (operator chose trailers-only). +- Squashing/reordering history (we preserve the real commit-by-commit story, minus traces). +- Anything in beta-prep #3 (README, done), #4 (installer, done), #5 (MOTD, done).