Files

396 lines
17 KiB
Markdown
Raw Permalink Normal View History

2026-03-25 22:03:39 +01:00
# AZA - Master Handover / Operational Runbook
## Working Mode Rules
- User does not tinker or manually edit files.
- Always provide 1:1 Composer patches (usually Opus).
- One step at a time.
## CURRENT PROJECT PHASE (2026-03-25)
2026-03-30 07:59:11 +02:00
**Phase:** B1 Backend Sprint Server-Side OpenAI Architecture (Variant B mandatory)
**ARCHITECTURE DECISION VARIANT B (mandatory, 2026-03-25):**
- NO OpenAI key in the desktop app
- NO per-customer OpenAI key requirement
- NO shared OpenAI key client-side
- Instead: AZA Office talks ONLY to own AZA backend
- Only the AZA backend talks to OpenAI
- The OpenAI key resides EXCLUSIVELY server-side
- NO half-measures, NO shared-key hacks
2026-03-25 22:03:39 +01:00
**Honest Assessment:**
- Desktop app is far advanced and fundamentally usable
2026-03-30 07:59:11 +02:00
- Architecture decision Variant B is made
- Hetzner backend is now being seriously built as server path
- WordPress/Shop (Hostpoint) continues in parallel
2026-03-25 22:03:39 +01:00
2026-03-30 07:59:11 +02:00
## B1 BACKEND SPRINT (4 Weeks)
2026-03-25 22:03:39 +01:00
| Week | Approx. Dates | Focus |
|------|--------------|-------|
2026-03-30 07:59:11 +02:00
| **1** | Mar 25 Apr 01 | Backend Chat Proxy Endpoint (POST /v1/chat) + Auth + Rate Limiting |
| **2** | Apr 02 Apr 08 | Desktop app: all OpenAI calls via backend instead of direct |
| **3** | Apr 09 Apr 15 | Hetzner Deploy: Docker/Caddy/HTTPS + Production Env + Smoke Tests |
| **4** | Apr 16 Apr 22 | E2E Test Desktop→Backend→OpenAI + Customer journey without OpenAI key + Go-Live |
2026-03-25 22:03:39 +01:00
2026-03-30 07:59:11 +02:00
## CURRENT PRIORITY ORDER (B1 Backend Sprint)
2026-03-25 22:03:39 +01:00
2026-03-30 07:59:11 +02:00
1. **Backend Chat Proxy POST /v1/chat** (Week 1) Foundation of architecture
2. **Desktop app migration to backend** (Week 2) Migrate all OpenAI calls
3. **Hetzner Deploy** (Week 3) Set up production server
4. **E2E Test + Go-Live backend path** (Week 4) Customer journey without OpenAI key
5. WooCommerce/Stripe purchase path runs IN PARALLEL (Hostpoint)
2026-03-25 22:03:39 +01:00
**EXPLICITLY DEFERRED / NOT NOW:**
- Update comfort / separate auto-updater
2026-03-30 07:59:11 +02:00
- Browser-AZA web app (after backend architecture)
- Large refactors not serving Variant B
2026-03-25 22:03:39 +01:00
**HOSTPOINT vs. HETZNER:**
2026-03-30 07:59:11 +02:00
- Hostpoint remains for website/marketing/WooCommerce/Stripe
- Hetzner is NOW the backend path for OpenAI proxy/API
- Both run in parallel, not against each other
## DONE: B1-W1 Backend Chat Proxy Endpoint (2026-03-26)
**POST /v1/chat IMPLEMENTED AND VERIFIED.**
| Aspect | Detail |
|---|---|
| Auth | `require_api_token` (X-API-Token header, existing pattern) |
| Rate Limiting | `default_ip_limiter` + `default_token_limiter` |
| Request Schema | `ChatRequest`: model, messages[], temperature?, max_tokens?, top_p? |
| Message Schema | `ChatMessage`: role (system/user/assistant), content |
| Model Whitelist | gpt-5.2, gpt-5-mini, gpt-5-nano, gpt-4o, gpt-4o-mini, gpt-4o-mini-search-preview |
| Input Limits | Max 64 messages, max 100k chars/message |
| OpenAI Call | `_get_openai().chat.completions.create(**params)` server-side |
| Response Schema | `{success, content, finish_reason, model, usage, request_id, duration_ms, error}` |
| Secret Scrubbing | `sk-`, `sk-proj-`, `org-` in error texts are replaced |
| Tests | 7/7 green (auth, model, validation, OpenAI proxy, /license/status, schema) |
**File:** `backend_main.py` (~100 lines added)
## DONE: B1-W2 Desktop Chat Migration (2026-03-26)
**Desktop app: Chat completions now routed through backend POST /v1/chat.**
**Migrated:**
1. `call_chat_completion()` in basis14.py central wrapper -> `_backend_chat_completion()`
2. News search direct `self.client` call -> `_backend_chat_completion()`
3. Comments own `OpenAI()` client -> `_backend_chat_completion()`
4. Med detail short info own `OpenAI()` client -> `_backend_chat_completion()`
5. Letter style analysis in `aza_text_windows_mixin.py` -> `_backend_chat_completion()`
**New architecture in basis14.py:**
- `_BackendChatResponse` wrapper class, OpenAI-interface compatible (.choices[0].message.content, .usage)
- `_backend_chat_completion(**kwargs)` POST /v1/chat using existing token mechanism (get_backend_url/get_backend_token)
- `call_chat_completion(**kwargs)` consent/capacity check, then `_backend_chat_completion()`
- Timeout: 5s connect, 120s read
- Errors: RuntimeError with readable message, no secrets
**Remaining for later blocks:** translate.py, congress_window.py, aza_email.py (standalone modules)
**7/7 tests green:** Health, Auth, Model validation, E2E Desktop->Backend->OpenAI, Response schema, /license/status OK.
## PREPARED: B1-W3 Hetzner Deploy (2026-03-26)
**Deploy files for Hetzner/Variant B ready.**
| File | Change |
|---|---|
| `requirements.txt` | NEW: fastapi, uvicorn, openai, stripe, pydantic, python-dotenv, python-multipart |
| `deploy/Dockerfile` | Hardened: requirements.txt first, HEALTHCHECK, curl |
| `deploy/.env.example` | OPENAI_API_KEY added as required variable |
| `deploy/.env` | OPENAI_API_KEY added as required variable |
| `deploy/DEPLOY_CHECKLIST.md` | Fully rewritten for Hetzner/Variant B |
| `deploy/PRODUCTION_CONFIG.md` | OPENAI_API_KEY + desktop section for Variant B |
| `.gitignore` | .env and backend_token.txt added |
**Deploy path on Hetzner:**
```
1. DNS: api.aza-medwork.ch -> Hetzner IP (A record)
2. Clone repo on Hetzner
3. cd deploy && cp .env.example .env && vim .env (set real keys)
4. docker compose --profile proxy up -d --build
5. curl https://api.aza-medwork.ch/health
```
**Desktop app backend URL:** `https://api.aza-medwork.ch`
**Remaining:** Set DNS, enter real secrets, run docker compose, smoke test.
## CURRENT MAIN BLOCK: B1-W4 E2E Test (2026-03-26)
### Part A Manual Hetzner Steps (required, in order)
**Step 1: DNS A Record**
- Create A record: `api.aza-medwork.ch``<Hetzner VPS IP>`
- Set short TTL (300s). Verify: `nslookup api.aza-medwork.ch`
- Do NOT modify existing DNS entries for aza-medwork.ch
**Step 2: Server Setup**
```bash
ssh root@<HETZNER-IP>
apt update && apt install -y docker.io docker-compose-plugin git
git clone <REPO-URL> /opt/aza && cd /opt/aza/deploy
```
**Step 3: .env with real values**
```bash
cp .env.example .env && nano .env
```
Required: `OPENAI_API_KEY`, `MEDWORK_API_TOKENS`, `AZA_DOMAIN=api.aza-medwork.ch`, `ACME_EMAIL`
**Step 4: Start**
```bash
docker compose --profile proxy up -d --build
```
**Step 5: Server smoke test**
```bash
curl https://api.aza-medwork.ch/health
curl -X POST https://api.aza-medwork.ch/v1/chat \
-H "Content-Type: application/json" -H "X-API-Token: <TOKEN>" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Say OK"}],"max_tokens":5}'
```
### Part B E2E Test: Desktop → Hetzner → OpenAI
**Desktop config:**
1. `backend_url.txt`: `https://api.aza-medwork.ch`
2. `backend_token.txt`: same token as server `MEDWORK_API_TOKENS`
3. REMOVE local `OPENAI_API_KEY`
| # | Test | Expected | Variant B proof |
|---|------|----------|-----------------|
| 1 | Start app, check health icon | Green status | Backend reachable via HTTPS |
| 2 | Dictate → generate KG | KG text appears | `call_chat_completion` → backend → OpenAI |
| 3 | Open comments window | Medical comments appear | `_backend_chat_completion` via Hetzner |
| 4 | Click medication detail | Short info appears | `_backend_chat_completion` via Hetzner |
| 5 | Open news tab | Medical news load | `_backend_chat_completion` via Hetzner |
| 6 | Start app WITHOUT local OpenAI key | Everything works | **THE Variant B proof** |
| 7 | Check `/license/status` in browser | JSON response, no error | No regression |
### Part C Most Likely Failure Points
| # | Issue | Symptom | Fix |
|---|-------|---------|-----|
| 1 | DNS not propagated | Could not resolve host | Wait 5-60 min, check nslookup |
| 2 | Let's Encrypt fails | ACME challenge failed | Ports 80+443 open? DNS correct? |
| 3 | OPENAI_API_KEY missing | HTTP 502 on /v1/chat | Check env in container |
| 4 | Token mismatch | HTTP 401 on /v1/chat | Desktop token must match server token |
| 5 | Wrong backend URL | Connection refused | Check backend_url.txt, use HTTPS |
| 6 | Firewall blocks 443 | Timeout | `ufw allow 443/tcp` |
| 7 | Docker build fails | pip errors | Check requirements.txt |
| 8 | No TLS cert | HTTPS error | Domain must point to VPS before starting |
**Blocker Rule:** One clear block at a time.
2026-03-25 22:03:39 +01:00
## CUSTOMER JOURNEY ANALYSIS (2026-03-25)
| Step | Status | Detail |
|---|---|---|
2026-03-30 07:59:11 +02:00
| Purchase (WooCommerce) | IN PROGRESS (parallel) | Docs ready, 7 admin steps on Hostpoint |
| Download | PARTIAL | Mechanism exists, WooCommerce upload missing |
2026-03-25 22:03:39 +01:00
| Installer | FUNCTIONALLY COMPLETE | No code signing (SmartScreen warning) |
2026-03-30 07:59:11 +02:00
| Activation | BRIDGE IMPLEMENTED | AZA key sets full mode, trial dialog shows status |
| First Start | BEING REBUILT (VARIANT B) | OpenAI key being replaced by backend proxy. B1 sprint running. |
2026-03-25 22:03:39 +01:00
## OPEN FOCUS BLOCKS
| Priority | Block | Description |
|---|---|---|
2026-03-30 07:59:11 +02:00
| DONE | B1-W1: Backend /v1/chat | Chat Proxy Endpoint foundation of Variant B |
| DONE | B1-W2: Desktop migration | All OpenAI calls in basis14.py + mixin via backend |
| PREPARED | B1-W3: Hetzner Deploy | Deploy files ready, DNS + docker compose to execute |
| IN PROGRESS | B1-W4: E2E + Go-Live | Test plan documented, manual Hetzner steps defined |
| HIGH (parallel) | FB-A: WooCommerce | Stripe purchase path on Hostpoint |
| MEDIUM | FB-C: Signing | Signing readiness prepared |
| DEFERRED | FB-D: Update Comfort | Only after stable customer journey |
2026-03-25 22:03:39 +01:00
## PRODUCT NAME CURRENT DIRECTION
**Current favorite:** AZA Office
**Preferred long form:**
- **AZA Office Ihr medizinischer KI-Arbeitsplatz fuer die Praxis**
**Second good variant:**
- AZA Office Die KI-Assistenz fuer medizinische Dokumentation
**Status:** Current preferred naming direction. Not yet legally/brand-strategically finalized. Use for WooCommerce/website/download/Go-Live/product presentation.
**Earlier shortlist (AZA Desktop):** Documented in project_roadmap.json and project_plan.json. Superseded by AZA Office.
## WORKING PRINCIPLES FOR NEXT CHATS
- Root-cause-first instead of blind patching
- One clear block at a time
- No 10 construction sites simultaneously
- Weight real installed builds higher than code assertions
- Don't declare "done" too early
- Distinguish Desktop into:
1. Dev code
2. Newly built installer
3. Real behavior in installed build
## Key Commands
**Local start:**
```
cd "C:\Users\surov\Documents\AZA\backup 24.2.26"
powershell -ExecutionPolicy Bypass -File .\deploy\local_reset_and_start.ps1
```
**Tests:**
```
powershell -ExecutionPolicy Bypass -File .\deploy\authorized_status.ps1
powershell -ExecutionPolicy Bypass -File .\deploy\smoke_suite.ps1
powershell -ExecutionPolicy Bypass -File .\deploy\docker_smoke.ps1
```
## Desktop UX Block (2026-03-18)
- Benutzerdaten bei Deinstallation erhalten (Inno Setup, Standard: Nein)
- Signatur-UI: Haekchen "Profilname verwenden" + abweichender Signaturname in Einstellungen
- Rechtsklick-Haekchen im Minifenster (synchronisiert)
- Kommentare-Fenster (TEILWEISE: Grundstruktur, Detailmodus + Live-Update offen)
- Briefstil-Lernen (DOCX-Upload, Stilprofil-Analyse, Profilauswahl, Integration)
- Briefstil-Profile Fix: KISIM/Klinisch als feste Systemprofile, vereinheitlichtes Stilprofil-UI im Brief-Bereich
- Autotext Root-Cause-Fix (_is_admin NameError, Listener-Revert)
- Persistenz-Patch: Erststart-Consent, Profil+Code, Kommentare-Toggle, Einstellungs-Gruppierung
- Uebersetzer-Stabilitaetsfix: Toplevel-Embedded statt Tkinter-in-Thread
- Briefprofile: KISIM Bericht + Klinischer Bericht als vordefinierte Profile
## AZA Clean Uninstall / Reset Tool
**Per Doppelklick:** `AZA_Deinstallieren.bat`
**Per PowerShell:**
```
powershell -ExecutionPolicy Bypass -File .\tools\aza_clean_uninstall.ps1
```
- Modus 1: Nur App entfernen, Benutzerdaten behalten
- Modus 2: Vollstaendig zuruecksetzen (App + Benutzerdaten)
- Kein Neustart noetig (Standardfall)
- Danach direkt Neuinstallation moeglich
## Do-Not-Break Rules
1. Do not change existing API response formats (especially /license/status).
2. Do not modify auth/security mechanisms.
3. Do not log or print secrets/tokens/keys.
4. Signature fallback: profile name used when no explicit signature set.
5. User data in %APPDATA%\AZA Desktop NOT deleted on uninstall by default.
6. Style learning from old letters: only style/structure, NEVER copy patient data or old content.
## Correction Patch (FIX-01) 2026-03-19
1. Translator label: "Fachuebersetzer" renamed to "Uebersetzer" everywhere
2. Comments window: auto-open checkbox (persistent), live-update on KG change, clickable diagnosis headings with detail popups
3. Correction window: scrollbar for saved corrections list
4. Style profile live application: brief regenerated immediately on profile change/toggle
5. "Profil anwenden" button in style profile management dialog
6. KG creation writes directly to main window (no popup)
7. Persistence: dokumente_collapsed now properly saved/loaded
8. Main window centering: robust delayed centering after widget build via self.after()
## FIX-02 Nachschaerfungs-Patch (2026-03-19)
1. Style profile dialog: now management-only (status display, learn, delete). Active selection exclusively in brief window.
2. Comments window: auto-opens after KG creation when checkbox is active (not just refreshes existing window).
3. Logo separation: Wassertropfen (logo.ico) for EXE/desktop/installer/title bar icon. Original logo (logo.png) for internal branding (bottom-left).
Files: basis14.py, aza_text_windows_mixin.py, aza_desktop.spec, logo.png, logo.ico
## FIX-09/10/11 Quellenstrenge Kommentarlogik Inhaltsquelle / Originallink Trennung (2026-03-22)
Root Cause (FIX-09): LLM generated medication info purely from model knowledge.
Root Cause (FIX-10): PharmaWiki regex wrong (used h2/h3 instead of span#subtitle). Compendium.ch is SPA (not scrapable).
Root Cause (FIX-11): Content source and external link were mixed in one dropdown/dict.
Fix (FIX-11): Clean separation of content source and external link:
**CONTENT SOURCE** (what is shown in detail window):
- `_fetch_doccheck_info(med_name)`: DocCheck Flexikon (default) server-rendered, h2/h3 headings
- `_fetch_pharmawiki_info(med_name)`: PharmaWiki (fallback) span#subtitle headings
- User-selectable via "Inhaltsquelle:" dropdown, persistent in `med_content_quelle`
- New dict `_MED_CONTENT_QUELLEN` (DocCheck, PharmaWiki)
- Curated `_MEDICATION_FACTS` as offline fallback
**EXTERNAL LINK** ("Originalquelle oeffnen" button):
- CH = Compendium, AT = BASG, DE = BfArM UNCHANGED
- Still via `_MED_QUELLEN` / `medikament_quelle` NOT modified
Therapies/procedures: separate handling via DocCheck/PharmaWiki (unchanged).
Candidate logic (Dermowarte→Dermovate etc.): untouched.
Files: basis14.py
## ARCH-MED: Medication Source Architecture (2026-03-22)
Architecture (implemented):
1. Detect medication in KG text (existing tagging + validation)
2. Fetch content from DocCheck Flexikon (default) or PharmaWiki (user choice/fallback)
3. Inject source text into LLM prompt (strict: only use provided data)
4. Curated facts list as offline fallback
5. External link always country-based (CH/AT/DE)
6. Omit anything not from the source
Coverage: All medications available on DocCheck Flexikon + PharmaWiki.
Next steps:
1. Caching strategy (cache fetched content locally)
2. Robustness against HTML structure changes
3. Long-term: Compendium API / HCI Solutions data license evaluation
Later market profiles (DE: BfArM, AT: BASG) separately.
## Zukunftsblock Internationalisierung / Laender- und Quellenprofile (GEPARKT)
**Status:** Zukunftsthema NICHT fuer jetzt. Erst nach DACH-Stabilitaet und Produkterfolg.
**Voraussetzung:** DACH (CH/DE/AT) stabil, Go-Live gesichert, Produkt erfolgreich.
**Zielbild:**
- UI/Sprache, Medikamenten-/Diagnose-/Therapiequellen pro Markt anpassbar
- Profil-Felder: app_language, market_region, med_source_profile, dx_source_profile, therapy_source_profile
- Manueller Override durch Benutzer/Praxis
- Nicht hart nach Herkunftsland schalten saubere Profil-Logik
- Handelsnamen/Zulassungen/Fachinfos sind laenderspezifisch → Quellenprofile pro Markt
**Kein aktueller Implementierungsblock.** Erst nach Go-Live und DACH-Erfolg relevant.
## Windows Code-Signing / Smart App Control Readiness (2026-03-23)
**Problem:** Windows Smart App Control blockiert unsignierte Apps bei Kunden.
**Status:** Signing-Readiness vorbereitet, noch NICHT produktiv aktiviert.
**Vorbereitet:**
- `sign_release.ps1` signiert EXE + DLLs/PYDs + Installer (DryRun-Modus verfuegbar)
- `build_and_test_release.ps1` Signing-Schritt integriert (optional, graceful skip)
- `build_release_artifacts.ps1` Artefakt-Report mit Signatur-Status
- `SIGNING_READINESS.md` Vollstaendige Dokumentation
**Signing-Reihenfolge:** DLLs/PYDs → EXE → Installer-Build → Installer signieren → Artefakt-Report
**Vor Kundenauslieferung noch offen:**
1. EV Code-Signing-Zertifikat beschaffen
2. signtool.exe installieren (Windows SDK)
3. Publisher-Name abstimmen (Zertifikat ↔ AppPublisher ↔ Handelsregister)
4. Produktiver Signierlauf + Test auf Windows-PC mit Smart App Control
**Publisher-/Namenskonsistenz (Analyse 2026-03-23):**
3 Namensformen im Projekt beabsichtigt, keine Inkonsistenz:
- **AZA MedWork** = Firma/Publisher → SIGNING-KRITISCH (Installer, Legal, Consent, E-Mail-Absender)
- **AZA Desktop** = Produktname → konsistent, kein Handlungsbedarf
- **AZA Medical AI Assistant** = interner Projektname → nicht signing-relevant
Vor Zertifikatskauf: HR-Name pruefen, AppPublisher auf Zertifikats-Subject abstimmen.
Falls HR-Name abweicht: alle signing-relevanten Stellen gemeinsam anpassen (Liste in handover.md).
Nach Festlegung: Publisher-Name NICHT mehr wechseln (SmartScreen-Reputation).