Repository navigation
fix(spec): os migrate meta --stored lists a stored object bound external to a stored or registered managed datasource - #25299
Conversation
…t the datasource the save door resolves Claude-Session: https://claude.ai/code/session_01P6EWuSSDC4q1RNpSTNbyCU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6EWuSSDC4q1RNpSTNbyCU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 1 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 — 145 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 0c1ecfc677c8a454a36d35d6c860207d544bcbde && git checkout 0c1ecfc677c8a454a36d35d6c860207d544bcbde
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f47f7f222f1c8c03ab866efa428f4b50ff72b9b8 aa0ca3a339eb4c6fada43313722da119f1244f6c && git checkout -B drift-repro f47f7f222f1c8c03ab866efa428f4b50ff72b9b8 && git merge --no-ff aa0ca3a339eb4c6fada43313722da119f1244f6c
node scripts/docs-audit/affected-docs.mjs --json f47f7f222f1c8c03ab866efa428f4b50ff72b9b8
|
Contract reviewServed-tier: Inputs read: card #25279 (body and all three comments: claim 6350967049, claim amendment 6351011546, os-dev-report 6351912835), PR #25299 (body, 7-file list, net diff against ① Derived judgmentsAccept-set and public-surface changes the diff implies, each judged:
Observations, none blocking: (a) a datasource read that throws inside the loop aborts the pass rather than recording the row ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
Keeps both refused-shape describes in refused-shapes.test.ts. The has() argument detector reads the stack alone and ignores the context parameter #25279 added. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jc59n5NWaMp1khDwL7Fici
Requested by Bill · project thread
Fixes #25279
Clause-②: yes (widening)
Metadata-protocol: none
The stored-shape listing for
object-external-binding-managed-datasource-refusedresolved an object's named datasource only from the stack being converted.os migrate meta --storedhands the chain one stored row at a time, so for a stored object row the listing could judge only a binding on the primary datasource ('default'). The object save door from #25257 also refuses a binding on a datasource stored or registered beside the object, so the pass missed exactly the rows whose next save is refused.This takes the card's first branch: the pass hands down the datasource the door would read, and the detector judges it through the same predicate.
externalBindingDatasourceDefinition(name)inprotocol.tsis thereadMetaItem({ type: 'datasource', name })read thatexternalBindingSaveRefusalalready did inline (the environment's stored row, else the definition the code layer registers). The object half of the save door now calls it, and so does the pass, so the two cannot judge a binding against different datasources.migrateStoredMetadata, for each non-flow row, asksstoredRowKnownDatasources(singular, body). For anobjectrow withexternaland a nameddatasource, it resolves that one datasource and passes it down as a one-entry map throughconvertStoredItemDetailed→applyConversionsToStoredItem. Any other row passes nothing. A failed read propagates, as it does at the door.@objectstack/spec: an optionalApplyConversionsOptions.knownDatasources, a read-only map from datasource name to that datasource's definition (only itsschemaModeis read), whichStoredConversionOptionsinherits.applyConversionshands it toreportRefusedStoredShapesas aRefusedShapeContext, so only the refused-shape listing reads it (only with anonTodosink). Theexternaldetector reads the stack's owndatasourcesfirst, then the map.refused-shapes.tsstays internal (not re-exported).surfacestring now reads "…on the primary datasource, or on a managed one the stack declares or the caller resolves (a stored or registered datasource)".Measure first (base
c9be979dd)externalBindingSaveRefusal, object half): one read,readMetaItem({ type: 'datasource', name }). That read takes the environment's active stored row first and falls back to the code layer's definition (the MetadataService registration, then the registry). A code-defined datasource name declines a stored row (declinesStoredRow), so the code definition wins there.migrateStoredMetadata): it reads everyactiveanddraftsys_metadatarow in two queries before the loop. So the stored datasource rows are in reach before any object row is converted. The pass now uses the door's own read for each object row, so registered datasources and the code-wins rule are covered too.onTodocallers:applyConversionsToStoredItemis called with a sink only byconvertStoredItemDetailed(the pass). The other sink,canonicalizeStoredFlowinservice-automation, goes throughapplyConversionsToFlowover{ flows: [...] }, which theexternaldetector never reads. Neither changes behaviour without the new option.Tests
packages/spec/src/conversions/refused-shapes.test.ts(+7): a stored row on a known datasource declared'managed', or with noschemaMode, is listed once and not rewritten. A known'external'or'validate-only'datasource, or one the row does not name, is not listed. A datasource the stack declares is read before the caller's. Without anonTodosink the map is never read.packages/metadata-protocol/src/protocol.external-binding-named-datasource-refusal.test.ts(+6). An object row is planted straight into thesys_metadatadouble (stored before the door existed), and thenmigrateStoredMetadataruns in preview. Pinned cases:skippedwith this entry's TODO atobjects[0].external, and the same body offered tosaveMetaItemis refused422 INVALID_METADATAatexternal;@objectstack/spectest 708 files / 21527 tests passed, typecheck exit 0.@objectstack/metadata-protocoltest 258 files / 20921 tests passed, typecheck exit 0.await this.storedRowKnownDatasources(singular, body)was replaced withundefinedviascripts/ablation-replace.mjs(anchor 1 → 0, blob919137f3→cad58c17). Result: 3 failed / 25 passed, with the three listing pins red and every control green. This is the base behaviour of the pass. Restore proven: blob == HEAD919137f3,git diff HEADempty.@objectstack/specresolved to adist/built from this branch.?? context.knownDatasources?.get(name)was dropped (anchor 1 → 0, bloba387f79e→39524e03). Result: 2 failed / 60 passed, the two listing pins red. Restore proven: blob == HEADa387f79e,git diff HEADempty. The test importssrcrelatively.Gates
All local readings were taken on head
aa0ca3a33(the final commit,origin/main49185cdb8merged in).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 88 families.--ranreconciliation: "88 derived famil(ies) accounted for — 87 run, 1 NOT-MEASURED". 87 exit 0.pnpm check:dual-build-cjs-loadsexits 3 (PREREQUISITE NOT MET: it loads every package'sdist, and this worktree built only the closure), so it is NOT MEASURED locally and left to CI.check-plugin-teardown-shape.mjs --self-test(shallow history; the named commit was fetched) andpnpm check:lean-entry-closure(@objectstack/objectqlnot yet built) exited 3. Both exit 0 on re-run.pnpm --filter @objectstack/spec check:generated: "All 14 generated artifacts are up to date", against adistbuilt from this branch withoutOS_SKIP_DTS.Acceptance notes
external. The pass lists the pair at each object row, not at the datasource row. Either fix the owner chooses (federate the datasource, or dropexternal) clears both. A datasource-row TODO would need its own D3 id and was not asked for.external, not cached across rows. The read is the door's own, and anapplyrun may rewrite rows between two object rows.os migrate meta --storedlists each such row. That sentence is now true for stored and registered datasources, so the entry is unchanged and the second branch's guidance edit is not needed.packages/spec/src/conversions/apply.tswas added to the claim's declared surface. The option has to pass throughapplyConversionsto reach the detector. The seat was told before the edit.Generated by Claude Code