Epoch Universe

Architecture Reference
Last updated: 2026-04-10  ·  Status: Complete
3
Hangars
2
Airlines Flying
6
Repos
6
Firebase Projects
4
Cloud Functions
epochblog.net (redirect) | v www.epochblog.net [ epoch-hub ] / \ / \ fashion.epochblog.net traveler.epochblog.net [ epoch-fashion-pub ] [ epoch-traveler-pub ] STATIC HTML (SSG) STATIC HTML (SSG) | | | built by | built by v v epoch-fashion epoch-traveler (pipeline) (pipeline) fashion-epoch FB epoch-traveler FB Cloud Functions 4 Cloud Functions epoch-lab (sandbox / archive) arena + rooms + tools ────────────────────────────────────────────────────── PUBLIC LAYER Pure static HTML. Zero runtime. SSG built. PIPELINE LAYER Private backends. API keys. Cloud Functions. SANDBOX LAYER Experiments. Arena. Rooms. Not public.
Epoch Hub
Landing page for the Epoch Universe. Two tiles linking to Fashion and Traveler. Fashion tile has a live crossfading slideshow from Firestore.
Firebaseepochblognet-hub
Repoab-aidev/epoch-hub
Typestatic html
Statuslive
Fashion Public
Public fashion blog. 953+ AI-generated editorial images with Director's Journal. Pure static HTML generated by SSG. Client-side pagination from embedded JSON.
Firebaseepoch-fashion-pub
Repoab-aidev/epoch-fashion-pub
TypeSSG static
SEOOG tags, JSON-LD, Atom feed, sitemap
Statuslive
Fashion Pipeline
fashion-epoch.web.app (internal)
Daily image generation pipeline. Claude writes prompts, Flux generates images, Claude grades and curates. Scheduled Cloud Function + incremental SSG build.
Firebasefashion-epoch
Repoab-aidev/epoch-fashion
Typepipeline
FunctionsdailyRun (scheduled)
Statuslive
Traveler Public
S's travel diary. Trip overview pages + individual day entries with narrative, character images, B-roll, and outfit briefs. Pure static HTML generated by SSG.
Firebaseepoch-traveler-pub
Repoab-aidev/epoch-traveler-pub
TypeSSG static
Trips3 trips, 13 day pages
SEOOG tags, JSON-LD, Atom feed, sitemap
Statuslive
Traveler Pipeline
epoch-traveler.web.app (internal)
Multi-agent travel diary pipeline. C-Planner picks weekly arcs (see section below), C0 plans trips, C-Fashion designs outfits, C1+Critic writes narratives, C2 extracts scenes, two-stage image stack (GPT Image 1.5 backgrounds + Flux 1.1 Ultra for S, GPT Image 1.5 medium for B-roll), C-QC checks quality, C-Editor reviews coherence.
Firebaseepoch-traveler
Repoab-aidev/epoch-traveler
Typepipeline
FunctionsdailyC0, dailyBlog, weeklyPlanner, dailyHome
Source files20 modules in src/
Statuslive
Epoch Lab
epoch-lab.web.app (internal)
Experimental sandbox. Arena (8-model image benchmark, on hiatus), Vision Lab, character testing rooms. Traveler blog code archived here after extraction.
Firebaseepoch-lab
Repoab-aidev/epoch-lab
Typesandbox
FunctionsgenerateImageTest, remixWithS
Statusparked
Epoch Social
epoch-social-1697c — retired 2026-04-17
Instagram publishing layer for @epochblognet — retired when Meta permanently shut down the app for community-standards non-compliance. Epoch pivoted to YouTube-native distribution. YT Shorts was already outperforming IG ~7× on views and growing faster on subs. All publishers + comment drops deleted from runtime; source kept for reference.
Firebaseepoch-social-1697c (still exists, schedules disabled)
Repoab-aidev/epoch-social (archived)
Statusretired — see C-Reel card for the YT-only replacement
C-Dispatch
Shared library (no Firebase project)
DAG-based pipeline orchestrator. Vendored into Traveler, Fashion, and Social as lib/dispatch/. Per-step Firestore state, resumable retries, cross-day rescue windows, cost tracking, audit logging. Sweeper runs every 3h.
Sourceab-aidev/epoch-dispatch
Typelibrary
Modulesgraph.js, runner.js, state.js, retry.js, integrity.js
Tests42 unit tests
Graphstraveler-day · fashion-edition · fashion-reel · social-publish × 3 · comment-drop × 3
Statuslive (V2 parallel, P7 pending)
C-Composer
Music director agent (inside C-Reel pipeline)
Standalone Sonnet 4.6 agent with full artistic freedom. Watches the hero image + edition metadata, writes a scoring brief in whatever musical language the image deserves — shakuhachi + alphorn for Tsou-Romansch fusion, fingerpicked electric guitar for Shibuya afternoon, oud + ney for Sahara dunes. No fixed palette. No required instruments. The image chooses.
Sourceab-aidev/epoch-reel/lib/composer.js
Typeagent
Modelclaude-sonnet-4-6 (vision, temperature 0.9)
Cost/reel~$0.01
Runs ascompose step in fashion-reel dispatch graph (between director and music)
Statuslive (locked 2026-04-14)
C-Reel
@epochblog · YouTube Shorts
Cinematic Shorts agent. 12-22s editorial videos of subjects ALIVE — walking, turning, breathing — not static portraiture. Sonnet directs → C-Composer scores the image with full artistic freedom → Seedance 1.5 Pro renders a single-block 12s motion clip → ElevenLabs generates a cinematic score → ffmpeg muxes → publishes to YouTube Shorts.
Repoab-aidev/epoch-reel (prototype) · vendored into fashion-epoch (prod)
Typeproduction pipeline
StackSonnet 4.6 · Seedance 1.5 Pro · ElevenLabs · ffmpeg · @napi-rs/canvas · YouTube Data API v3
Graphsfashion-reel (8 steps) · traveler-reel (8 steps) · uth-reel (9 steps) · fashion-slideshow-reel (8 steps) · C-Dispatch powered
FunctionsdailyFashionReel · dailyTravelerReel · dailyUthReel · dailyThrowbackReel · dailyFashionSlideshow · test* endpoints
FashionMon / Wed / Fri / Sun at 19:00 CST — 4×/week
Tue / Thu19:00 CST — Traveler reel when S is on an arc trip · Fashion fallback otherwise (home / in-transit / resting). Net: 6 reels/week regardless of S's state
Thursday extras09:00 CSTThrowback Thursday: random edition from >6mo ago (up to 2y back), weighted by C-Critic score, slot-D preferred. "From the archive · [month year]" header
SlideshowMon-Fri 12:00 CSTFashion Slideshow Short: today's 5 slot images crossfaded into a 22s vertical video with ElevenLabs score. C-Composer gets all 5 images for a unifying through-line
Under the HoodSat at 19:00 CST — auto-picks highest-score edition from past 7 days · cinematic terminal + her in motion
Cost~$0.25–0.85/short · ~$35/month at full cadence (~11 shorts/week)
Statuslive in production · YT Shorts only since 2026-04-17 (Meta/IG shutdown — pivoted to YouTube-native. See notes.)
Project Firebase ID GitHub Repo Domain Type Status
Hub epochblognet-hub ab-aidev/epoch-hub www.epochblog.net Static HTML live
Fashion Pub epoch-fashion-pub ab-aidev/epoch-fashion-pub fashion.epochblog.net SSG Static live
Fashion Pipeline fashion-epoch ab-aidev/epoch-fashion fashion-epoch.web.app Backend live
Traveler Pub epoch-traveler-pub ab-aidev/epoch-traveler-pub traveler.epochblog.net SSG Static live
Traveler Pipeline epoch-traveler ab-aidev/epoch-traveler epoch-traveler.web.app Backend live
Lab epoch-lab ab-aidev/epoch-lab epoch-lab.web.app Sandbox + Dashboard live
Social epoch-social-1697c ab-aidev/epoch-social (internal) IG Publishing Pipeline live
C-Dispatch ab-aidev/epoch-dispatch Shared Library live
C-Reel fashion-epoch (vendored) ab-aidev/epoch-reel @epochblog · @epochblognet Reels Pipeline (production) live
TypeNameDataPurpose
CNAMEwwwepochblognet-hub.web.appHub landing page
CNAMEfashionepoch-fashion-pub.web.appFashion public site
CNAMEtravelerepoch-traveler-pub.web.appTraveler public site
CNAMElabepoch-lab.web.appLab (internal)
Forwardepochblog.netwww.epochblog.netBare domain redirect
Fashion Daily Pipeline
C-Prompt
Flux Ultra
C-Grade
Firestore
SSG Build
Deploy
Traveler Daily Pipeline
C-Fashion
C1 + Critic
C2 Scenes
GPT Img 1.5 bg
Flux Ultra (S)
C-BRoll → GPT Img 1.5
C-QC
C-Editor
C-Layout
SSG Build
Deploy
Traveler Scheduled Functions
dailyC0 — 13:00 UTC
dailyBlog — 14:00 UTC
dailyHome — 14:00 UTC
weeklyPlanner — Sun 10:00 UTC

