Phase 1 of the scene-scope empty state plan (docs/plans/scene-scope-
empty-state.md). When the active scene's scope tags filter a module
down to zero results, the ListView now shows a dedicated empty state
with a one-click "Bereich zurücksetzen" button instead of the generic
"Keine Aufgaben"/"Keine Treffer" message. Previously the user couldn't
tell whether the list was empty because of missing data or because of
the scope filter.
- New `ScopeEmptyState.svelte` shared component.
- New `hasActiveSceneScope()` reactive helper on the scene-scope store.
- Wired into todo, notes, calendar, contacts ListViews — the four
modules that currently use `filterBySceneScopeBatch`.
- 4 unit tests for the scope primitives.
Phase 2 (per-module hidden count) and Phase 3 (persistent scope badge)
remain optional follow-ups.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 10 unit tests for the two helpers we hardened this session:
- toScene round-trips the core presentation fields and the two
previously-dropped extras (viewingAsAgentId, scopeTagIds). Guards
against the silent field-loss regression fixed in a1baf1053.
- pickActiveId covers empty lists, surviving current, MRU fallback,
skipping deleted MRU entries, corrupted-JSON MRU payload, and
non-string entries. Locks down the fallback ladder introduced in
4e5c3179f so scenes[0] stays a last resort.
Both helpers are now exported from the .svelte.ts store. The test
file mocks `$app/environment.browser=true` and polyfills localStorage
so it runs without jsdom (the web app doesn't bundle jsdom as a test
dep).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hoisted the rootMargin and threshold literals into PREMOUNT_MARGIN and
INTERSECTION_THRESHOLD constants next to MAX_MOUNTED. Same behavior —
the intent of the three tuning knobs is now visible at a glance and
easy to adjust from one spot if the lazy-mount envelope needs tweaking.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- pickActiveId now consults a per-device MRU list (top 5 recent
scenes, stored in localStorage) when the current scene disappears
(delete, sync pull, tier filter). Previously the fallback was
always scenes[0], which could strand the user on whatever sorted
first after a delete rather than the scene they were just on.
- reorderScenes runs all per-scene order patches inside one Dexie
rw-transaction. A partial failure previously left the scene list
with gapped or duplicated `order` values visible to subscribers;
the transaction makes the reorder all-or-nothing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleRequestRename focused the scene-header h1 after a hard-coded
120 ms setTimeout. On slower hardware the query fired before the
SceneHeader had re-rendered with the new active scene, focusing the
previous scene's h1. Replacing the timeout with `await tick()` flushes
Svelte's pending DOM updates before the query and removes the magic
number.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add EventProvider interface (base.ts) with fetchEvents(url, name, ctx, config)
- Refactor iCal parser and website extractor as provider adapters
- Add Eventbrite provider: API v3 search by location, category mapping,
price info extraction. Requires EVENTBRITE_API_KEY env var.
- Add Meetup provider: GraphQL API search by location, topic→category
mapping, HTML stripping. Requires MEETUP_API_KEY env var.
- Provider registry (getProvider, PROVIDER_TYPES) replaces hardcoded
switch in crawl-scheduler
- Crawl scheduler now joins sources with regions for ProviderContext
(lat/lon/radius/label) — platform providers need this for geo-search
- Source creation accepts 'eventbrite' and 'meetup' types (url optional)
- Both providers gracefully return empty when API keys unconfigured
116 tests (all passing), no regressions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously the intersection-observer cache grew monotonically: once a
card mounted its ListView + Dexie liveQuery, it stayed mounted for the
lifetime of the workbench page. A user who scrolled through 20 apps
kept 20 parallel liveQueries alive.
Now the cache is capped at MAX_MOUNTED=8 with insertion-order LRU
semantics: re-intersecting a mounted card bumps it to MRU, and the
oldest gets evicted when a new mount pushes the set over cap. Set
insertion-order is used for the LRU list so the template's has()
check stays O(1).
The cap is well above typical working-set sizes (3–6 apps) so regular
workbench use never hits the eviction path. Users with large scenes
pay at most one extra liveQuery + chunk re-request when scrolling back
to an evicted card.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add discover_events (auto) and suggest_event (propose) to shared-ai
tool catalog. discover_events reads the discovery feed, suggest_event
creates a proposal to save a discovered event to the user's calendar.
- Add Event-Scout agent template with daily "Events der Woche" mission.
Policy: discover_events=auto, suggest_event=propose, all else denied.
- Add frontend tool implementations in events/tools.ts — discover_events
calls the feed API, suggest_event delegates to discoveryStore.saveEvent.
- Add feedback.ts — computes implicit user profile from save/dismiss
history (category affinity + source quality as 0–2x weight multipliers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Workbench CRUD handlers now emit a localized toast on failure instead
of only logging to the console. Quota, structured-clone or Dexie
transaction failures are now user-visible, so an add/remove/resize
that silently rejected can no longer leave the user guessing at a
frozen UI.
- Added a dev-only onMount that checks for stale Service Workers on
the homepage. vite-plugin-pwa is disabled in dev (see vite.config.ts
`devEnabled: false`), but a surviving SW from a previous `pnpm build
&& pnpm preview` session keeps serving cached HTML — e.g. showing
`/email-verified` at `/`. We detect, warn via toast, and unregister
automatically. Prod builds are unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two backlog items landed in one commit because an earlier amend in a
parallel terminal dropped the initial Phase 3b commit and the BYO-keys
work was blocked on the same wiring.
openai-deep-research (async):
- New research.async_jobs table persists the OpenAI response.id, query,
reservation, and cached result/error.
- POST /v1/research/async reserves credits, submits to the Responses API
with background=true, returns a taskId. Submit failure refunds.
- GET /v1/research/async/:taskId polls upstream, commits the reservation
on completion, refunds on failure, short-circuits for terminal states.
- GET /v1/research/async lists the user's async tasks.
BYO-keys:
- research.provider_configs CRUD at /v1/provider-configs. Keys are masked
(••••last4) on read so the raw secret never re-transits to the browser.
Currently stored plaintext with a TODO for AES-GCM-256 via the shared
KEK — single call site in storage/configs.ts.decryptKey().
- New frontend route /research-lab/keys lets the user paste a key per
provider, toggle enabled, and set daily/monthly credit budgets.
- ListView grew a 🔑 link in the header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Merge the onMount + $effect deep-link handlers into one $effect gated
on `workbenchScenesStore.initialized`. On cold load the effect fires
early (store not ready), bounces, and re-fires once init completes.
Removes the duplicated logic and eliminates the race between the two
paths. onMount now only kicks off initialize().
- `dispose()` now resets `initializedState` and `subscribeRetryCount`
so a navigate-away → back cycle re-runs `initialize()` with a fresh
subscription and a clean retry budget. scenesState is left intact
to avoid an empty-workbench flash while the new liveQuery re-emits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The existing RSS-based path stays the default (shallow, free). `depth=deep`
fans out to two research agents in parallel (Perplexity Sonar first, then
Gemini Grounding if available) via the new mana-research /v1/research/compare
endpoint, merges their answers + citations into a single markdown context
block that the AI can cite from, and attaches the runId so the user can
revisit the comparison in Research Lab later.
AI missions keep calling the tool with no depth arg — they still get
free RSS results. Only explicit depth=deep consumes credits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Branding: research-lab registered in @mana/shared-branding with requiredTier: 'beta' + a custom flask-on-purple icon, so guest/public users are filtered out of the workbench picker.
- Backend: compare routes now return resultId alongside each CompareEntry so the frontend can wire ratings to the eval_results rows in research.*.
- Frontend: click-to-rate stars in CompareColumn (persists via POST /v1/runs/:runId/results/:resultId/rate), recent-run list rows are now buttons that navigate to /research-lab/runs/[id], and the detail route reconstructs CompareEntry shapes from eval_results + reuses CompareColumn for a full read-only view of any past run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- PageCarousel now creates the IntersectionObserver exactly once when
the track mounts and diffs observed wrappers on pages changes,
instead of tearing down and rebuilding the entire IO on every
add/remove. Already-mounted pages no longer re-fire the intersection
callback on each reactive tick.
- SceneAppBar receives stable callback identities. The bar-props effect
previously recreated fresh inline arrows on every reactive pass
(carouselPages / appTitles / DEFAULT_WIDTH change), forcing the bar
to see new props even when only data changed. Hoisted handlers make
the bar's prop diff a pure data comparison.
- `createScene` failures from the bar now surface in the console
instead of silently rejecting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses a "frozen workbench until reload" bug where adding a new page
sometimes stopped updating the UI and no further changes rendered until
the user reloaded.
- Wrap the workbench-scenes liveQuery `next` handler in try/catch so a
single malformed row can't kill the reactive chain. Re-subscribe on
terminal errors (up to 3× with backoff) so transient Dexie failures
(e.g. DatabaseClosed during a schema upgrade in another tab) recover
automatically instead of requiring reload.
- Rewrite `patchActiveScene` as a Dexie rw-transaction that reads the
row fresh and skips writes that produce the same array reference, so
two rapid writes (add A, then add B before the liveQuery echoes the
first change) can no longer clobber each other with a stale snapshot.
- Restore `viewingAsAgentId` and `scopeTagIds` in `toScene` — they were
silently dropped, breaking the agent-avatar pill in SceneAppBar and
the auto-inferred scope in SceneHeader.
- Surface Dexie write failures from the workbench CRUD handlers. Previous
fire-and-forget calls swallowed quota / structured-clone rejections,
leaving the picker closed but no new page visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Voice-based interview for the profile module — users choose between text,
voice (question read aloud + mic for answer), or conversation mode (fully
automatic flow with auto-save).
Interview audio:
- 92 pre-rendered MP3 files (23 questions × 4 voices) via Edge TTS
- Voices: Seraphina (DE-f), Florian (DE-m), Leni (CH-f), Jan (CH-m)
- User picks voice via dropdown, persisted in localStorage
- Web Speech API fallback for missing audio files
Profile UI:
- Interview hero block on overview with 3 start modes (text/voice/conversation)
- Voice/conversation toggle + voice picker in interview view
- Mic button on text/textarea/tags inputs for per-question voice input
- Conversation mode: auto-save + auto-advance after STT transcription
- Recording/transcribing/speaking state indicators
mana-tts service:
- New Orpheus TTS backend (German finetune, SNAC codec)
- New Zonos TTS backend (Zyphra, 200k hours, emotion control)
- Endpoints: POST /synthesize/orpheus, POST /synthesize/zonos
- espeak-ng installed on GPU server for Zonos phonemizer
- Compare script for side-by-side voice quality testing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New SvelteKit module that consumes mana-research directly on port 3068
(JWT auth + CORS are already wired). Three modes — search, extract, agent
— with a shared ephemeral session store (sessionStorage, no Dexie) so
tab-refreshes survive but nothing leaks into the local-first data layer.
Components:
- ProviderPicker — chip multi-select per category. Shows free/ready/
needs-key status + per-call pricing from the providers catalog.
- CompareColumn — one provider's result: search hits (ordered list +
snippets + scores), extract (title + excerpt + body preview + stats),
or agent answer (text + citations list + token usage).
- ListView — mode toggle, query/URL input, providers row with cost
estimate, results grid, recent-runs history list.
Data flow: store calls api.ts → fetch(`${getManaResearchUrl()}/...`) with
Bearer JWT. Cost, latency, cache-hit, and billing-mode come back in each
result's meta and are rendered inline per column.
The module registers itself as "Research Lab" (Flask icon, purple brand
color). No collection, no IndexedDB table, no sync — this is purely a
live query interface over the comparison backend.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When clicking "KI-Einstellungen öffnen" from the companion chat while
settings is already open on a different tab, the settings panel now
correctly switches to the right tab and scrolls to the anchor.
The workbench deep-link $effect dispatches a custom
workbench:navigate-anchor event after opening/focusing the target panel.
The settings ListView listens for both this event and native hashchange,
then calls navigateToHash() to switch activeCategory and scroll.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move getUserMessage() to the base LlmError class so every error type
gets a German explanation with a clickable settings deep-link:
- TierTooLowError: "Kein KI-Modell aktiviert. Mindestens X benötigt."
- ProviderBlockedError: "… hat die Anfrage blockiert (Inhaltsfilter)."
- BackendUnreachableError: "… ist nicht erreichbar."
- EdgeLoadFailedError: "Browser-Modell konnte nicht geladen werden."
- Generic fallback: also includes the settings link now
The companion engine now catches LlmError (base class) instead of
only NoTierAvailableError, covering all failure modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The onMount handler only fires once, so clicking a /?app=settings link
from the companion chat (or any in-app link) while already on the
workbench page did nothing. Add a reactive $effect that watches
$page.url.searchParams for 'app' changes and opens/scrolls to the
target panel on every navigation, not just on initial mount.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On mobile, long-press was selecting the row text instead of firing
`contextmenu`. Adds `user-select: none` + `-webkit-touch-callout: none`
on collapsed rows across 12 ListViews (notes, todo, dreams, journal,
firsts, contacts, places, mail, moodlit, chat, calendar) and re-enables
`user-select: text` on the inline-editor variants so the textarea
stays selectable while editing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lightning was inherited from the ai-rituals era (AI-feature vibe) and
didn't match what rituals actually express — recurring, cyclical
practice. ArrowClockwise reads as "loop / recurrence / do this
regularly", which fits both utility and ceremony flavours.
Lightning is kept for `automations` (still the right match there).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Track skip reasons per tier in the orchestrator (no-consent,
no-backend, not-available, not-ready, runtime-error) and expose
them via NoTierAvailableError.getUserMessage() with actionable
German text pointing the user to the right settings page.
Before: "No tier could run task 'companion.chat' (attempted: cloud)"
After: "Cloud (Gemini): Cloud-Einwilligung fehlt. Aktiviere sie
unter Einstellungen → KI."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clicking the ? icon in a workbench panel header toggles an inline help
view that replaces the ListView content. Shows module description,
feature list, and usage tips. Help content for ~45 modules defined in
a central help-content.ts registry, auto-attached via registerApp().
- AppDescriptor gains optional `help` field (description, features, tips)
- PageShell gains onHelp/helpOpen props with highlighted active state
- AppPage renders help inline in the page-body, not as a popover
- help-content.ts: per-module descriptions, features, and tips (German)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The module was named "ai-rituals" because every step was a tool call
(log drink, show tasks, create task from text input). That framing
excluded a whole class of rituals that *don't* capture data —
personal ceremonies that just want to hold the user's attention for a
minute: the morning coffee, the Sunday reset, the before-bed shutdown.
Changes:
- Renamed the module: apps/web/src/lib/modules/ai-rituals → rituals
- App id 'ai-rituals' → 'rituals' in app-registry/apps.ts
- Moved the category from 'ai' to 'life' in app-registry/categories.ts
(personal practice, not an AI subsystem)
- Added RitualCategory = 'utility' | 'ceremony' | 'mixed' on both
LocalRitual and RitualTemplate. Defaults to 'utility' on read so
existing data from before this change stays accessible.
- 3 new step types in the RitualStepConfig union:
- presence : markdown body + optional countdown, no tool call.
Use case: "Fünf Minuten still trinken."
- breath : guided breathing with a circle that expands/contracts
on inhale/exhale. Presets: box (4-4-4-4), 4-7-8,
coherent (5-0-5-0), plus custom timings.
- media : image + caption (mantra / photo / quote) with
optional linger timer.
- RitualRunner extended: timer teardown on step change, breath state
machine with phase-driven scaling animation, stop/early-exit for
both.
- 3 ceremony templates seeded:
- Morgenkaffee : Wasser → Aufbrühen → 3 tiefe Atemzüge →
5 Min still trinken
- Sonntag-Reset : Ankommen → Streaks → Was nehme ich mit? →
Nächste Woche → Handy weg (mixed)
- Vor dem Schlaf : Bildschirme aus → 4-7-8 Atmung → Journal-
Eintrag → Loslassen
- ListView: category filter chips (Alle / Utility / Zeremoniell),
templates grouped by category in the picker, category pill on each
ritual row (hidden for the default 'utility').
- docs/MODULE_REGISTRY.md: moved from AI-System (now 8) to Gesundheit
& Wellness (now 11).
No schema migration — the new `category` field is optional on
LocalRitual and falls back to 'utility' when undefined, so Dexie
doesn't need a version bump. Existing rituals (none in production)
keep working.
Heads-up for scenes: anyone who had 'ai-rituals' pinned to a workbench
scene will need to re-add it as 'rituals'. Acceptable given
pre-launch state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- LocationPicker: horizontal scroll instead of wrapping chips, action
buttons now show text labels (Standort, Hinzufügen, Verwalten)
- CurrentConditions: temperature is now the dominant hero element (4rem),
deduplicate "Berlin, Berlin" → "Berlin", added last-updated timestamp
- Fix "Uebersicht" → "Übersicht" umlaut in tab labels
- Better visual hierarchy: temperature dominates, details recede
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rework LocationPicker with three actions:
- "+" button opens search that saves results as persistent locations
- Gear button opens manage panel to set default (★) or remove cities
- Saved locations appear as chips; default location auto-loads on open
Search results show "Speichern" label and auto-save on click. Already
saved locations show "Gespeichert" instead. Default location marked
with a blue dot in chips and a star in the manage panel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The EntryForm labels were using --color-text-muted at 0.85rem, which on
a dark theme made them almost invisible against the purple-tinted
inline-create container. Same issue for the <legend>s.
Changes:
- labels + legend use inherit (full foreground color) at 0.88rem with
font-weight 500 so they pop against the background
- inputs get a more opaque background (--color-background fallback)
and slightly darker borders so fields are distinguishable as
interactive targets, not just text runs
- added explicit ::placeholder color so the muted hint text is
readable but not competing with the actual value
- h2 bumped to font-weight 600
- details-section summary gets color: inherit + slightly bigger font
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the compact ListView with the full weather view including
LocationPicker, tab switcher (Uebersicht / Quellen-Vergleich),
all weather components, and the multi-model comparison — same
functionality as the /wetter page route.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New control order: search (full-width, flex) + "+ Neu" button → KindTabs
→ status chips + favourites toggle. Search is the primary entry point
when the user knows what they're looking for; tabs + filters refine the
grid when browsing. The create button stays aligned with search because
that's where the user's eye already is when they decide "nope, not here,
I'll add it".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New module for managing wishes/gift ideas with lists, price targets,
product URLs, price history, and AI tools. Includes ListView with
filter tabs, inline list management, search, and DetailView with
notes and price history. Encrypted at rest (title, description, URLs,
notes). Registered in database v24, module-registry, crypto registry,
seed registry, tool init, and DnD type system.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The workbench already renders the app name ("Bibliothek") as the scene
tile header, so the ListView's own <h1>Bibliothek</h1> + subtitle was
a duplicate header stack. Removed.
The "+ Neu" flow is no longer a full-screen modal overlay. The button
now sits inline at the end of the KindTabs row and toggles between
"+ Neu" / "× Schließen". When active, the EntryForm expands in-place
right under the controls, above the grid — no z-index dialog, no
backdrop, no ESC-to-close plumbing. Fits the workbench tile/window
aesthetic (content stays within the tile boundary, no overlays
escaping the window).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New "Quellen-Vergleich" tab on the weather page that fetches the same
location from 5 weather models in parallel (DWD ICON-D2, ICON-EU,
ECMWF IFS, NOAA GFS, Open-Meteo Best Match) and displays them stacked
for easy comparison of temperature, precipitation, and daily forecasts.
Adds /api/v1/wetter/compare endpoint and SourceComparison.svelte.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Library was listed in MANA_APPS (for the tier/branding registry) and had
a /library route, but it wasn't yet registered in the workbench app
registry — so users couldn't add it as a tile to their Workbench scene.
Adds:
- registerApp({ id: 'library', icon: Stack, … }) in app-registry/apps.ts
with a "+ Neuer Eintrag" context-menu action (emits the standard
mana:quick-action CustomEvent, ready to be listened to in ListView
once we want an in-workbench create flow).
- library → 'creative' entry in app-registry/categories.ts so it
appears in the Kreativ section of the app picker.
Only the list view is registered; the detail view still uses the
route-based pattern (/library/entry/[id]) and the route-level +page.svelte
wrapper. Migrating DetailView to the workbench ViewProps shape (params +
navigate + goBack) is deferred — the route works fine for now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Weather data is public — no user-specific data involved. Move the
wetter route registration above authMiddleware() so requests don't
require a JWT token. Rate limiting still applies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ListView.svelte (compact weather card for workbench panels),
register via registerApp() in apps.ts with CloudSun icon, and
assign to 'life' category so it appears in the Leben section
of the AppPagePicker.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ProgressControls.svelte renders typ-spezifische Fortschritts-UI:
- book → range slider + page input + "Fertig"-Button; auto-completes
the entry (status=completed, times++) when current == total
- series → collapsible season/episode grid; each episode is a toggleable
pill that writes into details.watched with a watchedAt stamp;
auto-completes once watched.length == totalEpisodes
- comic → ±1 issue bumper; auto-completes on issueCount reach
- movie → atomic, no progress widget
libraryEntriesStore.restartEntry: flips a completed entry back to active,
stamps startedAt=today, clears completedAt. Preserves the per-episode
watched list so users keep the history of the previous run-through; they
can reset individual episodes via the tracker if they want a fresh pass.
DetailView embeds <ProgressControls {entry}> below the status row and
renders a "↻ Nochmal lesen/sehen" button whenever status === 'completed'.
docs/plans/library-module.md: M1 + M2 + M3 marked DONE with commit IDs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
M1 skeleton for a new media-consumption module. Single-table design with
a `kind: 'book' | 'movie' | 'series' | 'comic'` discriminator and a
discriminated `details` union for kind-specific fields (pages / runtime /
episode tracker / issue count). Shared kern: status, rating, review,
favourites, times counter, completedAt — which enables cross-media
queries like a year-in-review.
Dexie migration v26 was already registered in module-registry.ts /
database.ts via the preceding wetter commit (62aac6dfd); this commit
adds the actual module code, encryption registry entry, app-icon,
MANA_APPS entry, Kreativität & Medien category row, and the module
plan at docs/plans/library-module.md.
Encrypted fields (via ENCRYPTION_REGISTRY):
title, originalTitle, creators, review, tags
Plaintext (intentional):
kind, status, year, rating, genres, completedAt, isFavorite, times,
externalIds, details — all needed for the tab filter, status chips,
Jahresrückblick range-scan, and progress UIs.
Product decisions (frozen in the plan):
- audiobooks = kind='book' with details.format='audio'
- manga = kind='comic' (no sub-discriminator)
- metadata lookup (M7) lands as an endpoint in apps/api, not a
standalone service
Guest seed ships one example per kind (Dune, Arrival, Severance, Saga)
so first-run users immediately see what the module does.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New module providing weather data for the DACH region via three sources:
- Open-Meteo (DWD ICON-D2 model) for current conditions and 7-day forecast
- DWD warnings endpoint for severe weather alerts
- Rainbow.ai / Open-Meteo fallback for minute-level rain nowcast
Includes API proxy with in-memory caching, Svelte 5 UI with location
picker, hourly/daily forecast, alert cards, and precipitation bar chart.
Two AI tools (get_weather, get_rain_forecast) enable the companion to
answer weather questions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Six P2 items from the AI Workbench audit:
#7 Prompt ↔ loop budget sync:
System prompt now says "1 bis 5 Schritte pro Planungsrunde, bis zu 5
Planungsrunden" — matches MAX_REASONING_LOOP_ITERATIONS. Cross-ref
comment added to runner.ts.
#9 SceneHeader: useAgents() → useAgent(id):
Only loads the single bound agent instead of the full agent list.
Eliminates unnecessary Dexie churn on every scene header render.
#10 Unified scope filter:
New scope-filter.ts with filterByScopeTagMap() (batch, sync) and
filterByScopeAsync() (per-record). Both scope-context.ts (AI) and
scene-scope.svelte.ts (UI) now import from the shared module —
zero duplicated filter logic.
#11 Research dedup:
Research input ID changed from `news-research-${Date.now()}` to
`news-research-${mission.id}` — re-runs overwrite instead of
appending duplicates.
#12 Kontext injection policy clarified:
loadAgentKontextAsResolvedInput no longer falls back to the global
singleton. Comment + code aligned: kontext injection is explicit
(via input picker), not auto. Dead loadKontextAsResolvedInput
kept for potential future opt-in auto-inject feature.
Audit doc updated with all items marked DONE.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Four P1 fixes from the AI Workbench audit:
#3 N+1 junction queries → batch lookups:
- TagLinkOps gains getTagIdsForMany(entityIds) — single
where(field).anyOf(ids).toArray() instead of N calls.
- filterBySceneScopeBatch() uses a pre-fetched Map<id, tagId[]>.
- All 4 module queries (notes, todo, contacts, calendar) migrated.
- 500 notes now = 2 Dexie queries (records + junctions) instead of 501.
#4 Vault-locked detection in readLocalNote:
- Catches VaultLockedError from decryptRecords.
- Throws descriptive "Vault ist gesperrt" instead of returning null.
- Tools surface it as a clear error to the planner ("bitte Vault
entsperren") instead of "Notiz nicht gefunden".
#5 Debug log hardening:
- Resolved-input content truncated to 500 chars before storage.
- Time-based purge: entries older than 7 days auto-deleted.
- Reduces privacy exposure if device is stolen/profile synced.
#6 Timeout 90s → 180s:
- 5 LLM calls on slow models (Ollama/GPU) regularly hit 90s.
- 180s gives comfortable headroom for the reasoning loop.
Audit doc updated with status markers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two critical fixes from the AI Workbench audit:
1. Tool exceptions in the reasoning loop:
stage(ps, aiActor) is now wrapped in try-catch. If a tool throws
(Dexie error, vault locked, network timeout), the step is recorded
as failed with the error message in the summary, and the loop
continues with the next step. Previously, one broken tool crashed
the entire iteration.
2. Concurrent mission scope interleaving:
runMission() now serializes through a promise-based mutex. Two
concurrent calls (double-click, cadence overlap) queue instead of
interleaving — prevents the ambient withAgentScope() from stomping
a running mission's scope with a different agent's tags.
scope-context.ts also gains filterByScopeExplicit(records, scopeTagIds,
getTagIds) — the explicit, race-safe variant that doesn't read
ambient state. Callers that already have the scope should prefer it.
Also adds docs/optimizable/ai-workbench-audit-2026-04-16.md with the
full audit (P0–P2, 12 items).
Runner tests: 8/8.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three toast.success('\1') calls broke Vite's strict-mode parser on
startup. Replaced with proper German messages: purchase success,
cancel confirmation, reactivation confirmation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>