⚡ Swarm Architecture

"Chat with Claude" Floating Widget — Design Spec

# "Chat with Claude" Floating Widget — Design Spec

_2026-05-26 · workstation-lc · status: draft for review_

Problem

The dashboard's Ask-Claude is a form/template panel on one tab. We want a floating "chat with us"-style widget — a contrasted launcher (white circle + chat-bubble glyph) fixed lower-right, opening a conversational popover — so lucas, darshan, and lyreco (Pierre) can just chat with their service-Claude to produce deliverables, from any tab (it follows them around). It must keep a persistent, long-lived context per user that survives new threads / session expiry / reloads, auto-compacting at ~1M tokens the way Claude's own context does.

Goals / scope

  • Global, persistent widget (lives outside the tab-pages; visible on every tab) for the 3 dashboard users.
  • Conversational multi-turn chat over the existing claude -p runner.
  • Persistent per-user context with ~1M-token compaction → carry-over.
  • v1 deliverable handling = read + copy (markdown/tables rendered, per-answer copy). No save-to-Reports/export, uploads, or voice in v1.
  • Bypass per decision: lucas/lyreco run immediately; darshan keeps the extraction-approval gate.
  • Superadmin/Lucas Chat Oversight (calibration): a panel gated to {lucas, superadmin} (NOT darshan) to view + edit/clear any user's carry-over memory and see their recent chat turns. Every oversight edit/clear is audit-logged (who changed whose memory). Darshan is chat-only (no oversight).

Architecture

` Browser (any tab) ┌ launcher (white ⬤ + bubble, fixed lower-right) ──toggles── popover │ popover: thread (user/Claude bubbles, markdown, copy) + input │ holds current thread token + cumulative tokens in localStorage └────────────────────────── POST /api/chat (JSON) ───────────────────────┐ ▼ dashboard/app.py POST /api/chat {message, continue_token?, carry?} • _ac_user(request) → attribute + bypass decision • extraction gate: lucas/lyreco bypass; darshan → DM-approval (existing flow) • prompt = (carry-over prefix if reseeding) + message • _ac_run_claude_with_fallback(prompt, resume_session_id, parent_token, timeout=floor) • accumulate usage tokens on the thread; if >= COMPACT_AT → flag compaction • returns {token, answer_markdown, session_id, status, cumulative_tokens, compacted} │ persistent context (server): │ • per-user carry-over logs/chat_memory/.md ◄── compaction writes • per-thread token tally in the ask-claude state file (reuse _ac_save) `

Behaviour

  • Multi-turn: widget sends the prior turn's token as continue_token → _ac_run_claude_with_fallback resumes that Claude session (existing thread-depth + resume-fallback logic).
  • Persistent memory via compaction: the endpoint accumulates the thread's token usage (from the claude -p result JSON). When cumulative ≥ COMPACT_AT (default 850_000, safely under 1M; env-tunable), it runs a compaction turn — asks Claude to condense the conversation into a compact carry-over, writes it to logs/chat_memory/.md, and the next turn starts a fresh thread seeded with that carry-over (no continue_token). Cumulative resets.
  • Durability: the carry-over also reseeds a fresh thread when --resume fails (session aged out) or the user returns after a reload — so context persists indefinitely; only raw transcript is shed at the 1M mark.
  • Bypass: reuse _AC_EXTRACTION detection. user in {lucas, lyreco} → run now (audit the bypass). darshan + extraction-shaped → existing approval gate (DM commander, _ac_send_dm), returned to the widget as status="pending_approval".
  • Timeout: the existing per-user floor already gives Lucas 1200s; chat uses the same effective_timeout path.

Components (each separable)

1. POST /api/chat (dashboard/app.py) — JSON entry point wrapping _ac_run_claude_with_fallback; bypass/gate decision; token accumulation; returns JSON. No HTML. 2. core/chat_memory.py (pure-ish) — carry_path(user), read_carry(user), write_carry(user, text), should_compact(cumulative, threshold). Unit-testable; file I/O isolated to read/write. 3. Compaction step — a function that, given a thread token, asks the runner to summarize → write_carry → returns the carry text. Triggered by the endpoint when should_compact. 4. Widget frontend — markup in dashboard/templates/home.html (launcher + popover, placed once outside the tab
s, position:fixed), a