Polish-4 — traffic simulation infra (paused at T29)

Polish-4 shipped a cron-triggered simulator that produces realistic activity through real /v1/admin/* endpoints, deepening the 6 Polish-3 D1-backed surfaces with production-shape data. T25–T29 backend is live. The remaining simulator-finishing work moved to Polish-7 so we can audit, polish, and broaden coverage before deepening further.

TurnStatusTitleScopeGate
T25 Shipped Simulator scaffold + persona base 5-min cron worker, 40 Aperture staff personas, events_studio_simulator_state D1 table, /admin/simulator shell route. 148/148
T26 Shipped Calendar/availability engine Per-persona rolling 14-day availability, deterministic sick/time-off injection. 1680 availability rows + 1682 audit actions live. 149/149
T27 Shipped Event creation cadence 15 templates, capacity- and skill-matched auto-create through existing admin endpoint. 31 events live. Idempotent re-runs. 150/150
T28 Shipped Booking/RSVP curve engine S-shaped arrival curves, past-event finalization with 75/22/3 attended/no-show/cancelled split. 1591 registrations + 3182 booking responses live. 151/151
T29 Backend ✅ Failure-mode injection 5 chaos engines (sick-cascade, overbooking+waitlist, double-booking, payment-declined, bulk-cancel-24h). Audit table with severity + recovery tracking. Backend live; full cumulative gate deferred to Polish-7. deferred

T33 — Polish audit pass (next turn)

One dedicated turn to re-audit the 52 tracked Events-in-Studio routes against the Issue #22 baseline, the latest Claude design output, and this morning's operator findings. Produces the canonical Polish-5 backlog plus any newly surfaced Polish-6 / Polish-7 / Polish-8+ work.

TurnStatusTitleScopeGate
T33 Shipped Polish audit pass
  • Re-walk the 52 routes from Issue #22's gap table — current status per route (real / sample / static / not-built)
  • Compare every live route to its Claude design counterpart — record visual + interaction divergences with screenshots
  • Re-inventory the 12 operator findings filed this morning — verify still-broken, log fixes if any have shipped incidentally
  • Refresh apps/stories-pages/implementation-progress.html with current truth — no more stale numbers
  • Produce polish-5-final-backlog.md with confirmed P5 items in priority order
  • Identify any new candidates for Polish-6 / Polish-7 / Polish-8+ surfaced by the audit
  • Honest "Issue #22 progress" report — what fraction of the 44 pixel-replicas is now real
  • Turn workflow: branch, work, non-ff merge to main, push both repos

Polish-5 — visual polish + operator-filed UX (queued)

Polish-5 keys off the operator's morning findings in polish-5-usability-findings.md, prioritized after T33's audit. Order may be revised by T33; below is the current best ordering. P5-T1 (chrome + icon visual re-sync against Claude design) must ship first — operator-elevated 2026-05-28T~12:50Z.

TurnStatusTitleScopeGate
P5-T1 Shipped Chrome + sidebar + icon visual re-sync vs Claude design Pure discriminating UI pixel-correctness + interaction audit against latest Claude design. No new functionality; just match the design.
  • Sidebar structure (operator: "completely changed in Claude design" — needs full reconciliation)
  • Icon styling — currently "amateurish": tiny icons in comparatively huge boxes. Match Claude design's icon-to-box ratio
  • Panel headers + selectors for Settings, Integrations, Events, etc. — all changed in Claude design
  • Brand mark + tenant switcher layout
  • Interaction audit — button click visuals, hover states, transitions, focus rings should match Claude design
  • 100% pixel correctness, not "close enough"
subsumes item #4
P5-T1.5 Shipped (72 passed gate) Chrome follow-ups: shared <StatCard> + sidebar collapse wiring Bundles two P5-T1 follow-ups before moving to P5-T2:
  • Shared <StatCard> component. StatTile currently duplicated locally in 7 routes (HubsRoute, LandingPagesRoute, ReportsOverviewRoute, BookingResponsesRoute, CheckInsTodayRoute, GuestImportsRoute, RegistrationsTodayRoute), each with 15px-icon-in-30×30-box (50% ratio — "amateurish"). Extract to packages/events-studio-shell/src/components/StatCard.tsx, set design-correct icon-to-box ratio (target ~60-67%), delete the 7 duplicates, spec ratio on each route.
  • Sidebar collapse wiring. P5-T1 added the data-jrni-component="nav-toggle" button but the collapse state isn't wired or persisted. Wire toggle to switch between 196px expanded and ~64-72px collapsed (exact width per design), persist via localStorage key events-studio:nav-collapsed:v1, spec the round-trip across reload.
extends item #4
~64
P5-T2 Shipped (81 passed gate) Tenant switcher + bug-widget cleanup + publish roadmap as hidden route Three things bundled:
  • Higher-Ed tenant scrub item #5 — single-tenant renders as plain span with data-jrni-tenant-mode="single"; ?tenant=higher-ed fixture preserved for tests
  • Bug-widget hotkey gate item #3 — pills hidden by default; double-tap Cmd/Ctrl reveals; Escape, blur, click-outside, or pill-use dismisses; "Press Cmd/Ctrl twice for support tools" hint in the account menu
  • Hidden roadmap live at /internal/roadmap.html on Aperture Pages — canonical source is now this file (voyage/apps/events-studio/frontend/public/internal/roadmap.html) so every deploy refreshes the PM-facing URL automatically
81
P5-T3 Shipped (84 passed gate) Google OAuth callback 401/404 fix item #11 OAuth round-trip lands cleanly. Three pieces shipped:
  • Worker now resolves a tenant-aware EVENTS_STUDIO_BASE_URL and the GSuite callback redirects to events-in-studio.vxge-aperture.porivo.com/settings/integrations/setup?provider=gsuite&gsuite=connected
  • App-host route hydrates the Google Workspace wizard and marks connect-workspace step state connected on land — stable selectors data-jrni-component="gsuite-setup-wizard", data-jrni-step-id, data-jrni-step-state
  • Integrations-host stale URL now returns HTTP 308 (with Location at the app host) — pre-auth, no operator-facing 401
Verified live: app-host → 200; integrations-host → 308; followed redirect → 200 on the wizard.
84
P5-T4 Shipped (87 passed gate) Event-drawer tab full-refresh fix item #12 Drawer tabs converted to path-based URLs (/events/:id, /events/:id/guests|tickets|share|settings) using React Router <Link> so tab switches are router-internal. Root cause was actually useRouteSkeletonLoading(pathname, search) being keyed off location.search — the visible refresh was the route skeleton flashing the background events list, not a duplicate API fetch. Codex pivoted from the directive's <a href> / [location]-deps guesses to the real fix. Stable selectors event-drawer, event-drawer-tabs, drawer-tab added; legacy event-detail-drawer preserved for Issue #22 regression coverage. Live verify: /events/evt-winter-collection-promo/guests — 200, no background flash on tab switch. 87
P5-T5 Shipped (91 + 1 flake) Google setup wizard interaction polish + copy buttons items #7+#8+#9+#10 All four pieces shipped. Step 6 opens OAuth in a new tab, polls every 2.5s until {status:"connected"}, 5-min timeout. Step 4 auto-completes when step 5 credentials land (no-op Validate removed). Step 5 auto-greens on credentials saved (trivial Validate removed). Shared <CopyField> primitive (data-jrni-component="copy-field") applied to OAuth consent app name/domain/scopes, app name, redirect URI, Client ID, Client Secret. Cumulative gate 91 passed + 1 flaky shell HTML load that passed on retry — the flake was a transient net::ERR_NETWORK_CHANGED on initial nav, not a P5-T5 regression. 5 turn-branches landed via non-ff merges (wizard polish → 2x spec-fix → worker port → cumulative spec refresh). Live: Wizard. 91+1
P5-T6 Subsumed by P5-T5 Copy buttons — bundled into P5-T5 item #10 Copy buttons are small enough to bundle with the Validate-flow rework. Ship together so PM walk-through covers the full wizard.
P5-T7 Subsumed by P5-T2 Higher-ed tenant scrub item #5 Shipped in P5-T2 (single-tenant render as plain span[data-jrni-tenant-mode="single"], ?tenant=higher-ed fixture preserved). Row kept for ID-stability across links/notes.
P5-T8 ✅ Shipped + verified (live) Event-creation final-screen polish items #1+#2 Both items done — this row was stale (Queued); corrected 2026-05-30:
  • #1 Publish → navigates to /events: CreateEventRoute.publishAndNavigate() routes to /events/:eventId on publish success.
  • #2 Duplicate Save-draft / Publish buttons removed: the final step renders exactly one of each affordance.
Verified: polish5-t8-create-event-publish-dedupe spec passes live (2/2 re-runs); shipped at gate 132.
132
P5-T8.5 Shipped /events stat-card chrome cleanup + capacity-detail overflow item #14 The four /events stat cards (UPCOMING / LIVE NOW / TOTAL REGISTERED / ARCHIVED) were NOT in the P5-T1.5 seven-route shared <StatCard> migration — still on route-local StatTile with the pre-fix tiny-icon-in-big-box "amateurish" look. PLUS the TOTAL REGISTERED card has the green detail 24% of capacity wrapping word-by-word vertically instead of inline next to the value. Fix is small: migrate to the shared <StatCard> (will inherit the 28×28 / 14px / soft-tinted layout), confirm detail-text wraps correctly. Should bundle as a fast follow-up — small fix, high visible signal because /events is a frequently-viewed PM surface. Could be promoted ahead of P5-T8 / P5-T9 if operator wants the chrome story 100% closed first.
P5-T8.6 Bundled into P5-Chrome-Finale Wizard chrome polish (CopyField density + masked credentials) — folded into chrome finale Items #16 + #17 bundled into the chrome finale turn below per operator direction 2026-05-28T~19:28Z. Single turn for the entire chrome closure.
P5-Chrome-Finale Shipped (100 passed) Shared <Button> + icon library audit + wizard chrome density + masked credentials items #15+#16+#17 Operator direction 2026-05-28T~19:28Z (priority change): "I'm also going to reject the UI as complete… I need that icon component polished turn right now, and I want the cards that we have for these integration steps not to be so stupidly huge… So again, next turn is polish on this integration and finally polish on these icons." Ships the moment P5-T8.5 closes. NO interleaving with P5-T8 / P5-T9 / anything else until the chrome story is 100% closed.
  • Shared <Button> component #15 matching the design's Btn primitive in foundation.jsx — variants (primary / secondary / ghost), proper icon slots, proper caret affordance for dropdowns.
  • Icon library audit — add caret-down, IconLock, IconUnlock, plus, and any missing icons to packages/jrni-ui/src/icons/Icons.tsx. No text characters as icon stand-ins.
  • Deep hand-rolled-button scrub #15 — replace every hand-rolled button + inline SVG + text-character icon. Grep targets cover literal caret/plus glyph stand-ins, <a className=".*button", and <button[^>]*style=". Audit asserts zero matches after the sweep.
  • CopyField density + panel padding #16 — read foundation.jsx for CopyTarget/KeyValue; tighten per-field padding, gap, possibly background/borderless. Reduce wizard step panel padding so cards "fit the available contents horizontally and vertically, and feel more elegant" (operator quote).
  • Connected-state masked credentials #17 — Step 5 connected renders Client ID/Client Secret as •••••••• with inline IconLock; click to unlock + edit + Update.
Stable selectors: data-jrni-component="button" + variants, field-lock; reuses P5-T3/T5 selectors. Specs: button scrub, copyfield density, masked credentials. Live-URL after-screenshots of /events top-right + wizard Step 3 + wizard Step 5 connected. Largest visual turn of Polish-5 but each replacement is pattern-match. Operator-stated patience: "I need that icon component polished turn right now."
P5-IconLibrary-Refresh Shipped (107 passed) Icon library visual quality refresh + platform-wide inline-SVG scrub item #19 Operator direction 2026-05-28T~20:18Z: "I want every icon in the platform to fit in appropriate and professional looking design. That means every icon, whether it's in a stats card or elsewhere, needs to match this component type." After P5-Chrome-Finale shipped, the operator screenshots /events stat cards (UPCOMING / LIVE NOW / TOTAL REGISTERED / ARCHIVED) and /dashboard stat cards (EVENTS TODAY / REGISTERED TODAY / CHECKED IN TODAY / EVENT REVENUE) still feel "garbage" — the chrome finale fixed icon AFFORDANCES (no more text-character `v`/`+` carets) but did NOT touch the visual quality of the icon glyphs themselves.
  • Design-source icon audit — read foundation.jsx + per-screen design JSX to determine canonical icon style (outline vs filled, stroke width, viewport size, optical alignment). If design uses an external library (Phosphor / Lucide / etc.), use it. If custom, transcribe.
  • Replace every icon in packages/jrni-ui/src/icons/Icons.tsx with design-faithful versions.
  • Platform-wide inline-SVG scrub — audit consumers across packages/events-studio-shell/, apps/events-studio/, packages/integration-setup-portal/, packages/jrni-ui/. After this turn, every visible icon sources from the library. Hard requirement: rg '<svg ' packages/events-studio-shell/src/ apps/events-studio/ packages/integration-setup-portal/src/ returns ZERO matches outside the icon library itself.
  • Stat-card layout audit — confirm /events stat cards (shared StatCard from P5-T1.5/T8.5) and /dashboard stat cards (not yet migrated) at least share the same icon visual treatment after this turn. /dashboard migration to shared StatCard remains P6 work.
  • Stable selector data-jrni-icon-source="library|inline" so specs can assert library-sourcing without manual grep.
After-screenshots of /events stat cards, /dashboard stat cards, wizard step icons, sidebar nav, topbar — all confirming design fidelity. P5-T8.9 OAuth 401 demo blocker is REQUEUED behind this turn per operator direction.
P5-T8.9 Shipped (112 passed) Google OAuth "Error 401: invalid_client" — credential precedence fixed item #18 Operator completed the OAuth flow in Google's UI and landed on Google's "Error 401: invalid_client" page instead of the events-studio callback. The credentials stored in Step 5 aren't being accepted by Google. Likely encryption pipeline corruption — the worker reads back a different value than what was typed at save time. Probe the encrypt → store → decrypt → OAuth-URL-construction path; deterministic round-trip spec; live smoke with known-good credentials. Same encryption codebase as P5-T9 (Salesforce decrypt) so this turn may also surface or resolve the Salesforce bug. Operator quote: "clearly something is wrong with the client ID we've saved." REQUEUED 2026-05-28T~20:18Z behind P5-IconLibrary-Refresh per operator direction.
P5-T8.93 Shipped (119 passed) Event status not derived from start_at/end_at vs now — Feb events still "Live" in May item #23 Operator screenshotted /events showing events dated Fri, Feb 6, 2026 with a ● Live pill — three months in the past. Status was a stored field set at create time, never re-evaluated.
  • Operator quote: "Whether or not something's live should be a pure calculation of whether the event happened or is happening right now." Yes.
  • Fix: derived-status helper (start > now → Upcoming / start ≤ now ≤ end → Live / end < now → Completed); updates events-list query + stat-card counts + public-landing route. Stored field left deprecated/NULL.
  • Specs assert status matches temporal derivation for past / present / future fixtures.
  • Demo blocker — pulled ahead of P5-T8.95 / .96.
P5-T8.92 Shipped (116 passed) "Open landing page" returns "event not found" runtime error item #22 Clicking Open landing page on an admin event drawer routes to /public/events/<slug> — Pages returns HTTP 200 (SPA shell loads) but the SPA's data fetch returns "event not found." The admin sees the event as Live; the public landing-page worker can't find it. Most-likely root cause: public events live in a different D1 source than admin events (separate events_public table, or a publish-snapshot pipeline that simulator-seeded events skipped). Could also be slug mismatch or tenant scoping. Operator quote: "If I click open landing on some events I get event not found. This should never happen." Sample URL: https://events-in-studio.vxge-aperture.porivo.com/public/events/winter-collection-promotion. Same "no toy, no vaporware" pattern as the recurring concern — admin claims Live but the user-facing landing isn't actually wired. Probe: which D1 table is the public worker querying, does the admin event exist there with the same slug, what's the simulator's publish-to-public path. Pull ahead of P5-T8.96 (chrome polish) — this is a demo blocker.
P5-T8.96 Shipped (124 passed) Minor-polish bundle — multi-icon pill padding + public landing loading state items #21+#24 Two operator-stated minor findings bundled into one fast turn. Multi-icon format pills now expose data-jrni-format-pill-variant="single|multi" with explicit horizontal padding and 12px icon sizing; single-icon variant keeps the existing 38px square geometry. Public landing route now runs a { status: 'loading' | 'loaded' | 'error' } state machine with data-jrni-public-event-state on the root; skeleton renders during fetch, "Event not found" only renders after the fetch resolves missing. Three specs added (pill padding + loading transition + 404 regression). Honest accounting: directive referenced /events/new but the current shell only mounts /events/create/* — Codex correctly used the existing route rather than add a routing alias inside a styling-scope turn. Live URLs: /events/create for pills; /public/events/winter-collection-promotion for loading state; /public/events/this-event-does-not-exist for 404 regression. Repos: voyage f85b9bd6, voyage-aperture 20bb9ab1. 124
P5-T8.97 Closed by P5-T8.96 Event public page flashes "event not found" before rendering item #24 Closed by P5-T8.96's loading/loaded/error state machine. Public landing route now renders skeleton during fetch and "Event not found" only after fetch resolves missing — the flash is eliminated. Row kept for ID stability.
P5-T8.95 Shipped (121 passed) Account-details page shows wrong identity ("Lori" placeholder) item #20 Clicking the avatar dropdown's Account details menu item brought the operator to a profile page rendering "Lori" — a placeholder / Polish-4 simulator staff persona — instead of their actual authenticated identity. Root cause was fixture/sample account details still being passed into the route even though the tenant shell already had identity-worker whoami session data. Shipped fix: account details now derives name/email/role/tenant from the authenticated session, exposes data-jrni-account-identity-source="identity-worker", and renders Not set for identity-worker gaps instead of falling back to fixtures. Live gate added direct-route and avatar-menu regressions proving no "Lori" text inside data-jrni-component="account-details". Operator quote: "I'm brought to what is clearly not my identity or my configuration. It's clearly some placeholder, somebody named Lori that's fucked up."
P5-T8.98 Shipped Event drawer Guests tab — count mismatch + inert arrows + inert Export item #25 Three sub-issues on the event drawer Guests tab from operator's "Vendor breakfast - 2026-05-28" screenshot:
  • Count mismatch (39 vs 6 shown). Tab badge says 39 guests, page renders only 6, no pagination/Load-more affordance. Either UI hardcodes a render cap or the worker is paginated and the UI ignores the cursor. Per global CLAUDE.md production-first rule: lists MUST be paginated with `page`/`limit`/`total`/`hasMore`. Surface those controls.
  • Per-row arrows inert. Each guest row has a → button that does nothing. Wire to a guest detail view (drawer-in-drawer or `/guests/:id` navigation).
  • Export button inert. The Export button at the panel top-right is decorative. Wire to a CSV/XLSX download handler that fetches the full guest list (all pages) and triggers download.
Three specs (pagination assertion + arrow navigation + Export download trigger). All three on the same tab component, so single turn handles all three. Operator-stated future-pass priority.
P5-T9 Shipped (127 passed) Salesforce integration credential decryption fix item #13 Root cause class (e) — worker D1 binding drift, NOT a crypto bug.
  • Encryption pipeline confirmed clean via byte-for-byte round-trip specs (salesforce-token-crypto.test.ts, salesforce-verify-decrypt.test.ts).
  • Live failure: voyage-aperture-integrations bound to stale voyage-aperture-tenant-ops (old decrypt-error row) instead of canonical aperture-ops. Fresh OAuth against canonical ops decrypts + verifies cleanly.
  • No sentinel/fallback credential path introduced — directive's hard rule respected.
  • Codex flagged an intermediate live-worker version missing Salesforce setup routes → redeployed current source to restore /setup-status + /validate-step/* before final gate.
  • Refs: follow-up merges 93bfd2b6 + 8c9f2f6b; integrations worker 74b3d490; voyage 4574ba6d + tenant 9157189a; live /settings/integrations/salesforce.
127
P5-T10+ TBD Additional items surfaced by T33 audit + ongoing operator review The T33 audit and ongoing operator surface walk-throughs will continue to add polish items. Slot them in here in priority order.

Polish-6 — real-data sweep: 4 priority surfaces ✅ COMPLETE

Operator priority: convert /dashboard, /hubs, /events/templates, /landing-pages from SAMPLE_* to real D1-backed data. All four shipped. Each turn added a server-side aggregate endpoint (bounded/paginated, gateway-routed, source:"d1"), wired the shell to it, removed the SAMPLE_* path, and proved it with a red→green spec + the serial cumulative gate. Closing audit: rg "SAMPLE_" packages/events-studio-shell/src → zero matches in the live path (fixture mode stays intentionally sample-backed).

TurnStatusTitleScopeGate
P6-T1 Shipped /dashboard StatCards → real D1 GET /v1/admin/dashboard/summary (gateway-routed) returns server-computed aggregates only — events-today / registered-today / checked-in-today / event-revenue, each source:"d1". Added idx_events_studio_guests_tenant_registered_at to both ops DBs. No client raw-row reduction. 144
P6-T2 Shipped /hubs → real D1 Bounded GET /v1/admin/hubs?page&limit (gateway-routed) with page/limit/total/hasMore + per-hub server aggregates (eventsCount / visits30d / registrations30d). Hubs already a first-class table — no fake rows; visits30d honestly disclosed as a registration-count proxy until a real hub-visit table exists. 145
P6-T3 Shipped /events/templates → real D1 Bounded GET /v1/admin/events/templates?page&limit — templates derived from real event data (normalized event titles), server-computed usage/registration aggregates, derivation disclosed in the payload (no fake template table invented). Added idx_events_studio_events_tenant_active_updated to both ops DBs. 146
P6-T4 Shipped /landing-pages → real D1 (hardened) /landing-pages already consumed the real /v1/landing endpoint (from P5-T24/T25); this turn hardened it — bounded pagination + server aggregates (visits30d / registrations30d / conversionPct), removed the live SAMPLE_LANDING_PAGES fallback, added idx_landing_pages_tenant_updated to both ops DBs. Closing SAMPLE_* audit ran here: zero matches. 147

Platformization — eliminate aperture-custom code ✅ COMPLETE (operator north star 2026-05-28)

Aperture must contain zero custom code — it is only a deployment of Voyage. Done: all aperture-local workers were upstreamed into Voyage and composed into Aperture, then the aperture-local copies deleted. Identity worker (incl. the phone_number SSO-SMS column) lives in Voyage; notifications consolidated on canonical voyage-notifications (D1 binding drift fixed); integrations / events / porivo-ai / preview-proxy / salesforce-mock / _shared all composed from Voyage.

TurnStatusTitleScopeGate
P5-T11→T25 Shipped Upstream + compose all aperture workers; delete aperture-local copies Identity worker upstreamed (src + migrations incl. phone_number), composed, aperture copy deleted, SSO/login green. Remaining workers (notifications / integrations / events / porivo-ai / preview-proxy / salesforce-mock / _shared) reconciled to Voyage canonical + composed. Zero aperture-custom code remains.

Jim's event-wizard feedback (PM validation) — ✅ J7 #1–#10 + re-val #3 (#44/#45) ALL DONE + Jim-validated

PM validation of /events/new (thread: porivo #voyage-product). Resolved arc: J2/J3 validated; publish→landing (P7-T4); landing/Hubs CRUD (P7-T5); wizard polish (P7-T6/T7); re-val #2 — register submit blocker fixed with a real fill→submit→confirm test (P7-T8), landing polish (P7-T9), event-creation wizard (P7-T10), and #10 email+SMS confirmations launched + verified delivering to a real recipient (P7-T12/T14). Jim confirmed email + SMS received on a live registration (2026-05-29). All 10 actionable items done. Re-val #3 also DONE (P7-T15): #44 (step-3 Dates selectable time list, start + end) and #45 (registration email/phone format validation + international phone country selector → E.164) shipped + deployed. Operator UX #47 (P7-T17) DONE: integration cards now render as distinct staggered index cards. Operator QA #48 (P7-T18, in flight): Google Calendar status pill reads "Disconnected" despite a complete setup — reconciling. Full lists: QA findings.

#StatusItem
J1ShippedNew-event-from-scratch path (no forced template); + Event → blank wizard or dropdown new-from-template.
J2✅ ValidatedHero image upload/import. Jim refinement #33 (P7-T6): show the uploaded image in the console + preview with a clear "received/accepted" confirmation on import (currently only renders in preview).
J3✅ ValidatedDynamic, editable tags derived from page selections — confirmed by Jim.
J4ShippedDedupe registration open/close date+time — removed from Dates screen, kept on Registration.
J5ShippedSeeded default registration questions (First/Last/Email/Phone, formatted + validated) + custom questions.
J6✅ ReadyBranding controls work + published landing renders (P7-T4).
J7✅ ALL #1–#10 done + Jim-validatedAll 10 items shipped, walked live by Claude, confirmed by Jim.
  • register submit (P7-T8 — live 201 + guest); landing polish (P7-T9 — hero #36/#4, register CTA #37/#6, share-link removable #38/#7).
  • wizard (P7-T10 — blank/scratch #39/#1, theme-coherent seed #40/#2, ticket-optional #41/#9, explicit date #42/#8); publish→landing unavailable-flash (#3) gone.
  • #10 email + SMS confirmations launched + verified delivering to a real recipient (P7-T12/T14).
  • Jim confirmed (2026-05-29): "Confirmed the email and SMS were received upon successful registration of a newly created event."
  • Jim re-val #3 (#44 dates time-list picker, #45 registration validation + intl phone) shipped as P7-T15.
J7b✅ Fixed (P7-T4)Publish→public-landing now renders (finding #27). Root cause: public endpoint only matched landing_pages.slug, but public URLs use the event id. Fixed to resolve (slug OR event_id); verified live — operator's event returns JSON 200 with published content; real wizard-publish→public-page e2e spec added. Gate 149.

Operator QA findings — 2026-05-29 (walking the publish flow before handing to Jim)

Live QA by the operator. #27 is the prioritized next turn (P7-T4).

#StatusFinding
#27✅ Fixed (P7-T4, gate 149)Publish→public-landing now renders. Public endpoint resolved only by landing_pages.slug; public URLs use the event id → fell through to the not-live fallback. Fixed to resolve (slug OR event_id); verified live (operator's event → API 200 w/ published content); real wizard-publish→public e2e spec added.
#32✅ Fixed (P7-T5, gate 151)landing-pages + Hubs admin CRUD now works. Added LandingPageEditor route + hub metadata columns/DELETE endpoint; wired create/edit/delete/publish to real D1 on both. Verified live: created+edited a landing page and a hub, both persisted through reload + API. Migration in both ops DBs.
#33✅ Fixed (P7-T6)J2 hero-image upload feedback. Imported/uploaded hero images now show the accepted feedback state + preview image in the admin basics console.
#28✅ Fixed (P7-T6)Event date defaults + validation. New scratch events default to local today+14d, 12:00→13:00; past dates blocked; invalid schedules show inline validation; Continue disabled until valid.
#29✅ Fixed (P7-T6)"Welcome copy" placeholder. Brand-kit voice guidance is now a placeholder only — no longer populates/submits as welcome copy; empty stays empty in preview + payload.
#31✅ Fixed (P7-T7, gate 155)Staff avatars render. Placeholder photoUrls didn't resolve; shared Avatar primitive now swaps failed images → initials fallback (onError). Live: 25 broken → 0.
#30✅ Fixed (P7-T11, gate 160)Simulator controls consolidated + gated behind platform-admin RBAC. First-class users.platform_role claim (migration 0008), server-side requirePlatformAdmin() (non-admin → 401/403 + redirect), one admin-only simulator nav. Live: admin sees one entry + 200; non-admin no chrome + 401/403.

Jim re-validation #2 — 2026-05-29 (J7 still not complete)

#StatusFinding
#34/#35✅ Fixed (P7-T8, gate 156)Public registration now submits. #34: loading state wasn't passed through → "unavailable" rendered during load (fixed). #35: phone validation rejected normal `(415) 555-0199` → Continue silently disabled (now accepts + normalizes US formats, visible errors). Real fill→submit→confirm e2e + live proof: 201, guest created, phone `+14155550199`.
#36✅ Fixed (P7-T9, gate 157)Hero image dedupe. Public hero used the event image as a CSS background (un-assertable, duplicated under added blocks); now renders exactly one <img> with the gradient as a separate layer. Live: heroDataImages 0→1, heroBackgroundUrlCount 1→0.
#37✅ Fixed (P7-T9, gate 157)Single register CTA. Renderer hardcoded a hero CTA + a sidebar reserve-card CTA. Hero CTA is now the single default; sidebar CTA is opt-in via showSidebarRegisterCta (default off), toggle in the landing editor, persisted by the tenant worker. Live: registerButtons 2→1.
#38✅ Fixed (P7-T9, gate 157)Share-link removable. Share card was unconditional; now gated by showShareCard (default off) with a landing-editor toggle. Live: shareSections 1→0; opt-in proof re-enables it.
#39✅ Fixed (P7-T10, gate 158)Blank / start-from-scratch path is now first-class — "Start from scratch" in the type picker yields an empty draft (no theme seed) via a shared blank-draft builder.
#40✅ Fixed (P7-T10, gate 158)Template seeds are theme-coherent — draft seeds keyed off the selected template's own name/category/description; cross-theme markers avoided; unit coverage in create-event-drafts.test.ts.
#41✅ Fixed (P7-T10, gate 158)Ticket-optional / registration-only mode. Step-4 registration-only mode hides ticket tiers, carries through the draft, summarizes "Registration-only — no ticket tiers" on publish; worker accepts blank optional fields (live 201).
#42✅ Fixed (P7-T10, gate 158)No silent date default. Design source treats dates as explicit scheduling fields; scratch dates now start empty and block progression until a valid start/end pair is entered (no hidden today+14d commit).
#43✅ Launched + verified (P7-T12/T14)Jim #10 — email + SMS confirmations launched, both verified to a real recipient. SMS: wired the missing Twilio secrets onto voyage-aperture-notifications (was sandbox no-op) → real Twilio, Twilio API status=delivered to a real phone (lands in spam on first contact — known; enterprise sender deferred). Email: Cloudflare send_email from noreply@porivo.com → operator confirmed inbox receipt; a live probe to an arbitrary external address was accepted (NOT verified-destinations-only). Sent from the Voyage backend, no special-casing.

Jim re-walk (Claude, 2026-05-29): personally drove all 10 items end-to-end on live before any re-engagement. 9 of 10 verified to terminal success — incl. a green live spec that creates a registration-only event and submits a public registration through to "Registration confirmed" + guest read-back (the dead-Continue blocker does NOT recur for registration-only events). #10 (above) launched + verified. The readiness gate did its job — caught the cosmetic-vs-real-dispatch gap before any false "ready" ping.

Jim re-validation #3 + operator UX follow-ups — 2026-05-29

#StatusFinding
#44✅ Fixed (P7-T15, gate 164)Step-3 Dates — selectable time list restored. Replaced the native <input type="time"> hand-type spinner with a selectable time-list combobox (15-min increments, 96 options) for both start and end. Spec asserts the datalist + selectable values; Continue stays enabled.
#45✅ Fixed (P7-T15, gate 164)Registration validation + international phone. Email + phone now have real inline format validation that blocks submit on bad input; phone input has a country selector (flag + dial code, incl. GB) normalizing to E.164. Spec asserts inline [data-jrni-reg-field-error] + Continue disabled on invalid, normalized on valid.
#46✅ Fixed (P7-T16, gate 165)Integration setup panels — width capped.
  • Capped setup/provider panels at 640px (Salesforce field-map table at 820px for its 760px content floor, no wrap).
  • Live before/after: GSuite 1220→640, SF guest-sync 824→640.
  • Added data-jrni-component="integration-provider-card" marker; focused live specs 3/3.
#47✅ Reverted (P7-T22)Staggered index cards — REVERTED per operator. P7-T17 added an even-row 20% inset stagger; operator rejected it ("only changed Gmail, that's bogus"). P7-T22 removes the stagger (keeps the #46 640px cap + card chrome) and replaces the direction with collapsible completed cards (#52).
#48✅ Fixed (P7-T18 + P7-T19)Google Calendar status pill read "Disconnected" despite a complete setup.
  • Root cause: the gsuite walkthrough stores its connection as ('gsuite','custom'), but the google_calendar pill queried ('google_calendar','calendar') → no row.
  • P7-T18 reconciled the catalog connection + verification lookups; P7-T19 made a successful gsuite verify-step record a passing verification (ok=1) + reconciled the Run-verification handler.
  • Live-verified (data layer): latest verification ok=1 + active connection → pill derives "Connected".
  • Refs: deploys dce566cd + 34d78ace; spawned UX follow-up #50.
#50✅ Fixed + live-verified (P7-T20)Setup walkthrough + OAuth config disappear once connected. Fixed: ConnectedProviderManage now renders the walkthrough for any setup-backed provider (not just stripe/zoom), keeps the "Provide OAuth Client credentials" step editable (Unlock stored credential → Update credentials, no disconnect needed), and suppresses the misleading empty "Configuration" card. Component test + my own live Playwright check on the deployed host both green. Deploy Pages 634fb7dd.
#51✅ Fixed + live-verified (P7-T21)Calendar entry click full-refreshed the screen. Root cause: a route split (`/events` vs `/events/:eventId/:eventTab?`) remounted the calendar on selection. Fixed by collapsing to one optional route /events/:eventId?/:eventTab? — selection updates params in place, drawer opens, URL deep-links, no remount. Spec proves the calendar DOM node persists (not a remount); gate 172/172. Claude re-ran it live. Deploy e64190b1.
#52✅ Fixed + live-verified (P7-T22)Completed cards collapse (roll-up); stagger dropped. Reverted the #47 stagger (cards align; 640px cap + chrome kept). Completed setup-step cards now collapse to a header disclosure by default (aria-expanded/aria-controls + chevron + pointer cursor); click the header to expand/re-collapse; non-complete steps stay open; a just-locally-completed step stays open so the success isn't hidden. Component tests + Claude's live Playwright check (collapsed→click→expand→re-collapse) both green. Deploy 9e7f04c0.
#49Awaiting operatorGmail test send "succeeded" but not received. NOT a stub — hits the real Gmail API (no Google mock; GOOGLE_GMAIL_API_BASE_URL unset), returns a real message id. Most likely a self-send: messages.send sends from the connected account; a self-addressed message files under Sent/All Mail, not Inbox (subject "Aperture setup verify"). Awaiting operator confirmation (search All Mail) before any code change.

Polish-7 — simulator realism (underway)

Simulator realism: P7-T1 fixed the time window, P7-T2 made the cron cadence grow the dashboards organically, P7-T3 shipped interactive operator controls. P7-T4 (priority) pivots to the operator's 2026-05-29 QA: fix the broken publish→public-landing round-trip (#27). Then event-wizard date/validation + welcome-copy (#28/#29), staff avatars (#31), and the platform-admin RBAC gate for the simulator (#30) + chaos-engine gate + real visit tables. See Operator QA 2026-05-29.

TurnStatusTitleScopeGate
P7-T1 Shipped Current-window traffic realism (finding #6)
  • Root cause: fixed-tick/admin paths accepted arbitrary timestamps → 2039-2049 audit rows, while real-D1 surfaces query today/30-day windows
  • Deterministic clock clamp (now ± 30d) across simulator state / events / bookings / failures routes; bounded POST /trigger/backfill (default 6, cap 12); worker unit tests
  • Bounded live cleanup: archived 438 far-future events, purged ~12k far-future audit/guest rows; seeded believable current-window mix
  • Live after: dashboard events today 2 / registered 491 / checked-in 421 / $71.9k / 2026-05-29; beyond +30d = 0
147
P7-T2 Shipped Registration-curve realism + ongoing daily cadence Fixed registrationsSkippedNoCurveDue — current-window events (incl. backfill/auto-created) now get a registration curve so each cron tick registers against them. Bounded organic per-tick booking cadence so the today/30-day aggregates grow tick-over-tick instead of freezing at the P7-T1 backfill snapshot. Worker tests cover curve coverage + tick-over-tick growth. 147
P7-T3 Shipped Operator simulator controls Interactive /admin/simulator?panel=controls: persisted pause/resume, bounded rate multiplier (0.25–3), six per-engine on/off flags. GET/PATCH state endpoints with bounded validation; dispatcher reads persisted state every tick. Live toggle-off proof: all engines off → tick produced 0 registrations + 0 chaos events; reset restored. Migration applied to both ops DBs. (Shipped UNGATED — gating to platform admins is finding #30, queued.) 148
P7-T4 Shipped Fix publish→public-landing round-trip (#27) Root cause: public endpoint resolved only by landing_pages.slug, but public URLs use the event id → scheduled-event not-live fallback → blank. Fixed to resolve (slug OR event_id) with slug precedence; shell "View landing" routes to /public/events/<id>. Real wizard-publish→public-page e2e spec added. Verified live: operator's event → API 200 w/ published content + public page renders. Gate 149. 149
P7-T5 Shipped landing-pages + Hubs admin CRUD (#32 — cleared Jim J7) Added LandingPageEditor route + hub metadata columns + DELETE endpoint; wired create/edit/delete/publish to real D1 on both surfaces; fixed picker navigation + hub save stale-state. Live proof: created+edited a landing page and a hub, both persisted through reload + API. Migration in both ops DBs. Gate 151. 151
P7-T6 Shipped Event-wizard polish: J2 image feedback (#33) · date defaults/validation (#28) · welcome-copy placeholder (#29) Dates default today+14d / 12:00→13:00 local, past dates blocked, Continue gated on valid schedule. Welcome-copy guidance = placeholder, not value. Hero image shows accepted state + preview. Also root-caused a create→editor race (async create handlers were void-wrapped) + stabilized two pre-existing flaky specs. Gate 154. 154
P7-T7 Shipped Staff UI avatars render (#31) Placeholder photoUrls didn't resolve; shared Avatar primitive now swaps failed images → initials fallback (onError) + stable selectors. Live: 25 broken → 0. Gate 155. 155
P7-T8 Shipped Fix public registration submit blocker (#34/#35, Jim re-val #2) Root causes: loading-state not passed through (→ "unavailable" flash) + phone validation rejecting normal formats (→ Continue disabled). Now accepts/normalizes US phone, visible errors, real loading state. Real fill→submit→confirm e2e (clicks Register, normal phone, asserts 201 + confirmation + reads guest back). Live proof: 201, guest created, phone normalized. Gate 156. 156
P7-T9 Shipped Landing-page polish (#36/#37/#38) One real hero <img> (was a CSS bg that duplicated); single default Register CTA with opt-in sidebar CTA; share card opt-in (default off) with editor toggle; tenant worker persists both flags. Live before/after: heroDataImages 0→1, registerButtons 2→1, shareSections 1→0. Gate 157. 157
P7-T10 Shipped Event-creation polish (#39/#40/#41/#42) First-class blank/start-from-scratch path (#1); theme-coherent template seeds (#2); registration-only mode hides ticket tiers + worker accepts blank optional fields, live 201 (#9); explicit scratch dates, no silent default (#8, design-checked); publish→landing unavailable-flash verified gone (#3). Direct-API 201 proof + focused live spec 1/1. Gate 158. 158
P7-T11 Shipped Platform-admin RBAC primitive + consolidate/gate simulator controls (#30) First-class users.platform_role claim (migration 0008) surfaced via whoami + edge; server-side requirePlatformAdmin() (non-admin → 401/403 on simulator endpoints + redirect, service actors allowed); one consolidated admin-only simulator nav. Red-on-main proven; focused live 2/2. Also stabilized brittle networkidle waits in old P5/P6/P7 gate specs. Gate 160. 160
P7-T12 Shipped Public registration → confirmation email/SMS (#43 / Jim #10) Public register path now enqueues a real confirmation (email always; SMS when phone present) via the existing event.registration.confirmedNOTIFICATIONS_OUTBOX_QUEUE mechanism; idempotent; UI "sent" copy gated on actual enqueue. Live-proven: real D1 outbox rows email sent + SMS sent. Clean single gate, no reruns. Closes the last Jim item. Gate 161. 161
P7-T13 Shipped Events-worker unit-suite audit + chaos-engine gate groundwork Broad @voyage/events-worker test RED→GREEN: 4 failed files/33 tests → 56 files/542 tests, zero quarantines (all stale-date fixtures fixed). One deterministic chaos engine (overbooking-waitlist) gated + double-booking-conflict worker coverage. Anti-churn rule held: 1 isolated flake → 1 focused rerun → proceeded. Gate 162. 162
P7-T14 Shipped Email/SMS notification launch — delivery verification + hardening Email + SMS launched + verified to a real recipient. SMS real Twilio (delivered); email Cloudflare send_email (probe confirmed it accepts arbitrary recipients — not verified-only; operator confirmed inbox receipt). Delivery-verification spec asserts real providers (not sandbox); Twilio/retry/DLQ failure hardening. (Resend scaffold added here is being reverted in P7-T15 — Cloudflare-only per operator.) Gate 162. 162
P7-T15 Shipped Jim re-val #3: Dates time-list picker (#44) + registration validation/intl phone (#45) + revert Resend #44 step-3 Dates: replaced native type=time with a selectable time-list combobox (96 options, start + end). #45 registration: email/phone inline format validation (blocks submit) + an international phone country selector (flag + dial code → E.164). Reverted the unwanted Resend scaffold (Cloudflare-only per operator). Gate 164. 164
P7-T16 Shipped #46 integration-card max-width cap + payment-declined-rollback chaos engine Capped integration setup/provider panels at 640px (field-map 820px, no wrap); live before/after GSuite 1220→640, SF guest-sync 824→640; data-jrni-component="integration-provider-card" marker. Gated the payment-declined-rollback chaos engine (P7-T13 pattern). Gate 165; 2 old polish5 nav flakes → focused rerun 5/5 (anti-churn). 165
P7-T17 Shipped #47 integration cards as staggered index cards + 2 chaos engines Distinct card chrome (design tokens) + 16px gaps + staggered even-row 20.0% inset via margin-left: max(0px, min(20%, calc(100% - 640px))) (responsive collapse, 640px cap held). Gated staff-sick-cascade-cancel + bulk-cancel-24h — chaos backlog closed. Gate 171 (1 unrelated landing-CRUD flake → focused rerun 1/1, anti-churn). Deploy worker c0037eb1 / Pages d7843285. 171
P7-T18 Shipped #48 Google Calendar status-pill reconciliation Reconciled the google_calendar catalog status (connection + verification lookups) to the gsuite ('gsuite','custom') keys. Test-first, demonstrated the half-fix trap. Worker suite 384 tests; gate 170/171 (unrelated P6-T4 aggregate flake → focused rerun 1/1). Deploy dce566cd. Live check: connection now recognized → pill is "Needs attention" (no ok=1 verification recorded yet) → P7-T19 takes it to "Connected". 171
P7-T19 Shipped #48 follow-through: gsuite verify-step → "Connected" Successful gsuite verify-step now records a passing integration_connection_verifications ok=1 row + the google_calendar Run-verification handler reads the reconciled keyspace. Worker suite 387 tests; gate 169/171 (2 old flakes → focused rerun 2/2). Deploy 34d78ace. Live-verified: latest verification ok=1 + active connection → pill derives "Connected". 171
P7-T20 Shipped #50 keep walkthrough + OAuth config reachable/updatable once connected Walkthrough now renders in the connected view for any setup-backed provider; "Provide OAuth Client credentials" step editable (Unlock→Update, no disconnect); misleading empty-config card suppressed. Also root-caused + fixed an old P5-T4 drawer-remount (route collapse to /events/:eventId/:eventTab?). Component test + Claude's live Playwright check both green. Deploy 634fb7dd. 171
P7-T21 Shipped #51 calendar entry click → reactive update (no full-refresh) Collapsed the events route split to one optional route /events/:eventId?/:eventTab? so calendar entry selection updates params in place (no remount). Spec proves the calendar DOM node persists; gate 172/172 no reruns; Claude re-ran live. Deploy e64190b1. 172
P7-T22 Shipped #52 revert stagger + collapsible completed setup-step cards Stagger removed (aligned cards, 640px cap kept); completed setup-step cards collapse to a header disclosure (aria-expanded/chevron), click to expand. Component tests + Claude live check green; gate 173. Deploy 9e7f04c0. Closes the events-studio polish cluster (#46–#52). 173
P8-T1 ✅ Shipped + live-verified De-fraud #1: user/team management → real identity tenant_memberships Identity-worker admin member API (GET/POST/PATCH/DELETE, admin-gated via permission catalog, bounded) + useTeamMembers hook + live host wiring; SAMPLE_TEAM_MEMBERS removed. Found+fixed a real prod-D1 role-constraint mismatch (migration 0010) + CORS + 2 flaky tests. Claude re-ran the live acceptance spec (invite→role→remove via UI+API, 1/1) + confirmed 12 real members in D1. Gate 173/174 (1 race → focused rerun). Deploys identity 081439bb + Pages 044d3fa7. = SSO Phase 1. 173
P8-T2 ✅ Shipped (feature verified) De-fraud #2 (small): reports "Today" drilldown → real D1 New GET /v1/admin/reports/drilldowns/today (bounded D1 today check-ins/registrations) + useReportsTodayDrilldown hook; /reports Today cards consume real totals (data-report-source="events-api"); SAMPLE_DRILLDOWNS_TODAY removed. Claude verified the endpoint directly (HTTP 200, real D1, 0.5s, no rate-limit). Deploy worker d0affa63 + Pages cfc5faf6. (Surfaced a gate-health issue → P8-T3.) ~real
P8-T3 ✅ Shipped + Claude-verified Gate stabilization — restore a deterministic, trustworthy per-turn live gate Defined a canonical deterministic core gate — scripts/events-studio-live-gate.sh, 7 specs / 69 tests (shell-smoke + polish6 real-D1 ×4 + polish8 team-management + polish8 reports-today-drilldown). Fixed the flaky waitForResponse races (reports spec now reads the endpoint directly + asserts rendered markers), relocated volatile simulator/seed-dependent specs to a separate suite (events-studio-volatile-live-gate.sh — coverage preserved, not deleted). No product code touched. Codex proved 69/69 ×2 + post-merge; Claude independently re-ran 69/69 on events-in-studio (a 64/69 first run was Claude's own stale admin.-host override, not a gate flake — corrected). Voyage a44d7183 + aperture e5ecbb4f. 69/69
P8-T4 ✅ Shipped + Claude-verified De-fraud #3: command-center KPIs + check-in pace + activity feed → real D1 New GET /v1/admin/dashboard/command-center (source:"d1") + useCommandCenterData hook → real kpiMetrics (registered 50 / checked-in 13 / waitlist 1), 60-min checkInPace, bounded activity; SAMPLE_KPI_METRICS/SAMPLE_CHECK_IN_PACE/SAMPLE_ACTIVITY removed from the live path. polish8-command-center-real spec asserts real markers + API-value matches + sample-absence. Claude independently probed the endpoint (real D1) + re-ran the gate (command-center green; lone 69/70 fail = unrelated templates-smoke flake, passed focused rerun 829ms). Codex caught+fixed a real marker bug (ActivityFeed markers normalized to card → owned wrapper). Worker fae75258 + Deploy run 26672537073 success; live at /dashboard/command. Voyage d9dcc7b5 + aperture c0a5a3a9. 70/70
P8-T5 ✅ Shipped + Claude-verified De-fraud #4: campaigns + notification templates → real D1 (record CRUD, no mass-send) Real D1-backed /v1/admin/campaigns + /v1/admin/notification-templates (8 record-only endpoints, source:"d1", safety:{dispatchEnabled:false}, migration + indexed tables + seed) + useCampaignsData/useNotificationTemplatesData hooks through /comms + /comms/templates; SAMPLE_CAMPAIGNS/SAMPLE_NOTIFICATION_TEMPLATES removed from live path. Codex caught the live-404 = edge-gateway route table missing the new paths → fixed + deployed BOTH workers (events fcdddb01 + gateway 312f685d). Claude probed both endpoints (real records + dispatchEnabled:false + pagination) + gate green (lone 70/71 = unrelated public-hub-smoke flake, focused rerun 344ms). Deploy run 26673490354 success. Voyage 24dcc5f1 + aperture aa5f9ddb. 71/71
P8-T6 ✅ Shipped + Claude-verified De-fraud #5: segments + invite templates + send-invite records → real D1 (record CRUD, no dispatch) Real D1 /v1/admin/segments + /v1/admin/invite-templates + /v1/admin/invites (10 endpoints, 3-table migration + indexes); SAMPLE_SEGMENTS/SAMPLE_INVITE_TEMPLATES + fake @segments.local removed. Segment audience computed server-side from events_studio_guests — Claude probed live: 6 / 1 / 13 / 37 (real; checked-in 13 = P8-T4 KPI, internally consistent). dispatchEnabled:false, route scan confirms no dispatch binding. Edge-gateway routes added proactively + both workers deployed (events 5c3a81d6 + gateway a3b6fa5f). Claude probed both endpoints + gate green (lone 71/72 = unrelated /reports-smoke flake, focused rerun 705ms). Voyage ffaa71da + aperture 1bfbebcb; live at /guests/segments + /guests/invite. 72/72
P8-T7 ✅ Shipped + Claude-verified De-fraud #6: dashboard alerts → real D1-computed (agenda verified already-real) New GET /v1/admin/dashboard/alerts (source:"d1") computes real alerts from D1 — capacity ≥90% / waitlist / registration follow-up / check-in pace + an "operations clear" fallback; SAMPLE_ALERTS removed from the live path. Claude probed live: 5 real alerts tied to real event ids (alert-capacity-evt-style-consultation, alert-waitlist-evt-spring-sale-launch, …). Agenda confirmed already-real (/v1/admin/calendar/agenda → real event ids), no patch needed. Edge-gateway needed no change (/dashboard/* already routed). Bonus: cleaned a leftover SAMPLE_CHECK_IN_PACE ref. Gate 73/73; events worker 8eab8f55 + Deploy run 26675098174 success; live at /dashboard. Note: the shell-smoke nav tweak (waitUntil:commit) shifted the recurring flake (nav-timeout → faster visibility race) but didn't kill it — proper Playwright retries fix deferred to P8-T8. Voyage 2cb72fb0 + aperture 7c23d9da. 73/73
P8-T8 ✅ Shipped + Claude-verified De-fraud #7: guest-imports history → real D1 + proper shell-smoke retries fix Real GET /v1/admin/guest-imports (backed by contact_import_batches, 4 real seed batches; SAMPLE_GUEST_IMPORTS removed).
  • Fixed a route-priority bug (/:guestId was capturing /guests/imports) + added the edge-gateway route.
  • Recurring shell-smoke flake FIXEDretries:1 scoped to shell-smoke only (de-fraud specs still fail loudly) → gate 74/74 clean (first clean run in 4 turns).
  • Verified: regression-probe confirmed campaigns / segments / alerts / command-center still real (no 500s).
  • Refs: events worker 5ea243f3 · gateway dac41028 · deploy 26675810908 · voyage cd6f5bdb + aperture 2a3a8af1 · live at /guests/imports.
74/74
P8-T9 ✅ Read shipped + Claude-verified De-fraud #8: workspace settings (locations + branding + account) → real D1 (READ) Real GET /v1/admin/settings (source:"d1", migration on aperture-ops, edge-gateway route + both workers deployed) → 4 real locations + real branding (#0A6B68) + real account; SAMPLE_LOCATIONS/SAMPLE_BRANDING/SAMPLE_ACCOUNT_DETAILS removed from the live display. Claude probed the endpoint + gate green (74 passed + 1 flaky auto-retried = EXIT 0; the retries fix caught a live-load /guests nav flake within the run — no manual rerun). Write handlers still no-op (onCreateLocation/etc., onSaveBranding) → P8-T10. Voyage a8f8d5a5 + aperture ac642d72; live at /settings/locations, /settings/branding, /account/details. 75/75
P8-T10 ✅ Shipped + Claude-verified (live CRUD round-trip) Settings WRITE: make the inert location/branding buttons interactive (real CRUD) Added POST/PATCH/DELETE location routes + PATCH branding (tenant-scoped, validated, source:"d1", index on aperture-ops); wired all four handlers (onCreateLocation/onUpdateLocation/onDeleteLocation/onSaveBranding) to real round-trips. Claude drove the live write path personally: POST→201 (real D1 location loc-7d539c22…), GET read-back→present, DELETE→200→gone, cleaned up to baseline. Spec is a true create→read-back→UI-markers→delete→confirm-gone→branding→restore round-trip (PM-validation terminal-success-state bar). Gate 76/76 (75 + 1 flaky auto-retried, EXIT 0). Account stays read-only (no save form). Workers 75fc2883/096f8339 + Pages 26677414164; voyage 10a08bb0 + aperture 29c10d1e. "Complete coverage = interactive" gap CLOSED. 76/76
P8-T11 ✅ Shipped + Claude-verified — WAVE CLOSED De-fraud completeness audit + finish any remaining live-path SAMPLE_ Classified all ~37 SAMPLE_* consts with evidence (REAL / fixture-only / higher-ed-only / FRAUD) and finished the long tail.
  • 9 more FRAUD surfaces fixed — billing, security, workspace-general, create-event draft, CSV preview/mapping, notifications feed, VIP confirmations, branding — now boot empty/not-configured on live.
  • New polish8-defraud-completeness spec (6 tests, RED-proofed); gate 82/82 + 6/6.
  • Honest classification: billing/security have no backend → "empty-not-fake" (not claimed real); public landing/register outside the verdict; mass-send + inbox-inbound deferred-by-design.
  • Refs: voyage 8e70b90a + aperture 9a15b045 · deploy 26678301246.
  • De-fraud wave: 11 turns + 4 checked-already-real → CLOSED.
82/82
P8-T12 ✅ Shipped + Claude-verified Docs audit (Phase 1): audit /help, author credible core docs, capture screenshots Audited /help (11-entry gap table) and authored credible docs.
  • 10 English articles covering every real flow + 6 new categories + 18 live screenshots.
  • New polish8-help-docs-real spec (RED-proofed, 2 tests): articles render + NO internal terms.
  • Verified: source grep + live-render spec both clean; gate 83 + 1 retry = EXIT 0, help-docs spec 2/2.
  • Refs: voyage d81c109c + aperture ebd3499b · deploy 26679071508 · live at /help.
  • Translation = Phase 2 (after operator language sign-off).
84/84
P8-T13 ✅ Shipped + Claude-verified Help-docs i18n foundation + supported-language recommendation The app was English-only (no i18n framework). Built the foundation:
  • Locale model (HelpLocale + locales.en baseline).
  • Real URL-state-preserving language switcher on /help (writes ?lang=; refresh-safe + deep-linkable).
  • Honest English fallback for pending locales ("English content is shown while <language> translation is pending").
  • No bulk machine-translation — quality risk + needs the operator's language decision.
  • polish8-help-i18n-real spec asserts: switch→?lang=es, reload keeps the language, deep-link /help?lang=fr→French, fallback renders.
  • Verified: clean gate 86/86 EXIT 0, i18n spec 2/2, banned-term re-check clean.
  • Refs: voyage 157dbc1d + aperture ec188597 · deploy 26679818574.
  • Recommended language set (operator to confirm): en · es · fr · de · pt-BR · ja.
86/86
P8-T14 ✅ Shipped + Claude-verified (live re-run) Full-lifecycle cross-surface capstone e2e (system-level verification) The system-level check the per-turn gate doesn't do — a true end-to-end lifecycle proving the de-frauded surfaces work together with real live D1 data.
  • polish8-lifecycle-capstone.spec.tsretries:0, idempotent hard-delete cleanup across 5 D1 tables.
  • Create real event → publish landing → register guest via admin API (no public submit → no external send, proven by notification_outbox==0).
  • Assert registeredCount:1 + report aggregates (registrationsTotal:1 / byStatus.registered:1 / byTicketTier.general:1 / byChannel.Manual:1) + command-center activity + dashboard registered-today delta.
  • Assert all 5 rendered UI surfaces (admin drawer, guest list, report page, command-center, public landing) show the same persisted data → hard-delete → all 5 tables back to zero.
  • No product bug found. Passed 5/5 (focused, pre-merge ×2, post-merge, + Claude's own live re-run, 21.3s).
  • Claude read the spec end-to-end + re-ran it personally against merged main — the de-frauded system genuinely works together.
  • Refs: spec/gate-only (no deploy) · aperture 01468add + voyage f3b42636 · gate → 87.
87/87
P8-T15 ✅ Shipped + Claude-verified Canonical-gate flake stabilization (deterministic green, no rerun crutch) Pre-merge gate #2 exposed two recurring NON-product flakes that force a focused rerun: (1) polish8-segments-real asserts exact equality between an API count and a re-fetched page count, which races the live traffic simulator (3701→3703); (2) polish8-help-docs-real hit a nav ERR_ABORTED. Both are test-robustness issues. Harden them to deterministic green (segments: tolerate the simulator's monotonic drift while still proving the count is real/server-computed/non-fixture; help-docs: robust nav) WITHOUT weakening the de-fraud guarantee or adding retries to retries:0 specs. Prove each fix by running it 3–5× consecutively + the gate twice-green pre-merge with NO focused rerun. Test-only; same class as P8-T3/P8-T8. 87
P8-T16 ✅ Shipped + Claude-verified (live) Help-docs translation (i18n Phase 2) Populate real translations on top of the P8-T13 English baseline so the /help switcher renders localized content (honest English fallback stays for anything untranslated).
  • Recommended set (operator-unblocked): es · fr · de · pt-BR · ja.
  • Quality bar — idiomatic, markup-preserving, banned-term-clean in every locale.
  • RED→GREEN spec asserts a translated phrase renders per locale + switcher URL-state holds.
+1
P8-T17 ✅ Shipped + Claude-verified (live) Campaign + invite mass-send (real dispatch) Wire the campaigns/invites “Send” buttons to real dispatch through the proven notifications worker (voyage-aperture-notifications — Twilio + Cloudflare email, P7-T14).
  • Bounded, idempotent, deduped (no unbounded blast; re-send doesn't double-dispatch).
  • Safety (my discipline, not a gate): tested against a controlled test audience I own — never the live guest list.
  • Acceptance: send → real notification_outbox rows (provider=twilio/cloudflare, status=sent) → dedupe-verified → cleanup.
+1
P8-T18 ✅ Shipped + Claude-verified (live) Inbox inbound ingestion (guest reply → thread) The inbox is send + actions today (P8-T4); this makes it two-way — a guest email/SMS reply lands on the matching inbox thread instead of vanishing.
  • Inbound webhook endpoints (Cloudflare Email Routing + Twilio inbound) → parse + match to guest/thread → append a real D1 inbox message (direction=inbound).
  • Idempotent (provider-message-id dedupe), bounded, sanitized.
  • Acceptance: POST synthetic inbound email + SMS → message appears in-thread + renders in the live inbox → duplicate POST no-ops → cleanup.
+1
P8-T19 ✅ Shipped + Claude-verified (live) Billing real backend (honest: real D1/activity-backed, no fabricated charges) Turn the “empty-not-fake” billing surface into a real module backed by D1 + actual tenant activity.
  • Real plan/tier + usage derived from real activity (events, registrations, messages dispatched), server-computed + bounded.
  • No fabrication — if there are no real invoices/charges, show an honest usage-this-period state, never invented amounts.
  • Acceptance cross-checks usage against the tenant's real activity aggregates; no SAMPLE_ values.
+1
P8-T20 ✅ Shipped + Claude-verified (live) Inbound EMAIL worker bridge (complete the P8-T18 email path) CF Email Routing can't POST to an HTTP endpoint — it needs a Cloudflare Email Worker. This builds the missing bridge.
  • Email Worker: receives inbound mail → parses → POSTs to the existing /inbound/email endpoint (reuses its dedupe/persist).
  • Confirmation Reply-To → a dedicated inbound subdomain (apex stays Google Workspace, untouched).
  • Operator's remaining step: a couple of CF dashboard clicks (enable Email Routing on the subdomain → “Send to a Worker”).
+1
P8-T21 ✅ Shipped + Claude-verified (live) Security real backend (honest: real data, no fabricated controls) Turn the “empty-not-fake” security surface into real platform/D1-backed data.
  • Real account security info + integration auth statuses; a real audit trail where feasible (high-value admin actions → D1 → bounded feed).
  • No fabrication — no fake 2FA/password toggles; SSO controls referenced as managed by Voyage SSO (Fleet's domain), not invented.
  • Acceptance asserts real source:"d1" data; honest empty/managed-elsewhere where there's no backing.
+1
P8-T22 ✅ Accepted (Claude live-verified) porivo.net Reply-To (config var) + inbound-email body-parse fix Closed the inbound-email loop after the operator wired reply@porivo.netevents-inbound-email worker (live test reply landed ✅).
  • Confirmation Reply-To is now a config var EVENTS_REPLY_TO_ADDRESS = reply@porivo.net, wired into every send path (no more hardcoded domain) — verified events 12/12 · notifications 9/9.
  • Fixed the inbound worker MIME body parse — Gmail multipart/alternative + quoted-printable now decodes to the real reply text (not the empty fallback); 4/4 unit, including a fixture of the operator's exact failure.
  • Deployed events + notifications + events-inbound-email workers; runbook → porivo.net. Operator to re-send a live reply to confirm body lands.
92
Inbound wiring ✅ SMS live · email reply LANDED (porivo.net) P8-T18 inbound: provider→inbox delivery needs dashboard wiring The inbound endpoints are deployed + verified; real provider delivery needs two credential-gated dashboard steps (Codex's tokens 403'd / Twilio creds are write-only secrets):
  • Cloudflare email — Email Worker bridge built + deployed (P8-T20); Reply-To wired to reply@inbound.porivo.com. Operator step: enable Email Routing on the inbound.porivo.com subdomain (apex stays Google) → “Send to a Worker” → events-inbound-email. Runbook: docs/events-inbound-email-runbook.md.
  • Twilio inbound SMS webhookconfigured by operator ✅ (replies now flow to /inbound/sms).
Inbox (checked) ✅ Already real Message inbox — operator's 3rd named area Checked during P8-T4 review: the inbox is NOT fraudulent — useInboxMessages is D1-backed for the default tenant (/v1/admin/inbox list + detail; real actions mark-read/unread, archive/unarchive, flag, reply; pagination + search + status filter). Only remaining gap = inbound ingestion (guest email/SMS reply → new thread) which needs provider webhooks (Cloudflare Email Routing + Twilio inbound) + likely operator config; deferred to a deliberate later turn, not an unattended overnight one. real
Polish-8 wave ✅ COMPLETE — 25 turns, all Claude-verified live · clean bill of health De-fraud / real-data wave — Opus-audited backlog (operator overnight directive 2026-05-29) Done — all 25 turns live-verified:
  • P8-T1 user/team management → real tenant_memberships
  • P8-T2 reports “Today” drilldown → real D1
  • P8-T3 canonical live-gate stabilization
  • P8-T4 command-center KPIs / pace / activity → real D1
  • P8-T5 campaigns + notification templates → real D1
  • P8-T6 segments + invites → real D1 (server-computed audience)
  • P8-T7 dashboard alerts → real D1
  • P8-T8 guest-imports history + shell-smoke gate-flake fix
  • P8-T9 settings READ → real D1
  • P8-T10 settings WRITE → real CRUD round-trips (interactive)
  • P8-T11 completeness audit — de-fraud wave CLOSED
  • P8-T12 help-docs Phase 1 (10 articles + 18 live screenshots)
  • P8-T13 i18n foundation (URL-state language switcher)
  • P8-T14 full-lifecycle cross-surface capstone
  • P8-T15 canonical-gate flake stabilization (deterministic, no rerun crutch)
  • P8-T16 help-docs translation — es · fr · de · pt-BR · ja
  • P8-T17 campaign + invite mass-send — real bounded/deduped dispatch (controlled-audience tested)
  • P8-T18 inbox inbound ingestion — guest reply → thread (SMS live; email bridge in P8-T20)
  • P8-T19 billing real backend — D1/activity-backed plan + usage, no fabricated charges
  • P8-T20 inbound email worker bridge — Cloudflare Email Worker
  • P8-T21 security real backend — real account/integration data + real audit feed, no fabricated controls
  • P8-T22 porivo.net Reply-To (config var) + inbound-email body-parse fix — Gmail multipart body now decodes correctly
  • P8-T23 real visit tables — killed the visits_30d = SUM(registration_count) proxy; real events_studio_page_visits table + public beacon + true conversion%, simulator-seeded (901 organic rows)
  • P8-T24 Salesforce interactive import — mock seeded w/ 36 demo Contacts; real import wizard → guests land source='salesforce' → read back → cleanup (Claude re-ran the acceptance spec live: 1 passed)
  • P8-T25 canonical-gate hardening — help-i18n split per-locale (deterministic 7/7, no rerun crutch); all-guests CSV export real; calendar pre-fetch chrome neutralized
Outcome — clean bill of health (operator-ordered 5-agent revalidation, all PASS):
  • Tests 715+ green / 0 dishonest · Simulator deployed+cron+organic D1 · Source fraud sweep clean (no SAMPLE_ on the live admin path) · Live gate real-d1 + live mutation · Guests/Salesforce round-trip live-verified.
  • Both revalidation gaps closed (Salesforce import → P8-T24; help-i18n flake → P8-T25). Also shipped: real visit tables + voyage main reclaim (Fleet work preserved on build/phase-G-identity-main).

The de-fraud wave is COMPLETE, clean, and CLOSED (operator stand-down 2026-05-30). SSO/IAM + the tenant-management UI belong to the Voyage Fleet Management sibling app — not this wave.

98
P7-T21+ ✅ Visit tables done (P8-T23) Real visit tables (done in P8-T23); enterprise SMS sender (10DLC); #49 if a fix emerges Real visit tables shipped in P8-T23 (the visits_30d = SUM(registration_count) proxy is gone; real events_studio_page_visits table + beacon). Still deferred (operator-gated, not de-fraud): 10DLC brand/campaign for clean SMS inbox placement; #49 Gmail cross-account delivery — under investigation, becomes a turn only if a code fix is identified. done

Beyond Polish-7 — the rest of Issue #22

Issue #22 listed 44 pixel-replica routes; Polish-3 addressed 6 (events, guests, calendar, reports, inbox, booking responses) and Polish-6 added 4 more (/dashboard, /hubs, /events/templates, /landing-pages) — 10 of 44, ~23% now real-D1-backed. The remaining ~34 routes (below) are candidate Polish-8+ waves.

AreaStatusScope
Event detail 7-tab shell Not started Details / Landing page / Related services / Pricing / Guest management / Event notifications / Message guests. Largest single block of unbuilt functionality.
Dashboard home Not started Awaiting Figma per Issue #22. Blocked on full sidenav drop.
Notifications stack Not started Notifications worker exists with liquidjs templating but UI not wired. Email composer + SMS composer + notifications list page.
Segments / Campaigns Not started Pixel replicas, no backend.
/your-business Not started 6 screens: locations, staff, resources, business-details. Pixel replicas.
/account + /settings/* Not started 6 screens: account, billing, branding, general, security, team. Stub labels or pixel replicas.
Bulk importer Not started Events + customers bulk import flows. Pixel replicas.
Customize / Templates / Canvas Not started 3 screens at the Claude Skill boundary. Pixel replicas.