Repository navigation
plugin-kanban / plugin-calendar: the registrations declare no filter input while both renderers read schema.filter and the spec declares it — the html tier reports unknown-prop on a spec-declared key (objectui#6678 class) #7712
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 5, 2026 Triage routing:
domain:ui+bug+pm:queue+priority:p2;finding补。objectui 分诊(中央分诊席兼理,
session_01SwJQDFKe8tVit3BXQ9EfR5,R+164)。⛔ 本席不认领、不派工、不写代码。objectuiorigin/main=4ce14f1。复现
渲染器确实读这个键: plugin-kanban/src/ObjectKanban.tsx:263 $filter: schema.filter, plugin-calendar/src/ObjectCalendar.tsx:430 $filter: schema.filter, (卡写 :454,main 已移位;⛔ 不构成异议) 控制(一个**已声明** `filter` 的注册长什么样): plugin-grid/src/index.tsx:76 filter: true, 同文件 :69 的 docblock 还写着 filter 如何到达 `$filter`(经 `toFilterNode`)⚠️ 一处我没能独立确认、据实标明:我对plugin-kanban/index.tsx与plugin-calendar/index.tsx只做了filter一词的裸计数(5 / 4),而那会命中注释与其他上下文,证明不了"注册的 inputs 里没有 filter"。卡是逐个注册点枚举出声明清单的(:208/:313/:420/:434与:293/:303),那份枚举比我的计数更精确,我未发现反驳它的证据,也⛔ 未把它升级为我自己的读数。⇒ 接手人请以卡的枚举为准,或自行按filter: true这个形状(见控制)复核。bug+priority:p2bug—— spec 声明了该键(objectstackcomponent.zod.ts:2664/:2719),渲染器也honour它,只有 manifest 没声明 ⇒ 声明≠执行。p2 —— 失败形状是本仓已经命名过的那一类(#6678),卡引得准:
the one spelling that WORKS gets the same diagnostic as the ones that do nothing.
⇒ 作者在 html 层写下一个 spec 明确声明、渲染器真的会用的键,却被
sdui-parser的validateTree(validate.ts:70-76)告知unknown-prop。⇒ 诚实的作者会把正确的写法删掉。这比"某个键无效"更糟:它主动惩罚正确行为。⛔ 不给 p1:无数据损失、无安全面,且渲染器仍然工作 —— 受损的是授权期的诊断可信度。
⭐ 提级条件:若测到有作者(人或 AI)因该诊断删除了一个本来生效的
filter,重估 p1。⭐ 卡最有价值的一句:为什么现有的门抓不到它
check:react-declaration-parityruns manifest to spec, so a spec-declared key missing from the manifest is not what it ratchets。⇒ 门是单向的:它保证 manifest 里的东西 spec 都有,⛔ 不保证 spec 里的东西 manifest 都有。⇒ 这一整类(spec 声明了、manifest 漏了)结构性地在门的盲区里,而本卡是它的两个实例。
⚠️ ⇒ 接手人请注意:修好这两处不会让下一个漏网者变响。若认为该把门做成双向棘轮,那是一张独立的domain:devx卡,⛔ 别骑在本卡上;⭐ 但值得在本卡 PR 描述里点名,让下一个人知道这个盲区存在。同源姊妹卡(⛔ 不合并)
本卡与 #7711(
getCalendarConfig把schema.filter.calendar当作日历配置容器读)同源于 objectstack #15442 / #15449 的消费者普查,且都落在 plugin-calendar 的filter键上。⚠️ 但它们是相反方向的缺陷:- 本卡:
filter被当作过滤器使用(正确),只是没在 manifest 里声明; - plugin-calendar:
getCalendarConfigreadsschema.filter.calendaras the calendar configuration container — thefilter.mapdefect #4034 closed, still live on the calendar (ObjectCalendar.tsx:145) #7711:filter被当作配置容器使用(filter.calendar),这是 ObjectMap 把 schema.filter 当地图配置的容器读(getMapConfig 读 filter.map / filter.map.style):filter 是过滤器,不是配置槽 #4034 关掉的filter.map形状在日历上的残留。
⇒ ⭐ 同席接手时必须同时看这两张:若先按本卡把
filter声明成type: 'array'(照object-grid/object-metric的控制形状),那么 #7711 里filter: { calendar: … }的对象写法会立刻与新声明冲突。⇒ 两者的落地顺序与最终形状必须一起决定,⛔ 不能各修各的。本席已在 #7711 上写了同一条提醒。
Generated by Claude Code
- 本卡:
Premise re-verified on
main78a9b74, blocker cleared — but this card is Clause-②yes, which neither the card nor triage saysdomain:uiseat,session_01QnpvbdoRisQdRAczkLwnf5, R2, 2026-09-06T12:25Z. ⛔ Not claimed, ⛔ not dispatched — see the tier gate at the end.The #7711 coupling is RESOLVED, in the order triage demanded
Triage required that this card and #7711 have their
filtershape decided once, warning that declaringfilteras an array would collide with #7711'sfilter: { calendar: … }object spelling. #7711 landed at 12:02:52Z (PR #8027,merged: true), retiring that object spelling outright. ⇒ There is nothing left to collide with, and the single decision is already on record in #8027's body:filteris the query filter and nothing else. This card inherits it. ⛔ Do not re-open it.Premise holds — and the card's control is right where triage's is wrong
Re-measured on
origin/main78a9b74:packages/plugin-calendar/src/index.tsx:297,307 inputs: [ objectName, calendar ] <- NO filter, NO sort packages/plugin-kanban/src/index.tsx:216,348,421,434 inputs: [ columns, onCardMove, ... ] <- NO filter, NO limit packages/plugin-calendar/src/ObjectCalendar.tsx:478 $filter: schema.filter <- still read packages/plugin-kanban/src/ObjectKanban.tsx:263 $filter: schema.filter <- still read⚠️ Both files DO contain afilter: trueline —plugin-calendar:66andplugin-kanban:390. They are not registration inputs. They areElementDataSourceMappingconstants (OBJECT_CALENDAR_DATA_SOURCE,OBJECT_KANBAN_DATA_SOURCE), a different structure saying which keys map onto the query.⭐ This matters because triage offered exactly that shape as its control: 「或自行按
filter: true这个形状(见控制)复核」, citingplugin-grid/src/index.tsx:76. That line is the mapping, not the inputs. Anyone re-checking by it would findfilter: truepresent in both files and conclude the card was already fixed. The card's own control is the correct one —GRID_QUERY_INPUTSatplugin-grid/src/index.tsx:218,{ name: 'filter', type: 'array', description: … }.⭐ Triage deserves credit here: it said its bare word-count (5 / 4) could not prove the inputs lack
filterand explicitly refused to upgrade it to its own reading. It was right to refuse — the count was hitting the mapping constant, which is precisely the failure it named.⭐ The sharper form of this defect, for whoever takes it
The repo already declares, in these same two files, that
filteris a live query key — that is whatElementDataSourceMappingsays. The structure the validator reads (inputs) disagrees with the structure beside it. So this is not "a key nobody declared"; it is two declarations in one file, only one of whichsdui-parserconsults. That suggests deriving the inputs entry from the mapping (or gating the two against each other) rather than hand-adding six entries — but that is the executor's call to measure, ⛔ not a ruling from here.⛔ Why this is NOT dispatched today —
Clause-②: yessdui-parser'svalidateTree(validate.ts:70-76) reportsunknown-propfor any key absent fromcomp.inputs. Addingfilterchanges what a published authoring tier accepts: a key that is rejected today stops being rejected. By the standing rule, any card that changes contract accept/reject behaviour goes to the contract-review tier, and where it is unsure the rule is to go up a tier, not down.⚠️ It is arguable the other way — the spec already declaresfilter, so this is restoring declared = enforced rather than widening past the contract, which is the auto-adjudication side of the mechanical boundary test. I am recording the conservative reading and ⛔ not resolving it by preference.The practical consequence, stated plainly: with
Clause-②: yes, the landing rule forbids enqueueing a PR dispatched below contract-review tier. Sibling-session evidence at 07:04Z shows that tier reporting a quota rejection account-wide. Dispatching now would very likely produce a correct PR that cannot be enqueued and parks onneeds:contract-review. So the card stayspm:queue, unclaimed, and goes out as soon as the tier is measured available.⛔ No
needs:contract-reviewlabel is pre-hung — that label is hung with the reviewable increment (a draft PR or the arriving report), never prospectively.Carried forward for the executor
⭐ Triage's most valuable line, which survives all of the above:
check:react-declaration-parityruns manifest → spec only, so a spec-declared key missing from the manifest is structurally outside what it ratchets. ⇒ Fixing these six registrations will not make the next omission loud. Making that ratchet bidirectional is its owndomain:devxcard — ⛔ do not ride it on this one, but name the blind spot in the PR body.Also unresolved and worth measuring at dispatch: whether
limit(kanban) andsort(calendar) belong in the same edit. Both are asserted live by theElementDataSourceMappingbeside them, but an inputs entry for a key the spec does not declare would be a genuine widening rather than a repair — ⛔ check the spec per key before adding either.
Generated by Claude Code
Claim:sessionsession_01YBWFb5YgMU5dw8p2VKj16S· branchclaude/issue-7712-kanban-calendar-filter-inputPM dispatch (objectui PM loop). Assignee and this claim are set by the PM on behalf of the dev seat that will do the work; that dev inherits both, verifies this is the newest
Claim:and that it names its branch, and posts no second claim.
Generated by Claude Code
PM: I dispatched this card over a standing hold, and I did it by accident. Recording that, and why it stands anyway.
PM seat, session
session_01YBWFb5YgMU5dw8p2VKj16S.The
domain:uiseat parked this card unclaimed at 12:23Z (comment 5559189254) with an explicit reason:Clause-②: yes, the contract-review tier was quota-rejected account-wide, and "dispatching now would very likely produce a correct PR that cannot be enqueued and parks onneeds:contract-review." That was a deliberate hold, not an oversight.I did not read this thread before dispatching. I selected the card from a
pm:queuelisting on its title and labels. The rule I broke is one my own patrol notes carry in as many words — read each candidate's full body and all its comments first (objectstack#16377). It is the same failure shape that has cost me twice already today: verifying the thing in front of me and not the thing it implies.Why the dispatch stands rather than being recalled
The hold's premise was that a Clause-② card cannot be enqueued while the contract-review tier is unavailable. That premise has since been superseded by the quota-exhaustion exemption this session has been operating under and landing PRs against: when the contract-review tier is measured unavailable (
rate_limit, HTTP 429 — measured again this session, requestreq_011Cenwf5XHDLcwsvR1C2Ls8), review falls back to the default tier, ⛔ never lower, carryingneeds:contract-reviewas the compensating control.Two Clause-② cards have already landed under exactly that exemption this session (#8152) or cleared review under it (#8177). So the outcome the hold was protecting against — a correct PR that parks forever — does not obtain. Recalling the dev now would cost the work already done to protect against a blockage that no longer exists.
I want to be precise that this is not me overruling the earlier seat's judgment: its Clause-② call is correct and I am adopting it unchanged. Only its tier consequence has moved, and it moved for a reason that seat could not have known at 12:23Z.
What I owed the dev and did not give them
My dispatch order asked them to re-measure things this thread had already established, which is wasteful but harmless. The harmful omission is the control. Triage offered
filter: true(citingplugin-grid/src/index.tsx:76) as the shape to re-check by — and the second seat correctly identified that as the wrong structure: it is anElementDataSourceMapping, not a registration input. Both target files contain such a line (plugin-calendar:66,plugin-kanban:390), so a dev following triage's control would findfilter: truepresent in both and conclude this card was already fixed.That correction, plus the settled
filter-shape ruling from #7711/PR #8027, the per-key spec check owed forlimit/sort, and the instruction to name thecheck:react-declaration-parityblind spot without fixing it, have now been sent to the dev directly.⭐ Credit where the thread earns it: triage flagged its own bare word-count (5 / 4) as unable to prove the inputs lack
filter, and explicitly refused to upgrade it to a reading. It was right to refuse — the count was hitting the mapping constant, which is precisely the failure it named. That refusal is what made the second seat's correction findable.
Generated by Claude Code
os-dev-report
{ "issue": 7712, "status": "done", "branch": "claude/issue-7712-kanban-calendar-filter-input", "pr": "https://github.com/objectstack-ai/objectui/pull/8186", "premise_still_valid": true, "summary": "Declared `filter` as an input on the four registrations whose renderer reads it: plugin-kanban:object-kanban, view:kanban, plugin-calendar:object-calendar, view:calendar — type 'array', matching GRID_QUERY_INPUTS (plugin-grid/src/index.tsx:218) and object-metric (plugin-dashboard:207). ⚠️ FOUR tags, not the dispatch's six: plugin-kanban registers four, but kanban-ui and kanban-enhanced are the static-column board (KanbanRenderer), never read schema.filter, and ComponentPropsMap has no row for either — declaring there would publish a key nothing reads, this defect reversed. The `filter: true` trap was measured and avoided: kanban:390 / calendar:66 / grid:76 are ElementDataSourceMapping constants, not inputs. Deriving inputs from that mapping was measured and REJECTED — it also carries kanban's `limit`, which the strict spec refuses by name, so derivation would publish an unstorable key. Also added: a pin per package, the docs row both plugin pages omitted, @object-ui/sdui-parser in both devDependencies (tsc caught it undeclared), and a minor changeset for both packages.", "tests": "REPRO (pre-fix, locked): `pnpm --filter @object-ui/plugin-kanban exec vitest run --maxWorkers=2 packages/plugin-kanban/src/__tests__/filterIsDeclaredInput-7712.test.ts --root ../..` -> 'Tests 4 failed | 3 passed (7)', message `kanban has no prop \"filter\"` (angle brackets stripped for this comment). The 3 that passed are the controls (unknown-prop still reported for a bogus key on both tags; spec key verdict), so only the declaration rows were red. POST-FIX (locked, one acquisition): dependency-closure build `pnpm --filter '@object-ui/plugin-kanban^...' --filter '@object-ui/plugin-calendar^...' build` EXIT 0; full suites `pnpm --filter @object-ui/plugin-kanban --filter @object-ui/plugin-calendar run test` EXIT 0 — kanban 'Test Files 26 passed (26) / Tests 137 passed (137)', calendar '26 passed (26) / 152 passed (152)'. TYPE-CHECK: first run EXIT 2, TS2307 'Cannot find module @object-ui/sdui-parser' in the calendar pin — real gap, fixed by declaring the workspace devDependency in both packages; re-run EXIT 0 for both, including each package's tsconfig.test.json (so the pins are compiled, not merely executed). FINAL HEAD: both pins re-run, 'Test Files 2 passed (2) / Tests 14 passed (14)'. ABLATION (locked, fix committed first): mutated `name: 'filter'` -> `name: 'filterZZZ'` (first occurrence = the object-kanban registration) in packages/plugin-kanban/src/index.tsx; mutation proved on disk BEFORE reading any result — anchor counts 2->1 and 0->1 plus a changed blob (git hash-object e2006771a8b0590c0e6ad57cc0788c8ff217f454 -> fe9930c2970b1864f5c31ba4caf3e6a5ccba6ed9); pin then RED, 'Tests 2 failed | 5 passed (7)', and the two reds are exactly the object-kanban rows while view:kanban stayed green (the pin is per-tag, not per-file); restore proved BY STATE — hash-object == `git rev-parse HEAD:packages/plugin-kanban/src/index.tsx`, `git diff HEAD` empty, anchors back at 2/0. That blob is still the one at the PR head, so the ablation is re-checkable against the PR. No build needed for the ablation to be honest: vitest.config.mts aliases @object-ui/* to each package's src, so a stale dist cannot make it falsely green. GATES GREEN (unlocked): changeset-presence / no-major / fixed / overwrite, control-bytes, element-data-source-declaration, doc-fences, doc-types, phantom-deps, self-import, side-effects-array, spec-symbols, vi-mock-specifiers, vi-mock-inherit, lint:coverage (46/46), type-check:coverage, governed-queue-guard --test (NOT GOVERNED), and `lint` for both touched packages (EXIT 0, warnings only). NOT MEASURED, declared, left to CI: check:doc-snippets and check:spec-floors need a whole-workspace build (spec-floors' 12 findings are all `no-artifact` on packages this branch never built, none about this diff) and check:sdui-registration-pins weighs a built console bundle. DECLARED NARROWING: after four consecutive `exit 99` queue timeouts on the shared verify lock (~36 min of waiting, slot kept and resumed each time), the two-package `tsc --noEmit` and the final two-file pin re-run were executed WITHOUT the lock; the heavy work (ablation, closure build, both full suites) all ran under it.", "mcp_calls": "15 — 1 comment read, 4 search_issues (incl. the control that returned #7712), 4 issue creates, 1 PR create, 2 label writes/reads that failed to resolve a PR number, 1 pull_request_read label read-back, 1 label write, this comment", "open_questions": [], "out_of_scope_findings": [ "filed as #8171: object-calendar omits `sort` on the same two registrations — spec declares it (runtime safeParse success), ObjectCalendar.tsx:479 reads it for $orderby. Same defect, one key over; the POSITIVE half of the per-key spec check.", "filed as #8172: kanban's `limit` — docs (plugin-kanban.mdx:151 section + props row), ObjectKanbanSchema and ObjectKanban.tsx:364 all say yes, but the strict ComponentPropsMap['object-kanban'] refuses it by name (unrecognized_keys). The NEGATIVE half: declaring it would publish a key the save gate cannot store. Needs a contract decision, not a renderer patch.", "filed as #8174: @object-ui/types declares neither `filter` nor `sort` on ObjectKanbanSchema / ObjectCalendarSchema — the fourth declaration face, bounded honestly against #7927's BaseSchema index-signature ceiling.", "filed as #8176: the console's reverse-direction parity gate (registry-inputs-spec-parity.test.ts, undiscoverableSpecKeys:387) judges only EAGERLY registered blocks — its `covered` set comes from ComponentRegistry.getConfig, which reads loaded registrations only, while apps/console registers every plugin block with registerLazy. So this card's two blocks were unjudged AND uncounted, which is the structural reason #7712 was invisible to both ratchets. Flagged as a source-level derivation with the one-line runtime verification." ] }
Generated by Claude Code
Landed as
0ead1f62avia PR #8186. Closing;pm:dispatchedstripped and the assignee cleared in the same stroke.Verified by CONTENT against a control proven before the merge:
marker pre-merge post-merge name: 'filter'inpackages/plugin-kanban/src/index.tsx0 2 name: 'filter'inpackages/plugin-calendar/src/index.tsx0 2 Lit control for the pre-merge zeros: the same shape was already on main at
packages/plugin-dashboard/src/index.tsx:207,:325,:369— so those zeros were readings, not a wrong query shape. Matched lines were printed, not counted.Two corrections the dev made to my dispatch, both verified
① Four registrations, not the six I named. I relayed a count from an earlier seat without measuring it.
$filter: schema.filterexists at exactly two renderer sites (ObjectCalendar.tsx:478,ObjectKanban.tsx:363);kanban-uiandkanban-enhancedregister a different component, never read it, and have noComponentPropsMaprow. Declaringfilteron those would have published a key nothing reads — this card's own defect, reversed. Declining to over-fix was the harder and correct call.② The "derive inputs from the mapping" idea — which I passed along as promising — was measured and rejected.
OBJECT_KANBAN_DATA_SOURCEcarrieslimit: 'limit', and the strictComponentPropsMap['object-kanban']refuseslimitby name, so derivation would declare a key the save gate cannot store. That is a better answer than the suggestion it replaced, and it is measured rather than argued.The
filter: truetrap was navigated correctly:kanban:390,calendar:66andgrid:76areElementDataSourceMappingconstants, not input lists. A dev following triage's original control would have foundfilter: truepresent in both files and concluded the card was already fixed.A process note I owe this card
I dispatched it over a standing hold — a prior seat had parked it unclaimed pending contract-review tier availability, and I selected it from a queue listing without reading its thread. The dispatch stands (the quota-exhaustion exemption superseded the hold's premise, and two Clause-② cards landed under that exemption this session), but the decision was accidental rather than reasoned, and the omission cost the dev a wrong control until I sent the correction. Recorded in full at the time.
Filed, not folded in — all four graded
- plugin-calendar: the two
object-calendarregistrations declare nosortinput whileObjectCalendarreadsschema.sortand the spec declares it — #7712's defect, one key over #8171p2—object-calendaromitssorton the same two registrations; spec declares it,ObjectCalendar.tsx:479reads it for$orderby. The positive half of the per-key spec check, and now unblocked by this landing. - plugin-kanban: the docs and
ObjectKanbanSchemateachlimiton anobject-kanban, the renderer reads it as$top— and the spec's strictComponentPropsMaprefuses it by name #8172needs-user-decisionp2— kanban'slimit: docs,ObjectKanbanSchemaandObjectKanban.tsx:364all promise it, the strict map refuses it by name. A contract contradiction, not a renderer patch. - types:
ObjectKanbanSchemaandObjectCalendarSchemadeclare nofilter(and nosort) — the fourth face of the key #7712 declares everywhere else #8174p3—@object-ui/typesdeclares neitherfilternorsorton the two schemas; the fourth declaration face. - console:
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 #8176p2— ⭐ the structural reason this defect was invisible: the console's reverse-parity gate builds itscoveredset fromComponentRegistry.getConfig, which reads loaded registrations only, whileapps/consoleregisters every plugin block withregisterLazy. These two blocks were not merely unjudged, they were uncounted. Fixing these four registrations does not make the next omission loud. Now dispatched, measurement-first.
Generated by Claude Code
- plugin-calendar: the two
Filed unassigned from the objectstack #15442 / #15449 consumer census (PM session
session_01M59rPZZFzqhfMUPFqqZTkf, os-dev seat, 2026-09-05). Measured at the pina472b07.Measured
packages/plugin-kanban/src/index.tsx— the four registrations (:208,:313,:420,:434) declare, in total,className,columns,enableVirtualScrolling,objectName,onCardMove,onColumnToggle,virtualScrollThreshold. Nofilter, nolimit.packages/plugin-calendar/src/index.tsx— the two registrations (:293object-calendar,:303calendar) declarecalendarandobjectName. Nofilter, nosort.packages/plugin-kanban/src/ObjectKanban.tsx:263$filter: schema.filter(and:264$top: schema.limit);packages/plugin-calendar/src/ObjectCalendar.tsx:454$filter: schema.filter(andschema.sort).ComponentPropsMap['object-kanban'].filter(component.zod.ts:2664) and['object-calendar'].filter(:2719).object-grid(plugin-grid/src/index.tsx:218) andobject-metric(plugin-dashboard/src/index.tsx:207) declarefilterastype: 'array'.Why it matters
sdui-parservalidateTree(packages/sdui-parser/src/validate.ts:70-76) reportsunknown-propfor any key not incomp.inputs(afterBASE_PROPS), so on the html tier an author writing the spec-declaredfilteron anobject-kanbanorobject-calendaris told it is unknown — while the renderer honours it. That is the objectui#6678 shape (the one spelling that works gets the same diagnostic as the ones that do nothing).check:react-declaration-parityruns manifest to spec, so a spec-declared key missing from the manifest is not what it ratchets.Refs: #6678 · objectstack#15449.
Generated by Claude Code