Kansho/docs/architecture/technical/mvp_implementation.md
2026-08-26 10:42:20 +02:00

32 KiB
Raw Blame History

title status date product_family document_role parent_document
Kanshō Technische Umsetzung des MVP-Journal-Slices Arbeitsstand 2026-08-25 Jinkendo Technical Chapter / MVP Implementation How-To / Module and Runtime Map technische_zielarchitektur.md

Kanshō Technische Umsetzung des MVP-Journal-Slices

Kanonisches Home dafür, wie der erste vertikale Slice gebaut ist: Laufzeit, Module, Daten, API, Pfade.

Nicht dieses Dokument:

  • Fachlicher Lieferumfang → ../functional/mvp.md
  • Foundation-Regeln → ../functional/implementation_foundation.md
  • Fit-Gap (Spec, Abweichungen, offene Punkte gegen Slice und Gesamtziel) → ../functional/mvp_stand_und_abgleich.md

Kapiteldetails (Auth, Gateway-Verfahren, Shell-Muster) bleiben in den jeweiligen technischen Homes. Hier die zusammenhängende Umsetzung.

Stand: 2026-08-25, Code unter frontend/ und backend/. Architecture-Correction-Pass am selben Tag (relationale Source-Refs, Selection-Specs, begrenztes Space-Source-Retrieval, Detect-Env, Response Validation, Konsolidierungssignale, /dialog als Admin-Harness).


1. Laufzeit

Lokal, eine Instanz, ein Profil-Nutzer nach Setup:

Prozess Stack Port Einstieg
Frontend React 18, Vite, React Router 6, PWA-Plugin 5188 (strictPort) frontend/npm run dev
Backend FastAPI, Python 3.12, Uvicorn 8018 backend/
Persistenz SQLite Datei backend/data/kansho.sqlite
Medien lokale Dateien backend/data/media/<profile_id>/
Secrets Env backend/.env (Keys nie in der DB)

Proxy: Vite leitet /api an 8018. CORS erlaubt localhost:5188.

Abweichung vom technischen Zielrahmen: product_frame_and_stack.md nennt PostgreSQL. Der Slice läuft auf SQLite. Lokal zulässig; Prod-Pfad offen. Siehe Fit-Gap.

Health: GET /api/health.


2. Schichten und Invariante

PWA (React)
  → /api  Session-Cookie
FastAPI-Router (kein Prompt-Bau im Frontend)
  → journal_policy / stores
  → context_builder + retrieval   (intern, Klartext, nie Egress)
  → privacy_gateway               (einziger generativer Egress)
       Policy → Maskierung → Detect (Testphase) → Generate-Call → Validierung → Demask
  → SQLite + media/

Persönlicher Kontext geht nicht direkt an OpenRouter. Router rufen kein LLM. Fail Closed: kein unsicherer Generate-Fallback.

Zwei Provider-Rollen (provider_settings): detect (Maskierung) und generate (Dialogzug und Journalentwurf). URL/Modell/Flags in der DB, Key in .env. Admin: Schnittstellen.


3. Backend-Module

Einstieg: backend/main.py (Router: auth, users, dialogue, journal, prompts, placeholders, subscription, admin).

Modul Verantwortung
routers/journal.py Produkt-API /api/journal
routers/dialogue.py Low-Level-Source /api/dialogue; derselbe Turn-Pfad
dialogue_turn.py Ein generativer Call, JSON {operation, impulse}, lokale Register-/Wächter, optional Repair
journal_generate.py Expliziter Draft: Stufe 1 Rekonstruktion, Stufe 2 Narration
journal_reconstruct.py Lokale Validierung der Stufe-1-JSON
prompt_budget.py / model_catalog.py Kontextfenster, Output-Reserve, konservative Tokenschätzung
journal_store.py Days, Drafts, Entries, Versionen, Scratch
journal_policy.py Explizites Generate, Origins, Konsolidierungsangebot aus lokalen Signalen
journal_body.py / journal_shape.py Titel, Markdown-Body, lokale Nachformung
context_builder.py Interner Prompt-Kontext über Selection-Specs, keine Recency-API
retrieval.py Austauschbare Specs: day_messages, space_entries, space_recent_sources, writing_profile. Recency/SQL-Kappen sind Implementierung.
conversation_signals.py Lokale operative Metadaten (narrative_mode, reflection_depth, current_focus), kein Extra-LLM
privacy_gateway.py Egress; Response Validation vor Demask
entity_detect.py / pronoun_bind.py / identity_store.py Detection, Dialog-Pronomen, Mapping (Klasse A)
writing_profile_store.py Lokaler Journal-Stilbrief, Facets, Governance, kein Extra-LLM, keine Dialogsteuerung
profile_review.py Lokale Evidence-/Drift-Erkennung, Review-Pakete, API- und Paste-Kanal; kein Call nach jedem Dialog
profile_analysis.py Gemeinsamer ProfileAnalysisPackage-/Result-Vertrag, Evidence-Auswahl, Copy/Paste-Prompt
writing_profile_infer.py Lokale Heuristik für Facets, kein LLM
media_store.py Upload, Datei, Token im Body
providers.py / provider_settings.py Provider-Rollen
db.py + schema.sql SQLite, Migrationen

