Skip to content

Commit 22c0279

Browse files
claude[bot]claude
andauthored
docs(spec,rest): anchor record-validator citations by symbol, not line (#16956)
* docs(spec,rest): anchor record-validator citations by symbol, not line Seven prose/comment citations of `packages/objectql/src/validation/record-validator.ts` pointed at line numbers that no longer resolve: the file has moved under them, and a `path:NNN` anchor rots silently because the line it lands on still looks like plausible code. Re-anchor each on something that cannot rot without saying so — the enclosing symbol `validateOne`, plus the verbatim snippet where one is load-bearing: - `packages/spec/src/kernel/functional-completeness.ts` (3 sites): the `select`/`radio` rule and the `multiselect` NON-rule now cite `validateOne` and quote `allowed.length > 0 && !allowed.includes(…)` and `// free-form (tags without options)` verbatim. - `packages/spec/src/kernel/functional-completeness.test.ts` (2 sites): same two anchors on the pinned NON-rule's comment. - `packages/rest/src/rest-hook-script-fault-envelope.test.ts` (2 sites): the text-coercion citation now names `validateOne` and its bounded-string branch (`BOUNDED_STRING_FIELD_TYPES.has(t)`, of which `text` is a member) and quotes the coercion line verbatim. Comments only. No rule, assertion, accept set or runtime behaviour moves; the quoted runtime text is unchanged and still present exactly once each. `record-validator.ts` itself is not touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x * chore(changeset): patch @objectstack/spec for the re-anchored doc block The functional-completeness module doc is emitted verbatim into `packages/spec/dist/kernel/index.d.ts`, which `files[]` publishes, so the citation text that moved ships to consumers. Half 1 of the published-text test says no (the source path is not in `files[]` and is not a `*.zod.ts`); half 2 says yes, measured on a fresh build. `@objectstack/rest` answers no on both halves — the changed file is a `.test.ts`, and its text is absent from `packages/rest/dist` while a positive control from the same file's module is present. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 07c56b5 commit 22c0279

4 files changed

Lines changed: 34 additions & 18 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`functional-completeness`'s published doc block now anchors its `record-validator.ts` citations on a SYMBOL and a verbatim snippet instead of a line number.
6+
7+
The module doc for the functional-completeness predicate is emitted verbatim into `dist/kernel/index.d.ts`, so the citations that justify its rules ship to consumers. Three of them cited `packages/objectql/src/validation/record-validator.ts` by line — `:452` for the `select`/`radio` rule and `:471` for the `multiselect` NON-rule — and the validator has moved twice since those numbers were written. Both landed hundreds of lines away, on unrelated prose inside a comment block, which is the failure mode a `path:NNN` anchor has by construction: the line it lands on still looks like plausible code, so nothing reads as broken.
8+
9+
Each citation now names the enclosing function `validateOne` and quotes the runtime text it relies on — `allowed.length > 0 && !allowed.includes(String(value))` for the empty-option-list gate, `// free-form (tags without options)` for the NON-rule. A snippet anchor cannot rot silently the way a line number does: it either still matches the file or it does not.
10+
11+
No rule, severity, accept set or exported symbol changes, and the pinned NON-rule (`multiselect` without `options` is deliberately not flagged) is untouched — the runtime text it quotes is unchanged and still present. Documentation only.

packages/rest/src/rest-hook-script-fault-envelope.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@
4242
//
4343
// The card's accept bar says `{"title": 12345}` must JOIN its neighbours —
4444
// `{}` → `400 VALIDATION_FAILED` with `fields[]`. It cannot, and asserting that
45-
// it does would pin a false statement. `record-validator.ts:503-504` accepts a
46-
// number in a `text` field:
45+
// it does would pin a false statement. `record-validator.ts`'s `validateOne`
46+
// accepts a number in a `text` field: its bounded-string branch is entered by
47+
// set membership (`BOUNDED_STRING_FIELD_TYPES.has(t)`, and `text` is a member),
48+
// and the first thing it does is coerce, verbatim:
4749
//
48-
// if (t === 'text' || …) { const s = typeof value === 'string' ? value : String(value); … }
50+
// const s = typeof value === 'string' ? value : String(value);
4951
//
5052
// — the value is COERCED, every length/format check runs against `"12345"`, and
5153
// the branch returns `null`. The shape guard above it (`invalid_value_shape`)
@@ -398,8 +400,8 @@ describe('[#7543] the control table from the report, guarded as one family', ()
398400
const r = mapDataError(REPORTED(), 'showcase_task');
399401

400402
// It does NOT join the 400 VALIDATION_FAILED family, and pinning that it
401-
// does not is the honest half: `record-validator.ts:503-504` COERCES a
402-
// number in a `text` field via `String(value)`, so this request breaks
403+
// does not is the honest half: `record-validator.ts`'s `validateOne`
404+
// COERCES a number in a `text` field via `String(value)`, so this breaks
403405
// no declared contract and names no offending field. What was wrong was
404406
// the raw fault text and the missing `code`; both are fixed.
405407
expect(r.body.code).toBe('INTERNAL_ERROR');

packages/spec/src/kernel/functional-completeness.test.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
* completeness gate that cannot fail on a known-inert instance is the
1010
* hollow-probe defect reproduced in the instrument built against it.
1111
* 2. The deliberate NON-rules are pinned as hard as the rules. `multiselect`
12-
* without options is runtime-blessed free-form (`record-validator.ts:471`,
13-
* verbatim: "free-form (tags without options)") — if someone "completes"
14-
* this module by flagging it, that is a false prescription, and this test
15-
* is where the attempt fails first.
12+
* without options is runtime-blessed free-form (`record-validator.ts`'s
13+
* `validateOne`, verbatim: "free-form (tags without options)") — if someone
14+
* "completes" this module by flagging it, that is a false prescription, and
15+
* this test is where the attempt fails first.
1616
*/
1717

1818
import { describe, expect, it } from 'vitest';
@@ -100,7 +100,8 @@ describe('checkFieldCompleteness — the verified inert shapes go red', () => {
100100
});
101101

102102
it('does NOT flag multiselect without options — the pinned NON-rule', () => {
103-
// record-validator.ts:471, verbatim: "free-form (tags without options)".
103+
// record-validator.ts's `validateOne`, verbatim:
104+
// "free-form (tags without options)".
104105
// The runtime blesses this as a mode; flagging it would be a false
105106
// prescription. If product direction ever changes, change the runtime
106107
// first — this pin makes the lint follow the code, never lead it.

packages/spec/src/kernel/functional-completeness.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@
3535
* - `lookup`/`master_detail` w/o `reference` → `objectql/engine.ts:3191`
3636
* `$expand` `if (!referenceObject) continue;` — the relationship silently
3737
* never resolves, and the record picker has no target to search.
38-
* - `select`/`radio` w/o `options` → `record-validator.ts:452`
39-
* `allowed.length > 0 && …`: an empty option list disables server-side
40-
* value validation entirely, while the form control offers nothing to pick.
41-
* - **NON-rule:** `multiselect` w/o `options` — `record-validator.ts:471`
42-
* says, verbatim, `// free-form (tags without options)`. The runtime
43-
* blesses it as a deliberate mode, which makes it ADR-0078 case (3)
38+
* - `select`/`radio` w/o `options` → `record-validator.ts`'s `validateOne`,
39+
* verbatim `allowed.length > 0 && !allowed.includes(String(value))`: an
40+
* empty option list disables server-side value validation entirely, while
41+
* the form control offers nothing to pick.
42+
* - **NON-rule:** `multiselect` w/o `options` — `record-validator.ts`'s
43+
* `validateOne` says, verbatim, `// free-form (tags without options)`. The
44+
* runtime blesses it as a deliberate mode, which makes it ADR-0078 case (3)
4445
* "genuinely optional", not an omission. Flagging it would be this
4546
* campaign's own false-prescription mistake again.
4647
* - `checkboxes` w/o `options` sits between the two: it shares the multi
@@ -105,8 +106,9 @@ const hasEntries = (v: unknown): boolean => Array.isArray(v) && v.length > 0;
105106

106107
/**
107108
* Field types whose single-choice control is dead without `options`
108-
* (`record-validator.ts:452` skips validation on an empty list, and the form
109-
* control has nothing to offer). `multiselect` is deliberately absent — see
109+
* (`record-validator.ts`'s `validateOne` skips validation on an empty list —
110+
* verbatim `allowed.length > 0 && !allowed.includes(String(value))` — and the
111+
* form control has nothing to offer). `multiselect` is deliberately absent — see
110112
* the NON-rule note in the module doc.
111113
*/
112114
const DEAD_WITHOUT_OPTIONS_ERROR: ReadonlySet<string> = new Set(['select', 'radio']);

0 commit comments

Comments
 (0)