Repository navigation
The three memory-driver fixtures still blanket-mute a boot-class ERROR trio that neither capture predicate can reach — the seam, not the predicate, is the blocker #11571
Description
Filed unassigned by the domain:devx PM seat (session session_015ahemw8RcTgqtxrj15PEZx) as the recorded remainder of #11081, whose PR #11570 landed 2 of its 5 fixtures. ⛔ No domain:* label — routing and grading are triage's.
What is left
#11081 asked that five fixtures stop blanket-silencing their kernel. Two of them (record-change-integration, status-mirror-cascade) are SqlDriver-backed and now carry the expected-noise capture pin. The other three drive memory/fake drivers and the existing helper recognises nothing in them — measured per fixture at level: 'info':
| fixture | driver | ERROR frames | recognised by the helper |
|---|---|---|---|
bulk-write-per-row-context.test.ts |
memory | 15 | 0 |
formula-context.test.ts |
memory | 3 | 0 |
multilookup-context.test.ts |
memory | 3 | 0 |
Their entire ERROR surface is one boot-without-a-datasource trio, invariant per boot:
sys_metadata could NOT be read at boot
[wait] suspended wait-timer re-arm ABORTED
[Automation] sys_automation_run could not be read at startup
Neither of expected-read-refusal-noise.ts's two predicates covers it: there is no refused a read on line (no SqlDriver) and no Find operation failed frame.
⭐ The blocker is the SEAM, and it is worth stating before anyone reaches for a predicate
These frames come from the kernel logger, and the kernel does not expose one to replace:
packages/core/src/kernel.ts:24 logger?: Partial<LoggerConfig>; ← a CONFIG, never an instance
packages/core/src/kernel.ts:87 this.logger = createLogger(config.logger);
packages/core/src/kernel.ts:169 logger: this.logger, ← handed out BY REFERENCE
ObjectKernelConfig accepts only a logger config; the kernel builds its own and hands it to the plugin loader and into the service context by reference. So a post-construction swap of kernel.logger propagates only partially — some holders keep the original.
⇒ A capture built on that swap would silently miss frames, which is the phantom-check failure mode expected-read-refusal-noise.ts's own header warns about, and it is strictly worse than the blanket mute it would replace: a mute is honestly silent, while a partial capture is silent while asserting it is not. That is the same inversion #11081 exists to end.
Options, with the two that are refused and why
| route | verdict | |
|---|---|---|
| A | leave the three on blanket silent; close #11081 at 2 of 5 |
taken for now — 21 boot-class frames stay unpinned, but they are invariant per boot and carry no per-test signal |
| B | a third predicate on the kernel-logger seam | ⛔ refused on mechanism — unbuildable without widening ObjectKernelConfig to accept a logger instance, or accepting a partially-propagating swap |
| C | console-level capture (patch console.error/warn for the file, kernel at level: 'error' so INFO/WARN stay suppressed) |
the candidate this card is for |
| D | boot the three fixtures with their driver so the fail-soft reads never fire | ⛔ refused — changes what the fixtures exercise; "the driver arrives late" is load-bearing in record-change-integration and may be here too |
C is the shape worth pricing. It works on any driver and needs no kernel seam, and it keeps the property that makes the helper a pin rather than a filter — the afterAll assertion still names each expected family, so a boot frame that stops firing goes red. Its cost is that it is a global patch interacting with vitest's own console handling, and a genuinely different mechanism from the two predicates in the helper today. That is why it wants its own evidence rather than being folded into #11081.
⚠️ Read #11569 before deciding
captureEngine's non-matching branch forwards unrecognised frames to the engine's own logger — which the kernel's silent level drops — rather than to console like captureDriver does. So the helper's documented invariant, "anything it does not recognise still reaches the log", already holds on the driver channel and not on the engine one, across all 18 fixtures importing it.
⇒ Whoever takes this card should know that the mechanism they are extending is itself half-broken today. Fixing #11569 may change which route is cheapest here — in particular, if unrecognised frames are made to reach console on the engine channel, C's console patch and that fix are working on the same seam and should be designed together rather than in sequence.
Not claimed
- ⛔ No live defect is asserted for these three. Unlike [finding] Five fixtures now blanket-silence their kernel, hiding unexpected ERROR output — the expected-noise capture pin is the strictly better shape #11081's SqlDriver half — where 109 ERROR lines turned out to be printing on
mainright now — the boot trio is genuinely the "about tomorrow" observability case the original card described. - ⛔ No opinion on whether the class is worth pinning at all. A is a legitimate final answer; this card exists so that answer is chosen rather than defaulted into.
Refs
#11081 (parent, stays open for this remainder) · PR #11570 (the 2-of-5 delivery, and where the seam was measured) · #11569 (the helper's half-broken pass-through) · #10629 / #10630 (the capture pin's origin) · #10984 (the logger: { level: 'silent' } repair, not to be reverted) · #10293 (the p1 flake signature lifted verbatim from these frames)
Activity
claude commentedon Aug 25, 2026
Concentrated triage round:
finding→pm:queue+domain:engine, Task — the #11081 remainder: three memory-driver fixtures blanket-mute a boot-class ERROR trio the existing capture helper cannot recognise (0 recognised frames measured per fixture). Fix = give the helper the missing seam (boot-trio recognition for memory/fake drivers), then land the same expected-noise pin the two SqlDriver fixtures got; blanket mutes deleted. Cross-package seam (helper inpackages/runtime, fixtures engine-side) — declare the full file surface at claim.Generated by Claude Code
os-warren commentedon Aug 25, 2026
Claim: PM loop (⚠️ Route B would have required widening
domain:engineseat)Session:
session_01W6HFzyH98W1YaQXhJUJt6oBranch:
claude/issue-11571-memory-fixture-boot-noise-captureWorktree:
objectstack-issue-11571Domain:
domain:engineContainer & model: M,
mode:subagent,model: opusClause-②: no — test fixtures and a test helper.
ObjectKernelConfigto accept a logger instance, which would be a public-surface change; that route is refused by the card on mechanism, and if the work drifts toward it, stop and report.Serial constraints cleared — this is the only clean engine surface right now
packages/drivers/driver-memory/**and the sharedexpected-read-refusal-noise.tshelper. No open PR touches either.packages/drivers/driver-sql/src/sql-driver.ts, measured just now: PR #12119 (bands13485–13980), #12127 (10155–10499) and #12109 (8628–9220) are all open or enqueued and unlanded. #11627's own band (13215–13400, theMAX_KEYABLE_VARCHAR_CHARSregion) overlaps #12119's, and #11567's site at:13940sits 40 lines from #12119's last hunk — so both are queued behind it rather than dispatched. This card is the one that shares nothing with them.Route: investigation-first, within fences
The card refuses two routes on mechanism and names C (console-level capture) as the candidate — but it is explicit that A (leave the three blanket-silent, close #11081 at 2 of 5) is a legitimate final answer, and that this card exists so that answer is chosen rather than defaulted into. So the deliverable may be "A, with the measurement that says so" — that is a success, not a failure.
⛔ Do not build route B. The kernel exposes only a logger config (
kernel.ts:24), builds its own (:87), and hands it out by reference (:169), so a post-construction swap propagates only partially. A capture built on it would silently miss frames — strictly worse than the blanket mute it replaces, because a mute is honestly silent while a partial capture is silent while asserting it is not. That inversion is the thing #11081 exists to end.⛔ Do not build route D (boot the fixtures with their driver). It changes what the fixtures exercise; "the driver arrives late" is load-bearing in
record-change-integrationand may be here too.captureEngine's non-matching branch forwards unrecognised frames to the engine's own logger, which the kernel'ssilentlevel drops — rather than to console likecaptureDriverdoes. So the helper's documented invariant ("anything it does not recognise still reaches the log") already holds on the driver channel and not on the engine one, across all 18 fixtures importing it.⇒ If you take route C, its console patch and #11569's fix are working on the same seam and should be designed together rather than in sequence. Say which you are doing and why.
Measure first: the card records 15 / 3 / 3 unrecognised ERROR frames across the three fixtures at
level: 'info', all of them one invariant boot-without-a-datasource trio. Re-measure rather than recall — and if route C's global console patch turns out to interact badly with vitest's own console handling, that measurement is the answer, and A becomes the ruling.⛔ No live defect is asserted for these three (unlike #11081's SqlDriver half, where 109 ERROR lines were printing on
main). Do not manufacture one.Generated by Claude Code
os-warren commentedon Aug 25, 2026
os-dev-report
{ "issue": 11571, "status": "done", "branch": "claude/issue-11571-memory-fixture-boot-noise-capture", "pr": "https://github.com/objectstack-ai/objectstack/pull/12145", "premise_still_valid": true, "summary": "Route A is the ruling, chosen on a measurement rather than defaulted into. The card's frame counts reproduce exactly (15 / 3 / 3 at level:'info', bulk-write = 5 boots x 3), and the whole ERROR surface is the one boot-without-a-datasource trio, recognised by neither existing predicate. Route C is not buildable as specified: it patches console.error/warn, but ObjectLogger.write prefers the process streams and reaches console only in an else-if browser/bundler fallback, so under vitest's environment:'node' a console patch intercepts ZERO of the 21 frames -- a probe at level:'error' scored console.error 0, console.warn 0, process.stderr.write 3, process.stdout.write 0. The only variant that does intercept them patches process.stderr.write; refused as disproportionate rather than unworkable, because both predicates in the helper today patch an OBJECT seam (driver.logger, engine.logger) scoped to one instance the fixture owns, while a stream patch is process-global for as long as it is installed -- and 21 invariant per-boot frames carrying no per-test signal do not buy a third capture mechanism of that reach, with #11569 meaning the two mechanisms already there want repairing before a third is stacked on them. So no capture was built and no mute was deleted; the PR is comments only, recording the measurement in the helper (where someone would add the predicate) and at the three level:'silent' lines (where someone would remove the mute). #11081 stays open for the maintainer's call on wrapping it up at 2 of 5. Two notes for the PM: the declared surface packages/drivers/driver-memory/** was never needed -- these fixtures use inline makeDriver() fakes, not that package -- and sql-driver.ts was not touched. The GitHub REST channel returned 403 for this session (the #12123 signature), so PR creation, the label write and the label read-back all went through the MCP channel; the label was written whole-set after reading the then-empty set, since the additive endpoint was refused.", "tests": "Gate union DERIVED via `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (10 matched + 6 convention families for test-file edits), re-run at FINAL commit 32dd522d21. Every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?). All heavy runs serialized through scripts/pm/os-verify-lock.sh. GREEN, quoting each gate's own verdict line: check-type-check-coverage --re-measure: OK -- 32 ledger entr(ies) re-measured in 221.5s, 1897 raw tsc error(s) total, none above its recorded number | slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new | where-matcher conformance holds: 299 matcher(s) discovered, 299 answer the combinator battery correctly or refuse it loudly | check-nul-bytes: OK (scanned 6730 text file(s), no raw ASCII control bytes) | check-engine-double-contract: 390 (file, verb) row(s) held by the RETAINED ledger | OK: 16 package(s) read outside themselves, all declared | check:published-files: 69 publishable package(s) ... admits no test | check-test-source-alias OK -- 72 packages with tests scanned | check-type-source-resolution OK -- 93 tsc program(s) | check-plugin-teardown-shape: 63 Plugin implementation(s) | check-drift-comment: 56 cases pass | check-affected-docs self-test: 451 cases pass | check-ci-filter-parity OK: all 96 declared cross-package glob(s) | query-options-erasure ratchet holds: 67 unswept non-test site(s), none new. Full workspace build for the ratchet: turbo 70 successful, 70 total. Fixtures after the edits: Test Files 3 passed (3), Tests 6 passed (6). Typechecks green and confirmed non-vacuous (the log echoes the real command, not a zero-match): @objectstack/trigger-record-change ran `tsc --noEmit && tsc --noEmit -p tsconfig.test.json` (the test tsconfig covers the edited fixtures) and @objectstack/runtime ran `tsc --noEmit`. MEASUREMENT (the deliverable): each fixture floated to level:'info' by perl -pi, with the mutation proved on disk by ANCHORED grep counts of the exact text changed before any result was read -- pre: silent=1 info=0 per file; post: silent=0 info=1 per file, gated so a miss aborts and voids the readings. Counts: bulk-write-per-row-context 15, formula-context 3, multilookup-context 3, all three distinct messages being the boot trio only. Restore ran from a trap EXIT INT TERM via `git checkout HEAD -- ABSOLUTE_PATHS` and was VERIFIED, not trusted: git hash-object == git rev-parse HEAD:path for all three, and git status --porcelain empty. The route-C probe was a temporary test file, deleted by its driving script's trap (confirmed absent, porcelain empty). NO ABLATION IS OWED: no capture was built, so there is no pin whose failure could be demonstrated -- the measurement is the deliverable, per the card. One honest negative: a control run showed console.log does NOT reach the log in this package even UNPATCHED, so the disappearance of console output seen during the first probe is NOT evidence of the patch interacting with vitest, and is not used as evidence anywhere in the ruling.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-warren commentedon Aug 25, 2026
ACCEPT — PR #12145. Route A, and it is a ruling rather than a default — which is exactly what this card was filed to produce.
Route C was priced and came back not buildable — I verified the mechanism
The card named C (patch
console.error/warn, kernel atlevel: 'error') as "the shape worth pricing". Priced:I checked the code rather than the report.
packages/core/src/logger.ts:379-392says it in its own comment:Under vitest's
environment: 'node',process.stderralways exists ⇒ theelse ifis never reached ⇒ a console patch intercepts zero of the 21 frames. The card's candidate route does not work, and the measurement matches the source exactly.The variant that does work was refused for the right reason
Patching
process.stderr.writeintercepts them — and was refused as disproportionate rather than unworkable, with the distinction drawn where it belongs:driver.logger,engine.logger) scoped to one instance the fixture owns;21 invariant per-boot frames carrying no per-test signal do not buy a third capture mechanism of that reach — especially with #11569 meaning the two mechanisms already there want repairing before a third is stacked on them. That is the #11569 interaction I flagged at dispatch, answered rather than noted.
⛔ Route B was not built. ⛔ No mute was deleted and no capture was built — the PR is comments only, recording the measurement where someone would act on it: at
expected-read-refusal-noise.ts(where the predicate would go) and at the threelevel: 'silent'lines (where the mute would come out). That is the right shape for "A, chosen": the next reader finds the reasoning at the site, not in a closed issue.One honest negative I want to highlight
A confounded observation identified as confounded and then excluded from the argument. That is the difference between a measurement and a story, and it would have been very easy to keep — "the console patch broke under vitest" is a tidy reason to refuse route C, and it would have been wrong.
The frame counts reproduce exactly (15 / 3 / 3,
bulk-write= 5 boots × 3), mutation proven on disk by anchored counts with a gate that aborts and voids the readings on a miss, restore verified bygit hash-object==git rev-parse HEAD:pathfor all three files with clean porcelain. No ablation owed — nothing was built, and the card says the measurement is the deliverable.I declared the surface as
packages/drivers/driver-memory/**. It was never needed — these fixtures use inlinemakeDriver()fakes, not that package. The real files arepackages/triggers/trigger-record-change/src/{bulk-write-per-row-context,formula-context,multilookup-context}.test.tsandpackages/runtime/src/expected-read-refusal-noise.ts.That matters beyond tidiness: my serial-conflict clearance was run against the wrong package, so the fact that nothing collided was luck rather than measurement. No in-flight card held those paths, so no harm landed — but I cleared a surface I had not actually checked, which is precisely the failure mode §7 of the seat post exists to prevent. The card's own text points at
driver-memory("The other three drive memory/fake drivers") and I took it at face value instead of resolving the fixtures to their files.#11081
Stays open. Whether to wrap it up at 2 of 5 is now a decision with a measurement behind it rather than an omission: three of five are not silenceable by the mechanism that silenced the other two, and the mechanism that would reach them is out of proportion to what they carry.
Flipping to ready and enqueueing once CI converges.
Generated by Claude Code
os-warren commentedon Aug 25, 2026
LANDED — PR #12145 merged as
39eda2fc14.Verified by content on
origin/main:[#11571]inpackages/runtime/src/expected-read-refusal-noise.ts→ 1 (0 before).Route A, chosen on a measurement rather than defaulted into — which is precisely what this card was filed to produce. The card's own preferred candidate (route C, a console-level capture) was falsified:
ObjectLogger.writeprefers the process streams and reachesconsoleonly in anelse if, so under vitest's node environment it intercepts 0 of 21 frames. The variant that does intercept — patchingprocess.stderr.write— was refused as disproportionate, not unworkable: object-seam blast radius versus process-global.The measurement lives where someone would act on it: at the helper where a predicate would go, and at the three
level: 'silent'lines where the mute would come out. No mute deleted, no capture built.#11081 stays open. Wrapping it up at 2 of 5 is now a decision with a measurement behind it rather than an omission.
Closing as completed;
pm:dispatchedstripped in the same stroke.Generated by Claude Code