Prompts in der DB, nicht im Anwendungscode: mvp.dialogue_turn, mvp.journal_reconstruct, mvp.journal_generate, mvp.entity_detect, mvp.profile_review. Seed: backend/config/prompts.seed.json. Unangetastete Templates aktualisiert init_db.


4. Daten (Ist, nicht Ziel-PostgreSQL)

Schema: backend/schema.sql. Isolation über profile_id.

Rahmen / Layer 0: profiles, Auth-sessions, usage_sessions, conversations, messages, Thread-/Space-Hüllen, derived_records (ungenutzt für Journal-Writes), identity_mappings, Prompt-/Feature-/Provider-Tabellen.

Journal-Slice:

Tabelle Rolle
spaces Sichtbarer Reflection Space (visibility='user')
journal_days (space_id, calendar_date); scratch_json lokal
journal_drafts Derived; neue Generierung historisiert den Draft
journal_entries Current über current_version_id; Soft-Delete deleted_at
journal_entry_versions History; Restore hängt eine Version an
journal_draft_source_refs / journal_entry_version_source_refs Relationale Provenance (Conversation/Message-IDs). JSON-Listen sind Altbestand und werden beim Start migriert.
media_assets Datei + Kind; Referenz nur im Body ![…](kansho-media:<id>)
writing_profiles / writing_profile_sources Current Brief
writing_profile_evidence / writing_profile_reviews / writing_profile_versions Review-Pipeline: lokale Evidenz, Paket/Ergebnis, Versionen

Assignment: conversations.space_id / journal_day_id sind Felder, nicht Identität. space_id ohne journal_day_id ist zulässig (Space ≠ Journal-Container). conversations.space_id ohne FK. Kalendertag ≠ messages.created. Operative Dialogsignale liegen als Felder auf conversations, nicht als Writing Profile.

Details: memory_storage_and_offline.md §6.16.2.


5. API (Produkt)

Prefix /api/journal, Session-Auth, Router backend/routers/journal.py. Vollständige Liste: backend_and_api.md §5.1.

Kernpfade:

  1. Dialogzug POST /days/{id}/conversations/{cid}/turn und POST /api/dialogue/conversations/{id}/turn
    Context Builder → Gateway (purpose=dialogue_turn) → ein Generate-Call → Impuls speichern. Pronomenbindung nur hier in user:-Zeilen.

  2. Journalentwurf POST /days/{id}/generate
    Nur explizit. Optional conversation_ids. Ohne Auswahl kein stilles Mergen. Zwei Gateway-Zwecke nacheinander: journal_reconstruct, dann journal_generate. Pronomen ungebunden. Entry-current_version_id bleibt unangetastet. UI öffnet ?draft=1.

  3. Speichern POST /entries
    Mit entry_id + origin=accepted_draft: neue Version desselben Entry. Sonst neuer Entry oder user_edit.

  4. Restore POST /entries/{id}/restore → neue Version, Origin restore.

  5. Scratch PATCH /days/{id}/scratch → nicht im Context Builder, Test: Token nicht im Egress.

  6. Media Upload/GET/DELETE unter dem Entry.

  7. Profile Review GET/POST /writing-profile/review… → lokale Evidenz, explizite API- oder Paste-Review, kein Call am Dialogzug.

include_existing existiert in der API und in der UI; Default ist aus. Die Nutzerfassung bleibt unangetastet. Bei gesetztem Flag gilt die gespeicherte Entry-Fassung; ein liegengebliebener Generate-Draft wird nur verwendet, wenn es noch keinen Entry gibt.

Fehlerkörper oft {detail: {code, message}}, Rahmen will {detail: string} — Abweichung, Fit-Gap.


6. Frontend-Umsetzung

Shell: frontend/src/App.jsx, Nav config/appNav.js (Start / Journal / Einstellungen; Admin extra). /dialog ist nicht in der Produkt-Nav. Breakpoint 1024px: Bottom-Nav vs Sidebar. Details: frontend_pwa_shell.md §6.1.

