Release history v0.8.0 → v0.15.0 (the Apache 2.0 open-source launch), with unusually candid rationale per decision — why model upgrades were rolled back, why deps were bumped, what each hardening pass fixed. Search hits land on the exact version section. Upgrade planning, or archaeology on why something changed.
Changelog
Version history for Lavern (codebase name: The Shem). Current version is recorded in CLAUDE.md under "System Identity".
v0.15.0 (Current) — Initial open-source release
Apache 2.0. First publicly tagged release.
LOCAL MODE by default. Auth (login, signup, Google OAuth, email
verification, password reset) and per-user billing are gated behind
LAVERN_AUTH_ENABLED=true. With the flag off — the default — every
request runs as a synthetic local-user, no cookies, no tokens, no
emails. Single-user installs (the dominant OSS use case) get a clean
straight-to-dashboard experience.
Test suite reconciled to the staging port. 1,677/1,677 passing on
main (12 new tests pinning the LAVERN_AUTH_ENABLED route-registration
contract). Four pre-OSS test files (Bearer-token auth, verified-email
gating, an older buildToolRegistry signature, an off-by-one budget
bound) were updated to the new contracts.
Build artefacts out of git. site/demo/ is no longer committed.
A new npm run build:site script and netlify.toml regenerate the
embedded demo SPA on every deploy.
Dead code sweep. Removed viz/src/engine/office-scene.ts and
viz/public/sprites/ (2.1 MB of Kenney isometric assets that no
view imported) and the now-unused phaser dependency.
Marketing site. New "An agentic law firm. Yours." index page,
matching architecture deep-dive, the cinematic /demo/ tour built
in cream paper, social card with the actual brand fonts.
Documentation honesty pass. README reframes "67 agents" as 67 system prompts + the four loops around them. The "driverless law firm" tagline softened everywhere user-facing. EU sovereignty claim scoped narrowly with a tracking issue (#7) for the three routes that still call Anthropic directly.
v0.14.3 — Claude Opus 4.7 Upgrade + Quality Bumps
Opus bumped to the 4.7 generation (Sonnet 4.7 is not yet released — Sonnet stays at 4.5):
- Primary orchestration:
claude-opus-4-6→claude-opus-4-7 - Router + claw per-doc processing:
claude-sonnet-4-5-20250929→claude-sonnet-4-5(short-form alias of the same model) - Briefing analyzer:
claude-haiku-3-5-20250929→claude-sonnet-4-5(quality upgrade — intake analysis shapes the entire engagement)
Quality upgrades on user-facing call sites (Sonnet → Opus 4.7):
- Quality gate (validates assembled documents before delivery)
- Session derivatives (
POST /api/sessions/:id/derivatives) — "Generate More" outputs - Session conversation (
POST /api/sessions/:id/conversation) — "Ask the Team" replies - Challenge (
POST /api/challenge/compare) — blind document comparison
Pricing table (src/utils/stream-messages.ts, src/assembly/document-assembler.ts):
- New keys added for
claude-opus-4-7andclaude-sonnet-4-5(same $/M rates as prior generation) - Legacy keys (
claude-opus-4-6,claude-sonnet-4-5-20250929) kept for in-flight sessions and archived cost records
Postmortem note: an earlier attempt shipped claude-sonnet-4-7 across the stack, which Anthropic returns not_found_error for — that model does not exist. Verified with a live probe against Anthropic's API (claude-opus-4-7 ✓, claude-sonnet-4-7 ✗, claude-sonnet-4-5 ✓) before re-shipping. Lesson: always probe new identifiers with a live API call before burning engineering time on rollout.
Test fixtures updated: stream-messages.test.ts, session-manager.test.ts, rate-limit.test.ts, api-routes.test.ts, api-validation.test.ts all reference the new canonical IDs.
v0.14.2 — Remote MCP Bridge, Ops Observability, 1500-User Load Test Readiness
Remote MCP Bridge (Stages 1 + 2 dispatcher landed):
- New src/mcp/remote-bridge/ module exposes 12 Counsel tools over JSON-RPC 2.0 so the Anthropic Managed Agents runtime can treat Lavern as a remote MCP server. Methods:
initialize,tools/list,tools/call,notifications/initialized. - Session-scoped dispatcher (
dispatcher.ts) reflects the in-process tool registry via the same 5 factories (createWorkflowTools,createMemoryTools,createKnowledgeBaseTools,createHandoffTools,createFeedbackLoopTools). No tool logic duplicated. - Zod
safeParseon every call returnsinvalid_argscleanly instead of uncaught handler exceptions. Discriminated outcome (ok | not_allowed | not_found | invalid_args | handler_error) maps 1:1 to JSON-RPC error codes (-32001…-32004). tools/listemits real JSON Schemas viaz.toJSONSchema()so generic MCP clients can build request bodies without a hand-written schema lookup.- Auth: Bearer secret (timing-safe compare against
LAVERN_MANAGED_AGENTS_BRIDGE_SECRET, ≥32 chars or bridge refuses to register) +X-Lavern-Session-Idheader. Archived sessions (no live event bus) are rejected. - Defense in depth: allowlist enforced at both the JSON-RPC server layer AND the dispatcher — a bug in either alone cannot open the surface.
- Gated behind
LAVERN_MANAGED_AGENTS_BRIDGE=1; production default is off. - See docs/managed-agents-migration.md for staged rollout status. Stage 2 executor (the half that calls the bridge) is still pending on Managed Agents beta access.
Per-User Spend Observability:
getUserSpendBreakdown(sinceIso, untilIso, limit)in src/db/database.ts — SQL aggregate oversession_archive LEFT JOIN userswith per-user total / avg / max / session count / last-session timestamp.GET /api/admin/user-spend(X-Admin-Key gated) — default window: today 00:00 UTC → now (matches spend-tracker reset boundary). ISO validation + limit clamping [1, 500]. Anonymous sessions bucketed underuserId: null.
1500-User Load Test Readiness:
- New env
LAVERN_LOAD_TEST_BYPASS_KEY+X-Load-Test-Bypassheader — timing-safe shared-secret bypass of both global (100/min/IP) and per-route auth rate limits (signup 3/min, login 5/min). Required to drive 1500+ simulated users from one host without throttle-sheds. - scripts/load-test.ts auto-scales batch sizes under bypass (auth 50/tick, sessions 25/tick; without: 10/5). Adds per-phase ETA, throughput, and RSS sampling. Warns if
--users ≥ 500without bypass key. - Script header documents the three server env overrides needed for a 1500-user run:
LAVERN_LOAD_TEST_BYPASS_KEY,SHEM_MAX_SESSIONS=2000,SHEM_SESSION_TTL_MS=3600000, plusNODE_OPTIONS=--max-old-space-size=4096for headroom.
Sentry Coverage Sweep on Silent-Failure Paths:
- src/session/session-manager.ts × 3 — session_end archive, destroy archive + releaseHold fallback, evict archive + releaseHold fallback. Archive failures mean data loss + frozen billable hours — now every one reaches Sentry with
sessionId+phasetags. src/api/routes/billing.ts— Stripe webhook signature verification catch. Persistent failures mean pack purchases + subscription upgrades silently stall.- src/api/routes/engage.ts × 3 — engage dispatch chain, failure-callback delivery, chain safety net. Keeps agent-mode integrations visible when they break.
- src/claw/delivery.ts × 2 — DOCX + HTML conversion failures (delivery "succeeds" but bundle is incomplete).
- src/claw/processor.ts × 2 — change detection + precedent indexing failures (persistent-state corruption).
Email Retry + Sentry:
- src/email/send.ts — transient Resend failures (408/429/500/502/503/504 or network) retry 3× with 500 ms / 1.5 s / 4.5 s backoff. Permanent failures reach Sentry with
to+subjectcontext.
Production Config Guards:
validateProductionConfig()now treatsLAVERN_PROVIDER=mistralwithoutMISTRAL_API_KEYandLAVERN_PROVIDER=managedwithoutANTHROPIC_API_KEYas fatal. Localhost defaults forSTRIPE_CANCEL_URLandGOOGLE_OAUTH_REDIRECT_URIwarn loudly.
Test Coverage: 1507 → 1556 tests (24 new across 3 files: mcp-bridge.test.ts, mcp-bridge-dispatcher.test.ts, user-spend-breakdown.test.ts). Clean tsc --noEmit on backend + viz/.
v0.14.1 — Voice Mode: Speak to the Firm
Voice Input — Three Phases:
- Phase 1 — Intake (Briefing): Mic toggle in
ConversationalChatenables voice-driven interviews.useVoiceInput(Deepgram + Web Speech fallback) fills the input field onfinalTranscript; auto-submits after 1.5s silence. Auto-restarts listening when interviewer finishes responding (stable primitive deps prevent per-chunk re-fires). Toggle in header; keyboard Send still works as fallback. - Phase 2 — Interrupt (Working): Floating mic FAB in
WorkingView(position above PacMan button). On click: listens, injects transcript into the session activity feed viaPOST /api/sessions/:id/inject. New endpoint emitstool_usedevent into the session event bus so the message appears as a speech bubble in the feed. 1000-char cap. - Phase 3 — Post-case (Delivery): Mic button beside the "Ask the Team" input in
ConversationTab. Same pattern: listen →sendTextRefauto-sends via stable ref, skipping stale-closure issues.
Reused Infrastructure (no changes):
- viz/src/partner/hooks/useVoiceInput.ts — Deepgram STT via
/api/voice/sttWebSocket + Web Speech API fallback. Used as-is in all three phases. - viz/src/partner/hooks/useVoiceOutput.ts — ElevenLabs TTS. Available for future question read-back.
- viz/src/partner/components/VoiceOrb.tsx — Audio-reactive orb. Available for future full-voice mode.
Bug Sweep:
- All three components destructure
useVoiceInput()return values (individual stable callbacks) instead of holding a reference to the returned object. Removedvoiceobject from alluseCallback/useEffectdeps — was causing effects to re-run on every render. - Removed stray
voice.isListeningreference inWorkingView.tsxJSX. - Zero
tsc --noEmiterrors on both frontend and backend.
New API:
POST /api/sessions/:id/inject— accepts{ message: string }(max 1000 chars), emitstool_usedevent into session bus, returns{ success: true, sessionId }.
v0.14.0 — Clawern: Law Firm in Your Mac Mini
Real-time Dashboard:
- WebSocket push via ClawEventBus (9 event types, late-join replay)
- Adaptive polling: 60s when WS connected, 10s fallback
- Pause/Resume with PAUSED badge + Scan Now disable
Voice Dispatch (/#/dispatch):
- Mobile-optimized dark cinematic interface with Web Speech API
- Keyword-based command parsing (no LLM cost): status, findings, scan, pause, resume, retry, budget
- SpeechSynthesis spoken responses
Notifications:
- Telegram bot with two-way chat control (long polling, authorized chat only)
- Email alerts for critical findings via Resend (sendClawAlertEmail)
- Weekly digest email (sendClawDigestEmail) triggered by heartbeat
- Notification redaction: configurable level (minimal/summary/full), confidential docs redacted
Change Detection:
- Findings diff between consecutive reviews (added/resolved/changed/unchanged)
- diff.json + manifest update per delivery
- DeliveryCard shows "+N new · -N resolved · ~N changed"
Cost Forecasting:
- forecastWork() — read-only cost estimation (no registry mutation)
- Forecast banner in OverviewTab with pending count, est. cost, local count
Portfolio Intelligence:
- GET /api/claw/portfolio — cross-document findings aggregation
- Portfolio Intelligence card: severity grid, highest-risk docs, recurring patterns
Observability:
- Audit logging (audit.ts): append-only JSON lines, 5MB rotation, 3 rotated files
- GET /api/claw/audit — last N entries
- GET /api/claw/metrics — Prometheus-format gauges/counters
- Daily backup of state.json, precedents.json, profile.json (30-day retention)
- Health verification in heartbeat (API key, watcher status)
Security Hardening:
- Watch path validation: reject
..traversal - Notification redaction for confidential documents
- WebSocket cleanup-first pattern (prevents listener leaks)
- Engage.ts safety-net .catch() on webhook dispatch chain
Setup Simplification:
claw validate— color-coded configuration health reportclaw pause/claw resume— CLI commands for direct control- Profile auto-migration for old profiles
- Post-init guidance with all available commands
Multi-client Isolation:
- ClientRegistry with per-client directories (~/.lavern/clients/{id}/)
- GET/POST /api/claw/clients endpoints
- Backward compatible: default client uses root ~/.lavern/
Linux Daemon (systemd):
- daemon-systemd.ts: user service management (no root)
- daemon-factory.ts: routes to launchd (macOS) or systemd (Linux)
Failed Document Recovery:
- Inline error display in DocumentsTab with per-document retry
- Document hash exposed in API for single-doc retry
macOS Menu Bar App (menubar/):
- Native SwiftUI status bar presence
- 30s polling, budget gauge, quick actions
Marketing Site:
- Clawern front page (site/claw/): dark cinematic design with crab hero
- 65% grain, fog vignette, 3-step setup
Hybrid Local+Frontier Processing:
- src/claw/anonymize.ts — Regex-based legal entity extraction (party names from defined terms, monetary amounts, dates, emails, phones), stable placeholder replacement, reversible de-anonymization
- src/claw/hybrid-analysis.ts — 5-stage pipeline: local triage → filter major/critical → anonymize flagged clauses → frontier dispatch (30% budget) → de-anonymize + merge with provenance tagging
- Processing mode config:
ClawProfile.processing: 'local' | 'frontier' | 'hybrid' - Fast path: if all findings are low-severity, skip frontier entirely
- Graceful degradation: frontier failure falls back to local-only findings
- Entity mapping stays in memory only, never serialized or sent externally
- Delivery with provenance tags (source: 'local' | 'frontier' | 'both') and hybridStats in manifest
Developer Experience:
- Demo mode: server starts without ANTHROPIC_API_KEY (dashboard, auth, Clawern dashboard work)
- Auto-copy
.env.example→.envon first run - RESEND_API_KEY downgraded from fatal to warning
Test Coverage: 1283 → 1440 tests (157 new across 11 test files)
v0.13.0 — Precedent Board (Institutional Memory)
Precedent Board — Cross-Document Intelligence:
- src/claw/precedent-board.ts — Persistent institutional memory for Claw Mode
- After each document is processed, significant findings (RED/YELLOW, confidence ≥ 0.7) are auto-indexed to
~/.lavern/precedents.json - Before processing future documents, matching precedents are queried and injected as agent context
- O(1) dedup index (SHA-256 of findingType + first evidence text)
- Relevance-scored search:
usage × 0.3 + effectiveness × 0.4 + recency × 0.3 - Reinforcement via incremental update (clamped [0, 1]) when patterns recur
- Time-based decay: configurable threshold (default 30d), deprecation at 6× threshold
- Compaction: archives deprecated/old entries to
precedents-archive.json - All logic is local (no LLM calls), per-client isolated, evidence-linked
Processor Integration:
- Precedent lookup before dispatch — injects matching context into agent request
- Sanitized context injection (control chars stripped, descriptions ≤ 200 chars, total ≤ 1000 chars)
- Precedent indexing after delivery — extracts findings from session debate state
- Non-fatal error handling: board failures never fail a document processing run
precedent_matchnotification on strong matches (relevance > 80%)
Dashboard:
- New "Precedents" tab in Clawern dashboard (5 tabs: Overview, Documents, Deliveries, Precedents, Config)
- Summary bar: active count, top patterns, deprecated count
- Searchable precedent cards with pattern name, description, evidence citation, metadata
- Evidence truncated to 200 chars, WCAG AA accessible (
role=list/listitem,aria-label) - Demo mode: 4 synthetic precedents (Contract Risk, Dark Pattern, Contract Deviation, Adversarial Ambiguity)
Heartbeat Integration:
- Precedent board decay + compaction runs on 12th heartbeat cycle (~6 hours)
- Deprecated precedent count surfaced in heartbeat alerts
API:
GET /api/claw/precedents— query by findingType, jurisdiction, documentType, text; validated limit [1, 100]
Configuration:
LAVERN_CLAW_PRECEDENT_DECAY_DAYS(default 30) — threshold before effectiveness decayLAVERN_CLAW_PRECEDENT_ARCHIVE_DAYS(default 90) — threshold before archivalLAVERN_CLAW_PRECEDENT_MAX_OUTCOMES(default 50) — cap on outcome history per entry
Bug Fix:
engage.ts: safety-net.catch()on webhook dispatch promise chain (prevents unhandled rejection)
Test Coverage: 1283 → 1319 tests (36 new: 26 core + 10 hardening edge cases)
v0.12.0 — Launch Ready + Hardening
Output Quality Hardening (7 gaps closed):
bestAttemptfallback re-validates before returning — structurally invalid documents never reach users- Quality gate fail-closed on first API error (was silently passing garbage through)
- Quality gate critique feeds into ALL retry attempts (was blind after attempt 3)
- Placeholder detection (
[TBD],[PLACEHOLDER]) added to structural validation (≥5 = fail) - Process contamination threshold dropped 20% → 5%;
First,/Now,/Next,excluded from full-text scan (legal prose) - Finding content sanitized via
sanitizeFindingContent()— strips agent preambles before display - Assembly context warns when analysis is unverified; quality gate applies stricter standards
Document Input Sanitization (SMAC-L1):
- src/documents/sanitize-text.ts — strips zero-width Unicode, HTML comments, ANSI escapes from all parsed document text
- NFC normalization for consistent Unicode representation
- Single sanitization point in
parser.ts— all downstream consumers (MCP tools, assembly, orchestrator, Claw) get clean text - Audit trail:
sanitizationLogfield onParsedDocument, server warning log, session event emission - Preserves legitimate Unicode (accented chars, CJK, Cyrillic, Arabic), markdown, legal bracket patterns
Security Hardening:
- Google OAuth: auto-link blocked unless BOTH existing account AND Google profile have verified emails (prevents account takeover)
- Session manager:
releaseHold()safety net on archive failure (prevents permanent billable hours lock) - Login: constant-time delay (80-120ms) for non-existent users (timing attack mitigation)
- Login error messages mapped to generic text (prevents account enumeration via error disclosure)
- GateDialog: removed
console.errorthat leaked stack traces to browser DevTools - Terms/Privacy footer: moved outside
.pagecontainer, matched main site layout + clickable mailto links
Legal Compliance:
- Terms of Service and Privacy Policy fully authored (all
[PLACEHOLDER]fields resolved)- Company: Lavern, Helsinki, Finland; Jurisdiction: Finland, courts of Helsinki
- AI disclaimer (Section 6): "does not constitute legal advice"
- Subprocessor list (Section 13): Anthropic, Mistral, Stripe, Resend, Plausible, Netlify
- Static
/termsand/privacypages on marketing site (dark cinematic design) - Footer links (Terms, Privacy) added to site/index.html
- Signup consent text: "By creating an account, you agree to our Terms and Privacy Policy"
- AI disclaimer in Delivery view: "does not provide legal advice"
Google OAuth:
GET /api/auth/google— CSRF state token + redirect to Google consent screenGET /api/auth/google/callback— Token exchange, profile fetch, 3-way account resolution:- Existing Google user → login
- Existing email user → link Google account
- New user → create account + auto-verify email + credit free trial hours
- "Continue with Google" button + divider in LoginView
- OAuth success/error redirect handling in App.tsx
- Login error display for OAuth failures (denied/failed)
Analytics & Monitoring:
- Plausible Analytics on marketing site (
script.js) and dashboard (script.hash.jsfor SPA) - Sentry React SDK in dashboard (
@sentry/react, ErrorBoundary with editorial fallback UI) VITE_SENTRY_DSNenv var for client-side Sentry
Stuck Agent Fix:
- Delivered-state fallback: force all active agents to 'complete' when workflow reaches 'delivered'
- Agent timeout: mark agents as 'Timed out' after 10 minutes of no events
Document Assembly Fix:
bestAttempttracking: keeps longest output even if validation failed- Returns best attempt instead of empty string on all-attempts failure
- Users get a document with warnings instead of perpetual loading
Strategy & Team Simplification:
- Strategy screen intro text: "Defaults work well for most engagements. Adjust only if you need to."
- Workflow picker: "Default" badge on Quick Counsel card
- TeamView: collapsible sections (Infrastructure, Legacy, Industry, Tech collapsed by default)
- Section headers show collapse chevron, selected count, and click-to-expand
- "Recommended for your engagement" banner when auto-selected team is present
Build & Type Safety:
- Clean
tsc --noEmitfor both backend and frontend (0 errors) - Fixed timestamp type mismatch in useWorkingState agent timeout logic
v0.11.3 — Marketing Site Mobile + DNS
Marketing Site (site/index.html):
- Mobile single-screen layout (≤768px): hero + footer only, all mid-sections hidden via CSS
- CTA heading changed from "Try it." to "Speak to Us."
- Mailto links updated: subject "Knock Knock", pre-filled body requesting demo
- Mist/smoke canvas effect preserved on mobile
- Hero Log In link hidden on mobile (clean single-CTA focus)
- Sub-pixel orange seam fix at hero bottom edge
DNS & Hosting:
www.lavern.aiCNAME → Netlify, SSL provisioned- Domain:
lavern.ai(ALIAS) +www.lavern.ai(CNAME) both live
v0.11.2 — 50-User Launch Hardening
Blocking Fix:
- Claude API retry wrapper (src/utils/retry-query.ts) — wraps
query()with exponential backoff (1s→2s→4s, cap 8s) on transient 429/500/502/503/529 errors; emits retry events to session so users see "Retrying..." instead of silence
Accessibility:
- Delivery tab panels:
role="tabpanel",aria-labelledby,aria-controlslinkage between tabs and panels
Free Trial & Billing:
- Free trial hours on signup: new users without invite code get 10 billable hours (~2 quick engagements) automatically
- Invite code now optional: validated if provided (bonus 50h), but signup works without one
- Config:
LAVERN_FREE_TRIAL_HOURS(default 10),LAVERN_WELCOME_HOURS(default 50 for invite users) - Session creation 402 handling: redirects to pricing page with clear "top up" messaging
- Billing hold system:
holdBillableHours/releaseHoldprevents TOCTOU race — hold placed at session start reduces visible balance, released + actual cost debited at session end. Concurrent sessions can't over-spend. - Credit idempotency scoped to non-debit entries (prevents edge case where a debit reference could block a credit)
Security:
- Session creation requires authentication —
POST /api/sessionsremoved from public paths; unauthenticated requests return 401 - Session listing requires authentication —
GET /api/sessionsremoved from public paths; prevents session ID enumeration (individual session access via ID remains public as a capability token) - Frontend auth gates on all session creation paths (YOLO, QuickStart, staffing) — redirects to login with toast
- Voice TTS route rate-limited (30 req/min per IP) to prevent API credit drain
Stability:
- EventBus max listeners raised from 50 to 200 for 50+ concurrent users
- Session eviction logged at info level instead of error (reduces noise in production logs)
UX Polish:
- Resend verification cooldown: 60-second countdown timer prevents repeated clicks and silent 429s
- Session error recovery overlay: prominent "Session Interrupted" card with "Start New Session" + "View Partial Results" CTAs and cost consumed display
- Signup form: invite code field marked optional with "Have a code? Enter it for bonus hours" helper text
- Landing page: updated copy from "Invite only" to "Sign up free. Two engagements on us."
Dev Tooling:
- scripts/load-test.ts — 50-user concurrent load test (auth, sessions, WebSocket, polling, teardown with p50/p95/p99 latencies)
v0.11.1 — Production Stability + Mobile Polish
API Resilience:
- Global fetch interceptor (
useApiFetch.ts) — catches 401/402/429/5xx across all API calls with toast dedup (3s window) - Offline detection (
useOnlineStatushook +OfflineBanner) — fixed amber banner on connectivity loss - Document upload retry with exponential backoff (3 retries, 1s→2s→4s→8s cap)
beforeunloadhandler on Briefing view when user has unsaved work
Security & Ops:
- Change password endpoint + My Page UI section (invalidates other sessions)
- Production startup validation — critical env vars (ANTHROPIC_API_KEY, RESEND_API_KEY) cause
exit(1)if missing - SQLite archive retention cleanup (default 180 days, configurable via
SHEM_ARCHIVE_RETENTION_DAYS) - Enhanced deep health check: DB size, email/Stripe/LLM key status
Mobile:
- Document upload: prominent "Upload Files" button on touch devices instead of drag-drop zone
- Mobile touch targets: minimum 44px height across components
v0.11 — Email Verification Enforcement + Security Hardening
Email & Auth Infrastructure — Complete email verification pipeline:
- Password reset flow: forgot-password → email with token → reset-password
- Email verification flow: signup → verification email → verify-email → banner clears
- Resend verification endpoint with rate limiting (3/min per IP)
- Receipt emails on billable hours purchase, low-balance warnings
- Welcome email on signup with verification link
Email Verification Enforcement — Server-side middleware blocking unverified users:
- src/api/middleware/require-verified.ts — Fastify
onRequesthook - Blocks authenticated unverified browser users from POST mutations (sessions, engage, matters)
- Skips: anonymous requests, API clients (Bearer), GET/HEAD/OPTIONS, exempt paths
- Exempt paths:
/api/auth/*,/api/billing/*,/api/documents/*,/api/waitlist,/api/briefing/* - Returns 403
EMAIL_NOT_VERIFIEDwith user-friendly message - Frontend
VerificationBanner— warm amber banner with pulsing dot, resend button, session-dismissible
Security Fixes (3 Crucial):
- Token race condition:
markTokenUsed()now atomic (UPDATE ... WHERE used_at IS NULL, returns boolean) - Password reset: token consumed FIRST before acting, remaining writes wrapped in DB transaction
- FileReader async race: document content reads now Promise-wrapped, awaited before submission
Stability Fixes (10 Smaller):
- Claw notify dedup Map: hard cap (10K entries) prevents memory leak in long-running daemon
useLLMInterview: mount guard onsetInterviewResultafter finalizationusePartnerConsult: mount guards on finalize, SSE JSON parse error logic fixed (was comparing error message to raw JSON string)- Voice route: Deepgram JSON parse failures now logged instead of silently swallowed
- HTML sanitizer: handles unquoted style attributes with
expression(), catches HTML-encodedjavascript:URLs useDeliveryData:cancelledRefchecks inretryAssemblyasync operationsuseSoundEffects: null check on soundDefs lookup
Frontend Polish:
- Login/signup:
<label>+autoCompleteattributes for accessibility and password managers - Real-time password length hints during signup and reset
- Landing: waitlist error contrast raised to WCAG AA, responsive input widths, ARIA labels
- QuickStart: low-balance color contrast fix, cowork folder error handling
- Decorative images:
role="presentation"across landing views - Button touch targets: minimum 36px height, improved padding
- MyCases: user-facing error messages instead of console.warn
Test Coverage Expansion — 1156 → 1179+ tests:
- Email verification middleware: 12 unit tests (skip/block/exempt logic)
- Email verification state: 3 integration tests
- Token atomicity: double-use prevention, concurrent race protection
v0.10 — Soft Launch Hardening + Working View Redesign
Working View Redesign — Transformed from static dashboard to lively team chat room:
- ActivityCard speech bubbles for agent start/stop/tool activity
- ReassuranceCard warm messages during processing silences (25s idle trigger)
- ProgressSidebar redesigned as Claude Code-style real-time checklist
- HeartbeatBand slimmed to single row (phase dots + stats)
- Warm team-avatar empty state with personalized greetings
WCAG AA Accessibility — Full keyboard navigation and screen reader support:
- Focus-visible indicators, skip-to-content link, ARIA landmarks
- Live regions for dynamic content, dialog/tablist/radiogroup semantics
- Color contrast raised to 4.5:1+ ratio,
prefers-reduced-motionsupport - GateDialog focus trap prevents background interaction
Responsive Layouts — Mobile/tablet/desktop via useMediaQuery hook:
- Sidebar stacking with toggle, grid collapse, header wrapping
- Desktop layout unchanged (conditional breakpoints only)
Production Hardening — 40+ security and stability fixes:
- SSRF prevention, command injection fix, TOCTOU race condition fix
- Session ID collision prevention (crypto.randomBytes entropy), FTS5/LIKE injection fix
- XSS sanitization on all HTML deliverables (script, iframe, event handler stripping)
- Gate timeout now rejects (was auto-approving), WebSocket reconnect resume
- Server-side WebSocket heartbeat (30s ping, 60s timeout)
- Webhook retry with exponential backoff (3 retries)
- Auth middleware hardened (removed /api/clients from default public paths)
- Mistral empty response guard, JSON.parse safety across frontend hooks
- Web Speech API bounds checks, replay audit entry null safety
- Claw delivery type safety (Finding field name corrections)
- Timer cleanup on component unmount (PartnerView)
- Structured logger (src/utils/logger.ts)
Error Surfaces — Users see what went wrong instead of silent failures:
- Connection Lost amber banner on WebSocket drop
- Session Expired overlay on server 4004
- Search error state in archive, offline indicator on team selection
- Analysis retry button in briefing, double-submit guard on QuickStart
UX Polish:
- View transition animations (350ms fade-up on non-landing views)
- Delivery loading skeleton (DeliverySkeleton component)
- Duplicate tab protection via BroadcastChannel (useTabLock hook)
- Back-navigation cleanup (stale sessionStorage keys removed)
- Custom agent edit mode in Agent Builder
- Error copy consistency (removed technical jargon from user-facing messages)
- Delivery view polish: responsive grids, markdown links, empty states, download feedback
Test Coverage Expansion — 610 → 1125 tests (84% increase):
- Auth routes: signup, login, profile, GDPR erasure/export (55 tests)
- Auth middleware: public paths, token validation, method-specific matching (50 tests)
- Rate limiting: sliding window, concurrent session caps, per-user isolation (11 tests)
- Dynamic permissions: phase deny rules, template overrides, orchestrator-only tools (20 tests)
- Format converter: XSS sanitization (8 tests)
- Database, session manager, config, router, Claw planner/registry coverage
Dev Tooling:
- scripts/smoke-test.sh — API end-to-end lifecycle test (no
jqdependency)
v0.9.1 — EU Sovereign Provider, Ethical Mode, Knowledge Base Expansion
- Mistral EU Provider — Full alternative LLM backend for EU data sovereignty
- src/providers/ — 5 new modules (client, executor, assembler, tool converter, types)
- Per-session provider selection: Claude (US) or EU Sovereign (Mistral)
- ProviderToggle segmented selector in Strategy view
- EU badge in Working view header when Mistral active
- Maximum Ethical Mode — One-click toggle for Clawern
- EU-only processing (Mistral), all docs confidential, conservative risk
- CLI:
lavern claw start --ethical - Dashboard: Config tab card with ON/OFF toggle, CommandStrip shield badge
- PATCH
/api/claw/ethicalendpoint
- Knowledge Base Expansion — 4 new datasets (6 total)
- ACORD: 126K+ expert-rated clause retrieval pairs (Atticus Project)
- UNFAIR-ToS: EU unfair terms of service clauses (LexGLUE)
- ContractNLI: Contract natural language inference pairs
- LEDGAR: 60K SEC contract provisions, 98 clause types (LexGLUE)
- Agent Builder Simplification — Face step reduced to avatar + seed + randomize
- Cleanup — Removed 3 dead MCP tool files (formatting-check, structure-check, verification-pipeline)
v0.9.0 — Soul, Heartbeat, Dashboard Polish
- Soul — User-defined firm personality (voice, principles, style, values)
- My Page soul editor (5000 char textarea, persists in profile)
- Injected into orchestrator system prompt for every engagement
- SOUL.md fallback for CLI/Claw mode
- Priority: session soul (user profile) > SOUL.md > empty
- Heartbeat — Periodic Clawern check-in (default 30min)
- Surfaces: budget warnings (>80%), stale docs, errors, flagged items
- Silent when everything is fine
- Configurable via
LAVERN_CLAW_HEARTBEATandLAVERN_CLAW_HEARTBEAT_INTERVAL
- Dashboard redesign
- Navigation: Landing → Briefing → Strategy → Team → Working → Delivery
- ProgressSidebar with step-by-step workflow progress
- Cowork folder mode (File System Access API for non-destructive local saves)
- QuickStart 3-tier express engagement (Quick, Standard, Deep)
- My Cases view with active + past engagement history
- 20 UX micro-fixes across 13 components
v0.8.1 — Security Hardening + Dual-Model Confidentiality
- Public API lockdown — POST mutations require auth (Bearer or cookie)
- Expired token cleanup — automatic at startup and hourly
- x402 payment middleware wired into engage route
- Dual-model confidentiality — confidential docs analyzed on-device ($0 cost)
- Sensitivity pattern matching —
*confidential*,*privileged*,*merger*, etc. - Input boundaries — symlink protection, file size limits, per-scan doc cap
- Notification system — webhook + macOS native with 5-min dedup
- Rate limiting — global + per-route limits (configurable via env vars)
- All hardcoded values extracted to config.ts with env var overrides
v0.8.0 — Clawern (Law Firm on Retainer)
- Autonomous document processing pipeline (watch, plan, process, deliver)
- Filesystem watcher with debounce
- Budget tracking and per-document cost estimates
- macOS LaunchAgent daemon
- Dashboard integration (status, documents, deliveries)
v0.7 — Production Hardening + Visual Dashboard
- Centralized config (src/config.ts) — all settings env-var configurable
- API validation (Zod schemas) — all mutation endpoints validated
- API authentication — Bearer token + cookie auth
- Error recovery — structured errors, session state preserved on failure
- Atomic memory writes — write-to-tmp-then-rename for memory/precedent files
- Frontend dashboard — SessionList, pixel-art office, real-time event streaming