Kansho/docs/architecture/technical/mvp_implementation.md
Lars 79e4fd0d4b
All checks were successful
Deploy Development / deploy (push) Successful in 56s
Test Suite / pytest-backend (push) Successful in 2m43s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-build (push) Successful in 15s
Load the test schema once and truncate data between files.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 15:07:33 +02:00

50 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 (lokal): product_frame_and_stack.md nennt PostgreSQL. Der Slice auf dem Windows-Checkout bleibt SQLite (5188/8018). Additiv 2026-09-07: Docker/Server nutzt PostgreSQL 16 (KANSHO_DB_BACKEND=postgres). Stores behalten SQLite-förmiges SQL; sql_compat.py übersetzt zur Laufzeit. Nummerierte Dateien ab backend/migrations/022_*.sql. Siehe runtime_and_deploy.md und docs/DEPLOYMENT.md. Additiv (Qualitätssystem 2026-09-07): Kanonischer Backend-Runner ist pytest im Dev-Checkout nach erfolgreichem Deploy. python -m pytest tests -m "not slow" gegen kansho_test. scripts/test-mvp.ps1 ist nur noch der lokale Wrapper (Unit ohne Postgres, Integration nur mit DB_NAME=kansho_test). SQLite-Testdateien sind abgelöst; test_local_backup.py prüft weiter das Windows-Backup-Format.

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 lokales Quellenartefakt, Stufe 2 Narration
journal_generation_policy.py Journalspezifische Ausgabeeinstellungen; wählt benannte Richtlinienausprägungen, keine Modelltemperatur
routers/generation_instructions.py Admin-API für versionierbare Generierungsrichtlinien
journal_reconstruct.py Parser/Validator und local_verified_artifact; kein Runtime-Modellpfad
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
debug_store.py Opt-in lokale Admin-Testspur (app_settings, debug_runs); Default aus
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.

Additiv 2026-09-07: Dieselbe schema.sql ist Greenfield für Postgres (Dialekt nur datetime('now')CURRENT_TIMESTAMP::text in db_init.py). SQLite-History 001021 wird auf leerem Postgres als applied erfasst, nicht als ALTER nachgespielt. Persönliche Daten kommen nicht über den Alltagspfad, sondern über sqlite_to_postgres.py.

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. Additiv 2026-08-28: app_settings (Instanzschalter, derzeit debug.persist_traces) und debug_runs (Admin-Testspur je Schritt, Profil-isoliert, keine Mapping-Tabelle).

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
journal_generation_settings Profildefaults der vier Journal-Ausgabeeinstellungen (0100). Nicht Writing Profile.
generation_instruction_fragments Administrierbare Anweisungstexte je Purpose/Slot/Stufe. Seed aus JSON, gleiches Governance-Muster wie ai_prompts.
journal_generation_selection Profildefaults als vier Ausprägungs-IDs. Nicht Writing Profile.
generation_guidelines Versionierbare benannte Ausprägungen je Purpose/Slot (draft/active/archived). Seed aus JSON.
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. Optional generation_policy (vier Ganzzahlen 0100) und remember_generation_policy. Ohne Snapshot gelten gespeicherte Profilwerte. Mit Snapshot gelten die Request-Werte; Persistenz nur bei remember_generation_policy=true. GET /generation-settings liest die Profildefaults. Stufe 1 lokal (local_source_artifact), Stufe 2 ein Gateway-Zweck 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 Vollständige semantische Detection des Generate-Egress. Externes Klartext-Detect nur Development/Test (KANSHO_ENV). Produktiv: lokales Detect-Modell. Kein Pattern-Fallback.
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 (Quellenartefakt, dann Narration). Der Runtime-Pfad ruft dafür kein Rekonstruktionsmodell mehr auf. Dialogzug bleibt ein Call. Details: §16.

