Flip 360 Commission Platform
INTERNAL PMO PAPER · Confidential — for Steerco distribution only · Tabled at SC#3 (2026-07-06)
← Backlog register  ·  EPIC-08
WS2 · Tech (Commission Engine + CRM) Delivered Sprint AD-1
EPIC-08

Sprint AD-1: Domain-bound Matt release + Carla approval workflow + .com retirement

Owner (Accountable)
Carla Oliver (Chair · policy owner) · AI agent team (CTO/EA/SA delivery)
Consulted
Mathew Punter (CRM operator · downstream recipient of URLs) · Corrina McGowan (WS1 · brand-voice on public surfaces)
Steerco Links
D110
Depends On
EPIC-04 · EPIC-01

Context

Prior to Sprint AD-1, the codebase served all surfaces from a single flip360crm.com origin (Matt CRM, investor brief, PMO library co-mingled). Carla directed on 2026-07-04 that this be split into three purpose-scoped .org domains before the 06 Jul CRM go-live and the 08-10 Jul AU/NZ investor meetings. Carla's verbatim directives (Sprint AD-1 kickoff): 1. "i dont want to use the old urls anymore. the cosaiflip360.org is my domain to run my PMO governance its not a BAU end user domain, its just my own place to store my own things on like having google drive" 2. "im confused? flip360.......org is the naming convention for the end user utility. flip360investor.org and flip360crm.org. cosaiflip360.org is my PMO library" Locked-in domain policy: - flip360crm.org → Matt's CRM tool + Handshake commission engine (BAU end-user) - flip360investor.org → Interactive investor brief for $500K raise (BAU end-user) - cosaiflip360.org → Carla's PMO library (private governance) - flip360crm.com + www.flip360crm.com → RETIRED Additionally, Carla directed that she reviews before Matt gets URLs — she ticks approval boxes on a review page, THEN Matt gets domain URLs. This required a new checklist surface at /engagement/pmo/matt-release-review with 4 panels (CRM surfaces, investor surfaces, vendor deferral disclosures, three-destination audit trail).

Outcome Statement

Three purpose-scoped .org domains bound to the Cloudflare Pages project with host-scoping middleware enforcing path allow-lists per scope; .com domains retired; magic-link email fallbacks fixed to .org so 06 Jul CRM launch survives even if AUTH_BASE_URL secret is unset; a public /handshake explainer live at flip360crm.org with 4 sections (event flow, rule chain, ledger inspector, money-flow example) and read-only D1 inspector returning live rows; a Carla-only /engagement/pmo/matt-release-review checklist surface at cosaiflip360.org with 4 panels, sessionStorage progress meter, and an "Approve for release" decision block with Matt's 3 URLs + DNS CNAME paste instructions.

Sub-Epics

EPIC-08a · Sprint AD-1
Domain binding + .com retirement + magic-link email fallback fix
AU + NZ combined
Acceptance criteria
  • flip360crm.org bound to CF Pages project flip360 via API v4 (POST /accounts/{aid}/pages/projects/flip360/domains) — domain id 085d95d8-f2f3-4b70-8856-75cfcaefb3f0, zone 16768c87fc3da2040ed4a966b248b5b7
  • flip360investor.org bound to CF Pages project flip360 via API v4 — domain id b33c0459-401d-4b29-860c-2791fcd27f1a, zone 770631f52fdbadb06fab194a15fd17c2
  • flip360crm.com + www.flip360crm.com unbound from CF Pages project (retired per Carla directive)
  • Both .org zones already delegated to CF nameservers (anderson, rosalie), status active
  • 5 functional .com → .org fixes in src/data/crm-auth.ts (comment line 274, AUTH_BASE_URL fallback line 303, AUTH_FROM_EMAIL fallback line 316, HTML footer link line 386, plain-text footer line 404) — critical for 06 Jul magic-link email correctness if AUTH_BASE_URL secret unset
  • 4 additional .com → .org fixes in src/routes/crm.tsx (user copy, fallback URL, comment, base URL fallback)
  • 1 outbound link fix in src/routes/chain-test.tsx line 454
  • 1 status card copy fix in src/routes/pmo-ops.tsx line 503
  • CNAME record paste instructions returned to Carla (API token lacks zone.dns.write scope — 3-step manual paste in CF dashboard: zone → DNS → CNAME @ → flip360.pages.dev → proxy ON)