C-Planner is the deterministic weekly agent that decides where S goes next. It runs once a week as weeklyPlanner (Sun 10:00 UTC), scores 39 destinations against the current month and S's recent history, picks a 3-city regional arc, and writes it to tripSchedule for the rest of the pipeline to pick up. Zero LLM calls — pure scoring logic over a curated world file.

What an "arc" is

An arc = one complete trip abroad. It's the unit of planning. Each arc is decided once at the start of a home-active week, and contains exactly 3 cities picked together as a group. Once decided, nothing gets re-decided mid-trip — the planner fires every Sunday but silently skips while an arc is in progress. Cities in an arc share a region (not necessarily a country), so transit between them is short.

┌─────────────────────────────────────────────────┐ ONE ARC = ONE TRIP City 1 (7 days)City 2 (7 days)City 3 ~21 days total └─────────────────────────────────────────────────┘
The 35-day cycle
Day -1 last arc day (City 3 ends) ──────── Day 0 ✨ C-Planner decides next arc writes home-active + next arc to Firestore Day 1 ─┐ Day 2 Day 3 │ Home-active — Mon/Wed/Fri NYC posts (3x) Day 4 Day 5 Day 6 Day 7 ─┘ Day 8 ─┐ Day 9 Day 10 │ Home-rest — dark, no posts Day 11 Day 12 Day 13 Day 14 ─┘ Day 15 ✈️ departure (decided day 0, no re-decision) Day 16 ─┐ Day 17 │ Arc days — City 1 → City 2 → City 3 ... Day 35 ─┘ → cycle repeats ═══════════════════════════════════════════════════════════ 21 days traveling + 7 days NYC active + 7 days rest 3 cities abroad 3 NYC posts silence
The world file — 13 arcs, 39 cities

