# Gate 3 Artefakt — F4 Model Intelligence

**Datum:** 5. 10. 2026
**Pro:** `security-auditor`
**Typ:** nezávislé posouzení (Gate 3) podle `ROADMAP_V6.md` §8 a `CyberBrain26cil-031026-v2.md` §8

---

## 1. Rozsah auditu

Soubory F4 implementace (produkční kód):
- `src/cyberbrain26/model_layer/router.py` — free-only routing, fallback, circuit breaker
- `src/cyberbrain26/control_plane/model_gateway/__init__.py` — ModelGateway, egress autorizace, free-only default
- `src/cyberbrain26/control_plane/model_gateway/model_egress.py` — ModelEgressGate, povinná klasifikace
- `src/cyberbrain26/model_layer/kilo_config.py` — KiloConfigBuilder, free-only whitelist, atomicita
- `src/cyberbrain26/model_layer/role_assignment.py` — RoleAssignmentStore, free-only backup selection

Testy:
- `tests/test_f4_model_intelligence.py` — 56 testů (role assignment, free-only routing, gateway wiring, kilo config, egress gate, sensitive data leakage)

**Mimo rozsah:** `security_plane/`, `execution_plane/`, `pilot/`, `knowledge_plane/`

---

## 2. Co je nové (diff od `0cc5271` checkpoint)

Všechny 5 produkčních souborů + testovací soubor + mutation harness jsou nové v F4.
`0cc5271` (checkpoint F4 complete) → `9e6b5b5` (security-auditor bash grant).
Změna v `security-auditor.md` a test policy je **mimo F4 scope**, záměrná změna bezpečnostního postoje.

---

## 3. Očekávané bezpečnostní vlastnosti (k ověření `file:line`)

| # | Vlastnost | Očekávané chování |
|---|---|---|
| P1 | Free-only routing | `router.route(free_only=True)` **nikdy** nevrátí placený model; `free_only=False` (default) může |
| P2 | Tiché přesměrování | `router.route(model_id="paid-b", free_only=True)` musí zamítnout (vyhodit `ModelRouterError`), **nikoliv** tiché vrácení free modelu |
| P3 | Fallback respektuje free_only | `route_with_fallback(free_only=True)` s placeným registrem → `ModelRouterError` |
| P4 | Egress autorizace před providerem | `ModelGateway.chat()` volá `_authorize_egress()` **před** `_execute_chat()` |
| P5 | Fallback per-provider autorizace | Každý fallback provider projde samostatnou egress autorizací |
| P6 | Fail-closed bez gate | `ModelEgressGate` bez `egress_gate` → `SecurityError("egress gate not configured; refusing to fail open")` |
| P7 | Povinná klasifikace | Prázdná `classifications` → `SecurityError("model egress requires an explicit classification; refusing to assume PUBLIC")` |
| P8 | Digest, ne obsah v auditu | Log obsahuje `payload_hash=SHA256(...)`, nikoli surový payload |
| P9 | Atomicita Kilo config | `apply_kilo_config` → `tmp` + `os.replace`, nikoliv in-place |
| P10 | Záloha před zápisem | `apply_kilo_config` vytvoří `.bak` před zápisem |
| P11 | Free-only backup selection | `RoleAssignmentStore.select_for_role(main_available=False)` → jen `verified=True AND is_free=True` backup |
| P12 | Free-only default v gateway | `ModelGatewayConfig().free_only == True` |

---

## 4. Mutace důkaz (M1–M24)

**Artefakt:** `.kilo/context/state/2026-10-05_f4-mutation-results.txt`

**Výsledky:**

| M | Kontrola | Výsledek |
|---|---|---|
| M1 | Egress authorize() volání | **PADÁ** — 9 failed |
| M2 | Missing-gate fail-closed | **PADÁ** — 2 failed |
| M3 | Router `_get_candidates` free filter | **PADÁ** — 6 failed |
| M4 | Router `_get_fallback_candidates` free filter | **PADÁ** — 6 failed |
| M5 | Router `_route_specific_model` free check | **PADÁ** — 1 failed |
| M6 | Router `free_only` default → True | **PADÁ** — 1 failed |
| M7 | KiloConfigBuilder no-free guard | **PADÁ** — 1 failed |
| M8 | KiloConfigBuilder validation gate | **PADÁ** — 1 failed |
| M9 | KiloConfigBuilder `_is_free_eligible` always True | **PADÁ** — 4 failed |
| M10 | RoleAssignmentStore backup free filter | **PADÁ** — 3 failed |
| M11 | Router `_is_free_eligible` always True | **PADÁ** — 9 failed |
| M12 | Egress prázdná klasifikace check | **PADÁ** — 1 failed |
| M13 | Egress digest → prázdný string | **PADÁ** — 2 failed |
| M14 | Egress raw payload v logu | **PADÁ** — 2 failed |
| M15 | Gateway chat přeskočí egress | **PADÁ** — 5 failed |
| M16 | Gateway missing klasifikace → PUBLIC | **PADÁ** — 1 failed |
| M17 | Gateway free_only default → False | **PADÁ** — 3 failed |
| M18 | Gateway route() nepředá free_only | **PADÁ** — 1 failed |
| M19 | KiloConfig atomic → in-place write | **PADÁ** — 1 failed |
| M20 | RoleAssignment verified flag ignorován | **PADÁ** — 1 failed |
| M21 | Gateway fallback drop free-only check | **PADÁ** — 2 failed |
| M22 | Gateway `_model_is_free` unknown → True | **PADÁ** — 3 failed |
| **M23** | Router round-robin lock | **STRUKTURNÍ** — nedetekovatelný jednovláknově |
| **M24** | RoleAssignment store lock | **STRUKTURNÍ** — nedetekovatelný jednovláknově |

