Uh oh!
There was an error while loading. Please reload this page.
test(runtime): withhold and assert the expected refused a read on noise in 16 passing pins - #10985
Conversation
…oise in 16 passing pins (#10629) Sixteen passing `@objectstack/runtime` fixtures printed expected read failures into the shared shard log: 134 `[sql-driver] DATABASE_ERROR — the backend refused a read on '<table>'` envelopes plus their 134 matching `ERROR Find operation failed` engine frames, 268 lines in all. Turbo interleaves package logs without attribution, so an ERROR-shaped line from a green test is indistinguishable from a real failure — lines of this shape were once lifted verbatim into a p1 flake signature (#10293). Every one is a fail-soft probe whose missing-table answer the runtime swallows by design: `resolveUserAuthzGrants`' six `sys_*` `tryFind`s, `ObjectQL.probeInstallOrganizations`, `SeedLoaderService.resolveSoleOrganizationId`, the lifecycle governance snapshot, `runBuildProbes`' view read, and the boot metadata load. Each was judged expected rather than diagnostic; none was silenced on the strength of looking like noise. Applies PR #10630's ruled shape verbatim, factored into one shared test-only module: withhold only a line naming an expected table AND carrying that same table's `no such table` reason, count what was withheld, and assert the counts. The engine frame is withheld only when it sits directly above a driver refusal the capture already recognised, so an identically-shaped fault from another cause still reaches the log with both halves intact. No test skipped, quarantined, retried or removed: 179 files / 2679 tests pass before and after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
📓 Docs Drift Check11 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d0635b8585236e27252f92fa3e52dfe816b2bfda && git checkout d0635b8585236e27252f92fa3e52dfe816b2bfda
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 471cfbd97501f5974a5437ef24d2ca266e6e8ede ef25e4a34148e7c0d77fc343cb4ccae90847cd54 && git checkout -B drift-repro 471cfbd97501f5974a5437ef24d2ca266e6e8ede && git merge --no-ff ef25e4a34148e7c0d77fc343cb4ccae90847cd54
node scripts/docs-audit/affected-docs.mjs --json 471cfbd97501f5974a5437ef24d2ca266e6e8ede |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 32546382828 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#10629
Sixteen passing
@objectstack/runtimefixtures printed expected read failures into the shared shard log. They now withhold and assert that noise instead of emitting it — the shape PR #10630 ruled for this class, applied verbatim.The re-measured population — and where the card's table was short
⛔ The card's counts were taken pre-#10630, so the first act was to re-run the enumeration at this branch's merge base (
926778bce0), not to work to the recorded number.926778bce0refused a read onlinesnotifications.hono.integration.test.ts(52)The total did not move, and neither did any row — but the card's table lists 15 files summing to 133, one short of the 134 it states. The missing row is
default-datasource-plugin.test.tsat 1 line, dropped from the table but present in the total. Nothing changed under #10630 (that PR's two files were already excluded from this 134 by construction); the card's table was simply a row short of its own total. Worked to 16, not 15.The card counted one channel. Attribution by the reporter's own
stderr | fileheader on a--no-file-parallelismrun shows the driver refusal and the engine frame are paired 1:1, so the real population is 268 lines:packages/runtime/src/)notifications.hono.integration.test.tsresolveUserAuthzGrants— sixsys_*tryFindspackage-list-commits-org-scope.integration.test.tsrunBuildProbesview read onanythingpackage-revert-commit-org-scope.integration.test.tsrunBuildProbesview read onanythingcascade-delete-multivalue-lookup-real-driver.integration.test.tsprobeInstallOrganizationsseed-multi-value-lookup-real-driver.integration.test.tsprobeInstallOrganizations+resolveSoleOrganizationIdbulk-write-real-driver.integration.test.tsprobeInstallOrganizations+resolveSoleOrganizationIdsandbox/undeclared-field-write-driver-split.integration.test.tsprobeInstallOrganizationsfederated-boot-binding.test.tssys_metadataexpand-nested-fields-join-key.integration.test.tsprobeInstallOrganizationspackage-revert-commit-attribution-org-scope.integration.test.tsrunBuildProbesview read onanythingbatch-row-http-status-real-driver.integration.test.tsprobeInstallOrganizationsbatch-row-driver-text-real-driver.integration.test.tsprobeInstallOrganizationspreserve-audit-real-driver.integration.test.tsprobeInstallOrganizationsseed-loader-driver-text-real-driver.integration.test.tsprobeInstallOrganizations+resolveSoleOrganizationIddefault-datasource-plugin.test.ts— the row the card's table omitsprobeInstallOrganizationssandbox/nested-write-real-sqlite.integration.test.tsprobeInstallOrganizationsBefore / after
Same command, same reporter, at
ef25e4a341:926778bce0)ef25e4a341)refused a read onERROR Find operation failedPer file, all sixteen go 134 → 0 and 134 → 0, measured on a
--no-file-parallelismrun over exactly the 16:Test Files 16 passed (16) · Tests 78 passed (78), noise count 0 — against 16 / 78 / 268 on the same command at the merge base. The test count is identical on both sides: nothing skipped, quarantined,.only-scoped, retry-wrapped or deleted.The 6 surviving
ERROR Find operation failedframes are not residue of this population. All 6 come fromcross-field-refusal-operand-withhold.test.tsand carry the #7929 cross-field refusal — a different feature that shares the engine's frame, with no table to key on and no driver refusal beneath it. Out of this card's scope; filed as #10983 (finding·domain:cli, nopm:queue).The #10630 pattern, and the one thing done differently
#10630's landed diff is the ruled shape and was read before anything was written. Preserved verbatim:
logger(assigned; a protected field with aconsoledefault, the idiom its own doc comment names) and the engine'sloggerbehind a Proxy onerroralone, so every other logger method resolves to the engine's own;no such tablereason. Verified across all 134: every refusal names its own missing table as the reason, 0 mismatched;DATABASE_ERRORon the same table from any other cause is not recognised by the driver sink, so its frame reaches the log with both halves intact;{ warn, error }soSqlDriver.logDurabilityFailurestill finds anerrorchannel and does not silently re-level durability messages this card is not talking about;Differently: it lives in one module rather than sixteen copies —
packages/runtime/src/expected-read-refusal-noise.ts, test-only (nothing insrc/index.tsimports it, and tsup's single entry issrc/index.ts, so it is not bundled; it imports novitest, so the assertions stay visible in the fixture that owns them). #10630 wrote the predicate inline because it had two call sites; this has sixteen, and sixteen copies of one predicate is sixteen places for it to drift — including drifting looser, the direction that turns a pin back into a mute with nothing going red. The mechanism is unchanged; only the duplication is gone.The assertion surface is
expect(capture.silentChannels()).toEqual([])— one call that still makes a silent channel name itself in the diff, which is what #10630's "one assertion per channel" bought at sixteen times the bulk.Per-file judgement: expected vs diagnostic
⛔ The fence that mattered most. Nothing was quieted for looking like noise. Each line was traced to its emitting call site through the engine frame's own stack, and every one is a fail-soft probe — a read the runtime issues to learn whether something is installed, whose missing-table answer it swallows by design, with the swallow documented at the source:
core/src/security/resolve-authz-context.ts—tryFindon sixsys_*objectql/src/engine.ts—probeInstallOrganizationsisMissingTableErrorand only that, which its own doc comment calls "the one benign cause"; anything else propagatesmetadata-protocol/src/seed-loader.ts—resolveSoleOrganizationIdobjectqllifecycle governance snapshotmetadata-protocol/src/build-probes.ts—runBuildProbesview_read_failedpublish issue, not an exception; the suites assertres.successand never read that issuesys_metadataNo file turned out to be a real failure signal, so no stop-and-report was needed. The one that came closest is
federated-boot-binding.test.ts, whose 5 lines appear only in theOS_SKIP_SCHEMA_SYNCcase — the flag means DDL is managed out of band, and this fixture manages none, so an absentsys_metadatais what the flag produces rather than a defect. The capture is scoped to that single case on purpose: the ordinary boot beside it creates the table and stays fully loud if it ever stops.Two other scoping decisions in the same spirit:
sandbox/undeclared-field-write-driver-split.integration.test.tshas a SQL family and a schemaless family. The memory boot declares an empty expectation rather than skipping the assertion, so the sharedafterEachstill fails loudly if a boot ever forgets to install a capture.notifications.hono.integration.test.tswithholds all sixsys_*tables but requires only the five read on every grant resolution.sys_settingis read on some routes only; requiring it would turn a single-test-trun red without meaning anything. This is test(runtime): withhold and assert the expected error noise in two passing pins #10630's own distinction, kept.Assertions sit in
afterEachwhere every test in the file provokes the probe, and in the test bodies where only some do (batch-row-http-status— its not-found case never writes, so never resolves a system-write org;notifications.hono— discovery and the anonymous 401 resolve no grants;default-datasource-plugin— one of twelve cases writes through the booted engine). AnafterEachassertion in those files would redden a test for a reason that has nothing to do with it.Vacuity check — every new assertion can fail
⛔ An assertion that expected noise was emitted is worthless if it cannot go red. Proven by ablating the shared predicate so nothing is ever withheld (the module is a relative in-package import that vitest transforms from source — no
distis involved and no rebuild is required, unlike a dist-resolved ablation), then re-running the same 16 files:All sixteen files go red, and every silent channel names itself:
The noise count returning to exactly 268 is the second half of the proof: the withhold is precisely what removed those lines, and nothing else was suppressing them. The ablation was then reverted and the file proven byte-identical to the committed blob (
git hash-objectagainstgit rev-parse HEAD:...).Gate verdicts
Union derived at the final commit with
node scripts/pm/dispatch-gates.mjs(no paths passed — the script takes the change set from the merge base itself), and run there.ef25e4a341, exit codes captured before any pipe.check:type-check-debt--re-measure: OK — 33 ledger entr(ies) re-measured in 191.2s, 1908 raw tsc error(s) total, none above its recorded number.check:type-check-coverageOK — 64/77 workspace packages type-checked (plus the root), 13 in the DEBT ledgercheck:engine-double-contractOK self-test: separates engine doubles from driver doubles AND from scoped repositories on BOTH write verbscheck:where-matcherOK self-test: separates conjoining, early-returning, combinator-blind and refusingcheck:query-options-erasure✓ ratchet holds: 67 unswept non-test site(s) in 17 file(s), none newcheck:slot-lookup✓ ratchet holds: 107 unswept site(s) in 25 file(s), none new … baseline key set verified against 926778b: no files added.check:cross-package-test-inputsOK: 13 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.check:test-source-aliascheck-test-source-alias --self-test OKcheck:type-source-resolutioncheck-type-source-resolution --self-test OKcheck:changeset-gate-self-tests✓ 118 + 212 + 116 assertions over real temp git reposcheck:objectui-changesetcheck-empty-changeset✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).check-changeset-no-major✓ This diff introduces no major bump.check-adr-0087-registration✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).check-plugin-teardown-shape✓ 57 Plugin implementation(s) across 4416 source(s) … every teardown-shaped method sits beside a real destroy()check-ci-filter-parityOK: all 82 declared cross-package glob(s) (71 unique) are coveredcheck-nul-bytesOK (scanned 6324 text file(s) … no raw ASCII control bytes).⛔ No gate weakened, no threshold moved, no baseline raised. The
@objectstack/runtimeTEST_DEBT entry sits at its recorded 227 — measured directly with the test exclusion lifted, before and after. One error this branch briefly introduced (an unused type import) was repaired at the source rather than absorbed by the ledger's headroom.Changeset
.changeset/runtime-expected-read-refusal-noise.md,@objectstack/runtime: patch.skip-changesetlabel here, but this one adds a real module underpackages/runtime/src/, andcheck-empty-changesetrejects an empty-frontmatter changeset outright — so a declaring one is the only shape that satisfies both. It states plainly that no runtime behaviour changes; the publisheddistis unaffected, since tsup's only entry issrc/index.tsand nothing there imports the new module.Out-of-scope findings
Both filed unassigned,
finding+domain:cli, nopm:queue— grading is triage's. Labels verified by read-back on both.@objectstack/runtimelog still carries 6 expectedERROR Find operation failedframes from one green test — same defect class, different feature (cross-field filter refusal) #10983 — the 6 remaining engine frames above, fromcross-field-refusal-operand-withhold.test.ts. Same defect class, different feature; closing it needs a predicate keyed on the refusal's own message rather than on a table, which is a judgement rather than a mechanical edit.new ObjectKernel({ logLevel: 'silent' })silences nothing —logLevelis not a key ofObjectKernelConfig, and 12 call sites across 7 files believe it is #10984 —new ObjectKernel({ logLevel: 'silent' })silences nothing:logLevelis not a key ofObjectKernelConfig(the logger config lives underlogger.level), so the kernel stays at itsinfodefault. 12 call sites across 7 files in three packages believe otherwise.tscdoes report it (TS2353), but the runtime package excludes its tests fromtsc, so it is one of the 227 frozen in the TEST_DEBT ledger and no gate is red for it. Two of this card's fixtures pass that option and printed ERROR-level frames anyway. ⛔ Not a suggested alternative fix — a blanketsilentis exactly the mute test(runtime): withhold and assert the expected error noise in two passing pins #10630 and this PR refuse.Generated by Claude Code