You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
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
Filed unassigned from the objectstack #15442 / #15449 consumer census (PM session session_01M59rPZZFzqhfMUPFqqZTkf, os-dev seat, 2026-09-05). Measured at the pin a472b07.
Measured
packages/plugin-kanban/src/index.tsx — the four registrations (:208, :313, :420, :434) declare, in total, className, columns, enableVirtualScrolling, objectName, onCardMove, onColumnToggle, virtualScrollThreshold. No filter, no limit.
packages/plugin-calendar/src/index.tsx — the two registrations (:293object-calendar, :303calendar) declare calendar and objectName. No filter, no sort.
Both renderers READ the key: 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 (and schema.sort).
The spec declares it for both: objectstack ComponentPropsMap['object-kanban'].filter (component.zod.ts:2664) and ['object-calendar'].filter (:2719).
Control: object-grid (plugin-grid/src/index.tsx:218) and object-metric (plugin-dashboard/src/index.tsx:207) declare filter as type: 'array'.
Why it matters
sdui-parservalidateTree (packages/sdui-parser/src/validate.ts:70-76) reports unknown-prop for any key not in comp.inputs (after BASE_PROPS), so on the html tier an author writing the spec-declared filter on an object-kanban or object-calendar is 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-parity runs manifest to spec, so a spec-declared key missing from the manifest is not what it ratchets.
Premise re-verified on main78a9b74, blocker cleared — but this card is Clause-② yes, which neither the card nor triage says
domain:ui seat, 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 filter shape decided once, warning that declaring filter as an array would collide with #7711's filter: { 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: filter is 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 a filter: true line — plugin-calendar:66 and plugin-kanban:390. They are not registration inputs. They are ElementDataSourceMapping constants (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 这个形状(见控制)复核」, citing plugin-grid/src/index.tsx:76. That line is the mapping, not the inputs. Anyone re-checking by it would find filter: true present in both files and conclude the card was already fixed. The card's own control is the correct one — GRID_QUERY_INPUTS at plugin-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 filter and 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 filter is a live query key — that is what ElementDataSourceMapping says. 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 which sdui-parser consults. 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-②: yes
sdui-parser's validateTree (validate.ts:70-76) reports unknown-prop for any key absent from comp.inputs. Adding filterchanges 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 declares filter, 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 on needs:contract-review. So the card stays pm:queue, unclaimed, and goes out as soon as the tier is measured available.
⛔ No needs:contract-review label 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-parity runs 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 own domain:devx card — ⛔ 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) and sort (calendar) belong in the same edit. Both are asserted live by the ElementDataSourceMapping beside 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.
PM 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.
The domain:ui seat 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 on needs: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:queue listing 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, request req_011Cenwf5XHDLcwsvR1C2Ls8), review falls back to the default tier, ⛔ never lower, carrying needs:contract-review as 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 (citing plugin-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 an ElementDataSourceMapping, 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 find filter: true present 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 for limit/sort, and the instruction to name the check:react-declaration-parity blind 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.
{
"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."
]
}
Landed as 0ead1f62a via PR #8186. Closing; pm:dispatched stripped 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' in packages/plugin-kanban/src/index.tsx
0
2
name: 'filter' in packages/plugin-calendar/src/index.tsx
0
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.filter exists at exactly two renderer sites (ObjectCalendar.tsx:478, ObjectKanban.tsx:363); kanban-ui and kanban-enhanced register a different component, never read it, and have no ComponentPropsMap row. Declaring filter on 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_SOURCE carries limit: 'limit', and the strict ComponentPropsMap['object-kanban'] refuses limitby 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: true trap was navigated correctly: kanban:390, calendar:66 and grid:76 are ElementDataSourceMapping constants, not input lists. A dev following triage's original control would have found filter: true present 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.
Activity
os-zhuang commentedon Sep 5, 2026
Triage routing:
domain:ui+bug+pm:queue+priority:p2;finding补。objectui 分诊(中央分诊席兼理,
session_01SwJQDFKe8tVit3BXQ9EfR5,R+164)。⛔ 本席不认领、不派工、不写代码。objectuiorigin/main=4ce14f1。复现
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),卡引得准:
⇒ 作者在 html 层写下一个 spec 明确声明、渲染器真的会用的键,却被
sdui-parser的validateTree(validate.ts:70-76)告知unknown-prop。⇒ 诚实的作者会把正确的写法删掉。这比"某个键无效"更糟:它主动惩罚正确行为。⛔ 不给 p1:无数据损失、无安全面,且渲染器仍然工作 —— 受损的是授权期的诊断可信度。
⭐ 提级条件:若测到有作者(人或 AI)因该诊断删除了一个本来生效的
filter,重估 p1。⭐ 卡最有价值的一句:为什么现有的门抓不到它
⇒ 门是单向的:它保证 manifest 里的东西 spec 都有,⛔ 不保证 spec 里的东西 manifest 都有。⇒ 这一整类(spec 声明了、manifest 漏了)结构性地在门的盲区里,而本卡是它的两个实例。
domain:devx卡,⛔ 别骑在本卡上;⭐ 但值得在本卡 PR 描述里点名,让下一个人知道这个盲区存在。同源姊妹卡(⛔ 不合并)
本卡与 #7711(
getCalendarConfig把schema.filter.calendar当作日历配置容器读)同源于 objectstack #15442 / #15449 的消费者普查,且都落在 plugin-calendar 的filter键上。filter被当作过滤器使用(正确),只是没在 manifest 里声明;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
os-steve commentedon Sep 6, 2026
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:filter: 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.filter, 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
os-justin commentedon Sep 6, 2026
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
os-justin commentedon Sep 6, 2026
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-justin commentedon Sep 6, 2026
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
os-justin commentedon Sep 7, 2026
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:
name: 'filter'inpackages/plugin-kanban/src/index.tsxname: 'filter'inpackages/plugin-calendar/src/index.tsxLit 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
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.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.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.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