Route Seite Technik
/ HomePage.jsx Kontinuität: Fortsetzen des letzten aktiven Journal Day oder Space-Einstieg
/dialog DialoguePage.jsx Admin-only Dev/Test-Harness, nicht in der Produkt-Nav. Unzugeordnete Conversations.
/journal/journal/:spaceId/:dayId Spaces, Space, JournalDayPage.jsx Chat, Generate, DayScratch.jsx
…/entry JournalEditorPage.jsx TipTap, Markdown in journal/document.js, Dirty-Flag context/UnsavedChanges.jsx
…/source/:conversationId JournalSourcePage.jsx Quelldialog
/settings SettingsPage.jsx Writing/Interaction, Review-Paket (API oder Copy/Paste)

Editor speichert Markdown, nicht HTML. Medien-Token bleiben lokal. Dirty: In-App-Links, Abmelden, beforeunload; Browser-Zurück nicht hart blockiert.


7. Zwei fachliche Calls, drei technische Schichten

Schicht Rolle Call
Maskierung detect Muster immer. Externes Klartext-Detect nur Development/Test (KANSHO_ENV). Produktiv: Muster oder lokale Detect-URL.
Dialogzug generate Operation + Impuls
Journalentwurf generate Explizit, getrennt

Explizite Writing-Profile-Review ist ein weiterer Generate-Zweck (profile_review), nicht der Dialogzug und nicht nach jedem Turn.

Additiv 2026-08-25: Der Journalentwurf ist fachlich zwei Stufen (Rekonstruktion, dann Narration), also zwei Generate-Calls hinter derselben expliziten Generate-Aktion. Dialogzug bleibt ein Call. Details: §16.

Interner Context-Builder (Klartext, lokal): fordert Selection-Specs an (day_messages, space_entries, space_recent_sources, writing_profile). Recency-Kappen (Day 80 Messages für den Dialogzug; Journal-Generate nutzt Tokenbudget statt der 80er-Kappe, Overflow bricht ab; 5 Space-Entries × 400 Zeichen; 3 Space-Source-Conversations × 400 Zeichen User-Ausschnitt) liegen in retrieval.py, nicht in der Context-Builder-API. Dialogzug: aktuelle Conversation primär, plus begrenzte frühere Original-Conversations desselben Space (ohne denselben Journal Day voll zu laden). Generate-Stufe 1: Day-Messages mit Budget. Generate-Stufe 2: Writing Profile plus validierte Rekonstruktion, ohne den vollen Dialog. WritingProfile steuert Journaltext, nicht den Dialogue Kernel; der Turn hat einen leeren interaction_hint-Slot. Retrieval austauschbar, keine Embeddings.

Gateway-Verfahren und offener Security Layer: privacy_gateway.md §9.19.2.


8. Tests (technisch)

Suite Ort
Rahmen, Auth backend/tests/test_frame.py
Journal-Slice, Scratch-Egress, Versionsschutz backend/tests/test_mvp_journal.py
Architecture Correction (Provenance, Space-Source, Detect-Env, Kernel-Grenzen) backend/tests/test_architecture_correction.py
Detect, Pronomen Dialog vs Journal, Response Validation backend/tests/test_privacy_detect.py
Shape / Namen backend/tests/test_journal_shape.py
Writing Profile backend/tests/test_writing_profile.py
Profile Governance backend/tests/test_profile_governance.py
Profile Review Pipeline backend/tests/test_profile_review.py
Journal-Budget / zweistufige Generierung backend/tests/test_journal_budget.py
Lokales Backup/Restore backend/tests/test_local_backup.py
Papierkorb / Purge backend/tests/test_journal_trash.py
Erster Journal-Impuls backend/tests/test_journal_opening.py
Writing-Profile-Hülle / Initial Build backend/tests/test_profile_review.py, backend/tests/test_profile_governance.py
Markdown-Runde frontend/src/journal/document.test.js
Lokaler Abnahmelauf scripts/test-mvp.ps1

Tests beweisen API-Verträge und Invarianten, nicht Dialogqualität.


9. Technische Abweichungen und nicht gebaut

Nur die technische Lage. Bewertung gegen Spec und Vision: Fit-Gap.

Thema Ist Zielrahmen / Produktiv
DB SQLite Datei PostgreSQL
Detect Muster immer; externes Klartext-Detect nur Development/Test; Produktiv fällt auf Muster zurück. Lokales HTTP-Detect bleibt Provider-Rolle. lokales Modell (Ollama) als Ziel
Fehlerkörper oft Objekt {detail: string}
/dialog Admin-Harness, aus der Produkt-Nav entfernt nicht MVP-Screen
Response Validation blockiert Klartext-Identität in der Rohantwort vor Demask; keine Quasi-Identifikatoren privacy_gateway.md §9.2 voll
Security Layer Pfad da; Minimierung/Audit/Encryption fehlen weiter privacy_gateway.md §9.2
Offline-Sync, Voice-Egress, Compose-Prod ungebaut jeweiliges Kapitel

