docs(rest): the spec REFUSES the legacy lookup spellings, it does not fold them - #13264
Merged
os-trump merged 1 commit intoAug 30, 2026
Merged
Conversation
… fold them `rest-server.ts` and `public-form-lookup-picker.test.ts` both justified the public-picker fallback chain with a claim about `packages/spec/src/data/field.zod.ts` that is false: that it folds `relatedTo` / `referenceTo` / `target` / `targetObject` / `lookupObject` onto the canonical `reference` at parse. It does not. `FieldSchema` is a `strictObject`, and its `aliases` table is consulted ONLY from the `unrecognized_keys` path, so an entry can only ever decorate a REFUSAL with a rename hint. The five spellings are rejected by name. The prose is corrected at both sites, the guard test's "stored pre-fold rows are live data" rationale is replaced with what is actually true (there is no fold, so there is no pre-fold row; the serving read path does no schema validation, so such a def would arrive verbatim), and the comment is brought into agreement with the chain beneath it: it now names the three spellings the chain actually reads (`referenceTo`, `target`, `options.objectName`) instead of the spec's five-entry hint list, which is a different set. A three-level control is pinned so the prose cannot rot back: `reference` ACCEPTED, an unknown key REFUSED without a rename hint, each of the five aliases REFUSED with one. No behaviour change: comments, one test title, and a new pin suite.
This was referenced Aug 29, 2026
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-trump
marked this pull request as ready for review
August 29, 2026 17:04
This was referenced Aug 29, 2026
os-trump
deleted the
claude/issue-13137-lookup-alias-comment-correction
branch
August 30, 2026 03:39
This was referenced Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13137
Two load-bearing sentences in
packages/restjustified the public-picker fallback chain with a claim aboutpackages/spec/src/data/field.zod.tsthat is false, and a third divergence meant the comment did not describe the code beneath it even on its own terms. This corrects the prose and pins the fact behind it.⛔ No behaviour change. Comments, one test title, and a new pin suite — the resolution chain itself is untouched.
What was false
rest-server.tssaiddata/field.zod.ts"foldsrelatedTo/referenceTo/target/targetObject/lookupObjectall onto it at parse", and the guard test said "Stored pre-fold rows never went through the alias table, so the legacy spellings are live data, not history."FieldSchemais astrictObject, and itsaliasestable is a claim about rejection, not a normaliser. The mechanism is stated in the file that owns it,packages/spec/src/shared/strict-object.ts, verbatim:So an entry can only ever decorate a REFUSAL with a rename hint. All five spellings are rejected by name. There was never a fold for a stored row to be "pre-" of, which is why that second sentence carries zero observational content about stored data.
The third divergence — the comment now agrees with the chain
The comment named five aliases; the chain beneath it reads four members, only two of which are on that list:
relatedTo/targetObject/lookupObjectwere named and read by nothing;options.objectNamewas read and named by nothing. The rewritten comment names the head (reference) and exactly the three tail spellings the chain actually reads, and says explicitly that this is not the spec's five-entry hint list.Why a prose card was worth landing
The natural next action for an agent reading the old comment is "the spec folds these, so my consumer should be tolerant too" — precisely the move #12920's card and its triage forbid (「Nobody should resolve this by making one more consumer match the other」). So the replacement does not merely stop being wrong: it names the mechanism, and states outright that the chain is not licence to be lenient anywhere else.
That is not a hypothetical reader. The
domain:cliseat recorded three independent misreadings of the same shared constructor in one day, across three seats and two schema surfaces:rest-server.tspublic-picker commentdata/field.zod.tsfolds … at parse"public-form-lookup-picker.test.tsguard rationaleexternal.validation.checkOnBoot) — card, triage and the dispatch orderdatasource.zod.ts'scheckonboot/validateonbootcalled a "key-fold table"The third one had a measurable cost: the dispatch order derived from it required an assertion that a fold be honoured — an assertion that would have been green under every implementation, because the fold does not exist. The dev pushed back and pinned the truth instead. One wrong sentence nearly manufactured a vacuous gate.
Scope — and what this deliberately does NOT do
⛔ The legacy resolution chain is not changed. Its fate belongs to #12920, which is in the decision inbox; that card stays open and is not addressed here.
⛔ This is not read as "dead code, delete it". The opposite holds, and the new comment says so: the serving read path replays ADR-0087 conversions (
applyConversionsToStoredIteminpackages/metadata/src/loaders/database-loader.ts) and performs no schema validation, so a stored def spelling the target the legacy way would reach the route verbatim. Whether one exists is #12920's open production census, and nothing here asserts anything in either direction.One bounded in-place addition, declared
The card names two prose sites. A scan of the file I was already editing (
grep -in 'pre-fold|folds|alias table') found the same false claim twice more inpublic-form-lookup-picker.test.ts— theroutesOver()jsdoc and the#7486suite jsdoc, the latter being the first thing a reader of that suite sees — plus the test titles readinga stored PRE-FOLD row …. Leaving them would have left the card's own purpose defeated two lines above the corrected sentence. Same defect class, same file, no new verification surface, so they are corrected here. Full file surface touched:packages/rest/src/rest-server.ts— the public-picker fallback comment (1 block).packages/rest/src/public-form-lookup-picker.test.ts—routesOver()jsdoc,#7486suite jsdoc, the guard-test rationale, the three legacy-def test titles, and the new pin suite.The same scan over
rest-server.tsfound three other comments using the word "fold" aboutRPC_QUERY_ALIAS_SLOTS. That is a genuinely different mechanism and its prose is accurate — deliberately untouched.The pin, and the ablation that proves it discriminates
#13137 FieldSchema REFUSES the legacy target spellings, it does not fold thempins the three-level control that made the original finding a measurement rather than a blanket deny:referenceis ACCEPTED and parses to.reference.zzz_not_a_keyis refusedunrecognized_keyswith no rename hint.unrecognized_keyswith a hint namingreference.Only the third level makes the second mean anything: without it, "the schema rejects
referenceTo" reads equally well as not measured. ⛔ The suite asserts nothing about whether any stored row exists.Reverse-verified against the BUILT artifact, since the suite resolves
@objectstack/spec/datathrough that package'sexports(i.e.dist/, notsrc/):packages/spec/src/data/field.zod.ts: (i) declarereferenceToon theFieldSchemashape, so it becomes ACCEPTED — the exact regression the pin exists to catch; (ii) delete the five alias-table entries, removing the rename hint.d6c5b1a6→b005bba1.@objectstack/spec, and proven to have reached the artifact:ablation-dist-preflight.mjs packages/spec OS_ABLATION_13137_MARKER→ "marker present in 26 built files".referenceTofailed onexpected true to be false(the fold/accept regression); the other four failed on the missing rename hint. LEVEL 1 and LEVEL 2 both stayed green, so the instrument was still working.git checkout HEAD -- packages/spec, blob back tod6c5b1a6,git status --porcelainempty, rebuild, then--absent→ "marker absent from all 215 built files", and the suite back to 19 passed (19).packages/spec: the ablation build'sgen:schemawrote the mutated key into the committed baselinepackages/spec/authorable-surface/data.json. Restoring only the mutated source left that artifact dirty, and the next build then refused the key as a deletion — a false red on an already-restored tree. Restore the whole package, not just the file you mutated.Verification
All at
59a1776e, exit codes captured before any pipe.pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2— 158 files / 2630 tests passed, exit 0.pnpm --filter @objectstack/rest typecheck— exit 0.check:test-typecheck: OK — @objectstack/rest's test layer compiles under packages/rest/tsconfig.test.json; 3 file(s) / 6 error(s) held in test-typecheck-debt.json (shrink-only). The debt ledger did not grow, and the test layer being in the program is what makes this a reading about the new file rather than about the sources beside it.pnpm lint(eslint . --no-inline-config, whole repo) — exit 0, 76s. Not narrowed.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed — the script reads the change set itself): 26 families run, 25 exit 0.node scripts/check-test-completeness.mjs— exit 3,PREREQUISITE NOT MET, which that gate documents as NOT MEASURED locally and explicitly not a red: it grades a savedturbo run testlog and none was passed. Same forpnpm check:dual-build-cjs-loads(exit 3, needs every package built). CI measures both.pnpm check:type-check-debt --re-measureneeds the whole workspace closure built and is left to CI;pnpm --filter @objectstack/rest typecheckabove is the targeted reading for the file this PR adds.skip-changesetLabelled, deliberately: this PR publishes nothing. The diff is source comments, one test title, and a new test — no runtime code, no exported surface, no schema or docs text. (#13198 correctly carries a changeset for the same theme because it edits a
.describe()string that ships in the generated JSON Schema; nothing here does.)Generated by Claude Code