Routes
flip360crm.org (bound) flip360investor.org (bound) cosaiflip360.org (bound, pre-existing)
Files
src/data/crm-auth.ts (5 fixes) src/routes/crm.tsx (4 fixes) src/routes/chain-test.tsx (1 fix) src/routes/pmo-ops.tsx (1 fix)
API token had pages scope but not zone.dns.write. CNAME records must be pasted by Carla in CF dashboard manually. Both zones pre-delegated so paste is the only remaining action for host-scope enforcement to go live.
EPIC-08b · Sprint AD-1
Host-scope middleware — SINGLE SOURCE OF TRUTH for domain policy
AU + NZ combined
Acceptance criteria
  • src/lib/host-scope.ts created — 181 lines, 4 exports (HostScope type, 4 host constants, classifyHost, isPathAllowedForScope, rootRedirectForScope, hostScopeMiddleware)
  • HostScope type: crm | investor | pmo | dev — 4 scopes classified from c.req.header('host')
  • HOST constants: HOST_CRM=flip360crm.org, HOST_INVESTOR=flip360investor.org, HOST_PMO=cosaiflip360.org, HOST_DEV=flip360.pages.dev
  • CRM_ALLOW_PREFIXES: /crm/, /crm-, /onboarding, /consent/, /handshake, /verify, /c/, /chain/, /me/, /api/, /app/, /legal, /devices/, /faq, /brand, /static/, /favicon, /robots.txt
  • INVESTOR_ALLOW_PREFIXES: /investors, /api/, /static/, /favicon, /robots.txt
  • PMO + DEV scopes = fail-open (no allow-list) — Carla and dev preview URL can access anything
  • Root-redirect per scope: crm → /crm/hub, investor → /investors/brief-v2, pmo/dev → null (pass-through)
  • Middleware wired into src/index.tsx line 154 — app.use('*', hostScopeMiddleware()) placed after observability + banner, before all routes
  • Old .com redirect handler removed from src/index.tsx (was lines 449-461, obsolete after .com retirement)
  • Cannot end-to-end test via preview URL curl (CF returns 403 on Host spoofing) — verification will happen post-CNAME against real .org domains
Routes
(applies to all routes via app.use('*'))
Files
src/lib/host-scope.ts (created, 181 lines) src/index.tsx (middleware wired at line 154)
AB-tail-5b discipline applied: atomic MultiEdit + grep-verify BEFORE build. Build clean at 142 modules after Step 2. Preview URL Host-header rejection documented as expected CF security behaviour — not a defect.
EPIC-08c · Sprint AD-1
Public /handshake explainer + read-only D1 ledger inspector
AU + NZ combined
Acceptance criteria
  • src/routes/handshake.tsx created — 569 lines, 2 routes (GET /handshake, GET /api/handshake/inspect)
  • Soft-gate: HANDSHAKE_GATE_KEY='flip360', STORAGE_KEY 'f360-handshake-gate' (different from investor gate to avoid cross-surface auth pollution)
  • 4 sections rendered: pageHead + heroBlock, eventFlowSection (7 FLOW_STEPS: referral → consent → chain → commission → GST → payout → RCTI), ruleChainSection (Rule 1/2/3 evidence chain), ledgerInspectorSection (live D1 query), moneyFlowSection (WORKED example: RRP=$200, 1% baseline + 14% CM = 15% take)
  • /api/handshake/inspect queries D1 tables commission_events, ledger_entries, payout_batches (best-effort with try/catch graceful fallback per Rule 3 evidence-pack discipline)
  • Prod-verified: returns 3 live rows from each table including Carla's real PAYOUT-2026-05-07-001 batch
  • siteNav active=null (handshake not in primary NavKey type — using null is correct per site-chrome.ts line 44)
  • Route wired at src/index.tsx via app.route('/', handshake) — mounts /handshake and /api/handshake/inspect
  • Interim deploy verified at https://6623e741.flip360.pages.dev/handshake — HTTP 200, all 12 content markers present
Routes
/handshake /api/handshake/inspect
Files
src/routes/handshake.tsx (created, 569 lines) src/index.tsx (route mount)
Prod-verified against live D1 rows including Carla's PAYOUT-2026-05-07-001 batch — demonstrates the money-flow chain is not mocked. Best-effort inspector pattern (try/catch per table) preserves graceful degradation if any table missing.
EPIC-08d · Sprint AD-1
Carla-only /engagement/pmo/matt-release-review — 4-panel approval checklist
AU + NZ combined
Acceptance criteria
  • src/routes/matt-release-review.tsx created — 35,191 chars, 1 route (GET /engagement/pmo/matt-release-review)
  • Panel A: 17 CRM surfaces with clickable path links + checkboxes (PANEL_A_SURFACES constant)
  • Panel B: 4 investor surfaces + 11 content assertions (PANEL_B_SURFACES + PANEL_B_CONTENT_CHECKS constants) — regulator-anchored number verification
  • Panel C: 4 vendor deferrals (Twilio, SendGrid/Resend, OpenAI, Stripe) with impact classification + individual ack checkbox each — HONEST DISCLOSURE ledger for informed approval
  • Panel D: 6-row audit trail table (sandbox commit, GitHub origin/main, CF Pages deploy, 2 domains bound, 1 domain retired) — three-destination discipline made visible
  • Client-side sessionStorage checkbox state under matt-review-{panel}-{idx} keys
  • Progress meter: computed live from checked state across all 4 panels
  • "Approve for release" decision block with Matt's 3 URLs (flip360crm.org, flip360investor.org?key=flip360, flip360crm.org/handshake?key=flip360) + DNS CNAME paste instructions
  • Route lives on cosaiflip360.org (PMO scope — fail-open per host-scope middleware) so Carla can access but Matt (on flip360crm.org scope) cannot
  • Route wired at src/index.tsx line 333 via app.route('/', mattReleaseReview)
