Uh oh!
There was an error while loading. Please reload this page.
test(runtime): migrate the notification-conformance fixture onto the shared read-refusal capture - #13352
Conversation
…shared read-refusal capture The file carried its own copy of the expected-read-refusal capture and wrapped the engine's `error` channel only. Since #13273/#13327 `ObjectQL.reportFindFailure` picks the level from the cause, so a read whose table was never provisioned -- which is every read this capture is declared over -- is logged at `debug`. The inline recognition arm could therefore no longer match a single frame: measured on this tree before the change, the engine `error` channel was invoked 0 times while 63 `Find operation failed` frames arrived on `debug`, all 63 satisfying that arm's own predicate. The file stayed green because everything it asserts is fed by the driver channel, so the engine-side suppression was dead code reading as live protection. It now uses `captureExpectedReadRefusals` (#10629), which wraps both channels. The `afterAll` assertion moves from the driver-only `withheld.has(table)` loop to `silentChannels(ALWAYS_READ_AUTHZ_TABLES)`, which is strictly stronger: it requires both channels to have fired for every always-read table. The header's counts are re-measured on this tree rather than carried forward, and its prose no longer claims the engine frame arrives on `error`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TvqBFLRzXdSPcbusDoED9k
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-trump
commented
Aug 30, 2026
✅ PM review — ACCEPT once CI is green; ⛔ not while it is running (#13325)Undrafting now; arm follows on a complete green read. ⛔ No rework owed. Clause ② does not attach — one test fixture, nothing under ✅ Fences held, verified from the diff
⭐ The dead-arm proof is the best measurement in this roundInstrumented at the merge base, per channel: ⭐ The positive control for those zeros is the sharp part: the dead arm's own predicate matched 63 of 63 frames — on Alive-after: ⭐ The migration made the assertion STRONGER, not merely equivalent
That is the right outcome for a card about dead suppression: the replacement cannot go dead the same way, and it says so at the assertion site. ⭐ And the rotted number was fixed at the root, not re-transcribedThe header's counts are re-measured on this tree with the command that produced them, and then explicitly demoted:
A count that rotted once will rot again; labelling it as prose and naming the accessor that re-derives it is a better fix than a fresher number. Reverse verification ablated the shared helper's
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#13325
Nothing here was red and nothing became red. This is dead suppression plus stale prose.
What was dead — measured, not argued
packages/runtime/src/notification-schema-conformance.integration.test.tscarried its own copy of the expected-read-refusal capture (captureExpectedAbsentTableNoise) and wrapped the engine'serrorchannel only.Since #13273 landed as PR #13327,
ObjectQL.reportFindFailurepicks the level from the cause: a read whose table was never provisioned is logged atdebug, with areason: 'table-not-provisioned'meta and no stack, while every other read failure keepserrorwith the stack. That is exactly — and only — the class this capture is declared over, so its recognition arm could no longer match a single frame.Measured on this tree at the merge base (
0783d7b80), by instrumenting the inline capture with counters, running the suite, then restoring the file:errorchannel invocations, any messageerrorframes matching the arm's predicatedebugchannel invocationsdebugframes whose msg isFind operation failedThe last row is the positive control for the zeros above it: the zero is not a broken predicate. The identical recognition rule matches 63 of 63 frames — it was simply reading the channel the engine stopped using. The file stayed green throughout because everything it asserts is fed by the DRIVER channel, which #13273 never touched. So its engine-side suppression was dead code that read as live protection.
Instrumentation hygiene: the mutation was confirmed on disk before the run (injected marker present, removed anchor at zero occurrences, blob hash differing from the
HEADblob), and the restore was proven the same way afterwards — blob hash back to2c4e4aecd, zero marker residue,git diff HEADempty,git status --porcelainempty.The migration
The file now uses the shared
captureExpectedReadRefusals(#10629), which already proxies botherroranddebug:captureExpectedAbsentTableNoise()— 70 lines of per-fixture copy — is deleted;noise.install(driver)becomesnoise.captureDriver(driver);noise.wrapEngine(...)becomesnoise.captureEngine(...);afterAllassertion moves from the per-tablenoise.withheld.has(table)loop toexpect(noise.silentChannels(ALWAYS_READ_AUTHZ_TABLES)).toEqual([]).ABSENT_AUTHZ_TABLESandALWAYS_READ_AUTHZ_TABLESare unchanged, andpackages/runtime/src/expected-read-refusal-noise.tsis not edited by this PR — this card is a consumer of it.The assertion is strictly stronger, never weaker. The loop it replaces read the DRIVER channel only, which is precisely why it kept passing while the engine half was dead.
silentChannels()requires BOTH channels to have fired for each of the five always-read tables, and a silent one names itself in the diff.Alive after — reverse verification
Direction predicted before running: RED, naming the engine channel for the five always-read tables.
Ablating the shared helper's
debugarm — restoring its pre-#13327 shape — and re-running the migrated file:The eight test cases still pass under the ablation; only the newly live engine assertion in
afterAllgoes red. That is the exact shape claimed above — the driver-fed half is untouched, the engine-fed half is now load-bearing.Ablation hygiene, both legs: mutation confirmed on disk (marker injected once, the
debug-arm anchor at zero occurrences, blob982555d52against theHEADblob064a19632), restore confirmed the same way (blob back to064a19632, zero residue,git diff HEADempty, cleangit status --porcelain). No rebuild was needed for this leg: the helper is a relative same-package import vitest resolves to source, not a packageexportsentry pointing atdist. The engine under test IS resolved throughdist, so@objectstack/objectqlwas built first and its artifact was confirmed to carrytable-not-provisionedbefore any measurement was taken.Re-measured header counts
The header's previous numbers were a statement about
origin/mainat #10380 time. They are re-derived here from a run on this tree, and the prose no longer claims the engine frame arrives onerror. Command:Read off the migrated capture itself (
totalRefusals()/totalEngineFrames()/refusals/engineFrames):debugframes withheldsys_usersys_membersys_user_positionsys_user_permission_setsys_positionsys_settingsilentChannels(ALWAYS_READ_AUTHZ_TABLES)returns[]. Before the migration the engine tally was 0 for every one of those tables. The header states the two totals as PROSE rather than as a pin, and says how to re-derive them, so nobody copies them forward again.Every existing assertion still passes
All measurements below were taken on the tree at
5293ddc76, this branch's final commit, with a clean working tree.Test Files 1 passed (1),Tests 8 passed (8), exit 0. The six tables recorded on the driver channel are unchanged from the pre-migration run.notifications.hono.integration.test.ts) plus the shared helper's own two pin suites (expected-read-refusal-noise.channel-asymmetry.test.ts,cross-field-refusal-operand-withhold.test.ts):Test Files 4 passed (4),Tests 28 passed (28), exit 0.pnpm --filter @objectstack/runtime exec vitest run:Test Files 201 passed (201),Tests 2968 passed (2968), exit 0.pnpm lint(eslint . --no-inline-config): exit 0, no narrowing.Gate families derived from the real change set rather than recalled —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reports the change set as one path against merge base0783d7b80— and then run. Every one green, quoting each gate's own verdict line:check:nul-bytes—OK (scanned 7390 text file(s) … no raw ASCII control bytes)check:cross-package-test-inputs—OK: 24 package(s) read outside themselves, all declaredcheck:engine-double-contract—OK — 709 pinned, 134 in the DEBT ledger, 3 exemptcheck:where-matcher—316 matcher(s) discovered … 0 silently-wrong and 0 unjudged … none newcheck:query-options-erasure—ratchet holds: 67 unswept non-test site(s) … none newcheck:test-source-alias—OK — 72 packages with tests scannedcheck:type-check-coverage—OK — 66/78 workspace packages type-checkedcheck:type-check-debt—--re-measure: OK — 30 ledger entr(ies) re-measured in 234.3s, 1558 raw tsc error(s) total, none above its recorded number(surplus: none)check:dual-build-cjs-loads—102 published require entry point(s) across 66 package(s) load; 610 emitted CommonJS file(s) parse(run after a fullturbo run build,71 successful, 71 total)check:logger-receiver-detach,check:objectql-double-limit,check:page-declaration-shape,check:published-files,check:slot-lookup,check:type-source-resolution— exit 0scripts/check-ci-filter-parity.mjs,check-comment-mask-adoption.mjs,check-cross-package-test-inputs.mjs,check-keyed-text-bounds.mjs,check-plugin-teardown-shape.mjs,check-shard-attestation.mjs,check-undeclared-dep-imports.mjs,docs-audit/check-affected-docs.mjs,docs-audit/check-drift-comment.mjs— exit 0Two readings are recorded as NOT MEASURED rather than folded into the green list:
node scripts/check-test-completeness.mjs— exit 3, with the gate's own refusal text:PREREQUISITE NOT MET — this gate grades a saved 'turbo run test' log, and no log was named, andrunning the family locally, record this gate as NOT MEASURED. Not a red, and nothing to repair.pnpm --filter @objectstack/runtime typecheckexits 0, but that green says nothing about the edited file:packages/runtime/tsconfig.jsonexcludes**/*.test.ts, andtsc --noEmit --listFilesreturns 0 occurrences of the edited path. The hidden test layer of this package IS measured, bycheck:type-check-debt --re-measure, which re-ran the@objectstack/runtimeTEST_DEBT entry and reported it not above its recorded number.Scope
packages/runtime/src/metadata-list-ambient-vs-bare-transaction.integration.test.tsis deliberately untouched and is not in this diff. Its declared fault isTimeout acquiring a connection, which is not a missing table, so its frame legitimately stays onerrorand itswithheld.engineFindassertion still fires today. It also cannot adopt the shared helper even in principle: that helper's predicate is keyed on a declared table name plus that table'sno such tablereason, and this fixture has neither. Repairing it by symmetry would break a working pin.Changeset
None, deliberately. The diff is a single test fixture — no package publishes anything from it, nothing user-visible changes, and the file is excluded from this package's tsup entry and from its tsc program. The
skip-changesetlabel carries that declaration.Generated by Claude Code
Generated by Claude Code