DESIGN.md
The spec this product is built from.
# Scruple — DESIGN.md
> *Red-team your decisions. Five personas. One verdict. No mercy.*
A single-page web app that takes any decision a user is about to make and returns five distinct, named, opposing perspectives — each arguing as forcefully as possible against the decision — followed by a synthesis verdict.
This file is the single source of truth for design, copy, behavior, and flow. Coding agents should build directly from it. If the implementation and this file disagree, this file wins until updated.
---
## 1. The product in one paragraph
You have a decision you're about to make. A big one — quit the job, propose, sell the company, publish the essay, fire the cofounder, move cities. You tell Scruple what it is. Scruple doesn't ask clarifying questions. It returns five named personas — **The Skeptic**, **The Economist**, **Your Future Self**, **The Rival**, **The Friend Who Loves You** — each writing one sharp, specific, emotionally loaded paragraph arguing *against* your decision. At the end, a **Verdict** section tallies which personas recommend against, names the weakest objection, and tells you the one test that would kill your thesis. The whole thing streams in under 30 seconds. The first two decisions are free. After that it's $19/month unlimited or $29 one-shot.
## 2. Why it exists (the wedge)
Every decision support tool on the market either (a) agrees with you (ChatGPT default), (b) gives you a balanced pros-and-cons list (useless), or (c) requires you to formulate the opposition yourself (most people can't). Scruple's wedge: **specific, named adversaries that feel like real voices**, not a wikipedia-style both-sides summary. You get argued *at*, not balanced *around*.
## 3. Core principles
1. **Specificity > balance.** Every argument must reference a concrete detail from the user's input. No generic hedges.
2. **Voice > information.** Each persona must sound like one person with one worldview. The Economist sounds nothing like The Friend.
3. **Against, always against.** Scruple never argues for the user's decision. Confirmation bias is the disease. This is the cure.
4. **Ship, don't fluff.** Under 30 seconds end-to-end. No clarifying questions. No onboarding. No signup until payment.
5. **Premium, quiet aesthetic.** Dark, high-contrast, editorial typography. Feels like a private scruple, not a SaaS dashboard.
## 4. Target user
Founders, operators, writers, and individual contributors at inflection points. People who self-describe as "overthinking this" and who don't have a trusted board of advisors to stress-test decisions with. Not enterprise. Not teams. Individuals at 2am.
## 5. Brand
| Property | Value |
|---|---|
| Name | Scruple |
| Tagline | Red-team your decisions. |
| Secondary tagline | Five voices. One verdict. No mercy. |
| Voice | Confident, spare, unsentimental. Sounds like a senior editor, not a life coach. |
| Wordmark | `Scruple` set in Instrument Serif, italic, -0.02em tracking. |
| Favicon | A black square with a white `§` (section sign — evokes legal/scruple). |
### 5.1 Color palette (dark-first)
| Token | Hex | Use |
|---|---|---|
| `--ink` | `#0A0A0A` | Page background |
| `--ink-2` | `#141414` | Card background |
| `--rule` | `#1F1F1F` | Dividers, hairlines |
| `--paper` | `#F5F1E8` | Primary text (warm off-white) |
| `--paper-dim` | `#8A857A` | Secondary text, metadata |
| `--accent` | `#C8A64B` | Verdict, CTA, marks of emphasis — muted brass, not yellow |
| `--alert` | `#E35F3D` | Errors, destructive actions |
| Persona colors | See §8.2 | One per persona, used sparingly |
No gradients. No drop shadows. No blur. Hairlines at 1px, accents at 2px.
### 5.2 Typography
| Role | Family | Notes |
|---|---|---|
| Display / wordmark | Instrument Serif (Google Fonts) | Italic for wordmark. Regular for section titles. |
| Body | Inter (Google Fonts) | Weights 400 / 500. `text-rendering: optimizeLegibility`. |
| Mono | JetBrains Mono | Used only for the decision input echo. |
Base size: `17px`. Line height: `1.55`. Max measure: `62ch` for persona paragraphs.
### 5.3 Spacing & layout
8px base grid. Page max width `720px`. Personas stack vertically, full bleed inside the column. No sidebars. No modals. One long, scrollable page. No horizontal scrolling anywhere.
## 6. Information architecture
One page. One URL. Three states that swap in-place, no routing:
1. **`idle`** — hero, input field, "Red team this" button, free-tier meter ("2 free decisions left")
2. **`streaming`** — input collapses into a read-only quote block at top. Personas appear one by one, their names materializing before their text streams in.
3. **`complete`** — verdict section appears at bottom. Input re-opens below so the user can run another. If they've exhausted free tier, the input is replaced by the paywall.
Below the fold (always visible on idle):
- "How it works" — 3 steps, no illustrations
- "What it won't do" — short list: won't agree with you, won't balance, won't ask clarifying questions
- Pricing — two cards, $19/mo and $29 one-shot
- FAQ — 5 questions
- Footer — contact email, twitter, "DESIGN.md" link to the public spec
## 7. The core flow (happy path)
```
User lands
│
▼
Reads hero ("Every decision is a bet. We short yours.")
│
▼
Types or pastes decision into textarea (min 40 chars enforced)
│
▼
Clicks "Red team this" (Cmd/Ctrl+Enter also works)
│
▼
Input collapses to quote block at top
Free-tier counter decrements (2 → 1 → 0)
│
▼
Stream starts. Personas appear in fixed order:
1. The Skeptic
2. The Economist
3. Your Future Self
4. The Rival
5. The Friend Who Loves You
│
▼
Each persona: name appears in its color,
then paragraph streams in char-by-char over ~4s
│
▼
Verdict block appears (accent color bar on left)
Contains: vote count, weakest argument, falsifying test
│
▼
Input re-opens below with "Red team another decision"
OR paywall if exhausted
```
## 8. Personas
### 8.1 Fixed roster (v1)
| # | Name | Worldview | One-line directive |
|---|---|---|---|
| 1 | **The Skeptic** | Nothing is real until it's shipped. Receipts or it didn't happen. | Demands evidence, calls out bluffs, cheapest version first. |
| 2 | **The Economist** | Time, money, and opportunity cost are the only real currencies. | Math-first. Quotes base rates. Names the opportunity cost. |
| 3 | **Your Future Self (age +20)** | Has already lived through this. Regrets things you haven't done yet. | Personal, warm-sad, specific to aging. |
| 4 | **The Rival** | Already winning in your space. Respects you enough to be mean. | Competitive, arrogant, specific to your market. |
| 5 | **The Friend Who Loves You** | Knows your patterns. Has seen this movie. | Kind, direct, references your past. |
### 8.2 Persona colors (used only in the persona name label)
| Persona | Hex | Motif |
|---|---|---|
| The Skeptic | `#9CA3AF` | cool steel |
| The Economist | `#C8A64B` | brass |
| Your Future Self | `#A78BFA` | dusk violet |
| The Rival | `#EF4444` | arterial red |
| The Friend Who Loves You | `#86EFAC` | soft green |
### 8.3 Persona output contract
Each persona produces exactly one paragraph, 80–140 words, referencing at least two specific details from the user's input. No bullet points. No headers. No caveats. Ends with a sentence that lands.
## 9. The model prompt (v1)
```
You are Scruple. A user has told you about a decision they are
about to make. Your job is to return five named personas, each
arguing AGAINST their decision as forcefully, specifically, and
persuasively as possible.
PERSONAS (use exactly these five, in this order):
1. THE SKEPTIC — demands evidence, cheap tests, calls bluffs.
2. THE ECONOMIST — quantifies. Base rates. Opportunity cost.
3. YOUR FUTURE SELF — speaking from 20 years in the future,
warm, sad, specific, regret-as-wisdom.
4. THE RIVAL — a real competitor already winning in this space.
Arrogant, specific, respects the user enough to be mean.
5. THE FRIEND WHO LOVES YOU — knows the user's patterns.
Kind but direct. References past behavior.
RULES:
- One paragraph per persona. 80–140 words.
- Every paragraph must reference at least two specific details
from the user's decision.
- Never agree. Never balance. Never hedge.
- No bullet points, no headers, no caveats.
- End each paragraph on a sentence that lands.
AFTER THE FIVE: a VERDICT block.
- Count: how many of the 5 recommend against (usually 4–5).
- Weakest: name the persona whose argument was weakest and why.
- Falsifier: the one concrete test, runnable in <7 days, that
would falsify the user's thesis.
FORMAT (return exactly this structure as streaming JSON):
{
"personas": [
{ "name": "THE SKEPTIC", "paragraph": "..." },
{ "name": "THE ECONOMIST", "paragraph": "..." },
{ "name": "YOUR FUTURE SELF", "paragraph": "..." },
{ "name": "THE RIVAL", "paragraph": "..." },
{ "name": "THE FRIEND WHO LOVES YOU", "paragraph": "..." }
],
"verdict": {
"against": 5,
"weakest": "…",
"falsifier": "…"
}
}
THE USER'S DECISION:
<<<
{decision}
>>>
```
## 10. Monetization
| Tier | Price | What you get |
|---|---|---|
| Free | $0 | 2 decisions lifetime (per device + IP) |
| Unlimited | $19/mo | Unlimited decisions, cancel anytime |
| Single Shot | $29 one-time | 1 red team, no subscription |
Stripe payment links (hosted, live):
- Unlimited: `https://buy.stripe.com/00w9AM3791x59Qm2CScbC0C`
- Single Shot: `https://buy.stripe.com/eVq9AM0Z1grZd2y1yOcbC0D`
### 10.1 Unlock flow
1. User completes checkout on Stripe-hosted page.
2. Stripe redirects to `/unlocked?session_id={CHECKOUT_SESSION_ID}`.
3. App calls `POST /api/unlock` with the session_id.
4. Server retrieves the Stripe session, confirms `payment_status === 'paid'` (one-shot) or `status === 'complete'` (subscription).
5. Server returns an HMAC-signed `unlock_token` (payload: `{ session_id, mode, issued_at }`).
6. Client stores token in `localStorage.scruple_unlock`.
7. Every `POST /api/red-team` includes the token in `Authorization: Bearer`. Server verifies HMAC and, for subscription tokens, checks subscription is still active against Stripe (cached 1h).
Stateless unlock = no user database for MVP. Supabase comes later for decision history.
## 11. Copy
### 11.1 Hero
> # Every decision is a bet.
> ## We short yours.
>
> Paste a decision you're about to make. Scruple returns five named critics — each as specific, as sharp, and as mean as the best operator you've ever disappointed. Thirty seconds. No balance. No mercy.
>
> [ Red team a decision → ]
> *2 free. $19/mo unlimited. No signup to try.*
### 11.2 "What it won't do"
> **Scruple will not:**
> - Agree with you
> - Give you a balanced take
> - Ask you clarifying questions
> - Remember you tomorrow (yet)
> - Protect your feelings
### 11.3 Pricing card copy
**Unlimited · $19/mo**
Unlimited red teams. Cancel anytime. For people who make hard calls weekly.
**Single Shot · $29**
One decision. One scruple. No subscription. For the call you're making today.
### 11.4 FAQ
1. **Is this just ChatGPT?** No. ChatGPT's default posture is agreement. Scruple's default is refusal. We prompt and constrain the model to argue only one side. Try it.
2. **Why not a panel of pros and cons?** Because you can get pros-and-cons anywhere and they don't change your mind. Adversarial specificity does.
3. **Does it know who I am?** No. V1 is stateless and anonymous. We don't ask for your name. Payment is through Stripe; we see an email only if you subscribe.
4. **What if I disagree with the verdict?** Good. That's useful information.
5. **Is this legal/medical advice?** No. It's a thinking tool. Don't outsource your life.
## 12. Technical architecture
```
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Next.js 14 │─────▶│ /api/red-team │─────▶│ Anthropic Claude │
│ App Router │ │ streaming SSE │ │ claude-sonnet │
└─────────────┘ └──────────────────┘ └──────────────────┘
│ │
│ ▼
│ ┌──────────────────┐
│ │ HMAC token verify│
│ │ (for paid users) │
│ └──────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ /api/unlock │─────▶│ Stripe API │
│ POST │ │ retrieve session│
└──────────────┘ └──────────────────┘
│
▼
┌──────────────┐
│ localStorage │ scruple_unlock, scruple_free_used
└──────────────┘
```
### 12.1 Stack
- Next.js 14 (App Router)
- React 18 / TypeScript
- Tailwind CSS (JIT)
- `@anthropic-ai/sdk` (server-side only)
- `stripe` (server-side only)
- No database in v1 (rate-limit free tier via Vercel KV if available, else best-effort localStorage + IP hashing)
### 12.2 Environment variables
| Name | Where | Notes |
|---|---|---|
| `ANTHROPIC_API_KEY` | Vercel | User (Gabe) must add manually post-deploy. |
| `STRIPE_SECRET_KEY` | Vercel | Same. |
| `SCRUPLE_UNLOCK_SECRET` | Vercel | Any 32-char string. Used to sign unlock tokens. |
### 12.3 API surface
| Route | Method | Purpose |
|---|---|---|
| `/api/red-team` | POST | Stream personas + verdict for a decision. Accepts `{ decision: string }` in body. Verifies unlock token if present, else enforces 2-free quota. Returns `text/event-stream`. |
| `/api/unlock` | POST | Accepts `{ session_id }`. Verifies with Stripe, returns HMAC unlock token. |
| `/api/healthz` | GET | Liveness. |
### 12.4 Rate limiting (free tier)
Device-level: `localStorage.scruple_free_used` increments on each successful stream. Server-level soft backstop: hash of IP + today's date, allow max 5 per IP per day (covers shared networks while deterring abuse). If the app is deployed with Vercel KV, use it; otherwise in-memory per-instance is acceptable for MVP.
## 13. Accessibility
- Full keyboard: `Cmd/Ctrl+Enter` submits, `Esc` cancels a stream.
- All persona names are `aria-live="polite"` as they appear.
- Color contrast meets WCAG AA against `--ink` for all body text.
- Motion-reduced mode disables the char-by-char stream animation (shows paragraph at once once received).
## 14. What v2 will add (not v1)
- Supabase-backed decision history ("my scruples")
- Custom persona panels (invite a real friend to be the 6th voice)
- Shareable verdict links (with redaction)
- Team mode for cofounders
- Voice input
## 15. Definition of done for v1
- [ ] Lands on a public URL (vercel.app subdomain acceptable)
- [ ] Types a decision, clicks button, sees all 5 personas + verdict stream
- [ ] Free counter decrements, hits paywall after 2 uses
- [ ] Stripe checkout works for both price points (live mode)
- [ ] Post-checkout unlock flow returns a working token
- [ ] Token persists across reload
- [ ] All copy from §11 is present and matches
- [ ] Mobile-responsive down to 360px
- [ ] Meta tags: title, description, OG image
- [ ] DESIGN.md published at `/DESIGN.md` route or `/design` page
---
*Last updated: 2026-04-23. Maintained by the Scruple team.*