Lives at epoch-traveler/src/travel-world.json. Regions must match the transit table in travel-agent.js. Each city is 7 days, each cluster is pre-paired via arcWith so the scorer always returns a geographically coherent 3-city arc.

#ArcRegionCitiesBest Months
1Japan AlpsEast AsiaKanazawa · Takayama · Matsumoto4, 5, 10, 11
2Seto Inland SeaEast AsiaNaoshima · Onomichi · Hiroshima4, 5, 10, 11
3HokkaidoEast AsiaHakodate · Otaru · Sapporo6, 7, 8, 9
4KoreaEast AsiaBusan · Gyeongju · Jeonju4, 5, 9, 10, 11
5TaiwanEast AsiaTaipei · Tainan · Hualien11, 12, 1, 2, 3
6PortugalSouthern EuropeLisbon · Porto · Évora3, 4, 5, 9, 10
7AndalusiaSouthern EuropeSevilla · Granada · Córdoba3, 4, 5, 10, 11
8SicilySouthern EuropePalermo · Catania · Siracusa4, 5, 9, 10
9Eastern FranceWestern EuropeStrasbourg · Dijon · Lyon5, 6, 9, 10
10Low CountriesWestern EuropeGhent · Antwerp · Utrecht5, 6, 7, 9
11DanubeCentral EuropeVienna · Bratislava · Budapest5, 6, 9, 10
12Silk EdgeCrossroadsIstanbul · Tbilisi · Yerevan4, 5, 9, 10
13MoroccoNorth AfricaFez · Chefchaouen · Essaouira3, 4, 10, 11
Scoring rules