Additiv 2026-08-26 (Narrationsvertrag): Stufe 2 formuliert eigenständige Journalprosa. Das VerifiedArtifact bestimmt, was gesagt werden darf; das Writing Profile, wie es gesagt wird. Der Quellwortlaut ist keine Ausgabevorlage. shape_journal überschreibt akzeptierten Modelltext nicht mit Dialogzeilen. Der aktuelle Tagesdialog ist keine Stilquelle desselben Laufs.

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: lokale Day-Messages, vollständiges VerifiedArtifact. Generate-Stufe 2: Writing Profile plus Artefakt, 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 und §9.5.


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
Detect-Vertragsretry, azyklische Diagnose, Persistenzschutz backend/tests/test_detect_contract_retry.py
Request-scoped Maskierungsmanifest / Pre-Egress backend/tests/test_privacy_manifest.py
Journal-Narration (Faktentreue, nicht Wortlaut) backend/tests/test_journal_narration.py
Journal-Editorial Modes, Profil, Stilreferenzen backend/tests/test_journal_editorial.py
Journal-Generation-Policy backend/tests/test_journal_generation_policy.py
Journal-Stilkontext der Stilanwendung backend/tests/test_journal_style_context.py
Journal-Legacy-Stimmen und immutable Seeds backend/tests/test_journal_style_legacy_immutable.py
Journal-Eval-Vertrag (kein Live-Qualitätsbeleg) backend/tests/test_journal_eval.py
Shape ohne Pronomenheuristik 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
Profile-Review-Fehlergrund und Kosten backend/tests/test_profile_review_errors.py
Journal-Budget / zweistufige Generierung backend/tests/test_journal_budget.py
Persistierte Admin-Testspur backend/tests/test_debug_persist.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 Wrapper scripts/test-mvp.ps1 (pytest, kein kanonischer Runner)

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

Additiv 2026-09-07: pytest ist der Backend-Runner (backend/pytest.ini). Gitea führt python -m pytest tests -m "not slow" nach erfolgreichem Dev-Deploy auf kansho_test aus. scripts/test-mvp.ps1 wrappt denselben Aufruf lokal. Unit-Marker laufen ohne Postgres; Integration nie gegen kansho_dev. Schema einmal pro Lauf, dazwischen nur Daten-Truncate — nicht 22× schema.sql.


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 Semantische Request-Detection; Chunking; fail-closed ohne vollständige Abdeckung; Detect-Treffer nicht auto-persistiert. Externes Klartext-Detect nur Development/Test. 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 ohne lokales Detect fail-closed). Lokales Detect bleibt austauschbare Rolle. Detect-Ausgabe persistiert keine aktive Identität.
  • 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 Wrapper scripts/test-mvp.ps1 (pytest; UTF-8, isolierte Temp-Daten, keine Live-Keys; Fake nur in den Suites, die ihn setzen). Kanonisch: Gitea pytest auf kansho_test.
  • 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}}. Additiv 2026-09-03: Vorhaben nur aus dem user-Dialog des vorigen Kalendertags (calendar_date an space_recent_sources); Journal-Entries und ältere Recency sind kein Opening-Mandat. Der Opening-Call setzt include_space_recency=False. Tests: abgeschlossener Entry mit „nächsten Tag“ und Plan von vorgestern → local_neutral; „Morgen wollen wir …“ nur am Folgetag → Modellpfad. Additiv 2026-09-07: Prefix-Recency (5×400 Entries, 3×400 Source-Conversations) bleibt im laufenden Dialogzug; das ist kein Opening-Mandat. Ein Nachsatz mit Morgenuhrzeit darf den Folgetag nicht über den Entry-Anfang steuern.
  • 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.

Additiv 2026-08-29 (Fehlergrund und Kosten bei API-Analyse): POST /writing-profile/review/api und .../initial-build/api liefern bei Gateway- und Parserfehlern detail.code, detail.message, optional http_status / provider_message und cost_report (billed yes|no|unknown, Detect-/Generate-Kosten soweit der Anbieter sie gemeldet hat). Ein gültiger Generate-Call, dessen JSON lokal nicht übernommen wird, bleibt ein Fehler mit generate_called=true und ausgewiesenen Kosten. Die Settings-UI zeigt Grund und Kostennotiz, nicht nur „Anfrage fehlgeschlagen“. Mapping, Klartext und Promptkörper gehören nicht in diese Fehlerantwort. Tests: backend/tests/test_profile_review_errors.py.

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

Additiv 2026-08-26: Der historische Ablauf mit zwei maskierten Gateway-Calls (journal_reconstruct dann journal_generate) gilt nicht mehr für den Runtime-Pfad. Stufe 1 ist lokal; nur Stufe 2 geht nach außen. Der ältere Text darunter bleibt als Herkunft der Claim-Bindung und Provenienzregeln.

Explizites Generate durchlief zuvor 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.

