|
22 | 22 | * Usage: |
23 | 23 | * pnpm --filter @objectstack/spec check:generated # report every stale artifact |
24 | 24 | * pnpm --filter @objectstack/spec check:generated --fix # + regenerate exactly those |
| 25 | + * pnpm --filter @objectstack/spec check:generated --reconcile-only # ledger audit only, no gates (CI) |
25 | 26 | */ |
26 | 27 |
|
27 | 28 | import { execSync } from 'node:child_process'; |
@@ -67,6 +68,15 @@ const NO_GENERATOR: ReadonlyArray<{ check: string; why: string }> = [ |
67 | 68 | // failing on `main` itself. The doc it checks against is hand-written, so there |
68 | 69 | // is no generator to name. |
69 | 70 | { check: 'check:variant-docs', why: 'audits that each schema variant appears in its hand-written doc — no artifact' }, |
| 71 | + // The #4177 story again, one day after #4203 closed it: #4232 added this script |
| 72 | + // and nothing in CI runs this reconciliation, so `main` went red for every local |
| 73 | + // wrapper run a second time. Caught while wiring `--reconcile-only` into |
| 74 | + // lint.yml's unfiltered job — the fix for exactly this class. The ledger it |
| 75 | + // audits is a hand-maintained doc (docs/audits/), so there is no generator. |
| 76 | + { |
| 77 | + check: 'check:strictness-ledger', |
| 78 | + why: 'audits the hand-written strictness ledger against the code it describes — no artifact', |
| 79 | + }, |
70 | 80 | // The odd one out: it audits the source's TYPES, but reads them from the BUILT |
71 | 81 | // `dist/*.d.ts` — the surface a consumer's import actually resolves to, which |
72 | 82 | // is the only place the defect is visible (#4171). So the `readsDist` caveat |
@@ -146,9 +156,32 @@ function run(script: string): { ok: boolean; output: string } { |
146 | 156 | } |
147 | 157 |
|
148 | 158 | const fix = process.argv.includes('--fix'); |
| 159 | +// CI mode (#4203): reconcile and stop — no gates. The reconciliation above only |
| 160 | +// ever ran where this aggregate ran, which was locally: CI runs the gates as |
| 161 | +// individual steps, so an unclassified `check:`/`gen:` script kept every CI gate |
| 162 | +// green while this wrapper exited red on `main` before running a single gate. |
| 163 | +// Twice in three days — #4177 (fixed only by colliding with #4194) and #4232 |
| 164 | +// (caught wiring this flag in). ci.yml's `check-generated` job cannot host the |
| 165 | +// fix: its `generated` paths filter does not watch packages/spec/package.json, |
| 166 | +// the one file every offending PR must touch — both offenders skipped that job |
| 167 | +// entirely. So lint.yml's unfiltered, required "TypeScript Type Check" job runs |
| 168 | +// this mode instead. Reads package.json and the arrays above; no build, <1s. |
| 169 | +const reconcileOnly = process.argv.includes('--reconcile-only'); |
149 | 170 | const scripts = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8')).scripts ?? {}; |
150 | 171 | reconcileLedger(scripts); |
151 | 172 |
|
| 173 | +if (reconcileOnly) { |
| 174 | + const checks = Object.keys(scripts).filter((n) => n.startsWith('check:')).length; |
| 175 | + const gens = Object.keys(scripts).filter((n) => n.startsWith('gen:')).length; |
| 176 | + console.log( |
| 177 | + `✓ check:generated ledger reconciles with package.json: ${checks} check: + ${gens} gen: scripts, ` + |
| 178 | + `all classified (${GATED.length} gated, ${NO_GENERATOR.length} source audits, ` + |
| 179 | + `${UNGATED_GENERATORS.length} ungated generators, 1 aggregate).\n` + |
| 180 | + ` --reconcile-only: no gates were run — this verifies coverage, not artifacts.`, |
| 181 | + ); |
| 182 | + process.exit(0); |
| 183 | +} |
| 184 | + |
152 | 185 | console.log(`Checking ${GATED.length} generated artifacts (every gate runs — the first failure does not stop the rest).\n`); |
153 | 186 |
|
154 | 187 | const stale: typeof GATED[number][] = []; |
|
0 commit comments