@onlineapps/conn-orch-validator 9.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +546 -0
  2. package/README.md +337 -19
  3. package/docs/DESIGN.md +32 -9
  4. package/manifests/biz-service.manifest.json +56 -6
  5. package/package.json +3 -2
  6. package/src/CookbookTestRunner.js +134 -22
  7. package/src/ValidationOrchestrator.js +312 -73
  8. package/src/cli/biz-ci-gate.js +191 -15
  9. package/src/cli/oa-sync-template.js +23 -8
  10. package/src/cli/oa-validate.js +70 -2
  11. package/src/index.js +21 -13
  12. package/src/lint/scripts/lintScripts.js +65 -18
  13. package/src/manifest/checks/composeRunnerBlock.js +37 -20
  14. package/src/manifest/checks/discoveryOrphan.js +2 -1
  15. package/src/manifest/checks/docsLintBridge.js +79 -21
  16. package/src/manifest/checks/gitTracked.js +14 -28
  17. package/src/manifest/checks/libraryPackage.js +3 -1
  18. package/src/manifest/checks/libraryWorkspace.js +18 -3
  19. package/src/manifest/checks/readmeRegion.js +9 -1
  20. package/src/manifest/checks/serviceConfig.js +29 -12
  21. package/src/manifest/checks/serviceDb.js +176 -7
  22. package/src/manifest/checks/serviceFiles.js +34 -7
  23. package/src/manifest/checks/serviceIdentityRows.js +3 -1
  24. package/src/manifest/checks/serviceRuntime.js +126 -1
  25. package/src/manifest/discovery.js +25 -7
  26. package/src/manifest/gitCheckout.js +84 -0
  27. package/src/manifest/runManifest.js +58 -7
  28. package/src/manifest/workspaceRoot.js +91 -5
  29. package/src/sync/serviceTemplate.js +76 -7
  30. package/src/sync/sharedEnv.js +11 -4
  31. package/src/sync/uniformFiles.js +91 -21
  32. package/src/utils/bizCiGateContract.js +25 -1
  33. package/src/utils/dbAccountGrants.js +126 -0
  34. package/src/utils/envContract.js +36 -6
  35. package/src/utils/envReads.js +102 -0
  36. package/src/utils/installContract.js +46 -5
  37. package/src/utils/libCompat.js +39 -19
  38. package/src/utils/preValidation.js +56 -11
  39. package/src/utils/stepFailure.js +106 -19
  40. package/src/utils/stepReferences.js +278 -0
  41. package/src/utils/testCoverageContract.js +60 -2
  42. package/src/utils/throwawaySchema.js +92 -7
  43. package/src/validatorIdentity.js +31 -0
  44. package/src/validators/ServiceStructureValidator.js +47 -15
  45. package/src/validators/ValidationProofGenerator.js +73 -34
  46. package/templates/business-service/.dockerignore +9 -1
  47. package/templates/business-service/.gitlab-ci.yml +199 -35
  48. package/templates/business-service/Dockerfile +49 -16
  49. package/templates/business-service/README.md +56 -9
  50. package/templates/business-service/config/env-templates/__SERVICE_NAME__.env +17 -5
  51. package/templates/business-service/config/env-templates/shared.env +8 -2
  52. package/templates/business-service/docker-compose.production.yml +9 -0
  53. package/templates/business-service/docker-compose.yml +17 -0
  54. package/templates/business-service/docs/80-setup/PLATFORM_MATRIX.md +1 -1
  55. package/templates/business-service/docs/80-setup/VALIDATION.md +1 -1
  56. package/templates/business-service/jest.config.js +9 -1
  57. package/templates/business-service/package.json.template +1 -1
  58. package/src/mocks/MockStorage.js +0 -188