Additiv 2026-08-26 (lokale Stufe 1, ein Narrations-Call): Der Runtime-Pfad führt Stufe 1 nicht mehr als mvp.journal_reconstruct aus. Ablauf:

  1. Tagesquellen lokal laden, Quellen-IDs vergeben, local_verified_artifact aus allen ausgewählten nichtleeren Nutzerquellen erzeugen. Assistant-Zeilen gehören nicht zum Artefakt. Reihenfolge bleibt die Begegnungsreihenfolge. Keine neue modellgestützte Semantik.
  2. Stufe 2 (mvp.journal_generate) erhält nur Verified Artifact, Writing Profile und optional bestehenden Text. Das ist der einzige externe Generierungs-Call des normalen Laufs.

mvp.journal_reconstruct bleibt als Prompt und Parser/Validator vorhanden, ist aber kein aktiver Modellpfad. Ungültiges ehemaliges Stufe-1-Modell-JSON ist deshalb im Runtime-Pfad irrelevant. Budget: kein separates Rekonstruktionsfenster; Artefakt plus Writing Profile plus optionaler Bestand müssen in das Stufe-2-Fenster passen. Übergröße bleibt prompt_budget_exceeded ohne stille Kürzung. Mindestfenster 32K und deaktivierte Context Compression bleiben. Detect prüft den gesamten Stufe-2-Egress (ggf. gechunkt). Nach erfolgreicher Detection: ein Narrations-Call, nur bei echtem aktivem Response-Leak maximal ein weiterer Narrationsversuch.

Additiv 2026-08-26 (semantische Detection): Detect ist intent-neutral. Der Journal-Adapter wählt nur, welcher Klartext in den Generate-Egress kommt. Keine Journalbegriffe in Provenienz oder Privacy. Details: privacy_gateway.md §9.5.

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. Stufe 1 erscheint als purpose=local_source_artifact ohne Provider/Modell, Tokens/Kosten 0, Coverage all_selected_sources, Status lokal erfolgreich. 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. local_label, Mapping-Tabelle, Klartextname und verworfene Rohantworten gehören nicht in persistente Diagnose.

16.6 Bewusst offen

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

Additiv 2026-08-26 (Journalprosa): mvp.journal_generate formuliert aus verifizierten Informationen eigenständige Journalprosa. Der Quellwortlaut ist keine Ausgabevorlage. shape_journal ersetzt akzeptierten Modelltext nicht durch Dialogzeilen. Der aktuelle Tagesdialog wird nicht als Stilprofil an denselben Lauf angehängt; ohne individuelles Writing Profile gilt ein neutraler Journalstil. Systemprompts mit unverändertem Template werden über seed_revision 2026-08-26-journal-narration-v1 idempotent aktualisiert; unabhängig editierte Prompts bleiben.

Additiv 2026-08-26 (Editorial Modes und wirksames Profil): Journal-Generate wählt lokal prose_edit oder notes_to_journal (journal_editorial.py), ohne zweiten Modellaufruf. Der Narrationsprompt trennt CURRENT_DAY_SOURCES, WRITING_PROFILE und STYLE_EXAMPLES. Stufe 2 erhält den kompakten bestätigten Task Brief (Core, Journal-Facet, höchstens sechs Traits) plus höchstens zwei historische finale Einträge oder Importe als Stilreferenz; der aktuelle Kalendertag ist ausgeschlossen. Unbestätigte Profile sind keine Stilautorität (neutraler Fallback). Textähnlichkeit steht als Diagnose im Trace, löst keinen Retry aus und verwirft keinen Text. Identitätsprüfung gilt für Titel und Textkörper. Private Gateway-Hilfsfunktionen werden über öffentliche, intent-neutrale Namen genutzt (canonical_token, is_identity_mention, identity_occurrence_count, identity_label_pattern).

Additiv 2026-08-27 (Narrationsvertrag v2): seed_revision 2026-08-27-journal-editorial-v2 macht den redaktionellen Auftrag explizit: unveränderlicher Inhalt versus erforderliche sprachliche Gestaltung, inklusive unvollständiger Sätze und Gewichtung belegter Kontraste. Der request-scoped Journal-Trace führt ohne Klartext: Prompt-Slug und Revision, Editorial Mode, Modelltext übernommen ja/nein, Antwort-Normalisierung, Provenienz, Writing-Profile-Präsenz (Core/Facet/Traits/Briefgröße), Stilquellen nach Typ und Größe, budgetentfernte optionale Blöcke, lexikalische Ähnlichkeit, unvollständige Syntax, Tokens, Kosten, Laufzeit. Compact-Diagnose bleibt intent-neutral (prompt_revision, generate_ms, response_normalization, generate_calls, model_text_accepted). Opt-in-Vergleich: backend/journal_eval.py (Baseline / vorheriger Vertrag / aktueller Prompt; --profile-ab). Nicht Teil des Produktionslaufs. Live nur mit --live --profile-id über das Privacy Gateway. Das Harness erklärt keinen Sieger.

