Skip to main content

Phase 3.5 — Polish & Production Readiness

Archive note: this phase document predates the cockpit-only pivot and still includes village/avatar/Tauri-era planning language. Treat it as historical implementation context, not current product guidance.

Goal: Complete unfinished Phase 3 UI work, fix backend gaps, harden infrastructure, and eliminate adoption barriers — making what exists production-quality before expanding in Phase 4.

Status: Mostly complete — 3.5.1 (Goal UI), 3.5.2 (Interruption Mode), 3.5.5 (Token-Aware Context), 3.5.6 (Timeouts), 3.5.9 (Accessibility), 3.5.10 (LLM Logging) are done. 3.5.4 (Calendar) functional when credentials configured. 3.5.3 (Avatar State), 3.5.7 (Tauri), 3.5.8 (Infra Hardening) remain planned.

Context: The REPORT.md analysis (2026-02-12) identified several gaps between Phases 3 (complete) and 4 (planned) — items that are neither new features nor security concerns, but quality, robustness, and developer experience improvements needed to ship a solid product.


3.5.1 Goal Management UI​

Backend has full CRUD APIs (POST /api/goals, PATCH /api/goals/{id}, DELETE /api/goals/{id}, GET /api/goals/tree, GET /api/goals/dashboard), but users can only create goals through chat.

Files:

frontend/src/components/quest/
GoalForm.tsx # Create/edit goal modal (title, level, parent, domain, notes)
GoalActions.tsx # Inline edit/delete/complete buttons on GoalTree nodes
QuestSearch.tsx # Search bar + filter/sort controls for QuestPanel

Design:

  • GoalForm: Modal triggered by "+" button in QuestPanel header
    • Fields: title, level (daily → life_vision), domain (productivity/performance/health/influence/growth), parent goal (dropdown from tree), notes
    • Edit mode: same form pre-filled, triggered by pencil icon on GoalTree nodes
    • Calls POST /api/goals (create) or PATCH /api/goals/{id} (edit)
  • GoalActions: Inline action buttons on GoalTree nodes
    • Complete (check icon) → PATCH /api/goals/{id} with status: "completed"
    • Edit (pencil icon) → opens GoalForm in edit mode
    • Delete (trash icon, with confirmation) → DELETE /api/goals/{id}
  • QuestSearch: Search/filter/sort for quest panel
    • Text search across goal titles
    • Filter by: level, domain, status (active/completed/stalled)
    • Sort by: created date, due date, progress percentage

RPG styling: Pixel-border modal matching existing DialogFrame aesthetic. Form inputs with retro styling consistent with Settings panel.


3.5.2 Interruption Mode UI​

Phase 3.9 documented this but it was never implemented. The backend API exists at GET/PUT /api/settings/interruption-mode.

Files:

frontend/src/components/settings/
InterruptionModeToggle.tsx # Three-state toggle (Focus / Balanced / Active)

Design:

  • Three-state toggle component in Settings panel
  • Visual descriptions for each mode:
    • Focus: Shield icon — "Only scheduled briefings. Zero interruptions."
    • Balanced: Scale icon — "Smart timing. 3-5 touchpoints per day." (default)
    • Active: Lightning icon — "Real-time coaching. Proactive nudges."
  • Calls PUT /api/settings/interruption-mode on change
  • Current mode indicator visible in HudButtons area (small icon next to ambient dot)
  • Seraph can suggest mode changes via proactive messages (already supported in backend)

3.5.3 Avatar State Reflection​

Phase 3.8 documented this but avatar still just wanders. The ambient WebSocket messages already flow to the frontend — the Phaser-level visual changes are missing.

Files:

frontend/src/game/objects/AgentSprite.ts   # Add ambient state handling
frontend/src/game/scenes/VillageScene.ts # Wire EventBus ambient events to sprite

Design:

  • AgentSprite receives ambient state from EventBus "agent-ambient-state" events
  • State-specific idle behaviors:
    • has_insight: Seraph sits at bench, writing animation, subtle glow particles
    • goal_behind: Seraph paces slowly, darker tint overlay
    • on_track: Seraph meditates at well, bright aura
    • waiting: Seraph waves occasionally from center
  • Click Seraph in has_insight state → opens chat with queued insights
  • Wandering behavior suspended while in ambient state (resumes on idle)