Nicht im Code: Threads-UI, semantisches Retrieval, Session-Summaries, Related-Space-UI, lokales Detect-Modell, Obsidian/mindnet/Kairo-Handoff. Papierkorb-UI, Entry-Liste, lokales Backup/Restore und erster Journal-Impuls: 2026-08-25.


11. Korrigierter Ist-Stand vs. bewusst offene Target-Fähigkeiten

Additiv zum Slice, 2026-08-25. Kein Target-Model-Vorbau.

Korrigiert im Ist:

  • Provenance von Draft und Entry-Version ist relational (journal_*_source_refs); JSON-ID-Listen werden verlustfrei migriert. Source-Messages bleiben unangetastet.
  • Context Builder spricht Selection-Specs; Recency bleibt die aktuelle Retrieval-Heuristik, nicht die fachliche API.
  • Dialogzug kann begrenzte jüngere Original-Conversations desselben Space sehen, ohne denselben Journal Day voll zu laden.
  • Externes Klartext-Detect ist kein Produktmodus (KANSHO_ENV=production fällt auf Muster zurück). Lokales Detect bleibt austauschbare Rolle.
  • Response Validation blockiert Klartext-Identität vor Demask. Dialog-Egress maskiert den ganzen Prompt inkl. Opening/Space-Kontext; ein Leak bricht den Zug nicht leer ab. Journal-Generate bricht bei einem Leak nach Retry nicht ab: lokales Quellenartefakt bzw. lokaler Entwurf, die leckende Modellantwort wird nicht verwendet.
  • Konsolidierung nutzt lokale Signale, nicht count >= 2.
  • /dialog ist Admin-Harness, nicht Produkt-IA.
  • WritingProfile hängt nicht am Dialogue Kernel; interaction_hint ist ein leerer Slot.
  • Conversation kann einem Space ohne Journal Day zugeordnet werden.

Bewusst offen (nicht in diesem Pass gebaut):

  • Semantisches/globales Retrieval, Threads, Self Model, Related Spaces, mindnet
  • Interaction-Profile-Engine, UI und Persistenz
  • Reflection-Space-Typen, Journal-Default am Gesprächsabschluss
  • Lokales Detect-Modell, Audit ohne Prompt, Encryption, inhaltliche Minimierung
  • Contextual Continuation als Ziel-IA (Home ist eine journalspezifische Fortsetzen-Keimzelle)

17. Freeze Candidate, Backup, Papierkorb, Opening (2026-08-25)

Additiv zum Slice. Fachliche Abnahme: ../functional/mvp_freeze_candidate.md.

  • Lokaler Testeinstieg scripts/test-mvp.ps1 (UTF-8, isolierte Temp-Daten, keine Live-Keys; Fake nur in den Suites, die ihn setzen).
  • Backup: backend/local_backup.py, Einstieg scripts/backup-local.ps1. SQLite-Backup-API, Medien, Manifest, keine Secrets. Restore bestätigt, prüft Checksummen, legt Sicherheitsbackup an, bricht bei geöffneter DB ab.
  • Papierkorb: GET /spaces/{id}/entries, GET /spaces/{id}/trash, POST /entries/{id}/undelete, POST /entries/{id}/purge.
  • Erster Impuls: journal_opening.py, derselbe Gateway-Pfad, {{opening_hint}}.
  • Home: GET /continuity.

Bewusst nicht: Docker, Postgres, Ollama, Verschlüsselung, Dialogmedien als Quelle, Space-Entries als Generate-Fakten.


12. Profile Governance (2026-08-25)

Additiv zu §11. Kein Self Model, keine Intent-Engine, kein ML-Training.

Writing Profile

  • Ein Profil als Hülle: Core, optionale Facets, dynamische Traits. Die früheren Stilschlüssel sind Seed, nicht Schema. compiled_brief bleibt abgeleitete Sicht aus Traits und zeitgestempelten Quellen.
  • Governance learning | advising | frozen. Automatische Pfade (Turn, Save, Import, Bootstrap) überschreiben keine Locks und keine manuellen/angenommenen Facets. frozen hält Facet-Werte. Explizites Rebuild ehrt Locks.
  • Rückwirkender Bootstrap aus gespeicherten Current-Entries. Unredigierte Drafts bleiben keine Stilquelle. Dialog ist schwächer und entfällt als Stimme, sobald Entries existieren.

