Files
yourpart-daemon/docs/FALUKANT_PRODUCTION_CERTIFICATE.md

64 lines
4.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# Falukant: Produktionszertifikate (Daemon)
Die Zertifikatslogik läuft im **24h-Daily-Tick** von `FalukantFamilyWorker` (`run_iteration`), **nicht** in einem eigenen Worker-Thread. Sie ist von der **Familien-Migration** entkoppelt: Schuldturm + Zertifikat laufen **immer** im Daily-Intervall; Liebhaber/Ehe/Monatslogik nur, wenn `QUERY_FAMILY_SCHEMA_READY` (Spalte `relationship_state.last_daily_processed_at`) erfüllt ist. Sie schreibt `falukant_user.certificate` fort (max. **+1** pro Tag, keine normale Herabstufung außer Bankrott / Erbfolge).
Implementierung: `src/worker/falukant_certificate.rs` (`run_daily`).
## SQL
- `QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS` Eingangsdaten je Falukant-User (Spielercharakter, Wissen, Produktionen, Ämter, Haus …)
- `QUERY_UPDATE_FALUKANT_USER_CERTIFICATE` Update der Stufe
## Logik (Kurz, Spec §4)
- **certificateScore** (Gewichte): Wissen 0,45 · Produktion 0,30 · Amt 0,08 · Adel 0,05 · Ruf 0,07 · Haus 0,05
- **raw_target** aus Score-Schwellen: **<0,9** → 1, **≥0,9** → 2, **≥1,8** → 3, **≥2,8** → 4, **≥3,8** → 5
- **effective_target** mit Mindestanforderungen je Stufe (Spec §4.5)
- Aufstieg nur wenn `effective_target > current`**`current + 1`** (gegen `effective_target` und 5 begrenzt)
- **Bankrott** (`money <= -5000`): Zertifikat auf **1**, mit Event
### Wichtig: UI vs. Daemon („48 h kein Aufstieg“)
Die **Mindestanforderungen** (z.B. Wissen ≥ 28, Produktionen ≥ 15 für Stufe 3) sind **nur ein Teil**. Zusätzlich gilt eine **Obergrenze aus der gewichteten Wertung** (`certificateScore``raw_target`): Es wird die **höchste Stufe ≤ `raw_target`**, die **alle** Mindestanforderungen erfüllt (`effective_certificate_target` in `falukant_certificate.rs`).
Typische Folge: Die UI zeigt **nur** zwei grüne Häkchen (Wissen/Produktionen), der Spieler bleibt aber auf Stufe 2, weil:
1. **`raw_target` = 2** (Wertung **unter** 1,8) — dann ist Stufe 3 **fachlich ausgeschlossen**, auch wenn die Mindestzahlen für Stufe 3 erfüllt sind. Oft liegt die Wertung knapp unter 1,8, wenn z.B. **Produktionspunkte** im Daemon niedrig sind (Bucket &lt; 20 abgeschlossene Produktionen in `falukant_log.production` trotz höherer Anzeige in der UI).
2. **Abweichende Eingangsdaten** gegenüber der UI: anderer gewählter Charakter (`DISTINCT ON … c.id DESC`), andere Zählung `falukant_log.production` (`producer_id`), Geld/Bankrott, etc.
**Diagnose:** Daemon mit `YPDAEMON_CERT_VERBOSE=1` starten. Wenn jemand die Mindestanforderungen für die **nächste** Stufe erfüllt, aber **nicht** aufsteigt, erscheint eine Zeile `[falukant_certificate] fu_id=… bleibt auf Stufe …` mit `certificate_score`, `raw_target`, `effective_target` und Rohwerten.
**SQL:** `QUERY_GET_PRODUCTION_CERTIFICATE_INPUT_ROWS` in `src/worker/sql.rs` — für einen Betroffenen `falukant_user.id` filtern und mit der UI abgleichen.
## Politische Ämter
Rang aus **`political_office_type.name`** (Substring-Heuristik im Daemon, ohne DB-Änderung). Anpassung über `political_name_to_rank` in `falukant_certificate.rs`.
## Kirchliche Ämter
`officePoints` aus **`max(hierarchy_level)`** der aktiven `church_office`-Zeilen (gekappt 05).
## Abgeschlossene Produktionen
**`COUNT(*)`** aus `falukant_log.production` mit `producer_id = falukant_user.id` **oder** `character.id` (Backend kann je nach Kontext die eine oder andere ID schreiben).
## Gewählter Charakter pro User
Bei mehreren lebenden Charakteren: **`DISTINCT ON (fu.id) … ORDER BY fu.id, c.id DESC`** — der Charakter mit der **höchsten** `character.id` (typisch zuletzt genutzter Slot), damit Wissen/Ruf/Ämter näher an der UI liegen.
## Events (WebSocket)
Bei Änderung der Stufe:
1. `falukantUpdateProductionCertificate` mit `reason`, `old_certificate`, `new_certificate`
2. `falukantUpdateStatus`
**`reason`:** `daily_recalculation` (normaler Aufstieg), `bankruptcy` (Geld ≤ 5000), `succession_no_heir` (Tod ohne Erben → Stufe 1).
`user_id` in den Events: **`app_user_id`** aus der Query (`COALESCE(fu.user_id, fu.id)`), sonst Fallback `falukant_user_id`.
## Nicht umgesetzt (optional / später)
- Feinere **Bankrott**-Definition
- **`political_office_history`** (nicht im Repo)