Skip to content

docs(rest): the spec REFUSES the legacy lookup spellings, it does not fold them - #13264

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-13137-lookup-alias-comment-correction
Aug 30, 2026
Merged

docs(rest): the spec REFUSES the legacy lookup spellings, it does not fold them#13264
os-trump merged 1 commit into
mainfrom
claude/issue-13137-lookup-alias-comment-correction

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Fixes #13137

Two load-bearing sentences in packages/rest justified the public-picker fallback chain with a claim about packages/spec/src/data/field.zod.ts that 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.ts said data/field.zod.ts "folds relatedTo / referenceTo / target / targetObject / lookupObject all 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."

FieldSchema is a strictObject, and its aliases table 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:

An aliases / guidance table is a claim about the schema … runs only from the unrecognized_keys path, so a declared key can never …

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:

referenceTo = def?.reference
    ?? def?.referenceTo
    ?? def?.target
    ?? def?.options?.objectName;

relatedTo / targetObject / lookupObject were named and read by nothing; options.objectName was 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:cli seat recorded three independent misreadings of the same shared constructor in one day, across three seats and two schema surfaces:

# site wording who misread it
1 rest-server.ts public-picker comment "data/field.zod.ts folds … at parse" this repo's own comment
2 public-form-lookup-picker.test.ts guard rationale "Stored pre-fold rows never went through the alias table" same
3 #13037 (external.validation.checkOnBoot) — card, triage and the dispatch order datasource.zod.ts's checkonboot / validateonboot called a "key-fold table" a PM seat, a filing seat and a triage seat

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 (applyConversionsToStoredItem in packages/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 in public-form-lookup-picker.test.ts — the routesOver() jsdoc and the #7486 suite jsdoc, the latter being the first thing a reader of that suite sees — plus the test titles reading a 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.tsroutesOver() jsdoc, #7486 suite jsdoc, the guard-test rationale, the three legacy-def test titles, and the new pin suite.

The same scan over rest-server.ts found three other comments using the word "fold" about RPC_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 them pins the three-level control that made the original finding a measurement rather than a blanket deny:

  • LEVEL 1 positive control — reference is ACCEPTED and parses to .reference.
  • LEVEL 2 negative control — zzz_not_a_key is refused unrecognized_keys with no rename hint.
  • LEVEL 3 — each of the five aliases is refused unrecognized_keys with a hint naming reference.

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/data through that package's exports (i.e. dist/, not src/):

  • Mutation, in packages/spec/src/data/field.zod.ts: (i) declare referenceTo on the FieldSchema shape, so it becomes ACCEPTED — the exact regression the pin exists to catch; (ii) delete the five alias-table entries, removing the rename hint.
  • Proven on disk, not by an exit code: injected marker count 1, deleted alias line count 0, and blob d6c5b1a6b005bba1.
  • Rebuilt @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".
  • Result: 5 failed | 14 passed (19). referenceTo failed on expected 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.
  • Restore leg, given the same treatment: git checkout HEAD -- packages/spec, blob back to d6c5b1a6, git status --porcelain empty, rebuild, then --absent"marker absent from all 215 built files", and the suite back to 19 passed (19).

⚠️ Worth recording for anyone ablating packages/spec: the ablation build's gen:schema wrote the mutated key into the committed baseline packages/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=2158 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.
  • Gate union derived with 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 saved turbo run test log and none was passed. Same for pnpm check:dual-build-cjs-loads (exit 3, needs every package built). CI measures both.
  • pnpm check:type-check-debt --re-measure needs the whole workspace closure built and is left to CI; pnpm --filter @objectstack/rest typecheck above is the targeted reading for the file this PR adds.

skip-changeset

Labelled, 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

… 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.
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/rest/src/rest-server.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 74049254d47bd0edd2a2fcd732dcc01c91504f10packageMentionDocs.

@os-trump
os-trump marked this pull request as ready for review August 29, 2026 17:04
@github-actions github-actions Bot added the tests label Aug 29, 2026
@os-trump
os-trump added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit a094205 Aug 30, 2026
36 of 37 checks passed
@os-trump
os-trump deleted the claude/issue-13137-lookup-alias-comment-correction branch August 30, 2026 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants