diff --git a/.changeset/runtime-expected-read-refusal-noise.md b/.changeset/runtime-expected-read-refusal-noise.md
new file mode 100644
index 0000000000..478cfb4590
--- /dev/null
+++ b/.changeset/runtime-expected-read-refusal-noise.md
@@ -0,0 +1,37 @@
+---
+"@objectstack/runtime": patch
+---
+
+**Tests (log hygiene):** the sixteen remaining passing `@objectstack/runtime`
+fixtures that printed expected `refused a read on` failures into the shared
+shard log now **withhold and assert** that noise instead of emitting it
+(#10629). No runtime behaviour changes and no test was skipped, loosened or
+removed — the same 78 tests pass, and 268 lines of expected-failure output
+(134 `[sql-driver] DATABASE_ERROR — the backend refused a read on '
'`
+envelopes plus their 134 matching `ERROR Find operation failed` engine frames)
+leave the `Test Core` log.
+
+Why this is worth a release note at all: turbo interleaves package logs without
+attribution, so an ERROR-shaped line from a **green** test is indistinguishable
+from a real failure in a shard log. Lines of exactly this shape were once
+lifted verbatim into a p1 flake signature (#10293) and sent a whole dispatch
+cycle at the wrong mechanism. Expected-failure noise from a passing test is a
+diagnosis tax on every future red shard.
+
+Each fixture provokes a **fail-soft probe** — a read the runtime issues to find
+out whether something is installed, and whose missing-table answer it swallows
+by design: `resolveUserAuthzGrants`' six `sys_*` `tryFind`s,
+`ObjectQL.probeInstallOrganizations`, `SeedLoaderService.resolveSoleOrganizationId`,
+the lifecycle governance snapshot, `runBuildProbes`' view read, and the boot
+metadata load. Every one of them was judged expected rather than diagnostic;
+none was silenced on the strength of "it looks like noise".
+
+⛔ This is not a mute. PR #10630 ruled the shape for this class on two files —
+withhold only a line that names an expected table **and** carries that same
+table's `no such table` reason, count what was withheld, and assert the counts —
+and this applies that shape verbatim through one shared, test-only module,
+`packages/runtime/src/expected-read-refusal-noise.ts`. A fixture that stopped
+provoking its probe, or whose table started resolving, now goes **red** instead
+of merely going quiet; the engine frame is withheld only when it sits directly
+above a driver refusal the capture already recognised, so an identically-shaped
+fault from any other cause still reaches the log with both halves intact.
diff --git a/packages/runtime/src/batch-row-driver-text-real-driver.integration.test.ts b/packages/runtime/src/batch-row-driver-text-real-driver.integration.test.ts
index 10ace48e5a..ac27a2c6fa 100644
--- a/packages/runtime/src/batch-row-driver-text-real-driver.integration.test.ts
+++ b/packages/runtime/src/batch-row-driver-text-real-driver.integration.test.ts
@@ -45,6 +45,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { validationFailureDetails, resolveThrownHttpError } from '@objectstack/types';
const PARENT = { name: 'bd_parent', fields: { name: { type: 'text' } } };
@@ -63,7 +67,20 @@ const NOTE = {
},
};
+/**
+ * [#10629] This fixture provisions its own business objects and nothing else,
+ * so the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ * Withheld and asserted rather than muted; `expected-read-refusal-noise.ts`
+ * says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('[#8502] a REAL driver fault is withheld from every batch row', () => {
+ /** [#10629] The expected-noise capture belonging to the latest rig. */
+ let noise: ExpectedReadRefusalCapture | null = null;
let dir: string | null = null;
let engine: ObjectQL | null = null;
@@ -71,6 +88,12 @@ describe('[#8502] a REAL driver fault is withheld from every batch row', () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so
+ // a failure here can never leave the engine running. Every test in this
+ // file rigs and writes, so the probe fires for each of them: this holds
+ // for a single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function rig() {
@@ -80,6 +103,11 @@ describe('[#8502] a REAL driver fault is withheld from every batch row', () => {
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed on the REAL driver (the one that logs) before it
+ // runs a statement — the `Object.create(real)` wrapper below resolves
+ // `logger` through the prototype chain to this sink.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(real);
await real.initObjects([PARENT, CHILD, NOTE]);
// Capture the RAW driver error at the seam and let it propagate
@@ -94,6 +122,7 @@ describe('[#8502] a REAL driver fault is withheld from every batch row', () => {
}
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
for (const o of [PARENT, CHILD, NOTE]) {
diff --git a/packages/runtime/src/batch-row-http-status-real-driver.integration.test.ts b/packages/runtime/src/batch-row-http-status-real-driver.integration.test.ts
index e1cb603f8c..a64548bae9 100644
--- a/packages/runtime/src/batch-row-http-status-real-driver.integration.test.ts
+++ b/packages/runtime/src/batch-row-http-status-real-driver.integration.test.ts
@@ -42,6 +42,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { resolveThrownHttpError, validationFailureDetails } from '@objectstack/types';
/** `maxLength` is what the real record validator rejects against. */
@@ -61,7 +65,29 @@ const CHILD = {
},
};
+/**
+ * [#10629] This fixture provisions its three business objects and nothing else,
+ * so the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ *
+ * ⛔ Asserted in the three tests that WRITE rather than in `afterEach`: the
+ * probe runs on the system-write org resolution, so the not-found test (which
+ * only deletes a ghost id) never reaches it. An `afterEach` assertion would
+ * make that test red for a reason that has nothing to do with it.
+ * `expected-read-refusal-noise.ts` says why this withholds instead of muting.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
+/** [#10629] The capture is a PIN, not a mute — this is the assertion half. */
+const expectExpectedNoiseWithheld = (noise: ExpectedReadRefusalCapture | null): void => {
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+};
+
describe('[#8570] a batch row carries the status its producer DECLARED — real driver', () => {
+ /** [#10629] The expected-noise capture belonging to the latest rig. */
+ let noise: ExpectedReadRefusalCapture | null = null;
let dir: string | null = null;
let engine: ObjectQL | null = null;
@@ -78,6 +104,11 @@ describe('[#8570] a batch row carries the status its producer DECLARED — real
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed on the REAL driver (the one that logs) before it
+ // runs a statement — the `Object.create(real)` wrapper below resolves
+ // `logger` through the prototype chain to this sink.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(real);
await real.initObjects([TASK, PARENT, CHILD]);
// Capture the RAW error at the seam and let it propagate untouched, so
@@ -92,6 +123,7 @@ describe('[#8570] a batch row carries the status its producer DECLARED — real
}
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
for (const o of [TASK, PARENT, CHILD]) {
@@ -141,6 +173,8 @@ describe('[#8570] a batch row carries the status its producer DECLARED — real
// …and the record is unchanged, so the refusal was a refusal.
expect((await engine!.findOne('hs_task', { where: { id: 'ok1' } }))?.name).toBe('ok');
+
+ expectExpectedNoiseWithheld(noise);
});
it('the same row in a MIXED batch — the asymmetry the card measured is gone', async () => {
@@ -164,6 +198,8 @@ describe('[#8570] a batch row carries the status its producer DECLARED — real
});
expect(res.results[1].errors[0].code).toBe('VALIDATION_FAILED');
expect(res.results[1].errors[0].httpStatus).toBe(400);
+
+ expectExpectedNoiseWithheld(noise);
});
it('a REAL driver fault still carries NO status — the over-broad direction', async () => {
@@ -195,6 +231,8 @@ describe('[#8570] a batch row carries the status its producer DECLARED — real
// causal row's text onto its siblings, so a row-only scan can miss a
// live path.
expect(JSON.stringify(res)).not.toContain('httpStatus');
+
+ expectExpectedNoiseWithheld(noise);
});
it('a not-found row still answers 404 — the population that already worked', async () => {
diff --git a/packages/runtime/src/bulk-write-real-driver.integration.test.ts b/packages/runtime/src/bulk-write-real-driver.integration.test.ts
index 6daf3c8dc0..a8700d0f54 100644
--- a/packages/runtime/src/bulk-write-real-driver.integration.test.ts
+++ b/packages/runtime/src/bulk-write-real-driver.integration.test.ts
@@ -16,6 +16,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SeedLoaderService } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
/**
* Wrap a real driver so specific methods can fail the way turso does. Hooks
@@ -93,22 +97,48 @@ function metadataFor(objects: any[]) {
} as any;
}
+/**
+ * [#10629] This fixture provisions its own business objects and nothing else,
+ * so the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ * Withheld and asserted rather than muted; `expected-read-refusal-noise.ts`
+ * says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('bulk-write hardening on a REAL SqlDriver (framework#3147–#3152, #3172, #3173)', () => {
let dir: string | null = null;
let engine: ObjectQL | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. Every test in this file
+ // boots and writes, so the probe fires for each of them: this holds for a
+ // single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function boot(objects: any[], plan: FaultPlan = {}) {
dir = mkdtempSync(join(tmpdir(), 'os-bulk-real-'));
const real = new SqlDriver({ client: 'better-sqlite3', connection: { filename: join(dir, 'data.sqlite') }, useNullAsDefault: true });
+ // [#10629] Installed on the REAL driver (the one that logs) before it runs
+ // a statement, and on the engine before it issues a read — the two sinks the
+ // expected refusal travels out on. `wrapDriver` prototype-delegates, so the
+ // wrapper resolves `logger` through the chain to this sink.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(real);
await real.initObjects(objects); // create real tables + sequences
const driver = wrapDriver(real, plan);
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
// Fast, deterministic summary-retry backoff (framework#3147).
diff --git a/packages/runtime/src/cascade-delete-multivalue-lookup-real-driver.integration.test.ts b/packages/runtime/src/cascade-delete-multivalue-lookup-real-driver.integration.test.ts
index 3dc4c2772f..ff26c9bc48 100644
--- a/packages/runtime/src/cascade-delete-multivalue-lookup-real-driver.integration.test.ts
+++ b/packages/runtime/src/cascade-delete-multivalue-lookup-real-driver.integration.test.ts
@@ -37,6 +37,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
const ACCOUNT = { name: 'zz_account', fields: { name: { type: 'text' } } };
@@ -84,14 +88,33 @@ const SINGLE = {
const OWNER_PACKAGE = 'com.objectstack.test.9362';
+/**
+ * [#10629] This fixture provisions its own business objects and nothing else,
+ * so the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ * Withheld and asserted rather than muted; `expected-read-refusal-noise.ts`
+ * says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('[#9362] REST DELETE on an object targeted by a multiple:true lookup — real driver', () => {
let dir: string | null = null;
let engine: ObjectQL | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest rig. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so
+ // a failure here can never leave the engine running. Every test in this
+ // file rigs and writes, so the probe fires for each of them: this holds
+ // for a single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function rig(objects: unknown[]) {
@@ -101,8 +124,13 @@ describe('[#9362] REST DELETE on an object targeted by a multiple:true lookup
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(real);
await real.initObjects(objects as any);
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(real as any, true);
await engine.init();
for (const o of objects) engine.registry.registerObject(o as any, OWNER_PACKAGE);
diff --git a/packages/runtime/src/default-datasource-plugin.test.ts b/packages/runtime/src/default-datasource-plugin.test.ts
index 8beb074961..415c3cd553 100644
--- a/packages/runtime/src/default-datasource-plugin.test.ts
+++ b/packages/runtime/src/default-datasource-plugin.test.ts
@@ -11,6 +11,7 @@ import type { IDataEngine } from '@objectstack/spec/contracts';
import { Runtime } from './runtime.js';
import { DefaultDatasourcePlugin } from './default-datasource-plugin.js';
import { AppPlugin } from './app-plugin.js';
+import { captureExpectedReadRefusals } from './expected-read-refusal-noise.js';
// [#10126] Pay the first transform of these dist-resolved workspace deps at MODULE
// LOAD. Each is reached below through a dynamic `import()` inside an `it()` body or a
@@ -24,6 +25,17 @@ import '@objectstack/service-datasource';
const BOOT_TIMEOUT = 60_000;
const ENV = 'OS_ALLOW_DRIVER_CONNECT_FAILURE';
+/**
+ * [#10629] Exactly one case in this file writes through the booted engine, and
+ * that write runs the engine's single-tenant probe
+ * (`ObjectQL.probeInstallOrganizations`) against a `sys_organization` this
+ * composition never creates. The probe is fail-soft by construction — it
+ * catches `isMissingTableError` and only that — but the driver and the engine
+ * each log the fault on the way out. Withheld and asserted in that one case
+ * rather than muted; `expected-read-refusal-noise.ts` says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
async function assemble(opts: {
driver?: string;
withAdminPlugin?: boolean;
@@ -158,6 +170,11 @@ describe('DefaultDatasourcePlugin — the default datasource as a declaration (#
const { createPrebuiltDriverFactory } = await import('@objectstack/service-datasource');
const { SqliteWasmDriver } = await import('@objectstack/driver-sqlite-wasm');
const hostBuilt = new SqliteWasmDriver({ filename: ':memory:' });
+ // [#10629] Installed before the driver runs a statement; the engine half
+ // is scoped after bootstrap, because the read it covers is the `insert`
+ // below rather than anything the boot itself does.
+ const noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(hostBuilt);
const kernel = await assemble({
driver: 'turso', // a kind the SHARED factory does not support — proves dispatch
factory: createPrebuiltDriverFactory(hostBuilt, { driverId: 'turso' }),
@@ -168,6 +185,7 @@ describe('DefaultDatasourcePlugin — the default datasource as a declaration (#
});
try {
await kernel.bootstrap();
+ noise.captureEngine(kernel.getService('objectql'));
const engine = kernel.getService('data');
const defaultName = engine.getDefaultDriverName?.();
expect(defaultName).toBeDefined();
@@ -176,6 +194,10 @@ describe('DefaultDatasourcePlugin — the default datasource as a declaration (#
await engine.insert('note', { title: 'through-the-adopted-default' });
const rows = await engine.find('note');
expect(rows.map((r: any) => r.title)).toContain('through-the-adopted-default');
+ // [#10629] The capture is a PIN, not a mute: the probe's two log lines
+ // are withheld from the shared shard log and asserted here instead, so
+ // a probe that stopped running goes red rather than merely quiet.
+ expect(noise.silentChannels()).toEqual([]);
} finally {
try { await (kernel as any)?.stop?.(); } catch { /* noop */ }
}
diff --git a/packages/runtime/src/expand-nested-fields-join-key.integration.test.ts b/packages/runtime/src/expand-nested-fields-join-key.integration.test.ts
index b904ed1b72..a3f430e015 100644
--- a/packages/runtime/src/expand-nested-fields-join-key.integration.test.ts
+++ b/packages/runtime/src/expand-nested-fields-join-key.integration.test.ts
@@ -39,6 +39,10 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
const ACCOUNT = {
name: 'showcase_account',
@@ -73,14 +77,32 @@ const TASK = {
},
};
+/**
+ * [#10629] This fixture provisions the four business objects it queries and nothing else, so the engine's
+ * own single-tenant probe (`ObjectQL.probeInstallOrganizations`, memoised once
+ * per engine) reads a `sys_organization` that was never created. The probe is
+ * fail-soft by construction — it catches `isMissingTableError` and only that —
+ * but the driver and the engine each log the fault on the way out. Withheld and
+ * asserted rather than muted; `expected-read-refusal-noise.ts` says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('#7537 expand with a nested `fields` that omits the join key (REAL SqlDriver)', () => {
let engine: ObjectQL | null = null;
let dir: string | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. Every test in this file
+ // boots and writes, so the probe fires for each of them: this holds for a
+ // single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function boot() {
@@ -90,8 +112,13 @@ describe('#7537 expand with a nested `fields` that omits the join key (REAL SqlD
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(driver);
await driver.initObjects([ACCOUNT, INVOICE, PROJECT, TASK]);
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
for (const obj of [ACCOUNT, INVOICE, PROJECT, TASK]) {
diff --git a/packages/runtime/src/expected-read-refusal-noise.ts b/packages/runtime/src/expected-read-refusal-noise.ts
new file mode 100644
index 0000000000..69ba40acb5
--- /dev/null
+++ b/packages/runtime/src/expected-read-refusal-noise.ts
@@ -0,0 +1,245 @@
+// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
+
+/**
+ * ═══════════════════════════════════════════════════════════════════════════
+ * [#10629] Expected `refused a read on` noise: WITHHELD from the shared log,
+ * and ASSERTED instead — the shape PR #10630 landed, factored out
+ * ═══════════════════════════════════════════════════════════════════════════
+ *
+ * ## The defect this closes
+ *
+ * A `@objectstack/runtime` fixture that provisions some objects and not others
+ * makes the runtime probe the ones it did not provision. Several of those
+ * probes are **fail-soft by construction** — they exist to answer "is this
+ * installed?" and treat a missing table as "no":
+ *
+ * * `resolveUserAuthzGrants` (`core/src/security/resolve-authz-context.ts`)
+ * `tryFind`s six `sys_*` tables per grant resolution — the resolver is
+ * fail-closed and must always resolve;
+ * * `ObjectQL.probeInstallOrganizations` (`objectql/src/engine.ts`) reads
+ * `sys_organization` and catches `isMissingTableError` **only**, which its
+ * own doc comment names as "the one benign cause";
+ * * `SeedLoaderService.resolveSoleOrganizationId`
+ * (`metadata-protocol/src/seed-loader.ts`) and
+ * `LifecycleService`'s governance snapshot read the same table best-effort;
+ * * `runBuildProbes` (`metadata-protocol/src/build-probes.ts`) reads the
+ * object a published view is bound to, and turns a failure into a
+ * `view_read_failed` publish issue rather than an exception;
+ * * the boot metadata load reads `sys_metadata` before anything created it.
+ *
+ * Each of those swallows the fault — but on the way out the driver and the
+ * engine have each already logged it:
+ *
+ * 1. `[sql-driver] DATABASE_ERROR — the backend refused a read on ''
+ * … no such table: `, from `SqlDriver.backendStatementFault`
+ * through the driver's own `logger.warn`;
+ * 2. `ERROR Find operation failed {"object":"",…}` one frame up
+ * (`objectql/src/engine.ts`), carrying the same fault as a stack.
+ *
+ * Turbo interleaves package logs without attribution, so in the shared shard
+ * log those are indistinguishable from a real failure. Not a hypothetical:
+ * lines of exactly this shape were lifted VERBATIM into a p1 flake signature
+ * (#10293) and sent a whole dispatch cycle at the wrong mechanism.
+ * Expected-failure noise from a green test is a diagnosis tax on every future
+ * red shard.
+ *
+ * ## ⛔ What this is NOT is a mute
+ *
+ * Silencing alone would make a fixture BLIND: if the probed read ever stopped
+ * happening (the runtime dropped it) or started SUCCEEDING (someone
+ * provisioned the table), the log would go quiet and NOTHING would notice — a
+ * live pin quietly demoted to a decoration. So each sink here does two things
+ * instead of one:
+ *
+ * * it withholds ONLY the expected fault — the line must name one of the
+ * caller's declared tables AND carry that same table's `no such table`
+ * reason. Any other table, any other reason, any other level and any other
+ * logger method is forwarded to the real console untouched;
+ * * it COUNTS what it withheld, per table and per channel, and the caller
+ * asserts those counts. A capture nobody asserts is a mute.
+ *
+ * ⛔ The engine gate is deliberately the narrowest of the two: a frame is
+ * withheld only when it sits directly above a driver refusal this capture
+ * already recognised ({@link ExpectedReadRefusalCapture.pending}). A
+ * `DATABASE_ERROR` on one of the same tables arising from any OTHER cause is
+ * not recognised by the driver sink, so its frame is not withheld here either
+ * — it reaches the log with both halves intact.
+ *
+ * ⛔ Nothing here reads or relaxes a fixture's own assertions. This is the
+ * console side-effect only.
+ *
+ * ## Why a shared module rather than a copy per fixture
+ *
+ * PR #10630 established this shape on two files and wrote it inline in each.
+ * The enumerated remainder is sixteen more, across four distinct probe sites,
+ * and sixteen copies of one predicate is sixteen places for it to drift —
+ * including drifting *looser*, which is the direction that turns a pin back
+ * into a mute without anything going red. The mechanism below is #10630's
+ * verbatim: the same two sinks, the same "named table AND named reason"
+ * predicate, the same pending-refusal gate on the engine frame, the same
+ * count-and-assert discipline. Only the duplication is gone.
+ *
+ * ⛔ Test-only. Nothing in `src/index.ts` imports it, so it is not bundled
+ * (tsup's single entry is `src/index.ts`); it carries no `vitest` import
+ * either, so the assertions stay visible in the fixture that owns them.
+ */
+
+/** One channel's tally, keyed by the table the withheld line named. */
+export type WithheldByTable = ReadonlyMap;
+
+/**
+ * The driver logger shape this capture installs. It mirrors the default's
+ * `{ warn, error }` so `SqlDriver.logDurabilityFailure` still finds an `error`
+ * channel to prefer — a sink with only `warn` would silently re-level every
+ * durability-degradation message this fixture is not talking about.
+ */
+interface DriverLoggerSink {
+ warn: (msg: string, meta?: unknown) => void;
+ error: (msg: string, meta?: unknown) => void;
+}
+
+export interface ExpectedReadRefusalCapture {
+ /** Per-table count of driver refusal envelopes withheld. */
+ readonly refusals: WithheldByTable;
+ /** Per-table count of engine `Find operation failed` frames withheld. */
+ readonly engineFrames: WithheldByTable;
+ /** Total refusal envelopes withheld, across every declared table. */
+ totalRefusals(): number;
+ /** Total engine frames withheld, across every declared table. */
+ totalEngineFrames(): number;
+ /** The declared tables that were seen at least once on the driver channel. */
+ tablesSeen(): string[];
+ /**
+ * The expected channels that never fired, one sentence each — the assertion
+ * surface. `expect(capture.silentChannels()).toEqual([])` is 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.
+ *
+ * ⛔ Repairing a failure here means re-deriving the declared table list or
+ * finding out why the probe stopped — NEVER relaxing this: a runtime read
+ * that stopped happening is a finding, and a table that started resolving
+ * means the fixture now provisions it.
+ *
+ * @param required the subset that must have fired. Defaults to every
+ * declared table. Narrow it for a table read on only SOME of a file's
+ * paths — requiring that one would turn a single-test `-t` run red without
+ * meaning anything, while still withholding it when it does fire.
+ */
+ silentChannels(required?: readonly string[]): string[];
+ /**
+ * Install the driver sink. ⛔ Call it BEFORE the driver runs any statement —
+ * `logger` is a protected field with a `console` default, and this is the
+ * idiom its own doc comment names ("Tests inject a spy") and that ~20 sibling
+ * driver suites already use.
+ */
+ captureDriver(driver: unknown): void;
+ /**
+ * Wrap the engine's `error` channel through a Proxy, so every OTHER logger
+ * method resolves to the engine's own. ⛔ Call it before the expected reads
+ * happen; the engine's logger is a private field with no setter, which is
+ * the same access `engine-readonly-when-parent.test.ts` established.
+ */
+ captureEngine(engine: unknown): void;
+}
+
+/**
+ * Build a capture for the tables a fixture deliberately does not provision.
+ *
+ * @param tables the object/table names whose `no such table` read failures are
+ * EXPECTED here. Derive them by measurement rather than from the prober's
+ * source, so a read the fixture stops provoking shows up as a changed set
+ * rather than silently.
+ */
+export function captureExpectedReadRefusals(
+ tables: readonly string[],
+): ExpectedReadRefusalCapture {
+ const refusals = new Map();
+ const engineFrames = new Map();
+ /** Recognised driver refusals not yet consumed by their engine frame. */
+ const pending = new Map();
+
+ const bump = (into: Map, table: string): void => {
+ into.set(table, (into.get(table) ?? 0) + 1);
+ };
+
+ /**
+ * The refusal envelope AND the dialect reason must name the SAME table.
+ * Matching the envelope alone would withhold a refusal whose cause is a
+ * permission denial, a dropped connection or a syntax fault — every one of
+ * which is a real signal on a table this fixture merely also happens to miss.
+ */
+ const expectedRefusal = (line: string): string | undefined =>
+ tables.find(
+ (t) => line.includes(`refused a read on '${t}'`) && line.includes(`no such table: ${t}`),
+ );
+
+ const sum = (m: Map): number => {
+ let n = 0;
+ for (const v of m.values()) n += v;
+ return n;
+ };
+
+ return {
+ refusals,
+ engineFrames,
+ totalRefusals: () => sum(refusals),
+ totalEngineFrames: () => sum(engineFrames),
+ tablesSeen: () => [...refusals.keys()].sort(),
+
+ silentChannels(required: readonly string[] = tables): string[] {
+ const out: string[] = [];
+ for (const t of required) {
+ if ((refusals.get(t) ?? 0) === 0) {
+ out.push(`the driver's read refusal for '${t}' was never emitted`);
+ }
+ if ((engineFrames.get(t) ?? 0) === 0) {
+ out.push(`the engine's 'Find operation failed' frame for '${t}' was never emitted`);
+ }
+ }
+ return out;
+ },
+
+ captureDriver(driver: unknown): void {
+ const sink: DriverLoggerSink = {
+ warn: (msg: string, meta?: unknown): void => {
+ const table = expectedRefusal(String(msg));
+ if (table !== undefined) {
+ bump(refusals, table);
+ bump(pending, table);
+ return;
+ }
+ console.warn(msg, meta ?? '');
+ },
+ error: (msg: string, meta?: unknown): void => {
+ console.error(msg, meta ?? '');
+ },
+ };
+ (driver as { logger: unknown }).logger = sink;
+ },
+
+ captureEngine(engine: unknown): void {
+ const base = (engine as { logger: Record }).logger;
+ (engine as { logger: unknown }).logger = new Proxy(base, {
+ get: (target: Record, key: string) =>
+ key === 'error'
+ ? (msg: string, err?: unknown, meta?: unknown) => {
+ const object = (meta as { object?: string } | undefined)?.object;
+ const outstanding = object !== undefined ? (pending.get(object) ?? 0) : 0;
+ const detail = String((err as { message?: string } | undefined)?.message ?? '');
+ if (
+ msg === 'Find operation failed' &&
+ object !== undefined &&
+ outstanding > 0 &&
+ detail.includes(`refused to run this query for object '${object}'`)
+ ) {
+ pending.set(object, outstanding - 1);
+ bump(engineFrames, object);
+ return;
+ }
+ target.error(msg, err, meta);
+ }
+ : target[key],
+ });
+ },
+ };
+}
diff --git a/packages/runtime/src/federated-boot-binding.test.ts b/packages/runtime/src/federated-boot-binding.test.ts
index 1fda629e52..c3afb037d7 100644
--- a/packages/runtime/src/federated-boot-binding.test.ts
+++ b/packages/runtime/src/federated-boot-binding.test.ts
@@ -42,6 +42,11 @@ import { describe, it, expect, afterEach, vi } from 'vitest';
import { Runtime } from './runtime.js';
import { DriverPlugin } from './driver-plugin.js';
import { AppPlugin } from './app-plugin.js';
+import type { Plugin, PluginContext } from '@objectstack/core';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
// [#10126] Pay the first transform of these dist-resolved workspace deps at MODULE
// LOAD. Each is reached below through a dynamic `import()` inside an `it()` body or a
@@ -120,7 +125,44 @@ function orphanArtifact() {
};
}
-async function boot(bundle: Record) {
+/**
+ * [#10629] The `OS_SKIP_SCHEMA_SYNC` case's expected read failures: WITHHELD,
+ * and ASSERTED.
+ *
+ * With boot schema sync skipped, nothing creates `sys_metadata` — that IS what
+ * the flag means (DDL managed out of band, and this fixture manages none). The
+ * boot metadata load reads it anyway and survives the miss by design, but the
+ * driver and the engine each log the fault on the way out: 5 `refused a read
+ * on 'sys_metadata'` lines and 5 matching `ERROR Find operation failed` frames,
+ * out of a test that PASSES. ⛔ Scoped to this one flag-set case on purpose —
+ * the ordinary boot beside it creates the table and must stay loud if it ever
+ * stops.
+ *
+ * Unlike every other fixture in this class the noisy read happens DURING
+ * `kernel.bootstrap()`, so the engine cannot be reached with `getService` after
+ * the fact. The capture rides in on a plugin that declares
+ * `requiresServices: ['objectql']` — ADR-0116's own ordering contract, which
+ * makes the kernel hoist `ObjectQLPlugin` ahead of it rather than leaving the
+ * order to `kernel.use()` position (this file is a boot-ORDER pin; assuming
+ * list order here would be exactly the mistake it exists to catch). It
+ * registers no service and has no `start`, so it cannot reorder the two plugins
+ * whose relative order this file measures.
+ */
+function noiseCapturePlugin(capture: ExpectedReadRefusalCapture): Plugin {
+ return {
+ name: 'com.objectstack.test.10629-noise-capture',
+ version: '1.0.0',
+ requiresServices: ['objectql'],
+ init: async (ctx: PluginContext) => {
+ capture.captureEngine(ctx.getService('objectql'));
+ },
+ };
+}
+
+/** The table `OS_SKIP_SCHEMA_SYNC` leaves uncreated in this composition. */
+const ABSENT_METADATA_TABLE = 'sys_metadata';
+
+async function boot(bundle: Record, capture?: ExpectedReadRefusalCapture) {
const { ObjectQLPlugin } = await import('@objectstack/objectql');
const { DatasourceAdminServicePlugin, createDefaultDatasourceDriverFactory } = await import(
'@objectstack/service-datasource'
@@ -128,8 +170,13 @@ async function boot(bundle: Record) {
const runtime = new Runtime({ cluster: false });
const kernel = runtime.getKernel();
- await kernel.use(new DriverPlugin(await makeDefaultDriver()));
+ // [#10629] Scoped before the driver runs a statement when the caller asked
+ // for a capture; the default boot passes none and stays fully loud.
+ const driver = await makeDefaultDriver();
+ capture?.captureDriver(driver);
+ await kernel.use(new DriverPlugin(driver));
await kernel.use(new ObjectQLPlugin());
+ if (capture) await kernel.use(noiseCapturePlugin(capture));
await kernel.use(new AppPlugin(bundle as never));
await kernel.use(
new DatasourceAdminServicePlugin({ driverFactory: createDefaultDatasourceDriverFactory() }),
@@ -185,8 +232,9 @@ describe('#7737 federated boot binding — declared external objects are bound w
it('binds federated objects even when boot schema sync is skipped (OS_SKIP_SCHEMA_SYNC)', async () => {
const previous = process.env.OS_SKIP_SCHEMA_SYNC;
process.env.OS_SKIP_SCHEMA_SYNC = '1';
+ const noise = captureExpectedReadRefusals([ABSENT_METADATA_TABLE]);
try {
- kernel = await boot(artifact());
+ kernel = await boot(artifact(), noise);
const engine = kernel.getService('data');
const driver = engine.getDriverByName('fed_ext');
await driver.execute('CREATE TABLE remote_customers (id text primary key, name text)');
@@ -195,6 +243,10 @@ describe('#7737 federated boot binding — declared external objects are bound w
await driver.execute("INSERT INTO remote_invoices (id, amount) VALUES ('i1', 100)");
expect((await engine.find('fed_customer')).map((r) => r.name)).toEqual(['Ada']);
expect((await engine.find('fed_invoice')).map((r) => r.id)).toEqual(['i1']);
+ // [#10629] The capture is a PIN, not a mute: if boot stops reading
+ // `sys_metadata`, or the table starts existing under this flag, the log
+ // goes quiet AND this goes red.
+ expect(noise.silentChannels()).toEqual([]);
} finally {
if (previous === undefined) delete process.env.OS_SKIP_SCHEMA_SYNC;
else process.env.OS_SKIP_SCHEMA_SYNC = previous;
diff --git a/packages/runtime/src/notifications.hono.integration.test.ts b/packages/runtime/src/notifications.hono.integration.test.ts
index dd06e8d245..31bc4246ea 100644
--- a/packages/runtime/src/notifications.hono.integration.test.ts
+++ b/packages/runtime/src/notifications.hono.integration.test.ts
@@ -10,6 +10,7 @@ import { MessagingServicePlugin, MessagingService } from '@objectstack/service-m
import { createDispatcherPlugin } from './dispatcher-plugin.js';
import { DriverPlugin } from './driver-plugin.js';
+import { captureExpectedReadRefusals } from './expected-read-refusal-noise.js';
import type { IHttpServer } from '@objectstack/spec/contracts';
/**
@@ -62,10 +63,50 @@ function fakeAuthPlugin(): Plugin {
};
}
+/**
+ * [#10629] The authz resolver's expected read failures: WITHHELD, and ASSERTED.
+ *
+ * This file is the direct sibling `notification-schema-conformance.integration.test.ts`
+ * names in its own header, and it carries the same never-provisioned `sys_*`
+ * authz reads through `resolveUserAuthzGrants`
+ * (`core/src/security/resolve-authz-context.ts`): the fixture provisions the
+ * messaging objects and nothing else, so every authenticated request reads six
+ * `sys_*` tables that were never created. `tryFind` swallows each one by design
+ * — the resolver is fail-closed and must always resolve — but on the way out
+ * the driver and the engine each log it. Measured on `origin/main`: 52
+ * `refused a read on` lines and 52 matching `ERROR Find operation failed`
+ * frames, out of a suite whose five tests all PASS.
+ *
+ * PR #10630 ruled the shape for this class and applied it to the sibling; this
+ * is the same shape through the shared `expected-read-refusal-noise.ts`, whose
+ * header carries the full rationale (⛔ it withholds ONLY a line that names one
+ * of these tables AND carries that same table's `no such table` reason, and it
+ * COUNTS what it withheld so the assertions below can be a PIN rather than a
+ * mute).
+ */
+const ABSENT_AUTHZ_TABLES = [
+ 'sys_user',
+ 'sys_member',
+ 'sys_user_position',
+ 'sys_user_permission_set',
+ 'sys_position',
+ 'sys_setting',
+] as const;
+
+/**
+ * The five read on EVERY grant resolution, i.e. on every authenticated request
+ * this file makes. `sys_setting` is deliberately NOT here: it is read on only
+ * some routes, so requiring it would turn a single-test `-t` run red without
+ * meaning anything — it is still withheld when it does fire.
+ */
+const ALWAYS_READ_AUTHZ_TABLES = ABSENT_AUTHZ_TABLES.filter((t) => t !== 'sys_setting');
+
describe('in-app notifications over a real hono server (integration, #3362)', () => {
let kernel: ObjectKernel;
let baseUrl: string;
let messaging: MessagingService;
+ /** [#10629] The expected-noise capture, asserted by every authed test below. */
+ const noise = captureExpectedReadRefusals([...ABSENT_AUTHZ_TABLES]);
beforeAll(async () => {
kernel = new ObjectKernel({ logLevel: 'silent' });
@@ -74,7 +115,11 @@ describe('in-app notifications over a real hono server (integration, #3362)', ()
// MessagingServicePlugin registers the `notification` service the dispatcher
// resolves and owns the inbox tables. Inline delivery (reliableDelivery:false)
// writes the inbox row synchronously so `emit()` is observable immediately.
- await kernel.use(new DriverPlugin(new SqliteWasmDriver({ filename: ':memory:' })));
+ // [#10629] The driver is named rather than inlined so its logger can be
+ // scoped before it ever runs a statement.
+ const driver = new SqliteWasmDriver({ filename: ':memory:' });
+ noise.captureDriver(driver);
+ await kernel.use(new DriverPlugin(driver));
await kernel.use(new ObjectQLPlugin());
// No app plugin registers `sys_notification` here: MessagingServicePlugin
// contributes the L2 event it writes, so this lean kernel needs nothing
@@ -90,6 +135,10 @@ describe('in-app notifications over a real hono server (integration, #3362)', ()
await kernel.bootstrap();
+ // [#10629] The engine only exists once the kernel has bootstrapped; the
+ // reads this scopes all happen later, per request.
+ noise.captureEngine(kernel.getService('objectql'));
+
const httpServer = kernel.getService('http.server');
baseUrl = `http://127.0.0.1:${httpServer.getPort!()}`;
messaging = kernel.getService('notification');
@@ -162,6 +211,17 @@ describe('in-app notifications over a real hono server (integration, #3362)', ()
const served = await authed('/api/v1/notifications?limit=5&read=false');
expect(served.status).toBe(200);
expect((await served.json() as { success: boolean }).success).toBe(true);
+
+ // ── [#10629] The capture is a PIN, not a mute. These lines used to reach
+ // the shared `Test Core` log out of a PASSING test and were read there as a
+ // real failure; they are withheld now and asserted here. Asserted per authed
+ // test rather than in `afterAll` because two tests in this file resolve no
+ // grants at all (discovery, and the anonymous 401), and an `afterAll` would
+ // make a single-test `-t` run of either of them red for no reason.
+ // ⛔ If one of these goes silent the repair is to re-derive the list above,
+ // NOT to relax this: a resolver read that stopped happening is a finding,
+ // and a table that started resolving means this fixture now provisions it.
+ expect(noise.silentChannels(ALWAYS_READ_AUTHZ_TABLES)).toEqual([]);
});
it('lists, marks specific read, then marks all read — flipping receipts and clearing the unread count', async () => {
@@ -207,6 +267,17 @@ describe('in-app notifications over a real hono server (integration, #3362)', ()
});
expect(receipts.length).toBe(2);
expect(receipts.every((r: any) => r.state === 'read')).toBe(true);
+
+ // ── [#10629] The capture is a PIN, not a mute. These lines used to reach
+ // the shared `Test Core` log out of a PASSING test and were read there as a
+ // real failure; they are withheld now and asserted here. Asserted per authed
+ // test rather than in `afterAll` because two tests in this file resolve no
+ // grants at all (discovery, and the anonymous 401), and an `afterAll` would
+ // make a single-test `-t` run of either of them red for no reason.
+ // ⛔ If one of these goes silent the repair is to re-derive the list above,
+ // NOT to relax this: a resolver read that stopped happening is a finding,
+ // and a table that started resolving means this fixture now provisions it.
+ expect(noise.silentChannels(ALWAYS_READ_AUTHZ_TABLES)).toEqual([]);
});
it('[#6436] mark-all-read clears an inbox LARGER than the list window — no readCount/unreadCount contradiction', async () => {
@@ -257,5 +328,16 @@ describe('in-app notifications over a real hono server (integration, #3362)', ()
});
expect(receipts.length).toBe(TOTAL);
expect(receipts.every((r: any) => r.state === 'read')).toBe(true);
+
+ // ── [#10629] The capture is a PIN, not a mute. These lines used to reach
+ // the shared `Test Core` log out of a PASSING test and were read there as a
+ // real failure; they are withheld now and asserted here. Asserted per authed
+ // test rather than in `afterAll` because two tests in this file resolve no
+ // grants at all (discovery, and the anonymous 401), and an `afterAll` would
+ // make a single-test `-t` run of either of them red for no reason.
+ // ⛔ If one of these goes silent the repair is to re-derive the list above,
+ // NOT to relax this: a resolver read that stopped happening is a finding,
+ // and a table that started resolving means this fixture now provisions it.
+ expect(noise.silentChannels(ALWAYS_READ_AUTHZ_TABLES)).toEqual([]);
}, 120_000);
});
diff --git a/packages/runtime/src/package-list-commits-org-scope.integration.test.ts b/packages/runtime/src/package-list-commits-org-scope.integration.test.ts
index d582f35119..becfc655c0 100644
--- a/packages/runtime/src/package-list-commits-org-scope.integration.test.ts
+++ b/packages/runtime/src/package-list-commits-org-scope.integration.test.ts
@@ -11,6 +11,10 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import {
SysMetadataObject,
@@ -92,10 +96,31 @@ const PLATFORM_PKG = '@objectstack/platform-objects';
const ACTIVE_ORG = 'org_active';
const OTHER_ORG = 'org_other';
+/**
+ * [#10629] Every publish this fixture makes runs the metadata-protocol build
+ * probes (`metadata-protocol/src/build-probes.ts`), and the views it publishes
+ * are bound to the placeholder object `anything` — the #7741 inline arm
+ * requires an object binding pair, and nothing here creates that table. The
+ * probe reads it, catches, and files a `view_read_failed` publish issue this
+ * suite does not read (it asserts `res.success`), but the driver and the engine
+ * each log the read on the way out. Withheld and asserted rather than muted;
+ * `expected-read-refusal-noise.ts` says why.
+ */
+const UNBOUND_PROBE_OBJECT = 'anything';
+
let cleanup: Array<() => void> = [];
+
+/** [#10629] The expected-noise capture belonging to the latest `boot()`. */
+let noise: ExpectedReadRefusalCapture | null = null;
afterEach(() => {
for (const c of cleanup) c();
cleanup = [];
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave an engine running. Every test in this file publishes at
+ // least once, so the probe fires for each of them: this holds for a single
+ // `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
/** REAL ObjectQL wired to a REAL SqlDriver over on-disk better-sqlite3. */
@@ -116,9 +141,14 @@ async function boot() {
SysMetadataAuditObject,
SysMetadataCommitObject,
] as any[];
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([UNBOUND_PROBE_OBJECT]);
+ noise.captureDriver(driver);
await driver.initObjects(objects);
const engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver as any, true);
await engine.init();
// `registerObject(schema, packageId)` — the second argument is REQUIRED.
diff --git a/packages/runtime/src/package-revert-commit-attribution-org-scope.integration.test.ts b/packages/runtime/src/package-revert-commit-attribution-org-scope.integration.test.ts
index 5dc3b45b03..54073e8b35 100644
--- a/packages/runtime/src/package-revert-commit-attribution-org-scope.integration.test.ts
+++ b/packages/runtime/src/package-revert-commit-attribution-org-scope.integration.test.ts
@@ -11,6 +11,10 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import {
SysMetadataObject,
@@ -83,10 +87,31 @@ const PLATFORM_PKG = '@objectstack/platform-objects';
const ACTIVE_ORG = 'org_active';
const OTHER_ORG = 'org_other';
+/**
+ * [#10629] Every publish this fixture makes runs the metadata-protocol build
+ * probes (`metadata-protocol/src/build-probes.ts`), and the views it publishes
+ * are bound to the placeholder object `anything` — the #7741 inline arm
+ * requires an object binding pair, and nothing here creates that table. The
+ * probe reads it, catches, and files a `view_read_failed` publish issue this
+ * suite does not read (it asserts `res.success`), but the driver and the engine
+ * each log the read on the way out. Withheld and asserted rather than muted;
+ * `expected-read-refusal-noise.ts` says why.
+ */
+const UNBOUND_PROBE_OBJECT = 'anything';
+
let cleanup: Array<() => void> = [];
+
+/** [#10629] The expected-noise capture belonging to the latest `boot()`. */
+let noise: ExpectedReadRefusalCapture | null = null;
afterEach(() => {
for (const c of cleanup) c();
cleanup = [];
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave an engine running. Every test in this file publishes at
+ // least once, so the probe fires for each of them: this holds for a single
+ // `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
/** REAL ObjectQL wired to a REAL SqlDriver over on-disk better-sqlite3. */
@@ -105,9 +130,14 @@ async function boot() {
SysMetadataAuditObject,
SysMetadataCommitObject,
] as any[];
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([UNBOUND_PROBE_OBJECT]);
+ noise.captureDriver(driver);
await driver.initObjects(objects);
const engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver as any, true);
await engine.init();
for (const o of objects) engine.registry.registerObject(o, PLATFORM_PKG);
diff --git a/packages/runtime/src/package-revert-commit-org-scope.integration.test.ts b/packages/runtime/src/package-revert-commit-org-scope.integration.test.ts
index 0f19de1851..c0d9872329 100644
--- a/packages/runtime/src/package-revert-commit-org-scope.integration.test.ts
+++ b/packages/runtime/src/package-revert-commit-org-scope.integration.test.ts
@@ -12,6 +12,10 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
import {
SysMetadataObject,
@@ -110,10 +114,31 @@ const PLATFORM_PKG = '@objectstack/platform-objects';
const ACTIVE_ORG = 'org_active';
const OTHER_ORG = 'org_other';
+/**
+ * [#10629] Every publish this fixture makes runs the metadata-protocol build
+ * probes (`metadata-protocol/src/build-probes.ts`), and the views it publishes
+ * are bound to the placeholder object `anything` — the #7741 inline arm
+ * requires an object binding pair, and nothing here creates that table. The
+ * probe reads it, catches, and files a `view_read_failed` publish issue this
+ * suite does not read (it asserts `res.success`), but the driver and the engine
+ * each log the read on the way out. Withheld and asserted rather than muted;
+ * `expected-read-refusal-noise.ts` says why.
+ */
+const UNBOUND_PROBE_OBJECT = 'anything';
+
let cleanup: Array<() => void> = [];
+
+/** [#10629] The expected-noise capture belonging to the latest `boot()`. */
+let noise: ExpectedReadRefusalCapture | null = null;
afterEach(() => {
for (const c of cleanup) c();
cleanup = [];
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave an engine running. Every test in this file publishes at
+ // least once, so the probe fires for each of them: this holds for a single
+ // `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
/** REAL ObjectQL wired to a REAL SqlDriver over on-disk better-sqlite3. */
@@ -135,9 +160,14 @@ async function boot() {
SysMetadataAuditObject,
SysMetadataCommitObject,
] as any[];
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([UNBOUND_PROBE_OBJECT]);
+ noise.captureDriver(driver);
await driver.initObjects(objects);
const engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver as any, true);
await engine.init();
// `registerObject(schema, packageId)` — the second argument is REQUIRED.
diff --git a/packages/runtime/src/preserve-audit-real-driver.integration.test.ts b/packages/runtime/src/preserve-audit-real-driver.integration.test.ts
index a24e82551f..90097cb92c 100644
--- a/packages/runtime/src/preserve-audit-real-driver.integration.test.ts
+++ b/packages/runtime/src/preserve-audit-real-driver.integration.test.ts
@@ -28,6 +28,10 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
const ISO_Z = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
const HISTORICAL = '2021-03-01T09:00:00.000Z';
@@ -42,21 +46,44 @@ const TICKET = {
},
};
+/**
+ * [#10629] This fixture provisions `ticket` and nothing else, so the engine's
+ * own single-tenant probe (`ObjectQL.probeInstallOrganizations`, memoised once
+ * per engine) reads a `sys_organization` that was never created. The probe is
+ * fail-soft by construction — it catches `isMissingTableError` and only that —
+ * but the driver and the engine each log the fault on the way out. Withheld and
+ * asserted rather than muted; `expected-read-refusal-noise.ts` says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('preserveAudit end-to-end on a REAL SqlDriver (#3493 / #3549)', () => {
let engine: ObjectQL | null = null;
let dir: string | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. Every test in this file
+ // boots and writes, so the probe fires for each of them: this holds for a
+ // single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function boot() {
dir = mkdtempSync(join(tmpdir(), 'os-preserveaudit-'));
const driver = new SqlDriver({ client: 'better-sqlite3', connection: { filename: join(dir, 'data.sqlite') }, useNullAsDefault: true });
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(driver);
await driver.initObjects([TICKET]); // create the real table + audit columns
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
engine.registry.registerObject(TICKET as any);
diff --git a/packages/runtime/src/sandbox/nested-write-real-sqlite.integration.test.ts b/packages/runtime/src/sandbox/nested-write-real-sqlite.integration.test.ts
index 2ab14a6438..52f10dbe1e 100644
--- a/packages/runtime/src/sandbox/nested-write-real-sqlite.integration.test.ts
+++ b/packages/runtime/src/sandbox/nested-write-real-sqlite.integration.test.ts
@@ -32,6 +32,10 @@ import { ObjectQL, bindHooksToEngine } from '@objectstack/objectql';
import { SqlDriver } from '@objectstack/driver-sql';
import { hookBodyRunnerFactory } from './body-runner.js';
import { QuickJSScriptRunner } from './quickjs-runner.js';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from '../expected-read-refusal-noise.js';
const EXPENSE_REPORT = {
name: 'expense_report',
@@ -68,9 +72,22 @@ const ROLLUP_HOOK = {
},
};
+/**
+ * [#10629] This fixture provisions `expense_report` / `expense_line` and
+ * nothing else, so the engine's own single-tenant probe
+ * (`ObjectQL.probeInstallOrganizations`, memoised once per engine) reads a
+ * `sys_organization` that was never created. That read is fail-soft by
+ * construction — the probe catches `isMissingTableError` and only that — but
+ * the driver and the engine each log it on the way out. Withheld and asserted
+ * below rather than muted; the module header explains why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('#1867 nested cross-object write — REAL SqlDriver (better-sqlite3, on-disk)', () => {
let engine: ObjectQL | null = null;
let dir: string | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest {@link boot}. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
@@ -81,8 +98,13 @@ describe('#1867 nested cross-object write — REAL SqlDriver (better-sqlite3, on
async function boot() {
dir = mkdtempSync(join(tmpdir(), 'os-nested-1867-'));
const driver = new SqlDriver({ client: 'better-sqlite3', connection: { filename: join(dir, 'data.sqlite') }, useNullAsDefault: true });
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(driver);
await driver.initObjects([EXPENSE_REPORT, EXPENSE_LINE]); // create real tables
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
for (const o of [EXPENSE_REPORT, EXPENSE_LINE]) engine.registry.registerObject(o as any);
@@ -108,5 +130,12 @@ describe('#1867 nested cross-object write — REAL SqlDriver (better-sqlite3, on
await e.update('expense_line', { id: line2.id, amount: 75, expense_report: report.id });
parent = (await e.find('expense_report', { where: { id: report.id } }))[0];
expect(parent.total_amount).toBe(175);
+
+ // ── [#10629] The capture is a PIN, not a mute. These two lines used to
+ // reach the shared `Test Core` log out of a PASSING test and were read
+ // there as a real failure; they are withheld now and asserted here. If the
+ // probe stops running, or `sys_organization` starts resolving, the log goes
+ // quiet AND this goes red — the failure a bare `console` mute would hide.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
}, 30000);
});
diff --git a/packages/runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts b/packages/runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts
index a180e30dfb..1ee0e021c5 100644
--- a/packages/runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts
+++ b/packages/runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts
@@ -149,6 +149,10 @@ import { InMemoryDriver } from '@objectstack/driver-memory';
import { hookBodyRunnerFactory } from './body-runner.js';
import { QuickJSScriptRunner } from './quickjs-runner.js';
import type { EngineQueryOptions } from '@objectstack/spec/data';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from '../expected-read-refusal-noise.js';
/**
* The read-back query, TYPED rather than cast. The `as any` reads elsewhere in
@@ -211,14 +215,35 @@ const UPDATE_TYPO_HOOK = {
body: { language: 'js', source: `ctx.input.stagee = 'won';` },
};
+/**
+ * [#10629] The SQL half of this fixture provisions `deal` and nothing else, so
+ * the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ * The schemaless half has no missing table and so declares NOTHING expected:
+ * its capture withholds nothing and asserts nothing, which is the honest
+ * reading rather than a skipped assertion. `expected-read-refusal-noise.ts`
+ * says why this withholds instead of muting.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('#4271 an undeclared field written by an L2 body — the real runtime split', () => {
let engine: ObjectQL | null = null;
let dir: string | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. Unconditional on purpose:
+ // a memory boot declares an EMPTY expectation, so this still fails loudly if
+ // a boot ever forgets to install a capture at all.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function bootSql(hook?: unknown) {
@@ -228,16 +253,26 @@ describe('#4271 an undeclared field written by an L2 body — the real runtime s
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed before the driver runs a statement — the sink the
+ // expected refusal's first half travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(driver);
await driver.initObjects([DEAL]); // a REAL table, with only the declared columns
return boot(driver, hook);
}
async function bootMemory(hook?: unknown) {
+ // [#10629] A schemaless driver never refuses a read on a missing table, so
+ // this family expects no noise at all — an EMPTY declaration rather than a
+ // skipped one, which keeps the shared `afterEach` assertion honest.
+ noise = captureExpectedReadRefusals([]);
return boot(new InMemoryDriver(), hook);
}
async function boot(driver: unknown, hook?: unknown) {
engine = new ObjectQL();
+ // [#10629] The engine frame that sits directly above the driver's refusal.
+ noise?.captureEngine(engine);
engine.registerDriver(driver as any, true);
await engine.init();
engine.registry.registerObject(DEAL as any);
diff --git a/packages/runtime/src/seed-loader-driver-text-real-driver.integration.test.ts b/packages/runtime/src/seed-loader-driver-text-real-driver.integration.test.ts
index 6b2bd6cba1..e5172461a7 100644
--- a/packages/runtime/src/seed-loader-driver-text-real-driver.integration.test.ts
+++ b/packages/runtime/src/seed-loader-driver-text-real-driver.integration.test.ts
@@ -55,6 +55,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SeedLoaderService } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
import { validationFailureDetails } from '@objectstack/types';
/** `email` declares UNIQUE, so the real driver — not a validator — rejects the duplicate. */
@@ -81,14 +85,32 @@ function metadataFor(objects: any[]) {
} as any;
}
+/**
+ * [#10629] This fixture provisions its own business objects and nothing else,
+ * so the engine's single-tenant probe (`ObjectQL.probeInstallOrganizations`,
+ * memoised once per engine) reads a `sys_organization` that was never created.
+ * The probe is fail-soft by construction — it catches `isMissingTableError` and
+ * only that — but the driver and the engine each log the fault on the way out.
+ * Withheld and asserted rather than muted; `expected-read-refusal-noise.ts`
+ * says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('[#8442] a REAL driver constraint violation is withheld from the seed response', () => {
let dir: string | null = null;
let engine: ObjectQL | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. The single test in this
+ // file boots and writes, so the probe fires for it.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
it('a duplicate on a UNIQUE column leaks neither the SQL nor the seeded values', async () => {
@@ -98,6 +120,11 @@ describe('[#8442] a REAL driver constraint violation is withheld from the seed r
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed on the REAL driver (the one that logs) before it runs
+ // a statement — the `Object.create(real)` wrapper below resolves `logger`
+ // through the prototype chain to this sink.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(real);
await real.initObjects([ACCT]);
// Capture the RAW driver error at the seam, then let it propagate
@@ -113,6 +140,7 @@ describe('[#8442] a REAL driver constraint violation is withheld from the seed r
};
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
engine.registry.registerObject(ACCT as any, 'com.objectstack.test.8442');
diff --git a/packages/runtime/src/seed-multi-value-lookup-real-driver.integration.test.ts b/packages/runtime/src/seed-multi-value-lookup-real-driver.integration.test.ts
index a5b45b249d..303c63e089 100644
--- a/packages/runtime/src/seed-multi-value-lookup-real-driver.integration.test.ts
+++ b/packages/runtime/src/seed-multi-value-lookup-real-driver.integration.test.ts
@@ -17,6 +17,10 @@ import { join } from 'node:path';
import { ObjectQL } from '@objectstack/objectql';
import { SeedLoaderService } from '@objectstack/metadata-protocol';
import { SqlDriver } from '@objectstack/driver-sql';
+import {
+ captureExpectedReadRefusals,
+ type ExpectedReadRefusalCapture,
+} from './expected-read-refusal-noise.js';
const AUTHOR = {
name: 'author',
@@ -59,14 +63,32 @@ const SEEDS = [
},
];
+/**
+ * [#10629] This fixture provisions the seeded business objects and nothing else, so the engine's
+ * own single-tenant probe (`ObjectQL.probeInstallOrganizations`, memoised once
+ * per engine) reads a `sys_organization` that was never created. The probe is
+ * fail-soft by construction — it catches `isMissingTableError` and only that —
+ * but the driver and the engine each log the fault on the way out. Withheld and
+ * asserted rather than muted; `expected-read-refusal-noise.ts` says why.
+ */
+const ABSENT_TENANCY_TABLE = 'sys_organization';
+
describe('multi-value lookup seeds on a REAL SqlDriver (framework#3911)', () => {
let dir: string | null = null;
let engine: ObjectQL | null = null;
+ /** [#10629] The expected-noise capture belonging to the latest boot. */
+ let noise: ExpectedReadRefusalCapture | null = null;
afterEach(async () => {
try { await engine?.destroy(); } catch { /* noop */ }
engine = null;
if (dir) { rmSync(dir, { recursive: true, force: true }); dir = null; }
+ // [#10629] The capture is a PIN, not a mute — asserted after teardown so a
+ // failure here can never leave the engine running. Every test in this file
+ // boots and writes, so the probe fires for each of them: this holds for a
+ // single `-t` run as well as for the whole file.
+ expect(noise?.silentChannels() ?? ['no capture was installed']).toEqual([]);
+ noise = null;
});
async function boot(objects: any[]) {
@@ -76,8 +98,13 @@ describe('multi-value lookup seeds on a REAL SqlDriver (framework#3911)', () =>
connection: { filename: join(dir, 'data.sqlite') },
useNullAsDefault: true,
});
+ // [#10629] Installed before the driver runs a statement and before the
+ // engine issues a read — the two sinks the expected refusal travels out on.
+ noise = captureExpectedReadRefusals([ABSENT_TENANCY_TABLE]);
+ noise.captureDriver(driver);
await driver.initObjects(objects); // real tables, real JSON column
engine = new ObjectQL();
+ noise.captureEngine(engine);
engine.registerDriver(driver, true);
await engine.init();
for (const o of objects) engine.registry.registerObject(o as any);