Skip to content

Commit d2f19cb

Browse files
os-justinclaude
andauthored
test(plugin-view,plugin-list,plugin-calendar): drop the retired target key from three tree fixtures (objectui#8031) (#9818)
Fixes #8031 Test-only. Three `type: 'tree'` fixture fields drop the retired snake_case target key — in `plugin-view`'s expand-gate suite, `plugin-list`'s relational-sort suite and `plugin-calendar`'s expand-gate suite — plus one empty-frontmatter changeset declaring that nothing releases. No renderer, no `detectParentField`, no sibling field touched. ## Every suite's verdict is unchanged, measured both ways Same command before and after the edit, from the repo root, through the shared serialisation lock: ``` pnpm exec vitest run --reporter=verbose \ packages/plugin-view/src/__tests__/ObjectView.expandGate.test.tsx \ packages/plugin-list/src/__tests__/ListView.relationalSort.test.tsx \ packages/plugin-calendar/src/__tests__/ObjectCalendar.expandGate-6453.test.tsx ``` | reading | before | after | |---|---|---| | summary | `Test Files 3 passed (3)` · `Tests 23 passed (23)` | identical | | per file | plugin-view 8 · plugin-list 6 · plugin-calendar 9 | identical | | test-name set | — | `diff` of the two sorted verbose logs is EMPTY | `turbo run type-check` for the three packages (each runs `tsc --noEmit` and then `tsc -p tsconfig.test.json`, so the edited test programs are in it): 19 tasks successful, 19 total. `check-changeset-presence`, `check-control-bytes`, `check-new-cross-file-line-citations`, `check-test-path-roots` and `check-governed-queue-guard --test` all exit 0; the last prints `NOT GOVERNED — 4 path(s) checked`. ## Why DROP and not rename — re-measured on the installed copy `@objectstack/spec@17.4.0`, `FieldSchema.safeParse`, with a nonsense key as the negative control: | input on a `tree` field | verdict | |---|---| | no target key (what this PR leaves) | ACCEPT | | retired snake_case target key | REFUSE — `unrecognized_keys` **plus** a rename hint | | nonsense key `zz_nonsense_key` | REFUSE — `unrecognized_keys`, **no** hint | | declared `reference`, this object's own name | ACCEPT | | declared `reference`, another object's name | ACCEPT | The hint is attached to a refusal, not to an acceptance — the card's two-directional measurement reproduces at 17.4.0. On the read side `detectParentField` has read only the declared spelling since objectui#6837 half 2, and the list sort picker's object-def branch reads only that spelling too. So the key was refused by the schema and read by nobody, and a refused key annotates nothing: renaming it would turn a refusal into an ACCEPTED self-annotation these fixtures never made. ## Three premises corrected — read this before reusing the card's framing 1. ⚠️ **These three are NOT foreign-target.** Each container declares an object identity and the retired key already named exactly it: `TASK_SCHEMA` is `task` and the field named `task`; the relational-sort suite's object def is `contacts` and the field named `contacts`; `VISIT_SCHEMA` is `visit` and the field named `visit`. What made these three refused is the SPELLING, not the target. The card's title, its "whose target names another object" framing, and its disposition clause "none of these three containers declares an object identity a self-reference could name" are all false of these three. The disposition survives the correction; the classification does not. 2. ⚠️ **The seven instances are therefore not indistinguishable.** objectui#7839's four were target-foreign or identity-less (three of them declare no object at all; the fourth declares `deal` and named `deals`). These three are target-self. What unites all seven is an inert, parse-refused target key on a `tree` field, which is what the shared disposition rests on. 3. ⚠️ **"A shape the spec refuses at parse" is true upstream, not of the installed pin.** Row five above: a declared `reference` naming another object is ACCEPTED at 17.4.0. `refuseForeignTreeReference` landed on objectstack `main` after this pin was cut, exactly as objectui#7839's own suite docblock records. A fourth correction is the card's line number for the `plugin-view` fixture, which the dispatching seat had already caught: every address was re-derived on today's `main`. ## The shape scan was re-run, and it found a fourth emit — reported, not touched Re-run as a shape scan rather than a key grep: every `type: 'tree'` emit in the tree (single- and double-quoted, template literals, JSON spellings, every file extension, constants holding the string, and field factories that take the type as a parameter). 31 literal emits. Of those, three are these fixtures; two carry a declared target that names the DECLARING object (legal self-references in `plugin-tree`); the deliberate foreign arms in objectui#7839's own suite are that suite's subject and must stay; the rest carry no target or are view entries rather than field defs. ⭐ One emit is invisible to BOTH sweeps and is a live instance of the card's own lesson: `packages/app-shell/src/utils/expandableFamily.identity-5874.test.ts` builds its fields through a factory that takes the type as an argument and attaches `reference: 'accounts'` to every one of them, including the `tree` member `parent_node` on an object named `deal`. A grep for the key sees a helper with no tree in sight; a grep for the emit sees no literal type at all. It is NOT in this PR's scope and NOT the same disposition — the target there is load-bearing (`expect(out.referenceTo).toBe('accounts')` reads it), so dropping it would go red. It is handed back for triage in the report, with dedupe words. ## The pin — a truthful "not soundly pinnable here" The population-shaped gate the card asks for ("no `type: 'tree'` emit carries a target naming another object") is writable but not soundly: I have a counterexample in hand. A gate keyed on literal emits would go green on the app-shell factory above, and that green would read as "the population is closed" — the exact false closure the card's own lesson names, repeated one level up. Judging "names another object" also needs the declaring object's identity, which in a hand-built fixture is an arbitrary neighbouring constant a scanner would have to guess at. The sound route is a type-level one — type these fixtures against the spec's own field type, so the compiler refuses the retired key and the tree arm — and that is a separate change, not something to smuggle in under a test-only card. ⛔ A pin that re-asserted these suites' existing assertions would measure nothing, so none was added. ## Acceptance notes - **To file (handed back, not filed here — this seat does not open cards):** the app-shell factory emit above. Dedupe words: `expandableFamily.identity-5874`, `field('tree')` factory, foreign tree target, `resolveActionParams` `referenceTo`, tree-emit enumeration blind spot. - **Noted, not filed:** at the parse door the retired spelling is refused on a `lookup` too (measured), so "dead on a `tree` field only" is about the READERS, not about `FieldSchema` — several app-shell readers still read the snake spelling for the lookup family, which is why the sibling lines are legitimately load-bearing and untouched. Carrier: the next card that moves the lookup-family spelling reads this; no PR is pending on it today. - `Clause-②: no` — three hand-built test fixtures. No symbol exported, removed, renamed or retyped, no published `.d.ts` member and no accept set moves. Measured, not inferred from a diff grep: the three files sit under `src/__tests__/`, which every published tsconfig excludes from the emitting program (enforced by `pnpm check:published-tsconfig-exclude`); each container is a module-local `const`; all three packages ship `dist` only; the diff adds zero `export` lines and deletes one property from an object literal whose own annotation already declares that key optional. --- _Generated by [Claude Code](https://claude.ai/code/session_012EpHzwH4wTy5sd7ibkD2yq)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent b0e5660 commit d2f19cb

4 files changed

Lines changed: 68 additions & 3 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
---
3+
4+
Test-only. Drops the retired snake_case target key from the three `type: 'tree'`
5+
fixture fields that objectui#7839's `reference:`-keyed sweep could not see — in
6+
`plugin-view`'s expand-gate suite, `plugin-list`'s relational-sort suite and
7+
`plugin-calendar`'s expand-gate suite. Nothing published moves, so this declares
8+
no release: the three files sit under `src/__tests__/`, which every published
9+
tsconfig excludes from the emitting program (`pnpm check:published-tsconfig-exclude`),
10+
each container is a module-local `const`, and all three packages ship `dist` only.
11+
12+
`FieldSchema` refuses that spelling BY NAME. Re-measured on the installed
13+
`@objectstack/spec@17.4.0` with a nonsense key as the negative control: the
14+
retired spelling draws `unrecognized_keys` **plus** a rename hint, the nonsense
15+
key draws the same refusal with **no** hint — so the hint is attached to a
16+
refusal, not to an acceptance. On a `tree` field the renderer side has read only
17+
the declared `reference` since objectui#6837 half 2, and the list sort picker's
18+
object-def branch reads only that spelling too. So the key was refused by the
19+
schema and read by nobody; every rule under test reads the declared TYPE. All
20+
three suites' verdicts are unchanged — 3 files, 23 tests, identical test-name
21+
sets before and after.
22+
23+
⚠️ One correction to the framing these three were filed under, measured on the
24+
fixtures themselves rather than inherited: they are NOT foreign-target. Each
25+
container declares an object identity and the retired key already named exactly
26+
it — `TASK_SCHEMA` is `task` and the field named `task`, the `objectDef` under
27+
test in the relational-sort suite is `contacts` and the field named `contacts`,
28+
`VISIT_SCHEMA` is `visit` and the field named `visit`. What made these three
29+
refused is the SPELLING, not the target; the foreign-target reading belongs to
30+
objectui#7839's four, whose containers either declare no identity at all or
31+
named a different one.
32+
33+
The key is still DROPPED rather than renamed, and the correction is why that
34+
matters: a refused key annotates nothing, so renaming it would turn a refusal
35+
into an ACCEPTED self-annotation these fixtures never made. Measured on the same
36+
installed copy, a `tree` field with no target and one that self-annotates are
37+
both accepted, and a FOREIGN target is accepted too — `refuseForeignTreeReference`
38+
landed upstream after this pin, so the canonical spelling would be a live claim
39+
none of these suites needs.
40+
41+
`Clause-②: no` — three hand-built test fixtures. No symbol exported, removed,
42+
renamed or retyped; no published `.d.ts` member and no accept set moves. The
43+
diff deletes one property from a module-local object literal and adds comments;
44+
the containers' own type annotation already declares that key optional and is
45+
untouched, as is every sibling field that legitimately carries it.

‎packages/plugin-calendar/src/__tests__/ObjectCalendar.expandGate-6453.test.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ const VISIT_FIELDS: Record<string, { type: string; label: string; reference_to?:
141141
amount: { type: 'currency', label: 'Amount' },
142142
owner: { type: 'user', label: 'Owner' },
143143
account: { type: 'lookup', label: 'Account', reference_to: 'account' },
144-
parent_visit: { type: 'tree', label: 'Parent', reference_to: 'visit' },
144+
// No target key. It carried the retired snake_case spelling, which
145+
// `FieldSchema` refuses BY NAME, so the line annotated nothing and the
146+
// `$expand` expectation is derived from the declared TYPE. On a `tree` the
147+
// target is optional; this map's object is `visit` and the value named it,
148+
// so renaming would turn a refused key into an accepted self-annotation
149+
// this fixture never made (objectui#8031).
150+
parent_visit: { type: 'tree', label: 'Parent' },
145151
line_item: { type: 'master_detail', label: 'Line item', reference_to: 'line_item' },
146152
};
147153

‎packages/plugin-list/src/__tests__/ListView.relationalSort.test.tsx‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,14 @@ const objectDef = {
3636
owner: { type: 'lookup', label: 'Owner', reference_to: 'sys_user' },
3737
account: { type: 'master_detail', label: 'Account', reference_to: 'accounts' },
3838
assignee: { type: 'user', label: 'Assignee', reference_to: 'sys_user' },
39-
parent: { type: 'tree', label: 'Parent', reference_to: 'contacts' },
39+
// No target key. It carried the retired snake_case spelling, which
40+
// `FieldSchema` refuses BY NAME, and the sort picker's object-def branch
41+
// reads only the declared `reference` (objectui#6837 half 2) — so the
42+
// line annotated nothing and was read by nothing. On a `tree` the target
43+
// is optional; this object is `contacts` and the value named it, so
44+
// renaming would turn a refused key into an accepted self-annotation this
45+
// fixture never made (objectui#8031).
46+
parent: { type: 'tree', label: 'Parent' },
4047
// The picker's SECOND withholding rule (#4243, and the shared judgement as
4148
// of objectui#3950): computed on read, no column to order by. Present in
4249
// this fixture so the exact option lists below pin its absence rather than

‎packages/plugin-view/src/__tests__/ObjectView.expandGate.test.tsx‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,14 @@ const TASK_FIELDS: Record<string, { type: string; label: string; reference_to?:
122122
due_date: { type: 'date', label: 'Due' },
123123
owner: { type: 'user', label: 'Owner' },
124124
account: { type: 'lookup', label: 'Account', reference_to: 'account' },
125-
parent_task: { type: 'tree', label: 'Parent', reference_to: 'task' },
125+
// No target key. It carried the retired snake_case spelling, which
126+
// `FieldSchema` refuses BY NAME (measured: `unrecognized_keys` plus a
127+
// rename hint, where a nonsense key draws the same refusal without one), so
128+
// the line annotated nothing and the `$expand` expectation is derived from
129+
// the declared TYPE. On a `tree` the target is optional; this map's object
130+
// is `task` and the value named it, so renaming would turn a refused key
131+
// into an accepted self-annotation this fixture never made (objectui#8031).
132+
parent_task: { type: 'tree', label: 'Parent' },
126133
line_item: { type: 'master_detail', label: 'Line item', reference_to: 'line_item' },
127134
};
128135

0 commit comments

Comments
 (0)