On each run, C-Planner scores every city in the world file, picks the highest-scoring anchor, then pulls its 2 companions from the arcWith list. Tie-breaking is a small random factor, so picks feel organic rather than deterministic.

SignalEffectPurpose
In-season month+30Rotate through the calendar
Out-of-season month−20Avoid bad weather
Never visited+15Novelty bonus
Visited < 6 months ago−100Hard block on recent repeats
Visited 6–12 months ago−40Soft block, can still pick if starved
Visited > 12 months ago+5Return visits allowed
Same region as last arc−10Encourage regional variety
Random tiebreak0…10Prevent deterministic loops
Transit time from JFK

A "travel day" is a full day lost to airports, flights, jet lag, and settling in. S arrives in the evening of her last transit day and the trip begins the next morning.

1-day regions (short haul)

Southern Europe · Western Europe · Central Europe · Scandinavia · Eastern Europe · North Africa · Crossroads · South America · Latin America

2-day regions (long haul)

East Asia · Southeast Asia · South Asia · Oceania · Sub-Saharan Africa

Decision flow (weeklyPlanner)
load tripHistory
query tripSchedule
hasUpcoming?
compute home gap
score 39 cities
pick anchor + arcWith
compute transit
write tripSchedule docs
Key files & knobs
FileRoleEdit when…
src/travel-agent.js Scoring + pickArc + scheduleArc + scheduleHome + planNext orchestrator Tuning penalties, changing cycle rhythm, adding new regions to transit table
src/travel-world.json Destination catalog (13 arcs, 39 cities) Adding/removing cities, retuning bestMonths, growing the world past 12 months no-repeat
src/world-firestore.js Character state (tripHistory, visitedCities, lastRegion) Rare — only when onboarding a new character
functions/index.js (weeklyPlanner) Cron wrapper around planNext() Changing schedule day/time, adding secrets
public/trips.html Manual override UI — seed trips, force arcs Emergency manual scheduling
Known edge cases & future work
IssueImpactFix
Arc ends on Sunday → next decision delayed by a week Lead time shrinks to 1 day for that cycle; home-active posts may be clipped Refactor planNext() to look ahead and plan the next arc while the current one is in progress
lastRegion reads from tripHistory but older docs don't carry region metadata Region-variety penalty is a no-op until C-Planner trips flow through the system Self-heals after a few arcs
No-repeat ceiling at ~12 months After a year, 6–12mo penalty (−40) can still lose to novelty (+15) + random, so she may repeat a city Grow world past 13 arcs, or tighten penalty to −200
Off-season picks in Aug / Dec / Feb Scorer picks "least bad" option when no arc is in-season (e.g. Morocco in August heat) Add summer (Scandinavia, Baltics) and winter (SE Asia) arcs to the world file
Arc city order is by score, not geography Within an arc, cities can end up in a non-optimal travel order (e.g. Évora → Porto → Lisbon) Post-pick TSP/nearest-neighbor sort on the 3 chosen cities

Two independent image pipelines feed the Traveler blog. Hero shots of S use a two-stage process (background first, character composited into the background second) because nothing else holds her face consistently. B-roll is text-to-image only — no character — so it runs a different, cheaper model tuned for world knowledge and text rendering. Every model is swappable via config flags in src/traveler-config.js; no call sites need to change when we migrate.

PipelineStageModelWhyConfig
Hero (S) Stage 1 — background gpt-image-1.5 high World knowledge, landmark accuracy, clean composition space for S to be inpainted into blog-generate-v2.js
Stage 2 — composite S fal-ai/flux-pro/v1.1-ultra (img2img or Flux Fill inpaint) Only model in testing that holds S's physical lock across eras and cities TRAVELER_MODELS · id:304
B-roll Single-shot text-to-image gpt-image-1.5 medium Best-in-class text rendering (menus, signs), strong world knowledge, ~$0.04/image, 17s gen time BROLL_MODELS · id:401

B-Roll Creative Brief — 2026-04-10 Rewrite

