Cutover — Design (Sub-project 6)
Date: 2026-07-06
Status: Approved for planning
Sub-project: SP6 (final) of the Wiki.js → Astro migration (docs/superpowers/specs/2026-07-06-wikijs-to-astro-migration-program-design.md). SP1–SP5 are complete; future.buckyverse.org has full feature parity.
Nature: An ops event, not a code build. The buildable artifacts are the redirect map + verification tooling; the production DNS flip and droplet retirement are executed by the human owner.
1. Goal & definition of done
Make buckyverse.org serve the Astro site, redirect the old Wiki.js URL surface with no user-visible dead ends, keep a safe rollback window, then retire the Wiki.js droplet. Done when: apex + www serve the Astro build; the parity checklist is green in production; old URLs redirect (orphans → home); the droplet is retired after a clean rollback window.
2. Deliverables & responsibility split
Built + deployed by the agent (safe, reversible):
- The complete redirect map in
apps/reader/public/_redirects(§3). - A book-slug redirect generator (
scripts/build-book-redirects.mjs) that emits book/source-page rules frombooks.config+ the catalog (§4). - A verification script (
scripts/verify-redirects.mjs) that, against a Pages preview URL, checks a sample of old→new paths redirect correctly and that real content still serves (guards the Cloudflare precedence question). - The written cutover runbook (the ordered checklist in §5), committed to
dev/wikijs/ordocs/.
Executed by the human owner (hard-to-reverse, production):
- Adding
buckyverse.org+wwwas custom domains on thebuckyverse-futurePages project. - The Route 53 DNS change (apex decision, §5).
- Retiring the droplet after the rollback window.
3. Redirect map
Amendment (found during SP6 implementation): Cloudflare Pages
_redirectshas a hard 100-rule cap — rules past #100 are silently dropped. The full enumerated map is ~215 rules, so it does not fit. Resolution: split the map. The ~19 pattern rules below (dictionary, knowledge/en, synergetics, orphan prefixes) ship in_redirects. The ~196 precise per-book rules move to a Cloudflare Bulk Redirects list (dev/cloudflare/cutover-bulk-redirects.csv, generated byscripts/build-bulk-redirects.mjs; up to 100k entries), imported by the owner (runbook Phase 1b). Verified: the pattern rules + content-serving pass onfuture.buckyverse.org("All checks passed").
Cloudflare Pages _redirects, order = priority (first match wins). Precise content rules first, then enumerated orphan catch-alls. No blind global /* (the preview test confirmed static assets serve and the enumerated prefixes fire correctly).
Content (precise):
- Dictionary (extends the SP3 rules already present):
/synergetics-dictionary/:letter/:card → /dictionary/:letter#:card(+/index → /dictionary/:letter, +/en/variants) — present./synergetics_dictionary/* → /dictionary/(the older underscore variant; coarse landing).
- Knowledge:
/en/knowledge/:cat/:slug → /knowledge/:cat/:slug 301(bare/knowledge/...is already identity on Astro — SP4 mirrored the projector).
- Books/sources (generated, §4):
/<wiki_dir> → /read/<reader_slug>and/<wiki_dir>/index → /read/<reader_slug>for readable books; unreadable/absent →/library.
- Synergetics book pages:
/synergetics → /read/synergetics,/synergetics/* → /read/synergetics(coarse; the book is one reader entry).
Orphans → home (enumerated prefixes, not a blind /*):
/receipts/*, /codexis/*, /cursor/*, /docs/*, /docs-site/*, /work/*, /zotero/*, /buckyverse2/*, /knowledge/raw/*, /knowledge/inventory/* → / 301.
Optional final fallback: /* / 302 — added ONLY after §5's preview test proves existing static assets are served (not redirected). If the test shows redirects would shadow real pages, the enumerated prefixes stand alone and unknown stragglers 404 (acceptable).
4. Book-slug redirect generation
Wiki.js served book/source pages at the repo-root dir path (e.g. /grunch_of_giants, /world_design_science_decade_1965-1975/document-2-the-design-initiative). The reader uses books.config slugs (e.g. grunch_of_giants, wdsd_2_the_design_initiative). Most match; the WDSD sub-pages and any renamed slugs differ.
build-book-redirects.mjs:
- Reads
books.config.ts(readable reader slugs) + the repo-root book dirs. - For each Wiki.js book path, emits
/<wiki_path> /read/<reader_slug> 301when a reader slug maps, else/<wiki_path> /library 301. - Hardcodes the known non-identity mappings (the six WDSD documents →
wdsd_1..6_*). - Output appended into
_redirects(between the content rules and the orphan catch-alls).
5. Cutover runbook (ordered checklist)
Phase 0 — pre-flight (agent + human):
- Parity checklist (roadmap §4) green in production
future.buckyverse.org. - SP3 dictionary image R2 upload finished (no card-image 404s). (In progress at spec time.)
- Redirect map deployed to
future.buckyverse.org;verify-redirects.mjspasses against it (old→new sample redirects; real content still serves).
Phase 1 — add domains (human, Cloudflare dashboard):
- Pages →
buckyverse-future→ Custom domains → addbuckyverse.organdwww.buckyverse.org. Cloudflare provisions TLS + shows the DNS target.
Phase 2 — DNS (human, Route 53) — apex decision:
- Option A (recommended): move the
buckyverse.orgzone to Cloudflare — cleanest apex support (CNAME-flattening), enables proxying/analytics; update the registrar's nameservers; recreate existing records (incl.graph/future, MX, etc.) in Cloudflare. - Option B: keep Route 53 —
www.buckyverse.orgCNAME →buckyverse-future.pages.dev(canonical); apexbuckyverse.org→ a redirect tohttps://www.buckyverse.org(Route 53 doesn't CNAME an apex topages.dev; use an S3/redirect or Cloudflare-worker redirector for the apex). - Apply the chosen option; the old apex A record (
64.23.238.243, the droplet) is replaced/redirected.
Phase 3 — verify in production (agent-scriptable):
-
buckyverse.org+wwwserve the Astro build (200,server: cloudflare). - Spot-check redirects live: a dictionary card URL, a knowledge article, a book page, an orphan → all land correctly.
- Parity checklist green on the production host.
Phase 4 — rollback window:
- Leave the droplet + Wiki.js DB running and untouched for the agreed window (default 7 days). Rollback = repoint apex/www DNS back to the droplet (Option A: change records in Cloudflare; Option B: restore the Route 53 records). Instant, no data reconciliation (parallel run).
Phase 5 — retire (human, after window):
- Snapshot/backup the droplet + Wiki.js Postgres (final archive).
- Destroy the droplet; decommission the Wiki.js Docker/compose; archive
dev/wikijs/scripts as historical. - Remove now-dead DNS (
do.buckyverse.org, etc.).
6. Rollback
Because SP1–5 ran in parallel and cutover is additive, rollback at any point before Phase 5 is a DNS revert to the droplet. The Wiki.js DB is untouched throughout, so no state is lost. (Ties into TODO-3's new-DB rollback note for any residual Wiki.js DB work.)
7. Risks
- Apex DNS mechanism (§5) — the main wrinkle; the runbook presents both options rather than assuming. Route 53 can't CNAME an apex to
pages.dev. - Cloudflare redirect-vs-asset precedence — a blind
/*could shadow real pages; mitigated by enumerated orphan prefixes + the preview verification before any global fallback. - Redirect completeness — 24k URLs; the map is category-based + generated for books, and
verify-redirects.mjssamples each category. Orphans → home means no dead ends regardless. - Premature retirement — the rollback window + a final DB backup guard against it; retirement is a deliberate post-window human step.
- In-flight content track — the dictionary image upload must finish first (Phase 0 gate).
8. Definition of done
buckyverse.org serves Astro; old URLs redirect with no dead ends; parity verified in production; the droplet retired after a clean rollback window with a final backup. The Wiki.js → Astro migration is complete.