Commit graph

2989 commits

Author SHA1 Message Date
Till JS
cf9f4ecd52 fix(llm): per-task tier override bypasses global allowedTiers gate
Bug: setting taskOverrides['companion.chat'] = 'byok' didn't work
when the user's allowedTiers was empty/['none']. The tier-too-low
check in run() compared task.minTier ('browser') against userMaxTier
('none') and threw TierTooLowError before the override was even read.

Same issue in canRun() and candidateTiers().

Fix: when a per-task override exists, treat it as opt-in to that tier
even if not in the global allowedTiers. The override is the user's
explicit per-task signal — overriding the global default is exactly
what an override is for.

- run(): effectiveMaxTier = max(override, userMaxTier)
- candidateTiers(task, override): adds override to baseTiers
- canRun(): now passes the override to candidateTiers

The Companion chat now correctly uses BYOK when selected from the
toolbar, even if the user hasn't enabled BYOK in their global LLM
settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:19:50 +02:00
Till JS
e95d0487b9 perf(workbench): split SceneAppBar registration from prop updates
The single $effect that wired SceneAppBar into bottomBarStore was
re-writing barComponent on every reactive tick — every change to
openApps, locale or activeSceneId redirected through .set() and
re-assigned the component reference identically.

Add a setProps() method to bottomBarStore that mutates only barProps,
and split the workbench effect in two: a registration effect that
fires on the scenes-empty/non-empty transition, and a props effect
that pushes fresh data without touching barComponent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:13:32 +02:00
Till JS
aa29ad860f chore: refresh pnpm-lock after nutriphi -> food rename
Workspace package names changed (apps/nutriphi -> apps/food,
@nutriphi/shared -> @food/shared, @nutriphi/landing -> @food/landing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:51:51 +02:00
Till JS
1249cc49e5 feat(sync): backup as .mana zip with manifest + sha256 (M3)
Replace the raw JSONL response with a zip container:

  events.jsonl  — one SyncChange per line, as before
  manifest.json — formatVersion, schemaVersion, userId, eventCount,
                  eventsSha256, apps, timestamps, schemaVersionMin/Max

Single DB pass: events.jsonl is written while a sha256 hasher tees
every byte of the uncompressed JSONL. The manifest lands as a second
zip entry after the stream closes, so eventsSha256 is filled without
rescanning.

Integrity-check on the restore side becomes trivial (re-hash the
decompressed events.jsonl and compare). Signature over manifest.json
is deferred to a later phase; sha256 already catches corruption.

Client-side: default filename + UI label updated to .mana. Fetch flow
is unchanged — browser gets a zip blob and triggers a download.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:49:47 +02:00
Till JS
53b3746b98 refactor: rename nutriphi module to food (Essen)
Complete rename across the entire monorepo pre-launch:
- Module, routes, API, i18n, standalone landing app directories
- All code identifiers, display names, logo component
- German user-facing label: "Essen" (English brand stays "Food")
- Dexie table nutriFavorites -> foodFavorites
- Infra configs (docker-compose, cloudflared, nginx, wrangler)

Zero residue of nutriphi remains. No data migration needed (pre-launch).

Follow-up: run pnpm install, update Cloudflare DNS
(food.mana.how), rename Cloudflare Pages project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:30:07 +02:00
Till JS
f5cb833b04 perf(workbench): lazy-mount carousel cards via IntersectionObserver
Each open workbench app card previously mounted its full ListView with
its own Dexie liveQuery on initial render — so 5+ open apps meant 5+
parallel IndexedDB reads and 5+ async chunk fetches before first paint,
even though only the 1-2 cards in the horizontal viewport are visible
at a time.

PageCarousel now wraps each card in an IntersectionObserver-driven
gate. The first card mounts eagerly so paint isn't gated on observer
callback timing; the rest swap in a fixed-size placeholder until they
enter the viewport (with 50% horizontal overshoot so the next card on
either side is ready before the user scrolls to it). Mount is sticky
— once a card has been instantiated we keep it resident, since
re-running a liveQuery and re-fetching its chunk costs more than
keeping the DOM tree around.

Affects all three carousel users: workbench /, /todo, /contacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:27:32 +02:00
Till JS
ceb5f72f12 feat(sync): wire /backup/export route + client + settings UI (M1 tail)
Recovering three files dropped when a parallel terminal reset past the
original M1 commit:

- cmd/server/main.go: register GET /backup/export outside billingMiddleware
- lib/api/services/backup.ts: browser-side downloadBackup() helper
- settings/my-data/+page.svelte: "Backup & Wiederherstellung" section

Pairs with the earlier backup handler + schema_version work already on
main (79996f946). With this commit the endpoint is actually reachable
end-to-end and the download button works.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:26:30 +02:00
Till JS
79996f946a feat(sync): schemaVersion + eventId on wire (M2 protocol hardening)
- sync_changes gains schema_version column (default 1, idempotent ADD)
- Change/Changeset carry schemaVersion; server refuses > MaxSupported
- server->client changes now carry eventId + schemaVersion so the
  restore path can dedup via eventId and route through a migration
  chain keyed on schemaVersion
- backup JSONL gains schemaVersion per line

Pre-M2 clients (omit the field) are treated as v1 for compatibility.
This is the stability contract we commit to before launch: once v1
events are in the wild, all future builds must replay them forward.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:25:32 +02:00
Till JS
e4f0a410d1 test(byok): add 35 unit tests + update docs to as-built status
Three new test suites covering the critical BYOK paths:

Pricing (14 tests): estimateCost for known/unknown models, scaling,
formatCost edge cases, coverage check for all model IDs.

ByokBackend (10 tests): tier identification, resolver behavior,
provider dispatch, parameter passthrough, onUsage callback, error
paths (no key, unregistered provider), invalidateAvailability.

ByokVault (11 tests): encryption at rest verification, decryption
round-trip, auto-default for first key, promoting default demotes
previous, getForProvider logic, listMeta excludes apiKey, soft
delete, recordUsage accumulation, cross-provider isolation.

Updates docs/architecture/BYOK_PLAN.md with as-built status —
phase table with commit references, deviations from original plan
(no server-proxy fallback, no sensitive opt-in UI, no per-task
provider override yet), test coverage matrix, troubleshooting
guide, v2 follow-ups.

Provider adapters remain unit-untested (need fetch mocking + SSE
parsing) — smoke tests only.

Total: 35/35 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:23:03 +02:00
Till JS
7c6567a815 perf(web): re-apply (app) layout idle-defer + lazy modals after BYOK merge
The BYOK vault commit (db8c2574d) reverted the layout to its pre-perf
shape while integrating initByok(). Restore the perf optimizations from
b196e7782 and slot initByok() into the Phase A-idle block alongside the
other side-effect initializers.

Verified bundle impact (perf vs pre-perf parent):
- (app)/+layout chunk: 42 KB → 26 KB gzip (-38%)
- 7 lazy chunks split out (modals on demand, toasts after idle)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:22:31 +02:00
Till JS
db8c2574d6 feat(byok): IndexedDB vault + settings UI for user API keys
Phase 4-5 of BYOK. Wires the BYOK backend into the running app and
gives users a UI to manage their keys.

Data layer (_byokKeys table, v16 schema):
- Encrypted at rest via user master key (wrapValue/unwrapValue)
- NOT in SYNC_APP_MAP — keys stay device-local on purpose
- Tracks per-key usage: count, total tokens, cumulative USD cost

byokVault API (lib/byok/):
- listAll() / listMeta() — decrypt on read
- create() — encrypts + auto-handles isDefault collisions
- update() — label/model/isDefault edits (not the key itself)
- delete() — soft-delete
- recordUsage() — called from backend's onUsage callback
- getForProvider() — resolver helper

initByok() wires the ByokBackend into the shared orchestrator at
app startup. The resolver picks the most-recently-used provider's
key by default. Usage callback updates the key's counters + cost
via estimateCost().

Settings page (/settings/ai-keys):
- List existing keys with provider, label, model, usage stats
- Add form: provider picker, label, API key (password input),
  model selector with provider defaults, isDefault toggle
- Inline edit for label + model
- Set-as-default action
- Soft-delete with confirmation
- Gracefully handles locked vault state

Companion chat dropdown already picks up the new 'byok' tier
from ALL_TIERS — user can select BYOK directly as KI-Modus.

Total BYOK implementation: ~1500 LOC across 12 files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:14:00 +02:00
Till JS
a33857fa39 feat(llm): add BYOK tier + 4 provider adapters (OpenAI, Anthropic, Gemini, Mistral)
Phase 1-3 of BYOK support. Introduces a 5th LLM tier 'byok' that
routes to user-provided API keys via direct browser fetches.

shared-llm additions:
- LlmTier extended with 'byok' (rank 3, between mana-server and cloud)
- ByokBackend: LlmBackend implementation that delegates key lookup
  to an app-provided resolver callback, then dispatches to the right
  provider adapter
- 4 provider adapters:
  - OpenAI (gpt-5, gpt-4o, o1 family)
  - Anthropic (Claude Opus/Sonnet/Haiku 4.6) with CORS header
  - Gemini (2.5 Pro/Flash) — REST API with different message format
  - Mistral — OpenAI-compatible, reuses shared openai-compat adapter
- Pricing table for 20+ models with USD per 1M tokens
- estimateCost() + formatCost() helpers

Keys stay device-local (IndexedDB in next phase). Browser-direct
fetches mean keys never touch Mana's server.

Updates two existing tier maps (memoro DetailView, SourceBadge) to
include the new tier.

Planning doc at docs/architecture/BYOK_PLAN.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:06:48 +02:00
Till JS
3817111f80 feat(themes): redesign theme picker with gradient cards + beefy mode selector
Replace the shared ThemePage component inside the Themes workbench
panel with a custom compact layout better suited to the narrow
(~300px) panel context. ThemePage was designed for a full-width
desktop route and reads as noisy/overloaded in a panel.

Mode selector (Hell/Dunkel/System) — primary-fill active state with
white icon+text (was subtle shadow-sm that barely registered in dark
mode), fill-weight icons when active, equal-width pill buttons in a
shared muted container.

Theme cards (Option D — "Farbton-Karte") — swap the 2×5 overlapping
color-dot preview for a large 16:10 gradient (primary → secondary in
the effective mode), theme name overlaid bottom-left with text-shadow,
subtle dark-overlay at the bottom for readability, white check badge
in the corner when active, 2px primary border + glow ring for the
active state. Hover lifts the card 1px. Renders all 8 variants
(default + extended) in a uniform 2-column grid.

Wallpaper tabs (Farben/Bilder/Upload + scope toggle) — restyle via
scoped :global() overrides to match the mode selector: muted pill
container, primary-fill active state, muted-foreground inactive.
Previously these used .bg-surface + .shadow-sm which was nearly
invisible against the panel background.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:58:10 +02:00
Till JS
b196e7782e perf(web): idle-defer non-critical (app) init + lazy-load modals
Homepage/workbench TTI was dominated by the layout synchronously booting
event-bridge, streak tracker, LLM queue, memoro watcher, dashboard store,
shared-uload and reminder scheduler before first paint, plus statically
importing 7 modals/toasts/banners that are rarely-to-never visible on
initial render.

- Keep critical path inline: local-store init (manaStore/tag/link),
  unified sync engine + billing, guest-mode setup.
- Move side-effect streams, projection workers and telemetry to
  requestIdleCallback (with setTimeout fallback).
- Dynamically import KeyboardShortcutsModal, OnboardingWizard,
  GuestWelcomeModal, SessionWarning, EncryptionIntroBanner,
  SuggestionToast, NudgeToast. Modals fetch on first demand; toasts
  mount after idle so their transitive deps (automationsStore,
  day-snapshot projection, streaks, crypto gate) don't land in the
  initial chunk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:41:27 +02:00
Till JS
c357a1cd1d feat(brain): AI tier selector in Companion chat toolbar
Dropdown at the top of the chat with options: Auto, Keine, Browser,
Mana-Server, Cloud. Selecting a tier sets
settings.taskOverrides['companion.chat'] so the choice only affects
the Companion, not other LLM tasks. "Auto" clears the override and
lets the orchestrator pick the user's preferred tier.

Also shows the current auto-selected tier inline so the user knows
what Auto resolves to, e.g. "Auto (Browser)".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:15:30 +02:00
Till JS
180e07d59e feat(credits): admin-gifted sync subscriptions
Admins can now grant Cloud Sync to users without charging credits. Gifted
rows carry is_gifted=true plus gifted_by/gifted_at audit columns; the
billing cron skips them, and /activate and /deactivate refuse to touch
them. New endpoints POST/DELETE /api/v1/admin/sync/:userId/gift.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:11:07 +02:00
Till JS
e885713fd0 refactor(brain): migrate Companion engine to LlmOrchestrator (4-tier system)
Replaces the ad-hoc local-first + server-fallback pattern with the
shared LlmOrchestrator, giving the Companion Chat full access to
the 4-tier system (none/browser/mana-server/cloud) and its privacy
+ user-preference enforcement.

New companionChatTask (lib/llm-tasks/companion-chat.ts):
- name: 'companion.chat'
- minTier: 'browser' (no rules fallback — needs an LLM)
- contentClass: 'personal' (allows server/cloud if user opted in;
  NOT 'sensitive' because the chat isn't restricted to browser-only,
  but the user can set it per-task via taskOverrides)
- requires: { streaming: true }

Engine changes:
- callLlm() now delegates to llmOrchestrator.run(companionChatTask, ...)
- Still preloads the local model when browser tier is available so
  the UI can show download progress
- isCompanionAvailable() now asks llmOrchestrator.canRun() which
  considers user settings + backend readiness + consent gates

User benefits:
- Tier-selector in the PillNav now applies to Companion Chat
- Users can force cloud/server/browser per-task via settings overrides
- Cloud tier only runs when cloudConsentGiven is set
- Privacy: content marked 'sensitive' in other tasks (Journal etc.)
  is still restricted to browser/rules — Companion respects the
  same orchestrator so privacy invariants hold consistently

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:09:57 +02:00
Till JS
4b2007e97c fix(pwa): wire up manifest link + SW registration so install prompt works
The PWA was configured end-to-end in vite.config.ts and built the
manifest + service worker correctly, but neither was ever loaded by
the browser — no <link rel="manifest"> in the HTML and no script
registering the generated registerSW.js. Chrome therefore never fired
beforeinstallprompt, no install icon appeared in the URL bar, and the
hand-rolled PwaUpdatePrompt hung on navigator.serviceWorker.ready
because no SW had been registered.

Changes:
- Render pwaInfo.webManifest.linkTag into <svelte:head> in the root
  layout so Chrome finds the hashed manifest.
- Replace the hand-rolled SW-update logic in PwaUpdatePrompt with
  useRegisterSW() from virtual:pwa-register/svelte — it registers the
  worker (immediate: true) and exposes reactive needRefresh +
  updateServiceWorker stores that match registerType: 'prompt'.
- Add triple-slash refs to vite-plugin-pwa/info and /svelte in
  app.d.ts for the virtual module types.
- Set manifest.id = startUrl in @mana/shared-pwa so Chrome doesn't
  warn and keeps the install identity stable across start_url edits.
- Keep devEnabled: false and expand the comment: the 2026-04-08
  dreams mic-button bug and the /offline navigateFallback both
  misbehave when Workbox precache doesn't run under vite dev. Test
  the install flow via `pnpm build && pnpm preview` instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:05:49 +02:00
Till JS
4192a4bd9b feat(brain): emit Companion chat + tool events for observability
Closes the gap where the Companion module wrote messages directly to
IndexedDB without participating in the Domain Event stream — chat
activity, tool invocations and conversation creation were invisible
to the Event Stream page, Goals, Streaks, and Memory layer.

New events (3 types):
- CompanionConversationStarted: emitted on chatStore.createConversation
- CompanionMessageSent: emitted on user/assistant messages (skips
  empty tool plumbing messages)
- CompanionToolCalled: emitted in engine.runCompanionChat after every
  tool execution, with tool name, source module, success/failure,
  latency in ms, and error message on failure

Event Stream page updated with icons (ChatCircle, Robot) and German
labels for the three new event types so they appear inline with all
other domain activity.

Now possible (future iterations):
- Goals like "5x Companion-Chat pro Woche"
- Streaks for daily Companion usage
- Tool performance analytics ("create_task hat 3% Fehlerrate")
- Memory facts about which tools the user uses most

Total: 70 event types, 51 tools across 31 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:54:43 +02:00
Till JS
9ff2cfcdac feat(workbench): unify system pages as workbench apps + categorize picker
Add 8 system pages as first-class workbench apps (Settings, Themes,
Profile, Admin, API Keys, Help, Feedback, Subscription) so they can be
opened as side-by-side panels next to other apps instead of requiring
a full-page route switch. Existing routes remain as fullscreen
fallback/deep-link targets.

Group the AppPagePicker by 5 categories (Companion, Leben, Arbeit,
Kreativ, System) with collapsible sections; System is collapsed by
default. Search still works as a flat fuzzy match across all apps.
Category assignment lives in a central map so registerApp() calls stay
unchanged — unmapped apps fall back to System, which surfaces
miscategorization at a glance.

Remove profile-data and theme-picker duplication from Settings (both
are separate workbench apps now): Settings defaults to 'Allgemein' and
passes showTheme={false} to GlobalSettingsSection; SettingsSidebar
accepts a categories override so the workbench version hides Profile.

Fix Cannot-read-'subscribe'-of-undefined crash in mood/sleep/body/
stretch ListViews when opened in the workbench: replace getContext
(which is only set by the route +layout.svelte) with direct query-hook
calls, matching the goals/companion pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:48:44 +02:00
Till JS
51c8a52811 fix(brain): companion can now act on previous tool results across turns
Five fixes from observed chat where user asked to complete two
tasks by title but the LLM had no way to find their IDs:

1. Tool result history: messagesToLlm() now includes previous
   tool_result messages as "[Previous tool result]" entries so
   the LLM can reference IDs/data from earlier turns.

2. Bare JSON tool call fallback: extractToolCall() now also
   matches bare {"name":..., "params":...} JSON without the
   ```tool fence — the LLM kept dropping the fence.

3. IDs in list message: list_tasks now formats each entry as
   "• [abc123] Title" so the LLM has the ID alongside the title.

4. New complete_tasks_by_title tool: case-insensitive substring
   match, completes all matches at once. Handles "erledige beide
   sicher sicher tasks" without needing IDs.

5. System prompt updates: explains the [id] bracket convention,
   warns the LLM to NEVER show raw IDs to users, and references
   the new tool for title-based completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:41:43 +02:00
Till JS
77d455a18d fix(brain): companion can now actually list tasks instead of hallucinating
Two fixes for the chat where the user asked "show my tasks" and the
LLM responded with "I don't have direct access to a dynamic task list":

1. New list_tasks tool with title/dueDate/priority for each task,
   filterable by open/completed/overdue/today/all (default: open).
   The message field returns a markdown bullet list so the LLM can
   pass it through directly.

2. System prompt rewritten to explicitly tell the LLM:
   - WHEN to call which tool (concrete examples for common questions)
   - "Erfinde keine Daten" — don't make up info, call a tool
   - Tool format must be JUST the JSON block, no preamble

Common questions now have explicit mappings:
- "Welche Tasks?" → list_tasks
- "Wie viel Wasser?" → get_drink_progress
- "Welche Termine heute?" → get_todays_events

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:31:13 +02:00
Till JS
daa5aaf0a1 fix(brain): companion chat — markdown rendering, loading status, streaming feedback
Three UX fixes for the Companion Chat:

1. Markdown rendering: assistant messages now render as HTML via
   marked (gfm + breaks). **bold**, _italic_, lists, code blocks,
   links all work. Custom CSS for Mana theme integration.

2. Loading status: while the local Gemma model downloads/loads
   (first use is ~500MB), the placeholder bubble shows "Modell
   wird geladen... 42%" with a spinning icon instead of just a
   blank "thinking" state.

3. Streaming feedback: the placeholder bubble appears immediately
   when sending and shows tokens as they stream from the LLM,
   rendered as markdown in real-time. Auto-scrolls on each chunk.

The pulse animation on the streaming markdown gives a subtle
"this is generating" hint.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:28:16 +02:00
Till JS
5ae78998d7 test(brain): add 29 unit tests for Event Bus, Tools, Goals, Streaks, Correlations
5 test suites covering the critical Companion Brain systems:

Event Bus (7 tests): typed delivery, onAny, unsubscribe, off(),
error isolation between handlers, multiple handlers

Tool Executor (8 tests): valid execution, unknown tool, missing
required params, string→number coercion, invalid coercion, enum
validation, error catching, optional params

Goal Tracker (6 tests): create from template, pause/resume, soft
delete, event-driven increment, filter matching, paused goals
not tracked

Streak Tracker (5 tests): event-driven seeding, no double-count
same day, filter correctness (water only), water event tracking,
independent multi-streak tracking

Correlation Engine (3 tests): insufficient data returns empty,
positive correlation detection, cross-module only filtering

Also fixes race condition in streaks.ts markActive() — concurrent
events could both try to seed the same streak, causing ConstraintError.
Now caught with try/catch.

All 29 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 00:14:55 +02:00
Till JS
399e927c00 feat(brain): add Goal Editor UI and event-driven incremental streaks
Final two TODOs resolved — the Companion Brain backlog is now empty.

Goal Editor (GoalEditor.svelte):
- Modal with event type picker (13 options), count/sum mode,
  optional filter field/value, target value/period/comparison
- Integrated into Goals ListView with "Eigenes" button alongside
  the existing "Vorlage" template picker
- Creates custom goals via goalStore.create()

Incremental Streaks (rewritten streaks.ts):
- Persistent _streakState table replaces the 90-day lookback scan
- 6 streak definitions: water goal, tasks, meals, workout, journal,
  meditation — each triggered by specific domain events
- Event bus subscription marks streaks active on matching events
- markActive() is O(1): read state → check if today already active
  → increment or reset based on consecutive day check
- useStreaks() reads from _streakState (single table scan, no
  per-day queries) instead of 270+ queries worst case
- startStreakTracker/stopStreakTracker wired into layout lifecycle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:52:45 +02:00
Till JS
677f6b799d feat(brain): add NudgeToast, server LLM fallback, trigger-event bridge
Three remaining TODOs resolved:

1. NudgeToast (in-app nudge display):
   - New NudgeToast.svelte in bottom-stack alongside SuggestionToast
   - Evaluates Pulse Rules every 60s, shows nudges as toasts
   - Action button navigates to module route, dismiss records outcome
   - Badge shows count when multiple nudges are queued

2. Server LLM fallback:
   - Companion engine now tries local LLM (Gemma/WebGPU) first
   - Falls back to mana-api /api/v1/chat/completions if no WebGPU
   - isCompanionAvailable() returns true if either path works
   - Graceful error messages when neither is available

3. Trigger-Event bridge (legacy automation migration):
   - event-bridge.ts maps 13 domain event types to legacy
     (appId, collection, op) format
   - Existing user automations now fire on domain events too
   - Domain events carry decrypted data → condition matching on
     encrypted fields (title, etc.) works correctly
   - Bridge wired into layout startup/cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:45:37 +02:00
Till JS
d6d50e4d94 feat(brain): add meditate+sleep, parallelize DaySnapshot, deprecate _activity
Final optimization pass for the Companion Brain.

New modules (31 total):
- Meditate: MeditationCompleted event + log_meditation tool
- Sleep: SleepLogged event + log_sleep tool

Performance: DaySnapshot buildSnapshot() now runs all 6 Dexie
queries + 4 decryption passes in parallel via Promise.all instead
of sequentially. Estimated 3-5x speedup on first render.

Cleanup: trackActivity() in database.ts is now a no-op — the
_activity table is no longer written to. getRecentActivity() in
activity.ts delegates to queryEvents() from the Domain Event Store,
converting domain events to the legacy ActivityEntry shape.

Totals: 69 event types, 49 tools across 31 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:37:33 +02:00
Till JS
6d7e4d0fb1 docs(brain): comprehensive status update — 29 modules, TODOs, architecture review
Updates the architecture doc with:
- Full module rollout table (29 modules with event/tool counts)
- List of 11 remaining modules with priority assessment
- Known technical debt: _activity table duplication, old trigger
  system overlap, streak query performance
- Optimization opportunities: parallel DaySnapshot queries,
  streak caching, Context Document expansion
- Feature gaps: no Goal editor UI, no in-app Nudge display,
  no WebGPU fallback for Companion Chat

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:28:01 +02:00
Till JS
c95aaa4d48 feat(brain): add domain events + tools for remaining 9 modules
Batches 5+6: extends to 29 modules. Adds events and tools for cycles,
firsts, guides, inventory, photos, plants, news, recipes, questions.

New domain events (12 types):
- Cycles: CycleDayLogged
- Firsts: FirstCreated
- Guides: GuideCreated
- Inventory: InventoryItemCreated
- Photos: PhotoDeleted
- Plants: PlantCreated, PlantDeleted
- News: ArticleSaved
- Recipes: RecipeCreated, RecipeDeleted
- Questions: QuestionAsked

New tools (7 tools):
- Cycles: log_cycle_day
- Firsts: create_first
- Guides: create_guide
- Inventory: create_inventory_item
- Plants: create_plant
- Recipes: create_recipe

Skipped (no simple create API): context (read-only), news (complex
LocalCachedArticle input), questions (requires questionId).

Totals: 67 event types, 47 tools across 29 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:26:57 +02:00
Till JS
c7de86282b feat(brain): add domain events + tools for music, storage, chat, memoro, skilltree
Batch 4: extends to 20 modules. Adds events and tools for music
(song added, playlist created), storage (folder created), chat
(message sent, conversation created), memoro (memo created), and
skilltree (skill created, XP added).

New domain events (10 types):
- Music: SongAdded, PlaylistCreated
- Storage: FolderCreated, FileDeleted
- Chat: ChatMessageSent, ChatConversationCreated
- Memoro: MemoCreated, MemoDeleted
- Skilltree: SkillXpAdded, SkillCreated

New tools (8 tools):
- Music: create_playlist
- Storage: create_folder
- Chat: create_chat_conversation
- Memoro: create_memo
- Skilltree: add_skill_xp, create_skill

Totals: 55 event types, 40 tools across 20 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:07:34 +02:00
Till JS
7752ba9ff9 feat(brain): add domain events + tools for finance, dreams, cards, times, events
Extends the Companion Brain to 15 modules. Adds semantic domain events
and LLM tools for finance, dreams, cards, times, and social events.

New domain events (10 types):
- Finance: TransactionCreated, TransactionDeleted
- Dreams: DreamCreated, DreamDeleted
- Cards: CardCreated, CardStudied
- Times: TimerStarted, TimerStopped
- Social Events: SocialEventCreated, SocialEventDeleted

New tools (7 tools):
- Finance: add_transaction
- Dreams: create_dream
- Cards: create_card
- Times: start_timer, stop_timer
- Events: create_social_event

Totals: 45 event types, 32 tools across 15 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 23:00:01 +02:00
Till JS
c51382a76e feat(brain): add domain events + tools for habits, journal, notes, contacts, body
Extends the Companion Brain to 10 modules (from 5). Adds semantic
domain events and LLM tools for the next 5 most valuable modules.

New domain events (15 types):
- Habits: HabitLogged, HabitCreated, HabitDeleted
- Journal: JournalEntryCreated, JournalMoodSet, JournalEntryDeleted
- Notes: NoteCreated, NoteDeleted
- Contacts: ContactCreated, ContactDeleted
- Body: WorkoutStarted, WorkoutFinished, SetLogged,
  MeasurementLogged, EnergyCheckLogged

New tools (12 tools):
- Habits: log_habit, get_habits, create_habit
- Journal: create_journal_entry, set_mood
- Notes: create_note
- Contacts: create_contact, get_contacts
- Body: start_workout, finish_workout, log_measurement

Totals: 35 event types, 25 tools across 10 modules.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:48:15 +02:00
Till JS
4211ce68da feat(brain): add 4 Companion Brain workbench pages
Registers Mein Tag, Event Stream, Companion Chat, and Ziele as
workbench apps so they can be added to scenes alongside existing
modules like Todo, Calendar, etc.

New workbench pages:
- Mein Tag (myday): DaySnapshot overview — tasks, events, water
  progress, nutrition, streaks at a glance
- Events (eventstream): live domain event feed with icons, labels,
  and timestamps — shows the system "pulse" in real-time
- Companion (companion): embedded chat interface that auto-creates
  a conversation on first use
- Ziele (goals): goal cards with progress bars, template picker
  for quick goal creation, pause/resume/delete

Each page registered in both app-registry (workbench views) and
shared-branding (app metadata, icons, descriptions, tier=guest).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:33:53 +02:00
Till JS
e1884cfbd1 fix(brain): fix companion page not rendering — schema version + query robustness
- Bump companion tables to db.version(14) so the schema upgrade
  runs even on browsers that already saw v10 with only _events
- Add try/catch to useConversations() and useMessages() queries
  to prevent silent crashes if tables don't exist yet
- Use db.table() directly in chat store instead of collections.ts
  module-level references (avoids eager table resolution issues)
- Add min-height to empty state and companion page container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:19:59 +02:00
Till JS
0847ec86e8 docs(brain): update architecture plan with Phase 5-7 completion and testing guide
Marks all 7 phases as completed with commit hashes, implementation
notes, and decisions made during implementation. Adds comprehensive
manual testing section covering Event Bus, Projections, Companion Chat,
Rituals, Goals, Memory, Correlations, and Pulse Rules — with code
snippets for DevTools Console and IndexedDB inspection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:11:04 +02:00
Till JS
87a1dd6829 feat(brain): add Semantic Memory, Pattern Extractors, and Correlation Engine
Phase 7 (final) of the Companion Brain architecture.

Semantic Memory (companion/memory/):
- MemoryFact model with confidence lifecycle (0.3 initial, +0.15 confirm,
  -0.15 contradict, weekly decay after 30 days, delete below 0.1)
- Store with recordFact (upsert by factKey), contradictFact, applyDecay
- 3 pattern extractors: day-of-week (recurring days), time-of-day
  (peak 4h window), frequency (daily average) — all rule-based, no LLM
- Runs across all 5 pilot modules (11 extraction rules total)

Correlation Engine (data/projections/correlations.ts):
- Pearson correlation between 7 daily metrics across 4 modules
- Metrics: tasks completed, water ml, coffee count, calories, meals,
  calendar events, places visited
- Only returns cross-module correlations with |r| >= 0.3 and >= 14 days
- Natural language sentence generation for each correlation

Context Document updated:
- Now accepts optional memory facts + correlations
- Appends "Bekannte Muster" section (top 6 high-confidence facts)
- Appends "Zusammenhaenge" section (top 3 correlations with r-value)

This completes all 7 phases of the Companion Brain architecture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:07:46 +02:00
Till JS
41357b2541 feat(brain): add Ritual system with guided routines and step execution
Phase 6 of the Companion Brain. Introduces guided routines ("rituals")
that walk users through multi-step sequences, executing tools and
displaying projection data at each step.

Data layer (companion/rituals/):
- LocalRitual + LocalRitualStep + LocalRitualLog types
- 6 step types: tool_call, number_input, text_input, mood_picker,
  info_display, checklist
- 3 templates: Morning routine (water + events + tasks + streaks),
  Evening routine (progress + reflection), Hydration check
- Store with createFromTemplate, CRUD, step management, completion logs
- Reactive queries for active/all rituals

UI:
- RitualRunner.svelte: step-by-step card UI with progress bar,
  tool execution, number/text input, projection data display,
  skip/next navigation
- /companion/rituals route: ritual list, template picker, play/pause

Adds rituals + ritualSteps + ritualLogs tables (v10 schema).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:01:37 +02:00
Till JS
46db527f8c feat(brain): add Companion Chat module with LLM tool calling
Phase 5 of the Companion Brain. Introduces the Companion Chat that
ties together all previous phases into a conversational interface.

Module (modules/companion/):
- types.ts: LocalConversation + LocalMessage with tool call/result fields
- collections.ts: companionConversations + companionMessages tables
- stores/chat.svelte.ts: conversation + message CRUD
- queries.ts: reactive useConversations() + useMessages()
- engine.ts: chat orchestration — builds system prompt from Context
  Document, sends to local LLM (Gemma via @mana/local-llm), handles
  tool calls via JSON extraction + executeTool(), supports multi-round
  tool calling (max 3 rounds)

UI:
- CompanionChat.svelte: message list, streaming output, tool result
  display, keyboard submit (Enter)
- /companion route: sidebar with conversation list + chat area

Also updates the architecture plan with Phase 1-4 completion status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:23:38 +02:00
Till JS
1e992d3c92 feat(sleep): add sleep module with tracking, hygiene checklists, and stats
New "Sleep/Schlaf" module for daily sleep tracking with morning quick-log,
quality ratings, sleep hygiene evening checklists, and comprehensive stats.

Includes: 10 preset hygiene checks, upsert-by-date entries, week bar chart
with goal line, sleep debt calculation, consistency score (stddev-based),
streak tracking, 30-day quality heatmap, and hygiene-quality correlation.

Dashboard shows last night summary, week overview, stats grid, and hygiene
impact. Morning log has smart defaults, star rating, interruption counter,
tag chips. Hygiene checklist supports custom user-created checks.

Registered in module-registry, encryption registry (4 tables), database v13,
seed-registry, app-icons (moon icon, indigo), mana-apps, and workbench.

Also updates MODULE_IDEAS.md with stretch (built), posture, skin, eyes entries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 21:19:52 +02:00
Till JS
66dd684bba feat(brain): add Tool Layer with LLM-accessible module operations
Phase 4 of the Companion Brain. Introduces a standardized tool
interface that gives LLMs read/write access to module operations
via function calling.

Core (data/tools/):
- ModuleTool interface with typed parameters and execute function
- Registry with dynamic registration and LLM schema generator
- Executor with parameter validation and error handling
- Init function wired into app layout startup

Module tools (13 tools across 5 modules):
- Todo: create_task, complete_task, get_task_stats
- Calendar: create_event, get_todays_events
- Drink: log_drink, get_drink_progress, undo_last_drink
- Nutriphi: log_meal, get_nutrition_summary
- Places: create_place, record_visit, get_places, get_current_location

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:47:32 +02:00
Till JS
777810d0d2 docs(mail): add TODO checklist for remaining Phase 1-4 work
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:46:00 +02:00
Till JS
a2f05409a4 chore(mail): add infra — port 3042, DB schema setup, pnpm install
Reserves port 3042 in PORT_SCHEMA.md, adds mail pgSchema to
setup-databases.sh and init-db scripts, installs mana-mail workspace
dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:42:12 +02:00
Till JS
9066b6c9ae feat(brain): add Goal system, Pulse Rule Engine, and Feedback Loop
Phase 3 of the Companion Brain architecture.

Goal System (companion/goals/):
- LocalGoal model with metric + target definitions
- Event bus subscriber that auto-tracks progress per period
- 6 goal templates (water, tasks, meals, calories, places, coffee)
- CRUD store with pause/resume/complete/abandon lifecycle

Pulse Rule Engine (companion/rules/):
- 5 deterministic rules: water reminder (90min interval), streak
  warning (18:00), morning summary (08:00), overdue tasks (10+15:00),
  meal reminder (12+19:00)
- ReminderSource adapter for existing reminder scheduler
- Interval + schedule triggers with per-rule last-run tracking
- Dismissal tracking via localStorage

Feedback Loop (companion/feedback/):
- NudgeOutcome model (acted/dismissed/snoozed/expired + latency)
- Persisted to _nudgeOutcomes IndexedDB table
- Stats + action rate queries for future rule optimization

Also adds companionGoals, _memory, _nudgeOutcomes tables (v10 schema).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:40:42 +02:00
Till JS
a3de6b3d81 feat(mail): add mana-mail service and frontend module (Phase 1 MVP)
Backend: Hono/Bun service on port 3042 with JMAP client for Stalwart,
account provisioning (@mana.how addresses on user registration),
thread/message/send/label API endpoints, and JWT + service-key auth.

Frontend: Mail module with 3-column inbox UI (mailboxes, thread list,
detail/compose), local-first encrypted drafts in Dexie, and API-driven
thread fetching. Scoped CSS with theme tokens.

Integration: Dexie v11 schema, mail pgSchema in mana_platform,
mana-auth fire-and-forget hook for account provisioning,
getManaMailUrl() in API config, app registry + branding update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:35:54 +02:00
Till JS
40e1145e9f feat(brain): add Projection Engine with DaySnapshot, Streaks, and Context Document
Phase 2 of the Companion Brain. Adds live-reactive projections that
aggregate data across all 5 pilot modules into high-level views:

- DaySnapshot: today's tasks (total/completed/overdue/due), calendar
  events (upcoming/next), drink intake (water/coffee/total with goals),
  nutrition (meals/calories/protein with goals), places visited
- Streaks: consecutive-day tracking for water goal, task completion,
  and meal logging with active/at_risk/broken status (90-day lookback)
- Context Document: ~500 token markdown generator combining DaySnapshot
  + Streaks for LLM system prompts

Also wires startEventStore() into the app layout so domain events
from Phase 1 are persisted to IndexedDB on every module mutation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:35:36 +02:00
Till JS
aabf130480 feat(stretch): add stretch module with guided routines, assessment, and reminders
New "Dehnen/Stretch" module for guided stretching with timer-based sessions,
mobility self-assessments, streak tracking, and configurable reminders.

Includes: 22 seed exercises, 5 preset routines (morning, desk break, evening,
upper body, lower body), fullscreen session player with Performance.now() timer
and Wake Lock, 6-step mobility assessment wizard with scoring, 30-day heatmap,
body region balance chart, custom routine builder, and reminder management.

Registered in module-registry, encryption registry (5 tables), database v9,
seed-registry, app-icons, mana-apps, and workbench app-registry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:29:06 +02:00
Till JS
e927c1f10f feat(brain): add Domain Event Bus and emit events from 5 pilot modules
Phase 1 of the Companion Brain architecture. Introduces a typed,
synchronous event bus with microtask-scheduled handlers, an append-only
event store persisted to IndexedDB (_events table, v10 schema), and
semantic domain events emitted from module stores.

Pilot modules with emit() calls:
- Todo: TaskCreated, TaskCompleted, TaskUncompleted, TaskDeleted, SubtasksUpdated
- Calendar: CalendarEventCreated, CalendarEventUpdated, CalendarEventDeleted
- Drink: DrinkLogged, DrinkEntryDeleted, DrinkEntryUndone
- Nutriphi: MealLogged, MealFromPhotoLogged, MealDeleted
- Places: PlaceCreated, PlaceDeleted, PlaceVisited, LocationLogged,
  TrackingStarted, TrackingStopped

Also includes the full architecture plan at
docs/architecture/COMPANION_BRAIN_ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 20:25:46 +02:00
Till JS
f5b9d0a31f feat(recipes): add recipe module with local-first data, encryption, and card UI
New "Rezepte" module following the established scoped-CSS + theme-token
pattern. Includes Dexie schema (v8), encryption for user-typed fields,
3 German seed recipes, search/filter/tag UI, inline creation form, and
expanded detail view with ingredients checklist and numbered steps.

Also documents the frontend styling inconsistency (13/40 ListViews use
Tailwind instead of scoped CSS) in docs/optimizable/ for future cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:45:41 +02:00
Till JS
65160024f7 feat(workbench): improve fullscreen, scene bar styling, and inline scene creation
- PageShell maximized: fill viewport edge-to-edge (max-width none, z-index 95
  above bottom bar, border none), constrain body/header to 48rem centered, Esc
  exits fullscreen
- SceneAppBar: pill-shaped bar and items, match TagStrip font size (0.9375rem),
  visual group bracket for active scene with separator, tab count on all scenes,
  inline name input for new scenes instead of modal dialog
- SceneRenameDialog: remove icon field, simplify to name-only

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:45:09 +02:00
Till JS
971ad37b4a style(landing): use generic tool categories instead of brand names
Some checks are pending
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
CI / Build mana-media (push) Blocked by required conditions
CI / Build mana-credits (push) Blocked by required conditions
CI / Build mana-web (push) Blocked by required conditions
CI / Build chat-backend (push) Blocked by required conditions
CI / Build chat-web (push) Blocked by required conditions
CI / Build todo-backend (push) Blocked by required conditions
CI / Build todo-web (push) Blocked by required conditions
CI / Build calendar-backend (push) Blocked by required conditions
CI / Build calendar-web (push) Blocked by required conditions
CI / Build clock-web (push) Blocked by required conditions
CI / Build contacts-backend (push) Blocked by required conditions
CI / Build contacts-web (push) Blocked by required conditions
CI / Build presi-web (push) Blocked by required conditions
CI / Build storage-backend (push) Blocked by required conditions
CI / Build storage-web (push) Blocked by required conditions
CI / Build telegram-stats-bot (push) Blocked by required conditions
CI / Build nutriphi-backend (push) Blocked by required conditions
CI / Build nutriphi-web (push) Blocked by required conditions
CI / Build skilltree-web (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build zitare-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
Replaces specific tool names (Todoist, Notion, ChatGPT, etc.) with
generic categories (Aufgaben-App, KI-Assistent, etc.) in the
comparison section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:43:35 +02:00