Additiv 2026-08-27 (Privacy vor dem Socket, Provenienz danach): Span-genaue Maskierung für request-lokale Detect-Treffer; bestätigte Registry bleibt separates Safety-Net. Response-Verarbeitung normalisiert aktiven Klartext lokal (active_cleartext_normalized) und startet keinen zweiten Generate-Call. Der Journal-Adapter prüft heutige Quellenbelege und speichert bei nicht lokal lösbarem Fehler keinen Draft (journal_generation_not_accepted). Editorial Mode klassifiziert über Bullet-/Fragment-/Satzstruktur. Detect-Eval bewertet exakte Spans; openai/gpt-4.1-nano bleibt konfiguriert und qualitativ unbestätigt.

Additiv 2026-08-27 (Generation Policy v3): seed_revision 2026-08-27-journal-policy-v3 ersetzt den unveränderten System-Default durch einen kürzeren Prompt ohne synthetische Arbeitsbeispiele. Vier journalspezifische Parameter (transformation_strength, detail_retention, voice_strength, narrative_shaping) werden lokal in Stufenanweisungen kompiliert und über die Prompt Engine als {{transformation_instructions}} und Geschwisterplatzhalter aufgelöst. Das sind semantische Ausgabeeinstellungen, keine Modelltemperatur und keine modellspezifischen Promptvarianten. Unveränderte Defaults werden idempotent aktualisiert; unabhängig editierte Prompts bleiben. Request-scoped Trace: Werte, Stufennamen, Quelle request/profile, remembered. Keine persistente Speicherung der kompilierten Anweisungstexte. Tests: backend/tests/test_journal_generation_policy.py.

Additiv 2026-08-27 (administrierbare Anweisungsfragmente): Die sprachlichen Stufentexte liegen nicht im Python- oder Frontend-Code. Tabelle generation_instruction_fragments, Seed backend/config/generation_instructions.seed.json, Governance wie ai_prompts (unangetastet aktualisieren, Admin-Edits behalten, Reset auf aktuellen Seed). Der Compiler wählt nur. Ungültige Laufzeitkonfiguration: generation_policy_invalid, kein Provideraufruf, kein Draft. Admin: /admin/generation und /api/admin/generation-instructions/{purpose}. Trace zusätzlich: selection (variant_key), config_revision, Editorial Mode.

Additiv 2026-08-27 (benannte Richtlinienausprägungen): Numerische 0100-Werte und Schieberegler sind im aktiven Pfad entfernt. Tabelle generation_guidelines trägt versionierbare Ausprägungen (transformation, detail, voice, narrative, plus automatische source_mode). Seed-Texte bleiben fachlich unverändert. Aktive Ausprägungen sind unveränderlich; Klon erzeugt einen Draft, Reset legt neue Seed-Drafts an und überschreibt keine historische Variante. Generate sendet generation_selection und optional remember_generation_selection (Default false). Unbekannte oder nicht aktive IDs: invalid_generation_selection. Leere Anweisung oder unaufgelöster Platzhalter: generation_policy_invalid / unresolved_placeholder, kein Provideraufruf, kein Draft. journal_drafts.generation_snapshot speichert IDs, Keys, Labels, Revisionen, Quellenmodus, Prompt-Slug/-Revision und Modell, nicht Promptkörper. Admin-UI: Karten je Dimension, Anweisungstext erst nach Öffnen. Tests: backend/tests/test_journal_generation_policy.py.

Additiv 2026-08-27 (Mischquellen, Promptvertrag ohne Source Mode): Die exklusive Modusentscheidung prose_edit/notes_to_journal (choose_editorial_mode, EDITORIAL_MODES, source_mode_instructions) war eine technische Zwischenlösung und ist aus dem aktiven Pfad entfernt. Mischquellen sind der Normalfall und werden in einem Generate-Aufruf verarbeitet; keine dritte Kategorie. Persistierte source_mode-Zeilen werden beim Seed archiviert und erscheinen nicht in der normalen Adminoberfläche. Runtime-Promptvertrag von mvp.journal_generate: transformation_instructions, detail_instructions, voice_instructions, narrative_instructions, writing_profile, style_examples, reconstruction, existing_text. seed_revision 2026-08-27-journal-mixed-sources-v1. Unveränderte Systemdefaults werden idempotent aktualisiert; unabhängig editierte Prompts nicht überschrieben. Legacy-Custom-Prompt mit veraltetem Platzhalter: prompt_contract_incompatible, kein Provideraufruf, kein Draft. Snapshot ohne source_mode/editorial_mode. Tests: backend/tests/test_journal_generation_policy.py.

