Uh oh!
There was an error while loading. Please reload this page.
feat(metadata-core, objectql): machine-readable provenance for injected system columns (#7865) - #8115
Conversation
…ed system columns (#7865) Direction B per the 2026-08-12 maintainer ruling: applySystemFields keeps injecting the platform anchors into external (ADR-0015) objects, and the anchors it registers without provisioning storage now carry a machine-readable provenance marker — spelled as an exported derivation (resolveInjectedColumnProvenance / unprovisionedInjectedColumns / platformProvisionsStorage in @objectstack/metadata-core, re-exported by @objectstack/objectql) rather than a provisioned key on the field defs, so no document byte changes anywhere: FieldSchema is strict (a data key would stamp _diagnostics invalidity on every served federated object, or become an authorable forgeable wall-off switch), and the #7859 Layer-0 guard plus the #4326 round-trip strip read the anchor defs by exact key-count identity. The three existing consumer guards (#7833 engine, #7859 plugin-security, #7858 plugin-sharing) are deliberately untouched — opportunistic convergence per the ruling. Proofs: unit matrix in metadata-core, marker-vs-live-injection parity in objectql, and a real showcase boot pinning the before-picture (7 anchors on showcase_ext_customer, byte-identical), the marker verdicts, behavioural parity with the live Layer-0 guard, and /meta serving an unchanged, valid post-injection document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014C8pAprWdmtecFsEprZax4
…ernal-anchor-provenance
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 3 package(s): 17 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also reference the affected code. These are read-only:
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#7865
What this implements — the 2026-08-12 maintainer ruling, direction B
applySystemFieldskeeps injecting the platform anchors intoexternal(ADR-0015) objects — deliberately unchanged — and the anchors it registers without provisioning storage now carry a machine-readable provenance marker, so consumers ask one authoritative question ("is this column actually provisioned by the platform?") instead of each re-deriving "that column is not really there".The marker is the ruling's licensed equivalent ("
provisioned: false或等价物"), spelled as an exported derivation in@objectstack/metadata-core(the package the definition tables already live in, per #6562's discipline), re-exported by@objectstack/objectqlat the injection site:platformProvisionsStorage(def)—falseexactly for ADR-0015externalobjects; the one exported spelling of the predicatesyncObjectSchemaroutes by.resolveInjectedColumnProvenance(def, column)—'author' | 'injected-provisioned' | 'injected-unprovisioned' | 'absent'.'injected-unprovisioned'is the marker: the platform's own injected anchor with no storage behind it.unprovisionedInjectedColumns(def)— the enumerable form.Why the marker is an API and not a
provisioned: falsekey in the dataMeasured, not asserted — the literal data-key spelling cannot land without moving surfaces the ruling fenced off:
FieldSchemaisstrictObject. An undeclared key on the injected defs reaches/meta(which serves the post-injection document) and stamps_diagnostics: { valid: false, unrecognized_keys }on every federated object — the exact meta:applySystemFieldsstampsindexedonorganization_id— a keyFieldSchemarejects by name, so every registry-backed object read answers_diagnostics: { valid: false }#6810 defect, closed once already. Declaring the key instead makes it authorable: a forgeable switch over the author's own tenant wall, the shapefederated-phantom-anchors.ts(fix(plugin-security): Layer 0 no longer walls a federated object with a phantom organization_id (#7835) #7859) records as deliberately rejected.equalsShippedDef) and the [P3] Read decorations (_diagnostics, _draft) round-trip into persisted sys_metadata bodies #4326 round-trip strip (isInjectedDefinition). Any key added to the external-object anchors flips both from "the platform's anchor" to "the author's field" — for the guard that re-emits the phantom tenant predicate, resurrecting the measured zero-rows defect, and the guard is explicitly out of this PR's scope.If the maintainer wants the data-key spelling regardless, that is the fork the card names, and this PR's evidence is the input to it.
Before / after — the card's own measurement, on a real boot
Real boot of the shipped showcase (
@objectstack/verifyharness, same as the #7835 measurement),packages/qa/dogfood/test/federated-anchor-provenance.dogfood.test.ts:Before-picture (preserved — direction B keeps injecting):
showcase_ext_customer(external.remoteName: "customers", remote table columnsname, email, region, lifetime_value) still registers all 7 platform anchors:organization_id, created_at, created_by, updated_at, updated_by, owner_id, owning_business_unit_id— every one byte-identical to the shipped definition tables (pinned per anchor; noprovisionedkey in any registered or served field definition).After-picture (the marker):
platformProvisionsStorage(showcase_ext_customer)→falseunprovisionedInjectedColumns(showcase_ext_customer)→ exactly the 7 anchorsresolveInjectedColumnProvenance(…, anchor)→'injected-unprovisioned'for each of the 7;'author'for the 4 declared remote columnsshowcase_project:'injected-provisioned', empty unprovisioned listBlast-radius surfaces — proved unchanged, one line each
/meta(post-injection document, meta: an overlay-backed object read OMITS the injected system columns a registry-backed read includes — the same endpoint answers two different field sets #6562 precedent): no serve-path file touched; on the real boot,GET /meta/object/showcase_ext_customerthrough the real HTTP stack answers 200 with the same field-name set as the registry, anchor defs byte-identical to the shipped tables, and no_diagnosticsinvalidity (dogfood case "/meta NO-REGRESSION", green). The/metaroute itself (domain:cliterritory) is unmodified.packages/specis untouched entirely (no schema, no authorable key, no react-blocks/SDUI artifact moved), and objectui is untouched.minor, non-breaking, so no ADR-0087 disposition marker is required (node scripts/check-changeset-no-major.mjsgreen;check:changeset-gate-self-testsgreen). The ruling's "ADR-0087 entries ride this PR if the marker needs one" resolves to: it does not need one.Consumer guards (#7833 engine, #7859 plugin-security, #7858 plugin-sharing): untouched, per the ruling's opportunistic-convergence clause. Behavioural parity with #7859's live guard is pinned on the real boot: marker
'injected-unprovisioned'⇔ Layer 0 composes noorganization_idpredicate on the federated object; marker'injected-provisioned'⇔ the wall stands on the local control. An author-declaredorganization_idon a federated object answers'author', never the marker, so convergence can never suppress a wall the author made real — the fail direction of any inexact match is'author', toward enforcement.#5378 / #6113 (author-time expression validation): the marker does not reach that surface in this PR.
@objectstack/lint's package contract is "depends on@objectstack/spec, never on a runtime", and the marker needs the definition tables that live in@objectstack/metadata-core(the WHICH/WHAT split of #3786). The derivation's inputs are all document-declared keys, so a future card can sink it into spec if that surface needs it; noted in the dev report.Tests and gates (local, real output in the dev report)
@objectstack/metadata-coretest: 10 files / 162 passed (new:injected-column-provenance.test.ts— verdict matrix, security direction, fail direction, array shape, opt-outs, the fence pin on the def tables).@objectstack/objectqltest: 190 files / 3379 passed (extendedinjected-system-columns-parity.test.ts: marker ↔ live-injection parity over the full branch matrix × external, byte-identity of injected defs, author-declared anchor stays'author').@objectstack/dogfood: full suite 96 files / 630 passed; the two measurement files re-run verbose: 10/10 green.check:durability-log-level✓,check:engine-double-contract✓ (169 pinned),check-engine-split-ratio✓,check:test-source-alias✓ (new metadata-core import aliased to source in dogfood's vitest config, per the gate's own prescription),check:query-options-erasure✓ (67 sites, none new),check:type-check-debt --re-measure✓ (35 entries, 1976 raw errors, none above its recorded number;--lowernot run, per dispatch),check:nul-bytes✓, ESLint on all changed files ✓,check:changeset-no-major✓.Serial-constraint note:
registry.tschanges are confined to the re-export block (~line 327) andapplySystemFields(~line 364) —uninstallPackage(#7970's seam) is untouched.Generated by Claude Code