🎙

Flood Recording Studios — Living Breakdown

The mirror of the old site, served from our own Worker · floodrecordingstudios.com
← Appolis · the city of apps

FLOOD RECORDING STUDIOS — MIRROR — Master Breakdown (Living Doc)

The old WordPress site (floodrecordingstudios.com on WP Engine) snapshotted page-for-page and served from our own Cloudflare Worker flood-recording-studios. This is M1 of the takeover — Mirror → DNS → Stage on the Website Builder → Flip — so the studio never goes dark while the domain moves and the new site is built elsewhere.
Preview: https://flood-recording-studios.appolis.workers.dev · the real domain attaches in M2 · the flip (M4) re-points the domain at the Website Builder and keeps this Worker as rollback.
Single source of truth for this app. Updated every time it changes. Board: Kosmos project flood-recording-studios — read 🧠 STATE (note_2696) first; the takeover brief and decisions are in 🗺 ROADMAP (note_2694); the measured as-is site is 📋 INVENTORY (note_2695); traps in note_2760; the lane split with the Website Builder in 🔗 LOCKSTEP (note_3084).

1. WHAT IT IS

A static snapshot, served from our own Worker. Nothing here talks to WordPress at run time: the pages are HTML files, the assets are the theme's own CSS/JS/images copied to the same paths, and the Worker's only logic is the routing table in lib/routes.js. That is the whole design — the mirror exists so that the DNS move (M2) has a target that cannot break, and so that the Website Builder (the sister lane) has a byte-exact reference of what the old site looked like.

What it deliberately is not: a redesign, a CMS, a booking system or a store. Booking and chat stay on Cal.com and GoHighLevel until the flip; afterwards Hermes owns them (Hermes board todo_3078). The new site is built on the Website Builder (project website-builder), never here.

2. THE HARVEST — scripts/harvest.js

node scripts/harvest.js (or npm run harvest) rebuilds site/ and harvest/ from the live WordPress site; --offline redoes only the rewrite from the raw pages already on disk:

  1. Fetches the 4 real pages with a Chrome user-agent. WP Engine's bot challenge blocks curl but not node's fetch with a browser UA (measured 2026-09-08) — the raw HTML is kept under harvest/raw/ for provenance.
  2. Collects every same-origin reference — absolute, protocol-relative (//floodrecordingstudios.com/…, which is how the theme loads Slider Revolution's CSS), JSON-escaped (https:\/\/…) and root-relative /wp-content|/wp-includes — from the pages, then from every stylesheet (url(), @import) and script it downloads, then the whole media library via /wp-json/wp/v2/media. Everything is saved under site/ at the same path. WP Engine rate-limits bursts (HTTP 429 after ~35 requests, measured), so requests are spaced 250 ms apart and a 429/5xx is retried with exponential backoff; a genuine 404 is recorded in harvest/manifest.jsonmissing[] and tolerated by the tests (the live theme has five: three owl.video.play.png paths, a LESS placeholder @{img-path}, a misspelled Barlow-SemiBoold.ttf), a 429 left behind fails them. Known quirk carried over as-is: the theme's placeholder.png files are JPEG data under a .png name; browsers sniff the bytes and render them, so they are served unchanged.
  3. Rewrites each page: same-origin URLs → root-relative (the canonical link keeps the absolute real domain, on purpose); strips the tags listed in the build line above; injects the JSON-LD before </head> and the address strip immediately before the © Copyright paragraph.
  4. Writes sitemap.xml (the 4 real URLs on the real domain), robots.txt, 404.html, and harvest/manifest.json (pages, every asset with its byte size, the media-library count, what was stripped, what was missing). Prints one line: HARVEST pages=4 assets=N missing=M media=K bytes=B stripped=S — at v0.1.0: HARVEST pages=4 assets=259 missing=5 media=76 bytes=15787504 stripped=68.

Re-run it whenever the WordPress site is edited before the flip (the routine session's page-count check tells you when). The tests then prove the new snapshot is complete and closed.

The two deliberate changes. Everything else on the pages is byte-for-byte what WordPress served.

  1. The address, hours and socials (Tyler, 2026-09-08, todo_2698 #8). The old site never states the address; listings do. Every page now carries <script type="application/ld+json" id="frs-localbusiness"> (LocalBusiness · 6003 28th St E Unit B, Bradenton, FL 34203 · +1 941-705-9356 · floodrecordingstudios@gmail.com · Instagram/Facebook · the map pin's coordinates) and <div id="frs-address"> with the same facts plus "Sessions by appointment — booking open 24/7", above the copyright line. The JSON-LD carries no opening hours on purpose: nobody has measured the studio's hours, and "open 24 hours" would have been invented.
  2. Search visibility becomes one switch, and it is OFF. Measured 2026-09-08: all four live pages carry <meta name='robots' content='noindex, nofollow' /> — WordPress's "Discourage search engines" switch is ON in the WP Engine install. Tyler's call, 2026-09-08: "Just keep it off because we're going to be remaking everything anyways. When we're done with the staging site and ready to push it live on our platform, then we can just not make anything be hidden." So: WordPress is not touched, the harvest strips the baked-in tag so the snapshot's HTML is clean, and the decision lives in lib/routes.js as SEARCH_VISIBLE = false — while it is false the Worker sends x-robots-tag: noindex, nofollow on every host, the apex included, and /__version reports "indexable":false. At go-live one constant flips; the tests and this section are coupled to it on purpose, and a preview host stays noindex either way.

3. ROUTES — lib/routes.js (pure, tested)

RequestResult
GET /__version (any host)200 JSON with app, role, version, date, pages, indexable
www.floodrecordingstudios.com + any path301 → https://floodrecordingstudios.com + same path and query
the 7 orphan pages /sample-page/ /testimonials/ /blog/ /about-page/ /dance-classes/ /teachers/ /an-instructor/ (with or without the slash)410 Gone — the dance-school theme demo left published since 2021–23; 410 tells search engines to drop them
the WordPress doors /wp-json /wp-admin /wp-login.php /xmlrpc.php /wp-cron.php /feed /comments/feed — each door and everything beneath it410 Gone — no back end here
everything elsethe static snapshot via the ASSETS binding: /booking/booking/ (auto trailing slash), ?ver= query strings ignored, unknown paths → 404.html
every host while SEARCH_VISIBLE is false; after go-live, every host except the apexsame response plus x-robots-tag: noindex, nofollow

wrangler.jsonc: assets.directory = ./site, binding = ASSETS, run_worker_first = true (the Worker sees every request first, then falls through to the assets), html_handling = auto-trailing-slash, not_found_handling = 404-page, workers_dev = true.

4. FILES


worker.js            the Worker — turns lib/routes.js decisions into responses
lib/routes.js        VERSION · SEARCH_VISIBLE · ORPHANS · DEAD · decide(url, opts)
scripts/harvest.js   the snapshot script (§2)
site/                the snapshot: index.html, booking/, contact/, availability-policy/, wp-content/, wp-includes/, sitemap.xml, robots.txt, 404.html — 266 files
harvest/raw/         the 4 pages as WordPress served them (provenance)
harvest/manifest.json what was harvested, byte for byte
test/routes.test.mjs the routing table + the search-visibility switch (8 tests)
test/site.test.mjs   the snapshot is complete, closed to WordPress, carries the changes (12 tests)
wrangler.jsonc · package.json · APP_BREAKDOWN.md (this file) · README.md
.gitattributes       `* -text` — byte-exact checkouts (this machine's git has core.autocrlf=true; without it every clone/worktree would rewrite 64 text files and break the manifest test)
.gitignore           /node_modules/ /.wrangler/ *.log /archive/ — ANCHORED: the theme ships isotope.pkgd.min.js under site/…/vendor/node_modules/, and an unanchored ignore dropped it from every clone (found by cloning); the WP Engine backup zip lives in archive/, never committed

5. TESTS

npm test (= node --test test/routes.test.mjs test/site.test.mjs) — no dependencies, node's own runner. 20 tests, 0 failing at v0.1.0.

6. DAILY CHECKS (the routine session, after the first deploy)

  1. https://flood-recording-studios.appolis.workers.dev/__version → version equals lib/routes.js VERSION and "indexable":false until Tyler says go-live.
  2. The 4 preview pages → 200 with their titles; /sample-page/ → 410; /wp-json/ → 410; every page answers with x-robots-tag: noindex, nofollow.
  3. /booking/ in the browser pane shows the Cal.com iframe (app.cal.com/flood-studios-amn0ru/…).
  4. The live WordPress page count (/wp-json/wp/v2/pages?per_page=100&_fields=id) is still 11 — if it moved, re-run the harvest and the tests.
  5. After M2 add: the real URL 200 · MX still aspmx.l.google.com · a test mail delivers · the apex still answers x-robots-tag: noindex (the studio stays hidden until the new site goes live).

7. COMPLIANCE

The Appolis connector does not open a flood-recording-studios app (measured 2026-09-08: appolis_compliance_list → "this connector does not open flood-recording-studios"), so nothing can be attested under this name. The suite-wide directives open on 2026-09-09 were read against this build: overlay lock (cmp_8c8134e18f) — the mirror opens no overlays of its own (the chat bubble is GoHighLevel's); schema proof (cmp_c7b85307b7) — no database, no migration; token discipline (cmp_18c8b53201) — no AI call anywhere; email identity (cmp_34f01cf47c) — sends no email (there is no form); progress on every wait (cmp_progress0001) — static files, no wait; Hephaestus first (cmp_b80e65baad) — the mirror makes no AI call to route; answers are a form (cmp_0f71c92a20) — this lane asks through Kosmos decision forms, not text lists. If the mirror ever grows a form, a wait or an AI call, it registers as an app first and attests then.

8. FEATURES DECK (plain-English rows)

9. NEXT

Rendered from flood-recording-studios/APP_BREAKDOWN.md · this page is generated — edit the markdown, not the HTML
print this page for a PDF