feat(lasts): M1-M7 — module ship + Meilensteine-Aggregator

Mirror sibling to firsts: das *letzte* Mal, das du etwas getan hast —
markiert oder rückwirkend erkannt. Plan: docs/plans/lasts-module.md.

M1 Skelett — Dexie v51 lasts-Tabelle, Encryption-Registry, Per-Space-
Welcome-Seed, Empty-State ListView. Kategorien aus firsts/types.ts
nach \$lib/data/milestones/categories.ts extrahiert (Re-Exports halten
firsts-API stabil).

M2 CRUD + DetailView — StatusTabs (Vermutet/Bestätigt/Aufgehoben),
Quick-Add mit Mode-Toggle, always-editable DetailView mit Lifecycle-
Buttons (Bestätigen, Aufheben mit Inline-Note), 44 i18n-Keys × 5 Locales.

M3 Inbox + Inferenz — Dexie v52 lastsCooldown (12-Monate-Cooldown,
deterministische ID), Source-Registry-Pattern in inference/, places-
Source mit Heuristik visitCount>=5 Span>=180d Silence>=365d. InboxView
mit Akzeptieren/Verwerfen + manueller Scan. contacts/habits → M3.b
sobald jeweilige Frequenz-Felder existieren.

M4 AI-Tools — 5 Tools im AI_TOOL_CATALOG (create_last, confirm_last,
reclaim_last, list_lasts, suggest_lasts), Webapp-Executor mit Vault-
Locked-Handling. Server-Drift-Test 4/4, Schema-Test 6/6.

M5 Reminders + Settings — Pivot zu In-App-DueBanner statt OS-Push (kein
PWA-Push-System im Repo). Pure date-math (12 Vitest cases), Settings-
Store mit 4 Toggles, DueBanner mit max-N rendering, Test-Banner-Knopf.

M6 Visibility + Unlisted-Sharing — VisibilityPicker + SharedLinkControls
in DetailView, buildLastBlob mit reflective-core whitelist (reclaimed
Lasts gehärtet ausgeblockt), SharedLastView public-render, Share-
Dispatcher kennt 'lasts'.

M7 Meilensteine-Aggregator — Cross-modul firsts vereinigt mit lasts
Timeline + Year-Recap. Pure aggregator (mergeMilestones,
buildMilestonesRecap), 12 Vitest cases. /milestones und
/milestones/recap/[year] Routes, Cross-Link in lasts/ListView.

Validation: 0 errors / 0 warnings (svelte-check 7645 files), 24/24
tests, i18n-parity 39x5 aligned (+2 namespaces), i18n-keys baseline-
equal, crypto 211 tables.

LOCAL TIER PATCH: lasts ist 'guest' für Testing — vor Release auf
'beta' setzen (packages/shared-branding/src/mana-apps.ts).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-26 21:40:29 +02:00
parent ad5e04a554
commit bf3bca268a
53 changed files with 6572 additions and 40 deletions

View file

@ -2299,6 +2299,186 @@ export const AI_TOOL_CATALOG: readonly ToolSchema[] = [
},
],
},
// ── Lasts (mirror sibling to firsts) ────────────────────────
{
name: 'create_last',
module: 'lasts',
description:
'Erstellt einen neuen "Last" — ein letztes Mal, das markiert oder vermutet werden soll. Status standardmaessig "suspected"; "confirmed" nur setzen wenn der User sicher ist.',
defaultPolicy: 'propose',
parameters: [
{
name: 'title',
type: 'string',
description: 'Was zum letzten Mal passiert ist (z.B. "Letzter Tag im alten Job")',
required: true,
},
{
name: 'category',
type: 'string',
description: 'Kategorie',
required: false,
enum: [
'culinary',
'adventure',
'travel',
'people',
'career',
'creative',
'nature',
'culture',
'health',
'tech',
'other',
],
},
{
name: 'status',
type: 'string',
description: 'Lifecycle-Status',
required: false,
enum: ['suspected', 'confirmed'],
},
{
name: 'date',
type: 'string',
description: 'Datum des letzten Mals (YYYY-MM-DD), falls bekannt',
required: false,
},
{
name: 'confidence',
type: 'string',
description: 'Sicherheit, dass es das letzte Mal war',
required: false,
enum: ['probably', 'likely', 'certain'],
},
{
name: 'meaning',
type: 'string',
description: 'Was es bedeutet hat (optional)',
required: false,
},
{
name: 'note',
type: 'string',
description: 'Freie Notiz (optional)',
required: false,
},
],
},
{
name: 'confirm_last',
module: 'lasts',
description:
'Bewegt einen Last von "suspected" auf "confirmed" und ergaenzt Reflexionsfelder. Setzt Datum auf heute, falls keines uebergeben wird.',
defaultPolicy: 'propose',
parameters: [
{ name: 'lastId', type: 'string', description: 'ID des Lasts', required: true },
{
name: 'date',
type: 'string',
description: 'Datum des letzten Mals (YYYY-MM-DD)',
required: false,
},
{
name: 'meaning',
type: 'string',
description: 'Was es bedeutet hat',
required: false,
},
{
name: 'whatIKnewThen',
type: 'string',
description: 'Was du damals nicht wusstest',
required: false,
},
{
name: 'whatIKnowNow',
type: 'string',
description: 'Was du heute siehst',
required: false,
},
{
name: 'tenderness',
type: 'number',
description: 'Wie sehr es dich heute beruehrt (1-5)',
required: false,
},
{
name: 'wouldReclaim',
type: 'string',
description: 'Wuerdest du es zurueckholen?',
required: false,
enum: ['no', 'maybe', 'yes'],
},
],
},
{
name: 'reclaim_last',
module: 'lasts',
description:
'Markiert einen Last als "aufgehoben" — es ist wieder passiert. Optionaler Notiz-Text beschreibt, was zurueckgekommen ist.',
defaultPolicy: 'propose',
parameters: [
{ name: 'lastId', type: 'string', description: 'ID des Lasts', required: true },
{
name: 'reclaimedNote',
type: 'string',
description: 'Was ist wieder passiert (optional)',
required: false,
},
],
},
{
name: 'list_lasts',
module: 'lasts',
description:
'Listet Lasts (id, title, status, category, date). Optional nach Status oder Kategorie filterbar.',
defaultPolicy: 'auto',
parameters: [
{
name: 'status',
type: 'string',
description: 'Nur einen Status zeigen',
required: false,
enum: ['suspected', 'confirmed', 'reclaimed'],
},
{
name: 'category',
type: 'string',
description: 'Nur eine Kategorie zeigen',
required: false,
enum: [
'culinary',
'adventure',
'travel',
'people',
'career',
'creative',
'nature',
'culture',
'health',
'tech',
'other',
],
},
{
name: 'limit',
type: 'number',
description: 'Maximale Anzahl (Standard 30)',
required: false,
},
],
},
{
name: 'suggest_lasts',
module: 'lasts',
description:
'Laesst die Inferenz-Engine ueber places/habits/contacts scannen und generiert "suspected"-Lasts mit inferredFrom-Provenance fuer Eintraege, die Frequenz-Drops zeigen. Dedupliziert gegen existierende Lasts und die Cooldown-Liste. Schreibt direkt in die Inbox — kein Proposal-Workflow noetig, weil die Eintraege als suspected landen und der User sie dort akzeptieren oder verwerfen kann.',
defaultPolicy: 'auto',
parameters: [],
},
];
// ═══════════════════════════════════════════════════════════════