Repository navigation
Commit 8366f3e
ci(gates): the last two ratchet ledgers become one-sided — a decrease passes and is reported (#26701)
Refs #26586 (items 1, 3)
Items 2 and 4 stay on the card: item 2 is a comment in the governed
`.github/workflows/lint.yml` (Tier H), and item 4 asks the maintainer
for a ruling, so neither is touched here.
## What changes
The two ratchet ledgers that #26549 left two-sided become one-sided, the
same treatment the other ten got: a decrease passes and is reported, an
increase reds, and a baselined entry whose file is still on disk that
the run never read is refused, so a dead scan stays red.
| Reader | Baseline | Decrease (now passes, printed on stdout) | Dead
scan stays red | Self-test |
|:---|:---|:---|:---|:---|
| `check-engine-double-contract.mjs` |
`engine-double-contract.baseline.json` (120 DEBT + 3 EXEMPT rows) |
`reconcileDebtLedger().down` — a fallen count, a file with nothing
unguarded left, an entry whose double is gone | new `UNREAD [verb]`
error: a ledgered file still on disk that the discovery walk never read
| new battery (11), roster floor 33 → 34 |
| `check-startup-registry-verdict.mjs` |
`startup-registry-verdict.baseline.json` (empty today) |
`reconcileLedger().stale` — printed with `↓`, exit 0 | new refusal: a
ledgered key whose file is still on disk and was not in the corpus the
walk read | new battery (8), roster floor 18 → 19 |
Both comparisons move into pure functions (`reconcileDebtLedger`,
`reconcileLedger` / `unreadBaselined`) so the self-test drives both
directions with no tree at all. Neither gate has a `--fix`/`--update`,
so each decrease line names the hand edit that records it and the
ceiling it leaves until then.
Kept as errors on purpose: a NEW unguarded engine double, a grown
per-file count (`PINNED`), a finding with no ledger row, `DECLARED`,
`DISCOVERED` / `SEAMS_DISCOVERED`, `RETAINED` / `SEAMS_RETAINED` (those
two ledgers record COVERAGE and may only grow — the opposite polarity,
untouched here), the population floors, and the dead-root refusals.
Item 3: the comment at
`packages/metadata-protocol/src/protocol.metadata-store-outage.test.ts:636-639`
said the durability ledger and the code are "pinned to each other in
both directions". Since #26549 only the restored-limb direction reds; a
stale entry left behind after the fix passes and is reported.
Comment-only, no assertion changed.
Header and `$comment` prose that described the old behaviour is
rewritten rather than left behind: the `RECONCILED` invariant in
`check-engine-double-contract.mjs` (with a new `UNREAD` row beside it),
the RETAINED block's "ratchet the LEDGERED population in both
directions" paragraph, which is now dated to #9680 rather than stated as
current, one aside at L647, and both baselines' `$comment` blocks. No
counts or keys moved in either baseline.
## Why
A stale entry reds a PR that never touched the gate: delete a listed
test file, or fix the site a row licenses, and CI goes red with a remedy
that belongs to a different card. #26444's Wanted 3, measured below on
this card's own two ledgers.
## Verification
### Premise, measured on the base tree `fe53769a` before the change
Both gates exited 0 on that tree, and each redded on a legitimate
decrease:
| Gate | Mutation on the base tree | Exit |
|:---|:---|:---|
| engine-double-contract | deleted
`packages/cli/src/commands/serve-email-appname-precedence.test.ts`, a
file the ledger lists for both `delete` and `update` | **1** —
`RECONCILED [delete]: baseline entry for … declares no engine double
with a delete any more` plus the `update` twin |
| startup-registry-verdict | the ledger is empty, so a stale entry was
injected (`packages/core/src/gone-plugin.ts::GonePlugin.init()::cache`)
| **1** — `1 stale baseline entr(ies) … the site no longer violates, so
delete the entry` |
Each mutation was restored with `git checkout HEAD --` and `git status
--porcelain` read clean afterwards.
### Acceptance, on head `36c37411`
| Gate | DOWN: a legitimate decrease | UP: a new violation |
|:---|:---|:---|
| engine-double-contract | the same file deleted → **0**, `DEBT ledger:
2 (file, verb) entr(ies) fell (passing, reported)` | the same file
copied to a new path → **1**, `PINNED [delete]`/`PINNED [update]` on the
copy |
| startup-registry-verdict | an injected row for a gone file → **0**, `↓
1 stale baseline entr(ies)`; and an injected row for a file the walk
DOES read (`packages/core/src/kernel.ts`) → **0**, reported the same way
| a new `init()` probe that warns on the absent `cache` service, written
into `packages/core/src` → **1**, `1 startup registry read(s) record a
verdict the boot can still contradict` |
### Dead scans stay red
Each mutation landed through `node scripts/ablation-replace.mjs` (anchor
must hit; write and restore verified on disk), and each restore printed
`blob == HEAD` with an empty `git diff HEAD`:
| Gate | Mutation (the gate's own walk, one run) | Exit |
|:---|:---|:---|
| engine-double-contract | the discovery walk skips any directory named
`commands` | **1**, four `UNREAD` findings naming the two ledgered files
under `packages/cli/src/commands` for both verbs |
| startup-registry-verdict | `collectSourceFiles` skips `core`, with a
row injected for a file under `packages/core/src` | **1**, `1 baselined
entr(ies) … name a file still on disk that the scan never read` |
The population refusals both gates already had are untouched:
`DISCOVERED`/`SEAMS_DISCOVERED`, the four population floors reached by
every scanning branch, the dead-root `assertRootsResolvable` refusal,
and the empty-corpus refusal.
### Tests
- `node scripts/check-engine-double-contract.mjs --self-test` → exit 0,
carrying the new 11-case one-sided battery; production run → exit 0, `OK
— 1091 pinned, 120 in the DEBT ledger, 3 exempt`.
- `node scripts/check-startup-registry-verdict.mjs --self-test` → exit
0, `18 analysis case(s) + the dead-root hard error … + the one-sided
ledger (8 case(s)) all passed`; production run → exit 0, `46
startup/open-registry seam(s) across 3585 file(s)`.
- `pnpm --filter @objectstack/metadata-protocol exec vitest run
--maxWorkers=2 src/protocol.metadata-store-outage.test.ts` → exit 0,
`Test Files 1 passed (1)`, `Tests 32 passed (32)` (under the shared
verify lock, `VERDICT command-exit 0`).
- No package test exercises either script. Eight `*.test.ts` files name
one of them; all eight mention it in a comment only, and none asserts
behaviour this PR changes.
<details><summary>Derived gate families — 66/69 exit 0, 3 NOT MEASURED
(prerequisite), on 36c3741</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 69 families over the five changed
paths; each ran with its exit captured before any pipe. 66 exited 0,
including `pnpm check:engine-double-contract`, `pnpm
check:startup-registry-verdict`, `pnpm check:durability-log-level`,
`pnpm check:objectql-double-limit`, `pnpm
check:ratchet-remedy-authority`, `pnpm check:self-test-wired`, `pnpm
check:entry-guard` and `pnpm check:doc-authoring`.
Three exited **3**, the prerequisite code, so they are NOT MEASURED
rather than red — the same classification `--ran` derives from the
recorded codes:
- `node scripts/check-plugin-teardown-shape.mjs --self-test` — its
positive-control fixture is pinned to a commit this shallow checkout
cannot reach (`git rev-parse --is-shallow-repository` is true).
- `pnpm check:dual-build-cjs-loads` — reads built output; 72 packages
have no `dist/` in this container.
- `pnpm check:lean-entry-closure` — its own self-test passed (22 cases);
the gate then refused because `packages/objectql/dist/core.mjs` is
absent.
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran FILE`, with each line recorded as `COMMAND :: exit CODE`, printed:
`69 derived famil(ies) accounted for — 66 run, 3 NOT-MEASURED (3 DERIVED
from a recorded exit 3)`, 0 UNRUN.
The local-only families were not run: this diff touches neither
`packages/spec`'s build nor a four-axis frame file. The repo-wide scans
(`pnpm lint` first among them) and the whole farm are CI's run, and the
`Lint & Repo Gates` check-run is the reading of record for both gates on
this PR.
</details>
<details><summary>Changeset</summary>
`skip-changeset`. Root `scripts/*.mjs` and `scripts/*.json` ship in no
package's `files[]`. The one `packages/**` path is a comment in a
`*.test.ts`, and `@objectstack/metadata-protocol` ships
`["dist","README.md","CHANGELOG.md"]` — measured after the build: the
comment's text (`pinned to each other`) has zero hits anywhere under
`packages/metadata-protocol/dist`, with `listCommits` as the positive
control hitting `dist/index.js`, `dist/index.cjs` and
`dist/index.d.cts`.
</details>
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:#26549
漏下的两本棘轮台账(`engine-double-contract.baseline.json`、`startup-registry-verdict.baseline.json`)也改成单侧:数减少、文件被删、站点被修好
→ 通过并在输出里报告;新增违例、计数变大 →
照旧变红。空扫描仍然变红:台账里列着、文件还在磁盘上、这次却没读到,就拒绝。另外改了一条过期注释(item 3)。
- 为什么改:删一个被台账列着的测试文件,或者修好一处被台账许可的站点,今天就会让一个根本没碰这个门禁的 PR 变红,而修法属于另一张卡。
- 风险与回滚:代价是「余量」——减少后没有手改台账,旧数仍是上限,可以悄悄补回同样多的违例;每条报告都写明了这个数。两个门禁都没有
`--update`,所以每条报告直接给出该手改哪一行。回滚就是 revert 本 PR,没有数据或接线变化。
- 先看:`scripts/check-engine-double-contract.mjs` 的
`reconcileDebtLedger()`(唯一带计数的一本,新增 `UNREAD` 拒绝),然后是
`scripts/check-startup-registry-verdict.mjs` 的 `reconcileLedger()` /
`unreadBaselined()`。
- 两分钟验证:`git rm
packages/cli/src/commands/serve-email-appname-precedence.test.ts && node
scripts/check-engine-double-contract.mjs; echo $?; git checkout HEAD --
packages/cli/src/commands/serve-email-appname-precedence.test.ts`,应看到
`DEBT ledger: 2 (file, verb) entr(ies) fell (passing, reported)`,退出码 0。
- 需要你做的:item 4 的裁决(本 PR
不改行为):收窄后的检测器如果让活数下降,生产运行不再变红;对这两个门禁而言「磁盘上有、这次没读到就拒绝」这一臂能抓住哪些、抓不住哪些,写在下面的
Acceptance notes 里。item 2 在受管的 `lint.yml` 里,也留在卡上。席位落地本 PR。
## Acceptance notes
- **Headroom is the stated cost.** A one-sided ledger left above reality
licenses that many sites until someone edits it by hand. Every decrease
line names the number it leaves as the ceiling. The engine-double
ledger's rows are keyed `(file, verb)`, so a stale row re-licenses that
exact pair; if a file is later recreated at the same path it rides the
stale count, and the report says to delete the entry.
- **Item 4, measured on these two gates.** The `UNREAD` arm catches a
scan that lost part of its *corpus* — a walk that stopped descending, a
root that moved, a file-kind filter that stopped matching — because it
asks an identity (on disk, and not read) rather than a count. It does
**not** catch a *recognizer* that narrowed:
`check-engine-double-contract`'s discovery still reads every ledgered
file, so a `scanSource` that stopped recognising a double's shape leaves
the file read and its row a passing decrease. For that half the gate has
two other guards it already had, both untouched here: `DISCOVERED` (a
slice finding zero doubles anywhere) and the four population floors, of
which `discoveredFiles ≥ 700` and `pinnedRows ≥ 600` are exactly the
recognizer's own output — this run reads 1119 and 1000, so a narrowing
that costs more than ~37% of discovery, or 40% of pins, refuses with
exit 2 rather than reporting decreases. What stays uncovered is a
narrowing smaller than that band which happens to land on ledgered
files; for that, the self-test batteries driving the real recognizer
over synthetic sources in both directions are the only instrument.
`check-startup-registry-verdict` has no population floor and an empty
ledger, so for it the whole detector-weakening axis rests on its
self-test: the 18 analysis rows are what would red if a vocabulary entry
or the AST walk stopped matching, and the empty-corpus and dead-root
refusals cover only the total collapse. A floor there would be invented
rather than measured (46 seams, 39 of them read-only), which is why this
PR adds none.
- Not touched: `scripts/engine-double-contract.pinned.json` and
`scripts/engine-double-contract.seams.json` (GENERATED, opposite
polarity — they record coverage and may only grow; open PR #26337
touches the pinned ledger), the durability vocabulary staleness checks,
and `.github/workflows/lint.yml` (item 2, governed).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbaFpnMwgpCgneKe2qS
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbaFpnMwgpCgneKe2qS)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 88fb255 commit 8366f3e
5 files changed
Lines changed: 296 additions & 93 deletions
File tree
- packages/metadata-protocol/src
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
635 | 635 | | |
636 | 636 | | |
637 | 637 | | |
638 | | - | |
639 | | - | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
640 | 641 | | |
641 | 642 | | |
642 | 643 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | | - | |
| 177 | + | |
178 | 178 | | |
179 | | - | |
180 | | - | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
181 | 189 | | |
182 | 190 | | |
183 | 191 | | |
| |||
189 | 197 | | |
190 | 198 | | |
191 | 199 | | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
197 | | - | |
198 | | - | |
199 | | - | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
200 | 211 | | |
201 | 212 | | |
202 | 213 | | |
| |||
633 | 644 | | |
634 | 645 | | |
635 | 646 | | |
636 | | - | |
637 | | - | |
| 647 | + | |
| 648 | + | |
638 | 649 | | |
639 | 650 | | |
640 | 651 | | |
| |||
3157 | 3168 | | |
3158 | 3169 | | |
3159 | 3170 | | |
| 3171 | + | |
| 3172 | + | |
| 3173 | + | |
| 3174 | + | |
| 3175 | + | |
| 3176 | + | |
| 3177 | + | |
| 3178 | + | |
| 3179 | + | |
| 3180 | + | |
| 3181 | + | |
| 3182 | + | |
| 3183 | + | |
| 3184 | + | |
| 3185 | + | |
| 3186 | + | |
| 3187 | + | |
| 3188 | + | |
| 3189 | + | |
| 3190 | + | |
| 3191 | + | |
| 3192 | + | |
| 3193 | + | |
| 3194 | + | |
| 3195 | + | |
| 3196 | + | |
| 3197 | + | |
| 3198 | + | |
| 3199 | + | |
| 3200 | + | |
| 3201 | + | |
| 3202 | + | |
| 3203 | + | |
| 3204 | + | |
| 3205 | + | |
| 3206 | + | |
| 3207 | + | |
| 3208 | + | |
| 3209 | + | |
| 3210 | + | |
| 3211 | + | |
| 3212 | + | |
| 3213 | + | |
| 3214 | + | |
| 3215 | + | |
| 3216 | + | |
| 3217 | + | |
| 3218 | + | |
| 3219 | + | |
| 3220 | + | |
| 3221 | + | |
| 3222 | + | |
| 3223 | + | |
| 3224 | + | |
| 3225 | + | |
| 3226 | + | |
| 3227 | + | |
| 3228 | + | |
| 3229 | + | |
| 3230 | + | |
| 3231 | + | |
| 3232 | + | |
| 3233 | + | |
| 3234 | + | |
| 3235 | + | |
| 3236 | + | |
| 3237 | + | |
| 3238 | + | |
| 3239 | + | |
| 3240 | + | |
| 3241 | + | |
| 3242 | + | |
| 3243 | + | |
| 3244 | + | |
| 3245 | + | |
| 3246 | + | |
| 3247 | + | |
| 3248 | + | |
| 3249 | + | |
| 3250 | + | |
| 3251 | + | |
| 3252 | + | |
| 3253 | + | |
3160 | 3254 | | |
3161 | 3255 | | |
3162 | 3256 | | |
| 3257 | + | |
| 3258 | + | |
3163 | 3259 | | |
3164 | 3260 | | |
3165 | 3261 | | |
3166 | | - | |
3167 | | - | |
| 3262 | + | |
| 3263 | + | |
| 3264 | + | |
| 3265 | + | |
| 3266 | + | |
| 3267 | + | |
3168 | 3268 | | |
3169 | 3269 | | |
3170 | 3270 | | |
| |||
3182 | 3282 | | |
3183 | 3283 | | |
3184 | 3284 | | |
3185 | | - | |
3186 | | - | |
3187 | | - | |
3188 | 3285 | | |
3189 | 3286 | | |
3190 | 3287 | | |
| |||
3197 | 3294 | | |
3198 | 3295 | | |
3199 | 3296 | | |
3200 | | - | |
3201 | | - | |
3202 | | - | |
3203 | | - | |
3204 | | - | |
3205 | | - | |
3206 | | - | |
3207 | | - | |
3208 | | - | |
3209 | | - | |
3210 | | - | |
3211 | | - | |
3212 | | - | |
3213 | | - | |
3214 | | - | |
3215 | | - | |
3216 | | - | |
3217 | | - | |
3218 | | - | |
3219 | | - | |
3220 | | - | |
3221 | | - | |
3222 | | - | |
3223 | | - | |
3224 | | - | |
3225 | | - | |
3226 | | - | |
3227 | | - | |
3228 | | - | |
3229 | | - | |
3230 | | - | |
3231 | | - | |
3232 | | - | |
3233 | | - | |
3234 | | - | |
3235 | | - | |
3236 | | - | |
3237 | | - | |
3238 | | - | |
3239 | | - | |
3240 | | - | |
3241 | | - | |
3242 | | - | |
3243 | | - | |
| 3297 | + | |
| 3298 | + | |
| 3299 | + | |
| 3300 | + | |
| 3301 | + | |
3244 | 3302 | | |
3245 | 3303 | | |
3246 | 3304 | | |
| |||
3318 | 3376 | | |
3319 | 3377 | | |
3320 | 3378 | | |
3321 | | - | |
| 3379 | + | |
3322 | 3380 | | |
3323 | 3381 | | |
3324 | 3382 | | |
| |||
3449 | 3507 | | |
3450 | 3508 | | |
3451 | 3509 | | |
3452 | | - | |
| 3510 | + | |
3453 | 3511 | | |
3454 | 3512 | | |
3455 | 3513 | | |
| |||
3528 | 3586 | | |
3529 | 3587 | | |
3530 | 3588 | | |
| 3589 | + | |
| 3590 | + | |
| 3591 | + | |
| 3592 | + | |
| 3593 | + | |
| 3594 | + | |
| 3595 | + | |
| 3596 | + | |
3531 | 3597 | | |
3532 | 3598 | | |
3533 | 3599 | | |
| |||
3636 | 3702 | | |
3637 | 3703 | | |
3638 | 3704 | | |
| 3705 | + | |
3639 | 3706 | | |
3640 | 3707 | | |
3641 | 3708 | | |
3642 | 3709 | | |
3643 | | - | |
| 3710 | + | |
3644 | 3711 | | |
3645 | 3712 | | |
3646 | 3713 | | |
| |||
5287 | 5354 | | |
5288 | 5355 | | |
5289 | 5356 | | |
| 5357 | + | |
| 5358 | + | |
| 5359 | + | |
| 5360 | + | |
| 5361 | + | |
| 5362 | + | |
| 5363 | + | |
| 5364 | + | |
| 5365 | + | |
| 5366 | + | |
| 5367 | + | |
| 5368 | + | |
| 5369 | + | |
| 5370 | + | |
| 5371 | + | |
| 5372 | + | |
| 5373 | + | |
| 5374 | + | |
| 5375 | + | |
| 5376 | + | |
| 5377 | + | |
| 5378 | + | |
| 5379 | + | |
| 5380 | + | |
| 5381 | + | |
| 5382 | + | |
| 5383 | + | |
| 5384 | + | |
| 5385 | + | |
| 5386 | + | |
| 5387 | + | |
| 5388 | + | |
| 5389 | + | |
| 5390 | + | |
| 5391 | + | |
| 5392 | + | |
| 5393 | + | |
| 5394 | + | |
| 5395 | + | |
| 5396 | + | |
| 5397 | + | |
| 5398 | + | |
| 5399 | + | |
5290 | 5400 | | |
5291 | 5401 | | |
5292 | 5402 | | |
| |||
5376 | 5486 | | |
5377 | 5487 | | |
5378 | 5488 | | |
5379 | | - | |
| 5489 | + | |
| 5490 | + | |
| 5491 | + | |
5380 | 5492 | | |
5381 | 5493 | | |
5382 | 5494 | | |
| |||
0 commit comments