@onlineapps/conn-orch-validator 9.0.0 → 10.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 (49) hide show
  1. package/CHANGELOG.md +373 -0
  2. package/README.md +83 -9
  3. package/docs/DESIGN.md +21 -7
  4. package/manifests/biz-service.manifest.json +28 -5
  5. package/package.json +2 -2
  6. package/src/CookbookTestRunner.js +84 -16
  7. package/src/ValidationOrchestrator.js +73 -20
  8. package/src/cli/biz-ci-gate.js +28 -14
  9. package/src/cli/oa-sync-template.js +23 -8
  10. package/src/cli/oa-validate.js +7 -1
  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 +12 -1
  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/serviceFiles.js +34 -7
  22. package/src/manifest/checks/serviceIdentityRows.js +3 -1
  23. package/src/manifest/checks/serviceRuntime.js +3 -1
  24. package/src/manifest/discovery.js +25 -7
  25. package/src/manifest/runManifest.js +58 -7
  26. package/src/manifest/workspaceRoot.js +91 -5
  27. package/src/sync/serviceTemplate.js +76 -7
  28. package/src/sync/sharedEnv.js +11 -4
  29. package/src/sync/uniformFiles.js +91 -21
  30. package/src/utils/bizCiGateContract.js +25 -1
  31. package/src/utils/installContract.js +46 -5
  32. package/src/utils/libCompat.js +39 -19
  33. package/src/utils/preValidation.js +56 -11
  34. package/src/utils/stepFailure.js +106 -19
  35. package/src/utils/testCoverageContract.js +60 -2
  36. package/src/utils/throwawaySchema.js +92 -7
  37. package/src/validatorIdentity.js +31 -0
  38. package/src/validators/ServiceStructureValidator.js +41 -15
  39. package/src/validators/ValidationProofGenerator.js +73 -34
  40. package/templates/business-service/.dockerignore +9 -1
  41. package/templates/business-service/.gitlab-ci.yml +91 -25
  42. package/templates/business-service/README.md +14 -5
  43. package/templates/business-service/config/env-templates/__SERVICE_NAME__.env +17 -5
  44. package/templates/business-service/config/env-templates/shared.env +7 -1
  45. package/templates/business-service/docs/80-setup/PLATFORM_MATRIX.md +1 -1
  46. package/templates/business-service/docs/80-setup/VALIDATION.md +1 -1
  47. package/templates/business-service/jest.config.js +9 -1
  48. package/templates/business-service/package.json.template +1 -1
  49. package/src/mocks/MockStorage.js +0 -188
