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.