22 KiB
| 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-Call |
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_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  |
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.1–6.2.
5. API (Produkt)
Prefix /api/journal, Session-Auth, Router backend/routers/journal.py. Vollständige Liste: backend_and_api.md §5.1.
Kernpfade:
-
Dialogzug
POST /days/{id}/conversations/{cid}/turnundPOST /api/dialogue/conversations/{id}/turn
Context Builder → Gateway (purpose=dialogue_turn) → ein Generate-Call → Impuls speichern. Pronomenbindung nur hier inuser:-Zeilen. -
Journalentwurf
POST /days/{id}/generate
Nur explizit. Optionalconversation_ids. Ohne Auswahl kein stilles Mergen. Gatewaypurpose=journal_generate. Pronomen ungebunden. Entry-current_version_idbleibt unangetastet. UI öffnet?draft=1. -
Speichern
POST /entries
Mitentry_id+origin=accepted_draft: neue Version desselben Entry. Sonst neuer Entry oderuser_edit. -
Restore
POST /entries/{id}/restore→ neue Version, Originrestore. -
Scratch
PATCH /days/{id}/scratch→ nicht im Context Builder, Test: Token nicht im Egress. -
Media Upload/GET/DELETE unter dem Entry.
-
Profile Review
GET/POST /writing-profile/review…→ lokale Evidenz, explizite API- oder Paste-Review, kein Call am Dialogzug.
include_existing existiert in der API, die UI setzt es nicht.
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 |
Stub, Link zum Journal |
/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.
Interner Context-Builder (Klartext, lokal): fordert Selection-Specs an (day_messages, space_entries, space_recent_sources, writing_profile). Recency-Kappen (Day 80 Messages; 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 ohne Prior-Entries, mit Writing Profile. 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.1–9.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 |
| 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 |
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, Papierkorb-UI, lokales Detect-Modell, Obsidian/mindnet/Kairo-Handoff.
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=productionfällt auf Muster zurück). Lokales Detect bleibt austauschbare Rolle. - Response Validation blockiert Klartext-Identität vor Demask.
- Konsolidierung nutzt lokale Signale, nicht
count >= 2. /dialogist Admin-Harness, nicht Produkt-IA.WritingProfilehängt nicht am Dialogue Kernel;interaction_hintist 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
- Stub-Home / Contextual Continuation als Start-IA
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_briefbleibt 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.frozenhä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_observationlegt höchstens Suggestions an und wird nicht ausrun_turnaufgerufen.- 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:
- lokale Evidence-/Drift-Erkennung (billig, ohne LLM)
- Review Candidates in
writing_profile_evidence - semantische AI Review nur explizit, Evidenzen gebündelt
- Governance entscheidet die Übernahme (
learningwendet unlocked an,advisingqueued Suggestions,frozennicht) writing_profile_versionshä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:
- Core
- optionale context/output-Facets
- darin dynamische semantische Traits (datengetriebene Slugs)
- Evidence References
- Representative Exemplars
- Governance
- 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: uninitialized → initial_pending → confirmed. 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.
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 - Routen:
frontend_pwa_shell.md§6.1 - Stack-Ziel:
product_frame_and_stack.md - Ports:
runtime_and_deploy.md