Interaction Profile

  • Getrennt, Default advising. Produktdefaults (Antwortlänge, ein Anschlussimpuls, Zuhören bei Erzählung) sind gelabelt und überschreibbar.
  • {{interaction_hint}} füllt sich aus Defaults + persönlichen Prefs + aktuellem Dialogue State. Nicht-Widersprechen ist kein Lernsignal. propose_observation legt höchstens Suggestions an und wird nicht aus run_turn aufgerufen.
  • Keine Intent-Taxonomie. Reflection Intent bleibt ungebaut. Dialogue State (narrative_mode, reflection_depth, current_focus, emotional_intensity, long_story) bleibt automatisch.

Kleine Settings-Erweiterung: Lifecycle/Initial Build, dynamische Traits statt festem Facet-Raster, Prefs, Vorschläge. Keine Profile-Management-App.

JSON-Portabilität (additiv): kind kansho.writing_profile / kansho.interaction_profile / kansho.profiles, format_version 1. Restore ersetzt Facets/Prefs und Governance ausdrücklich; Journalquellen, Dialoge und Identity-Mapping gehören nicht ins Dokument.


13. Profile Review Pipeline (2026-08-25)

Additiv zu §12. Keine neue Profil-Engine.

Nach Save/Import/Turn werden semantische Traits nicht neu berechnet. Der frühere Bootstrap-Infer über Zählfacetten entfällt; Quellen werden nur assembliert. Kontinuierliche Queue erst nach bestätigtem Initial Build (§14). Danach:

  1. lokale Evidence-/Drift-Erkennung (billig, ohne LLM)
  2. Review Candidates in writing_profile_evidence
  3. semantische AI Review nur explizit, Evidenzen gebündelt
  4. Governance entscheidet die Übernahme (learning wendet unlocked an, advising queued Suggestions, frozen nicht)
  5. writing_profile_versions hält den Stand nachvollziehbar

Writing-Trigger: starke User-Änderung eines AI-Drafts, heuristische Abweichung, Stichprobe längerer Dialogtexte (Cooldown, kein LLM), periodische Bereitschaft nach Zeit/Volumen, explizite Nutzer-Review.

AI Review vergleicht Evidenz mit dem bestehenden Profil und liefert Änderungsvorschläge (keep/update), kein komplettes neues Profil. Paket unterscheidet core und facet und darf repräsentative Source-Beispiele an Merkmalen führen.

API (POST /writing-profile/review/api) und Copy/Paste (POST .../paste plus .../import) nutzen denselben Vertrag: kansho.profile_review_package / kansho.profile_review_result. Paste erzeugt lokal einen kopierbaren Prompt; das Ergebnis kommt strukturiert zurück. Interaction-Präferenzen entstehen nicht aus fehlendem Widerspruch oder normalem Gesprächsverlauf.

Das statische Facet-Raster aus §12 ist nicht die Profilstruktur. Semantische Traits und der Initial Profile Build: §14. Kanonischer Analysevertrag, Copy/Paste als Testpfad und Import ohne Auto-Apply: §15. Die älteren kind-Namen bleiben als Legacy lesbar.


14. Writing-Profile-Hülle und Initial Build (2026-08-25)

Additiv zu §12 und §13. Keine neue große Engine.

Stabile Hülle, nicht ein geschlossenes Merkmalsraster:

  1. Core
  2. optionale context/output-Facets
  3. darin dynamische semantische Traits (datengetriebene Slugs)
  4. Evidence References
  5. Representative Exemplars
  6. Governance
  7. Versioning

rhythm, humor, detail, chronology und verwandte Keys bleiben Seed-/Ordnungshilfe (seed_catalog). UI, Persistenz und Review binden sich nicht an diese Liste. Lokale Heuristiken (writing_profile_infer) sind Mess- und Triggerhilfe; sie schreiben keine Traits.

Lifecycle: uninitializedinitial_pendingconfirmed. Quellen (Journal und Import) tragen occurred_at und context_hint. Neuere Texte wiegen stärker für die aktuelle Ausprägung, ältere belegen Langzeitmerkmale. Urlaubstagebücher mappen auf autobiographical_journal. Ein globaler Core wird nur geschrieben, wenn der Korpus unterschiedliche Quellenarten trägt.

Initial Profile Build liest den historischen Korpus (nicht nur zwei Entries). Review-Aktionen folgen Existing-before-New: bestätigen, präzisieren, Scope ändern, zusammenführen/aufteilen, erst dann neu anlegen. Kontinuierliches Learning/Advising (Queue, Drift, Dialog-Sample) startet erst nach bestätigtem Build.