Additiv 2026-08-28 (Textintegrität, Detect-Diagnose, Richtlinienauswahl): shape_journal trimmt nur noch. Die frühere lokale Namens-/Pronomenheuristik nach Demaskierung ist aus dem akzeptierten Modellpfad entfernt; Wiederholung bleibt besser als ein falsch erratener Personenbezug. paragraphize gilt nur für source=fallback. Trace reply ist der gespeicherte Entwurf. Bestätigte Registry-Namen und Aliase werden unabhängig vom Detect-Modell im gesamten Egress maskiert; Detect-Treffer bleiben request-scoped. Diagnose behauptet keine semantische Vollständigkeit. Die Journal-UI sendet die gewählte Richtlinien-ID; Preview und Generate nutzen compile_selection. Ungültige Auswahl: invalid_generation_selection vor Detect/Generate. Katalog-Publish oder Default ändert eine gespeicherte Nutzerauswahl nicht. Wortlaut der Generierungsrichtlinien unverändert.

Additiv 2026-08-28 (sichtbare Modellantwort): _normalize_active_cleartext bleibt die interne Prüffassung. complete() speichert _visible_response: Originalantwort, nur ausgegebene Platzhalter rehydriert. Modell-Klartext behält die Oberflächenform.

Additiv 2026-08-29 (Stilkontext der Stilanwendungsvariante): generation_guidelines.style_context_json gehört zur Dimension voice. Seed 2026-08-29-voice-style-context-v1 legt die Ausprägungen Ohne persönliches Profil, Persönliche Stimme dezent, Persönliche Stimme deutlich und Persönliche Stimme mit Beispielen an und aktualisiert unangetastete Systemseeds; Klone und gespeicherte IDs bleiben. compile_task_brief filtert Core/Facet/Traits; Style Examples nur bei Freigabe. Leere WRITING_PROFILE-/STYLE_EXAMPLES-Blöcke entfallen. Prompt mvp.journal_generate seed_revision 2026-08-29-voice-style-context-v1. Request-scoped Trace style_application: angeforderte vs. wirksame Quellen, Zeichen/Tokens je Block, Auslassungsgrund inkl. Budget. Admin bearbeitet Anweisung und Stilquellen gemeinsam. Generate-UI bleibt bei einer Auswahl „Persönliche Stimme“. Tests: backend/tests/test_journal_style_context.py.

Additiv 2026-08-29 (Legacy-Vergleich und immutable Seeds): Seed 2026-08-29-voice-legacy-immutable-v1 fügt vier Vergleichs-IDs journal-generate-voice-legacy-* hinzu, ohne die aktuellen Voice-IDs, Defaults oder gespeicherten Auswahlen zu ändern. seed_generation_instructions überschreibt vorhandene IDs nicht mehr; abweichende Semantik erzeugt eine Nachfolge-ID (cloned_from, erhöhte revision). Prompt mvp.journal_generate lässt den Standing-Satz zu STYLE_EXAMPLES weg, wenn kein Beispielblock gesendet wird. Tests: backend/tests/test_journal_style_legacy_immutable.py.

Additiv 2026-08-29 (Detect-Vertragsretry und azyklische Fehlerdiagnose): Ein ungültiger Detect-Vertrag verwirft den Pass und startet genau einen vollständigen Neuversuch mit allgemeiner Schemaanweisung. Der zweite Fehlschlag bleibt fail-closed vor dem Narrationsmodell. trace.budget ist ein Compact-Snapshot, kein Live-diagnostics. sanitize ist zyklensicher. Persistenzfehler ersetzen den EngineError nicht. Tests: backend/tests/test_detect_contract_retry.py.

Tests: backend/tests/test_journal_budget.py, backend/tests/test_journal_narration.py, backend/tests/test_journal_editorial.py, backend/tests/test_journal_eval.py, backend/tests/test_journal_shape.py, backend/tests/test_privacy_response_integrity.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