Repository navigation
refactor(core, plugin-security, plugin-sharing): one isFederatedObject binding, read from the spec predicate - #31896
Conversation
Core's isFederatedObject now returns !platformProvisionsStorage(schema). plugin-security and plugin-sharing re-export core's binding instead of holding their own copies, and the other runtime copies of the external presence test (objectql search companion, cli migrate x3, analytics federation gate, schedule trigger bind line, save-door datasource binding) ask the same binding. Claude-Session: https://claude.ai/code/session_01ADHkmSLoDy6zFb6HpabaTT Co-authored-by: Claude <noreply@anthropic.com>
…ngeset Claude-Session: https://claude.ai/code/session_01ADHkmSLoDy6zFb6HpabaTT Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 8 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 85 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2e68567ae236e7502a78fc600a3931f3de960c02 && git checkout 2e68567ae236e7502a78fc600a3931f3de960c02
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ac8fede8f938439d2228f5b1404042d113a3812e 10eb82edc405e735543d18b08e21f1ec1dee51c7 && git checkout -B drift-repro ac8fede8f938439d2228f5b1404042d113a3812e && git merge --no-ff 10eb82edc405e735543d18b08e21f1ec1dee51c7
node scripts/docs-audit/affected-docs.mjs --json ac8fede8f938439d2228f5b1404042d113a3812e
|
|
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM 维护者速读
席位核对 — ACCEPT · domain:server#2 · Head-sha
|
Requested by Steve · project thread
Fixes #31849
Clause-②: no
Metadata-protocol: none
维护者速读
external)”在运行时只剩一个函数:core 的isFederatedObject,它直接读 spec 的platformProvisionsStorage;plugin-security、plugin-sharing 改为转出 core 的同一个函数,另外 7 处手写的external判断也改为调用它。schemaMode)就得逐处去找,漏一处就会出现两层答案不一致。null、undefined、数组、原始值,新旧答案相同。回滚即撤本 PR。packages/core/src/security/tenant-field.ts(isFederatedObject本体);packages/plugins/plugin-security/src/federated-phantom-anchors.ts(删掉的副本);packages/core/src/security/tenant-field.test.ts(新钉)。git grep -n "external != null\|external == null" packages/core packages/objectql packages/plugins预期在非测试源码里零命中。What changes
@objectstack/coreisFederatedObject(security/tenant-field.ts) returns!platformProvisionsStorage(schema)from@objectstack/spec/data, the spelling [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's convergence map names.@objectstack/plugin-securityand@objectstack/plugin-sharingfederated-phantom-anchors.tsdrop their own copy (security:external != null; sharing: its own!platformProvisionsStoragewrapper) andexport { isFederatedObject }from@objectstack/core. The export name stays where it was, so every importer keeps resolving.@objectstack/objectqlkeeps re-exporting the core binding, unchanged.packages/objectql/src/search-companion.tsisDdlManaged(external == null)packages/cli/src/commands/migrate/unmapped-columns.ts(the "federated" outside reason)packages/cli/src/utils/schema-migrate.ts(managed-drift driver scan)packages/cli/src/utils/schema-migration-plugins.ts(coverage passfederatedcount)packages/services/service-analytics/src/plugin.tsisExternalObject(ADR-0062 D6 gate)packages/triggers/trigger-schedule/src/time-relative-trigger.tsorganizationScopeIsInertForpackages/metadata-protocol/src/protocol-save-door-refusals.ts(datasource-boundexternalread; was!== undefined && !== null)objectqlengine-driver-options.ts,integrity/dangling-reference-audit.ts;plugin-securitysecurity-plugin.ts;plugin-sharingsharing-service.ts.packages/objectql/src/federated-injected-column-readers.test.tsgains a READERS rowsearch-companion.ts#isDdlManaged :: isFederatedObject()(not-a-read), because the search companion now calls the seam by name.Why
Measure on
origin/main3f0e3ac (git grep -n "external != null\|external == null\|external !== undefined\|isFederatedObject\|platformProvisionsStorage"underpackages/, non-test): besides the card's table, seven more source sites restated the presence test (listed above). Seat ruling 1 converges them too. Not converged, because they are a different question:service-datasourceexternal-datasource-service.ts:1374(external !== undefined || datasource !== 'default', a wider "routed off the default datasource" test) anddatasource-admin-service.ts(patch.external, a patch field, not an object).packages/speckeeps the one definition (no spec edit).Input equivalence (seat ruling 2):
platformProvisionsStorageanswerstrue(not federated) fornull,undefined, arrays and any non-object, and the old?.external != nullansweredfalse(not federated) on each of those, so the two agree. The only inputs where they differ are a function or an array that carries an ownexternalproperty; no producer hands one to these seams (all read registry or overlay object records).Tests
packages/core/src/security/tenant-field.test.ts(new):isFederatedObject(x) === !platformProvisionsStorage(x)and the expected verdict for{},{ external: {} },{ external: null },{ external: undefined },null,undefined,[]; plus barrel identity.packages/plugins/plugin-security/src/federated-object-binding.test.tsandpackages/plugins/plugin-sharing/src/federated-object-binding.test.ts(new): the module exporttoBecore's binding.Ablations (ruling 4), each restored with blob == HEAD and empty diff
export function isFederatedObject(schema) { return schema?.external != null }→federated-object-binding.test.tsRED (expected [Function isFederatedObject] to be [Function isFederatedObject] // Object.is equality),federated-tenant-layer0.test.tsstays green (behaviour identical). Tests 1 failed | 8 passed.!platformProvisionsStoragewrapper →federated-object-binding.test.tsRED, same assertion; Tests 1 failed | 57 passed.?.external != null→tenant-field.test.tsstays GREEN (8/8): the agreement pin cannot see a revert, by design, which is why the re-export layers carry the identity pin.scripts/ablation-replace.mjs(anchor 1 → 0, marker grep 1); tests import the module under test fromsrc, so no dist leg.Acceptance notes
external != nullin prose or as a test variant (e.g.injected-system-columns-parity.test.ts, the dangling-audit federated phantom test); they describe what a mutation would look like, not a predicate copy.packages/spec/src/data/injected-system-column-provenance.ts:263still says plugin-security holds a hand-rolled copy; that doc is spec prose and this card forbidspackages/spec/**edits.Verification
Head
10eb82ed(origin/main 1a66fcf merged in; the merge brought #31876, which edits plugin-sharing and core in other files).git grep -n "external != null\|external == null" packages/core packages/objectql packages/plugins(non-test, CHANGELOG excluded): 0 hits. Under all ofpackages/, the only remaining hits are the definition inpackages/spec/src/data/injected-system-column-provenance.ts.turbo run build --filter='./packages/**' --filter='./examples/**'under the verify lock, VERDICT command-exit 0.Package suites and typecheck
At
10eb82ed(after the main merge):federated-injected-column-readers,federated-object,search-companion*,package-scoped*(6 files, 212/212)At
73db2448(before the merge; the code these suites exercise is unchanged since):--project unit: 404/406 on the first run. The 2 failures werepublished-subpath-console.pinandpublished-subpath-hook-body.pin, which needpackages/cli/distand stopped with "packages/cli is not built". After the full build they passed 2/2 (29 tests). The integration tier is left to CI.--no-inline-configon the changed.tsfiles, exit 0.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackat10eb82ed: all 80 derived families exit 0.--ranreconciliation: "80 derived famil(ies) accounted for — 80 run, 0 NOT-MEASURED".node scripts/check-platform-checklist.mjs --changed-from origin/main: OK, 0 anchors lost.Human Minimum
None; the seat lands it.
Generated by Claude Code