Catches up all docs with the current state of the AI tool system. services/mana-ai/CLAUDE.md: - New v0.6 status section documenting NewsResearchClient, pre-planning research injection, config.manaApiUrl, and the full 28-tool / 11-module inventory (17 propose + 11 auto). apps/mana/CLAUDE.md: - New "Tool Coverage" table in the AI Workbench section listing all tools per module with their policy (propose vs auto). - New "Templates" subsection documenting the two-section gallery (agent vs workbench templates), the seed-handler registry, and the current handlers (meditate, habits, goals). - Architecture cross-reference updated to include §23. docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md: - §23.2 gains a "Server-Side Research (mana-ai, ab v0.6)" subsection explaining how NewsResearchClient mirrors the client-side research pre-step: same endpoints, same trigger regex, but HTTP-direct from the Docker network instead of SvelteKit-internal. docs/plans/README.md: - workbench-templates.md added to the roadmap table (T1 shipped). - Multi-agent description updated to mention 28 tools + server-side web-research. - Architecture cross-reference includes §23. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
15 KiB
CLAUDE.md — Mana Unified App
Project-level guidance for apps/mana/. For monorepo-wide patterns (auth, services, dev commands, env vars), see the root CLAUDE.md.
Project Overview
Mana is the unified web app at mana.how, serving 27+ product modules (todo, calendar, contacts, chat, notes, dreams, memoro, cards, picture, presi, music, storage, …) under one SvelteKit build, one IndexedDB, one auth session, one deployment.
apps/mana/apps/
├── web/ # SvelteKit 2 + Svelte 5 unified app — the main surface
├── mobile/ # Expo / React Native (lower priority, may lag)
└── landing/ # Astro static landing → Cloudflare Pages
Module System
Each module lives in apps/web/src/lib/modules/{name}/ and registers itself via module.config.ts. Module state is split into three files:
| File | Role |
|---|---|
collections.ts |
Dexie table references + (sometimes) seed data |
queries.ts |
Read-side — Dexie liveQuery hooks, type converters, pure helpers for $derived |
stores/*.svelte.ts |
Write-side — mutation methods. Never reads for UI rendering (queries.ts does that). Only reads when a mutation needs existing state (toggle, increment). |
Module store pattern
// modules/todo/stores/tasks.svelte.ts
export const tasksStore = {
async createTask(input: {...}) {
const newLocal: LocalTask = { ...input, id: crypto.randomUUID() };
const plaintextSnapshot = toTask({ ...newLocal });
await encryptRecord('tasks', newLocal);
await taskTable.add(newLocal);
return plaintextSnapshot;
},
};
// modules/todo/queries.ts
export function useAllTasks() {
return useLiveQueryWithDefault(async () => {
const locals = await db.table<LocalTask>('tasks').orderBy('order').toArray();
const visible = locals.filter((t) => !t.deletedAt);
const decrypted = await decryptRecords('tasks', visible);
return decrypted.map(toTask);
}, [] as Task[]);
}
Data Layer (Local-First)
The app reads and writes IndexedDB first, then syncs to mana-sync (Go, port 3050) in the background. One Dexie database (mana) holds 120+ collections from every module — colliding table names get a module prefix (e.g. todoProjects, cardDecks, presiDecks).
User action (e.g. tasksStore.createTask)
│
▼
Module store builds the LocalRecord
│
▼
encryptRecord(tableName, record)
│
▼
table.add(encryptedRecord) ← Dexie write
│
▼
Dexie hooks (database.ts):
- stamp userId
- stamp __fieldTimestamps per field
- stamp __lastActor + __fieldActors (user / ai / system — see AI Workbench)
- record into _pendingChanges (tagged with appId + actor)
- record into _activity
│
▼
Sync engine (sync.ts) — debounced 1s
- groups changes by appId
- POSTs to mana-sync
│
▼
mana-sync → PostgreSQL with field-level LWW + RLS
│
▼
Other clients pull via SSE / polling
│
▼
applyServerChanges → Dexie hooks (suppressed) → liveQuery → decryptRecord → UI
Deep dive: apps/web/src/lib/data/DATA_LAYER_AUDIT.md — sync engine, retry/backoff, quota recovery, telemetry, RLS, encryption rollout, threat model. Single most important file for understanding how the app works under the hood.
At-Rest Encryption
User-typed content in 27 tables is encrypted with AES-GCM-256 before it touches IndexedDB. Master key lives in mana-auth (KEK-wrapped) and is fetched on login.
| Mode | Default | What Mana can decrypt |
|---|---|---|
| Standard | ✅ Yes | The user's master key, via the server-side KEK |
| Zero-Knowledge | Opt-in (Settings → Sicherheit) | Nothing — recovery code lives only with the user |
When writing module code that touches sensitive fields:
- Add the table to
apps/web/src/lib/data/crypto/registry.tswith the field allowlist await encryptRecord(tableName, record)beforetable.add()/table.update()await decryptRecords(tableName, visible)after the Dexie query, before the type converter- The Dexie hook in
database.tsdoes NOT auto-encrypt — every store does it explicitly. This is by design (Web Crypto is async, hooks are sync).
Defaults: encrypt for new user-typed text fields; plaintext for IDs / timestamps / sort keys / enum discriminators.
User-facing docs: apps/docs/src/content/docs/architecture/security.mdx.
Routing
apps/web/src/routes/
├── (auth)/ # Public auth pages (login, register, recovery)
├── (app)/ # Auth-gated app surface — 27+ module routes
│ ├── dashboard/ # Customizable widget grid
│ ├── settings/
│ │ └── security/ # Vault status + recovery code + ZK opt-in
│ ├── todo/ # …and many more module routes
│ └── …
└── api/ # SvelteKit API endpoints (rare; most data is local-first)
The (app) group is wrapped by AuthGate, which redirects unauthenticated users to /login and reads the access tier from the JWT to gate beta/alpha/founder-only modules.
Legacy Supabase: removed. Anything mentioning @supabase/ssr, safeGetSession(), or event.locals.supabase is leftover from a much earlier iteration and should be deleted on sight.
Path Aliases (apps/web/svelte.config.js)
$lib → src/lib · $components → src/lib/components · $stores → src/lib/stores · $utils → src/lib/utils · $types → src/lib/types · $server → src/lib/server
Auth Access Pattern
Auth state lives in $lib/stores/auth.svelte.ts. The current user id is also pushed into $lib/data/current-user.ts so the Dexie creating-hook can auto-stamp userId on every record. Module stores never need to know who the current user is — they just write, and the hook stamps the right userId.
Development Commands
For full local-dev (Mana Auth + mana-sync + web together), use the root-level pnpm run mana:dev or pnpm dev:*:full commands. See root CLAUDE.md and docs/LOCAL_DEVELOPMENT.md.
Web-app-only:
cd apps/mana/apps/web
pnpm dev # Dev server on :5173
pnpm build # Production build
pnpm preview # Preview production build
pnpm check # svelte-check type check
pnpm lint # Format check + ESLint
pnpm format # Prettier write
pnpm test # Vitest (unit + integration with fake-indexeddb)
pnpm test:e2e # Playwright
Tech Stack
- Web: SvelteKit 2 + Svelte 5 (runes mode), TailwindCSS, Vite
- Auth: Mana Auth (Better Auth + EdDSA JWT) via
@mana/shared-auth - Data: Dexie.js (local-first) + mana-sync (Go) backend
- Encryption: AES-GCM-256 via Web Crypto, server-wrapped MK with optional zero-knowledge
- Local AI:
@mana/local-llm(Gemma 4 E2B, WebGPU) +@mana/local-stt(Whisper, WebGPU) — both run entirely in-browser via transformers.js - Testing: Vitest, Playwright
- Mobile: Expo, Expo Router, NativeWind, EAS Build
Svelte 5 runes are mandatory — no legacy let count = 0; $: doubled = count * 2. Always $state, $derived, $effect. See .claude/guidelines/sveltekit-web.md.
AI Workbench
The companion is a second actor that works alongside the human in every module. Full pipeline live end-to-end:
- Actor attribution — every event, record, and sync row carries
{ kind, principalId, displayName }(+ mission/iteration/rationale for AI).principalIdis the userId / agentId /system:<source>sentinel;displayNameis cached at write time so rename doesn't rewrite history. Factories in@mana/shared-ai/src/actor.ts; runtime ambient context insrc/lib/data/events/actor.ts. - Agents — named AI personas that own Missions.
/ai-agentsmodule for CRUD (policy editor, memory, budget, concurrency). Default "Mana" agent auto-bootstrapped on first login; legacy missions backfilled.data/ai/agents/{store,queries,bootstrap}.ts. - AI policy — per-tool
auto | propose | deny. Lives on the agent (agent.policy). Proposable tool names come from@mana/shared-ai'sAI_PROPOSABLE_TOOL_NAMES; the mana-ai service runs a boot-time drift guard against the same list. Resolution insrc/lib/data/ai/policy.ts; executor loadsagent.policyfor every AI write. - Proposal inbox — drop
<AiProposalInbox module="…" />into any module page to render pending proposals inline with approve / freitext-reject buttons. Cards show the owning agent's name + avatar chip. Wired in/todo,/calendar,/places,/drink,/food,/news,/notes. The mission-detail view also embeds a cross-module inbox (<AiProposalInbox missionId={id} />): shows all pending proposals for that mission across all modules with a module-badge per card, so the user can review and approve without navigating to individual module pages. - Reasoning loop — the foreground Runner chains up to 5 planner calls per iteration. Read-only tools (
list_notes,get_task_stats, etc.) execute inline as auto-policy, their outputs are fed back as syntheticResolvedInputs for the next planner call. The loop exits when a propose-policy tool is staged (human must approve), the planner returns 0 steps, or the budget exhausts. This enables "read → reason → act" missions like "list all notes and tag them" in a single run. Code:data/ai/missions/runner.tsreasoning loop. - Missions — long-lived autonomous work items at
/ai-missionswith concept + objective + linked inputs + cadence + owning agent (AgentPicker in the create flow). Both the foreground tick AND the server-sidemana-aiservice produce plans under the agent's identity;data/ai/missions/server-iteration-staging.tstranslates server-source iterations into local Proposals on sync. - Input picker —
<MissionInputPicker>sources candidates from theinput-indexregistry (notes / kontext / goals / tasks / calendar). The Runner resolves via the parallelinput-resolversregistry. Encrypted tables (notes, tasks, …) decrypt client-side only. - Auto-injected context — the Runner automatically appends the user's
kontextDocsingleton (decrypted client-side) to every planner call as a standing-context input, unless already linked manually. For missions whose objective matches research keywords (recherchier|research|news|…), a web-research pre-step runs thenews-researchRSS pipeline (discoverByQuery+searchFeeds) and injects results with explicitsave_news_articleinstructions. - Debug log — per-iteration capture of system/user prompts, raw LLM responses, resolved inputs, and auto-tool outputs. Stored in local-only Dexie table
_aiDebugLog(never synced — contains decrypted user content). Toggled vialocalStorage('mana.ai.debug')(on by default in DEV). Rendered as expandable<AiDebugBlock>under each iteration card with copy-as-JSON button. Code:data/ai/missions/debug.ts,components/ai/AiDebugBlock.svelte. - Scene lens — workbench scenes can bind to an agent via
scene.viewingAsAgentId(context menu → "An Agent binden…"). Pure UI lens, not a data-scope change.SceneAppBarshows the agent avatar on bound scene tabs. - Workbench timeline —
/ai-workbenchrenders every AI-attributed event grouped by mission iteration with per-agent filter, per-module, per-mission. Each bucket header shows agent avatar + name + mission title. Per-bucket Revert button undoes the iteration's writes viadata/ai/revert/(TaskCreated → delete, TaskCompleted → uncomplete, etc., newest-first). Separate "Datenzugriff" tab exposes the server-side decrypt audit (for missions with Key-Grants).
Tool Coverage (28 tools, 11 modules)
Agents interact with the app through tools — each one either auto (executes silently during reasoning) or propose (creates a Proposal card the user must approve). Canonical list in @mana/shared-ai/src/policy/proposable-tools.ts; server-side definitions in services/mana-ai/src/planner/tools.ts; webapp auto-tool list in src/lib/data/ai/policy.ts.
| Module | Propose | Auto |
|---|---|---|
| todo | create_task, complete_task, complete_tasks_by_title |
get_task_stats, list_tasks |
| calendar | create_event |
get_todays_events |
| notes | create_note, update_note, append_to_note, add_tag_to_note |
list_notes |
| places | create_place, visit_place |
get_places, location_log |
| drink | undo_drink |
get_drink_progress, log_drink |
| food | — | nutrition_summary, log_meal |
| news | save_news_article |
— |
| news-research | research_news |
— |
| journal | create_journal_entry |
— |
| habits | create_habit, log_habit |
get_habits |
| contacts | create_contact |
get_contacts |
Server-side web-research: mana-ai calls mana-api's /api/v1/news-research/discover + /search directly before the planner prompt is built (pre-planning injection). Missions with research-keyword objectives get real article URLs + excerpts injected as a synthetic ResolvedInput. See services/mana-ai/src/planner/news-research-client.ts.
Templates
Pre-configured starter-kits at /agents/templates — two sections:
- Agent-Templates (with AI): Recherche-Agent, Kontext-Agent, Today-Agent
- Workbench-Templates (no AI): Calmness, Fitness, Deep Work
Each template bundles: optional agent + optional scene layout + optional starter missions (paused) + optional per-module seeds. Template shape: WorkbenchTemplate in @mana/shared-ai/src/agents/templates/types.ts. Applicator: src/lib/data/ai/agents/apply-template.ts. Seed-handler registry: src/lib/data/ai/agents/seed-registry.ts — modules register via side-effect imports in missions/setup.ts. Current handlers: meditate, habits, goals. Plan: docs/plans/workbench-templates.md.
Full architecture (Planner prompt + parser in @mana/shared-ai, server-side runner, Postgres actor column, materialized snapshots, Multi-Agent gating, server-side web-research, Prometheus metrics + status.mana.how integration): docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md §20 (AI Workbench) + §21 (Mission Grants) + §22 (Multi-Agent Workbench).
Reference Documents
| Path | Purpose |
|---|---|
apps/web/src/lib/data/DATA_LAYER_AUDIT.md |
Data-layer + sync deep dive, encryption rollout, threat model, Actor attribution, backlog |
docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md |
Companion brain + AI Workbench (Actor, Policy, Proposals, Missions roadmap) |
apps/docs/src/content/docs/architecture/security.mdx |
User-facing security walkthrough |
apps/docs/src/content/docs/architecture/authentication.mdx |
Auth flow + JWT structure |
Root CLAUDE.md |
Monorepo overview, services, dev commands, env vars |