diff --git a/packages/runtime/src/expected-read-refusal-noise.ts b/packages/runtime/src/expected-read-refusal-noise.ts
index cf4bdb6760..d506e10494 100644
--- a/packages/runtime/src/expected-read-refusal-noise.ts
+++ b/packages/runtime/src/expected-read-refusal-noise.ts
@@ -415,3 +415,73 @@ export function captureExpectedCrossFieldRefusalNoise(
},
};
}
+
+/**
+ * ═══════════════════════════════════════════════════════════════════════════
+ * [#11571] ⛔ There is deliberately NO third, console-level predicate — the
+ * kernel's ERROR frames never pass through `console` under Node
+ * ═══════════════════════════════════════════════════════════════════════════
+ *
+ * ## The case that asked for one
+ *
+ * Three memory-driver fixtures in `@objectstack/trigger-record-change`
+ * (`bulk-write-per-row-context`, `formula-context`, `multilookup-context`)
+ * boot a kernel with no datasource, so every boot-time fail-soft read refuses.
+ * Measured on this branch by floating each fixture to `level: 'info'`, their
+ * ENTIRE ERROR surface is one invariant trio, repeated once per boot:
+ *
+ * | fixture | ERROR frames | distinct messages |
+ * |------------------------------|---------------|-------------------|
+ * | `bulk-write-per-row-context` | 15 (5 boots) | 3 |
+ * | `formula-context` | 3 (1 boot) | 3 |
+ * | `multilookup-context` | 3 (1 boot) | 3 |
+ *
+ * 1. `sys_metadata could NOT be read at boot …` (`objectql/src/plugin.ts`);
+ * 2. `[wait] suspended wait-timer re-arm ABORTED …`
+ * (`service-automation/src/builtin/wait-node.ts`);
+ * 3. `[Automation] sys_automation_run could not be read at startup …`
+ * (`service-automation/src/plugin.ts`).
+ *
+ * Neither predicate above can reach them: with no SqlDriver there is no
+ * `refused a read on '
'` line — so no `pending` entry for the engine
+ * gate to sit above — and no `Find operation failed` frame at all. Those three
+ * fixtures therefore KEEP the blanket `logger: { level: 'silent' }` that the
+ * two SqlDriver-backed fixtures were able to drop.
+ *
+ * ## Why the obvious third mechanism does not exist
+ *
+ * The proposal was a console-level capture: patch `console.error`/`console.warn`
+ * for the file and float the kernel to `level: 'error'` so INFO/WARN stay
+ * suppressed. **Measured: it captures ZERO of the 21 frames.**
+ * `ObjectLogger.write` (`core/src/logger.ts`) prefers the process streams and
+ * reaches console only as a fallback:
+ *
+ * if (stream) { // process.stderr for error/fatal
+ * stream.write(line + '\n');
+ * } else if (typeof console !== 'undefined') {
+ * … console.error … // browsers / bundler shims ONLY
+ * }
+ *
+ * Under vitest's `environment: 'node'`, `process.stderr` always exists, so the
+ * `console` arm is unreachable there. A probe booting this exact plugin stack
+ * at `level: 'error'` with all four sinks counted 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`,
+ * and it is refused here as disproportionate rather than as unworkable. Both
+ * predicates above patch an OBJECT seam (`driver.logger`, `engine.logger`)
+ * whose blast radius is one instance the fixture itself owns; a stream patch
+ * is process-global and sits in the path of everything the worker writes —
+ * the reporter's own diagnostics included — for as long as it is installed.
+ * Twenty-one invariant per-boot frames that carry no per-test signal do not
+ * buy a third capture mechanism of that reach, and #11569 (this module's
+ * engine pass-through already lands in a silenced logger) means the two
+ * mechanisms here want repairing before a third is stacked on them.
+ *
+ * ⛔ Nor is the repair a predicate pointed at `kernel.logger`: the kernel takes
+ * a logger CONFIG, builds its own and hands it to the plugin loader and the
+ * service context BY REFERENCE (`core/src/kernel.ts`), so a post-construction
+ * swap propagates only partially. That yields a capture which misses frames
+ * while asserting it does not — the exact inversion this module exists to
+ * prevent, and strictly worse than the honest silence of a blanket mute.
+ */
diff --git a/packages/triggers/trigger-record-change/src/bulk-write-per-row-context.test.ts b/packages/triggers/trigger-record-change/src/bulk-write-per-row-context.test.ts
index 6d7afb730c..a4fe3b207f 100644
--- a/packages/triggers/trigger-record-change/src/bulk-write-per-row-context.test.ts
+++ b/packages/triggers/trigger-record-change/src/bulk-write-per-row-context.test.ts
@@ -113,6 +113,12 @@ function makeDriver(): any {
}
async function bootStack() {
+ // [#11571] The blanket mute is kept here deliberately. With no datasource
+ // this boot emits one invariant 3-frame ERROR trio that neither predicate
+ // in `runtime/src/expected-read-refusal-noise.ts` can recognise, and the
+ // console-level capture proposed to reach it intercepts ZERO: ObjectLogger
+ // writes ERROR to `process.stderr`, never through `console`, under Node.
+ // See that module's closing section for the measurement.
const kernel = new ObjectKernel({ logger: { level: 'silent' } });
await kernel.use(new ObjectQLPlugin());
await kernel.use(new AutomationServicePlugin());
diff --git a/packages/triggers/trigger-record-change/src/formula-context.test.ts b/packages/triggers/trigger-record-change/src/formula-context.test.ts
index 3672183f8e..5da9272bc0 100644
--- a/packages/triggers/trigger-record-change/src/formula-context.test.ts
+++ b/packages/triggers/trigger-record-change/src/formula-context.test.ts
@@ -67,6 +67,12 @@ function makeDriver(): any {
describe('record-change context hydrates read-time formula fields (#3426)', () => {
it('resolves a formula field ({record.full_name}) in a seeded flow record', async () => {
+ // [#11571] The blanket mute is kept here deliberately. With no datasource
+ // this boot emits one invariant 3-frame ERROR trio that neither predicate
+ // in `runtime/src/expected-read-refusal-noise.ts` can recognise, and the
+ // console-level capture proposed to reach it intercepts ZERO: ObjectLogger
+ // writes ERROR to `process.stderr`, never through `console`, under Node.
+ // See that module's closing section for the measurement.
const kernel = new ObjectKernel({ logger: { level: 'silent' } });
await kernel.use(new ObjectQLPlugin());
await kernel.use(new AutomationServicePlugin());
diff --git a/packages/triggers/trigger-record-change/src/multilookup-context.test.ts b/packages/triggers/trigger-record-change/src/multilookup-context.test.ts
index 6f5624b092..fe6521884a 100644
--- a/packages/triggers/trigger-record-change/src/multilookup-context.test.ts
+++ b/packages/triggers/trigger-record-change/src/multilookup-context.test.ts
@@ -56,6 +56,12 @@ function makeDriver(): any {
describe('record-change context hydrates multi-lookup from input (#1872)', () => {
it('fires a record-after-create flow gated on a multi-lookup the driver did not echo', async () => {
+ // [#11571] The blanket mute is kept here deliberately. With no datasource
+ // this boot emits one invariant 3-frame ERROR trio that neither predicate
+ // in `runtime/src/expected-read-refusal-noise.ts` can recognise, and the
+ // console-level capture proposed to reach it intercepts ZERO: ObjectLogger
+ // writes ERROR to `process.stderr`, never through `console`, under Node.
+ // See that module's closing section for the measurement.
const kernel = new ObjectKernel({ logger: { level: 'silent' } });
await kernel.use(new ObjectQLPlugin());
await kernel.use(new AutomationServicePlugin());