diff --git a/docs/runbooks/org-migration-rename.md b/docs/runbooks/org-migration-rename.md index a5d380e..55f99d9 100644 --- a/docs/runbooks/org-migration-rename.md +++ b/docs/runbooks/org-migration-rename.md @@ -72,13 +72,49 @@ gh api repos/smartwatermelon/dotfiles --jq '.owner.login + " " + .owner.type' # cd ~/Developer/dotfiles && gh pr list --limit 1 # identity guard passes, no error ``` +## Stop: the transfer happens outside this runbook + +Parts A–E cover **Step 2** of the design only: the rename and the org. No +repo has moved yet. Parts F and G below verify a transfer, so do not run +them here — every repo would still report `owner is twistedmelonman (User)`, +which is correct for this moment and looks like 31 failures. + +Go to `docs/superpowers/specs/2026-09-03-org-migration-design.md` and do: + +- **Step 3** — transfer `github-workflows` alone and prove one consumer per + org still runs green. It is the cross-org dependency: both orgs reference + `uses: smartwatermelon/github-workflows/...`. Read the run log to confirm + the reusable workflow resolved from the new owner; a green check by itself + is not evidence. + + ```bash + bash scripts/org-migration/transfer.sh scripts/org-migration/move-list.txt \ + --only github-workflows --dry-run + bash scripts/org-migration/transfer.sh scripts/org-migration/move-list.txt \ + --only github-workflows + ``` + +- **Step 4** — transfer the remaining repos: + + ```bash + bash scripts/org-migration/transfer.sh scripts/org-migration/move-list.txt --dry-run + bash scripts/org-migration/transfer.sh scripts/org-migration/move-list.txt + ``` + +Come back to Part F when Step 4 is done. + ## F. Verify the transfer (`verify.sh`) +Run this **after** design Steps 3 and 4, not before. + ```bash bash scripts/org-migration/verify.sh scripts/org-migration/move-list.txt \ - docs/data/org-migration/baseline "$(mktemp -d)" + docs/data/org-migration/2026-09-04-baseline "$(mktemp -d)" ``` +The baseline directory is dated. `verify.sh` fails and names the path if you +get it wrong, rather than reporting every repo as a missing snapshot. + **Always give `verify.sh` a fresh, empty after-dir.** It refuses a directory that already holds files, because a leftover JSON from an earlier run would be compared as though this run had just written it — a repo whose snapshot failed @@ -103,8 +139,17 @@ real drift. ## G. The other two machines (TILSIT, MIMOLETTE) -Run section D on each, before the alias-removal PR (plan Task 12) merges. -Until then the alias keeps the old `hosts.yml` name working. +Run section D on each, before the alias-removal PR (plan Task 12, design +Step 6) merges. Until then the alias keeps the old `hosts.yml` name working. + +Section D re-logins the keyring. On a machine whose shell was already open +before the rename, the sourced `gh` wrapper is also stale: reload it with +`exec bash -l` before verifying, or the old wrapper still tries to switch to +`smartwatermelon` and fails closed. `which gh` and `hash -t gh` show the +staleness; `command -v` does not. + +Steps 5–7 of the design (org secret, cleanup, docs) are not covered by this +runbook. ## Undo diff --git a/scripts/org-migration/tests/test-verify.sh b/scripts/org-migration/tests/test-verify.sh index b563c18..8cd55b0 100755 --- a/scripts/org-migration/tests/test-verify.sh +++ b/scripts/org-migration/tests/test-verify.sh @@ -194,4 +194,30 @@ else _fail "nested clone broken: rc=${rc} err=${err}" fi +# Case 10: a baseline dir that does not exist must fail on the path, not +# degrade into a "missing snapshot" line per repo. A mistyped baseline path +# otherwise reads as total drift across the whole move list. +out="$(OM_TEST_CORE="${WORK}/core-good" PATH="${WORK}/bin:${PATH}" bash "${VERIFY}" \ + "${WORK}/list" "${WORK}/nonexistent-baseline" "${WORK}/after-missing-base" \ + --clones "${WORK}/clones-good" 2>&1)" +rc=$? +if [[ "${rc}" -eq 1 && "${out}" == *"does not exist"* && "${out}" != *"missing snapshot"* ]]; then + _pass "missing baseline dir: named as the failure, exit 1" +else + _fail "missing baseline dir: rc=${rc} out=${out}" +fi + +# Case 11: an empty baseline dir is the same class of mistake -- a path that +# exists but holds no snapshots. +mkdir -p "${WORK}/empty-baseline" +out="$(OM_TEST_CORE="${WORK}/core-good" PATH="${WORK}/bin:${PATH}" bash "${VERIFY}" \ + "${WORK}/list" "${WORK}/empty-baseline" "${WORK}/after-empty-base" \ + --clones "${WORK}/clones-good" 2>&1)" +rc=$? +if [[ "${rc}" -eq 1 && "${out}" == *"is empty"* && "${out}" != *"missing snapshot"* ]]; then + _pass "empty baseline dir: named as the failure, exit 1" +else + _fail "empty baseline dir: rc=${rc} out=${out}" +fi + exit "${fail}" diff --git a/scripts/org-migration/verify.sh b/scripts/org-migration/verify.sh index 3f4c413..79492a0 100755 --- a/scripts/org-migration/verify.sh +++ b/scripts/org-migration/verify.sh @@ -34,6 +34,20 @@ base="${positional[1]}" after="${positional[2]}" fail=0 +# The baseline dir must exist and hold snapshots. Without this check a wrong +# path is not an error: every repo simply misses its baseline file and the +# comparison loop reports "missing snapshot" for the whole move list, which +# reads as catastrophic drift rather than as a typo. Fail on the path itself +# so the message names the real problem. +if [[ ! -d "${base}" ]]; then + echo "verify: baseline-dir ${base} does not exist" >&2 + exit 1 +fi +if [[ -z "$(ls -A "${base}" 2>/dev/null || true)" ]]; then + echo "verify: baseline-dir ${base} is empty" >&2 + exit 1 +fi + # The after-dir must be ours alone. A leftover JSON from an earlier run would # be compared as though this run had just written it, so a repo whose snapshot # failed now could still be reported ok from stale state.