Uh oh!
There was an error while loading. Please reload this page.
docs(runtime): state the expected-read-refusal capture's real per-channel pass-through loudness - #12295
Conversation
…rough asymmetry The module header claimed, of both channels, that anything the capture does not recognise is forwarded to the real console. That holds for `captureDriver` (its non-matching branch calls `console` directly) and not for `captureEngine` (its non-matching branch calls the engine's own kernel-derived logger, which drops an `error` frame whenever the configured level ranks above `error` — `fatal` or `silent`). Documentation only: no sink, predicate, count or assertion changes, and no consuming fixture becomes loud. A new pin measures both directions of the asymmetry on a real kernel so the prose cannot rot unnoticed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbG3rGVLjZStHQxHDtzJdJ
…gine-noise-doc-asymmetry
…ss-through loudness Boots a real lean kernel (ObjectQL + sqlite-wasm) at four logger levels and counts what each channel's pass-through puts in front of a reader: level driver pass-through engine pass-through info 1 1 error 1 1 fatal 1 0 silent 1 0 plus a recognised-pair case proving the withholding/counting half is unchanged. Comment-only edits elsewhere in the module; this is what keeps those sentences from rotting unmeasured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HbG3rGVLjZStHQxHDtzJdJ
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. 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 ce669dc3d610c506625a2be13bc48a9811b84bb5 && git checkout ce669dc3d610c506625a2be13bc48a9811b84bb5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8cdd696592b0c3c7bb7c3caa776f91aa6eb8cb68 f811e755a6258e968041f50a8c2f72015a689e35 && git checkout -B drift-repro 8cdd696592b0c3c7bb7c3caa776f91aa6eb8cb68 && git merge --no-ff f811e755a6258e968041f50a8c2f72015a689e35
node scripts/docs-audit/affected-docs.mjs --json 8cdd696592b0c3c7bb7c3caa776f91aa6eb8cb68 |
… not uniform
Measured on origin/main: only 3 of the 19 fixtures that carry
captureExpectedReadRefusals construct their kernel with
`logger: { level: 'silent' }`; the other 16 leave it at the default
`info`, where an unrecognised engine frame IS logged. The header now says
so, so the silent-fixture case is not read as the rule for all of them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HbG3rGVLjZStHQxHDtzJdJUh oh!
There was an error while loading. Please reload this page.
Fixes#11569
Documentation-only correction to
packages/runtime/src/expected-read-refusal-noise.ts,plus one new pin that measures what the corrected prose claims. No sink, predicate,
count or assertion changes. No fixture becomes loud. None of the 19 consumers of
captureExpectedReadRefusalsis touched.Scope is the triage re-ruling of 2026-08-25 15:56Z, which overturned the 03:29Z grading:
narrow the documented claim to the measured truth. A loud-channel mechanism
(caller-supplied sink / level floor / console sink) is explicitly out of scope and is not
here.
What the prose got wrong
The module header claimed of BOTH channels that anything the capture does not recognise is
"forwarded to the real console untouched", and the engine-gate paragraph said an
unrecognised
DATABASE_ERROR"reaches the log with both halves intact". True on thedriver channel; half-false on the engine channel:
captureDriver's non-matching branch callsconsole.warn/console.errordirectly;captureEngine's non-matching branch callstarget.error(...), wheretargetis theengine's own logger — the
ObjectLoggerthe kernel built from itsloggerconfigand handed to the engine by reference.
ObjectLogger.writereturns early unlessLEVEL_ORDER.error >= LEVEL_ORDER[config.level], so the frame is dropped whenever theconfigured level ranks above
error.The measurement the new prose is written from
A real lean kernel (
ObjectKernel+DriverPlugin(SqliteWasmDriver ':memory:')+ObjectQLPlugin), a real capture on both channels, and one real failing read of a tablethe capture was not told about — so the pair is unrecognised and both channels take
their pass-through path. The driver channel is counted at
console.warn; the enginechannel at
process.stderr(ObjectLoggerprefers the process streams underenvironment: 'node', soconsoleis unreachable there — the same finding this module's[#11571]block already records).logger.levelFind operation failed)infoerrorfatalsilentSample of the engine frame at
error(present) — the same read, atsilent, producedzero lines on
process.stderr:Three things that measurement settles:
composition does reach the log at
errorand atinfo. The positive control is acase in the pin, not a footnote.
silent.fataldrops theframe too. The prose says "ranks above
error(fatal,silent)" for that reason.silentstill withholds onboth channels, counts 1 refusal + 1 engine frame, and
silentChannels()is[].⭐ All four pin cases are green both before and after this PR's comment edits — a
comment edit cannot redden a test. They are regression guards on the behaviour the new
prose describes, never red-before evidence for it.
A correction to the card's own premise, measured
The card states "Every fixture that carries this capture also constructs its kernel with
logger: { level: 'silent' }". That is not what the tree says. Grepping the 19consumers for a kernel actually constructed that way (code, not prose) returns three:
The other sixteen leave the kernel at
createLogger's defaultinfo, where theinforow above says the unrecognised engine frame is logged. The asymmetry the card
reports is real and the three silent fixtures are blind to it exactly as described — but
it is a per-fixture property, not a property of the capture's whole consumer set. The new
prose says that explicitly, so the next reader does not inherit the over-broad reading.
What changed in the comments
console untouched" became "forwarded untouched to the sink that would have received it",
followed by a new
⚠️ [#11569]block stating the per-channel loudness and its exactcondition, a
⇒line saying the engine channel's loudness is the caller's and is notuniform across consumers, and a
⇒line reading the guarantee per channel (driver:pinned both directions; engine: pinned one direction only).
"this capture never swallows it", split into the DRIVER half (reaches the log intact on
the direct console sink) and the ENGINE half (handed back to the engine's own logger;
dropped under
logger: { level: 'silent' }).captureEngine, at the implementation — a new note naming where a non-matchingframe actually goes, why it is deliberately not repaired into a console sink, and what
that does not weaken. Plus a two-line pointer on the interface member.
what "the log" means per channel.
One correction beyond the three the re-ruling names
Called out here because an unnamed drive-by is unreviewable. The
[#11571]block at thefoot of the same file — added by #12145, merged 15:06Z, fifty minutes before the 15:56Z
re-ruling — cross-referenced #11569 as a defect meaning "the two mechanisms here want
repairing before a third is stacked on them". The re-ruling makes that clause false: the
pass-through's level-dependence is now a documented limit, not a pending repair. The
clause was narrowed to say exactly that, in the same file, in the same defect class. No
other text in that block moved, and the block's own conclusion (no third mechanism) is
unchanged.
Scope, measured
git diff --name-only origin/main...HEADlists exactly two paths, both underpackages/runtime/src/.git grep -ln "captureExpectedReadRefusals" -- packagesreturns 20 files onmain—the definition plus 19 consumers. The intersection of that list with this PR's diff is
empty, so neither the 16
packages/runtime/**fixtures nor the twodomain:servicessurfaces (
plugin-approvals,trigger-record-change) is touched.Verification
Gate family derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, re-derived after the change set was final. Every exit codecaptured before any pipe; each verdict below is the gate's own line.
Re-run in full at the final commit
f811e755a6:check:nul-bytes—OK (scanned 6800 text file(s) … no raw ASCII control bytes)check:test-source-alias—OK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/check:cross-package-test-inputs—OK: 16 package(s) read outside themselves, all declaredcheck:engine-double-contract—OK — 409 pinned, 133 in the DEBT ledger, 2 exemptcheck:where-matcher—✓ where-matcher conformance holds: 299 matcher(s) discovered … none newcheck:published-files,check:slot-lookup,check:type-source-resolution— exit 0check:query-options-erasure—✓ ratchet holds: 67 unswept non-test site(s) in 17 file(s), none newcheck:type-check-coverage— exit 0check-plugin-teardown-shape—✓ 63 Plugin implementation(s) across 4714 source(s) … baseline fully burned downcheck-ci-filter-parity—OK: all 96 declared cross-package glob(s) … coveredcheck-affected-docs— exit 0Tests and types at
f811e755a6, under the shared verify lock(
VERDICT command-exit 0 · held the lock 16s):— the new pin plus three consumers of the touched module
(
federated-boot-binding,notifications.hono.integration,cross-field-refusal-operand-withhold).pnpm --filter @objectstack/runtime typecheckexit 0.
Two declared narrowings, so they are not read as full coverage:
pnpm testfor@objectstack/runtimewas narrowed to the pin plus those threeconsumers rather than the whole package suite. The edit outside the new file is
comments only, so no other fixture's behaviour can move; CI runs the full suite
regardless.
pnpm lintwas narrowed to a targetedeslint --no-inline-config --format jsonover the two changed files: 2 files linted, 0 errors, 0 warnings(count read from eslint's own JSON, not asserted). The narrowing is sound because
eslint.config.mjsnever enables type-aware linting (noparserOptions.project,no typed
@typescript-eslintrules) for any file — stated in the config itself, withits own measurement — so a diff confined to these two files cannot move a verdict on
any untouched file.
check:type-check-debt --re-measurewas not run locally: it requires the wholeworkspace closure built and refuses outright otherwise. Not measured here; CI runs it.
No changeset — comments plus one test file, in a module that is not exported from
src/index.tsand therefore not bundled, so this PR publishes nothing. Theskip-changesetlabel carries that declaration (same route as #12145 on this same file).Generated by Claude Code