Assignment
Build a complete product spec and implementation plan for the "Letter Sending" feature — a production-ready physical mail system replacing abandoned Lob integration.
Subset: Full roster
Roster
What we found
1. There's already substantial code — 12+ files across Python, TypeScript, and JavaScript — but it's fragmented across 3 overlapping implementations and frozen. The plumbing exists; it just needs consolidation and the right gates.
2. Stannp ($868/month for 1,000 letters) beats Lob ($1,130-$1,750) on cost and formatting flexibility. The real fix is architectural: render HTML to PDF locally, upload to any vendor.
3. The letter content needs a voice overhaul — operator-first language, specific facts that prove research, and strict word counts that fit a physical page.
4. A research reliability gate (reject letters when data is insufficient) is the single most important trust-building feature.
Why this matters
Letters are Next Chapter's highest-signal outbound channel for owner-operators over 55. Without them sending, the firm loses its primary differentiation from email-only brokers. The 10-day critical path to first letter means this isn't a quarter-long project — it's a focused sprint.
Where we agreed
Full consensus on:
- Build a vendor abstraction layer (don't lock to one vendor)
- `letter_integration.py` is the canonical generator (merge, don't rebuild)
- Research gate must block bad letters BEFORE generation
- Dynamic font sizing via character-count tiers (A/B/C/D)
- Score-tiered routing: auto-approve ≥0.85, QA queue 0.65-0.84, reject <0.55
- Operator-first voice rules (10 specific rules defined)
- 5-phase implementation, first letter in ~10 days
Where we disagreed
Vendor choice: Hunter recommended Stannp (cheapest). Quarterback recommended staying with Lob (code exists, switching costs). Resolution: recommend Stannp but build abstraction layer so switching back is one env var change. Ewing decides.
Auto-approve threshold: Architect said 0.85, Quarterback said 0.80. Resolution: 0.85 for batch (cold outreach needs highest quality), 0.65 for ad hoc (post-call, you already know the person).
What surprised us
- The existing code is far more complete than expected — scoring rubric, variant system, batch CLI, approval workflow all exist. The gap is wiring, not building.
- Lob's HTML rendering limitations have NOT been fixed as of 2026 — the same Webkit renderer with the same restrictions. The fix was always "render PDF locally first."
- 400 letters are sitting frozen with 0 approvals due to one missing function call in `ingest_letters.py` (the K2 gap).
What we'd do differently
- Check for frozen/disabled features before assuming a capability doesn't exist (the letter system was built but gated behind `LETTER_SENDING_ENABLED=false`)
- The voice guide and emotional mapping should be versioned documents (not just in code comments) so Ewing can iterate on them without touching Python
Currency events
| From → To | Action | Multiplier | Base | Score | Notes |
|---|---|---|---|---|---|
| hunter → architect | Vendor formatting limitation research informed PDF-first design | 3x | 2 | 6 | Architect didn't need to research Lob's renderer |
| writer → architect | Word count targets (350/250/150) informed typesetter tier design | 3x | 1 | 3 | Tiers calibrated to actual content lengths |
| draper → writer | Trust trigger research directly shaped voice Rule 3 (concrete over abstract) | 3x | 2 | 6 | Emotional research → implementable writing rule |
| listener → quarterback | Tension resolution (ad hoc vs batch thresholds) resolved sequencing | 3x | 1 | 3 | Different gates for different contexts |
Cross-system gaps
| Flagger | Affected | Gap | Recommended change |
|---|---|---|---|
| architect | letter_integration.py | No research gate check before generation | Add `research_gate.py` call at top of `generate_letter_for_target()` |
| quarterback | campaign_manager.py | Stub implementations for `_render_letter` and `_send_lob` | Wire to canonical engine + vendor abstraction |
| listener | targets table | No `research_gate_status` column | Add column + index for enrichment pipeline |
| writer | quality_gate.py | Missing broker-term ban list | Extend FORBIDDEN_PHRASES with 12 banned terms |