The Atlas Lavern's documentation, bound to its code
111 documents
CHANGELOG.md

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-6claude-opus-4-7
  • Router + claw per-doc processing: claude-sonnet-4-5-20250929claude-sonnet-4-5 (short-form alias of the same model)
  • Briefing analyzer: claude-haiku-3-5-20250929claude-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-7 and claude-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 safeParse on every call returns invalid_args cleanly 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/list emits real JSON Schemas via z.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-Id header. 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 over session_archive LEFT JOIN users with 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 under userId: null.

1500-User Load Test Readiness:

  • New env LAVERN_LOAD_TEST_BYPASS_KEY + X-Load-Test-Bypass header — 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 ≥ 500 without 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, plus NODE_OPTIONS=--max-old-space-size=4096 for 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 + phase tags.
  • 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 + subject context.

Production Config Guards:

  • validateProductionConfig() now treats LAVERN_PROVIDER=mistral without MISTRAL_API_KEY and LAVERN_PROVIDER=managed without ANTHROPIC_API_KEY as fatal. Localhost defaults for STRIPE_CANCEL_URL and GOOGLE_OAUTH_REDIRECT_URI warn 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 ConversationalChat enables voice-driven interviews. useVoiceInput (Deepgram + Web Speech fallback) fills the input field on finalTranscript; 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 via POST /api/sessions/:id/inject. New endpoint emits tool_used event 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 → sendTextRef auto-sends via stable ref, skipping stale-closure issues.

Reused Infrastructure (no changes):

Bug Sweep:

  • All three components destructure useVoiceInput() return values (individual stable callbacks) instead of holding a reference to the returned object. Removed voice object from all useCallback/useEffect deps — was causing effects to re-run on every render.
  • Removed stray voice.isListening reference in WorkingView.tsx JSX.
  • Zero tsc --noEmit errors on both frontend and backend.

New API:

  • POST /api/sessions/:id/inject — accepts { message: string } (max 1000 chars), emits tool_used event 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 report
  • claw 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.env on 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_match notification 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 decay
  • LAVERN_CLAW_PRECEDENT_ARCHIVE_DAYS (default 90) — threshold before archival
  • LAVERN_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):

  • bestAttempt fallback 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: sanitizationLog field on ParsedDocument, 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.error that leaked stack traces to browser DevTools
  • Terms/Privacy footer: moved outside .page container, 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 /terms and /privacy pages 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 screen
  • GET /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.js for SPA)
  • Sentry React SDK in dashboard (@sentry/react, ErrorBoundary with editorial fallback UI)
  • VITE_SENTRY_DSN env 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:

  • bestAttempt tracking: 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 --noEmit for 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.ai CNAME → 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-controls linkage 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/releaseHold prevents 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/sessions removed from public paths; unauthenticated requests return 401
  • Session listing requires authentication — GET /api/sessions removed 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 (useOnlineStatus hook + OfflineBanner) — fixed amber banner on connectivity loss
  • Document upload retry with exponential backoff (3 retries, 1s→2s→4s→8s cap)
  • beforeunload handler 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 onRequest hook
  • 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_VERIFIED with 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 on setInterviewResult after finalization
  • usePartnerConsult: 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-encoded javascript: URLs
  • useDeliveryData: cancelledRef checks in retryAssembly async operations
  • useSoundEffects: null check on soundDefs lookup

Frontend Polish:

  • Login/signup: <label> + autoComplete attributes 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-motion support
  • 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:

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/ethical endpoint
  • 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_HEARTBEAT and LAVERN_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