API additiv: POST /writing-profile/corpus, GET /writing-profile/corpus, PATCH /writing-profile/traits/{slug}, POST /writing-profile/initial-build/paste, POST /writing-profile/initial-build/api. Paketvertrag bleibt format_version 1 mit zusätzlichen Feldern mode, corpus, existing_before_new, seed_catalog.

Tests: backend/tests/test_profile_governance.py, backend/tests/test_profile_review.py.


15. Profile Analysis Export / Import (2026-08-25)

Additiv zu §13 und §14. Keine zweite Profilhülle, kein Auto-Apply.

Zwei Modi, ein Vertrag für API und Copy/Paste:

  • initial_build — vollständiges Profile Proposal; bestehende Struktur zuerst; Strukturänderungen nach Existing-before-New; gedacht auch für Bootstrap aus einem bereits kontextualisierten externen Chat.
  • review — inkrementeller Vergleich neuer Evidence mit dem bestehenden Profil (keep / update / add / remove / reclassify / merge / split). Kein komplett neues Profil bei jeder Review.

Paket: kansho.profile_analysis_package (format_version 1). Enthält Modus, Target writing, bestehendes Profil samt Core/Facets/Traits, Governance, repräsentative Kanshō-Evidence und Exemplare, Source-Referenzen, lokale Messsignale nur als Hilfsinformation, semantische Aufgabenstellung, expected_result. Legacy-kind kansho.profile_review_package bleibt lesbar.

Ergebnis: kansho.profile_analysis_result. initial_build darf ein vollständiges profile-Proposal mitliefern; review beschreibt primär changes. Legacy-kind kansho.profile_review_result bleibt lesbar.

Copy/Paste ist in der Testphase ein vollwertiger Ausführungsweg, kein Debug-Hilfsmittel. Kanshō erzeugt aus dem Package einen kopierbaren Prompt. Das externe Modell darf authentische Nutzertexte aus dem bestehenden Chat ergänzend nutzen und muss das als evidence_basis: external_chat_history kennzeichnen. Keine erfundenen Quellen, Beispiele oder Kanshō-Source-IDs. Externe Chat-Historie ist Provenienz, kein interner Source-Layer.

Evidence-Auswahl bleibt begrenzt (KANSHO_PROFILE_PACKAGE_CHARS, Default 12000): aktuelle Texte, ältere Baseline, unterschiedliche Kontexte, keine unlimitierte Volltextsammlung, keine Trigger-/UI-Metatexte als Stil-Evidence.

Import niemals direkt als Current Profile. Ablauf: JSON einlesen, Schema prüfen, unbekannte Referenzen listen, Proposal gegen den aktuellen Stand zeigen, einzelne Änderungen übernehmen oder ablehnen oder alles übernehmen; bei initial_build zusätzlich „Als neue Profil-Baseline übernehmen“. Erst danach entsteht eine neue Profilversion. Der Current Brief ist abgeleitetes Runtime-Artefakt, nicht Source of Truth, nicht Export, nicht Import, nicht Proposal. Nach Übernahme kompiliert der Brief-Compiler neu; für Journal Generate nur relevanter Core, autobiographical_journal-Facet-Deltas und wenige Exemplare, nicht das volle historische Korpus.

Ein vollständiges Proposal darf core / facets / traits auch oben im Resultat stehen; summary gilt als Wert. Externe Provenienz wie dayone_export wird als external_context gelesen, nicht als Kanshō-Quelle. Ein Resultat ohne übernehmbare Änderungen wird abgelehnt statt still verschluckt.

API additiv: POST /writing-profile/review/accept, POST /writing-profile/review/reject (optional indexes). Settings: „Profil initial erstellen“ / „Profil überprüfen“ → Prompt kopieren → Ergebnis einfügen → Vorschläge prüfen.

Tests: backend/tests/test_profile_review.py.


16. Journal-Budget und zweistufige Generierung (2026-08-25)

Additiv zu §7 und §15. Keine stillschweigende Kürzung autobiografischer Fakten. Chunk-and-Merge bleibt eine spätere Option, ist in diesem Schritt nicht gebaut.

16.1 Ablauf

