You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found during #7737's end-to-end re-verification (measured on a real os dev boot of examples/app-showcase, origin/main @ b432ae472, SQL captured at SqlDriver.findRows with temporary instrumentation). Filed unassigned, observation-class: no wrong user-visible answer was measured on any current dialect — the cost today is wasted work and log noise, plus two latent halves documented below.
The measurement
Two platform background sweeps interrogate federated (ADR-0015 external) objects about columns the platform injected into the registered schema but never provisioned on the remote table — the same phantom-anchor family #7835 closed for plugin-security's predicate injectors, but these are PROJECTION consumers, which #7835's scope did not cover.
Pinyin search companion backfill (backfillSearchCompanion, packages/plugins/plugin-pinyin-search/src/companion-projection.ts), at every boot, stack-confirmed via kernel hook dispatch:
select `id`, `name`, `__search` from `customers` order by `id` asc limit 1000
Lifecycle dangling-reference audit (auditDanglingReferences, packages/objectql/src/integrity/dangling-reference-audit.ts, via LifecycleService.sweep — first pass 60s after boot), stack-confirmed:
Both against the remote table customers of the showcase_external datasource, which physically has only id, name, email, region, lifetime_value (plus orders, same shape of statement). Neither __search nor any of the five anchors exists remotely.
Why nothing visibly breaks today
Directly verified with better-sqlite3 13.0.2: a backtick-quoted unknown column in a SELECT projection raises no such column — it does NOT take SQLite's double-quote literal fallback. The failing statement is then caught by SqlDriver.findRows's unknown-column retry ladder (the isUnknownColumn branch, packages/drivers/driver-sql/src/sql-driver.ts), which retries select *: rows come back without the phantom columns, so
the dangling-reference audit sees undefined anchors and reports no false dangling ids;
the pinyin backfill sees no blob and (on this fixture, ASCII data) no CJK source, and skips.
The retry predicate also matches Postgres/MySQL's column ... does not exist, so the absorption is cross-dialect.
The costs and the latent halves
Per federated object, per pass: one doomed statement plus a full-row select * retry (up to 500/1000 rows fetched to audit columns that cannot exist). The lifecycle sweep repeats on its interval, the pinyin backfill on every boot.
Each pass also emits the 分页读取在没有 orderBy 时同样不确定:tie-breaker 只覆盖了「排了序的翻页」 #4363 "Paged read ... is NOT deterministic" stderr warning per federated object (the retry drops nothing here — the walk itself pages a table the driver did not create).
Latent (a): a federated remote holding CJK text would make the pinyin backfill compute companion values and attempt engine.update on a read-only federated object every boot — refused by the external write gate, one warn per row.
Latent (b): both sweeps are protected only by the retry ladder's error-message sniffing; SqlDriver.count has no such ladder, so any future sweep that counts by a phantom column fails hard.
Suggested direction (not prescriptive)
Sweep enumerators should skip phantom columns at the source — the provenance derivations #7835 used for Layer 0 (platformProvisionsStorage / the injectedSystemColumnDefs family in @objectstack/spec/data) answer exactly this question — rather than any consumer-side tolerance of the failing read (Prime Directive #12).
Found during #7737's end-to-end re-verification (measured on a real
os devboot ofexamples/app-showcase,origin/main@b432ae472, SQL captured atSqlDriver.findRowswith temporary instrumentation). Filed unassigned, observation-class: no wrong user-visible answer was measured on any current dialect — the cost today is wasted work and log noise, plus two latent halves documented below.The measurement
Two platform background sweeps interrogate federated (ADR-0015
external) objects about columns the platform injected into the registered schema but never provisioned on the remote table — the same phantom-anchor family #7835 closed for plugin-security's predicate injectors, but these are PROJECTION consumers, which #7835's scope did not cover.backfillSearchCompanion,packages/plugins/plugin-pinyin-search/src/companion-projection.ts), at every boot, stack-confirmed viakernelhook dispatch:auditDanglingReferences,packages/objectql/src/integrity/dangling-reference-audit.ts, viaLifecycleService.sweep— first pass 60s after boot), stack-confirmed:Both against the remote table
customersof theshowcase_externaldatasource, which physically has onlyid, name, email, region, lifetime_value(plusorders, same shape of statement). Neither__searchnor any of the five anchors exists remotely.Why nothing visibly breaks today
Directly verified with better-sqlite3 13.0.2: a backtick-quoted unknown column in a SELECT projection raises
no such column— it does NOT take SQLite's double-quote literal fallback. The failing statement is then caught bySqlDriver.findRows's unknown-column retry ladder (theisUnknownColumnbranch,packages/drivers/driver-sql/src/sql-driver.ts), which retriesselect *: rows come back without the phantom columns, soundefinedanchors and reports no false dangling ids;The retry predicate also matches Postgres/MySQL's
column ... does not exist, so the absorption is cross-dialect.The costs and the latent halves
select *retry (up to 500/1000 rows fetched to audit columns that cannot exist). The lifecycle sweep repeats on its interval, the pinyin backfill on every boot.engine.updateon a read-only federated object every boot — refused by the external write gate, one warn per row.SqlDriver.counthas no such ladder, so any future sweep that counts by a phantom column fails hard.Suggested direction (not prescriptive)
Sweep enumerators should skip phantom columns at the source — the provenance derivations #7835 used for Layer 0 (
platformProvisionsStorage/ theinjectedSystemColumnDefsfamily in@objectstack/spec/data) answer exactly this question — rather than any consumer-side tolerance of the failing read (Prime Directive #12).Related
plugin-security/src/federated-phantom-anchors.ts).tenantIdwithholding for federated objects.Generated by Claude Code