Skip to content

Time Machine — Thursday 14 May 2026

Session shape

Started late morning Brisbane time. Tim had been away briefly with visitors from the Netherlands and was reorienting after the two-day Terraform arc closed on Sunday 10 May. Session ran across roughly seven hours including breaks. The session's character was substrate-readiness assessment — did the foundation work of the past week leave dev and prod genuinely separated, and is it safe to resume product development? The answer turned out to be more nuanced than green or red, and required several iterations of investigation before the full picture surfaced.

Two substantive workstreams ran in parallel: closing DOCS-RECORD-RENAME-01 (the Documents → Record cleanup), and assessing the dev/prod substrate state. Both produced durable findings. The second produced more than expected.


DOCS-RECORD-RENAME-01 — closed clean

The brief that was drafted at the end of yesterday's session executed cleanly this morning. Alex hit one substantive obstacle during the pre-flight: the mkdocs-redirects Python package on PyPI changed upstream ownership between versions 1.2.2 (canonical, github.com/mkdocs/mkdocs-redirects) and 1.2.3 (ProperDocs org, github.com/ProperDocs/properdocs-redirects, created 8 weeks prior with no contact info, pulling in a renamed fork of mkdocs itself called properdocs). Classic supply-chain compromise shape. Alex caught it during install before any docs file changes landed, paused, and surfaced.

Tim's response cut through cleanly: "no one would have an old bookmark because it's just me and you that deal with it. It seems as though we're adding in something to fix a problem that doesn't exist." Whole question of redirect mechanism disappeared. The brief simplified — delete documents.md outright, archive the v0.1 content with superseded-by header, no plugin needed, no supply-chain question to resolve. Alex executed, all four browser checks confirmed (404 on old URL, Record renders, sidebar clean, index page reads Record). Brief moved from outputs/ to docs/docs/briefs/archive/.

The supply-chain catch itself produced a candidate standing rule: "For any new pip/npm/cargo dep, pin to specific known-good version and check upstream URL hasn't moved between versions." Carrying as candidate. Hasn't graduated yet (one instance).

The simplification produced a deeper architectural insight worth holding: enterprise-grade caution applied to a sandbox is the wrong calibration. The OPERATING-CALIBRATION rule was the right frame. The redirect mechanism solved a problem that didn't exist at three-person scale.


Substrate-readiness assessment — the main workstream

How it started

Tim wanted confirmation that the foundation work hadn't broken anything on dev, and that dev and prod were genuinely separated. Not a stock-take of the whole project. Not the cutover to marketing-prod (rtopacks.com.au stays as placeholder until the product is ready). Just: can I and Alex resume building on dev without the foundation re-opening underneath us?

