|
34 | 34 | // |
35 | 35 | // ## Invariants |
36 | 36 | // |
| 37 | +// DISCOVERED at least one driver package was found. Zero is not an empty |
| 38 | +// matrix, it is a broken run: the other three invariants iterate |
| 39 | +// the discovered set, so they all pass vacuously and this script |
| 40 | +// prints OK while checking nothing. The case-set axis cannot fail |
| 41 | +// this way -- CASE_SETS is a declared expectation, so a vanished |
| 42 | +// `spec/src/data` fails CLASSIFIED's reverse direction -- but the |
| 43 | +// driver axis is disk-discovery with nothing declared to |
| 44 | +// reconcile against, and RECONCILED's reverse direction walks |
| 45 | +// LEDGER, which is empty in the intended steady state. |
37 | 46 | // CONSUMED every (driver x case-set) cell is either covered -- some file |
38 | 47 | // under the package's `src/` imports the case-set's marker export |
39 | 48 | // from `@objectstack/spec/data` -- or carries a DEBT/EXEMPT entry |
@@ -157,6 +166,27 @@ function discoverDrivers() { |
157 | 166 | .sort(); |
158 | 167 | } |
159 | 168 |
|
| 169 | +/** |
| 170 | + * DISCOVERED — the errors for a discovery that found nothing. |
| 171 | + * |
| 172 | + * Split out from `audit()` so the self-test can drive the invariant itself |
| 173 | + * rather than a proxy for it. The previous guard lived only in the self-test |
| 174 | + * and read `drivers.length >= 3 && drivers.includes('driver-sql')` — a |
| 175 | + * hardcoded name and count inside the one script whose stated rule is that |
| 176 | + * drivers come from disk and are never listed. Both would have needed editing |
| 177 | + * the next time a driver is added or the packages move, which is exactly when |
| 178 | + * the guard matters. |
| 179 | + */ |
| 180 | +function discoveredErrors(drivers) { |
| 181 | + if (drivers.length) return []; |
| 182 | + return [ |
| 183 | + `DISCOVERED: no driver package found under ${DRIVERS_DIR.slice(ROOT.length + 1)}/. ` |
| 184 | + + 'Either these packages moved and DRIVERS_DIR is stale, or they are gone. ' |
| 185 | + + 'Every other invariant iterates the discovered set, so a zero-driver run ' |
| 186 | + + 'reports OK having checked nothing — it fails here instead.', |
| 187 | + ]; |
| 188 | +} |
| 189 | + |
160 | 190 | /** Every `*-conformance.ts` under spec/src/data, and the case-set exports in it. */ |
161 | 191 | function discoverCaseSets() { |
162 | 192 | const found = []; |
@@ -217,6 +247,9 @@ function audit() { |
217 | 247 | const errors = []; |
218 | 248 | const rows = []; |
219 | 249 |
|
| 250 | + // DISCOVERED — the precondition the other three iterate over. |
| 251 | + errors.push(...discoveredErrors(drivers)); |
| 252 | + |
220 | 253 | // CLASSIFIED — both directions between CASE_SETS and the files on disk. |
221 | 254 | const onDisk = discoverCaseSets(); |
222 | 255 | const classified = new Set(CASE_SETS.map((c) => c.marker)); |
@@ -370,9 +403,12 @@ function selfTest() { |
370 | 403 | expect('discovers TEMPORAL_CASES on disk', found.includes('TEMPORAL_CASES')); |
371 | 404 | expect('discovers PAGINATION_UNORDERED_CASES on disk', found.includes('PAGINATION_UNORDERED_CASES')); |
372 | 405 |
|
373 | | - // Discovery must find the drivers, for the same reason. |
374 | | - const drivers = discoverDrivers(); |
375 | | - expect('discovers driver packages from disk', drivers.length >= 3 && drivers.includes('driver-sql')); |
| 406 | + // DISCOVERED: the invariant itself, in both directions, then against the |
| 407 | + // real tree. No driver name or count is asserted — the point of the gate is |
| 408 | + // that the set comes from disk. |
| 409 | + expect('a discovery that found nothing is an error', discoveredErrors([]).length === 1); |
| 410 | + expect('a discovery that found something is not', discoveredErrors(['driver-anything']).length === 0); |
| 411 | + expect('discovers driver packages from disk', discoverDrivers().length > 0); |
376 | 412 |
|
377 | 413 | if (failures.length) { |
378 | 414 | for (const f of failures) console.error(` x self-test: ${f}`); |
|
0 commit comments