Explizites Generate durchläuft zwei maskierte Gateway-Calls, beide Klasse B, Fail Closed, ZDR unverändert:

  1. Stufe 1 inhaltliche Rekonstruktion (mvp.journal_reconstruct, purpose=journal_reconstruct). Eingabe: budgetierter Tagesdialog (alle Nutzerzeilen vollständig; vorausgehende Assistant-Zeile nur als Antwortkontext derselben Conversation). Jede nichtleere Nutzerzeile erhält lokal eine stabile Quellen-ID (u1, u2, …), Assistant-Zeilen a1, a2, …. Ausgabe: strukturiertes JSON mit source_order (lokale Quellenreihenfolge) und chronology (Ereignischronologie, nur umordenbar bei expliziten Zeit-/Reihenfolgeangaben). Lokal fail-closed validiert: vollständige Abdeckung der Nutzer-IDs, keine Umordnung oder Neunummerierung vor der Prüfung, kein Umschreiben ungültiger source-Werte, wörtliche evidence in der zitierten Nutzerzeile, keine Assistant-Tatsachen, Uhrzeiten/Unsicherheiten/Widersprüche/Korrekturen/Planänderungen bleiben. Ungültige Rekonstruktion geht nicht an Stufe 2; es gibt keine stillen Auto-Reparaturen.
  2. Stufe 2 persönliche Narration (mvp.journal_generate, purpose=journal_generate). Eingabe: validierte Rekonstruktion, kompakter aufgabenspezifischer Writing Brief, optional bestehender Text. Kein erneutes Mitschicken des vollen Tagesdialogs.

Additiv 2026-08-25 (Claim-Bindung): Chronologieeinträge tragen keine freien Faktenlisten mehr. Jedes inhaltliche Element ist ein Claim {kind, evidence} mit eigener, lokal geprüfter Evidence: ein wörtlicher Ausschnitt genau der genannten Nutzerzeile. source ist Pflicht und nur der exakte Wert user. Legacy-Felder (events, allgemeine evidence, …) werden abgelehnt. Eine gültige Evidence legitimiert keine zweite, unbelegte Behauptung. Assistententext ist niemals Evidence. Uhrzeiten in time müssen ein reines Zeitstoken sein (6:00, 06:00, 6:00 Uhr) und in derselben source_id vorkommen; vertauschte Uhrzeiten sind time_source_mismatch. Chronologische Umordnung nur, wenn jede dafür verwendete Uhrzeit source-lokal bestätigt ist und die Minutenfolge nicht abfällt; gleiche oder relative Angaben bleiben in Quellenreihenfolge (reorder_unjustified). Top-Level-contradictions / uncertainties / plan_changes / corrections sind sourced {source_id, evidence}. Stufe 2 konsumiert ausschließlich diese geprüften Ausschnitte plus bestätigte Zeittoken. Damit gilt: kein Fakten-, Zeit-, Gefühls-, Wahrnehmungs-, Bewertungs-, Zitat-, Korrektur- oder Planänderungselement erreicht Stufe 2, ohne dass seine Herkunft aus der angegebenen Nutzernachricht lokal überprüft wurde. Stufe-2-Narration kann stilistisch umformulieren; sie erhält keine unbestätigten Modellparaphrasen als Inhaltsquelle.

Additiv 2026-08-25 (Provenienz / Vollständigkeit): Die Claim-Bindung allein sichert Herkunft, nicht Vollständigkeit und nicht die Semantik von kind. Der Journal-Adapter nutzt die allgemeine Schicht provenance_verification.md: Coverage all_selected_sources rehydriert den kanonischen Volltext jeder ausgewählten Nutzerquelle lokal. Ein Claim Morgens entfernt weder Brot noch Anna noch Suppe. Modell-Labels sind unverifiziert und fehlen im Stufe-2-Artefakt. Stufe 2 erhält nur das lokal erzeugte VerifiedArtifact (kind=verified_artifact, sources[].text). Allgemeine Invarianten und Tests: backend/provenance.py, backend/tests/test_provenance.py. Journal-Adapter: journal_reconstruct.py.

Additiv 2026-08-26 (lokale Coverage-Autorität): Stufe-1-JSON bleibt untrusted. Ist es unvollständig oder ungültig, wird es nicht still repariert und nicht an Stufe 2 weitergereicht. Stattdessen erzeugt der Journal-Adapter lokal ein VerifiedArtifact aus allen ausgewählten Nutzerquellen (volle Rehydration). Stufe 2 läuft damit weiter. Abbruch nur, wenn es gar keinen Nutzertext gibt (no_user_sources). Das ist keine Auto-Reparatur des Modell-JSON.

Additiv 2026-08-26 (Identitätsleak): Response Validation bleibt fail-closed. Klartext-Identität in der Rohantwort wird nicht demaskiert und nicht als Entwurf übernommen. Nach einem Korrekturversuch erzeugt der Journal-Adapter lokal denselben Coverage-Pfad (Stufe 1) bzw. einen Entwurf aus dem lokalen Artefakt (Stufe 2). Homonyme (Speise vs. Personenname) gelten nicht als Leak.

