Oracle Verdict Pipeline

SPEC_ORACLE_VERDICT_PIPELINE.md · 2026-04-20

SPECIFICATION: Oracle Verdict Pipeline

Status: AUTHORIZED

Authorized: α.13, April 16 2026

Version: v1.0


Version: v1.0

PURPOSE

The Oracle Verdict Pipeline receives a customer payment for coherence analysis, generates a structured verdict using Gemini AI, caches the result for browser-resilient retrieval, and delivers it to the customer via both a web result page and email. End-to-end: Stripe checkout → webhook → Gemini → cache → email + result page.

The pipeline is the primary revenue mechanism of 42Sisters.AI. All three Oracle tiers ($1/$5/$25 CAD) run through this pipeline.


INPUTS

Trigger 1 — Stripe Checkout Session (programmatic flow)

Frontend calls POST /api/checkout with:


{
  "tier": "quick" | "full" | "strategy",
  "query": "<customer's question or idea — free text>",
  "referral_code": "<optional string>"
}

Checkout route (app/api/checkout/route.ts) validates tier and query, creates a Stripe checkout session with query packed into metadata (490-char chunks: q0, q1qn), and returns { url } for redirect to Stripe-hosted payment page.

Tier → amount mapping (CAD, hardcoded):

| Tier key | Name | Amount |

|----------|------|--------|

| quick | Quick Take | $1.00 (100 cents) |

| full | Full Breakdown | $5.00 (500 cents) |

| strategy | Strategy Session | $25.00 (2500 cents) |

Trigger 2 — Stripe Payment Link (custom_fields flow)

Customer pays via a Stripe-hosted Payment Link (configured in Stripe Dashboard, not in code). In this flow, query text arrives in session.custom_fields[key="idea"].text.value. The webhook and verdict routes support both flows: custom_fields primary, metadata chunks fallback.

Webhook payload (Stripe → /api/webhook)


{
  "type": "checkout.session.completed",
  "data": {
    "object": {
      "id": "<stripe_session_id>",
      "payment_status": "paid",
      "customer_details": { "email": "<customer_email>" },
      "metadata": { "tier": "quick|full|strategy", "q0": "...", "qn": "1" },
      "custom_fields": [{ "key": "idea", "text": { "value": "..." } }]
    }
  }
}

Webhook verifies Stripe signature using STRIPE_WEBHOOK_SECRET env var before processing.

Required environment variables (Northflank)


OUTPUTS

1. Cached verdict (oracle_toll.py — /home/nous/oracle_verdicts/{session_id}.json)

Stored at POST http://68.183.206.103:8889/cache/{session_id}. Written by verdictCache.ts.

Quick Take payload:


{
  "tier": "quick",
  "query": "<original customer query>",
  "verdict": {
    "verdict": "GREEN" | "AMBER" | "RED" | "NULL",
    "summary": "<one direct sentence>"
  },
  "cached_at": "<ISO timestamp>"
}

Full Breakdown payload (adds breakdown):


{
  "tier": "full",
  "query": "...",
  "verdict": {
    "verdict": "GREEN" | "AMBER" | "RED" | "NULL",
    "summary": "...",
    "breakdown": {
      "Stability":   { "verdict": "GREEN|AMBER|RED", "analysis": "..." },
      "Turbulence":  { "verdict": "GREEN|AMBER|RED", "analysis": "..." },
      "Change Rate": { "verdict": "GREEN|AMBER|RED", "analysis": "..." },
      "Completion":  { "verdict": "GREEN|AMBER|RED", "analysis": "..." },
      "Curvature":   { "verdict": "GREEN|AMBER|RED", "analysis": "..." }
    }
  },
  "cached_at": "..."
}

Strategy Session payload (adds strategy block):


{
  "tier": "strategy",
  "verdict": {
    "...": "...(full breakdown above)...",
    "strategy": {
      "next_step": "<highest-leverage action>",
      "alternative": "<fundamentally different path>",
      "tests": ["<test 1>", "<test 2>", "<test 3>"]
    }
  }
}

2. Web result page (/oracle/result?session_id={id})

