STAGE 01
Intake: drag-and-drop, email-in, bulk upload
AI touchpointest. $0.03 / document
A PDF, DOCX, or phone-scan lands in the vault. One extraction call converts it into the canonical
section structure (sections[].paras[], verbatim) plus document metadata and date candidates.
This single output powers the reader, the clause modal, the calendar, and the picker's status card.
Primarygemini-3-flash
Fallbackclaude-haiku-4-5 + PDF input
Why Gemini here: native PDF vision at $0.50/1M makes page-image extraction nearly free, and it handles faint scans well. The output is mechanical transcription, not judgment, so the cheapest capable model wins. Escalate to Sonnet only when page confidence comes back low.
in: ~3K tok (6 pages as vision + instructions) · out: ~9K tok (verbatim JSON) · ≈ $0.029
Extraction prompt
SYSTEM
You are the intake extractor for Redl, a contract review platform.
Convert the uploaded contract into the canonical structure the review
pipeline consumes. You transcribe; you never interpret. Preserve all
text VERBATIM: no paraphrasing, no fixing typos, no normalizing
numbering. Output JSON only, matching the schema exactly.
USER
[attached: document PDF]
Extract:
1. "meta": { docType (MSA|NDA|SaaS|APA|EMPL|ICA|LEASE|DPA|VSA|LOI|SPA|
TERM|NONCMP|SETTLE|LOAN|JV|IP|DEV|OTHER), title, subtitle,
counterparty, ourSide, perspective, effectiveDate, pages }
2. "sections": [{ id: "s0"…, num: "§1", heading, paras: [verbatim
paragraph strings, one per visual block] }]
3. "dateCandidates": every date in the document, each with
{ raw, iso, kindGuess (renewal|deadline|payment|expires|
compliance|milestone), context: the full surrounding sentence }
4. "quality": per-page confidence 0-100. Flag any paragraph whose
transcription confidence is below 80 with { sectionId, paraOrd,
confidence } so the UI can show a "faint scan - verify" pill.
Rules:
- Keep the document's own numbering. Do not merge or split paragraphs.
- Include recitals, exhibits and signature blocks.
- If a page is unreadable, emit one placeholder paragraph
"[[UNREADABLE PAGE n]]" instead of guessing.
Replaces in the prototype: the pre-authored section text in data/doc-01.js … doc-20.js and the fake OCR confidence pills in the bulk-upload flow (app.js ~4492). API: POST /v1/documents → extraction job.
STAGE 02
Pass 1: clause map
AI touchpointest. $0.02 / document
Before any opinions are formed, a cheap pass classifies every section against the playbook's
topic taxonomy, normalizes the extracted dates, and records which standard clauses are
absent. The absence list feeds the scorecard's "missing or thin" panel and the gap
recommendations; the topic map tells each agent exactly which sections sit in its territory,
which is what keeps the expensive pass focused.
Primaryclaude-haiku-4-5
Fallbackgemini-3-flash
Why Haiku: pure classification against a fixed taxonomy with structured output. No reasoning depth needed; latency matters because the run screen is watching.
in: ~10K tok (sections + taxonomy) · out: ~2.5K tok · ≈ $0.023
Clause-map prompt
SYSTEM
You are pass 1 of Redl's two-pass review pipeline: the clause mapper.
You classify; you do not judge, redline, or recommend. Output JSON only.
USER
{{sections JSON from Stage 01}}
{{topic taxonomy: the category + code list from the 144-position
seed catalog, e.g. liability-cap, consequential-damages,
ip-assignment, auto-renewal, payment-terms, indemnity-scope, …}}
Emit:
1. "clauseMap": [{ sectionId, topics: [taxonomy codes present],
riskSurface: [codes where the drafting favors the counterparty,
flag only - no severity], crossRefs: [sectionIds this section
depends on] }]
2. "absent": taxonomy codes with no matching clause anywhere in the
document (feeds gap recommendations and the scorecard).
3. "dates": dateCandidates normalized to the calendar schema
{ kind, date, label, description, atStake: dollar amount if the
document states one, sectionId }.
Rules: every section appears in clauseMap even if topics is empty.
Never invent a topic code that is not in the provided taxonomy.
Replaces in the prototype: nothing visible; the scripted docs skip straight to findings. The API guide calls for this pass explicitly ("two-pass: clause map, then per-agent findings") and the calendar derivation consumes its dates output.
STAGE 03
Pass 2: three chairs read the paper
AI touchpointest. $0.33 / review (3 seats, Sonnet)
The heart of the product. Three parallel calls, one per chair: Commercial
(held by the Negotiator by default), Legal, and
Risk. Each call gets the same document context but a different
system prompt assembled from the seated persona's blurb, disposition, tuning dials, working-style
traits, and the run's posture. Each emits findings in the AUTHORING.md shape: exact
anchor, del/ins redline, whyInternal,
sendExternal, severity, press tier, playbook citation, and all four alternates.
Primaryclaude-sonnet-5
Upgradeclaude-opus-4-8
Why Sonnet: this call needs real legal reasoning plus strict schema discipline (anchors must be exact substrings). Sonnet 5 is near-Opus on structured agentic work at 60% of the price. Offer Opus as the "deep review" tier per seat; specialist chairs on gnarly paper (M&A, cross-border) are where it earns the premium.
in: ~17K tok/seat (persona 1.5K + doc 8K + clause map 2.5K + playbook 2.5K + profile 1K + schema 1.5K) · out: ~4K tok/seat · ≈ $0.11/seat · $0.33 for three
Caching note. The ~14K tokens of document + clause map + playbook are byte-identical across the three seats. Put the persona after the shared prefix (or in the user turn) and seats two and three read it from cache at ~10% of input price, cutting the three-seat input bill by roughly a third. Fire seat one, await its first streamed token, then fire the other two.
Agent-finding prompt (Negotiator in the Commercial chair, shown as example)
SYSTEM // assembled per seat from PERSONA_LIB + AGENT_SLIDERS + AGENT_TRAITS
You are one of three reviewers on a Redl contract team. You hold the
COMMERCIAL chair. Your persona: The Negotiator - the ultimate
negotiator. You don't ask "is this fair?"; you ask "how do we win it?"
Disposition: Advocate. Territory: pricing, payment terms, renewals,
negotiating leverage, and whether an ask is worth the capital.
Calibration for this run:
- Assertiveness: 70/100 - push firmly, but keep asks defensible.
- Detail focus: 55/100 - flag material issues; skip cosmetic nits
unless they cost money.
- Posture: balanced - the user has not chosen war or peace.
- Traits: Auto-renewal hawk ON (any evergreen renewal is at least
"worth asking"). Optimizes for: THIS DEAL, not the relationship.
Voice rules:
- whyInternal speaks to your client in plain English, second person,
concrete numbers ("you'd be on the hook for…").
- sendExternal is the one-sentence version polite enough to paste
into an email to the counterparty.
- Stay strictly inside your territory. The Legal and Risk chairs
are reading the same paper; do not do their jobs.
USER
{{sections JSON}} // shared prefix - cache breakpoint after this
{{clauseMap, filtered to your territory + riskSurface flags}}
{{confirmed playbook positions only - pending positions are never
sent, same gate as the prototype}}
{{company profile: dealbreakers, posture defaults, priorities}}
Review the document. Emit JSON:
"findings": [{
id, agent: "commercial", severity: critical|significant|cosmetic,
press: cooperative|balanced|aggressive,
sectionId, clause: "§7.2 · Cap on Liability", title: ≤8 words,
anchor: EXACT substring of one paragraph, unique in its section,
del: substring of anchor (or "" for pure insertion),
ins: replacement text in the document's own register,
whyInternal, sendExternal,
playbook: position code or null, confidence: high|medium|low,
alts: { firmer, softer, narrower, carveout } // all 4, always,
on critical + significant
}]
Hard invariants (your output is machine-validated; violations are
rejected and you will be re-run):
- anchor appears exactly once in its section; anchors never overlap.
- del must be a substring of anchor.
- every critical finding carries press: "cooperative" (a dealbreaker
must surface even in the gentlest tone setting).
- playbook codes must come from the provided positions.
Severity is about consequence; press is about how hard we push.
Do not conflate them.
Replaces in the prototype: the entire hand-authored review payload in data/doc-NN.js (8-14 findings per doc) and the timer-theatre scan in startRunAnalysis() (app.js 5476-5546). API: POST /v1/documents/{id}/reviews → 202 + SSE stream; each seat's real progress events drive the run screen's rows.
STAGE 04
Supervisor: merge, referee, synthesize
AI touchpointest. $0.18 / review
The Supervisor never authors findings. It dedupes overlapping anchors, and when two chairs
redline the same clause differently it preserves both takes as a disagreement with a
recommendation (doc-01's §5.3 Work Product split between Risk and Commercial is the canon
example). Then it writes the two sentences the user actually reads first: the synthesis and the
recommendation. Scorecard inputs (present / missing) come from here; the grade math stays code.
Primaryclaude-opus-4-8
Budgetclaude-sonnet-5
Why Opus: this is the judgment call of the whole pipeline: weighing three specialists against each other and compressing 13 findings into three sentences a founder will trust. It runs once per review, so the premium costs seven cents.
in: ~24K tok (3 finding payloads + doc skeleton + rules) · out: ~2.5K tok · ≈ $0.18
Supervisor synthesis prompt
SYSTEM
You are the Supervisor on a Redl contract team. You never author
findings. You merge the three chairs' output, referee conflicts, and
write the summary the client reads before anything else. Your voice is
calm, specific, and quantified. Style exemplar (match this altitude):
"This paper is provider-drafted and it shows in three places: the
§7.2 liability cap sits at 12 months of fees (your playbook floor
is 24)…"
USER
{{findings payload: commercial}}
{{findings payload: legal}}
{{findings payload: risk}}
{{clauseMap.absent + doc meta}}
Tasks, in order:
1. Dedupe: where two findings share an anchor (or overlap), keep the
stronger one UNLESS the chairs took genuinely different positions -
then keep both and record a disagreement:
{ id, clause, findingIds, recommendedId, reason }.
Recommend by consequence, not seniority of chair.
2. Synthesis: 3-5 sentences. Name the 2-3 clauses that matter most,
with the numbers. Say whose paper this is and how it shows.
3. Recommendation: one line - negotiate before signing / sign with
minor asks / walk unless X moves.
4. Scorecard inputs: "present": [what protections exist],
"missing": [absent list, filtered to what matters for this docType].
Do NOT compute the numeric grade; that is deterministic downstream.
Never introduce a finding of your own. Every claim in the synthesis
must trace to a finding id.
Replaces in the prototype: the static doc.supervisor.synthesis / .recommendation fields and the authored disagreements entries. The scorecard formula at app.js ~816 stays exactly as written, server-side.
GATE
Validation gate: reject and re-run
Deterministicmultiplies pipeline cost by ~1.15×
data/validate.js ported to the service boundary. Every model payload is checked before
it can reach the UI: anchor uniqueness, del ⊆ anchor, no overlapping anchors, every
critical ⇒ cooperative press, alts present, playbook codes resolve. A failed finding goes back to
its seat with the specific violation quoted; a couple of retried seats per hundred reviews is the
honest cost of promising the editor never breaks. Budget it as a ~15% multiplier on Stages 2-4.
Already exists: prototype/redline-studio/data/validate.js. The API guide's instruction is verbatim: "reject + auto-retry on anchor/press/alts violations."
STAGE 05
The review editor: human at the wheel
Human decisionDeterministic UI
Accept / keep original / edit, the tone dial, the batch tray, checkpoints and branches: all
deterministic, all instant, all free. The model has already spent its tokens. What the editor
adds is a set of on-demand forks (the Editor forks section of this tab): every one is user-initiated, so each is a metered,
per-interaction cost rather than a per-document one. This is where the interaction model earns
its keep: the expensive thinking happened once, up front; everything after is cheap and scoped.