3.5.4 Calendar Scan Job​

The calendar_scan job is registered in the scheduler but the implementation appears incomplete — the calendar source needs actual Google Calendar API integration.

Files:

backend/src/scheduler/jobs/calendar_scan.py    # Complete implementation
backend/src/observer/sources/calendar_source.py # Google Calendar API polling

Design:

  • Poll Google Calendar API every 15 minutes (already scheduled)
  • Cache events in CurrentContext.upcoming_events
  • Detect state transitions: meeting starting → in_meeting, meeting ending → transitioning
  • Requires Google Calendar OAuth token (setup in Settings or .env)
  • Graceful degradation: if no calendar configured, job runs but returns empty

3.5.5 Token-Aware Context Window​

Session history is fixed at 50 messages. Long conversations lose important context as older messages are truncated without summarization.

Files:

backend/src/agent/context_window.py    # Adaptive token-based windowing

Design:

  • Replace fixed 50-message window with token-counting approach
  • Use tiktoken (or model-specific tokenizer) to count message tokens
  • Target budget: 80% of model context window for history (remaining for system prompt + tools)
  • When budget exceeded:
    1. Keep first 2 messages (system context) + last 20 messages (recent context)
    2. Summarize middle messages into a "conversation so far" block using cheap model
    3. Cache summaries to avoid re-computing
  • Configurable via settings: context_window_strategy = fixed (current) | adaptive (new)

3.5.6 Agent Execution Timeout​

No timeout handling for agent execution — can hang indefinitely on slow API calls or tool execution.

Files:

backend/src/agent/timeout.py           # Timeout wrapper for agent runs

Design:

  • Wrap agent execution in asyncio.wait_for() with configurable timeout
  • Default timeout: 120 seconds for chat responses, 60 seconds for strategist ticks
  • On timeout: return graceful error message to user ("I'm taking too long — let me try a simpler approach")
  • Log timeout events for monitoring
  • Per-tool timeout support: shell_execute gets 30s, browse_webpage gets 60s, others 15s

3.5.7 Tauri Desktop App​

The Docker requirement is the biggest barrier to adoption. OpenClaw runs with npx openclaw. Seraph requires Docker Compose + daemon setup. A thorough Tauri analysis exists at docs/architecture/tauri-analysis.md.

Files:

desktop/
src-tauri/
src/main.rs # Tauri entry point, backend process management
tauri.conf.json # App config, window settings, permissions
Cargo.toml
src/ # Frontend (reuse existing React app)
main.tsx
package.json

Design:

  • Tauri v2 wraps the existing React frontend in a native window
  • Sidecar process: bundle the Python backend as a PyInstaller binary
  • Bundle the daemon: no separate daemon process needed — Tauri's Rust layer handles window polling directly via macOS APIs
  • System tray: always-on presence with menu (open village, interruption mode toggle, quit)
  • Native notifications: replace WebSocket-only delivery with OS-level notifications
  • Auto-updater: Tauri's built-in update mechanism
  • Single .dmg installer — eliminates Docker, Python, and Node.js requirements

Migration path:

  1. Package frontend as Tauri app (web view)
  2. Bundle backend as sidecar (PyInstaller)
  3. Move daemon functionality to Rust (Tauri plugin)
  4. Add system tray + notifications
  5. Build auto-updater + installer

3.5.8 Infrastructure Hardening​

Several infrastructure gaps identified that affect developer experience and deployment.

Production Docker Compose​

Files:

docker-compose.prod.yaml      # Production configuration

Design:

  • Health checks for all services (/health endpoint)
  • Restart policies (unless-stopped)
  • Resource limits (memory, CPU)
  • Volume mounts for persistent data (data/, logs/)
  • Non-root user in containers
  • Log rotation configuration

CI/CD Pipeline​

Files:

.github/workflows/
ci.yaml # Lint, type-check, test on PR
build.yaml # Build Docker images on merge to main

Design:

  • CI (on PR): TypeScript type-check (tsc --noEmit), ESLint, Python type-check (mypy), Python lint (ruff), unit tests
  • Build (on merge): Build and tag Docker images, push to GitHub Container Registry
  • Badge in README showing CI status