package/CHANGELOG.md CHANGED
@@ -4,6 +4,552 @@ All notable changes to this package. Follows [Keep a Changelog](https://keepacha
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [11.0.0] — 2026-09-17
8
+
9
+ ### Changed — deploy job aplikuje seedy třídy PRODUCTION_LIKE (d.589, W589)
10
+
11
+ `templates/business-service/.gitlab-ci.yml` (blok `oa-ci v1`): po migracích a před přepnutím se aplikují deklarované
12
+ `database.seeds`, jejichž hlavička nese `Dataset-Class: PRODUCTION_LIKE` (standard `repository-installation-sql-contract.md`
13
+ §4/§5; converter po 10 migracích měl 22 prázdných tabulek). Filtr běží na runneru — box neparsuje JSON ani hlavičky a o třídě
14
+ nerozhoduje; `TEST_ONLY` se nikdy neaplikuje a job to řekne jménem souboru. Seedy se netrackují a jedou při každém nasazení,
15
+ licencí je `-- Idempotency: yes`, její absence i třída mimo slovník odmítnou nasazení před ssh. Devátý poziční argument
16
+ `BIZ_DB_SEEDS`; na boxu `apply_mariadb_seeds_over_tcp <host> <port> <db> <file>...` z běžce neseného z klonu api
17
+ (INFRA `7045a736`), s pojmenovaným odmítnutím mezi předpoklady, když ho klon nenese. Bats `biz-deploy-db-migrations.bats` 24 → 29.
18
+
19
+ ### Fixed — box se resetuje na commit, který runner měřil (d.589b, W589)
20
+
21
+ Desátý poziční argument `$CI_COMMIT_SHA`; `git merge-base --is-ancestor` + `git reset --hard "$COMMIT_SHA"` místo
22
+ `origin/production` — push během běhu jobu dosud rozešel to, co runner četl v kontraktu, s tím, co box nasadil.
23
+
24
+ ### Fixed — existence `../shared.env` a `config/env-active/<svc>.env` se ověřuje pro každou službu (d.589c, W589)
25
+
26
+ Dosud jen ve větvi s migracemi; služba bez databáze (pdfgen, hello) se o chybějícím souboru dozvěděla až od
27
+ `docker compose`. Kontrola je před větvením s `Fix:` na runbook, který ty dva soubory zakládá
28
+ (`docs/operations/production-box-provisioning.md` § 3).
29
+
30
+
31
+ ### Added — řádek uniformy `R-USER` (check `process-identity`) (d.588c, W588)
32
+
33
+ Produkční stage `Dockerfile` deklaruje `USER node` a uzel služby v `docker-compose.yml` pinuje `user: "1000:1000"`. Druhý
34
+ platformní fakt vyjmutý ze třídy `own` (prvním je node major, `R-NODE`); běžec drží `F-RUNNER`, produkční compose `G-PROD`
35
+ (kontrolní případy, že `R-USER` mlčí, kde hlásí ten druhý). Zčervená u všech osmi služeb — vždy jedním nálezem o `Dockerfile`
36
+ (dev compose už `user:` má 8/8). `doc` = `docs/biz/00-model/service-shape.md` (vzor `R-PORTS-DEV`); sekce „Process identity"
37
+ v README šablony, generovaná oblast řádků přegenerována.
38
+
39
+ ### Fixed — krok, který spadl výjimkou, nechá svůj záznam; nález nese krok (d.602, W602)
40
+
41
+ Wrapper 9.0.x rozhoduje o opakování padlé FÁZE 0.2 podle `result.steps.<krok>.valid`; krok, jehož tělo vyhodilo výjimku, ale
42
+ záznam nezanechal, došel k wrapperu jako „validator spadl" = přechodně, a koupil si šest pokusů (18,5 min bootu, který uspět
43
+ nemůže). `runStep()` je síť pod všemi sedmi kroky: zapíše `{ valid: false, success: false, threw: true, errors: […] }` s tím,
44
+ co krok naměřil, a nález `{ type: 'STEP_THREW', step, message }`; objektové nálezy kroku 1 nesou v `result.errors` `step`
45
+ (kopie). Věty kroků 2–7 zůstávají větami — `describeValidationFinding()` vydaného wrapperu vykreslí objekt jen s `type` i
46
+ `message`; sjednocení na objekty čeká na wrapper, který `errors[].step` čte (pak 11.0.0). `attributeToStep()` odmítne nálezy,
47
+ které nejsou pole, hláškou §5. Krok 5 hlásí `success`, ostatní `valid` — dvě pole pro jeden pojem, zapsáno jako nález.
48
+
49
+
50
+ ### Changed — BREAKING: šablona biz služby — produkce běží jako uid 1000, běžec má vlastní `conn-runtime` (d.588, W588)
51
+
52
+ `templates/business-service/Dockerfile`: obě stage deklarují `USER node` (uid 1000/gid 1000 v `node:24-alpine`) a zakládají
53
+ `/app/logs` + `/app/conn-runtime` pod tím účtem (`COPY --chown=node:node`). Produkční obraz dosud běžel jako root (změřeno
54
+ `docker run --rm <obraz> id` → `uid=0`), zatímco dev kontejner jako 1000:1000 — asymetrie, kterou nikdo nedeklaroval.
55
+ `docker-compose.production.yml`: `user: "1000:1000"` shodně s dev → řádek `G-PROD` u každé služby zčervená do
56
+ `oa-sync-template docker-compose.production.yml`. Blok `oa-test-runner v1` v `docker-compose.yml` dostal anonymní svazek
57
+ `/app/conn-runtime`, aby proof běžce nepřepisoval proof služby (změřeno converterem: běžec nad `.:/app` zapsal
58
+ `validation-proof.json` běžící instance) → řádek `F-RUNNER` zčervená do `oa-sync-template docker-compose.yml`. Nové sady
59
+ `templateProcessIdentity`, `templateRunnerRuntime`, `templateImageIdentity.integration` (skutečný `docker build` + `docker run`;
60
+ bez démona/checkoutu/registry NOT RUN s důvodem, nikdy červená za síť).
61
+
62
+ ### Changed — BREAKING: šablona neměří uniformu při buildu obrazu — verdikt patří CI (d.588b, W588)
63
+
64
+ Řádek `RUN … oa-validate.js .` ve stage `production` (d.215b, `4b5ed9e6`) měřil uniformu nad build kontextem bez `.git`,
65
+ compose a README — u každé reálné služby (`.dockerignore` je správně vyřazuje) `NOT DEPLOYABLE — 10 finding(s)`, všechny
66
+ „absent", a build spadl. Konfirmace 006/2 („information, never a gate"), 010 (`validate-uniform` z každého commitu nad
67
+ checkoutem) a 011 (strom bez git checkoutu = NOT RUN) větu 001 §3.1 překonaly; řádek je pryč i s testem `add-service.bats`
68
+ 54, který teď tvrdí opak. `templateImageIdentity` už nepotřebuje srovnat scaffold pinovanou verzí (19 s místo 46 s).
69
+
70
+ ### Fixed — fixtury uniformy dosynchronizovány na SSOT sdílené sady (dovětek d.533, W588)
71
+
72
+ 14 fixtur `shared.env` a 3 `shared-env.json` se od `f61501cc` lišily jedním klíčem (`JWT_SECRET` `why`/`consumers`);
73
+ `manifestServiceConfig` (`G-SHARED-ENV`) byla proto v HEAD červená. Záměrně rozejitá `service-workspace/api_biz/beta`
74
+ zůstala (vstup případu „not the platform manifest rendered").
75
+
76
+ ### Changed — BREAKING: krok 7 mimo git checkout je NOT RUN, ne „NOT DEPLOYABLE" (d.592, W592)
77
+
78
+ Produkční obraz není checkout a nenese compose ani README, takže uniforma měřená tam hlásila 12 nálezů nepřítomnosti a
79
+ služba se registrovala `deployable: false` (BIZ-pdfgen 2026-09-16). Verdikt z toho není přísnost, ale špatná odpověď:
80
+ krok vypíše jednu větu NOT RUN s odkazem na CI job `validate-uniform`, `results.deployable` zůstává `null` (= neměřeno,
81
+ jiné tvrzení než `false`), banner ani `ci/deployability.json` nevznikají (konfirmace `biz-service-manifest` 010, 011).
82
+ Sonda `git ls-files` má jedno místo `src/manifest/gitCheckout.js` (`gitCommand`, `listTrackedFiles`, `isGitCheckout`,
83
+ věta `NOT_A_CHECKOUT`); `X-IGNORED` ji bere odtud. Fixtury kroku 7 jsou nově checkouty přes `makeGitCheckout`.
84
+
85
+ ### Added — Tier-1 běžec předává výstup kroku dalším krokům (d.590, W590)
86
+
87
+ `{{steps.<step_id>.output.…}}` v `input` kroku se rozřeší proti tomu, co dřívější kroky téhož receptu vrátily, takže recept
88
+ si fixturu vytvoří, použije a smaže (vlastník: konfirmace `converter-boot-cookbook` 001 — „opravit v knihovně"). Syntaxe
89
+ i průchod zůstávají cookbook formátu a `@onlineapps/cookbook-core` (`resolveReferencesWith`, `resolveReferencePath` —
90
+ táž kolej jako `WorkflowOrchestrator`); běžec přidává jen scope běhu (`src/utils/stepReferences.js`) tvaru `format.md`
91
+ § Runtime context (`context.steps`): definice kroku + `output` + `_execution`, adresováno `step_id`. Expanduje se jen
92
+ `input`; `output` se zapisuje jen u kroku, který prošel; scope je per cookbook. `CookbookTestRunner.executeStep()` má
93
+ pátý parametr (kontext běhu; samostatný krok dostane prázdný). Po spadlém kroku s `expect` se další kroky nepouští
94
+ (dnešní chování, nově připnuté testem). NOVÁ ZÁVISLOST: `@onlineapps/cookbook-core` (SSOT) — do `dependencies` před vydáním.
95
+
96
+ ### Added — Tier-1 běžec odmítá recept, který produkce nepřijme (d.598, W598)
97
+
98
+ Před během: volání template helperu v `input` (`jméno(...)` — Tier-1 registr helperů nemá a `cookbook-template-helpers`
99
+ táhne `content-resolver`, tedy I/O), `{{steps.<id>}}` na krok, který recept nedefinuje (včetně tečkového `{{steps.0}}`;
100
+ gateway odpovídá 400, konfirmace `cookbook-validation-placement` 001 = throw u příjemce jako pojistka), a `depends_on` na
101
+ nedefinovaný krok. Dosud obojí tiše propadlo jako literál. `utils/stepReferences.checkStepInputExpressions()` používá týž
102
+ `resolveReferencesWith()`. Literálem zůstává, co jím podle normy být má (`{{steps[0]}}`, `{{api_input.…}}`, `{{context.…}}`,
103
+ `{{current.…}}`, odkaz na definovaný, ale neběžící/selhavší krok). Dopad na dnešní recepty služeb: 0 ze 43.
104
+
105
+ ### Fixed — `MISSING_COOKBOOKS` nese `Fix:` právě jednou (d.599, W598)
106
+
107
+ Jediný ze 17 nálezů `ServiceStructureValidator` měl nápravu i uvnitř `message`; wrapper 9.0.1 složením `message (Fix: fix)`
108
+ ji tiskl dvakrát.
109
+
110
+ ### Added — `oa-validate --env-reads [<serviceRoot>]` (d.533c, W533c)
111
+
112
+ Vypíše jména proměnných prostředí, která služba čte: po řádcích, seřazená, bez duplicit, nic jiného (žádný banner, žádný
113
+ verdikt, žádné `ci/deployability.json`), exit 0; bez čitelného `config/service/integration-contract.json` exit 2 a
114
+ `[oa-validate] … Fix: …` na stderr; nekombinuje se s `--json`, `--library`, `--all`, `--workspace`. Odpovídá bráně nasazení
115
+ `api/scripts/validate-env.sh`, která u biz cíle shazovala nasazení na `JWT_SECRET=CHANGE_ME` — klíč, který žádný biz obraz
116
+ nečte (dohoda s INFRA 2026-09-17). `utils/envContract.js` vystavuje `collectDeclaredEnvNames({serviceRoot, contract})` —
117
+ množina, proti které měří řádek `C-ENV-READS` (`env` blok ∪ `${VAR}` z `config/service/*.json` ∪ endpointové proměnné
118
+ vyžadovaných konektorů), vytažená z `verifyEnvCompleteness`, aby druhý konzument nezaložil druhou definici.
119
+
120
+
121
+ ### Documentation — zabalený `shared.env` nese komentář u `JWT_SECRET` podle SSOT (d.533)
122
+
123
+ Šablona `templates/business-service/config/env-templates/shared.env` je render SSOT `api/config/shared-env.json`; `why` u `JWT_SECRET`
124
+ teď jmenuje čtyři infra čtenáře a říká, že business řetězec klíč nese a nečte (`@onlineapps/service-common` bere tajemství jako vstup).
125
+ Vydání 10.0.0 (22:32) přistálo o čtyři minuty dřív než `f61501cc` — zabalená kopie je o tento komentář pozadu, chování beze změny.
126
+
127
+ ### Added — CI pod účtem služby (d.567)
128
+
129
+ Nový příkaz `oa-biz-ci-gate setup-db-account`: založí účet, který služba **deklaruje** (`DB_USER`
130
+ v `config/env-templates/<služba>.env` — týž klíč, který měří řádek `D-DB-ACCOUNT`), a udělí mu granty na
131
+ jeho schéma. Je to **jediný** krok biz pipeline pod rootem databáze a poslední takový: všechno za ním
132
+ se připojuje jako služba, stejně jako produkce. Důvod je konfirmace `db-migrations-first-deploy` 001
133
+ („migrace nikdy pod rootem"): sedm pipeline dnes aplikuje tutéž migrační sadu pod `DB_USER: "root"`,
134
+ takže CI dokazuje průchod migrací právy, která produkce neuděluje.
135
+
136
+ Věty SQL mají **jednu definici** — `src/utils/dbAccountGrants.js`. Produkční runbook a CI jsou dva
137
+ volající, ne dvě kopie (`db-migrations-first-deploy` 001: „žádný nový generátor SQL, žádná druhá kolej").
138
+ Jediný rozdíl CI proti produkci je grant na zahazovací jmenný prostor `` `<schéma>\_%` `` (staví do něj
139
+ integrační tier, `throwawaySchema.js`); `_` je escapované, takže grant zůstává uvnitř jedné služby —
140
+ neescapovaný je `_` zástupný znak LIKE a `oagen_meta_%` by sáhlo i na `oagen_metadata`.
141
+ `GRANT … ON information_schema.*` v sadě **není**: server ho odmítá i rootu (změřeno na dev
142
+ `gen_mariadb10.5`, MariaDB 10.5.17, `ERROR 1044`), a každý účet ten katalog čte filtrovaně sám.
143
+
144
+ Rootové přihlášení má vlastní jména `CI_DB_ROOT_USER` / `CI_DB_ROOT_PASSWORD` a nebere se z `DB_USER`
145
+ (`architecture-principles.md` §8) — právě to, že ty dvě identity byly jedna hodnota, příkaz končí.
146
+ Heslo účtu je zahazovací hodnota jobu (`DB_PASSWORD`), nikdy `CHANGE_ME` ze šablony: šablona deklaruje
147
+ klíč, ne tajemství. Služba bez bloku `database` hlásí `NOT APPLICABLE` a končí nulou, jako `setup-db`.
148
+ Žádné heslo se na výstup nedostane ani na cestě selhání.
149
+
150
+ Že ty granty **stačí** — a pořád nejsou příliš — není vlastnost textu SQL, ale odpovědi serveru, takže je
151
+ to změřeno proti skutečné MariaDB (dev `gen_mariadb10.5`, 10.5.17): se samotným produkčním grantem server
152
+ stavbu zahazovacího schématu odmítne (`ERROR 1044`), po `setup-db-account` projde, a účet přitom pořád
153
+ nedosáhne na sousední službu — ani na její schéma, ani na zahazovací schéma v jejím jmenném prostoru, ani
154
+ na řádky, ani na její jméno v katalogu. Soused se jmenuje `<ns>ax` k `<ns>a` právě proto, že neescapované
155
+ `_` by ho pustilo dovnitř.
156
+
157
+ Kaskáda: sedm repozitářů s databází přepíše v jobu `test` `DB_USER: "root"` na účet služby a zařadí
158
+ `setup-db-account` před `ci:gate:setup`.
159
+
160
+ ### Added — řádek uniformy `D-DB-CI-ACCOUNT` (d.567)
161
+
162
+ `D-DB-ACCOUNT` měří deklaraci (`DB_USER` v šabloně prostředí); nic neměřilo prostředí, ve kterém se ta
163
+ migrační sada každý den skutečně aplikuje. Nový řádek (`severity: deploy`, vlastník BIZ-general) hlásí
164
+ `DB_USER` s hodnotou `root` a `ci:gate:setup`, před kterým neběží `setup-db-account`. Samostatný řádek,
165
+ ne rozšíření `D-DB-ACCOUNT`: jiný soubor, jiná oprava, a jeden řádek se dvěma opravami jsou dva
166
+ mechanismy pod jedním jménem (`automation-gates.md` §1.2). Čte řádky **mimo** blok `oa-ci v1` (ten je
167
+ G-CI), takže nekřísí problém, kvůli kterému byl celosouborový řádek nad `.gitlab-ci.yml` po třech dnech
168
+ stažen.
169
+
170
+ Měří, KDO kroky nad databází pouští, a záměrně ne, ZDA je repozitář pouští: job, který nestaví schéma,
171
+ nemá účet, který by tento krok zakládal. Hranice je napsaná, protože nevyslovená hranice se čte jako
172
+ pokrytí (`automation-gates.md` §5). Obě pravopisné podoby téhož: `DB_USER: "root"` v `variables:` i
173
+ `DB_USER=root` jako předřazené přiřazení na samotném kroku (změřený tvar `api_biz/hello-service`).
174
+ Komentář, který jmenuje `ci:gate:setup`, krokem není (změřený tvar `api_biz/meta`).
175
+
176
+ Změřeno nad všemi osmi repozitáři: sedm hlásí dva nálezy s číslem řádku a proveditelnou opravou,
177
+ `pdfgen` (bez bloku `database`) mlčí z konstrukce. Zrcadla generovaného seznamu řádků (README uniforma
178
+ šablony i všech fixtur, `api/templates/business-service/README.md`) přegenerována v témže commitu.
179
+
180
+ ## [10.0.0] — 2026-09-16
181
+
182
+ ### Removed — `MockStorage` (d.583)
183
+
184
+ Dvojník bez konzumenta (po d.576) smazán i se souborem a vlastním testem; `MockMQClient`/`MockRegistry` zůstávají
185
+ (interní čtenáři), povrch exportů beze změny (17 jmen).
186
+
187
+ ### Changed — `tests/cookbooks` je podmínka bootu, ne doporučení (d.584)
188
+
189
+ Chybějící nebo prázdný adresář = error `MISSING_COOKBOOKS` (severity boot; orchestrátor fail-fast na kroku 1) místo dvou
190
+ warningů, které nechaly absenci projít až k odmítnutému důkazu. `tests/unit`/`tests/integration` zůstávají doporučením
191
+ (uniforma jejich existenci nevymáhá). Jedna kolej s F-DOCKERIGNORE (`!tests/cookbooks`). Kaskáda: žádná — 8/8 služeb recepty má.
192
+
193
+ ### Changed — F-DOCKERIGNORE vylučuje testovací strom, `tests/cookbooks` zůstává v obrazu (d.560)
194
+
195
+ `tests/` všech 8 služeb jelo do produkčního obrazu (`COPY . .`, ingest 632 K). Pravidlo `dockerignoreEntries()` = gitignore ∪
196
+ {`tests`, `**/tests`} ∖ {`tests/cookbooks`}: testy do artefaktu nepatří (analogie `L-PACK-TESTS`), cookbooky jsou deklarace
197
+ Tier-1, které boot čte ve fázi 0.2 — obraz bez nich nenaběhne (`ValidationProofGenerator` odmítne důkaz s `testsRun 0`).
198
+ Blok `oa-dockerignore v1` je výstup pravidla; ověřeno skutečným buildem (BuildKit `!tests/cookbooks`). Běžec netrpí (bind mount).
199
+ Kaskáda: služby přegenerují `.dockerignore` (`oa-sync-template`).
200
+
201
+ ### Fixed — šablona `jest.config.js` má `testTimeout` 120 000 ms (d.580)
202
+
203
+ 30 s na sdíleném dev stroji (load 47–94, běžci víc služeb) shazovalo `beforeAll` sad, které samostatně projdou (BIZ-converter);
204
+ limit má chytat zaseknutý test, ne frontu na procesor. `maxWorkers: 1` beze změny (konf `biz-memory-limits` 001). Řádek F-JEST
205
+ generovaný → služby přegenerují `jest.config.js`.
206
+
207
+ ### Removed — koncept `mockInfrastructure` (d.576)
208
+
209
+ Volba konstruktoru `CookbookTestRunner` i pole `test.mockInfrastructure` v receptu se odmítají JMÉNEM (hlášky s `Fix:`,
210
+ recept navíc se jménem souboru); `src/index.js` nepublikuje `MockMQClient`/`MockRegistry`/`MockStorage` ani `createMock*`
211
+ (surface 23 → 17 jmen). Krok se dispatchuje in-process proti vlastnímu v3 handleru, DB je skutečná — `this.mqClient`/
212
+ `this.registry` nikdo nečetl, mimo balíček 0 konzumentů (konf `declaration-removal` 001). Nahrazuje řádek „Documentation —
213
+ `mockInfrastructure` staví nápodobu MQ a registry" (d.521). DOPAD kaskády: recepty s tím polem (converter `ping.json`, meta 2,
214
+ hello 12) a volba v bootstrap testech (converter, ingest, hello, emailer) prevalidaci shodí, dokud služba pole neodstraní.
215
+
216
+ ### Fixed — agregát běhu sčítá jen změřené hodnoty (d.576b)
217
+
218
+ `results.totalTests/passedTests/failedTests` přes `readStepMeasurement()`; `|| 0` byl zdroj neměřené nuly, kvůli které
219
+ odmítnutí generátoru (d.524) nikdy nevystřelilo. Změřená nula zůstává nulou. Smazán mrtvý `createEncodedProof` v testu.
220
+
221
+ ### Fixed — throwaway schéma odmítne příkaz kvalifikovaný cizím schématem (d.538)
222
+
223
+ `<schema>.<tabulka>` jiného než throwaway schématu poslal příkaz testu do živého schématu (změřeno: řádek přistál v živé
224
+ tabulce). Jediná výjimka je read-only `information_schema` (22 živých migrací ve 4 repech). Úvodní `USE` se dál stripuje —
225
+ standard `repository-installation-sql-contract.md` o `USE` neříká nic; zpřísnění patří vlastníkovi standardu.
226
+
227
+ ### Fixed — hlavičková brána instalačního kontraktu kryje i `database.seeds` mimo `migrations/**` (d.541)
228
+
229
+ Konf `installation-sql-contract-scope` 003: rozsah hlavičky je, co instalátor aplikuje. Deklarovaný chybějící seed =
230
+ nález, ne ENOENT. Změřeno nad 8 repy: žádné nezčervená (ingest opravil `bfd04b9`).
231
+
232
+ ### Fixed — každá NOT RUN věta nese nápravu, padlý recept své jméno, `docsLintBridge.js` je zase text (d.523)
233
+
234
+ `biz-ci-gate.js` hlásil u odmítnutého receptu `step_id: undefined`; 5× `describeNotRun` a `runManifest` bez `Fix:` →
235
+ jeden vlastník `describeWorkspaceFix`; duplicitní blok a NUL v `docsLintBridge.js`; `uniformFiles.js` lepil druhý `Fix:`.
236
+ `biz-ci-gate.js` má mód 755 jako ostatní CLI (`bin` + shebang).
237
+
238
+ ### Changed — validační důkaz píše výhradně `ValidationProofGenerator`, i na bootovací cestě (d.524)
239
+
240
+ Druhá kolej v `ValidationOrchestrator` mohla zapsat důkaz s `testsRun: 0`; jméno a verze validátoru se čtou z vlastního
241
+ `package.json` a nelze je přebít (`validatorVersion || '1.0.0'` pryč). Služba bez receptů v `tests/cookbooks/` validaci
242
+ neprojde (dřív psala důkaz, který registry stejně odmítl jako NO_TESTS) — všech 8 biz rep recepty má.
243
+
244
+ ### Removed — `ValidationProofGenerator.createValidationError()` (d.524)
245
+
246
+ Bez konzumentů.
247
+
248
+ ### Fixed — ctx Tier-1 běžce nese `step_id`, jako produkční `OperationContext` (d.549)
249
+
250
+ Content-resolver 4.0.1 `createDescriptor` bez `step_id` odmítá; producent tak mohl přejít na `ctx.step_id` až teď (BIZ-pdfgen).
251
+
252
+ ### Documentation — balíčková kopie `shared.env` má čtenáře; zapsáno k otázce d.536 (d.557)
253
+
254
+ `renderTree` → `oa-sync-template template` → `templateMirror` drží zrcadlo `api/templates/business-service` byte za bytem; zrcadlo
255
+ čtou `biz-deploy-uniform-gate.bats:165` (kopie do fixtury), `redis-requirepass.bats:51,55,164` (kontrakt `REDIS_URL`) a
256
+ `shared-env-sync.bats`. d.536 ukončilo jen čtení VERDIKTEM (`G-SHARED-ENV` renderuje SSOT, `runNew` přepisuje živým renderem).
257
+ Soubor zůstává, šablona má dál 25 souborů.
258
+
259
+ ### Added — řádky `S-UNIT-C` a `S-COOK-C`: každý běžcový skript šablony má svůj řádek (d.527)
260
+
261
+ Šablona nesla `test:container` a `test:cookbooks:container` bez řádku manifestu, converter měl `test:bootstrap/unit/integration/
262
+ all/cookbooks:container`, property žádný `*:container` — mapa vydání jmenovala příkaz, který ve službě neexistoval (BIZ-converter
263
+ nález 25). Šablona = výstup řádků: `test:unit:container` (`S-UNIT-C`), `test:cookbooks:container` (`S-COOK-C`) vedle `S-ALL-C`/`S-INT-C`;
264
+ výjimka pro běžcový skript bez řádku zrušena. Kaskáda: 5 služeb přejmenuje `test:container` → `test:unit:container`, hello a pdfgen doplní
265
+ `test:cookbooks:container`.
266
+
267
+ ### Changed — env šablona služby nedeklaruje `SERVICE_NAME` (d.539)
268
+
269
+ Identitu vlastní `config/service/config.json` (`ServiceWrapper._serviceName()`, předává se do `MonitoringConnector.init({serviceName})`);
270
+ klíč v env nečetl nikdo, emailer a pdfgen bez něj běží (BIZ-emailer, BIZ-invoicing). Komentář k `MARIADB_MIGRATION_USER/PASSWORD` říká
271
+ hostitelský kontrakt (`env-active/<svc>.env` na boxu, konf `db-migrations-first-deploy` 001 fáze 1); README šablony říká, že F-JEST ruší
272
+ lokální `setupFiles` (env bere běžec z `env_file`).
273
+
274
+ ### Changed — `oa-sync-template` chybějící blok `init.sh` vloží (d.552)
275
+
276
+ Řádek F-INIT jen hlídal existující blok a chybějící kázal opsat ručně (`BLOCKED … paste the block from the template once`, BIZ-invoicing);
277
+ hláška „udělej to ručně" u generovaného obsahu je defekt. `insertBlockAfterFunction` + `F-INIT.insert_after_function`: bez kotvy odmítne s `Fix:`.
278
+
279
+ ### Removed — scaffold už nerenderuje `scripts/verify-deploy-uniform.sh` do služby (d.555)
280
+
281
+ Oba CI joby ho od d.529 volají z pinovaného balíčku; soubor v repu služby nečetl nikdo. `PACKAGE_ONLY` v renderu, zrcadlo
282
+ `api/templates/business-service/scripts/` smazáno; šablona má 25 souborů.
283
+
284
+ ### Fixed — `GIT_CLONE_PATH` uniformního běhu má vlastní slot runneru (d.556)
285
+
286
+ `validate-uniform` běží na každém pipelinu; dva souběžné pipeliny na jednom runneru sdílely build adresář →
287
+ `$CI_BUILDS_DIR/oa-uniform/$CI_CONCURRENT_ID/api_biz/<svc>`.
288
+
289
+ ### Added — job `validate-uniform` v generovaném bloku `oa-ci v1`: uniforma běží na každém pipelinu (d.529)
290
+
291
+ Konfirmace `biz-service-manifest` 010 (vlastník 2026-09-16): 008 říká, KDE brána musí být před nasazením, ne že
292
+ se jinde nepouští. Job `validate-uniform` (stage `test`, rules MR/main/devel/production) klonuje `api` a spouští
293
+ celou uniformu biz služby; `deploy-production` zůstává závaznou poslední instancí. `secret_detection` běží i na
294
+ `devel`, kam post-commit zrcadlo posílá každý commit (dosud `pipelines?ref=devel` = 0). `build` na `devel` záměrně
295
+ NEběží — 010 žádá kontrolu z každého commitu, ne obraz; `IMAGE_LATEST` z devel by přepsal `latest` platformy.
296
+
297
+ ### Changed — oba běhy uniformy volají `verify-deploy-uniform.sh` z pinovaného balíčku (d.529)
298
+
299
+ `scripts/verify-deploy-uniform.sh` neexistoval v žádném z 8 biz repozitářů (jen v šabloně) → `deploy-production`
300
+ padal na prvním kroku `script:`. Oba joby ho volají z `node_modules/@onlineapps/conn-orch-validator/templates/
301
+ business-service/scripts/` — brána i engine z jednoho pinu, žádná kopie do 8 rep; klon `api` přes `$API_CHECKOUT`
302
+ nejde, skript ho sám maže. Rozvržení a runtime obou jobů deklaruje jednou skrytý klíč `.oa-uniform` (`extends`).
303
+ Řádek `G-CI`: `why` přepsáno na dnešní stav (šablona od d.470 nenese `verify-installation-contract:`), `doc` → 010.
304
+
305
+ ### Changed — `G-SHARED-ENV` čte SSOT `api/config/shared-env.json` ve workspace, ne render zabalený v balíčku (d.536)
306
+
307
+ Ingest, emailer, hello, invoicing, pdfgen po dovydání 2: řádek porovnával s renderem zabaleným v 9.0.0 (d.229),
308
+ `oa-sync-template shared-env` renderoval z živého SSOT (d.500/d.510) → služba nemohla být zároveň „synced" i
309
+ DEPLOYABLE („10 line(s) differ"). Řádek renderuje SSOT týmž rendererem jako sync; bez dosažitelného workspace
310
+ NOT RUN s `Fix:` (v deploy jobu je `api` naklonované, 008). Kaskáda: každá biz služba po pinu spustí
311
+ `oa-sync-template readme-uniform --target .` (region README nese `from` řádku).
312
+
313
+ ### Changed — `oa-validate .` odvozuje kořen workspace od kořene služby (d.540)
314
+
315
+ Z `<workspace>/api_biz/<svc>` bez `--workspace` hlásil validator instalovaný v `node_modules` služby
316
+ `Workspace root: NOT RESOLVED` (kořen hledal jen od balíčku) → U-*/D-* NOT RUN. Třetí otázka: nejbližší předek
317
+ kořene služby s `api/config/services.json`; explicitní `--workspace` dál vyhrává.
318
+
319
+ ### Fixed — F-RUNNER porovnává směrem render; zpětná substituce jména služby zrušena (d.531)
320
+
321
+ `normalizeRunnerBlock` nahrazovala jméno služby v bloku zpět na `__SERVICE_NAME__`, takže u služby jménem `meta`
322
+ přepsala i literál v komentáři šablony (`api_biz/meta/docker-compose.yml carries the full numbers`) a řádek hlásil
323
+ nález, zatímco `oa-sync-template docker-compose.yml --check` říkal `unchanged`. Jedna kolej = referenci vyrenderovat
324
+ jménem služby a porovnat (`renderRunnerIdentity`); `normalizeRunnerBlock` smazána (jediný čtenář byl tento řádek).
325
+
326
+ ### Changed — důkaz validace odmítá neměřenou nulu; kontrakt odmítá `db: true` bez bloku `database` (d.521)
327
+
328
+ `ValidationProofGenerator.generateProof()` čte `durationMs`, `testsRun`, `testsPassed`, `testsFailed` z agregátu
329
+ runneru JMÉNEM a chybějící měření odmítne místo doplnění `0`; běh, který nic nespustil (`testsRun === 0`), je
330
+ odmítnut u autora, ne až v registru (`ValidationProofCodec.decode()` = NO_TESTS). Integrační kontrakt odmítá
331
+ `requiredConnectors.db: true` bez bloku `database` — směr, který přechodová výjimka F4 nechala otevřený
332
+ (shoda hotová: 7/8 služeb má obojí, pdfgen ani jedno, 0 repozitářů nese `ci-setup-db.js`).
333
+
334
+ ### Removed — `ValidationProofGenerator.extractTestSummary()` (d.521)
335
+
336
+ Nula konzumentů od 98d155f1 (2025-09-30); jediné pole nad agregátem, `coverage`, na platformě nic neměří.
337
+
338
+ ### Documentation — `mockInfrastructure` staví nápodobu MQ a registry, databázi ne (d.521)
339
+
340
+ README § „What `mockInfrastructure` covers — and what it does not": `run-prevalidation` běží proti skutečnému
341
+ schématu každé služby, která ho deklaruje, a patří až za `ci:gate:setup`; pokrytou množinu připíná test.
342
+
343
+ ### Fixed — každá NOT RUN věta nese svou nápravu, každý padlý záznam svou identitu (d.516, d.516b, d.518)
344
+
345
+ Jediný vlastník věty `Fix: run with --workspace pointing at a checkout that carries …` je
346
+ `manifest/workspaceRoot.js` § `describeWorkspaceFix`; berou ji `docsLintBridge` (D-PORT, D-RETIRED, D-LINT),
347
+ `libraryWorkspace.js` § `notRunBecause` (U-ORPHAN, U-MISMATCH, L-PINS, L-CONSUMER, L-TOOLING) a `readmeRegion.js`
348
+ (L-README-REGION). `X-IGNORED` nad stromem bez `.git` jmenuje vlastní nápravu — běh nad klonem, ne nad exportem.
349
+ `results.steps` runneru nese dva druhy záznamů (`kind: step` / `cookbook-load-failure`): odmítnutý recept se hlásí
350
+ jako recept (soubor + chyba), krok bez `step_id` je chyba §5, `validateCookbook` vyžaduje `step_id` jako cookbook-core 5.0.0.
351
+
352
+ ### Changed — kontrola se scope vyžadujícím workspace vlastní svou NOT RUN větu (d.518)
353
+
354
+ `runManifest.js` už nepůjčuje výchozí větu „the workspace root is not reachable"; kontrola bez `describeNotRun`
355
+ běh odmítne na každém běhu. Na výchozí větu nedosahovala žádná registrovaná kontrola (`automation-gates.md` §5).
356
+
357
+ ### Fixed — `results.steps` říká, co každý záznam je; krok bez `step_id` se neobejde pojmenováním (d.516b)
358
+
359
+ `results.steps` nese dva druhy záznamu a nejsou to oba kroky: výsledek kroku, a záznam, který
360
+ `CookbookTestRunner.runCookbooks` uloží za recept, jejž formátová kontrola odmítla — celý recept, který
361
+ žádný krok nevydal. Splývaly, takže odmítnutý soubor hlásil krok, který neexistuje:
362
+ `cookbook "broken.json" step "step #1": … cookbook format version is missing`. Každý záznam teď svůj druh
363
+ **deklaruje** (`kind: 'step'` / `kind: 'cookbook-load-failure'`) tam, kde vzniká, a nikdo ho nedovozuje
364
+ z chybějícího pole (`architecture-principles.md` §8). Nenačtený recept se hlásí jménem souboru:
365
+ `cookbook "broken.json" could not be run: …`.
366
+
367
+ Teprve na tom stojí druhá polovina: `utils/stepFailure.js` § `describeStepIdentity` **odmítne** záznam
368
+ kroku bez `step_id` místo aby ho pojmenoval pozicí (`step #3 (operation: convert)`). Ta náhrada byla
369
+ správná odpověď, dokud se tvar usazoval — alternativou bylo `undefined`. Dnes je `step_id` povinné od
370
+ `@onlineapps/cookbook-core` 5.0.0 a orchestrátor od d.460 odmítne task krok bez operace
371
+ (`_requireStepOperation`), takže krok bez `step_id` je vadný recept a pravděpodobné jméno ho čte jako
372
+ v pořádku (§3). Pozice zůstává v hlášce — jako to, čím se vadný krok najde, ne jako jeho jméno (§5).
373
+
374
+ `CookbookTestRunner.validateCookbook` nově žádá `step_id` jako první ze tří polí, která cookbook-core 5.0.0
375
+ u task kroku vyžaduje; tím zmizel i `Step ${step.step_id || 'unknown'}` z hlášek o `service`/`operation` —
376
+ případ, který zastupoval, už nenastane. Ověřeno pozitivní kontrolou nad 43 ostrými recepty
377
+ (`api/templates/business-service` + `api_biz/*/tests/cookbooks`): žádný krok bez `step_id`.
378
+
379
+ ### Fixed — NOT RUN „workspace není dosažitelný" nese týž Fix (d.516b)
380
+
381
+ `checks/docsLintBridge.js` § `describeNotRun` (obě kontroly) končil u toho, že workspace není — druhý kanál
382
+ vedle toho, který zavřela d.516, a týž nedostatek (`automation-gates.md` §1 požadavek 4). Věta pochází
383
+ z téhož vlastníka, `workspaceRoot.js` § `describeWorkspaceFix`. Ostatní `describeNotRun` (`readmeRegion.js`,
384
+ `libraryWorkspace.js`) a výchozí hláška v `runManifest.js` Fix stále nenesou — nahlášeno, neopraveno.
385
+
386
+ ### Fixed — most k dokumentačnímu lintu píše u NOT RUN týž Fix jako chybějící kořen (d.516)
387
+
388
+ `checks/docsLintBridge.js` skládal větu NOT RUN z vlastního textu a končil tam, kde skončil linter:
389
+ `F002:http-ports could not be decided — api_biz/*/docker-compose.yml lives in a sibling checkout this run
390
+ does not have, so the probe could not be evaluated`. Pravda, a nic, co může čtenář udělat — přitom ve
391
+ **stejném běhu** a o **témže chybějícím adresáři** řekl `U-ORPHAN` příkaz (`automation-gates.md` §1
392
+ požadavek 4). Dvě věty pro jednu nápravu jsou druhá kolej (`change-discipline.md` § One rail per concern).
393
+
394
+ Příkaz má teď jednoho vlastníka, `workspaceRoot.js` § `describeWorkspaceFix`; `runManifest.js`
395
+ § `describeMissingRoots` (d.511) i obě kontroly mostu (`D-PORT`/`D-RETIRED`/`D-LINT`) berou větu odtud.
396
+ Co musí checkout nést, zůstává na volajícím: řádek své kořeny deklaruje a jmenuje, kdežto most dostává
397
+ text od linteru, jehož kanál `skipped` nese jen `{ rule, reason }` (`api/scripts/ci/lint-biz-docs.mjs`
398
+ § `markSkipped`) — parsovat tu větu by byl soukromý dialekt mezi řádkem a nástrojem, jmenovat sourozence
399
+ zde by z mostu udělalo druhého vlastníka konfigurace prób. Měřeno nad `git archive HEAD` do adresáře bez
400
+ `api_biz`; kontrolní případ: nad workspace, který `api_biz` nese, nejde žádný `D-*` řádek do NOT RUN.
401
+
402
+ ### Fixed — `runPreValidation` nehlásí padlý krok pod jménem jeho operace (d.516)
403
+
404
+ `utils/preValidation.js` skládal seznam padlých kroků jako `step_id: step.step_id || step.operation`, takže
405
+ krok bez `step_id` dorazil ke čtenáři výsledku pod **jménem operace** — pole se jmenuje `step_id` a neslo
406
+ něco, co jím není, bez jakékoli stopy po záměně (`architecture-principles.md` §3). Od
407
+ `@onlineapps/cookbook-core` 5.0.0 je `step_id` povinné (`schemas/cookbook.v2.schema.json`
408
+ definitions.TaskStep.required jmenuje step_id, type, service, operation) a od d.460 orchestrátor odmítne
409
+ task krok, který nejmenuje operaci (`_requireStepOperation`), takže krok bez `step_id` je vadný recept —
410
+ a vadný recept má výsledek ukázat, ne ho přejmenovat. Kontrolní případ: krok s oběma poli hlásí své
411
+ vlastní `step_id` beze změny.
412
+
413
+ ### Fixed — NOT RUN chybějícího sourozence nese svůj Fix (d.511)
414
+
415
+ `describeMissingRoots` (`src/manifest/runManifest.js`) hlásil, CO chybí, a nikdy CO s tím; věta s příkazem
416
+ se tiskla jen u čistého verdiktu (`report.js` § `describeClearOutcome`), takže běh, který zároveň něco
417
+ našel, Fix neukázal nikde. Příkaz je odvozen z chybějících kořenů. Měřeno nad exportem
418
+ `api_biz/hello-service` vedle klonu api bez `api_biz/`; v rozvržení deploy brány (`<root>/api_biz/<služba>`
419
+ vedle `<root>/api`) je `notRun` prázdné — U-ORPHAN i R-NODE se rozhodnou (R-NODE i bez `api_biz`, d.238).
420
+
421
+ ### Added — úplnost manifestu proti šabloně v obou směrech (d.511, nález 18)
422
+
423
+ `tests/unit/templateCoverage.test.js` žádal řádek nebo třídu `own` pro každý soubor šablony; opačný směr
424
+ mlčel — `describeFromProblem` kontroluje tvar reference `from:`, nikdy existenci souboru, takže přejmenovaný
425
+ soubor šablony nechal balíček zelený a spadl až v cizím repu (`[ManifestDiscovery] Packaged file not found`).
426
+ Nově každá `from: { package }` jmenuje soubor, který balíček nese, a každý `files.own.allowed` má v šabloně
427
+ něco za sebou. Rozdíl obou množin je dnes nulový — bez baseline, bez výjimky.
428
+
429
+ ### Fixed — `sharedEnv.js` neslibuje kontrolu pole `consumers`, kterou nic nedělá (d.510)
430
+
431
+ Komentář nad `renderSharedEnv` tvrdil, že kdo klíč čte, je „fakt, který kód vlastní a kontrola
432
+ měří". Neměří: pole `keys[].consumers` nečte žádný kód tohoto balíčku (`grep -rn consumers src/`
433
+ vrací jen `MockMQClient` a jednu větu komentáře) a žádná brána jinde — manifest
434
+ `api/config/shared-env.json` to sám říká od d.505 (`_consumers`: „NOTHING measures it").
435
+ Slib mechanismu, který neexistuje, je přesně vada z `doc-code-binding.md` §5: odstavec se čte
436
+ jako krytý, takže se nikdo nepodívá.
437
+
438
+ Komentář teď říká obě poloviny pravdivě: **měřená** je jen ta, že se `consumers` nerenderuje —
439
+ kontrolní případ v `tests/unit/sharedEnv.test.js` polem hne a tvrdí, že vykreslený text se
440
+ nehne; obsah pole drží **review**; a strojová odpověď na „kdo tento název čte" je per služba,
441
+ řádek `C-ENV-READS` (check `env-contract`), který porovnává názvy čtené kódem služby s jejím
442
+ `config/service/integration-contract.json`. Renderer se nezměnil o řádek.
443
+
444
+ ### Changed — `LOG_LEVEL` v zabalené šabloně říká, kdo ho čte a kdo ne (d.510)
445
+
446
+ Klíč nese komentář „the log level an infra service's config/logging.json resolves through
447
+ `${LOG_LEVEL}`" — pravdivý o infrastruktuře, mlčící o biz řetězci, kde ho **nečte nikdo**:
448
+ v `shared/*/src` je 66 čtení `process.env` a ani jedno tohoto názvu, `@onlineapps/monitoring-core`
449
+ nečte prostředí vůbec (princip 1) a úroveň bere z konfigurace, kterou dostane. Čtenář biz
450
+ `shared.env` tak mohl klíč přenastavit a čekat účinek, který mít nemůže.
451
+
452
+ Klíč zůstává v jedné sdílené sadě — každá kopie je bajtově shodná s platformní šablonou
453
+ (konfirmace `biz-service-manifest` 003 §18, akceptace bod 4), takže odebrat ho jen biz nositelům
454
+ by znamenalo druhou sadu, tedy změnu konceptu, ne opravu věty. Opravena je věta: `why` nově
455
+ jmenuje infrastrukturní kolej i to, že v biz řetězci je klíč **nesen, ne čten**, a proč tam
456
+ přesto je. `consumers` je srovnán s měřením (přibyly `api_monitoring` a `api_meta_reader`, které
457
+ `LOG_LEVEL` čtou — `src/consumer/config/logging.json`, `src/config.js`).
458
+
459
+ Dopad na konzumenty: vykreslený komentář klíče se změnil, takže `oa-validate` nad biz službou,
460
+ která svůj `shared.env` nepřegenerovala, nahlásí `G-SHARED-ENV`. Náprava je týž jeden běh jako
461
+ u d.500: `npx oa-sync-template shared-env --target .`.
462
+
463
+ ### Fixed — `oa-sync-template` neplánuje řádky, ze kterých nemá co renderovat (d.507)
464
+
465
+ `npx oa-sync-template --target . --check` končil v každém biz repu dvěma řádky, které nešlo
466
+ vyřešit: `NOT RUN G-PROD-IMAGE …` a `NOT RUN G-SETUP docs/80-setup/ - the row declares no
467
+ "from" reference`. Oba řádky manifestu žádnou `from:` referenci nedeklarují, protože to nejsou
468
+ soubory renderované z reference — `G-PROD-IMAGE` je požadavek NA obsah souboru (pin digestem),
469
+ `G-SETUP` požadavek na existenci adresáře; odpovídá na ně běh manifestu (`npx oa-validate`).
470
+ Hlášení, se kterým čtenář nemůže nic udělat, je falešná záruka (`automation-gates.md` §5) a
471
+ hláška bez `Fix` (§1 požadavek 4).
472
+
473
+ Množina řádků synchronizace se nově bere podle toho, co manifest už dnes rozlišuje: řádek
474
+ s `from:` referencí je soubor, který běh renderuje (`syncRows`), řádek bez ní se neplánuje
475
+ vůbec. Rozhoduje reference, ne seznam id — nový požadavkový řádek tedy nevyžaduje změnu kódu.
476
+ `uniformRows` zůstává celou deklarací tříd `identical`/`generated`/`contains`, takže CLI umí
477
+ odmítnout `docs/80-setup/` jako požadavek (se jménem řádku a během, který na něj odpovídá), ne
478
+ jako překlep.
479
+
480
+ `NOT RUN` zůstává pro svůj jediný případ: řádek, který běh renderovat MÁ a nedosáhne na svou
481
+ referenci (reference do workspace, běh mimo workspace) — nově i s `Fix: … --workspace <root>`.
482
+ Chování řádků s referencí se nemění (kontrolní případ v `tests/unit/uniformFilesSync.test.js`).
483
+
484
+ Dopad na konzumenty: výstup `--check` nad konformním repem je o dva řádky kratší a neobsahuje
485
+ žádné `NOT RUN`; `oa-sync-template docs/80-setup/ --target .` nově končí exit 2 s vysvětlením
486
+ místo NOT RUN.
487
+
488
+ ### Changed — zabalená šablona `shared.env` nese dvě meze souborového logu (d.500)
489
+
490
+ `@onlineapps/monitoring-core` 3.0.0 vyžaduje `file.maxSize` a `file.maxFiles` bez defaultu
491
+ v kódu (`src/logger.js` § `REQUIRED_FILE_BOUNDS`) — služba, která je nedeklaruje, spadne při
492
+ konstrukci loggeru. Hodnoty rozhodl vlastník: `LOG_MAX_SIZE_BYTES=52428800`, `LOG_MAX_FILES=10`
493
+ (`api/docs/governance/confirmations/log-file-bounds.md` 001). Klíče přibyly do manifestu
494
+ `api/config/shared-env.json`, který je vlastníkem klíčové sady, a tím i do zabalené kopie
495
+ `templates/business-service/config/env-templates/shared.env`, kterou čte řádek `G-SHARED-ENV`.
496
+
497
+ Dopad na konzumenty: `oa-validate` nad biz službou, která klíče ve svém `shared.env` nemá,
498
+ nahlásí `G-SHARED-ENV`. Náprava je jeden běh: `npx oa-sync-template shared-env --target .`.
499
+
500
+ ### Added — `resolveMigrationPlan` na veřejném API balíčku (d.492)
501
+
502
+ Migrační plán (které `.sql` soubory tvoří množinu a v jakém pořadí) byl dostupný jen z vnitřní
503
+ cesty `@onlineapps/conn-orch-validator/src/utils/setupDatabase`. Cesta dovnitř balíčku není
504
+ kontrakt: každý přesun v `src/utils/` rozbije konzumenta, který na nic takového nepřistoupil.
505
+ Funkce je nově na `src/index.js` jako **tatáž** funkce (test `toBe` proti vnitřnímu modulu), ne
506
+ obal — jedna definice, jedno chování (`change-discipline.md` § One rail per concern). Množina
507
+ exportů se jinak nemění; kontrolní případ v `tests/unit/indexExports.test.js` porovnává celý
508
+ seznam klíčů se snímkem před změnou.
509
+
510
+ Biz repa mají přejít na veřejnou cestu `require('@onlineapps/conn-orch-validator')`; přechod
511
+ v jednotlivých repech patří jejich vláknům (vnitřní cestou dnes importují
512
+ `api_biz/converter/tests/integration/integrationEnv.js` a tři soubory v `api_biz/emailer/tests/`).
513
+
514
+ ### Fixed — brána test-coverage hlásila PASS nad prázdnou množinou testů (d.486, nález d.205)
515
+
516
+ Prázdný match je jestí exit 0 bez výstupu a každé porovnání brány iteruje množinu, takže nad
517
+ prázdnou nenašlo protipříklad („0 file(s) matched, 0 run by the test:all chain"). Totéž o krok
518
+ níž — krok řetězu `test:all` mířící do prázdného adresáře. Obojí je nález `TEST_SET_EMPTY` se
519
+ jménem skriptu i vzoru; hlásí se jen tam, kde je kořenem (prázdné T a `TEST_NOT_MATCHED` umlčují
520
+ kontrolu jednotlivých kroků). `TEST_COVERAGE_SCOPE` nové měření jmenuje.
521
+
522
+ ### Fixed — R6 odmítá `^`/`~`/`latest` v každé `@onlineapps/*` závislosti, i biz-only (d.485)
523
+
524
+ Kontrola exaktnosti pinu (`libCompat.js`) seděla uvnitř větve `gated`, takže balíček, který
525
+ žádná infra služba neinstaluje, mohl plavat pod zelenou branou (W413). Zúžení podle
526
+ `infraConsumed` dál rozhoduje jen o tom, co se porovnává s infra verzí; exaktní pin platí pro
527
+ každou `@onlineapps/*` závislost bez výjimky (`architecture-principles.md` § Version pinning).
528
+ Hláška je pro gated i biz-only jedna a nese `Fix: npm install <pkg>@<verze> --save-exact`;
529
+ kontrola existence v SSOT předchází kontrole exaktnosti, aby `Fix:` mohl verzi jmenovat.
530
+
531
+ ### Added — pravidlo `S010`: invertované tvrzení v bats netvrdí nic (d.482)
532
+
533
+ `lintScripts` čte nově i **kódové** řádky testů a hlásí příkaz s invertovaným návratovým kódem
534
+ ve dvou tvarech, které bats nese: `! cmd` samostatně a `cmd || ! cmd`. Změřeno na bats 1.13.0:
535
+ takový příkaz je vyjmut z `errexit`, takže pod `set -e` **nezpůsobí pád testu**, pokud zrovna
536
+ není posledním příkazem těla — řádek vypadá jako tvrzení a tvrzením je jen náhodou polohy, kterou
537
+ libovolný vložený řádek pod ním tiše zruší. Pravidlo proto hlásí **každý** výskyt a nerozlišuje
538
+ poslední příkaz. Oprava je podmíněný tvar, který návratový kód spotřebuje záměrně a řekne, co našel
539
+ (`if <co nesmí platit>; then echo "[ctx] problem - found: …" >&2; return 1; fi`); `if ! cmd; then … fi`
540
+ je právě ta oprava, ne vada, a nehlásí se. Brána přistává nad již čistým rozsahem — 0 nálezů
541
+ ve 146 souborech, 32 výskytů platformy přepsáno předem — bez baseline a bez skip flagu
542
+ (`automation-gates.md` §3). Norma: `api/docs/standards/SCRIPTS-STANDARD.md` § Enforcement.
543
+
544
+ ### Changed — rozsah citací je `tests/**`, ne jen `tests/scripts/` (d.482)
545
+
546
+ `CITATION_SCOPE` je nově `tests` (rekurzivně; přípony `.bats`/`.bash`/`.sh` beze změny), a platí pro
547
+ `S009` i `S010`. Adresář, ve kterém se `S009` narodilo, není jediný, který testy drží: mimo něj leží
548
+ 12 shellových souborů (`tests/scripts-docker`, `tests/e2e/helpers`, `tests/fixtures/…`). Pravidlo, které
549
+ čte jeden adresář a sousední ne, má díru, kterou nikdo nevidí — běh ohlásí `0 finding(s)` a nikdy neřekne,
550
+ které soubory neotevřel (`automation-gates.md` §5). Rozsah byl před rozsířením změřen čistý pro obě
551
+ pravidla, takže ani zde není baseline. Jméno konstanty se nemění — exportuje ji CLI do svých hlášek.
552
+
7
553
  ## [9.0.0] — 2026-09-15
8
554
 
9
555
  ### Removed — šablona už nenese job `verify-installation-contract` (d.470)