Das vollständige Writing Profile bleibt lokale Source of Truth. compile_task_brief("journal_generate") kompiliert nur den Aufgabenbrief (Core knapp, Facet-Delta autobiographical_journal, höchstens sechs relevante Traits, ein bis zwei Exemplare, Zielgröße ca. 3.0004.000 Zeichen, feldweise an Wortgrenzen, keine Wortmitte).

16.2 Budgetmodell

Vor jedem Journal-Egress:

Größe Herkunft
Effektives Kontextfenster OpenRouter Models-API context_length / top_provider.context_length, sonst Env KANSHO_PROVIDER_CONTEXT_LENGTH
Maximale Ausgabelänge top_provider.max_completion_tokens, sonst Env KANSHO_PROVIDER_MAX_COMPLETION_TOKENS
Reservierte Ausgabe min(4096, unterstützte max_completion_tokens)
Sicherheitsmarge 15 % des Fensters (KANSHO_JOURNAL_SAFETY_MARGIN)
Input-Schätzung konservativ 2 Zeichen/Token (KANSHO_TOKEN_CHARS_PER_TOKEN); kein Modell-Tokenizer, keine Scheingenauigkeit

Mindestfenster für Journalgenerierung: 32.768 Tokens. Darunter kontrollierte Ablehnung, kein Modellwechsel.

Katalog: backend/model_catalog.py, Cache TTL 3600 s (KANSHO_MODEL_CATALOG_TTL_SECONDS). Bei Fetch-Fehler gilt ein noch gültiger Stale-Cache; sonst Env-Fallback; sonst model_metadata_unknown. Fake-Provider: 32K/4096.

Request nur, wenn geschätzter Input + reservierte Ausgabe in das geminderte Fenster passen. Tagesdialog: konfigurierbare Nachrichten-Sicherheitskappe KANSHO_JOURNAL_DAY_MAX_MESSAGES (Default 500) plus Tokenbudget. Overflow bricht ab; keine stillen Mittelkürzungen, keine Wortmitte.

16.3 OpenRouter

Journal-Calls setzen max_tokens auf die reservierte Ausgabe und plugins: [{"id":"context-compression","enabled":false}]. Nur OpenRouter. Lokale und andere Provider erhalten diese Felder nicht. provider.data_collection=deny bleibt der bestehende ZDR-Pfad.

16.4 Fehlercodes

Code Bedeutung
model_context_too_small Effektives Fenster unter 32K
prompt_budget_exceeded Input plus Reserve plus Marge passt nicht; nichts wurde gekürzt
model_metadata_unknown Fenster nicht sicher bestimmbar
reconstruction_invalid Modell-JSON der Stufe 1 unvollständig oder Assistant als Tatsache (Validator; Generate bricht daran nicht ab)
no_user_sources Ausgewählte Gespräche enthalten keinen Nutzertext; Generate bricht ab
output_limit_unsupported Reservierte Ausgabelänge nicht tragfähig
provider_context_length_rejected Anbieter lehnte wegen Kontextlänge ab

Nutzertext plus diagnostics ohne Prompts, Antworten, Klarnamen, Mapping.

16.5 Diagnose

Admin-Trace (trace.budget, trace.stages): verwendetes Modell, geschätzte Input-Tokens, tatsächliche prompt_tokens / completion_tokens / total_tokens / Kosten soweit geliefert, Fenster, Reserve, Marge, Budget-OK, Compression disabled/not_applicable, Abbruchgrund. Vollständige Prompts und Antworten nur im direkten Admin-Response des aktuellen Requests, nicht in einer globalen Historie oder Datenbank. Compact-Diagnose ohne Promptkörper darf prozessweit nur den letzten kompakten Status halten.

16.6 Bewusst offen

Chunk-and-Merge für übergroße Tage, Tokenizer je Modellfamilie, persistente Audit-Tabelle ohne Prompt-Inhalt, Quasi-Identifikatoren.

Tests: backend/tests/test_journal_budget.py. Allgemeine Provenienzschicht: provenance_verification.md, backend/tests/test_provenance.py.


10. Querverweise

  • Fit-Gap: ../functional/mvp_stand_und_abgleich.md
  • API-Liste: backend_and_api.md §5.1
  • Tabellen: memory_storage_and_offline.md §6
  • Egress: privacy_gateway.md §9
  • Journal-Budget und zweistufige Generierung: §16
  • Routen: frontend_pwa_shell.md §6.1
  • Stack-Ziel: product_frame_and_stack.md
  • Ports: runtime_and_deploy.md