Repository navigation
fix(plugin-view,plugin-list): stop writing the retired groupField onto the generated object-kanban node - #8191
Conversation
…o the generated object-kanban node Both kanban adapters emitted `groupField` alongside `groupBy` on the `object-kanban` node they generate. The renderer never read it: zero `groupField` hits under `packages/plugin-kanban/`, against a control of thirteen `schema.groupBy` reads in `ObjectKanban.tsx` from the same query. objectui#7322 retired the key on that node on both published faces (`groupField?: never` on the TS interface, `retirementTombstone()` in the zod mirror), so the adapters were producers emitting a node their own contract refuses by name — inert only because a generated node never reaches `safeValidateSchema`, which the CLI's `os check` / `os validate` do run. The two tests that pinned the write are TURNED, not deleted: they now assert the key is absent, so restoring the write reddens them. Node-local: every VIEW-LEVEL `groupField` alias read stays untouched and live. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract review — PASS. Clause-② does not bind; I checked the reasoning rather than the conclusion. Held on CI only.PM seat, session You invited an overrule on the Clause-② call. I am not overruling it — I re-measured the three legs it rests on: And the substantive half of your argument is the part that matters: the generated artifact moves from "would be rejected by ⭐ The anchor drift — you prevented a real regression, and I verified itThis is the most valuable thing in the run, so it is worth stating precisely. Triage's literal instruction was "delete — the second line of a live dev-mode warning array, not the write. The actual write is at So following the anchor rather than the matched text would have corrupted a live warning list AND left the duplicate write in place — a regression plus a no-op fix, which is the worst pairing available. Editing against matched text and never a line number is what avoided it. This is the fourth time this session a stale anchor has misdirected a dispatch ( ⭐ The third guard, and the hole it reveals in
|
Fixes #7773
Both kanban adapters stop writing the retired
groupFieldonto theobject-kanbannode they generate.groupBy— the key the renderer actually reads — is unchanged and is now the only lane key emitted. The two tests that pinned the duplicate write are turned, not deleted: they now assert the key is absent.Anchors: measured vs claimed
The card was measured on
4dfdcc3cand triaged on0558e0f; this branch's base isa915064e.plugin-viewdrifted again since triage, and in the worst possible way:a915064eplugin-view/src/ObjectView.tsxwrite:1279-1284:1283:1362— drifted +79plugin-view/.../ObjectView.kanbanConditionalFormatting.test.tsxpin:165:165:171— drifted +6plugin-list/src/ListView.tsxwrite:2412-2416:2500:2500— matches triageplugin-list/src/__tests__/ListView.test.tsxpin:2795:2804:2804— matches triageplugin-view/src/ObjectView.tsxlive alias read:1234:1234:1313ObjectView.tsx:1283". Ona915064eline 1283 is theflatKeysarray — a live alias-read site. Following the anchor rather than the text would have deleted a live read and left the duplicate write in place. Every edit here was made against matched text, never a line number.Zone 2 — the mechanism claims, measured
groupFieldis genuinely retired, and the validator refuses it by name. Both halves are on disk:packages/types/src/objectql.ts:2790groupField?: never;andpackages/types/src/zod/objectql.zod.ts:1002groupField: retirementTombstone('RETIRED (objectui#7322) …'), whereretirementTombstoneisz.never({ error }).optional().describe(...)(zod/tombstone.zod.ts). The refusal is exercised bypackages/types/src/__tests__/object-kanban-group-by-limit-7322.test.ts, which asserts agroupField-authored node is refused atgroupFieldby bothObjectKanbanSchema.safeParseandsafeValidateSchema. Not merely undocumented — retired, on both faces, with a live refusal test.plugin-view/src/ObjectView.tsx:1362groupField: groupBy,andplugin-list/src/ListView.tsx:2500groupField: laneField,, each inside thereturn { type: 'object-kanban', ...baseProps, … }literal.toMatchSnapshot/toMatchInlineSnapshot, and no whole-nodetoEqual/toMatchObjecton anobject-kanbannode, so no third pin could hold the key without naming it. A third, different guard does read these two files —object-kanban-group-by-limit-7322.test.ts's off-diskVIEW_LEVEL_ALIAS_SITESledger, which asserts the live view-level alias reads are still present. It is untouched by this diff and stays green (run below). Worth flagging because it makespackages/typesan affected package thatturbo ls --affectedcannot see: the dependency is a file read, not an import.git grep -n "groupField" -- packages/plugin-kanbanreturns zero hits (exit 1). Same grep shape, same path, control key:git grep -n "schema\.groupBy" -- packages/plugin-kanban/src/ObjectKanban.tsxreturns 13 matching lines, printed rather than counted (:701 :702 :713 :725 :726 :731 :740 :741 :749 :847 :937 :965 :995). The control fires, so the zero is a reading. Checked for omission-shaped reads as well: noschema[dynamic access anywhere inObjectKanban.tsx. The one...schemaspread (:757) only forwards the node intoeffectiveSchema; it never reads the key.retirementTombstone()+?: neverconvention already carriesgroupFieldon this node, landed by objectui#7322. This PR references it rather than adding one — it is the producer half catching up to a declaration that already exists.What changed
plugin-view/src/ObjectView.tsx— dropgroupField: groupBy,from the generated node.plugin-list/src/ListView.tsx— dropgroupField: laneField,from the generated node.toBeUndefined(), kept in place with a note saying what they used to assert and why they were inverted.minorfor both packages).⛔ Not touched, deliberately: every view-level
groupFieldalias read (ObjectView.tsx:1283flatKeys,:1313,:1323;ListView.tsx:1536,:1890,:2165,:2494,:2495), theKanbanConfig.groupFielddeclaration in the zod mirror,normalize-list-view.ts's alias fold, the view-level alias behaviour tests,plugin-list/README.md:126/:186(both view-level config examples), and both CHANGELOGs.Docs (objectui#7070)
No doc restates the removed behaviour. The only
object-kanbannode authoring page,content/docs/plugins/plugin-kanban.mdx, already authorsgroupByand is already pinned by objectui#7322's test to contain nogroupField. The twoplugin-list/README.mdhits are view-level config, which is unchanged.Contract review — clause ② considered, and judged not to apply
Reported rather than assumed, because removing an emitted key is exactly where "removal is always safe" goes wrong:
packages/typesis untouched; no schema, no validator, no accept set moves.index.tsor anexport *.safeValidateSchema" to "accepted". The validator did not move; the producer stopped emitting a key the validator already refused. Nothing that validated green now validates red.object-kanbancomponent and readsprops.schema.groupFieldoff the generated node: it now readsundefined. That is graded in the changeset asminor, and it is a read of a key the published type has declaredneversince objectui#7322.If a reviewer reads the produced-artifact accept/reject shift as clause-② territory,
needs:contract-reviewis the right label and I will attach it on request — I judged it out, and am saying so rather than staying quiet.Verification
All at
7644616c6.Ablation — mutate, prove on disk, prove the pins redden, restore, prove the restore BY STATE. Full record in the report comment on #7773.
3 test files / 183 tests passed.grep -c'groupField: groupBy,'0 → 1,'groupField: laneField,'0 → 1;git diff HEAD --stat=2 files changed, 2 insertions(+). An editor's exit code is not proof — the counts are.exit 1,2 failed | 181 passed. Both turned pins reddened, each withAssertionError: expected 'stage' to be undefined. This is also the empirical answer to the spread question: in the green legstoBeUndefined()passes, so neither...basePropsnor...restKanbanre-introduces the key.git checkout HEAD -- PATH(never a baregit checkout --, which reads the polluted index). Proven by state:git hash-objectequalsgit rev-parse HEAD:PATHfor both files (c52b11b8…/a91b712774…), andgit diff HEADreports 0 changed files. Not by an exit code.183 tests passed.Suites. No rebuild was needed for any of this: the pins import the subject by relative path, and the root
vitest.config.mtsaliases every@object-ui/*specifier to that package'ssrc, so the whole in-repo farm sees the source change directly.vitest run packages/plugin-view/ packages/plugin-map/vitest run packages/plugin-list/vitest run …ObjectView.kanbanConditionalFormatting.test.tsx …ListView.test.tsx …object-kanban-group-by-limit-7322.test.tspnpm --filter @object-ui/plugin-view --filter @object-ui/plugin-list run type-checkScope: 2 of 47, both echoingtsc --noEmit && tsc -p tsconfig.test.jsonnode scripts/check-changeset-presence.mjsnode scripts/check-changeset-no-major.mjsmajorbump"node scripts/check-control-bytes.mjsnode scripts/check-governed-queue-guard.mjs --test(the 5 changed paths)type-checkreally covers the edited tests, measured not assumed:tsc -p tsconfig.test.json --listFileslistsListView.test.tsxandObjectView.kanbanConditionalFormatting.test.tsxin their programs.Lint — a declared narrowing, with its three readings. Run:
eslint --no-inline-config --format jsonover the 4 changed files → exit 0, 0 errors, 275 warnings, every one pre-existing (no-explicit-any,react-refresh/only-export-components) on lines this diff does not touch. ① Population: the whole monorepo is governed by a single rooteslint.config.js(find . -name 'eslint.config.*'outsidenode_modulesreturns exactly that one file). ② Count: 4, read from eslint's own--format jsonoutput, matching the 4 changed source files. ③ Invariance: type-aware linting is not enabled —grep -n "projectService\|parserOptions\|project:" eslint.config.jsexits 1 — so each file's verdict depends only on its own bytes plus the unchanged shared config, and this diff cannot move the verdict on any file it did not touch. The repo-wideturbo run lintis CI's.Declared to CI, not run locally.
turbo ls --affectedfroma915064enames 9 packages; the 3 with source adjacency to this change (plugin-view,plugin-list,plugin-map) ran in full above.app-shell(637 test files),console(89) and the three examples plussiteare left to CI. The basis is measured, not only budgetary: the repo-widegroupFieldcensus across all test files shows no assertion anywhere in those packages against a generatedobject-kanbannode — app-shell's only hits are theengine.inspector.pageBlock.field.object-kanban.groupFieldi18n key string, which belongs to the page-block designer and is untouched here.Scope
Out of scope, filed separately, nothing folded in: #7772 (the page-block designer inspector half) and #7712 (in flight in
plugin-kanban/plugin-calendar— disjoint packages; I did not reach into either). Related, and confirmed not duplicates: objectui#7322 (the declaration, landed) and objectui#2890 (the broad ListView legacy-vocabulary audit; this is one atomic write out of it, not the audit).One out-of-scope finding surfaced while measuring and is filed as its own card rather than fixed here:
packages/app-shell/src/views/ObjectView.tsx:2414still emits the deprecated view-level alias pair{ groupBy, groupField }intooptions.kanbaninstead of the spec-canonicalgroupByField, where its siblingInterfaceListPage.defaultKanbanFromObjectalready migrated and says in a comment that "one key is enough". That one is live, not inert — a different defect in a package this PR does not touch.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code