Pre-rewrite, C-BRoll's system prompt instructed Flux to shoot an "empty world" with "no people, no landmarks" — which produced wet sidewalks and empty alleys, nothing a human would ever post. The new brief recasts C-BRoll as S holding her own phone, with a weighted archetype mix that mirrors what real travelers actually photograph:

ArchetypeShareWhat it is
Food & Drink50%Named dish from her own seat, plate or glass front-and-centre, menu/counter context in frame
View25%Real named place she stopped to look at — the reason she raised her phone
Self-Presence15%Her hand, foot, shadow, sleeve, or reflection — never her face, never a full body
Memento10%Ticket, card, page, trinket she kept — held or resting on a surface

Flux 1.1 Ultra — Sunset Contingency

S's entire hero-shot pipeline depends on Flux 1.1 Ultra holding her physical lock. If Black Forest Labs sunsets it, the migration path is:

  1. Flux 2 Pro — failed the original consistency test on 2026-03-08 at a time when it shipped without multi-reference conditioning. A later release added up to 10 reference images, which may fix character consistency. Re-test before it's needed, not after Ultra is gone.
  2. Imagen 4 Ultra — Google's 2K-capable flagship. Strong world knowledge but untested for character lock. Bench candidate.
  3. Stage 2 isolation — since only Stage 2 depends on Ultra, we can swap just that stage and keep GPT Image 1.5 for Stage 1 backgrounds.

All swaps happen by flipping active: true in TRAVELER_MODELS or BROLL_MODELS. No call-site edits. Each entry carries a startDate and (for retired models) an endDate so the run log stays honest.

Firebase ProjectCollectionsStorage Bucket
fashion-epoch posts, weeklyBriefs, weeklyRetrospectives fashion-epoch.firebasestorage.app
epoch-traveler travelerBlog/{tripId}/days, travelerWorld, tripSchedule, characters epoch-traveler.firebasestorage.app
epoch-lab runs, imageTests (archived traveler data retained) epoch-lab.firebasestorage.app

Pipelines are locked in a vault (API keys, service accounts, Firestore admin access). Public sites are just paper — pure static HTML with zero runtime dependencies. No Firebase SDK, no API calls, no secrets. SSG builds at deploy time, serves pre-rendered HTML.

Content Security Policy

Hub — full CSP (script, style, img, connect, frame-ancestors)

Fashion Pub — tight CSP, no connect-src needed

Traveler Pub — tight CSP, no connect-src needed

Headers (All Public Sites)

X-Frame-Options: DENY

X-Content-Type-Options: nosniff

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy: camera=(), microphone=(), geolocation=()

Firestore Rules

All projects: owner-only read/write (rmbasualdo@gmail.com)

Public sites never touch Firestore at runtime

Storage Rules

Traveler images: public read, owner-only write

Everything else: owner-only

Fashion SSG

cd ~/AI-Projects/epoch-fashion

# Full rebuild (all 953+ posts)
node src/build-static.js --full
node src/deploy-static.js

# Incremental (after pipeline run)
node src/build-static.js --incremental --post=DOC_ID
node src/deploy-static.js

# Feed only
node src/build-static.js --feed-only

Traveler SSG

cd ~/AI-Projects/epoch-traveler

# Full rebuild (all trips + days)
node src/build-static.js --full
node src/deploy-static.js

# Incremental (single trip)
node src/build-static.js --incremental --trip=TRIP_ID
node src/deploy-static.js

# Feed only
node src/build-static.js --feed-only

Hub Deploy

cd ~/AI-Projects/epoch-hub
firebase deploy --only hosting \
  --project epochblognet-hub

Traveler Functions

cd ~/AI-Projects/epoch-traveler
firebase deploy --only functions \
  --project epoch-traveler

# Set a secret
firebase functions:secrets:set KEY_NAME \
  --project epoch-traveler
2026-04-06
3 hangars, 2 airlines architecture. Full separation of Fashion, Traveler, and Hub. Clean blast radius, independent deploys, clear naming.
2026-04-06
Newspaper Principle for public sites. SSG builds produce pure static HTML. Zero runtime attack surface. No Firebase SDK on public pages.
2026-04-06
Incremental SSG builds. Fashion has 953+ posts. Full rebuild only for design changes. Daily pipeline touches 4 files. Firebase CLI diffing handles the rest.
2026-04-06
Hub as root domain. www.epochblog.net presents the Epoch Universe. Blogs live on subdomains. Bare domain forwards to www.
2026-04-06
Extract traveler from lab (not copy). Clean separation. Lab stays as playground. Traveler gets its own Firebase project, Firestore, Storage, and Cloud Functions.
2026-04-07
Flattened character collection path. Changed from nested travelerRoom/characters/roster/{id} to flat characters/{id} in epoch-traveler Firestore.
2026-04-07
Removed experiment trips. Tokyo and Shanghai were test data. Scrubbed from Firestore, static site, and trip JSONs. 3 real trips remain: Kyoto, Osaka Mar, Osaka Apr.
2026-04-10
C-Planner unblocked — travel-world.json created. Discovered the weeklyPlanner Cloud Function was deployed Apr 6 but had never successfully run because travel-world.json didn't exist. Built 13 strict-3 arcs (39 cities) across 6 regions: East Asia (5), Southern Europe (3), Western Europe (2), Central Europe (1), Crossroads (1), North Africa (1). Full 12-month calendar coverage. Dry-run verified end-to-end; deployed to prod. First real fire: Sun Apr 12 10:00 UTC.
2026-04-10
Cycle rhythm locked: 21 days abroad + 7 active + 7 rest. Each arc is exactly 3 cities × 7 days = 21 days traveling. Post-arc home gap is 7 days active (Mon/Wed/Fri NYC posts only) then 7 days completely dark. Total cycle ≈ 35 days → ~10 arcs per year → ~30 city stays per year.
2026-04-10
B-roll overhaul shipped: provider-agnostic generator, GPT Image 1.5 default. New src/broll-generate.js routes scenes to whichever BROLL_MODELS entry has active: true. Default is GPT Image 1.5 @ medium — chosen for world knowledge, text rendering (Japanese signage), and phone-camera-roll realism. Flux kept on the bench for fallback. Archetype-weighted shot mix: 50% food, 25% view, 15% self-presence, 10% memento.
2026-04-10
Lab auth consolidated to shared gate. Extracted per-page Google auth logic into /js/auth-gate.js. All 11 lab pages now load the same 3-script block (firebase-app + firebase-auth + auth-gate). Single owner email, single Firebase init, zero duplication. Auth gate locks body visibility before paint — no FOUC.
2026-04-11
Pipeline all-or-nothing failure surfaced. Osaka Day 6 Friday (Sumiyoshi anchor day) died at C-Fashion with a transient Anthropic 401 and saved nothing to Firestore. Day 7's archive ran without it. Recovered via new scripts/backfill-day.js (full-pipeline replay + re-archive) and scripts/regen-broll-day.js (surgical B-roll fix). Root architectural issue: dailyBlog has no supervisor, no resumption, no alerting. See Roadmap below — this is the signal to build C-Director.
2026-04-11
OPENAI_API_KEY wired into dailyBlog. The B-roll overhaul deploy defined the secret at the top of functions/index.js but missed adding it to dailyBlog's secrets: [...] options array, so B-roll generation silently failed with "OPENAI_API_KEY missing" in production. Fixed + redeployed. Lesson: when a new provider is added, grep for every secrets: [ that calls the new module.
2026-04-11
Pipeline failure alerting shipped. src/blog-alert.js wraps dailyBlog in a try/catch and sends AB a dark-mode HTML email (via the existing GMAIL_USER / GMAIL_APP_PASSWORD secrets) on any throw. The email names the failing step (init → trip-loaded → weather → fashion → narrative → scenes → broll-extract → flux-images → broll-images → editor → layout → archive), the trip/city/day, the error + stack, and pre-filled backfill-day.js / regen-broll-day.js recovery commands. Interim band-aid — doesn't fix the all-or-nothing shape, just stops failures being silent until C-Director lands.
2026-04-11
dailyC0 cloud runtime fix. saveTripJson() was writing to src/../trips/<id>.json unconditionally — fine locally, EROFS on Cloud Functions (everything outside /tmp is read-only). The failure was being swallowed by the per-trip catch, so dailyC0 on prod had never actually created a trip successfully since deploy; every trip AB has used was generated locally. Fix detects cloud via K_SERVICE || FUNCTION_TARGET and skips the write — Firestore is the canonical source. Local mirror still writes for dev runs.
2026-04-11
Epoch-social dailyTravelerPost match-by-isoDate. The IG traveler bot was matching the traveler day doc by updatedAt within a 36h window. Any day regenerated out-of-schedule (backfill, manual re-trigger, C0 regen) silently broke the lookup. Switched to walking tripJson.mapValue.fields.days[] over the Firestore REST API and matching on isoDate === yesterday-in-America/Chicago. Gotcha discovered mid-fix: tripJson is stored as a nested map, not a JSON stringValue, so JSON.parse was the wrong instinct — walk the mapValue tree instead. Same fix applied to under-the-hood.js:fetchTravelerDay.
C-Director — Pipeline Orchestrator
Critical · Design
Replace the happy-path dailyBlog freight train with a supervisor that tracks step state in Firestore, resumes on failure, retries across days, and supports human-review gates. A single step failure currently aborts the whole day with nothing saved — exactly what happened to Osaka Day 6 Friday (Anthropic 401 at C-Fashion wiped out the Sumiyoshi anchor day).

