docs/skills/letter-generation-pipeline/SKILL.md
Complete reference for the Talk to My Lawyer AI letter generation pipeline. Covers the 3-stage orchestrator (Perplexity research → Anthropic draft → Anthropic assembly), intake normalization, status machine, database writes, error handling, and n8n fallback path. Use when building, debugging, or extending the letter generation system.
npx skillsauth add jamilahmedansari/www.talk-to-my-lawyer.com- letter-generation-pipelineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
The letter generation pipeline converts a subscriber's intake form submission into a polished, attorney-ready legal letter through three sequential AI stages. This skill documents every stage, data shape, status transition, and failure mode.
⚠️ Schema Changes: All schema changes must be applied via Drizzle migrations. Follow the
drizzle/migrations/000X_description.sqlnaming convention.
Subscriber submits intake form
│
▼
[routers.ts] letters.submit
│ Creates letter_request (status: submitted)
│ Creates workflow_job record
│ Fires runLetterPipeline() async
▼
┌─────────────────────────────────────────┐
│ Stage 1: RESEARCH (Perplexity sonar-pro) │
│ Status: submitted → researching │
│ Timeout: 90s │
│ Output: ResearchPacket JSON │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ Stage 2: DRAFTING (Claude claude-opus-4-5) │
│ Status: researching → drafting │
│ Timeout: 120s │
│ Output: DraftOutput JSON │
└─────────────────┬───────────────────────┘
▼
┌─────────────────────────────────────────┐
│ Stage 3: ASSEMBLY (Claude claude-opus-4-5) │
│ Status: drafting → generated_locked │
│ Timeout: 120s │
│ Output: Final letter text (HTML/text) │
└─────────────────┬───────────────────────┘
▼
Letter version created (type: ai_draft)
Email sent to subscriber ("Your letter is ready")
Subscriber sees paywall → pays → pending_review
File: server/routers.ts → letters.submit
The submit procedure:
createLetterRequest() → insert into letter_requests table (status: submitted)createAttachment() for each uploaded filerunLetterPipeline(letterId, intakeJson) asynchronously (non-blocking){ letterId } to the client immediatelyFile: server/intake-normalizer.ts
Before any AI stage, raw intake JSON is normalized via buildNormalizedPromptInput(dbFields, intakeJson). This produces a canonical NormalizedPromptInput object used by all prompt builders.
Key normalizations:
toneAndDelivery.tone or legacy tonePreferencetoneAndDelivery.deliveryMethod or legacy fieldcommunications.summary or legacy fieldInput shape: See references/data-shapes.md → IntakeJson
Provider: Perplexity API (sonar-pro model) via OpenAI-compatible client
Fallback: If PERPLEXITY_API_KEY is not set, falls back to Claude claude-opus-4-5
Timeout: 90 seconds
submitted → researchingResearchPacket JSONresearch_runs table and as a letter_version (type: ai_draft, stage: research)workflow_job record for the stageThe prompt instructs the AI to produce a structured JSON ResearchPacket containing:
See references/data-shapes.md → ResearchPacket
Provider: Anthropic Claude (claude-opus-4-5)
Timeout: 120 seconds
researching → draftingDraftOutput JSONworkflow_job record for the stageThe prompt provides:
tonePreferenceSee references/data-shapes.md → DraftOutput
Provider: Anthropic Claude (claude-opus-4-5)
Timeout: 120 seconds
Exported as: runAssemblyStage() (also used by n8n callback)
letter_version record (type: ai_draft) with final contentletter_requests.currentAiDraftVersionId pointerdrafting → generated_locked or generated_unlocked (free-trial path)workflow_job record for the stagesubmitted ──→ researching ──→ drafting ──→ generated_locked
│
└──────────────→ generated_unlocked
│
├─→ pending_review
└─→ upsell_dismissed
generated_locked ─────────────────────────────────────→ pending_review
pending_review ──→ under_review ──→ approved | rejected | needs_changes
needs_changes ──→ researching | drafting
Valid transitions defined in shared/types.ts → ALLOWED_TRANSITIONS.
submittedworkflow_job record updated with error details and failed statuspipeline_failedadmin.retryJob procedure → calls retryPipelineFromStage(letterId, intakeJson, stage)review.requestChanges with retriggerPipeline: true → retries from specified stageresearch or drafting stage (skips completed stages)Activation: Requires ALL three conditions:
N8N_PRIMARY=true environment variableN8N_WEBHOOK_URL is sethttps://Currently: N8N_PRIMARY is NOT set → always uses direct 3-stage path.
When active, the n8n path:
/api/pipeline/n8n-callbackserver/n8nCallback.ts) processes results| Stage | Table | Record Type |
|-------|-------|-------------|
| Submit | letter_requests | New request (status: submitted) |
| Submit | attachments | File references (S3 keys) |
| Submit | workflow_jobs | Job tracking record |
| Research | research_runs | Research packet storage |
| Research | letter_versions | Research content (ai_draft, stage: research) |
| Draft | workflow_jobs | Stage completion record |
| Assembly | letter_versions | Final draft (ai_draft) |
| Assembly | review_actions | Pipeline completion log |
| Variable | Required | Purpose |
|----------|----------|---------|
| ANTHROPIC_API_KEY | Yes | Stages 2 + 3 (Claude claude-opus-4-5) |
| SONAR_API_KEY / PERPLEXITY_API_KEY | Recommended | Stage 1 research (falls back to Claude) |
| N8N_WEBHOOK_URL | No | n8n webhook (dormant) |
| N8N_CALLBACK_SECRET | No | n8n auth header |
| N8N_PRIMARY | No | Activate n8n path (default: off) |
| RESEND_API_KEY | Yes | Email notifications |
| File | Purpose |
|------|---------|
| server/pipeline.ts | 3-stage orchestrator + all prompt builders |
| server/intake-normalizer.ts | Canonical intake normalization |
| server/n8nCallback.ts | n8n webhook handler (dormant) |
| server/routers.ts (L1-180) | letters.submit procedure |
| server/db.ts (L119-280) | Letter request CRUD helpers |
| drizzle/schema.ts | Database schema + enums |
| shared/types.ts | IntakeJson, ResearchPacket, DraftOutput types |
references/data-shapes.mdreferences/prompt-strategies.mdreferences/status-workflow.mdBefore starting work in this skill, initialize with the current frontend issue register (agentId: a28925291b7edfefb) and treat these as active priorities:
as any usage, fix email verification typing/logic, improve localStorage failure handling, replace SPA-breaking redirects, and surface upload/submission failures.If implementation scope is unclear, default to Critical issues first.
development
Complete reference for the Talk to My Lawyer attorney review pipeline. Covers the full review workflow from payment unlock through attorney claim, inline editing, approval/rejection, PDF generation, and subscriber notification. Use when building, debugging, or extending the letter review center, attorney dashboard, or approval workflow.
development
Comprehensive AI assistant instructions for Talk to My Lawyer platform. Covers 3-stage AI pipeline (Perplexity research → Anthropic draft → Anthropic assembly), review workflow, payment processing, database operations, frontend patterns, and all coding conventions. Optimized for AI code generation assistants.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.