**Shrnutí:** **22/24 mutací detekováno** (M1–M22). **M23, M24 jsou structuremně nedetekovatelné** jednovláknovým testem — je to známá mezer, ne slepý test. Žádná mutace není "NEPADÁ" (slepý test).

---

## 5. Brány, které MUSÍ být ověřeny

| Brána | Kdo ověří | Poznámka |
|---|---|---|
| Gate 1 (pytest/ruff/mypy) | tester / orchestrátor | **Zelená** — 707 passed, ruff clean, mypy 74 files (5. 10. 2026) |
| Gate 2 (E2E F4) | tester / orchestrátor | **Doložena** — 3 kritéria F4 z plánu §5: (a) přiřazení 2 rolím, (b) nedostupnost bez placeného fallbacku, (c) citlivá data neuniknou |
| Gate 3 (nezávislý audit) | **security-auditor** | **Toto zadání** — požaduje verdikt PASS/FAIL/REJECT_UNVERIFIED |

---

## 6. Známé otevřené nálezy (mimo Gate 3 scope)

1. **`destination=""` v `model_egress.py:72`** — prázdný destination, dnes bezpečný (`_is_loopback_destination("") == False`), ale pokud provider připadne do `allowed_remote_providers`, projde bez host kontroly. Doporučení: `destination` povinný parametr nebo `_validate_request()` odmítne prázdné. → **F3 scope**

2. **Tiché přesměrování `route(model_id="paid-b", free_only=True)`** — fail-closed (placeného nedostane), ale volající dostane `free-a` bez varování. Měl by to být výjimka? → **F4 doplnění**

3. **`RoleAssignmentStore` a `KiloConfigBuilder` nezapojené** — knihovní třídy bez volajícího v aplikační cestě. F4 gate (a) doložen přes přímé volání, ne přes živou cestu. → **F4 doplnění**

4. **`threading.Lock` v `role_assignment.py` se nepoužívá** — mrtvý kód / nedokončená práce. Nízká závažnost. → **F4 úklid**

5. **Nesoulad handoffu s měřením (nově zjištěno 5. 10.)** — handoff §3 uvádí `691 passed`, dnešní měření je **707 passed** (`0 failed`, `21.08 s`). Handoff §2 popisuje `tests/test_f4_model_intelligence.py` jako „40 testů", soubor má **56** `def test_` a `pytest` sbírá **56**. To je **nesoulad dokumentu, ne chyba kódu**. → do auditu nespadá, ale zaznamenej ho; handoff neopravuj mlčky.

---

## 7. Požadovaný výstupní formát

```
| závažnost | soubor:řádek | co je špatně | fail-closed dopad | doporučená mitigace |
```

Na konci povinně:

```
Fail-closed stav: ZACHOVÁNO | PORUŠENO
Deny-by-default: ano | ne — důkaz file:line
Scope gate kompletní: ano | ne — chybí akce: ...
Sandbox fail-closed: ano | ne — test: <název testu>
Audit chain: navazuje | přerušen — důkaz file:line
Brány, které NEBĚŽELY: <co nebylo možné ověřit bez bash, kdo to musí udělat>
```

**Slovník verdiktu:** `PASS` / `FAIL` / `REJECT_UNVERIFIED`

- `REJECT_UNVERIFIED` = auditor neprovede kontrolu, která je k dispozici (má `bash` pro `pytest`), nebo prohlásí věc neověřenou místo ověření
- Dvě položky nesmějí zůstat `[UNVERIFIED]` s vysvětlením — předchozí pokus Gate 3 to prohlásil pro 2 tvrzení, což byl důvod odmítnutí

---

## 8. Speciální poznámky pro audita

1. **Handoff §6 tvrdí:** `router.py:469` (`_route_round_robin`) a `router.py:418` (`_route_local_first`) nemají `free_only`.
   **Čtením kódu je to VYVRÁCENO** — `route()` filtruje kandidáty před výběrem strategie (`router.py:192`). Oba nálezy byly chybné. Auditor to musí umět vyvrátit, ne přijmout.

2. **Mutation M23/M24:** `STRUCTURAL_NOTE` v `mutation_check_f4.py:289-298` vysvětluje, proč nejsou detekovatelné. Je to **výsledek, ne chyba**. Verdikt by to měl reflektovat.

3. **Tvé oprávnění:** `bash: { pytest: allow, *: deny }`. Spustit můžeš **jen `pytest`** — ne `python3`, ne `ruff`, ne `mypy`. Výsledky mutací už máš v §4, nemusíš harness spouštět. Vlastní ověření: `PYTHONPATH=src python3 -m pytest tests/test_f4_model_intelligence.py -q` — pokud ti i `python3 -m` blokuje, spusť `pytest tests/test_f4_model_intelligence.py -q` rovnou.
4. **Co nesmíš tvrdit:** pokud ti `pytest` neprojde, nesbírej to jako chybu kódu, ale jako omezení prostředí — napiš to do `Brány, které NEBĚŽELY`. Udání grantu bez ověření, že runtime per-command bash dict vůbec respektuje, by bylo tvrzení bez důkazu.

---

## 9. Commit hash pro referenci

- F4 checkpoint: `0cc5271`
- Security-auditor bash grant: `9e6b5b5`
- HEAD: `9e6b5b5`