API Key Security​

  • Rotate the OpenRouter key committed in .env.dev
  • Move to .env.dev.example with placeholder values
  • Add .env.dev to .gitignore
  • Document environment variable setup in README

3.5.9 Frontend Accessibility​

Small but impactful UI improvements.

Files:

frontend/src/index.css                    # Font size adjustments
frontend/src/components/HudButtons.tsx # Keyboard shortcut hints

Design:

  • Increase minimum font sizes from 7-9px to 11-12px in chat messages and quest panel
  • Add keyboard shortcuts: C toggle chat, Q toggle quests, S toggle settings, Esc close active panel
  • Show shortcut hints on HudButtons hover tooltips

3.5.10 LLM Call Logging​

Global observability for all LiteLLM calls — both direct (litellm.completion()) and via smolagents (LiteLLMModel). Uses LiteLLM's CustomLogger callback system so no per-call-site changes are needed.

Files:

backend/src/llm_logger.py        # SeraphLLMLogger + init_llm_logging()
backend/config/settings.py # 5 new settings (llm_log_*)

Design:

  • SeraphLLMLogger(CustomLogger) registered via litellm.callbacks in app lifespan
  • Writes one JSON line per call to {llm_log_dir}/llm_calls.jsonl via RotatingFileHandler
  • Fields: timestamp, status, model, call_type, provider, tokens (input/output/total), cost_usd, latency_ms, stream, api_base, error
  • Content logging (messages/response) gated by llm_log_content (default off for privacy)
  • All logging wrapped in try/except — never breaks an LLM call
  • Graceful degradation: if log directory is unwritable, logs warning and skips
  • No new dependencies — uses litellm.integrations.custom_logger.CustomLogger (already a dependency) and stdlib logging.handlers.RotatingFileHandler

Implementation Order​

  1. Interruption Mode UI (3.5.2) — lowest effort, unlocks attention guardian for users
  2. Goal Management UI (3.5.1) — low effort, backend already complete
  3. Frontend Accessibility (3.5.9) — quick wins, improves daily usability
  4. Avatar State Reflection (3.5.3) — visual polish, leverages existing ambient system
  5. Agent Execution Timeout (3.5.6) — backend robustness, prevents hangs
  6. Token-Aware Context Window (3.5.5) — conversation quality improvement
  7. Calendar Scan Job (3.5.4) — enables calendar-aware proactivity
  8. Infrastructure Hardening (3.5.8) — CI/CD + prod Docker + key rotation
  9. Tauri Desktop App (3.5.7) — largest effort, biggest adoption impact, do last

Verification Checklist​

  • Goal create/edit modal works from QuestPanel (Phase 3.5.1)
  • Goal delete with confirmation works (Phase 3.5.1)
  • Quest panel search filters goals by text (Phase 3.5.1)
  • Interruption mode toggle changes mode via API (Phase 3.5.2)
  • Current interruption mode shown in HUD area (Phase 3.5.2)
  • Avatar changes behavior in has_insight / goal_behind / on_track states
  • Click avatar in has_insight state opens chat
  • Calendar scan job polls Google Calendar and updates context
  • Long conversations (100+ messages) maintain quality with adaptive windowing (Phase 3.5.5)
  • Agent execution times out gracefully after 120s (Phase 3.5.6)
  • docker-compose.prod.yaml starts all services with health checks
  • GitHub Actions CI runs on PR (lint + type-check)
  • No API keys committed in repo
  • Font sizes readable (9px+ with pixel font) (Phase 3.5.9)
  • Keyboard shortcuts toggle panels (Phase 3.5.9)
  • LLM call logging writes JSONL with tokens, cost, and latency per call (Phase 3.5.10)
  • Tauri app launches with embedded backend (stretch goal)

Dependencies​

Backend​

  • tiktoken>=0.8 — Token counting for adaptive context window
  • google-auth-oauthlib>=1.2 — Google Calendar OAuth (for calendar scan)
  • google-api-python-client>=2.0 — Google Calendar API client

Frontend​

  • No new packages — React + Zustand + Phaser + Tailwind cover everything

Infrastructure​

  • GitHub Actions (free for public repos)
  • PyInstaller (for Tauri sidecar bundling)
  • Tauri v2 CLI (cargo install tauri-cli)