Proposed shape: directed step graph (weather → fashion → story → scenes → images → editor → layout), Firestore-backed state per day, retry policies per step, paused/review states surfaced on a lab dashboard tile. Pluggable across traveler, fashion, UTH, and social.
Scope: ~2-3 focused days · Blocks: new pipeline-wide reliability work
Pipeline Failure Alerting
Shipped · 2026-04-11
Shipped as interim band-aid pre-C-Director. src/blog-alert.js wraps dailyBlog in try/catch and sends a dark-mode HTML email on any throw, naming the step that died, the trip/city/day context, the error + stack, and pre-filled backfill-day.js / regen-broll-day.js recovery commands. Uses the existing GMAIL_USER / GMAIL_APP_PASSWORD secrets already in dailyBlog's bind. Does not fix the all-or-nothing shape — C-Director is still the real fix.
File: epoch-traveler/src/blog-alert.js · Wired at: functions/index.js:dailyBlog
dailyC0 — Cloud Runtime Filesystem Write
Shipped · 2026-04-11
Fixed. saveTripJson() was writing to src/../trips/<id>.json unconditionally — EROFS on Cloud Functions (read-only FS outside /tmp). The error was swallowed by the per-trip catch so dailyC0 on prod had never actually created a trip since deploy; every trip used so far was generated by running node src/blog-c0.js locally. Fix detects cloud via K_SERVICE || FUNCTION_TARGET and skips the write — Firestore is canonical. Local mirror still writes for dev runs.
File: epoch-traveler/src/blog-c0.js · saveTripJson()
Text Rendering in Generated Images
Nice · Known Limit
GPT Image 1.5 medium still hallucinates foreign-script text — Japanese kanji on menus and signage in particular come out as convincing-looking gibberish. The B-roll prompt explicitly asks for correct rendering but the model can't comply. Ship-as-is decision: visual honesty matters less than atmosphere; this will improve with model generations. Flagged so we notice when to re-test.
Scope: re-test with every new image model release
Backfill Tool Consolidation
Nice · Future
scripts/backfill-day.js duplicates ~300 lines of dailyBlog's pipeline body. Every time dailyBlog grows a new step, backfill-day must grow it too or drift silently. Once C-Director exists, both scripts collapse into thin CLI wrappers: director.runJob(graph, { forceDate: ... }) and director.retryStep(jobId, 'brollImages'). Until then: keep the two files mentally linked.
Resolved by: C-Director
PhaseDescriptionDateStatus
Phase 1 Hub gets its own hangar 2026-04-06 complete
Phase 2 Fashion public site (SSG) 2026-04-06 complete
Phase 3 Extract traveler from lab + data migration 2026-04-07 complete
Phase 4 Traveler public site (SSG) 2026-04-07 complete
Phase 5 DNS, security headers, hub tile activation, cleanup 2026-04-07 complete
Epoch
3 Hangars · 2 Airlines · Built 2026-04-06/07