Kansho/docs/architecture/technical/mvp_implementation.md
2026-08-25 13:57:23 +02:00

18 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-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
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 ![…](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. Gateway purpose=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, 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.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
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=production fä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.
  • /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
  • 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_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.


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.


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