CYBER-CONTRACT.md
# Cyber Command — kontrakt etapu dokumentacyjnego
Status: **kontrakt decyzji i reuse**, nie specyfikacja silnika
Data: **2026-09-11**
Handoff: `docs/games/cyber/CYBER-HANDOFF.md`
Ten plik wiąże granice. **Nie** opisuje kampanii, HUD-u ani pełnego schematu stanu ataku.
To, czego tu nie ma, jest albo **OPEN DECISION**, albo należy do późniejszej karty Etapu 2.
---
## 1. Wariant architektury
Cyber jest **ósmą grą** w monorepo, nie forkiem Gminy i nie nową platformą.
```text
SHARED: platforma + kontrakty CORE (użycie, bez zmiany sygnatur)
ADAPT: mechanizmy kryzysowe Gminy → kopia logiki w src/lib/cyber
NEW: ontologia IR / adwersarz / twin IT / prawo / scoring Cyber
```
Zakaz runtime:
```javascript
// ❌
import { selectGminaInjects } from '@/lib/gmina/gmina-injects';
import { enqueueDelayedEffect } from '@/core/consequences/delayed-effects';
import { needsWeeklyPriority } from '@/lib/weekly-loop';
```
CORE wolno importować tylko tam, gdzie kontrakt jest neutralny: RNG, `createDecisionRecord`, później adapter `createUniversalMetrics` i obserwacje kompetencji.
`enqueueDelayedEffect` z CORE **nie** jest kolejką Cyber (tury + `cashDelta`).
---
## 2. Czas i determinizm
| Pole | Wartość |
|---|---|
| Jednostka | 1 minuta symulacji (`simTime`, liczba całkowita ≥ 0) |
| Tick ścienny | zakazany w ścieżce obliczeń |
| Postęp | event-driven: następny `dueAtMinute` spośród działań, injectów, ataku, comms, deadline’ów, kolejki skutków. C-01 ustawia tylko `clock.simTime` skokiem (`advanceCyberClockTo`), bez ticka co minutę. |
| Los | wyłącznie `mulberry32(seed)` z `src/core/simulation/rng.js` |
| Tożsamość | `seed + state + decisions` → identyczny stan |
Gmina liczy **godziny** i **tury 2 h**. Cyber adaptuje **tę samą ideę znacznika absolutnego**, nie ten sam przelicznik. Okno injectu Gminy (`window: [fromTurn, toTurn]`) w Cyber ma być oknem minutowym albo oknem zdarzeniowym — decyzja implementacyjna karty `C-03`, nie zmiana Gminy.
---
## 3. Co wolno zapisać w stanie (szkielet, nie schemat)
Na Etapie 1 (prymitywy headless) wystarczy:
- `meta`: `gameKey`, wersje, `seed`
- `clock.simTime`
- kolejka `{ id, dueAtMinute, payload, note, sourceId }`
- bank injectów jako **dane** + `firedInjectIds`
- warstwa wiedzy vs fakty silnika (fog) — bez ontologii IT
Pola kill chain, twin IT, regulatory, scoring, inbox aktorów Cyber — **Etap 2 / NEW**. Nie projektować ich w tym pliku.
UI nigdy nie czyta ukrytej prawdy ataku. To niezmiennik, gdy te pola powstaną.
C-04: `toPlayerKnowledgeView` oddaje wyłącznie `observations` / `assessments` / `revealedSubjectIds`. Warstwa wiedzy nie przyjmuje i nie zwraca `truth` ani ground truth.
C-05: graf to **topologia**. Krawędź `{ from, to }` znaczy: **TO zależy od FROM**. Przykład: `{ from: 'active-directory', to: 'wms' }` = WMS zależy od AD. Brak statusu, knowledge i atakującego. Dane świata: `src/lib/cyber/world/company-v1-graph.js` — silnik grafu nie importuje COMPANY-01.
C-06: `FULL GRAPH + knowledgeView + baselineVisibleNodeIds → { nodes, edges }`. Node widoczny = baseline ∪ revealed (tylko id z grafu). Edge tylko gdy oba końce widoczne. Assessments per topic, bez `node.status` i bez liczników ukrytych. UI nie dostaje pełnego grafu. Finalna lista baseline COMPANY V1 = karta E-01/E-02, nie C-06.
---
## 4. Metryki i kompetencje
- Scoring rozgrywki Cyber **nie** jest P&L Hotelu.
- Adapter `createUniversalMetrics` — dopiero przy zapisie do panelu; oczekiwany kierunek jak w Gminie: `revenue = 0`, koszty / osie w `domainMetrics`. **Nie zmieniać** `src/core/metrics/universal.js`.
- `createDecisionRecord`: `context × choice × actual`, bez oceny.
- Katalog CORE (`capacity_awareness`, `prioritization`, `risk_management`) — mapowanie albo pominięcie = **OPEN DECISION**. Nie rozszerzać `evaluate.js` na start.
---
## 5. Zapis i nauczyciel
Gdy powstanie surface: `saveGameProgress` w kontekście `game_key` + `class_instance_id`.
Replay: kontrakt `PlatformGameModule.loadReplayComponent`, komponent w drzewie Cyber.
Panel nie liczy wyniku — czyta zapis.
Na Etapie 1 **nie** ma manifestu ani subdomeny, o ile LGD nie każe inaczej.
---
## Reuse Map
Strategia: **SHARED** = import wspólnego kontraktu; **ADAPT** = TARGETED REUSE REVIEW, potem lokalna kopia logiki; **NEW** = brak wzorca, projekt po prymitywach.
| Cyber mechanism | Existing GRAMY mechanism | Source | Strategy | Files allowed for targeted review |
|---|---|---|---|---|
| Auth / launch / zajęcia / sloty | Platforma wielogry | `docs/KONTRAKT-PLATFORMY-WIELOGRY.md`, `src/lib/AuthContext.jsx`, `src/surfaces/GameSurfaceApp.jsx` | SHARED | te 3; nie czytać silników gier |
| Save / resume | `game-progress-save` | `src/lib/platform/php-platform-adapter.js` (`saveGameProgress`), wzorzec payloadu w `src/pages/GminaShell.jsx` (tylko zapis, nie silnik) | SHARED | adapter + 1 shell jako wzorzec payloadu |
| Teacher panel / replay contract | `PlatformGameModule` | `src/platform/contracts/game-module.js`, `src/platform/games/gmina/index.js` | SHARED | kontrakt + 1 adapter jako wzorzec; nie cały panel |
| Neutral UI / help shell | Intro / help | `src/components/help/IntroGuide.jsx`, `src/lib/help/help-kb.js` (powłoka, nie treść Gminy) | SHARED | 2 pliki powłoki |
| Deterministic RNG | `mulberry32` | `src/core/simulation/rng.js` | SHARED | ten plik |
| Decision record | `createDecisionRecord` | `src/core/decisions/record.js` | SHARED | ten plik |
| Competency observations | `createObservation` / `buildCompetencyProfile` | `src/core/competency/evaluate.js` | SHARED (adapter later) | ten plik; **nie** zmieniać katalogu 3 osi |
| Panel metrics adapter | `createUniversalMetrics` | `src/core/metrics/universal.js`, wzorzec mapowania: `src/lib/gmina/gmina-metrics.js` | SHARED + ADAPT mapowania | te 2; nie zmieniać CORE |
| Crisis clock (minuty, event-driven) | Zegar kryzysowy (godziny / tura 2 h, `elapsedHours`, `dueAtHour`) | `src/lib/gmina/gmina-constants.js`, `src/lib/gmina/gmina-effects.js` | ADAPT | te 2 |
| Delayed crisis effects | `enqueue* / process* / { state, fired }` + `payload` | `src/lib/gmina/gmina-effects.js` (kształt); **nie** `src/core/consequences/delayed-effects.js` jako runtime | ADAPT | `gmina-effects.js`; CORE tylko żeby **nie** kopiować ładunku `cashDelta` |
| Conditional inject engine | okno → warunek → priorytet → limit; inne zdarzenie po przygotowaniu; zamknięte `effect.kind` | `src/lib/gmina/gmina-injects.js` (`GMINA_EFFECT_KINDS`, `selectGminaInjects`) | ADAPT | ten plik; test Gminy injectów tylko gdy karta C-03 |
| Fog of war / knowledge vs real | węzeł istnieje przed odkryciem; `revealedNodes`; `message.truth` ukryte przed graczem | `src/lib/gmina/gmina-state.js` (pola `revealedNodes`; nie cały plik — nagłówek stanu + `runAudit`), `src/lib/gmina/gmina-contract.js` (`createMessage`) | ADAPT | te 2, wąski wycinek |
| Infrastructure dependency concepts | graf: hard/soft, `delay`, zapas, kaskada; KPI z węzłów, nie z eventu | `src/lib/gmina/gmina-graph.js` (interfejs + reguły), `src/lib/gmina/gmina-contract.js` (`createNode`, `createEdge`) | ADAPT (koncepcja) | te 2; ontologia IT = NEW, nie kopiować węzłów gminy |
| Inbox / information flow | skrzynka, źródło, wiarygodność, status potwierdzenia | `src/lib/gmina/gmina-contract.js` (`createMessage`), `src/lib/gmina/gmina-information.js` (założenia 1–3 + `verify` — nie cały chaos społeczny) | ADAPT | te 2 |
| AAR / facts-then-score | najpierw fakty z zapisu, scoring i tekst czytają ten sam obiekt | `src/lib/gmina/gmina-report.js` (nagłówek + `collectGminaFacts`), `src/lib/gmina/gmina-scoring.js` (nagłówek rubryki) | ADAPT (wzorzec) | te 2; osie i wagi Cyber = NEW |
| Weekly loop / CAPEX / P&L | pętla tygodnia, `cashDelta` | `src/lib/weekly-loop.js`, `src/core/consequences/delayed-effects.js` | **NIE UŻYWAĆ** | — |
| Attacker / kill chain / identity / lateral / ransomware | — | brak w GRAMY | NEW | brak review Gminy |
| IT digital twin (identity, segment, datastore, control) | graf Gminy jest analogią zależności, nie modelem IT | `gmina-graph.js` tylko jako koncepcja kaskady | NEW + ADAPT koncepcji | jak wiersz zależności |
| Cyber resources / IR actions | zasoby i zadania Gminy / Zespołu — inna ontologia | nie importować | NEW | review dopiero na karcie zasobów (1 plik Gminy `gmina-contract.js` `createResource` jeśli LGD potwierdzi analogię pojemności) |
| Comms SOC / IT / CEO / DPO / media | źródła wiadomości Gminy (urząd, operator) | `createMessage` + słownik źródeł | NEW aktorzy, ADAPT kształt wiadomości | `gmina-contract.js` (`createMessage`) |
| Regulatory deadline engine | `dueAtHour` zadań i obietnic, nie prawo | `gmina-effects.js` / zadania — tylko idea absolutnego terminu | NEW | brak; nie czytać GUNB/recovery |
| Cyber scoring / scenario data | rubryki i banki Gminy | — | NEW | nie kopiować injectów blackout/powódź |
---
## 6. SHARED-EXTRACTION CANDIDATES (nie wykonywać)
Zgłaszać przy implementacji, jeśli kontrakt się ustabilizuje i będzie **identyczny** u dwóch konsumentów:
1. Kolejka `{ dueAt, payload }` + `enqueue` / `process` / `{ state, fired }` — Gmina V-1 już to odkłada; Cyber będzie piątym wariantem lokalnym.
2. Selektor injectów: okno + warunek + priorytet + limit + remis z RNG.
3. Wzorzec AAR: `collectFacts(state)` → scoring i narracja z jednego obiektu.
**Zakaz ekstrakcji** bez osobnej decyzji (P4 / Codeks).
---
## 7. CROSS-GAME ESCALATION
Dotknięcie któregokolwiek z poniższych przerywa kartę Cyber:
- `src/core/*` (sygnatura lub zachowanie)
- `src/lib/gmina/*`, `src/lib/hotel/*`, inne gry
- `src/lib/weekly-loop.js`
- tożsamości `universal-metrics` / katalog kompetencji
- twarde rejestry powierzchni (`vite.config.js`, `app-surface.js`, `build:all-surfaces`) — tylko na osobnej karcie platformy
---
## Otwarte decyzje LGD
Nie rozstrzygać w kodzie. Lista robocza — max. 5 w raporcie agenta:
1. Identyfikatory: `game_key`, nazwa handlowa, subdomena.
2. Tryb MVP: tylko SOLO headless → SOLO UI, czy od razu observer nauczyciela.
3. Pierwsza organizacja / sektor kampanii (dane, nie silnik).
4. Ramy prawne V1 i moment startu zegara obowiązku (zdarzenie vs wiedza).
5. Kompetencje: mapować 3 osie CORE, czy trzymać ocenę IR tylko w AAR Cyber.