Observation recorded while implementing #7865 (PR #8115); the card asked whether the marker reaches the author-time surface (#5378 / #6113 history), and the measured answer is: it does not, structurally.
The gap
#5378 taught the author-time expression validator to RESOLVE registry-injected system columns (resolveInjectedSystemColumns, @objectstack/spec/data), so has(record.owner_id) on an injection-only object stopped being rejected. That derivation is deliberately storage-blind: it answers "is this name addressable", never "is there a column behind it".
After #7865 the runtime can answer the second question (resolveInjectedColumnProvenance / unprovisionedInjectedColumns, @objectstack/metadata-core): on an external (ADR-0015) object the injected anchors are registered but unprovisioned, and a predicate over one degrades silently on SQLite (constant-false: HTTP 200, zero rows, no error).
The author-time surface cannot ask it. @objectstack/lint's package contract is "depends on @objectstack/spec, never on a runtime", and the provenance derivation needs the injected-column DEFINITION tables, which live in @objectstack/metadata-core by the #3786 WHICH/WHAT split. So an author writing record.owner_id in an expression, view filter, or highlight on a federated object gets a clean lint pass today, and the failure surfaces at query time, silently, on the default dev dialect.
Why filed as a finding and not a defect
Pointers
Generated by Claude Code
Observation recorded while implementing #7865 (PR #8115); the card asked whether the marker reaches the author-time surface (#5378 / #6113 history), and the measured answer is: it does not, structurally.
The gap
#5378 taught the author-time expression validator to RESOLVE registry-injected system columns (
resolveInjectedSystemColumns,@objectstack/spec/data), sohas(record.owner_id)on an injection-only object stopped being rejected. That derivation is deliberately storage-blind: it answers "is this name addressable", never "is there a column behind it".After #7865 the runtime can answer the second question (
resolveInjectedColumnProvenance/unprovisionedInjectedColumns,@objectstack/metadata-core): on anexternal(ADR-0015) object the injected anchors are registered but unprovisioned, and a predicate over one degrades silently on SQLite (constant-false: HTTP 200, zero rows, no error).The author-time surface cannot ask it.
@objectstack/lint's package contract is "depends on@objectstack/spec, never on a runtime", and the provenance derivation needs the injected-column DEFINITION tables, which live in@objectstack/metadata-coreby the #3786 WHICH/WHAT split. So an author writingrecord.owner_idin an expression, view filter, or highlight on a federated object gets a clean lint pass today, and the failure surfaces at query time, silently, on the default dev dialect.Why filed as a finding and not a defect
__readScopeown/unit to anowner_idpredicate on federated objects, whereowner_idis a phantom column #7858) cover the runtime half of the class.@objectstack/spec(its inputs are all document-declared keys —external,fields, the plan inputs — so it is spec-representable, but that moves part of the WHAT half of 排查「手抄 spec 清单 + "keep in sync" 注释」模式:一天内确认三例,全部曾静默漂移 #3786's split), or let lint grow a declared exception to its no-runtime rule formetadata-core(which depends only on spec + zod). Both shapes deserve a deliberate ruling, not a rider on [Decision]applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865.Pointers
packages/metadata-core/src/injected-system-columns.ts(provenance section, [Decision]applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865).packages/lint/src/system-fields.ts,packages/lint/src/validate-expressions.ts(buildFieldIndex).record.owner_id/created_at/organization_idare rejected as unknown fields #5378 (validator blindness to injected columns), 作者时表达式校验不认识平台注入列:record.owner_id == os.user.id被判为 unknown field,最常见的归属谓词编译不过 #6113, [Decision]applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865 (the ruling and its fence).Generated by Claude Code