package/CHANGELOG.md CHANGED
@@ -4,6 +4,379 @@ All notable changes to this package. Follows [Keep a Changelog](https://keepacha
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [10.0.0] — 2026-09-16
8
+
9
+ ### Removed — `MockStorage` (d.583)
10
+
11
+ Dvojník bez konzumenta (po d.576) smazán i se souborem a vlastním testem; `MockMQClient`/`MockRegistry` zůstávají
12
+ (interní čtenáři), povrch exportů beze změny (17 jmen).
13
+
14
+ ### Changed — `tests/cookbooks` je podmínka bootu, ne doporučení (d.584)
15
+
16
+ Chybějící nebo prázdný adresář = error `MISSING_COOKBOOKS` (severity boot; orchestrátor fail-fast na kroku 1) místo dvou
17
+ warningů, které nechaly absenci projít až k odmítnutému důkazu. `tests/unit`/`tests/integration` zůstávají doporučením
18
+ (uniforma jejich existenci nevymáhá). Jedna kolej s F-DOCKERIGNORE (`!tests/cookbooks`). Kaskáda: žádná — 8/8 služeb recepty má.
19
+
20
+ ### Changed — F-DOCKERIGNORE vylučuje testovací strom, `tests/cookbooks` zůstává v obrazu (d.560)
21
+
22
+ `tests/` všech 8 služeb jelo do produkčního obrazu (`COPY . .`, ingest 632 K). Pravidlo `dockerignoreEntries()` = gitignore ∪
23
+ {`tests`, `**/tests`} ∖ {`tests/cookbooks`}: testy do artefaktu nepatří (analogie `L-PACK-TESTS`), cookbooky jsou deklarace
24
+ Tier-1, které boot čte ve fázi 0.2 — obraz bez nich nenaběhne (`ValidationProofGenerator` odmítne důkaz s `testsRun 0`).
25
+ Blok `oa-dockerignore v1` je výstup pravidla; ověřeno skutečným buildem (BuildKit `!tests/cookbooks`). Běžec netrpí (bind mount).
26
+ Kaskáda: služby přegenerují `.dockerignore` (`oa-sync-template`).
27
+
28
+ ### Fixed — šablona `jest.config.js` má `testTimeout` 120 000 ms (d.580)
29
+
30
+ 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);
31
+ limit má chytat zaseknutý test, ne frontu na procesor. `maxWorkers: 1` beze změny (konf `biz-memory-limits` 001). Řádek F-JEST
32
+ generovaný → služby přegenerují `jest.config.js`.
33
+
34
+ ### Removed — koncept `mockInfrastructure` (d.576)
35
+
36
+ Volba konstruktoru `CookbookTestRunner` i pole `test.mockInfrastructure` v receptu se odmítají JMÉNEM (hlášky s `Fix:`,
37
+ recept navíc se jménem souboru); `src/index.js` nepublikuje `MockMQClient`/`MockRegistry`/`MockStorage` ani `createMock*`
38
+ (surface 23 → 17 jmen). Krok se dispatchuje in-process proti vlastnímu v3 handleru, DB je skutečná — `this.mqClient`/
39
+ `this.registry` nikdo nečetl, mimo balíček 0 konzumentů (konf `declaration-removal` 001). Nahrazuje řádek „Documentation —
40
+ `mockInfrastructure` staví nápodobu MQ a registry" (d.521). DOPAD kaskády: recepty s tím polem (converter `ping.json`, meta 2,
41
+ hello 12) a volba v bootstrap testech (converter, ingest, hello, emailer) prevalidaci shodí, dokud služba pole neodstraní.
42
+
43
+ ### Fixed — agregát běhu sčítá jen změřené hodnoty (d.576b)
44
+
45
+ `results.totalTests/passedTests/failedTests` přes `readStepMeasurement()`; `|| 0` byl zdroj neměřené nuly, kvůli které
46
+ odmítnutí generátoru (d.524) nikdy nevystřelilo. Změřená nula zůstává nulou. Smazán mrtvý `createEncodedProof` v testu.
47
+
48
+ ### Fixed — throwaway schéma odmítne příkaz kvalifikovaný cizím schématem (d.538)
49
+
50
+ `<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é
51
+ tabulce). Jediná výjimka je read-only `information_schema` (22 živých migrací ve 4 repech). Úvodní `USE` se dál stripuje —
52
+ standard `repository-installation-sql-contract.md` o `USE` neříká nic; zpřísnění patří vlastníkovi standardu.
53
+
54
+ ### Fixed — hlavičková brána instalačního kontraktu kryje i `database.seeds` mimo `migrations/**` (d.541)
55
+
56
+ Konf `installation-sql-contract-scope` 003: rozsah hlavičky je, co instalátor aplikuje. Deklarovaný chybějící seed =
57
+ nález, ne ENOENT. Změřeno nad 8 repy: žádné nezčervená (ingest opravil `bfd04b9`).
58
+
59
+ ### Fixed — každá NOT RUN věta nese nápravu, padlý recept své jméno, `docsLintBridge.js` je zase text (d.523)
60
+
61
+ `biz-ci-gate.js` hlásil u odmítnutého receptu `step_id: undefined`; 5× `describeNotRun` a `runManifest` bez `Fix:` →
62
+ jeden vlastník `describeWorkspaceFix`; duplicitní blok a NUL v `docsLintBridge.js`; `uniformFiles.js` lepil druhý `Fix:`.
63
+ `biz-ci-gate.js` má mód 755 jako ostatní CLI (`bin` + shebang).
64
+
65
+ ### Changed — validační důkaz píše výhradně `ValidationProofGenerator`, i na bootovací cestě (d.524)
66
+
67
+ Druhá kolej v `ValidationOrchestrator` mohla zapsat důkaz s `testsRun: 0`; jméno a verze validátoru se čtou z vlastního
68
+ `package.json` a nelze je přebít (`validatorVersion || '1.0.0'` pryč). Služba bez receptů v `tests/cookbooks/` validaci
69
+ neprojde (dřív psala důkaz, který registry stejně odmítl jako NO_TESTS) — všech 8 biz rep recepty má.
70
+
71
+ ### Removed — `ValidationProofGenerator.createValidationError()` (d.524)
72
+
73
+ Bez konzumentů.
74
+
75
+ ### Fixed — ctx Tier-1 běžce nese `step_id`, jako produkční `OperationContext` (d.549)
76
+
77
+ 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).
78
+
79
+ ### Documentation — balíčková kopie `shared.env` má čtenáře; zapsáno k otázce d.536 (d.557)
80
+
81
+ `renderTree` → `oa-sync-template template` → `templateMirror` drží zrcadlo `api/templates/business-service` byte za bytem; zrcadlo
82
+ čtou `biz-deploy-uniform-gate.bats:165` (kopie do fixtury), `redis-requirepass.bats:51,55,164` (kontrakt `REDIS_URL`) a
83
+ `shared-env-sync.bats`. d.536 ukončilo jen čtení VERDIKTEM (`G-SHARED-ENV` renderuje SSOT, `runNew` přepisuje živým renderem).
84
+ Soubor zůstává, šablona má dál 25 souborů.
85
+
86
+ ### Added — řádky `S-UNIT-C` a `S-COOK-C`: každý běžcový skript šablony má svůj řádek (d.527)
87
+
88
+ Šablona nesla `test:container` a `test:cookbooks:container` bez řádku manifestu, converter měl `test:bootstrap/unit/integration/
89
+ all/cookbooks:container`, property žádný `*:container` — mapa vydání jmenovala příkaz, který ve službě neexistoval (BIZ-converter
90
+ 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`;
91
+ 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í
92
+ `test:cookbooks:container`.
93
+
94
+ ### Changed — env šablona služby nedeklaruje `SERVICE_NAME` (d.539)
95
+
96
+ Identitu vlastní `config/service/config.json` (`ServiceWrapper._serviceName()`, předává se do `MonitoringConnector.init({serviceName})`);
97
+ klíč v env nečetl nikdo, emailer a pdfgen bez něj běží (BIZ-emailer, BIZ-invoicing). Komentář k `MARIADB_MIGRATION_USER/PASSWORD` říká
98
+ hostitelský kontrakt (`env-active/<svc>.env` na boxu, konf `db-migrations-first-deploy` 001 fáze 1); README šablony říká, že F-JEST ruší
99
+ lokální `setupFiles` (env bere běžec z `env_file`).
100
+
101
+ ### Changed — `oa-sync-template` chybějící blok `init.sh` vloží (d.552)
102
+
103
+ Řá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);
104
+ 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:`.
105
+
106
+ ### Removed — scaffold už nerenderuje `scripts/verify-deploy-uniform.sh` do služby (d.555)
107
+
108
+ 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
109
+ `api/templates/business-service/scripts/` smazáno; šablona má 25 souborů.
110
+
111
+ ### Fixed — `GIT_CLONE_PATH` uniformního běhu má vlastní slot runneru (d.556)
112
+
113
+ `validate-uniform` běží na každém pipelinu; dva souběžné pipeliny na jednom runneru sdílely build adresář →
114
+ `$CI_BUILDS_DIR/oa-uniform/$CI_CONCURRENT_ID/api_biz/<svc>`.
115
+
116
+ ### Added — job `validate-uniform` v generovaném bloku `oa-ci v1`: uniforma běží na každém pipelinu (d.529)
117
+
118
+ Konfirmace `biz-service-manifest` 010 (vlastník 2026-09-16): 008 říká, KDE brána musí být před nasazením, ne že
119
+ se jinde nepouští. Job `validate-uniform` (stage `test`, rules MR/main/devel/production) klonuje `api` a spouští
120
+ celou uniformu biz služby; `deploy-production` zůstává závaznou poslední instancí. `secret_detection` běží i na
121
+ `devel`, kam post-commit zrcadlo posílá každý commit (dosud `pipelines?ref=devel` = 0). `build` na `devel` záměrně
122
+ NEběží — 010 žádá kontrolu z každého commitu, ne obraz; `IMAGE_LATEST` z devel by přepsal `latest` platformy.
123
+
124
+ ### Changed — oba běhy uniformy volají `verify-deploy-uniform.sh` z pinovaného balíčku (d.529)
125
+
126
+ `scripts/verify-deploy-uniform.sh` neexistoval v žádném z 8 biz repozitářů (jen v šabloně) → `deploy-production`
127
+ padal na prvním kroku `script:`. Oba joby ho volají z `node_modules/@onlineapps/conn-orch-validator/templates/
128
+ business-service/scripts/` — brána i engine z jednoho pinu, žádná kopie do 8 rep; klon `api` přes `$API_CHECKOUT`
129
+ nejde, skript ho sám maže. Rozvržení a runtime obou jobů deklaruje jednou skrytý klíč `.oa-uniform` (`extends`).
130
+ Řádek `G-CI`: `why` přepsáno na dnešní stav (šablona od d.470 nenese `verify-installation-contract:`), `doc` → 010.
131
+
132
+ ### Changed — `G-SHARED-ENV` čte SSOT `api/config/shared-env.json` ve workspace, ne render zabalený v balíčku (d.536)
133
+
134
+ Ingest, emailer, hello, invoicing, pdfgen po dovydání 2: řádek porovnával s renderem zabaleným v 9.0.0 (d.229),
135
+ `oa-sync-template shared-env` renderoval z živého SSOT (d.500/d.510) → služba nemohla být zároveň „synced" i
136
+ DEPLOYABLE („10 line(s) differ"). Řádek renderuje SSOT týmž rendererem jako sync; bez dosažitelného workspace
137
+ NOT RUN s `Fix:` (v deploy jobu je `api` naklonované, 008). Kaskáda: každá biz služba po pinu spustí
138
+ `oa-sync-template readme-uniform --target .` (region README nese `from` řádku).
139
+
140
+ ### Changed — `oa-validate .` odvozuje kořen workspace od kořene služby (d.540)
141
+
142
+ Z `<workspace>/api_biz/<svc>` bez `--workspace` hlásil validator instalovaný v `node_modules` služby
143
+ `Workspace root: NOT RESOLVED` (kořen hledal jen od balíčku) → U-*/D-* NOT RUN. Třetí otázka: nejbližší předek
144
+ kořene služby s `api/config/services.json`; explicitní `--workspace` dál vyhrává.
145
+
146
+ ### Fixed — F-RUNNER porovnává směrem render; zpětná substituce jména služby zrušena (d.531)
147
+
148
+ `normalizeRunnerBlock` nahrazovala jméno služby v bloku zpět na `__SERVICE_NAME__`, takže u služby jménem `meta`
149
+ přepsala i literál v komentáři šablony (`api_biz/meta/docker-compose.yml carries the full numbers`) a řádek hlásil
150
+ nález, zatímco `oa-sync-template docker-compose.yml --check` říkal `unchanged`. Jedna kolej = referenci vyrenderovat
151
+ jménem služby a porovnat (`renderRunnerIdentity`); `normalizeRunnerBlock` smazána (jediný čtenář byl tento řádek).
152
+
153
+ ### Changed — důkaz validace odmítá neměřenou nulu; kontrakt odmítá `db: true` bez bloku `database` (d.521)
154
+
155
+ `ValidationProofGenerator.generateProof()` čte `durationMs`, `testsRun`, `testsPassed`, `testsFailed` z agregátu
156
+ runneru JMÉNEM a chybějící měření odmítne místo doplnění `0`; běh, který nic nespustil (`testsRun === 0`), je
157
+ odmítnut u autora, ne až v registru (`ValidationProofCodec.decode()` = NO_TESTS). Integrační kontrakt odmítá
158
+ `requiredConnectors.db: true` bez bloku `database` — směr, který přechodová výjimka F4 nechala otevřený
159
+ (shoda hotová: 7/8 služeb má obojí, pdfgen ani jedno, 0 repozitářů nese `ci-setup-db.js`).
160
+
161
+ ### Removed — `ValidationProofGenerator.extractTestSummary()` (d.521)
162
+
163
+ Nula konzumentů od 98d155f1 (2025-09-30); jediné pole nad agregátem, `coverage`, na platformě nic neměří.
164
+
165
+ ### Documentation — `mockInfrastructure` staví nápodobu MQ a registry, databázi ne (d.521)
166
+
167
+ README § „What `mockInfrastructure` covers — and what it does not": `run-prevalidation` běží proti skutečnému
168
+ schématu každé služby, která ho deklaruje, a patří až za `ci:gate:setup`; pokrytou množinu připíná test.
169
+
170
+ ### Fixed — každá NOT RUN věta nese svou nápravu, každý padlý záznam svou identitu (d.516, d.516b, d.518)
171
+
172
+ Jediný vlastník věty `Fix: run with --workspace pointing at a checkout that carries …` je
173
+ `manifest/workspaceRoot.js` § `describeWorkspaceFix`; berou ji `docsLintBridge` (D-PORT, D-RETIRED, D-LINT),
174
+ `libraryWorkspace.js` § `notRunBecause` (U-ORPHAN, U-MISMATCH, L-PINS, L-CONSUMER, L-TOOLING) a `readmeRegion.js`
175
+ (L-README-REGION). `X-IGNORED` nad stromem bez `.git` jmenuje vlastní nápravu — běh nad klonem, ne nad exportem.
176
+ `results.steps` runneru nese dva druhy záznamů (`kind: step` / `cookbook-load-failure`): odmítnutý recept se hlásí
177
+ jako recept (soubor + chyba), krok bez `step_id` je chyba §5, `validateCookbook` vyžaduje `step_id` jako cookbook-core 5.0.0.
178
+
179
+ ### Changed — kontrola se scope vyžadujícím workspace vlastní svou NOT RUN větu (d.518)
180
+
181
+ `runManifest.js` už nepůjčuje výchozí větu „the workspace root is not reachable"; kontrola bez `describeNotRun`
182
+ běh odmítne na každém běhu. Na výchozí větu nedosahovala žádná registrovaná kontrola (`automation-gates.md` §5).
183
+
184
+ ### Fixed — `results.steps` říká, co každý záznam je; krok bez `step_id` se neobejde pojmenováním (d.516b)
185
+
186
+ `results.steps` nese dva druhy záznamu a nejsou to oba kroky: výsledek kroku, a záznam, který
187
+ `CookbookTestRunner.runCookbooks` uloží za recept, jejž formátová kontrola odmítla — celý recept, který
188
+ žádný krok nevydal. Splývaly, takže odmítnutý soubor hlásil krok, který neexistuje:
189
+ `cookbook "broken.json" step "step #1": … cookbook format version is missing`. Každý záznam teď svůj druh
190
+ **deklaruje** (`kind: 'step'` / `kind: 'cookbook-load-failure'`) tam, kde vzniká, a nikdo ho nedovozuje
191
+ z chybějícího pole (`architecture-principles.md` §8). Nenačtený recept se hlásí jménem souboru:
192
+ `cookbook "broken.json" could not be run: …`.
193
+
194
+ Teprve na tom stojí druhá polovina: `utils/stepFailure.js` § `describeStepIdentity` **odmítne** záznam
195
+ kroku bez `step_id` místo aby ho pojmenoval pozicí (`step #3 (operation: convert)`). Ta náhrada byla
196
+ správná odpověď, dokud se tvar usazoval — alternativou bylo `undefined`. Dnes je `step_id` povinné od
197
+ `@onlineapps/cookbook-core` 5.0.0 a orchestrátor od d.460 odmítne task krok bez operace
198
+ (`_requireStepOperation`), takže krok bez `step_id` je vadný recept a pravděpodobné jméno ho čte jako
199
+ v pořádku (§3). Pozice zůstává v hlášce — jako to, čím se vadný krok najde, ne jako jeho jméno (§5).
200
+
201
+ `CookbookTestRunner.validateCookbook` nově žádá `step_id` jako první ze tří polí, která cookbook-core 5.0.0
202
+ u task kroku vyžaduje; tím zmizel i `Step ${step.step_id || 'unknown'}` z hlášek o `service`/`operation` —
203
+ případ, který zastupoval, už nenastane. Ověřeno pozitivní kontrolou nad 43 ostrými recepty
204
+ (`api/templates/business-service` + `api_biz/*/tests/cookbooks`): žádný krok bez `step_id`.
205
+
206
+ ### Fixed — NOT RUN „workspace není dosažitelný" nese týž Fix (d.516b)
207
+
208
+ `checks/docsLintBridge.js` § `describeNotRun` (obě kontroly) končil u toho, že workspace není — druhý kanál
209
+ vedle toho, který zavřela d.516, a týž nedostatek (`automation-gates.md` §1 požadavek 4). Věta pochází
210
+ z téhož vlastníka, `workspaceRoot.js` § `describeWorkspaceFix`. Ostatní `describeNotRun` (`readmeRegion.js`,
211
+ `libraryWorkspace.js`) a výchozí hláška v `runManifest.js` Fix stále nenesou — nahlášeno, neopraveno.
212
+
213
+ ### Fixed — most k dokumentačnímu lintu píše u NOT RUN týž Fix jako chybějící kořen (d.516)
214
+
215
+ `checks/docsLintBridge.js` skládal větu NOT RUN z vlastního textu a končil tam, kde skončil linter:
216
+ `F002:http-ports could not be decided — api_biz/*/docker-compose.yml lives in a sibling checkout this run
217
+ does not have, so the probe could not be evaluated`. Pravda, a nic, co může čtenář udělat — přitom ve
218
+ **stejném běhu** a o **témže chybějícím adresáři** řekl `U-ORPHAN` příkaz (`automation-gates.md` §1
219
+ požadavek 4). Dvě věty pro jednu nápravu jsou druhá kolej (`change-discipline.md` § One rail per concern).
220
+
221
+ Příkaz má teď jednoho vlastníka, `workspaceRoot.js` § `describeWorkspaceFix`; `runManifest.js`
222
+ § `describeMissingRoots` (d.511) i obě kontroly mostu (`D-PORT`/`D-RETIRED`/`D-LINT`) berou větu odtud.
223
+ Co musí checkout nést, zůstává na volajícím: řádek své kořeny deklaruje a jmenuje, kdežto most dostává
224
+ text od linteru, jehož kanál `skipped` nese jen `{ rule, reason }` (`api/scripts/ci/lint-biz-docs.mjs`
225
+ § `markSkipped`) — parsovat tu větu by byl soukromý dialekt mezi řádkem a nástrojem, jmenovat sourozence
226
+ zde by z mostu udělalo druhého vlastníka konfigurace prób. Měřeno nad `git archive HEAD` do adresáře bez
227
+ `api_biz`; kontrolní případ: nad workspace, který `api_biz` nese, nejde žádný `D-*` řádek do NOT RUN.
228
+
229
+ ### Fixed — `runPreValidation` nehlásí padlý krok pod jménem jeho operace (d.516)
230
+
231
+ `utils/preValidation.js` skládal seznam padlých kroků jako `step_id: step.step_id || step.operation`, takže
232
+ krok bez `step_id` dorazil ke čtenáři výsledku pod **jménem operace** — pole se jmenuje `step_id` a neslo
233
+ něco, co jím není, bez jakékoli stopy po záměně (`architecture-principles.md` §3). Od
234
+ `@onlineapps/cookbook-core` 5.0.0 je `step_id` povinné (`schemas/cookbook.v2.schema.json`
235
+ definitions.TaskStep.required jmenuje step_id, type, service, operation) a od d.460 orchestrátor odmítne
236
+ task krok, který nejmenuje operaci (`_requireStepOperation`), takže krok bez `step_id` je vadný recept —
237
+ a vadný recept má výsledek ukázat, ne ho přejmenovat. Kontrolní případ: krok s oběma poli hlásí své
238
+ vlastní `step_id` beze změny.
239
+
240
+ ### Fixed — NOT RUN chybějícího sourozence nese svůj Fix (d.511)
241
+
242
+ `describeMissingRoots` (`src/manifest/runManifest.js`) hlásil, CO chybí, a nikdy CO s tím; věta s příkazem
243
+ se tiskla jen u čistého verdiktu (`report.js` § `describeClearOutcome`), takže běh, který zároveň něco
244
+ našel, Fix neukázal nikde. Příkaz je odvozen z chybějících kořenů. Měřeno nad exportem
245
+ `api_biz/hello-service` vedle klonu api bez `api_biz/`; v rozvržení deploy brány (`<root>/api_biz/<služba>`
246
+ vedle `<root>/api`) je `notRun` prázdné — U-ORPHAN i R-NODE se rozhodnou (R-NODE i bez `api_biz`, d.238).
247
+
248
+ ### Added — úplnost manifestu proti šabloně v obou směrech (d.511, nález 18)
249
+
250
+ `tests/unit/templateCoverage.test.js` žádal řádek nebo třídu `own` pro každý soubor šablony; opačný směr
251
+ mlčel — `describeFromProblem` kontroluje tvar reference `from:`, nikdy existenci souboru, takže přejmenovaný
252
+ soubor šablony nechal balíček zelený a spadl až v cizím repu (`[ManifestDiscovery] Packaged file not found`).
253
+ Nově každá `from: { package }` jmenuje soubor, který balíček nese, a každý `files.own.allowed` má v šabloně
254
+ něco za sebou. Rozdíl obou množin je dnes nulový — bez baseline, bez výjimky.
255
+
256
+ ### Fixed — `sharedEnv.js` neslibuje kontrolu pole `consumers`, kterou nic nedělá (d.510)
257
+
258
+ Komentář nad `renderSharedEnv` tvrdil, že kdo klíč čte, je „fakt, který kód vlastní a kontrola
259
+ měří". Neměří: pole `keys[].consumers` nečte žádný kód tohoto balíčku (`grep -rn consumers src/`
260
+ vrací jen `MockMQClient` a jednu větu komentáře) a žádná brána jinde — manifest
261
+ `api/config/shared-env.json` to sám říká od d.505 (`_consumers`: „NOTHING measures it").
262
+ Slib mechanismu, který neexistuje, je přesně vada z `doc-code-binding.md` §5: odstavec se čte
263
+ jako krytý, takže se nikdo nepodívá.
264
+
265
+ Komentář teď říká obě poloviny pravdivě: **měřená** je jen ta, že se `consumers` nerenderuje —
266
+ kontrolní případ v `tests/unit/sharedEnv.test.js` polem hne a tvrdí, že vykreslený text se
267
+ nehne; obsah pole drží **review**; a strojová odpověď na „kdo tento název čte" je per služba,
268
+ řádek `C-ENV-READS` (check `env-contract`), který porovnává názvy čtené kódem služby s jejím
269
+ `config/service/integration-contract.json`. Renderer se nezměnil o řádek.
270
+
271
+ ### Changed — `LOG_LEVEL` v zabalené šabloně říká, kdo ho čte a kdo ne (d.510)
272
+
273
+ Klíč nese komentář „the log level an infra service's config/logging.json resolves through
274
+ `${LOG_LEVEL}`" — pravdivý o infrastruktuře, mlčící o biz řetězci, kde ho **nečte nikdo**:
275
+ v `shared/*/src` je 66 čtení `process.env` a ani jedno tohoto názvu, `@onlineapps/monitoring-core`
276
+ nečte prostředí vůbec (princip 1) a úroveň bere z konfigurace, kterou dostane. Čtenář biz
277
+ `shared.env` tak mohl klíč přenastavit a čekat účinek, který mít nemůže.
278
+
279
+ Klíč zůstává v jedné sdílené sadě — každá kopie je bajtově shodná s platformní šablonou
280
+ (konfirmace `biz-service-manifest` 003 §18, akceptace bod 4), takže odebrat ho jen biz nositelům
281
+ by znamenalo druhou sadu, tedy změnu konceptu, ne opravu věty. Opravena je věta: `why` nově
282
+ jmenuje infrastrukturní kolej i to, že v biz řetězci je klíč **nesen, ne čten**, a proč tam
283
+ přesto je. `consumers` je srovnán s měřením (přibyly `api_monitoring` a `api_meta_reader`, které
284
+ `LOG_LEVEL` čtou — `src/consumer/config/logging.json`, `src/config.js`).
285
+
286
+ Dopad na konzumenty: vykreslený komentář klíče se změnil, takže `oa-validate` nad biz službou,
287
+ která svůj `shared.env` nepřegenerovala, nahlásí `G-SHARED-ENV`. Náprava je týž jeden běh jako
288
+ u d.500: `npx oa-sync-template shared-env --target .`.
289
+
290
+ ### Fixed — `oa-sync-template` neplánuje řádky, ze kterých nemá co renderovat (d.507)
291
+
292
+ `npx oa-sync-template --target . --check` končil v každém biz repu dvěma řádky, které nešlo
293
+ vyřešit: `NOT RUN G-PROD-IMAGE …` a `NOT RUN G-SETUP docs/80-setup/ - the row declares no
294
+ "from" reference`. Oba řádky manifestu žádnou `from:` referenci nedeklarují, protože to nejsou
295
+ soubory renderované z reference — `G-PROD-IMAGE` je požadavek NA obsah souboru (pin digestem),
296
+ `G-SETUP` požadavek na existenci adresáře; odpovídá na ně běh manifestu (`npx oa-validate`).
297
+ Hlášení, se kterým čtenář nemůže nic udělat, je falešná záruka (`automation-gates.md` §5) a
298
+ hláška bez `Fix` (§1 požadavek 4).
299
+
300
+ Množina řádků synchronizace se nově bere podle toho, co manifest už dnes rozlišuje: řádek
301
+ s `from:` referencí je soubor, který běh renderuje (`syncRows`), řádek bez ní se neplánuje
302
+ vůbec. Rozhoduje reference, ne seznam id — nový požadavkový řádek tedy nevyžaduje změnu kódu.
303
+ `uniformRows` zůstává celou deklarací tříd `identical`/`generated`/`contains`, takže CLI umí
304
+ odmítnout `docs/80-setup/` jako požadavek (se jménem řádku a během, který na něj odpovídá), ne
305
+ jako překlep.
306
+
307
+ `NOT RUN` zůstává pro svůj jediný případ: řádek, který běh renderovat MÁ a nedosáhne na svou
308
+ referenci (reference do workspace, běh mimo workspace) — nově i s `Fix: … --workspace <root>`.
309
+ Chování řádků s referencí se nemění (kontrolní případ v `tests/unit/uniformFilesSync.test.js`).
310
+
311
+ Dopad na konzumenty: výstup `--check` nad konformním repem je o dva řádky kratší a neobsahuje
312
+ žádné `NOT RUN`; `oa-sync-template docs/80-setup/ --target .` nově končí exit 2 s vysvětlením
313
+ místo NOT RUN.
314
+
315
+ ### Changed — zabalená šablona `shared.env` nese dvě meze souborového logu (d.500)
316
+
317
+ `@onlineapps/monitoring-core` 3.0.0 vyžaduje `file.maxSize` a `file.maxFiles` bez defaultu
318
+ v kódu (`src/logger.js` § `REQUIRED_FILE_BOUNDS`) — služba, která je nedeklaruje, spadne při
319
+ konstrukci loggeru. Hodnoty rozhodl vlastník: `LOG_MAX_SIZE_BYTES=52428800`, `LOG_MAX_FILES=10`
320
+ (`api/docs/governance/confirmations/log-file-bounds.md` 001). Klíče přibyly do manifestu
321
+ `api/config/shared-env.json`, který je vlastníkem klíčové sady, a tím i do zabalené kopie
322
+ `templates/business-service/config/env-templates/shared.env`, kterou čte řádek `G-SHARED-ENV`.
323
+
324
+ Dopad na konzumenty: `oa-validate` nad biz službou, která klíče ve svém `shared.env` nemá,
325
+ nahlásí `G-SHARED-ENV`. Náprava je jeden běh: `npx oa-sync-template shared-env --target .`.
326
+
327
+ ### Added — `resolveMigrationPlan` na veřejném API balíčku (d.492)
328
+
329
+ Migrační plán (které `.sql` soubory tvoří množinu a v jakém pořadí) byl dostupný jen z vnitřní
330
+ cesty `@onlineapps/conn-orch-validator/src/utils/setupDatabase`. Cesta dovnitř balíčku není
331
+ kontrakt: každý přesun v `src/utils/` rozbije konzumenta, který na nic takového nepřistoupil.
332
+ Funkce je nově na `src/index.js` jako **tatáž** funkce (test `toBe` proti vnitřnímu modulu), ne
333
+ obal — jedna definice, jedno chování (`change-discipline.md` § One rail per concern). Množina
334
+ exportů se jinak nemění; kontrolní případ v `tests/unit/indexExports.test.js` porovnává celý
335
+ seznam klíčů se snímkem před změnou.
336
+
337
+ Biz repa mají přejít na veřejnou cestu `require('@onlineapps/conn-orch-validator')`; přechod
338
+ v jednotlivých repech patří jejich vláknům (vnitřní cestou dnes importují
339
+ `api_biz/converter/tests/integration/integrationEnv.js` a tři soubory v `api_biz/emailer/tests/`).
340
+
341
+ ### Fixed — brána test-coverage hlásila PASS nad prázdnou množinou testů (d.486, nález d.205)
342
+
343
+ Prázdný match je jestí exit 0 bez výstupu a každé porovnání brány iteruje množinu, takže nad
344
+ prázdnou nenašlo protipříklad („0 file(s) matched, 0 run by the test:all chain"). Totéž o krok
345
+ níž — krok řetězu `test:all` mířící do prázdného adresáře. Obojí je nález `TEST_SET_EMPTY` se
346
+ jménem skriptu i vzoru; hlásí se jen tam, kde je kořenem (prázdné T a `TEST_NOT_MATCHED` umlčují
347
+ kontrolu jednotlivých kroků). `TEST_COVERAGE_SCOPE` nové měření jmenuje.
348
+
349
+ ### Fixed — R6 odmítá `^`/`~`/`latest` v každé `@onlineapps/*` závislosti, i biz-only (d.485)
350
+
351
+ Kontrola exaktnosti pinu (`libCompat.js`) seděla uvnitř větve `gated`, takže balíček, který
352
+ žádná infra služba neinstaluje, mohl plavat pod zelenou branou (W413). Zúžení podle
353
+ `infraConsumed` dál rozhoduje jen o tom, co se porovnává s infra verzí; exaktní pin platí pro
354
+ každou `@onlineapps/*` závislost bez výjimky (`architecture-principles.md` § Version pinning).
355
+ Hláška je pro gated i biz-only jedna a nese `Fix: npm install <pkg>@<verze> --save-exact`;
356
+ kontrola existence v SSOT předchází kontrole exaktnosti, aby `Fix:` mohl verzi jmenovat.
357
+
358
+ ### Added — pravidlo `S010`: invertované tvrzení v bats netvrdí nic (d.482)
359
+
360
+ `lintScripts` čte nově i **kódové** řádky testů a hlásí příkaz s invertovaným návratovým kódem
361
+ ve dvou tvarech, které bats nese: `! cmd` samostatně a `cmd || ! cmd`. Změřeno na bats 1.13.0:
362
+ takový příkaz je vyjmut z `errexit`, takže pod `set -e` **nezpůsobí pád testu**, pokud zrovna
363
+ není posledním příkazem těla — řádek vypadá jako tvrzení a tvrzením je jen náhodou polohy, kterou
364
+ libovolný vložený řádek pod ním tiše zruší. Pravidlo proto hlásí **každý** výskyt a nerozlišuje
365
+ poslední příkaz. Oprava je podmíněný tvar, který návratový kód spotřebuje záměrně a řekne, co našel
366
+ (`if <co nesmí platit>; then echo "[ctx] problem - found: …" >&2; return 1; fi`); `if ! cmd; then … fi`
367
+ je právě ta oprava, ne vada, a nehlásí se. Brána přistává nad již čistým rozsahem — 0 nálezů
368
+ ve 146 souborech, 32 výskytů platformy přepsáno předem — bez baseline a bez skip flagu
369
+ (`automation-gates.md` §3). Norma: `api/docs/standards/SCRIPTS-STANDARD.md` § Enforcement.
370
+
371
+ ### Changed — rozsah citací je `tests/**`, ne jen `tests/scripts/` (d.482)
372
+
373
+ `CITATION_SCOPE` je nově `tests` (rekurzivně; přípony `.bats`/`.bash`/`.sh` beze změny), a platí pro
374
+ `S009` i `S010`. Adresář, ve kterém se `S009` narodilo, není jediný, který testy drží: mimo něj leží
375
+ 12 shellových souborů (`tests/scripts-docker`, `tests/e2e/helpers`, `tests/fixtures/…`). Pravidlo, které
376
+ čte jeden adresář a sousední ne, má díru, kterou nikdo nevidí — běh ohlásí `0 finding(s)` a nikdy neřekne,
377
+ které soubory neotevřel (`automation-gates.md` §5). Rozsah byl před rozsířením změřen čistý pro obě
378
+ pravidla, takže ani zde není baseline. Jméno konstanty se nemění — exportuje ji CLI do svých hlášek.
379
+
7
380
  ## [9.0.0] — 2026-09-15
8
381
 
9
382
  ### Removed — šablona už nenese job `verify-installation-contract` (d.470)
package/README.md CHANGED
@@ -23,7 +23,7 @@ This is **NOT** a development testing tool. This is a **production validation or
23
23
  2. **Validates configuration** - `config.json`, `operations.json` and `integration-contract.json`, through the manifest rows that own them
24
24
  3. **Validates the environment contract** - every `env.required` name is set
25
25
  4. **Validates operations** - the v3 handler-registry rules
26
- 5. **Validates business logic** - cookbook tests with mocked infrastructure
26
+ 5. **Validates business logic** - cookbook tests against the two mocked doubles named below
27
27
  6. **Validates connector integration** - the two declarations agree and the environment backs them
28
28
  7. **Generates validation proof** - SHA256 proof carried into registration
29
29
 
@@ -58,7 +58,7 @@ runs Tier-1 validation in phase 0.2 — before MQ connects, before registration.
58
58
  sentences of its own, and the same defect was reported twice
59
59
  3. **Environment Contract** - every variable the contract declares `env.required` is set
60
60
  4. **Operations Compliance** - every operation declares `handler`, `bundle_scope`, `input`, `output`, and none carries a retired v2 field (`endpoint`, `method`, `path`); a missing `description` is a warning
61
- 5. **Cookbook Tests** - business logic + integration (MOCKED infra)
61
+ 5. **Cookbook Tests** - business logic + integration; every step is dispatched in-process, with no transport, and against the service's real database (see below)
62
62
  6. **Connector Integration** - the connector declarations agree and the environment backs them
63
63
  7. **Manifest conformance** - the repository against the uniform manifest shipped in this package (see below)
64
64
 
@@ -70,6 +70,35 @@ signal saved to `ci/deployability.json`
70
70
 
71
71
  ---
72
72
 
73
+ ## Pre-validation runs in-process — and against the real database
74
+
75
+ A cookbook step is dispatched **in this process**, against the service's own v3
76
+ handler, with no transport of any kind. There is nothing to mock: the ctx slots
77
+ are `null` (pinned by `tests/unit/CookbookTestRunner.ctxShape.test.js`), and a
78
+ handler that needs a schema reaches it the way it reaches it in production — by
79
+ importing the service's own database module. **That connection is real.**
80
+
81
+ `mockInfrastructure` — the option that once built a MockMQClient and a
82
+ MockRegistry for transport dispatch — is retired: the runner and the cookbook
83
+ format both refuse it by name (`tests/unit/mockInfrastructureRetired.test.js`).
84
+
85
+ What the real database requires is ORDER, and the contract is what makes the
86
+ order decidable:
87
+
88
+ - a service that needs a database declares **both** `requiredConnectors.db: true`
89
+ **and** the `database` block; the contract refuses either one standing alone,
90
+ in both directions;
91
+ - `setup-db` therefore builds a schema exactly where one is declared, and reports
92
+ `NOT APPLICABLE` — never `OK` — where none is;
93
+ - `run-prevalidation` (`npm run test:cookbooks`) comes **after** `ci:gate:setup`
94
+ in the job, because by then the schema its handlers will reach exists.
95
+
96
+ Run out of that order, or against a service whose schema was never built, the
97
+ cookbooks fail in the handler's own database error. That is the truthful
98
+ failure.
99
+
100
+ ---
101
+
73
102
  ## Environment contract (`env` block)
74
103
 
75
104
  One declaration in `config/service/integration-contract.json`, two consumers:
@@ -373,6 +402,17 @@ the test precisely so that the next file cannot be exempted by editing a test
373
402
  behind two files that had no row at all: `gitignore` (now `F-GITIGNORE`) and
374
403
  `.gitlab-ci.yml` (now `G-CI`).
375
404
 
405
+ The same suite reads the other two directions, which were silent until d.511 and silent in
406
+ a worse way, because nothing reads a `from:` reference until a run needs it. Every
407
+ `from: { package }` reference the manifest makes must name a file this package carries:
408
+ `describeFromProblem` (`manifestShape.js`) checks a reference's SHAPE and never whether the
409
+ file is there, so a template file renamed or deleted left every suite green here and threw
410
+ `[ManifestDiscovery] Packaged file not found` later — inside whichever repository ran the
411
+ uniform next, a service's CI or a container at boot. And every `files.own.allowed` entry
412
+ must have something behind it in the template, so the class stays a decision about real
413
+ files rather than a list nobody re-derives. Both read the references through `collectRows`,
414
+ the manifest's own walk, so a row that moves into another block cannot fall out of them.
415
+
376
416
  #### `.gitignore`: the `contains` class
377
417
 
378
418
  `F-GITIGNORE` compares the ignore ENTRIES the packaged template declares against the
@@ -628,13 +668,32 @@ the directories it walks (`requiresSiblings({ row, block })`, derived from the r
628
668
  patterns), and the runner verifies them before running it:
629
669
 
630
670
  ```
631
- NOT RUN L-CONSUMER — sibling root api_biz is not present in this checkout
671
+ NOT RUN L-CONSUMER — sibling root api_biz is not present in this checkout - an absent
672
+ directory is not an empty one, so this row is not answered. Fix: run with --workspace
673
+ pointing at a checkout that carries api_biz.
632
674
  ```
633
675
 
634
676
  Measured against a `git archive` export of this repository on 2026-09-09: without the gate,
635
677
  `--library --all` raised a blocking `L-CONSUMER` on `conn-base-db` about files nobody
636
678
  opened. Never a finding, never a pass (`automation-gates.md` §5).
637
679
 
680
+ The line carries its fix because of where it is read. Measured 2026-09-15 over a
681
+ `git archive` export of `api_biz/hello-service` placed beside an api clone with no
682
+ `api_biz/` around them: the reason ended at "is not present in this checkout", and the
683
+ report's own incomplete-run sentence — the one that does name the command — prints only on
684
+ a CLEAR verdict (`report.js` § `describeClearOutcome`), so a run that also had findings
685
+ named no fix anywhere. `automation-gates.md` §1 requirement 4 asks for the exact command,
686
+ and the sync half of this package already gave it (`src/sync/uniformFiles.js` § `planRow`).
687
+ The command is derived from the missing roots, so a row that starts reading a new sibling
688
+ needs no second edit.
689
+
690
+ In the layout the deploy gate demands — the service at `<root>/api_biz/<service>` beside
691
+ `<root>/api`, which `templates/business-service/.gitlab-ci.yml` asks GitLab for through
692
+ `GIT_CLONE_PATH` — nothing is skipped: measured the same day over that export, `notRun` is
693
+ empty and both `U-ORPHAN` and `R-NODE` decide. `R-NODE` left the NOT RUN list in d.238 and
694
+ decides even where `api_biz` is absent, because the platform major it reads is this
695
+ package's own `engines.node`.
696
+
638
697
  One case is neither mode: a service root that does not lie under the given workspace root.
639
698
  Filtering there would drop every finding and the empty table would read as "the row
640
699
  looked and found nothing", so each row that needs the workspace — `workspace` and `bearer`
@@ -875,12 +934,17 @@ class — `src/**`, `tests/**`, the content of `docs/**` — are never written.
875
934
  file, and since d.229 most of them read a file this package carries. The predicate is the
876
935
  one the manifest run uses (`rowNeedsWorkspace`), so the `fix` command of `F-INIT`,
877
936
  `F-JEST` and `F-RUNNER` can be typed where their finding is now raised: inside the service
878
- image. A row that does need one is printed `NOT RUN` by name, never skipped in silence.
879
-
880
- A row the manifest does not make renderable is printed `NOT RUN` with the reason instead
881
- of being filled from a guess. Today that is `G-PROD-IMAGE` and `G-SETUP`: they declare no
882
- `from:` reference, so nothing says what their content is rendered from — the first is a
883
- requirement ABOUT a file, the second a directory that must exist. A row whose file
937
+ image. A row that does need one is printed `NOT RUN` by name with the `--workspace` fix,
938
+ never skipped in silence.
939
+
940
+ A row the manifest does not make renderable is **not a row of this run**. Today that is
941
+ `G-PROD-IMAGE` and `G-SETUP`: they declare no `from:` reference, so nothing says what
942
+ their content would be rendered from — the first is a requirement ABOUT a file, the second
943
+ a directory that must exist, and `oa-validate` is the run that answers both. Until d.507
944
+ the sync planned them and printed `NOT RUN`, which every `--check` in every repository
945
+ ended on: a line naming a state its reader could do nothing about, with no `Fix`
946
+ (`automation-gates.md` §5 and §1 requirement 4). Naming a requirement path as an argument
947
+ is refused by what it is, with the run that does answer it. A row whose file
884
948
  blocks the write — an `init.sh` carrying no block marker, where inserting one would mean
885
949
  choosing a position among somebody's own install steps; an installation document missing a
886
950
  section, where writing one would mean rewriting somebody's prose — is printed `BLOCKED`
@@ -1107,6 +1171,16 @@ services/my-service/
1107
1171
  ```
1108
1172
 
1109
1173
  **Proof Lifecycle:**
1174
+ - **Every number in it is a measurement.** `testsRun`, `testsPassed`,
1175
+ `testsFailed` and `durationMs` are read from the aggregate the runner returns,
1176
+ and a missing one is refused by name rather than filled with `0`: the same
1177
+ number would otherwise stand for both "measured, and it was zero" and "nobody
1178
+ measured this", with no way for a reader to tell them apart. A run that
1179
+ executed nothing — a service whose `tests/cookbooks/` holds no `.json` file —
1180
+ is refused outright: it has no failing step, so it used to publish a signed
1181
+ proof asserting a passing validation of zero tests, which
1182
+ `ValidationProofCodec.decode()` then refused at the registry as `NO_TESTS`,
1183
+ days later and far from the cause.
1110
1184
  - **Written:** on every successful validation, i.e. on every boot. There is no
1111
1185
  proof cache — the one that existed skipped steps 1-3 and 5-6 to save 6 ms and
1112
1186
  bought a window of up to seven days in which validation asserted something no
package/docs/DESIGN.md CHANGED
@@ -25,17 +25,31 @@ surface was removed together with the now-retired `ServiceValidator` /
25
25
  - **Production-ready** — the exact same code runs locally, in CI and during
26
26
  wrapper startup.
27
27
  - **Fail-fast** — missing logger, missing operations.json: immediate throw.
28
- `serviceUrl` is *not* among them: it fed the retired `/health` probe and
29
- nothing reads it (ADR 0005). It is accepted and ignored so existing callers
30
- keep working.
28
+ `serviceUrl` is not among them because it is gone: it fed the retired
29
+ `/health` probe (ADR 0005), and neither `ValidationOrchestrator` nor
30
+ `CookbookTestRunner` reads, stores or documents it any more
31
+ (`tests/unit/ValidationOrchestrator.unit.test.js` § Constructor). Nor is one
32
+ passed: `ServiceWrapper._createValidationOrchestrator()` builds the
33
+ orchestrator from four options and this is not one of them
34
+ (`tests/unit/preValidation.test.js` § the runner is built with the service
35
+ identity and no retired options).
31
36
 
32
37
  ## Components
33
38
 
34
- ### Mock Infrastructure (for unit tests)
39
+ ### Internal test doubles — not published
35
40
 
36
- - `MockMQClient` — in-memory message queue simulation
37
- - `MockRegistry` — service registry simulation
38
- - `MockStorage` — object storage simulation
41
+ The package exports no infrastructure doubles. `mockInfrastructure`, the option
42
+ that built them, is retired (d.576): a cookbook step is dispatched in-process
43
+ against the service's own v3 handler, so there is no transport to stand in for
44
+ and the database a handler reaches is the real one
45
+ (`tests/unit/mockInfrastructureRetired.test.js`).
46
+
47
+ - `MockRegistry` — the registry stand-in of `helpers/createServiceReadinessTests`,
48
+ internal to the package
49
+ - `MockMQClient` — used by `tests/unit/RegistrationFlow.test.js` alone
50
+
51
+ A double with no consumer is not kept: `MockStorage` was deleted with its own
52
+ unit test in d.583 (`tests/unit/mockInfrastructureRetired.test.js`).
39
53
 
40
54
  ### Production Validation
41
55