Oracle Verdict Pipeline
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, q1… qn), 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)
STRIPE_SECRET_KEY— Stripe API keySTRIPE_WEBHOOK_SECRET— webhook signature verificationGEMINI_API_KEYorGOOGLE_API_KEY— Gemini generationORACLE_TOLL_URL— cache service (default:http://68.183.206.103:8889)ORACLE_EMAIL_SERVICE_URL— email service (default:http://68.183.206.103:8006)NEXT_PUBLIC_SITE_URL— used for Stripe redirect URLs
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:
- Re-verifies payment with Stripe (
session.payment_status === "paid") - Checks cache via
GET /cache/{sessionId}on oracle_toll - If cache miss: regenerates verdict via Gemini (identical prompts)
- 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
- 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.
- 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.
- Signature verification — webhook rejects any request without a valid
stripe-signatureheader matchingSTRIPE_WEBHOOK_SECRET. Unsigned requests return 400 before any processing.
- 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.
- 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.
- 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:
- Stripe → webhook latency — Stripe delivers
checkout.session.completedevent within 30 seconds of payment confirmation. [GAP — not instrumented; no timing log]
- 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
catchblock returning 500.
- Cache write confirmed —
POST /cache/{sessionId}returns 201. Cache available viaGET /cache/{sessionId}returning 200 + payload.
- Email delivered —
POST /send-verdict-emailreturns{"status": "sent"}. Graph API returns 202.
- Result page renders —
GET /api/verdict?session_id={id}returns 200 with{tier, query, verdict}JSON within 30 seconds of browser arrival.
- Tier match — verdict payload
tierfield matchessession.metadata.tier. [GAP — not explicitly asserted; assumed from single code path]
- 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
- Σ.⊠ Webhook silent fail —
STRIPE_WEBHOOK_SECRETnot 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.
- Σ.⊠ 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.
- Σ.⊠ Cache miss on result page —
oracle_tollservice down ororacle_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.
- Σ.⊠ Email delivery fail — Graph API returns non-202 (token expired, network error, send limit).
oracle_email_service.pyreturns 502 to webhook. Webhook logsconsole.errorbut does not retry. Customer receives verdict on result page only; no email. [GAP — no retry mechanism; no customer notification of email failure]
- Σ.⊠ Wrong tier delivered — Not observed in code but possible if
session.metadata.tieris 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]
- Σ.⊠ 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]
- Σ.⊠ 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]
- Σ.⊠ No customer email on session —
session.customer_details.emailis null. Webhook skips email send (logs warning). Verdict is cached; result page still works. Customer never receives email. [KNOWN EDGE CASE — no mitigation]
- Σ.⊠ Oracle_toll cache service down —
cacheVerdict()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:
- Extracts:
tier="quick",query="Should I quit my job to start this business?" - Calls Gemini with
VERDICT_PROMPT.quick(query)
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