Add field-reference-to-alias conversion so stored reference_to serves as reference - #13847
Add field-reference-to-alias conversion so stored reference_to serves as reference#13847os-warren wants to merge 1 commit into
Conversation
…s reference Protocol-18 ADR-0087 D2 entry canonicalizing the legacy objectql field-key dialect reference_to -> reference on object and object-extension fields, retiredFromLoadPath from day one (FieldSchema keeps its named rejection). Registered in the step-18 migration chain so os migrate meta (and --stored) rewrites old sources; serve-face end-to-end pins in metadata-protocol prove a stored row seeded around the Zod gate is served with only the canonical spelling across getMetaItems / getMetaItem / loadMetaFromDb. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
📓 Docs Drift CheckThis PR changes 1 package(s): 34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 127 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 9a50c30f3d2772c716c8bb4610ea2ad01620b441 && git checkout 9a50c30f3d2772c716c8bb4610ea2ad01620b441
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 86e765a7704eff4de2eb070330ff66a0e211c3aa ef1563e4db10bb088b0146cd9bf6fa8cc692efc4 && git checkout -B drift-repro 86e765a7704eff4de2eb070330ff66a0e211c3aa && git merge --no-ff ef1563e4db10bb088b0146cd9bf6fa8cc692efc4
node scripts/docs-audit/affected-docs.mjs --json 86e765a7704eff4de2eb070330ff66a0e211c3aa
|
Fixes #13700
What landed (work item 2 — the page-header-subtitle-alias precedent, adapted to a key FieldSchema already rejects)
field-reference-to-alias(packages/spec/src/conversions/registry.ts, protocol-18 bucket): field keyreference_torenames toreferenceonobjects[].fieldsandobjectExtensions[].fields, house ADR-0087 别名转换会把「被遮蔽的旧拼法」留在存量元数据里 —— 节点 config 收紧后它从静默丢弃变成执行期硬拒 #4923 precedence (an already-canonicalreferencewins; a redundant twin is dropped; a disagreeing pair is kept for the author).retiredFromLoadPath: truefrom day one — the key never had an accepted authoring window (FieldSchemahas always refused it by name), so the authoring-surface rejection with its rename hint is UNCHANGED. This is the precedent-implied relation to the existing rejection surface: normalization owns data at rest, the rejection keeps teaching authors; nothing was loosened at parse time. The widened accept surface is exactly the stored-rehydration pass and the migration chain.os migrate meta: registered in step 18conversionIdsplus rationale prose (packages/spec/src/migrations/registry.ts). No CLI code change was needed —migrate metareplays the chain viaapplyMetaMigrations, so chain registration IS the CLI support (the claim comment predicted possiblepackages/clitouches; they did not materialize).packages/rest/src/rest-server.tsuntouched (cross-seat fence respected).conversions.test.ts: notice shape, ADR-0087 别名转换会把「被遮蔽的旧拼法」留在存量元数据里 —— 节点 config 收紧后它从静默丢弃变成执行期硬拒 #4923 precedence, idempotence, load-posture exclusion (the plain posture does NOT rewrite — the schema rejection stays the only authoring answer), stored-row rehydration, and the premise pin thatFieldSchemastill declaresreferenceand rejectsreference_toby name.@objectstack/specminor.gen:spec-changesandgen:upgrade-guidewere run and produced byte-identical output — both artifacts project released majors only (protocol 17), and all 14 sibling protocol-18 conversions are equally absent from them today.check:spec-changes,check:upgrade-guide,check:migration-registry,check:adr-0087-registrationall green.Serve-face evidence (the credential ui#6837 half 2 waits on)
End-to-end pins in
packages/metadata-protocol/src/protocol.stored-conversions.test.ts: a stored object row carryingreference_tois seeded directly into the stub engine — bypassingsaveMetaItem's schema gate, exactly like a real legacy row — and served through the three real serve seams:getMetaItems: the served field carriesreference: 'crm_company'and the dialect key is ABSENT;getMetaItem: same, plus_diagnostics.valid: true(chain-owned history is not reported broken);loadMetaFromDb: boot hydration registers the CONVERTED body,invalid: 0.The serve log shows the live notice verbatim: "stored object/crm_contact carries a pre-protocol shape; converted field.reference_to at objects[0].fields.company_id.reference ... re-save it (Studio edit or save, or run
os migrate meta --stored --apply) to persist the canonical shape".Ablation — both legs rebuilt through dist (metadata-protocol resolves
@objectstack/specvia its exports, i.e.dist/, per the KNOWN_UNALIASED_TEST_IMPORTS ledger). Predicted direction, stated before the run: the 3 new pins go red, the 6 pre-existing ones stay green. Mutation leg: the conversion's two apply-call key arguments were replaced with a marker; the mutation was proven on disk by anchored grep counts (marker count 0 to 2, original callsite count 2 to 0), spec was rebuilt, the marker was proven present in dist viaablation-dist-preflight(plant mode), and the serve pin then failed exactly as predicted: 3 failed, 6 passed. Restore leg:git checkout HEADrestore proven byte-identical to the HEAD blob (git hash-objectequal,git diff HEADempty), spec rebuilt again, marker proven ABSENT from all 215 built dist files (preflight--absent), serve pin 9/9 green. The mutation script carried an EXIT/INT/TERM restore trap using absolute paths.Work item 1 — not re-measured
Per triage comment 5479038256 it is already answered (0
reference_tohits in conversions+migrations with an 89-hit control on the same file). The premise was re-verified once at pickup against origin/main (grep exit 1 on those paths; positive controlpage-header-subtitle-aliaspresent in registry, walk and tests): still uncovered until this PR — no race with a parallel landing.Work item 3 — census only, measured and NOT judged (per the dispatch fence, submitted for the spec-seat ruling)
Key readings (full table in the report comment on #13700):
reference_fieldhas ZERO occurrences in the whole objectstack tree (positive controlreference_to: 212 hits, same query shape);referenceFieldin objectstack is 14 hits, every one either the prose placeholder pattern "bracket referenceField colon parentId" or the unrelated identifierreferenceFieldNamesin packages/rest. In objectui (read-only, at origin/main a7807b4) the two spellings are TWO DIFFERENT CONCEPTS, both declared in objectui's OWN types package:reference_field= display field of the referenced object (39 hits, declared + read + emitted + taught in docs),referenceField= child-side FK column of a related list (105 hits, declared + heavily read). Neither spelling is a spec key today. Nothing was written into the conversion table.Verification (union at head ef1563e)
typecheckgreen including the test layer (check:test-typecheck OK).protocol.stored-conversions.test.ts9/9. Note: the package declares notypecheckscript — its type coverage rides the repo-level CI programs; NOT MEASURED locally.scripts/pm/dispatch-gates.mjsat ef1563e (stderr header names this tree/commit); the directly-implicated families ran green locally: check:adr-0087-registration, check:spec-changes, check:upgrade-guide, check:migration-registry, check:authorable-surface, check:engine-double-contract, check:cross-package-test-inputs, check:test-source-alias, check:query-options-erasure, check:objectql-double-limit, check:where-matcher, check:nul-bytes. The full farm is CI's run.Contract review
Clause-②: yes — the conversion makes a previously-rejected spelling accepted-and-normalized on the stored/migrate paths. Parked as draft with
needs:contract-review(dual carrier: this PR and card #13700), per the in-seat contract-review flow (#13795). ui#6837 remains open and is not addressed here — that card is half 2 and stays blocked on this one.Generated by Claude Code
Generated by Claude Code