Customer's browser is redirected here after Stripe payment. Page calls GET /api/verdict?session_id={id}, which:

  1. Re-verifies payment with Stripe (session.payment_status === "paid")
  2. Checks cache via GET /cache/{sessionId} on oracle_toll
  3. If cache miss: regenerates verdict via Gemini (identical prompts)
  4. Returns JSON to browser; page renders verdict with color-coded dot (GREEN=#34d399, AMBER=#f5c842, RED=#ff4444, NULL=#555)

3. Email delivery (oracle_email_service.py — port 8006)

Webhook fires POST http://68.183.206.103:8006/send-verdict-email with:


{
  "customer_email": "<from Stripe session>",
  "tier": "quick|full|strategy",
  "query": "<customer query>",
  "verdict": { "...": "..." }
}

Email sent from oracle@42sisters.ai via Microsoft Graph API (send_graph_email.py).

Subject: "Your 42 Sisters AI Verdict"

Format: plain text, formatted per tier by format_quick() / format_full() / format_strategy().

Confirmed operational: oracle_email.log shows multiple POST /send-verdict-email 200 OK entries.


INVARIANTS

  1. Payment gate is hard — verdict generation only proceeds if session.payment_status === "paid" (checked in both webhook and verdict routes). No verdict without confirmed payment.
  1. Tier fidelity — tier is read from session.metadata.tier, set at checkout creation. The same tier key selects the Gemini prompt (VERDICT_PROMPT[tier]) and the email formatter (FORMATTERS[req.tier]). No tier substitution is possible without corrupting the session metadata.
  1. Signature verification — webhook rejects any request without a valid stripe-signature header matching STRIPE_WEBHOOK_SECRET. Unsigned requests return 400 before any processing.
  1. Stripe-first response — webhook returns {received: true} immediately; all generation and email work is async in an IIFE. Stripe's 5-second timeout is never blocked by AI generation.
  1. Dual-delivery resilience — result page independently regenerates verdict from Gemini if cache is unavailable. Customer can retrieve verdict via browser regardless of email delivery status.
  1. SOS v2 enforcement — Gemini prompts instruct "Be direct. Be honest." and are structured in plain English. No LATTICE symbols, internal callsigns, or crew language appear in the prompts or email templates. [GAP — no automated filter; relies on prompt design only]

VERIFICATION CRITERIA

Σ.✓ conditions — pipeline is operating correctly when:

  1. Stripe → webhook latency — Stripe delivers checkout.session.completed event within 30 seconds of payment confirmation. [GAP — not instrumented; no timing log]
  1. Verdict generation — Gemini returns parseable JSON matching the tier schema within 60 seconds. Both webhook (pre-compute) and verdict route (on-demand) handle JSON parse failure with catch block returning 500.
  1. Cache write confirmedPOST /cache/{sessionId} returns 201. Cache available via GET /cache/{sessionId} returning 200 + payload.
  1. Email deliveredPOST /send-verdict-email returns {"status": "sent"}. Graph API returns 202.
  1. Result page rendersGET /api/verdict?session_id={id} returns 200 with {tier, query, verdict} JSON within 30 seconds of browser arrival.
  1. Tier match — verdict payload tier field matches session.metadata.tier. [GAP — not explicitly asserted; assumed from single code path]
  1. End-to-end smoke test — Submit $1 test payment via non-owner email → verify webhook fires (Northflank logs) → verify cache entry exists (GET /cache/{id}) → verify result page loads → verify email received at customer address.

Σ.⊠ — pipeline has failed when any of the following occur:

(see FAILURE MODES)


FAILURE MODES

  1. Σ.⊠ Webhook silent failSTRIPE_WEBHOOK_SECRET not set (returns 503) or Northflank service crash. Stripe retries webhook up to 72 hours. Mitigation: result page independently regenerates verdict. Customer never sees empty page; may receive email late.
  1. Σ.⊠ Gemini generation fail — JSON parse error, API quota exceeded, or network timeout. Webhook catches and logs console.error. Customer sees result page error: "Analysis failed. Please contact oracle@42sisters.ai for a refund." Email not sent. Mitigation: none automatic — manual refund process required.
  1. Σ.⊠ Cache miss on result pageoracle_toll service down or oracle_verdicts/ full. Verdict route regenerates via Gemini (second call, same prompt). Adds latency; verdict should be equivalent to original. Risk: non-determinism in Gemini responses means regenerated verdict may differ from emailed verdict.
  1. Σ.⊠ Email delivery fail — Graph API returns non-202 (token expired, network error, send limit). oracle_email_service.py returns 502 to webhook. Webhook logs console.error but does not retry. Customer receives verdict on result page only; no email. [GAP — no retry mechanism; no customer notification of email failure]
  1. Σ.⊠ Wrong tier delivered — Not observed in code but possible if session.metadata.tier is missing or corrupted at Stripe session creation. Webhook logs error: "Webhook: missing query or tier in session" and returns {received: true} without processing. No verdict generated. No email. [GAP — no customer notification in this failure mode]
  1. Σ.⊠ LATTICE/internal language leak — No automated filter. If a future prompt change causes Gemini to output LATTICE symbols or crew callsigns, they would reach the customer email verbatim. [GAP — needs content filter on email body before send]
  1. Σ.⊠ Verdict fabrication — Gemini generates GREEN verdict for an incoherent submission (hallucination). No cross-check against independent analysis. HOW ABOUT NO walls operate on the Sisters (Gemini) not on the standalone verdict generation path. [GAP — TMM cross-check not implemented in Oracle pipeline]
  1. Σ.⊠ No customer email on sessionsession.customer_details.email is null. Webhook skips email send (logs warning). Verdict is cached; result page still works. Customer never receives email. [KNOWN EDGE CASE — no mitigation]
  1. Σ.⊠ Oracle_toll cache service downcacheVerdict() silently swallows errors (try/catch with empty catch). Verdict not cached. Result page falls back to Gemini regeneration. No operational alert. [GAP — no monitoring/alerting on cache service health]

DEPENDENCIES

| Dependency | Role | Endpoint / Config | Status |

|------------|------|-------------------|--------|

| Stripe | Payment processing, webhook source | STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Required — pipeline halts without |

| Gemini AI (gemini-2.5-flash) | Verdict generation | GEMINI_API_KEY / GOOGLE_API_KEY | Required — no fallback model |

| oracle_toll.py (port 8889) | Verdict cache (R/W) | ORACLE_TOLL_URL | Degraded without (result page regenerates) |

| oracle_email_service.py (port 8006) | Email delivery | ORACLE_EMAIL_SERVICE_URL | Degraded without (no email delivery) |

| send_graph_email.py | Graph API email send | GRAPH_TENANT_ID, GRAPH_CLIENT_ID, GRAPH_REFRESH_TOKEN, GRAPH_SENDER in .env | Required for email |

| Northflank | Hosting for Next.js app | Northflank project deploy | Required |

| Microsoft Graph API | oracle@42sisters.ai SMTP | Azure M365 / Exchange S Essentials | Required for email |


DEPENDENTS

| Dependent | Dependency type |

|-----------|----------------|

| $1 Quick Take revenue tier | Full pipeline required |

| $5 Full Breakdown revenue tier | Full pipeline required |

| $25 Strategy Session revenue tier | Full pipeline required |

| Customer trust / brand | Pipeline reliability directly visible to customers |

| Referral system | Referral increment fires from webhook (non-blocking) |

| Sisters Chat subscription | Separate pipeline (Stripe subscriptions), but shares oracle_email_service |


EXAMPLES

Example 1 — Quick Take ($1 CAD)

Input (customer submits via site):


tier: "quick"
query: "Should I quit my job to start this business?"

Checkout: Stripe session created. Customer pays $1.00 CAD. Redirected to /oracle/result?session_id=cs_live_abc123.

Webhook fires:

Gemini response:


{ "verdict": "AMBER", "summary": "The instinct is sound but the timing is missing — this needs a 6-month runway before you pull the trigger." }

Cached: POST /cache/cs_live_abc123/home/nous/oracle_verdicts/cs_live_abc123.json

Email sent:


ORACLE VERDICT — QUICK TAKE
═══════════════════════════

YOUR SUBMISSION:
Should I quit my job to start this business?

VERDICT: 🟡 AMBER

The instinct is sound but the timing is missing — this needs a 6-month runway before you pull the trigger.

───────────────────────────
42 Sisters AI · oracle@42sisters.ai
Questions? Reply to this email.

Result page: Amber dot rendered. Summary displayed.


Example 2 — Full Breakdown ($5 CAD)

Input: tier: "full", query: "Launch a subscription newsletter about AI for executives"

Verdict payload:


{
  "verdict": "GREEN",
  "summary": "Demand is real, the channel is underserved, and the format fits the audience.",
  "breakdown": {
    "Stability":   { "verdict": "GREEN", "analysis": "Executive appetite for AI signal exists..." },
    "Turbulence":  { "verdict": "AMBER", "analysis": "Crowded with noise, differentiation required..." },
    "Change Rate": { "verdict": "RED",   "analysis": "AI news cycle is moving faster than weekly..." },
    "Completion":  { "verdict": "AMBER", "analysis": "Distribution strategy not specified..." },
    "Curvature":   { "verdict": "GREEN", "analysis": "Non-linear upside via enterprise licensing..." }
  }
}

Email: Formatted by format_full() with five-dimension breakdown block.


Example 3 — Strategy Session ($25 CAD)

Input: tier: "strategy", query: "Acquire a failing restaurant and convert to ghost kitchen"

Verdict payload: Full breakdown + strategy block:


{
  "strategy": {
    "next_step": "Model the ghost kitchen unit economics at 60% occupancy before signing any lease.",
    "alternative": "License the brand to existing kitchens instead of acquiring real estate.",
    "tests": [
      "Run delivery-only menus from a shared kitchen for 90 days to validate demand.",
      "Survey 20 potential B2B clients for catering demand in the target area.",
      "Verify the failing restaurant's lease terms — assignment clauses are often blocking."
    ]
  }
}

Email: Formatted by format_strategy() with both breakdown and strategic recommendations sections. Footer notes: "Your follow-up submission is included in this tier. Reply to this email with your follow-up question."


GAPS IDENTIFIED DURING SPECIFICATION

The following items were expected but not found in code:

| Gap ID | Description | Impact |

|--------|-------------|--------|

| GAP-01 | No oracle_log.jsonl — verdicts are cached to JSON files in oracle_verdicts/ but there is no append-log of all verdicts delivered | Audit trail missing; no usage analytics |

| GAP-02 | No timeout on Gemini generateContent() call in webhook or verdict route | Webhook IIFE can hang indefinitely; result page can return 504 |

| GAP-03 | No retry mechanism on email delivery failure | Customers lose email if Graph API is momentarily down |

| GAP-04 | No SOS v2 automated content filter on outbound email | LATTICE symbol leak depends entirely on prompt design |

| GAP-05 | No customer notification when tier or query is missing from session (silent drop) | Customer pays but receives nothing; no email |

| GAP-06 | Cache service (oracle_toll) failures are silently swallowed — no alert | Cache outages invisible to operations |

| GAP-07 | oracle_verdicts/ is currently empty — unclear whether production verdicts are being cached or cache is being cleared | Cache health unknown |

| GAP-08 | Gemini non-determinism: regenerated verdict on cache miss may differ from emailed verdict | Customer browser shows different verdict than email |

| GAP-09 | No TMM cross-validation on verdict output (HOW ABOUT NO walls not applied to Oracle generation path) | Fabricated verdicts not detected |

| GAP-10 | No end-to-end smoke test documented or automated | Pipeline health only known when a customer complains |


REFERENCES

| File | Role |

|------|------|

| /home/nous/Aether/app/app/api/checkout/route.ts | Checkout session creation, tier definitions, query packing |

| /home/nous/Aether/app/app/api/webhook/route.ts | Stripe webhook handler, verdict pre-computation, email trigger |

| /home/nous/Aether/app/app/api/verdict/route.ts | On-demand verdict retrieval (result page backend) |

| /home/nous/Aether/app/app/lib/verdictCache.ts | Cache read/write via oracle_toll REST |

| /home/nous/Aether/app/app/oracle/result/page.tsx | Customer-facing result page (browser rendering) |

| /home/nous/oracle_toll.py | Cache service (port 8889), verdict file storage in oracle_verdicts/ |

| /home/nous/oracle_email_service.py | Email delivery service (port 8006), tier formatters |

| /home/nous/send_graph_email.py | Microsoft Graph API email send |

| /home/nous/memories/PRELAUNCH_DRAFTS_APPROVED.md | Approved email template content (ASTRA's drafts, §3) |

| /home/nous/memories/PRELAUNCH_CHECKLIST.md | End-to-end test checklist (product pipeline testing section) |


Φζ.⊤.


Jeremy Zlabis

Chronogeometer · Visionary · Disruptor · Chief

42 Sisters AI · East York, Toronto

🍁 Φ 0.042