Files
aza/AzA march 2026 - Kopie (11)/project_handover.md
2026-04-16 13:32:32 +02:00

590 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-30)
**Phase:** Stripe/License Live Path PROVEN + Admin Control Panel v2 LIVE Next Block: WooCommerce/Production Sales Path
**STATUS: TECHNICAL FOUNDATION FULLY ESTABLISHED (2026-03-30)**
**1. Stripe/License Live Path PROVEN:**
- Complete license lifecycle technically proven: Purchase → License active → Cancel/Refund → License canceled → Desktop test mode
- Real live test purchase executed and cleanly neutralized
- Desktop correctly respects canceled status and falls back to test mode
- B1-W1 through B1-W5 all successfully completed
- Three bugs patched in `stripe_routes.py` (COALESCE fix, cancel_at_period_end, sync_subscription)
**2. Admin Monitor v1 + Control Panel v2 LIVE:**
- 8 internal admin endpoints, protected via X-Admin-Token / AZA_ADMIN_TOKEN
- v1: system_status, licenses_overview, backup_status, billing_overview
- v2: license_customer_map, revenue_overview, alerts, dashboard_summary
- Verified: without token → 401, wrong token → 401, correct token → data
**3. Backup/Storage Monitor ACTIVE:**
- Daily backup script configured (/root/aza-backups/backup_aza.sh, cron)
- /host_backups read-only mounted in container
- backup_status correctly detects backups
- ~137 GB free, ~4-5% used no acute storage pressure
**4. Solved Root Causes / Lessons Learned (2026-03-27 2026-03-30):**
- Sandbox and live must be strictly separated
- missing_lookup_key resolved with _lookup_key_from_price fallback
- Live context in container verified with sk_live_
- license_debug initially wrong due to old active sandbox row + new canceled live row without customer_email
- Operative hotfix: DB backup created, canceled row linked with customer_email, old incorrect active record removed
**Hetzner Backend is LIVE (2026-03-26)**
- Production API path: `https://api.aza-medwork.ch`
- DNS: `api.aza-medwork.ch``178.104.51.177`
- Repo on Hetzner: `/root/aza-app`
- Deploy directory: `/root/aza-app/deploy`
- Running services: `aza-api` + `aza-caddy`
- Variant B technically proven end-to-end
**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
**Successful Proofs (2026-03-26):**
- `curl https://api.aza-medwork.ch/health` → successful
- `curl -X POST https://api.aza-medwork.ch/v1/chat` with valid `X-API-Token` → success:true, content:OK
- Proven: Hetzner backend runs, Caddy/HTTPS runs, server-side OpenAI access works, Variant B works end-to-end
**Root Causes Solved During Deploy:**
1. Wrong git remote (`naswinterthur`) was unsuitable
2. Uploading only `deploy/` was wrong `docker-compose.yml` expects repo root context (`context: ..`, `dockerfile: deploy/Dockerfile`)
3. Host `nginx` blocked port 80
4. Caddy DNS/ACME issues due to resolver `127.0.0.53` → Fix: explicit DNS `1.1.1.1` + `8.8.8.8` in caddy service
**Final Hetzner State:**
- Services: `aza-api` + `aza-caddy`
- Compose runs from `/root/aza-app/deploy`
- `.env` is production-relevant there
- `AZA_DOMAIN=api.aza-medwork.ch`
- `ACME_EMAIL=info@aza-medwork.ch`
- `MEDWORK_API_TOKENS` is set productively
- `OPENAI_API_KEY` is set server-side
## B1 BACKEND SPRINT (4 Weeks)
| Week | Approx. Dates | Focus |
|------|--------------|-------|
| **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 |
## CURRENT PRIORITY ORDER (B1 Backend Sprint)
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)
**EXPLICITLY DEFERRED / NOT NOW:**
- Update comfort / separate auto-updater
- Browser-AZA web app (after backend architecture)
- Large refactors not serving Variant B
**HOSTPOINT vs. HETZNER:**
- 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.
## DONE: B1-W3 Hetzner Deploy (2026-03-26)
**Hetzner backend is LIVE.**
| Aspect | Detail |
|---|---|
| Domain | `https://api.aza-medwork.ch` |
| DNS | `api.aza-medwork.ch``178.104.51.177` (A record) |
| Repo on Hetzner | `/root/aza-app` |
| Deploy directory | `/root/aza-app/deploy` |
| Running services | `aza-api` + `aza-caddy` |
| Compose | runs from `/root/aza-app/deploy` |
| `.env` production | `AZA_DOMAIN=api.aza-medwork.ch`, `ACME_EMAIL=info@aza-medwork.ch`, `MEDWORK_API_TOKENS` set, `OPENAI_API_KEY` set server-side |
## DONE: B1-W4 Server E2E Test (2026-03-26)
**Server-side Variant B proven end-to-end:**
- `curl https://api.aza-medwork.ch/health` → successful
- `curl -X POST /v1/chat` with valid `X-API-Token` → success:true, content:OK
## DONE: B1 Desktop Finalization (Code Patches, 2026-03-27)
**All code patches for Variant B in the desktop app are verified and correct:**
| Patch | Status | Detail |
|---|---|---|
| `ensure_ready()` | CORRECT | Remote backend counts as ready (no local OpenAI key needed) |
| OpenAI key setup dialog | CORRECT | Dialog suppressed when remote backend configured |
| `backend_url.txt` | CORRECT | Points to `https://api.aza-medwork.ch` |
| `backend_token.txt` | CORRECT | Primary local token source, prioritized by app |
| `start_all.bat` guard | CORRECT | Variant B protection: does not overwrite URL for live backend |
| `start_backend_autoport.bat` guard | CORRECT | Variant B protection: does not overwrite URL for live backend |
| Chat/text path | CORRECT | Routes through `POST {backend_url}/v1/chat` |
**Token priority (confirmed):** `backend_token.txt` > Env `MEDWORK_API_TOKENS` > Env `MEDWORK_API_TOKEN`
**Backend URL priority (confirmed):** Env `MEDWORK_BACKEND_URL` > `backend_url.txt`
**Unchanged side paths (do NOT block main test):**
- `translate.py`, `aza_email.py`, `apps/diktat/diktat_app.py` still have local OpenAI clients → side paths only
- `kongress2_window.py` has hardcoded URL → congress feature is parked
## DONE: B1-W5 Local Desktop Live Test (2026-03-27)
**Desktop → Hetzner → OpenAI successfully tested in the real app.**
- `backend_url.txt` points to `https://api.aza-medwork.ch`
- `backend_token.txt` locally present, accepted by live backend
- `basis14.py` started locally and successfully tested against live backend
- No local OpenAI key needed
- No OpenAI key dialog appears
- Chat/text path routes through Hetzner backend
**Desktop-specific notes (for future chats):**
- `ensure_ready()` is correctly patched for remote backend
- OpenAI key dialog suppressed when remote backend configured
- Do NOT use local starters for live test (`start_all.bat`, `RUN_AZA_ONECLICK.bat`, `START_AZA.bat`, `start_backend_autoport.bat`) they set env to localhost
- Preferred start method: `python basis14.py` directly
## DONE: Desktop License Path Against Live Backend (2026-03-30)
Desktop app correctly interprets backend license status:
- `canceled` / `inactive` → test mode / DEMO
- `active` → full mode
- Relevant code: `check_license_status()` in `basis14.py`, `compute_license_decision()` in `aza_license_logic.py`
- Activation key fallback only applies when no remote backend configured
## DONE: Admin Monitor v1 + Control Panel v2 (2026-03-30)
**File:** `admin_routes.py` (mounted in `backend_main.py` with `prefix="/admin"`)
**Security:** X-Admin-Token / AZA_ADMIN_TOKEN (require_admin_token from aza_security.py)
**v1 endpoints:** system_status, licenses_overview, backup_status, billing_overview
**v2 endpoints:** license_customer_map, revenue_overview, alerts, dashboard_summary
**Key files on Hetzner:**
- `/root/aza-app/admin_routes.py`
- `/root/aza-app/stripe_routes.py`
- `/root/aza-app/backend_main.py`
- `/root/aza-app/deploy/docker-compose.yml`
- `/root/aza-app/deploy/.env`
- `/root/aza-backups/backup_aza.sh` + `/root/aza-backups/daily/` (cron)
## CURRENT MAIN BLOCK: WooCommerce / Production Sales Path
**Goal:** Make AZA Office production-ready for sale via aza-medwork.ch (Hostpoint WordPress + WooCommerce + Stripe).
**Option 1 (preferred): WooCommerce / Shop Admin**
1. Create products in WooCommerce
2. Checkout / My Account / Download / Email
3. Billing/Legal/Invoices (CHF, VAT, Terms)
4. End-to-end test purchase
5. Verify bank payouts
**Option 2 (technical): License/Customer Logic**
1. Clean customer/license lifecycle
2. Later team/practice logic
3. Better local persistence / status consistency
**Stripe/Webhook/Admin Monitor foundation is ESTABLISHED. Focus moves forward.**
## CURRENT SUBSCRIPTION PRICES (2026-03-27)
| Plan | Monthly | Yearly |
|---|---|---|
| **1 User (Basic)** | CHF 59 | CHF 590 |
| **2 Users (Team)** | CHF 89 | CHF 890 |
**Stripe Lookup Keys and Price IDs:**
| Lookup Key | Price ID | Amount |
|---|---|---|
| `aza_basic_monthly` | `price_1T53xHL5lREAW68VbuK43lmz` | CHF 59 |
| `aza_basic_yearly` | `price_1T542BL5lREAW68VNLQGCKWZ` | CHF 590 |
| `aza_team_monthly` | `price_1T544tL5lREAW68VkmnmZ21Q` | CHF 89 |
| `aza_team_yearly` | `price_1T545RL5lREAW68VLbIh73AN` | CHF 890 |
**OPEN REQUIRED DOCUMENTATION Stripe Account:**
- Active Stripe account (login / email): NOT YET DOCUMENTED
- Test mode or live mode currently active: NOT YET DOCUMENTED
- Bank account for payouts configured: NOT YET DOCUMENTED
- Webhook active and URL: NOT YET DOCUMENTED
- Leading success/cancel URLs: NOT YET DOCUMENTED
These items must be documented before the first real customer purchase.
## DONE: Stripe Webhook + Live Test COMPLETE (2026-03-30)
**Status:** Webhook functional. Sandbox AND live purchase processed. Live test purchase successfully neutralized (canceled + refunded). `license_debug` shows `status=canceled`.
**Solved problems (chronological):**
1. **StripeObject vs dict:** `.get(...)` on raw Stripe objects → Fix: `event = json.loads(body)`
2. **`.to_dict_recursive()` does not exist** → Never use it
3. **Double prefix 404:** → Fix: `@router.post("/webhook")` only
4. **Email lookup:** `customer_email` null → Cascade: `customer_email``customer_details.email``Customer.retrieve(id)`
5. **Container env trap:** `restart` not enough → `up -d --build --force-recreate`
6. **Decimal serialization:** `str(StripeObject)` fails with Decimal → Fix: `_stripe_to_dict()` + `_decimal_default()`
7. **Idempotency bug:** `_mark_processed()` ran after error → Fix: only after success inside try block
8. **Silent exits:** Missing data → 200 OK without log → Fix: comprehensive logging
9. **Sandbox API key mix:** Sandbox webhook + live secret key → Fix: BOTH sk_test_ + whsec_ for sandbox
10. **missing_lookup_key:** Sandbox omits `price.lookup_key` → Fix: `_lookup_key_from_price()` fallback
11. **customer_email loss on updates (Bug A, 2026-03-30):** `_upsert_license` overwrote `customer_email` with NULL → Fix: COALESCE in SQL
12. **cancel_at_period_end ignored (Bug B, 2026-03-30):** Webhook wrote `status=active` despite cancellation intent → Fix: explicit check
13. **sync_subscription too restrictive (Bug C, 2026-03-30):** Only searched `WHERE status='active'` → Fix: search most recent regardless of status
**NEVER AGAIN Stripe Webhook Rules:**
- Never use `.to_dict_recursive()`
- Never use `.get(...)` on raw StripeObjects
- After signature verification: `event = json.loads(body)`
- Use `_stripe_to_dict()` for Subscription/Customer objects (not `json.loads(str(...))`)
- Prefix routing: with `prefix="/stripe"`, decorator must be `@router.post("/webhook")`
- For `.env` changes: `docker compose up -d --build --force-recreate aza-api`
- Sandbox and live strictly separated: BOTH `sk_test_` + `whsec_` for sandbox
- After sandbox tests: restore live keys in `deploy/.env`
- `license_debug` is suitable for operational quick check
- After license anomalies: always check DB assignment first (licenses table, customer_email, subscription_id, status)
**Operational reference Hetzner:**
- Current file: `/root/aza-app/stripe_routes.py`
- Working backup: `/root/aza-app/stripe_routes.py.working_lookup_fallback_ok`
- SQLite DB: `/root/aza-app/data/stripe_webhook.sqlite`
- DB backup (after sandbox): `/root/aza-app/data/stripe_webhook.sqlite.backup_after_sandbox_success`
- DB backup (before email fix): `/app/data/stripe_webhook.sqlite.before_email_fix_1774895539`
- Logs: `docker logs -f --tail 20 aza-api`
- Event log: `tail -n 20 /root/aza-app/data/stripe_events.log.jsonl`
- Debug: `curl https://api.aza-medwork.ch/stripe/license_debug?email=admin@aza-medwork.ch`
- Sync: `curl -X POST https://api.aza-medwork.ch/stripe/sync_subscription`
- Live Stripe currently set in `/root/aza-app/deploy/.env`
## EXPLICITLY DEFERRED (Polish Phase)
- Autotext fix
- Window sizes adjustment
- Button sizes / layout for online presentation
- General UI polish
- Clean up direct OpenAI side paths in secondary modules (translate.py, aza_email.py, diktat_app.py)
**Blocker Rule:** One clear block at a time.
## CUSTOMER JOURNEY ANALYSIS (2026-03-25)
| Step | Status | Detail |
|---|---|---|
| Purchase (WooCommerce) | NEXT MAIN BLOCK | WooCommerce product setup, checkout, download, email, billing/legal |
| Download | PARTIAL | Mechanism exists, WooCommerce upload missing |
| Installer | FUNCTIONALLY COMPLETE | No code signing (SmartScreen warning) |
| Activation | STRIPE LIVE PATH PROVEN (2026-03-30) | Complete lifecycle: purchase → active → cancel/refund → canceled → test mode |
| First Start | VARIANT B LIVE (2026-03-27) | Desktop → Hetzner → OpenAI works. Desktop respects canceled status correctly. |
| Admin/Ops | CONTROL PANEL V2 LIVE (2026-03-30) | 8 endpoints, backup/storage/revenue/alerts/dashboard. X-Admin-Token protected. |
## FOCUS BLOCKS
| Priority | Block | Description |
|---|---|---|
| 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 |
| DONE | B1-W3: Hetzner Deploy | Backend LIVE on api.aza-medwork.ch |
| DONE | B1-W4: Server E2E | /health + /v1/chat successful, Variant B server-side proven |
| DONE | B1-W5: Desktop Live Test | basis14.py locally tested against live backend (2026-03-27) |
| DONE | Stripe Live Webhook | Webhook 200 OK, license entry in DB, StripeObject/routing/env traps solved (2026-03-27) |
| DONE | STRIPE-W2-LIVE: Live Test Purchase | Real live purchase + cancel + refund, license_debug=canceled, 3 bugs patched (2026-03-30) |
| DONE | Desktop License Path | canceled → test mode, active → full mode correctly (2026-03-30) |
| DONE | Admin Monitor v1 | system_status, licenses_overview, backup_status, billing_overview (2026-03-30) |
| DONE | Control Panel v2 | license_customer_map, revenue_overview, alerts, dashboard_summary (2026-03-30) |
| DONE | Backup/Storage Monitor | Daily backup, /host_backups mounted, ~137 GB free (2026-03-30) |
| **CURRENT** | WooCommerce / Production Sales Path | Products, checkout, download, email, billing/legal |
| NEXT | License/Customer Logic | Clean lifecycle, team/practice logic |
| DEFERRED | UI Polish | Autotext fix, window sizes, buttons, layout |
| DEFERRED | FB-C: Signing | Signing readiness prepared |
| DEFERRED | FB-D: Update Comfort | Only after stable customer journey |
## 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
**COMMUNICATION NOTE (2026-03-30):**
Do NOT reopen / consider ESTABLISHED:
- Hetzner deploy live and working
- DNS/Caddy/nginx/TLS completed
- Variant A/B debate Variant B is mandatory and productive
- Stripe webhook stabilization all root causes solved
- Stripe live test completed and neutralized (purchase + cancel + refund)
- Desktop license path validated, canceled/active correct
- Admin Monitor v1 + Control Panel v2 LIVE and functional
- Backup/Storage Monitor ACTIVE
- Stripe/Webhook/License/Admin foundation is TECHNICALLY PROVEN do not restart from basics
Current focus:
1. WooCommerce / Production sales path (products, checkout, download, email, billing/legal)
2. End-to-end test purchase validation
3. Then: License/customer logic professionalization or UI polish
Continuity that MUST NOT be lost:
- User does not tinker only exact commands or complete Composer prompts
- Root-cause-first, no monster patches
- Hostpoint remains main website, Hetzner is backend/API
- Desktop talks to backend, OpenAI key stays server-side
- Backend license status is authoritative (not activation key)
- Admin Control Panel is foundation for operator view
- Backup/Storage monitor is active
## Current Local Workspace
**CURRENT PATH (from 2026-03-27):**
```
C:\Users\surov\Documents\AZA_GIT\aza\AzA march 2026
```
**OLD PATH (HISTORICAL, do not use):**
```
C:\Users\surov\Documents\AZA\backup 24.2.26
```
## Key Commands
**Preferred start method for live test against Hetzner backend:**
```
cd "C:\Users\surov\Documents\AZA_GIT\aza\AzA march 2026"
python basis14.py
```
**DO NOT use for live test (set env to localhost):**
- `start_all.bat`, `RUN_AZA_ONECLICK.bat`, `START_AZA.bat`, `start_backend_autoport.bat`
**Local dev start (only for local development with own backend):**
```
cd "C:\Users\surov\Documents\AZA_GIT\aza\AzA march 2026"
powershell -ExecutionPolicy Bypass -File .\deploy\local_reset_and_start.ps1
```
**Tests:**
```
cd "C:\Users\surov\Documents\AZA_GIT\aza\AzA march 2026"
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).