Repository navigation
feat(spec): sink the #7865 injected-column provenance derivation into @objectstack/spec/data and warn at lint time - #8342
Conversation
… @objectstack/spec/data and warn at lint time (#8116) Move the injected-system-column definition tables (AUDIT_FIELD_DEFS and siblings), injectedSystemColumnDefs, and the #7865 provenance trio (platformProvisionsStorage / resolveInjectedColumnProvenance / unprovisionedInjectedColumns) into packages/spec/src/data, per the 2026-08-12 maintainer ruling on #8116 (option 1 — the WHAT-half move #3786 anticipated, scoped to the provenance predicate). @objectstack/metadata-core re-exports every previously-public name, so its surface and every downstream import are unchanged; the served- document injection/strip pair (#6562) stays there, now consuming the spec tables plus the newly exported isInjectedColumnDefinition. @objectstack/lint (spec-only by contract) consumes the export: validate-expressions warns on record.<anchor> / previous.<anchor> reads, and validate-semantic-roles warns on stageField / highlightFields pointers, when the anchor is an injected system column on an ADR-0015 external object — registered, addressable, and backed by no storage. Warning severity per the #7219 criterion (no closed oracle over the remote schema); an author-declared column of the same name is 'author' provenance and never warned (#7859's security direction). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…mpleteness guard The new unprovisioned-anchor pass introduces five locals whose member reads are Map/Set/AST plumbing, never metadata keys; the meta-guard's completeness check requires them excused by name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…ta exports (10 added, 0 removed) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 3 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
…tsc errors the TEST_DEBT ratchet caught (#8116) The #8116 test block's helper took Array<{ severity?: string }>, so every .message/.where read off its result was TS2339 under the lifted-exclusion measurement (recorded 20 -> measured 29). Typed over the module's own ExprIssue, the lifted-exclusion count is back to exactly the recorded 20; the ledger entry is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…venance-predicate-to-spec
…tree (#8116 x #8341/#8335) Discharges the os-regen deferral from the preceding merge commit: the data.json shards regenerate with both sides present — this PR's 10 new provenance exports and main's landed entries (zodIssuesToFields intact in the api.json shards). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2
…t serve, and refuse them at authoring time (objectstack-ai#7912) (objectstack-ai#8426) * fix(rest,lint,spec): prune nav entries whose destination object cannot serve, and refuse them at authoring time (objectstack-ai#7912) A `type: 'object'` nav entry pointing at an object that cannot answer a list was served in the `/meta` payload anyway. Two independent conditions make a destination unservable — `enable.apiEnabled: false` (404) and an `enable.apiMethods` whitelist without `list` (405) — and both are pure functions of the object's own `enable` block, so the destination is dead for every persona. That is why no `requiredPermissions` gate on the entry could ever prune it. Per the maintainer ruling of 2026-08-12 (option (c)): derive the fact, mint no new key. - `filterAppForUser` consults the destination's `enable` for every `type: 'object'` entry, on both `/meta` app routes and inside `children` and `areas[]`. Fail-open on unknown objects and unreadable metadata, so `requiresObject`'s client-only pin and objectstack-ai#3770 both stand. - The mandatory companion: `os validate` / `os build` / `os lint` now fail with `nav-object-unservable`, naming the entry, the object, the offending `enable` key path and which condition fired. The serving side logs the same facts. - The two-step gate order is declared once as `apiExposureDenialReason` / `canServeApiOperation` in `@objectstack/spec/data`. The REST data gate, the nav prune and objectstack-ai#7909's invariant test all read that one export instead of re-spelling it — the third spelling is what this extraction removes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj * chore(spec): regenerate api-surface / export-origins after merging origin/main (objectstack-ai#7912) Both artifacts were merged without a text merge: this branch and objectstack-ai#8342 each added exports to `@objectstack/spec/data`, so neither side's baseline covered the union. Regenerated from the merged tree, after rebuilding dist — the gate reads the BUILT dist, and on a stale one it reports phantom removals that the generator would then write. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj * fix(rest): give the objectstack-ai#7912 nav-servability test's import an explicit .js extension (objectstack-ai#7912) `check:type-check-debt` went red: @objectstack/rest's TEST_DEBT records 155 raw tsc errors and the re-measure reported 156 (+1). Attributed, not assumed. The package-level `typecheck` is genuinely clean — `packages/rest` is one of the 20 packages whose tsconfig hides its own tests from tsc, so nothing local can see the test layer. Replaying the ledger's own program (the package tsconfig with the test globs dropped from `exclude`) gives 156, of which exactly ONE is in the new file and exactly 155 are not — matching the recorded number, so nothing pre-existing on main drifted: meta-app-nav-servability-gate.test.ts(22,28): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './rest-server.js'? The package resolves NodeNext, so the extensionless spelling the older test files use is a TS2835 — 67 of them are frozen in the ledger. The fix is the extension, so a new file does not add the 68th. ⛔ The TEST_DEBT entry was NOT raised, and `scripts/check-type-check-coverage.mjs` was not touched. The ledger is a ratchet: `--re-measure` now reports "none above its recorded number / surplus: none — every entry sits exactly at its measurement". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7vaLs7bhBPi9m3JyzkhDj --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #8116
What this does
Implements the 2026-08-12 maintainer ruling on #8116 (option 1, verbatim 「接受你的全部建议。」): sink the #7865 injected-column provenance derivation into
@objectstack/spec, keep@objectstack/metadata-corere-exporting it so nothing downstream breaks, and let@objectstack/lintconsume the spec export to deliver the author-time warning the card exists for.1. The move (
packages/spec/src/data/injected-system-column-provenance.ts)Moved from
@objectstack/metadata-core, beside the WHICH-half derivation (resolveInjectedSystemColumns, #5378):AUDIT_FIELD_DEFS,TENANT_SCOPE_FIELD_DEF,OWNER_FIELD_DEF,OWNING_BUSINESS_UNIT_FIELD_DEF) — the WHAT-half move the ruling names as the one 排查「手抄 spec 清单 + "keep in sync" 注释」模式:一天内确认三例,全部曾静默漂移 #3786 anticipated,injectedSystemColumnDefs,applySystemFieldsinjects platform anchors intoexternalobjects the platform provisions no storage for — three consumers have now independently re-derived "that column is not really there" #7865 provenance trio:platformProvisionsStorage,resolveInjectedColumnProvenance,unprovisionedInjectedColumns(+ theInjectedColumnProvenancetype),isInjectedColumnDefinition— previously metadata-core-private, now exported because the strip (metadata-core) and the provenance verdict (spec) must share one identity spelling; also the convergence target the [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 ruling lists for the hand-rolledequalsShippedDefcopies.Scope fence respected: the served-document injection/strip pair (#6562) and every other derivation stay in
@objectstack/metadata-core; no unrelated derivation migrated.2. The shim (
packages/metadata-core/src/injected-system-columns.ts)Re-exports every previously-public moved name from
@objectstack/spec/data— metadata-core's public surface is unchanged. Proof: the #7865 producer test (packages/metadata-core/test/injected-column-provenance.test.ts, the 20-assertion matrix) is byte-unchanged and passes against the re-export. All downstream importers (objectql,metadata-protocol,plugin-security,plugin-sharing,qa/dogfood) import via metadata-core and need no change.3. The warning (
packages/lint)system-fields.ts:unprovisionedInjectedColumnsFor(objectDef)— the marker in per-object set shape, delegating to the spec export (never a hand-copied predicate).validate-expressions.ts: every object-bound expression surface (flow/edge conditions, validation rules, fieldrequiredWhen/readonlyWhen/visibleWhen, per-optionvisibleWhen, actionvisible/disabled, sharing-rule conditions, hook conditions, field formulas) warns when it readsrecord.ANCHOR/previous.ANCHORand the anchor is an injected system column on an ADR-0015externalobject — registered and addressable, but backed by no storage, so the predicate silently degrades at query time (on SQLite: constant-false, HTTP 200, zero rows, no error). AST-based member reads only (has()covered); bare identifiers are never judged, so flow variables cannot false-positive.validate-semantic-roles.ts:stageField/highlightFieldspointers at an unprovisioned anchor get the new advisory rulesemantic-role-field-unprovisioned(barrel-exported per theFLOW_TRIGGER_UNKNOWN_EVENT规则 id 常量没从@objectstack/lint导出 —— 消费者拿不到,只能对字面量 #5648 contract).Severity: warning, not error (dispatch assumption 2 confirmed): the #7219 family's criterion — an error needs a closed oracle, and lint cannot see the remote deployment's schema; the degradation is non-fatal; the security-critical members of the class are fenced at runtime (#7833 / #7859 / #7858). The
'author'fail direction is preserved end to end: an author-declared column of the same name is the author's real remote column and is never warned (#7859's tenant-wall direction, pinned in tests on both surfaces).File-surface note (declared surface + 2 files) and the view-filter residual
The dispatch's lint surface named
system-fields.ts/validate-expressions.ts; delivering the ruling's highlight half requiredvalidate-semantic-roles.ts(the one rule that resolvesstageField/highlightFieldsper object) plus its barrel line inindex.ts(a #5648-enforced consequence of the new rule id). No in-flight claim touches either file. View filters are not covered here: the filter-position field checks resolve against the object-independent blanketSYSTEM_FIELDS, so there is no per-object resolution site to attach the marker to without converting those rules first — #5378-shaped work beyond this card's "provenance predicate only" fence. Out of scope: #8340 remains open and tracks it.Mechanism assumptions (dispatch), verified
external,fields, the plan inputs); the moved module imports only spec siblings.Verification (all local, real readings)
@objectstack/spec: build green;check:generatedgreen after regenerating exactly the two artifacts it proved stale (api-surface/data.json,export-origins/data.json— delta is exactly the 10 new exports, 0 removed); full suite 389 files / 10255 tests green;typecheckgreen (src + test layer).@objectstack/metadata-core: build + 162/162 tests (provenance matrix unchanged);typecheckgreen.@objectstack/lint: full suite 72 files / 1970 passed (includes the new finding: author-time expression validation resolves injected anchors on external objects but cannot warn they are unprovisioned — the #7865 provenance marker is unreachable from @objectstack/lint #8116 blocks);typecheckgreen. One lap: the validate-expressions / validate-security-posture 也有同形的 spec 不声明键的??别名读法(#5009 建议 3 的核对结果) #5017 receiver-completeness guard required the new AST-walk locals excused by name (d212db942).@objectstack/objectql196 files / 3451 tests green;@objectstack/metadata-protocol77 files / 1117 tests green.check:nul-bytes,check:adr-anchors,check:spec-parsed-alias,check:cross-package-test-inputs,check:type-check-coverage,check:changeset-gate-self-tests,check-changeset-no-majorall green.check:type-check-debt --re-measurerefuses to run without the whole workspace's built closure (its own designed guard); CI builds that closure first.Changeset: minor for
@objectstack/spec+@objectstack/lint, patch for@objectstack/metadata-core. Not breaking — no ADR-0087 disposition required.Generated by Claude Code