The terminology needed cleaning up before the assessment could proceed. We landed on three working terms: - Workspace-prod = staging.rtopacks.com.au (the actual product, currently served at the staging URL because the prelaunch placeholder owns rtopacks.com.au) - Workspace-dev = rtopacks.dev (where build work happens) - Marketing-prod = rtopacks.com.au (the placeholder, irrelevant to today's assessment)

That terminology hasn't been formally adopted in the canonical docs but is useful for thinking about the substrate today.

Tim's architectural frame, captured plainly

"Dev and prod are sealed environments. There is no link in dev resolving to prod, no link in prod resolving to dev, ever. There is no legitimate use case for cross-environment linking. The two environments are halves of a sphere — they share reference data via -ref databases, but their UI, navigation, auth, and runtime are completely sealed from each other."

This frame is load-bearing for everything that followed. The brief Tim and Claude drafted (DEV-PROD-LINK-SEAL-01) captured it. The standing rule it implies (proposed as DEV-PROD-SEAL RULE) graduates once the brief closes.

The first finding — and what it actually meant

Walk-through of dev and staging surfaces revealed that clicking a qualification card on rtopacks.dev sends the user to my.rtopacks.com.au/auth — the production workspace login. Clicking the same qualification on staging.rtopacks.com.au goes to staging.rtopacks.com.au/qual/... correctly. Cross-environment link leak, dev to prod.

Tim's hypothesis at the time: "pollution is going to be on the dev side because that's where we copied it to. I think the staging side will probably be okay, but we should check."

DEV-PROD-LINK-SEAL-01 was drafted as an audit brief (Phase 1 inventory only, Phase 2 remediation drafts separately after review). Comprehensive scope: every customer-facing surface on both environments, every cross-env link, every redirect, every auth handoff, every hardcoded URL in config, every hardcoded URL in source. Tim sent to Alex.

Alex's audit — comprehensive, confirmed the hypothesis

Alex's Phase 1 audit confirmed Tim's hypothesis cleanly: - Dev side polluted — dozens of cross-env leaks across every customer-facing surface - Prod side clean — zero rtopacks.dev URLs in any prod-bound code path

Root cause is structural: all four customer-facing worker source trees (apps/site, apps/workspace, apps/admin, workers/internal-api) deploy the same source code to both prod and dev, distinguished only by the env.staging wrangler block. Source code hardcodes https://my.rtopacks.com.au, https://internal-api.rtopacks.com.au, https://admin.rtopacks.com.au in ~70 locations across 60+ files plus packages/ui. There is no URL env-detection layer anywhere.

The env-aware pattern does exist (env.ENV === "staging" ? <dev> : <prod>) — but it's applied only to ~12 email-recipient decisions. Was never extended to URL construction. That's the gap.

Audit filed at outputs/dev-prod-link-seal-01-audit.md. DEV-PROD-LINK-SEAL-01 Phase 1 closed. Phase 2 (remediation) drafts after the Path A commitment.

The deeper finding — Cloudflare MCP inventory

While Alex audited from the code side, Claude inspected the substrate directly via Cloudflare MCP. First pass found the customer-facing workers were named <name> (= dev) and <name>-staging (= prod), not the documented <name>-prod / <name>-dev paired model.

That triggered a complete substrate inventory across workers, D1, R2, KV — looking for the same pattern. Findings:

Workers (38 total). May 6 duplication pattern applied consistently. Original (= dev) plus -staging (= prod) for the four customer-facing surfaces. Sync workers and operational workers as singletons, mostly without -prod suffix.

D1 databases (14 total). Same May 6 pattern. Five customer-data databases duplicated with -staging suffix (rto-workspace-db / rto-workspace-db-staging, etc.). Four reference databases left as singletons (rto-nrt-db, rto-abs-db, rto-licensing-db, rto-radar-db). All have entity-prefix issue (rto- instead of canonical rtopacks- for customer-data, prefix dropped per Principle 6 for reference).

R2 buckets (8 total). Not part of the May 6 move. Three customer-data buckets (rtopacks-client-media, rtopacks-documents, rtopacks-session-archive) currently shared between dev and prod. This is a hard violation of Principle 4 ("If a resource doesn't meet all four conditions [for -ref], it's -prod and -dev, separate UUIDs"). Customer-uploaded data is currently shared by environment. Two buckets pass the -ref test (tga-content, radar-screenshots) but lack the -ref suffix. Two buckets (rtopacks-output, rtopacks-media) need content investigation. rtopacks-terraform-state is the only canonically-named resource on the entire substrate (created 9 May during STATE-MIGRATE-01).

KV namespaces (12 total). Same May 6 pattern. Customer-data pairs follow <NAME> + <NAME>_STAGING convention. One inconsistency: RTOPACKS_SESSIONS exists as singleton while SESSION_KV_STAGING exists separately — different naming style, role unclear. Cache namespaces (STATS_CACHE, SEARCH_CACHE, RADAR_CURSOR) are singletons with no env suffix — need explicit decision on whether they're _REF or per-env.

The canon-read mistake and what it taught

First pass at the inventory missed the entity-prefix issue, missed the database-name-following-content rule, and produced a framing where "Path A vs Path B" was open. Tim caught it implicitly when he asked: "If you search back through the documents, remember we said that we would put a suffix of -REF on the shared databases?"

Re-reading the canon in full made the picture much sharper. Principle 4's final line — "If a resource doesn't meet all four conditions, it's not -ref. It's -prod and -dev, separate UUIDs" — is absolute, not a guideline. Applied rigorously, the R2 customer-data buckets must be paired per-env. Not "decide whether to duplicate" — the canon already decided.

Tim's articulation of why this matters is worth preserving verbatim because it captures the architectural reasoning that produced the canon in the first place:

"The -ref is, for a human, a very quick visual way to understand that this isn't a product of either. It's shared. And also, it avoids the problem that we've just had now very clearly. Even without reading the canon in future, you'll look at it and go, 'Well, what's -ref mean?' It's a trigger to look up rather than it being [ambiguous]."

The -ref suffix does three jobs simultaneously: visual disambiguation, forcing function for unfamiliar readers, prevention of the exact failure mode Claude produced (having to reason about whether a customer-data bucket should be shared). Names that require reasoning are names that fail.

This produced a candidate standing rule: "Reference docs are read in full at the start of work that touches their domain, not paraphrased from memory." One instance, candidate status. If it fires again, graduates.

Final substrate inventory

Inventory v2 filed at outputs/complete-substrate-inventory-v2.md. 368 lines. Every resource on the substrate (38 workers + 14 D1 + 8 R2 + 12 KV = 72 resources) inventoried against the canon. Findings:

  • Workers: all 38 have at least one canon violation. Eight need -staging-prod. Twenty-five missing env suffix. Six have wrong entity prefix.
  • D1: all 14 have violations. Four reference DBs need entity-prefix removal + -db-ref. Ten customer-data DBs need entity-prefix change + -db removal + env suffix correction.
  • R2: 1 correct. 2 need -ref suffix. 3 need duplication + correct naming. 2 need content investigation.
  • KV: all 12 have violations. Customer-data pairs need entity prefix + env suffix correction. Cache singletons need explicit _REF vs per-env decision.

Plus the URL env-awareness work from Alex's audit — ~70 hardcoded prod URLs across 60+ files need env-aware construction.

The Path A commitment

The inventory document was framed as scaffolding for a decision: commit to Path A (bring substrate into line with canon) or Path B (update canon to match substrate). Reading Principle 9 literally — "If a new resource doesn't have an obvious canonical name, pause. Either the resource fits a pattern that the canon needs to absorb (extend the canon), or the resource is being created on the fly without clear intent (don't create it yet)" — Path B doesn't survive. The canon is the source of truth.

Tim has not yet formally committed to Path A. End-of-session position is "likely Path A, but the commitment itself is a fresh-window decision worth making with peak edge tomorrow."

The decision is significant because committing to Path A commits the substrate to renaming every resource on Cloudflare. Renames are metadata-only per Principle 8 (no UUID changes, no data movement), but bindings need updating in lockstep — wrangler configs reference workers and resources by name. Real coordinated workstreams.


The decomposition of the work ahead

Once Path A commits, the work decomposes into one decision plus five workstreams:

Decision (next session's first move): Tim commits to Path A explicitly. Not "I lean Path A" — an explicit decision, recorded.

Workstream 1 — DEV-PROD-LINK-SEAL-02 (URL env-awareness in source). Alex's audit findings remediated. ~70 locations made env-aware. Must happen before or alongside worker renames so dev workers don't keep pointing at prod even after rename.

Workstream 2 — Worker renames. All 38 workers brought into canon compliance. Coordinated with wrangler config updates.

Workstream 3 — D1 renames. 14 databases renamed. Bindings updated.

Workstream 4 — KV renames + cache decisions. All 12 namespaces. Plus explicit decision on cache singletons (_REF or per-env).

Workstream 5 — R2 cleanup. Most complex. Rename 2 reference buckets to add -ref. Investigate 2 ambiguous buckets, then rename or duplicate. Duplicate 3 customer-data buckets (real new infrastructure, not just renaming) and update worker bindings.

Sequencing: workstreams can run in order (URL env-aware first because it's the trigger point, then workers, then D1, then KV, then R2 last because R2 is most complex). Alternatively could run resource-type-by-resource-type. Tim decides shape.


Standing rule candidates from today

Two candidates earned today, neither graduated to formal status:

1. "Reference docs are read in full at the start of work that touches their domain." Produced by Claude's canon-half-read mistake during the substrate inventory. Re-reading in full produced a materially sharper picture in 15 minutes — surfaced the entity-prefix issue, the consumer-vs-content database naming rule, the strict Principle 4 application that closed the R2 question. If this pattern fires again on another canonical doc (design foundation, standing rules, product overview), it graduates.

2. "For any new pip/npm/cargo dep, pin to specific known-good version and check upstream URL hasn't moved between versions." Produced by the mkdocs-redirects supply-chain catch this morning. Same pattern would catch a hostile package-name takeover in any language ecosystem. One instance.

Plus a candidate from yesterday that hasn't yet been exercised: Single-path discipline for token-scope captures (dashboard only, no API alternative). Codified into Alex's CLAUDE.md yesterday.


Important workflow context for next-Claude

Most of this is unchanged from previous Time Machines and remains true:

  • Tim relays briefs verbatim to Alex. Claude does not message Alex directly.
  • Alex executes against Tim's actual filesystem. Single environment.
  • Credentials never reach Alex's session by design — Tim runs all credential-touching commands.
  • Brief drip rule holds — one brief at a time, no parallel-loading.
  • Cloudflare MCP active: account f95d45376ebeeeaf011a4f0ec0fb7b38. set_active_account before account-scoped calls. Was unhealthy this morning, came back after Tim reset the connector.
  • Browser MCP (Claude in Chrome): drives Tim's authenticated session. Works for navigation and get_page_text. Important caveat surfaced todayget_page_text reads the initial DOM and misses JavaScript-rendered content on React-heavy pages. Use screenshots for visual confirmation, not text extraction alone.
  • Internal docs proxy: https://rto-docs-proxy.dark-firefly-3289.workers.dev/f296da76d0cb073bc89ab387d98e7e58/

New context from today

The 6 May substrate move was systematic but incomplete. Workers, D1, KV all show the same <name> (dev) + <name>-staging (prod) pattern. R2 wasn't part of it at all. The canon describes a different model that was never implemented. This is the reality next-Claude needs to hold before reasoning about the substrate.

The DEV-PROD-SEAL RULE has been named but not formally added to standing-rules.md. Graduates when DEV-PROD-LINK-SEAL closes (likely Phase 2 close).

The browser MCP's get_page_text limitation produced a real misread today. Claude read text extractions from rtopacks.dev and staging.rtopacks.com.au and concluded they were rendering different content. Screenshots from Tim showed they're the same site rendering same content. Lesson: for visual product surfaces, use screenshots. get_page_text is for finding specific strings (like the 404 check), not understanding what's on the page.


What's open

Substrate decisions: - Path A commitment (the load-bearing one) - Brief sequencing after Path A commits - Cache namespace decision: _REF or per-env? - rtopacks-output and rtopacks-media R2 content investigation

Documentation reconciliation (waits until substrate moves): - infrastructure-reference.md — stale (April 2026), refer to ucca-terraform-state which is now rtopacks-terraform-state, worker count is 28 but reality is 38 - cloudflare-resource-inventory.md — describes target state; depending on Path A vs B might need updating or might stay as-is - Internal disagreement between infrastructure-reference.md and cloudflare-resource-inventory.md on database names — needs resolving - documents-spec.md still in project files even though archived from live corpus (project files mirror lag, normal)

Adjacent concerns named but parked: - ACCESS-IDENTITY-CLEANUP-01 (CF Access posture, identity-per-environment) — mentioned, not actioned - Dev DB empty state on admin dashboard — observed, not investigated - docs/site/ git-tracking wrinkle from DOCS-RECORD-RENAME-01 — flagged by Alex - terraform-promote-01.md still in outputs/ post-execution — tidy candidate

Carrying forward from previous sessions (unchanged): - TERRAFORM-V5-RECONCILIATION-01 (the 78 cosmetic v5 writes from Sunday's Terraform work) - RECOVERY-MODEL-RECONCILIATION-01 - NAMING-CANON-MIGRATE-01 (probably superseded by Path A workstreams) - NAMING-MIGRATE-R2-01, NAMING-MIGRATE-KV-01 (likely folded into Path A workstreams) - LANDSCAPE-MULTITENANT-01, LANDSCAPE-EDITORIAL-POLICY-01 - PROD-CUTOVER-INTENT-01 — the eventual cutover from staging → marketing-prod, well in the future - EMAIL-SURFACE-01 - CLASSIFICATION-MIGRATION-OSCA-01 (September 2026 trigger)


Fresh Claude reads this Time Machine plus: 1. outputs/complete-substrate-inventory-v2.md — the canon-applied inventory with every resource categorised 2. outputs/dev-prod-link-seal-01-audit.md — Alex's audit findings (URL env-awareness scope) 3. cloudflare-naming-canon.md (in full, per the candidate standing rule from today)

Then ask Tim: "Are you ready to commit to Path A explicitly?"

If yes — draft Workstream 1 (DEV-PROD-LINK-SEAL-02). If no — sit with it, no urgency, substrate isn't degrading.

This session stays open as a fallback for the 4% gap that doesn't translate to documentation. Tim can come back here to ask a clarifying question if next-Claude is missing context.


Calibration note

Today produced substantive work across multiple workstreams with the discipline holding throughout:

  • Two briefs handled (DOCS-RECORD-RENAME-01 closed, DEV-PROD-LINK-SEAL-01 Phase 1 closed)
  • A real supply-chain catch (mkdocs-redirects 1.2.3) with no damage done
  • A complete substrate inventory across all four Cloudflare resource types
  • Two candidate standing rules earned through use
  • One architectural decision frame (Path A vs B) brought to "almost committed"
  • A real triangulation between Alex's wrangler audit and Claude's MCP inspection
  • The browser-MCP-get-page-text limitation surfaced and named for future sessions

The session caught a Claude failure mode (skim-don't-read on canonical docs) and produced a candidate rule to prevent it. Tim's instinct to do the substrate walk-through before resuming development was the right call — without it, every new feature built on dev would have inherited the cross-env link pollution by default.

Tim's framing late in the session captures the operating principle the foundation work has been building toward: "This project is going to get really big, and we really need to be disciplined. Otherwise, we're just going to start falling over our own feet and possibly even bringing the house down on top of us if we're not really disciplined and really well documented."

The discipline is the substrate. Everything else runs on top of it.