Routes
/engagement/pmo/matt-release-review
Files
src/routes/matt-release-review.tsx (created, 35191 chars) src/index.tsx (route mount at line 333)
Enforces Carla's directive: "Carla reviews before Matt gets URLs". Matt does not receive URLs until Carla ticks the approval boxes on this surface. Panel C surfaces vendor deferrals for informed approval — not silent — per Rule 1 defensibility.
EPIC-08e · Sprint AD-1
Pre-flight sweeps + three-destination audit trail + register close-out
AU + NZ combined
Acceptance criteria
  • CRM pre-flight sweep: 17 surfaces tested against interim deploy — 0 defects, 4 vendor deferrals documented, artifact JSON written to /tmp/preflight/crm-preflight.json
  • Investor brief pre-flight: $500K raise confirmed, all NZ regulator numbers verified (FMA, FSPR, REINZ, IAA, NZQA, DIA Charities Services), 3 surfaces PASS, artifact JSON written to /tmp/preflight/investor-preflight.json
  • Resolved 3 apparent "defects" as correct behaviour: /onboarding 302 (redirects to step/1), /crm/status/live 0-byte (role-gate for un-authed viewers), 13,500 + 6,450 investor numbers (compact 13.5K + 6.5K form with green Regulator badges)
  • sleep 10 discipline applied after wrangler pages deploy (twice-caught lesson) — Step 3.5 interim deploy
  • AB-tail-5b discipline: 13 consecutive clean builds across Steps 2-6 (142 → 143 → 144 modules)
  • D-SEQ-01 discipline: no writes to engagement.tsx RAID_LOG or STEERCO_DECISIONS registers — this EPIC-08 lands cleanly in pmo-backlog.tsx only
  • Additive routing discipline: 3 new isolated files (host-scope.ts, handshake.tsx, matt-release-review.tsx) — no rewrites of existing route files beyond the 11 .com→.org string replacements
  • Register close-out: EPIC-08 with 5 sub-epics (08a-08e) inserted into BACKLOG_2026_07_04 EPICS array — mirrors EPIC-07 field shape exactly (id/epic/workstream/owner/consultedBy/status/sprintAnchor/dependsOn/steercoLinks/context/outcomeStatement/subEpics[]/hazards[]/ratificationNote)
Routes
/engagement/pmo/backlog-tech (renders EPIC-08 with Delivered badge)
Files
src/routes/pmo-backlog.tsx (EPIC-08 inserted) /tmp/preflight/crm-preflight.json (artifact) /tmp/preflight/investor-preflight.json (artifact)
Three-destination audit trail preserved throughout: sandbox commit → GitHub origin/main → Cloudflare prod. AB-tail-5b + sleep 10 disciplines held. All work additive — no existing epic content mutated.

Hazards

  • DNS CNAME paste is a Carla-side manual action — until she pastes the 2 CNAME records (@ → flip360.pages.dev, proxy ON) in the CF dashboard for both zones, host-scope enforcement cannot go live and Matt's 3 URLs will not resolve
  • Host-scope middleware cannot be end-to-end tested via preview URL (CF rejects Host-header spoofing with 403 as security measure) — verification only possible against real .org domains post-CNAME
  • Vendor deferrals (Twilio, SendGrid/Resend, OpenAI, Stripe) surfaced in Panel C — CRM operates in manual-send mode for magic-link emails until SendGrid/Resend keys land; this is HONEST DISCLOSURE not silent degradation
  • Magic-link email fallback fixes assume AUTH_BASE_URL secret may be unset on 06 Jul — belt-and-braces: fallback strings now point to .org so even secret-unset case survives
Ratification note for SC#3 chair
For SC#3 chair (2026-07-06): EPIC-08 was delivered under Sprint AD-1 across 8 sequential steps (bind → host-scope → handshake → interim deploy + acceptance sweep → CRM preflight → investor preflight → review checklist → register close-out + final deploy). All Carla directives honoured verbatim: (1) three purpose-scoped .org domains ("flip360investor.org and flip360crm.org. cosaiflip360.org is my PMO library"), (2) .com retirement ("i dont want to use the old urls anymore"), (3) Carla-reviews-before-Matt-gets-URLs approval workflow (matt-release-review checklist surface). Three-destination audit trail: sandbox commit · GitHub origin/main · Cloudflare prod flip360.pages.dev. AB-tail-5b discipline held (13+ consecutive clean builds this session). sleep 10 post-deploy discipline held (Step 3.5 + final Step 8). D-SEQ-01 discipline held (no writes to engagement.tsx registers — RAID_LOG and STEERCO_DECISIONS untouched; this EPIC-08 is the register entry for Sprint AD-1). HONEST DISCLOSURE ledger surfaced in Panel C of matt-release-review — 4 vendor deferrals disclosed to Carla for informed approval, not silent. Remaining Carla-side action: paste 2 CNAME records in CF dashboard (one per .org zone) to complete DNS cutover; API token lacked zone.dns.write scope so this action could not be automated.
← Back to backlog register  ·  Flying minutes 2026-07-04 →
Matt's page