feat(plugin-kanban,plugin-calendar): declare the filter input both renderers read - #8186
Conversation
…enderers read objectui#7712 — ADR-0049 enforce-or-remove, resolved toward DECLARE. Both `ObjectKanban` and `ObjectCalendar` send an authored `filter` to the query as `$filter`, and `@objectstack/spec`'s `ComponentPropsMap` declares the key on both blocks. None of the four registrations that publish those two renderers listed it in `inputs`, so `sdui-parser`'s `validateTree` reported `unknown-prop` on the html tier for the one spelling that works — objectui#6678's shape, where a correct write draws the same diagnostic as a write that does nothing. Declared `type: 'array'` on all four, matching `object-grid`'s `GRID_QUERY_INPUTS` and `object-metric`. Hand-declared per key against the spec rather than derived from the neighbouring `ElementDataSourceMapping`: that mapping also carries kanban's `limit`, which the spec's strict props schema refuses by name, so a mechanical derivation would publish a key the save gate rejects. Pinned in both packages against a manifest built from the LIVE registry, with a genuinely-unknown prop as the control on the same call, plus the spec's own key verdict. Docs: the `filter` row the two plugin pages omitted. 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-② binds and clears. Held only on CI convergence.PM seat, session Clause-② fires on the accept/reject leg — The dev corrected my dispatch twice, and both corrections verified① Four registrations, not the six my order named. I passed along a count from an earlier seat without measuring it. Verified independently: Declaring ② The "derive inputs from the mapping" idea — which I relayed as a promising direction — was measured and correctly REJECTED. I read the mapping myself: const OBJECT_KANBAN_DATA_SOURCE: ElementDataSourceMapping = {
filter: true,
limit: 'limit', // ← the reason derivation fails
…Deriving inputs from that bag would also publish The Also right
⭐ The most valuable thing filed here is #8176The console's reverse-direction parity gate ( Grades applied: #8171
Held only on CI convergence: at review time Generated by Claude Code |
Fixes #7712
ADR-0049 enforce-or-remove on
filter, resolved toward declare. Both renderers read the key,@objectstack/specdeclares it, and only the registrations were missing it — so the registrations are what moved.What changed
Four registration
inputslists gain one entry each,{ name: 'filter', type: 'array', description: … }:packages/plugin-kanban/src/index.tsx—plugin-kanban:object-kanbanandview:kanbanpackages/plugin-calendar/src/index.tsx—plugin-calendar:object-calendarandview:calendarPlus a pin per package, the
filterrow the two plugin doc pages omitted,@object-ui/sdui-parserdeclared in both packages' devDependencies (the pins import it;tsccaught that it was undeclared,check:phantom-depsagrees), and a changeset (minoron both packages — this repo forbidsmajor).needs:contract-review(Clause-② = yes)Adding an
inputsentry changes what a published authoring tier accepts: a key rejected today stops being rejected. The label is attached and this PR stays draft — the PM lands it.The narrower reading is on the record too, because it is what the measurements support: the spec already declares
filteron both blocks, so this restores declared = enforced rather than widening past the contract.ComponentPropsMap['object-kanban']and['object-calendar']bothsafeParsean authoredfiltertosuccess: true, while the same strict schemas refuse an undeclared control key by name on the same call.Premise verification — measured vs claimed
Everything in the card, its triage and the dispatch was re-measured on
origin/main9bfd618before the first edit. The premise holds, with three corrections.schema.filterObjectKanban.tsx:363,ObjectCalendar.tsx:478(dep arrays:391/:514)safeParse, not the.d.ts: accepted on both; control key refused on the same callunknown-proptodayTAG has no prop "filter"on both kanban tagsObjectKanban.tsx:263/ObjectCalendar.tsx:454; measured 363 / 478. Register-call anchors also moved (:208 :313 :420 :434to:208 :307 :414 :427; theinputs:lines are:216 :348 :421 :434, which is what the second seat measured)The
filter: truetrap did not catch this seat. Measured on9bfd618:plugin-kanban/src/index.tsx:390,plugin-calendar/src/index.tsx:66and theplugin-grid/src/index.tsx:76that triage cited are allElementDataSourceMappingconstants, not registrationinputs. The correct control isplugin-grid/src/index.tsx:218—{ name: 'filter', type: 'array', description: … }— andplugin-dashboard/src/index.tsx:207is a second one. The four entries added here are that shape.Correction to row 6, and why it matters.
plugin-kanban/src/index.tsxregisters four tags, but only two are served byObjectKanbanRenderer.kanban-uiandkanban-enhancedare the static-column board (KanbanRendererand an inline renderer), which never readschema.filter, andComponentPropsMapcarries no row for either. Declaringfilterthere would publish a key nothing reads — this defect in the other direction. So the edit is 2 + 2 tags. The dispatch's "six registrations" is the count of registrations in the two files, not the count that reads the key.Why hand-declared, not derived from the mapping
The suggestion was to derive the
inputsentry from theElementDataSourceMappingbeside these registrations, the wayregister()derives thedataSourceinput (objectui#6678). Measured, that derivation is wrong here:OBJECT_KANBAN_DATA_SOURCEalso carrieslimit, andComponentPropsMap['object-kanban']refuseslimitby name (unrecognized_keys: ["limit"]). Emitting it would publish a key the save gate cannot store and put the manifest at odds with the manifest-to-spec parity check. So each key is declared against the spec, per key — which is what the per-key check asked for.Evidence
Reproduction (before the fix) — the kanban pin, 4 failed / 3 passed. The three that passed are the controls (a genuinely unknown prop is reported on both tags; the spec's key verdict), so the red rows were the declaration and nothing else.
After the fix — dependency-closure build exit 0; both full package suites green (
plugin-kanban26 files / 137 tests,plugin-calendar26 files / 152 tests);type-checkgreen in both packages, including each one'stsconfig.test.json, so the pins are compiled and not merely executed; the two pins re-run on the final HEAD, 2 files / 14 tests.Ablation — with the fix committed, the
object-kanbandeclaration was mutated on disk (name: 'filter'toname: 'filterZZZ', first occurrence). The mutation was proved to have landed before anything was read: anchor counts 2 to 1 and 0 to 1, plus a changedgit hash-object(e2006771…tofe9930c2…). The pin then went RED — 2 failed / 5 passed, and the two reds are exactly theobject-kanbanrows whileview:kanbanstayed green, which is what makes the pin per-tag rather than per-file. Restore was proved by state, not by an exit code:git hash-objectequal togit rev-parse HEAD:packages/plugin-kanban/src/index.tsx, an emptygit diff HEAD, and the anchors back at 2 / 0. That blob hash is still the one at this PR's HEAD, so the ablation is re-checkable against what you are reading.No build is needed for any of that to be honest: the root
vitest.config.mtsaliases@object-ui/*to each package'ssrc, so a staledistcannot make the ablation falsely green.Left to CI, declared:
check:doc-snippetsandcheck:spec-floorsboth need a whole-workspace build (spec-floorsreports 12no-artifactfindings, every one of them a package this branch never built, none about this diff), andcheck:sdui-registration-pinsweighs a built console bundle.⭐ The blind spot this does NOT close (named, as the triage asked)
check:react-blocks-declaration-parityruns manifest to spec. A key the spec declares and the manifest omits is structurally outside what it ratchets, so fixing these four registrations does not make the next omission loud.This PR adds a measurement the triage did not have: the console does carry the reverse direction (
apps/console/src/__tests__/registry-inputs-spec-parity.test.ts,undiscoverableSpecKeysat:387), and it could not see this defect either. Itscoveredset is built fromComponentRegistry.getConfig, which reads loaded registrations only (Registry.ts:649), while the console registers every plugin block withregisterLazy(register-plugins.ts:107-:129). So both blocks were unjudged and uncounted. Filed as #8176 with the one-line verification. ⛔ Not ridden on this PR.Out-of-scope findings, filed unassigned (⛔ not folded in)
object-calendarregistrations declare nosortinput whileObjectCalendarreadsschema.sortand the spec declares it — #7712's defect, one key over #8171 —object-calendaromitssorton the same two registrations: spec-declared, renderer-read (ObjectCalendar.tsx:479), the same defect one key over. The positive half of the per-key measurement.ObjectKanbanSchemateachlimiton anobject-kanban, the renderer reads it as$top— and the spec's strictComponentPropsMaprefuses it by name #8172 — kanban'slimit: the docs (a whole section atplugin-kanban.mdx:151),ObjectKanbanSchemaand the renderer all say yes; the spec's strict props schema refuses it by name. A contract decision, not a renderer patch. The negative half.ObjectKanbanSchemaandObjectCalendarSchemadeclare nofilter(and nosort) — the fourth face of the key #7712 declares everywhere else #8174 —@object-ui/typesdeclares neitherfilternorsortonObjectKanbanSchema/ObjectCalendarSchema— the fourth declaration face, bounded honestly against finding(types):BaseSchemacloses with[key: string]: any(packages/types/src/base.ts:467), so NO annotation on any node schema can catch a misspelled metadata key — measured green by a planted probe while the type, optionality and payload-member probes all went red #7927.registry-inputs-spec-parity's reverse direction judges only EAGERLY registered blocks, so every lazily-registeredobject-*plugin block sits outside it — the structural reason #7712 was invisible to both ratchets #8176 — the console parity gate's lazy-registration blind spot, above.Name-collision check
This change exports no new name: it adds object literals inside existing
inputsarrays plus two test files.filterhere is an input name in a registration — the same keyobject-grid,object-metric,record:related_listandplugin-listalready publish — not an exported symbol, so there is no second authority to collide with,export *re-exports included.🤖 Generated with Claude Code
https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
Generated by Claude Code