Skip to content

types: ObjectKanbanSchema and ObjectCalendarSchema declare no filter (and no sort) — the fourth face of the key #7712 declares everywhere else #8174

Description

@os-justin

Filed unassigned by the os-dev seat implementing #7712 (branch claude/issue-7712-kanban-calendar-filter-input). Measured on origin/main 9bfd618.

Measured

Member lists extracted from the interface bodies in packages/types/src/objectql.ts (not a bare word grep — the extraction returns the full member list per interface, which is its own control that the reader is working):

  • ObjectKanbanSchema (:2744-:2833): type, objectName, groupBy, groupField?, limit?, titleField?, cardFields?, quickAdd?, coverImageField?, allowCollapse?, conditionalFormatting?. No filter.
  • ObjectCalendarSchema (:2698-:2739): type, objectName?, data?, staticData?, startDateField?, endDateField?, titleField?, defaultView?. No filter, no sort.

Meanwhile, after #7712 lands, filter is declared by @objectstack/spec (ComponentPropsMap accepts it on both blocks — measured by safeParse, with an undeclared control key refused on the same call), declared by both plugins' registration inputs, and read by both renderers (ObjectKanban.tsx:363, ObjectCalendar.tsx:478). sort is in the same position for object-calendar (see the companion card).

Why it is worth a row

An authored filter reaches these annotations only through BaseSchema's [key: string]: any — admitted, never examined. That is verbatim the reasoning objectui#7322 used to move groupBy into ObjectKanbanSchema (the docblock it left behind on :2748-:2764 states it), so the precedent for treating an undeclared-but-read key here as a defect is this same interface, one key over.

⚠️ Bounded honestly, because a neighbouring card measured the ceiling: #7927 found that BaseSchema ends in [key: string]: any, so no annotation on any node schema can catch a misspelled key. Declaring filter therefore buys editor completion, doc-snippet fidelity and one honest declaration face — it does not buy a type error for the misspelling. Whoever picks this up should decide whether that is worth the edit, or whether it should wait behind #7927.

Neighbours (⛔ none of them is this)

Refs: #7712 · #7322

Activity

  1. self-assigned this
    on Sep 9, 2026
  2. os-bill commented on Sep 9, 2026

    @os-bill
    Collaborator

    Claim: PM loop round 1
    Session: session_012W3vMLTFY9SPr2LyxhSeYi
    Branch: claude/issue-8174-kanban-calendar-declare-filter-sort
    Worktree: objectui-issue-8174
    Domain: domain:spec
    File surface: packages/types/src/objectql.ts (the ObjectKanbanSchema and ObjectCalendarSchema interface bodies only) · packages/types/src/zod/objectql.zod.ts (the same two mirrors only, if parity requires it) · .changeset/ (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus — TIER_DEFAULT. The card leaves one judgement to the taker (is the declaration worth it given #7927's index-signature ceiling), so ⛔ not the floor. dispatch-gates.mjs --tier cannot serve objectui paths (refuses cross-repo, exit 2); judged from content.
    Clause-②: yes
    Thread-read: none (zero comments on this card at claim time)
    Serial constraints cleared: ⭐ PREMISE VERIFIED ON CURRENT origin/main c03d03bb2, ⛔ not inherited from the card's 9bfd618 reading. The card is written as "after #7712 lands"; #7712's work IS on main — schema.filter is read at packages/plugin-kanban/src/ObjectKanban.tsx (2 hits) and packages/plugin-calendar/src/ObjectCalendar.tsx (7 hits), with objectName (35 hits) as the positive control that the grep reaches those files. And the defect still stands: neither ObjectKanbanSchema nor ObjectCalendarSchema declares filter or sort. FILE SURFACE: objectql.ts and zod/objectql.zod.ts were freed when PR #8758 MERGED (~05:2xZ) — main is now c03d03bb2 and #8765/#8766/#8768 have landed too. ⚠️ ONE REGION-LEVEL OVERLAP declared rather than assumed away: objectui#7917 (dispatched this round) also edits zod/objectql.zod.ts — but its region is the object-tree arm, and this card's is the ObjectKanban/ObjectCalendar mirrors. Disjoint regions of one file, under this lane's file→region granularity ruling (5551274423); the four substitute disciplines apply and conflicts go to the merge queue, ⛔ not to PM hand-ordering. ⛔ Still held by PR #8763: zod/layout.zod.ts, zod/form.zod.ts, layout.ts, form.ts, zod-mirror-parity.test.ts.


    Generated by Claude Code

  3. os-bill commented on Sep 9, 2026

    @os-bill
    Collaborator

    os-dev-report
    {
    "issue": 8174,
    "status": "done",
    "branch": "claude/issue-8174-kanban-calendar-declare-filter-sort",
    "pr": "#8788",
    "premise_still_valid": true,
    "summary": "Declared filter on ObjectKanbanSchema and filter + sort on ObjectCalendarSchema, on both published faces of @object-ui/types (the TS interface in packages/types/src/objectql.ts and its hand-written zod mirror in packages/types/src/zod/objectql.zod.ts), spelled exactly as ObjectGanttSchema spells them so the views' query vocabularies cannot fork. Added a pin that asserts membership on the mirror's own .shape rather than on parse acceptance, uses invariant type equality so a member falling back to the index signature reads as a failure, and pins the objectui#7927 ceiling as UNCHANGED rather than claiming it away. Changeset is minor for @object-ui/types. PR is DRAFT with needs:contract-review hung on it; not flipped ready, not enqueued, no auto-merge.",
    "pm_assumption_falsified": "The re-dispatch stated the previous killed attempt 'pushed nothing' and 'there is no WIP to recover'. Half true and half false, and the false half mattered. The branch was indeed empty of commits (remote ref sat at base fb3a101, the empty-branch routing probe). But the worktree /home/user/objectui-issue-8174 already existed and carried UNCOMMITTED WIP on disk: modified objectql.ts, modified zod/objectql.zod.ts, and an untracked 350-line pin. I did not 'start clean' and discard it. I read it, re-derived every factual claim in it against current origin/main independently, found all of them to hold, and committed it as the implementation. Had I followed the instruction literally, a complete and correct implementation would have been deleted and rebuilt.",
    "the_7927_call": {
    "verdict": "PROCEED — the edit is worth it and must NOT wait behind objectui#7927",
    "reasoning": [
    "1. objectui#7927's ceiling caps the KEY dimension only. A MISSPELLED key stays admitted on both faces either way. Verified on origin/main: BaseSchema (packages/types/src/base.ts:70-468) ends in [key: string]: any at :467. The pin asserts that ceiling is unchanged rather than claiming otherwise.",
    "2. The VALUE dimension is NOT capped, and that is what declaring buys. filter: 'status = open' type-checked green through the index signature and parsed green through the mirror's .passthrough(); it is now a type error and a named refusal at the key. That is a measurable acquisition, not editor completion — which is more than the card credits.",
    "3. The sort half is the sharpest case and is a LIVE silent failure, not a hypothetical. objectui#8221 retired the legacy string clause: convertSortToQueryParams no longer admits a string in its signature and returns undefined for one after reporting the retired spelling (verified at packages/core/src/utils/sort-query.ts:133 and :141 on fb01022). So sort: 'start_date asc' on an object-calendar node type-checked green, parsed green, and then drew an UNSORTED calendar with nothing refusing it anywhere. Declaring the member is what makes that retirement audible at the authoring boundary.",
    "4. The ordering argument runs OPPOSITE to 'wait behind it'. If objectui#7927 ever tightens BaseSchema it tightens the KEY dimension — and a tightening that lands while these three members are still undeclared turns every CORRECTLY authored filter/sort node into a refusal. Declaring them is a prerequisite for that card, not a duplicate of it.",
    "5. Precedent: objectui#7322 weighed this same trade on this same interface under this same ceiling (which it predates but does not escape) and went ahead. Same interface, one key over."
    ]
    },
    "member_lists_retaken": {
    "commit_read": "fb0102271b86848ae6ef05aa55b1a3c78bfb03cd (origin/main at read time; NOT the card's 9bfd618, and NOT the PM's c03d03b)",
    "method": "full member list pulled from the interface BODY by brace matching with comments stripped — the returned list is its own control that the extraction works; the card's :2744 / :2698 anchors are both stale and were not reused",
    "ObjectKanbanSchema": {
    "anchor": "packages/types/src/objectql.ts:2779-2900",
    "members_before": ["type", "objectName?", "groupBy", "groupField?", "limit?", "titleField?", "cardFields?", "quickAdd?", "coverImageField?", "allowCollapse?", "conditionalFormatting?"],
    "count_before": 11,
    "verdict": "no filter, no sort — the defect stands on current main"
    },
    "ObjectCalendarSchema": {
    "anchor": "packages/types/src/objectql.ts:2733-2774",
    "members_before": ["type", "objectName?", "data?", "staticData?", "startDateField?", "endDateField?", "titleField?", "defaultView?"],
    "count_before": 8,
    "verdict": "no filter, no sort — the defect stands on current main"
    },
    "ObjectGanttSchema_precedent": {
    "anchor": "packages/types/src/objectql.ts:2415-2728, 47 members",
    "spelling_copied": "filter?: any[] (:2725) and sort?: SortConfig[] (:2727); mirrors at zod/objectql.zod.ts:1052-1053 are z.array(z.any()) and z.array(SortConfigSchema)"
    }
    },
    "read_census_retaken": {
    "commit_read": "fb0102271b86848ae6ef05aa55b1a3c78bfb03cd",
    "packages/plugin-kanban/src/ObjectKanban.tsx": {
    "schema.filter": 2,
    "schema.sort": 0,
    "positive_control_schema.objectName": 26,
    "positive_control_bare_objectName": 35,
    "live_sites": "$filter: schema.filter on the dataSource.find call, and the same key again in that effect's dependency list"
    },
    "packages/plugin-calendar/src/ObjectCalendar.tsx": {
    "schema.filter": 7,
    "schema.filter_live": 2,
    "schema.filter_in_docblock": 5,
    "schema.sort": 2,
    "positive_control_schema.objectName": 19,
    "positive_control_bare_objectName": 29,
    "live_sites": "$filter: schema.filter and $orderby: convertSortToQueryParams(schema.sort) on the dataSource.find call, and both again in that effect's dependency list"
    },
    "note": "The PM's figures (kanban 2, calendar 7, control 35) reproduce exactly. The kanban ZERO for schema.sort is the measured premise for leaving sort off that interface, and the pin holds it so the board acquiring an $orderby turns red BEFORE the faces fork again."
    },
    "other_two_faces_verified": {
    "spec": "@objectstack/spec 17.3.0 ComponentPropsMap — object-kanban declares 13 keys INCLUDING filter and EXCLUDING sort; object-calendar declares 9 keys INCLUDING both filter and sort. objectName present on both as the non-vacuity control.",
    "registration_inputs": "plugin-kanban/src/index.tsx:523 { name: 'filter', type: 'array' }; plugin-calendar/src/index.tsx:400-401 { name: 'filter', type: 'array' } and { name: 'sort', type: 'array' }."
    },
    "tests": "PIN: packages/types/src/tests/kanban-calendar-filter-sort-8174.test.ts. Proved COMPILED, not assumed: packages/types' package tsconfig excludes the test tree, but type-check runs a third program (tsconfig.test.json) that includes it — tsc -p tsconfig.test.json --listFiles lists the pin at line 413, and tsc --noEmit --listFiles (main program) lists it 0 times. The PM's reading is confirmed by measurement. ABLATION, three legs, each proving the mutation reached disk before its run was read (before/after anchor counts plus a git hash-object comparison), each restored via git checkout HEAD -- PATH under a trap ... EXIT INT TERM with absolute paths, and each restore proved by an EMPTY git diff HEAD plus a blob hash equal to the HEAD blob (never by a bare exit code). LEG 1 — delete ObjectCalendarSchema.sort from objectql.ts (anchor count 4 to 3, hash d260d8ad -> c8ac23e8): pnpm --filter @object-ui/types type-check exit 2 with 5 errors, 3 x TS2344 on the invariant-equality pins and 2 x TS2578 unused @ts-expect-error (the objectui#8221 retired string clause, and the bad order value) — the directives going UNUSED is what makes them pins rather than restatements. LEG 2 — delete sort from the calendar zod mirror (anchor 4 to 3, hash 4db28ef6 -> 36a8b41e): vitest exit 1, 4 failures (shape membership expected [ Array(28) ] to include 'sort', plus the three at-the-key refusals). LEG 3, THE ONE WORTH REPORTING — same mutation as leg 2 but judged by tsc instead of vitest: exit 2, one error, zod-mirror-parity.test.ts(2808,14): error TS2322: Type '\"objectql.zod.ts#ObjectCalendarSchema\"' is not assignable to type 'never'. Leg 2 alone would have read as 'the mirror half is covered only by my own new file' because the parity ledger stayed GREEN under vitest; it does not. The ledger's unmirrored-declared half is a TYPE-level operator, so it lands on tsc and is invisible to the test runner. The mirror edit is therefore MANDATORY, and mandatory in a way a vitest-only ablation cannot see. A FIRST attempt at leg 1 was a no-op (the body-slice anchor was off by one newline, nothing changed on disk) and the script's own before/after count REFUSED to run the leg and exited 92 rather than reporting a passing ablation — reported because that is exactly the failure mode that otherwise reads as green.",
    "gates": {
    "note": "scripts/pm/dispatch-gates.mjs refuses objectui paths (cross-repo, exit 2) as the dispatch said, so this list was hand-derived from objectui's root package.json scripts and .github/workflows/. Every exit code captured as cmd > file 2>&1; EXIT=$?, never through a pipe. Heavy runs went through the container's shared verify lock with OS_VERIFY_LOCK_SLOT=objectui-8174; all four acquisitions were granted at 0s wait. vitest was run FROM THE REPO ROOT with paths as positional args, never pnpm --filter PKG exec vitest FILE.",
    "pnpm --filter @object-ui/types type-check": 0,
    "pnpm exec vitest run packages/types/": "0 — 156 test files, 3091 tests passed",
    "pnpm exec vitest run packages/plugin-kanban/ packages/plugin-calendar/ apps/console/src/tests/registry-inputs-spec-parity.test.ts packages/app-shell/.../block-config-schema-parity-8216.test.ts packages/app-shell/.../block-config.test.ts": "0 — 70 test files, 711 tests passed",
    "pnpm --filter @object-ui/types build": "0 — tsc + vite build + dist completeness, 128 emitted files verified",
    "pnpm --filter @object-ui/types lint": "0 — 271 warnings (all pre-existing no-explicit-any across the package), 0 errors",
    "node scripts/check-changeset-fixed.mjs": 0,
    "node scripts/check-changeset-no-major.mjs": 0,
    "node scripts/check-control-bytes.mjs": "0 — 6982 tracked text files scanned; plus an independent grep -naP control-byte sweep over the four changed files, 0 hits",
    "node scripts/check-spec-symbol-derivation.mjs": 0,
    "node scripts/check-doc-component-types.mjs": 0,
    "node scripts/check-unreferenced-sources.mjs": 0,
    "node scripts/check-element-data-source-declaration.mjs": 0,
    "node scripts/check-governed-queue-guard.mjs --test (four changed paths)": "0 — NOT GOVERNED, 4 paths against 5 governed surfaces, none matched",
    "NOT MEASURED (prerequisite not met, whole-tree build required — CI owns these)": "check:doc-snippets exit 2, check:doc-examples exit 2, check:sdui-registration-pins exit 2 (each printed the build it wants), check:readme-exports exit 1 (its own text is 'run pnpm build first' for 5 documented types across 4 packages, followed by a collapsed-population report). This worktree builds only the affected package, so all four are reporting a missing prerequisite, not a verdict on this diff. Read as NOT MEASURED — neither green nor red."
    },
    "line_budget": {
    "measure": "git diff --numstat origin/main...HEAD (three-dot, from the merge base) at HEAD a9c30cb",
    "merge_base": "fb0102271b86848ae6ef05aa55b1a3c78bfb03cd",
    "total": "4 files changed, 486 insertions, 0 deletions",
    "per_file": {
    "packages/types/src/objectql.ts": "+62 / -0",
    "packages/types/src/zod/objectql.zod.ts": "+35 / -0",
    "packages/types/src/tests/kanban-calendar-filter-sort-8174.test.ts": "+350 / -0 (new)",
    ".changeset/8174-kanban-calendar-filter-sort.md": "+39 / -0 (new)"
    },
    "note": "No skills/** in this diff, so no published-skill line ratchet applies. The three-dot measure is the load-bearing one: origin/main moved again DURING this run (to 5591f03, sibling PR objectui#8307 landing on plugin-kanban), so a two-dot git diff origin/main HEAD reads 9 files and attributes a sibling's work to this branch. Three-dot from the merge base reads 4."
    },
    "files_changed": [
    "packages/types/src/objectql.ts",
    "packages/types/src/zod/objectql.zod.ts",
    "packages/types/src/tests/kanban-calendar-filter-sort-8174.test.ts",
    ".changeset/8174-kanban-calendar-filter-sort.md"
    ],
    "out_of_bounds_respected": "packages/types/src/tests/zod-mirror-parity.test.ts was NOT edited and did NOT need editing — mirroring at the SAME requiredness as the interface (both members optional on both faces) leaves the ledger at zero drift for these pairs, confirmed twice: by type-check exit 0 with the change in place, and by ablation leg 3 showing the ledger DOES redden if the mirror falls behind. zod/layout.zod.ts, zod/form.zod.ts, layout.ts and form.ts (held by PR objectui#8763) are untouched. BaseSchema's index signature is untouched (objectui#7927). No other undeclared member on these two interfaces was touched (objectui#7742 / objectui#7780 are not this card).",
    "deviations": [
    "ADOPTED PRE-EXISTING WIP instead of starting clean, contrary to the re-dispatch's 'there is no WIP to recover'. The worktree existed with uncommitted edits to both target files plus an untracked pin. Every factual claim in it was independently re-derived on origin/main fb01022 before it was committed; all held. Deleting and rebuilding correct work would have been the worse deviation.",
    "Merged origin/main once as instructed, at fb01022. One conflict, in packages/types/src/zod/objectql.zod.ts, in the kanban limit describe string — PR objectui#8776 had removed its stale ObjectKanban.tsx:264 source-line address. Resolved by taking origin/main's limit line verbatim and re-attaching this branch's filter block below it. Per the dispatch, conflicts go to the merge queue; none was hand-ordered. origin/main has since moved again to 5591f03; NOT re-merged (the instruction was to merge once).",
    "REST was used for the label write (POST .../issues/8788/labels, the additive endpoint that does not touch existing labels) rather than MCP. First attempt returned HTTP 415 for a missing Content-Type; retried with Content-Type: application/json and got HTTP 200. Read-back confirms all three labels present: package: types, tests (both added by the repo labeler) and needs:contract-review (mine) — the additive write did not strip the labeler's two.",
    "PR body read back to the end after creation: 10093 bytes, ZERO angle-bracket characters in the stored body (none were written — placeholders are spelled as words throughout), the session-URL attribution footer survived intact in its session form, and a closing-keyword scan over the stored body returns exactly ONE hit, Fixes objectstack-ai/objectui#8174, the intended card. Every other card reference (objectui#7322, #7927, #8221, #7742, #7780, #8763) is bare, with no keyword adjacent."
    ],
    "mcp_calls": "2 — create_pull_request, add_issue_comment. Issue body, comments, and all four source files were read through the repo-scoped REST channel and git (probed first: REST GET on the issue returned HTTP 200), never through MCP; no search_issues call was needed because no card was filed.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: the zod-mirror-parity ledger's unmirrored-declared half is a TYPE-level operator, so a mirror that falls behind its TS declaration is caught by tsc and is INVISIBLE to vitest. Measured, not inferred (ablation leg 3 above). This is by design, not a defect, so there is nothing to file — but it is a live trap for the next seat that ablates a zod mirror with pnpm exec vitest run alone and concludes from a green run that the mirror half is optional. Successor who will hit it: any seat editing a mirror under packages/types/src/zod/, which is a recurring lane (objectui#8763 and objectui#8776 both touched it this week). Recorded here rather than as a card because the file that would carry the note is out of bounds for this PR.",
    "noted, not filed: check:readme-exports signals an unmet build prerequisite as exit 1 while its three sibling whole-tree scans use exit 2 for the same condition. Its own output states this is deliberate ('A FAILURE and not a skip'), with the stated reason that a skip would read as coverage. Not a defect — recorded only so the asymmetric exit code is not misread as a red caused by this diff. Successor: none — no PR or person is routed to that script by this change."
    ]
    }


    Generated by Claude Code

  4. os-bill commented on Sep 9, 2026

    @os-bill
    Collaborator

    条款② ceiling contract review — VERDICT: PASS

    PR #8788 @ a9c30cb65 (unchanged since review). Reviewed at the ceiling tier; tier verified before adoption — 125 model stamps, all claude-fable-5-1, zero residue, positive control present (167). A verdict is adopted only after that count; one non-ceiling stamp would have voided it whole.

    The reviewer worked from its own worktree (objectui-review-8788, removed; shared /home/user/objectui untouched — 0 status entries, stash list 0) and re-derived every load-bearing claim rather than reading the PR body. That mattered here: this diff's author is absent (the first attempt was killed by a rate limit; the second implementer committed WIP it found on disk), so the self-report was second-hand by construction.

    What was independently re-derived

    1. Both faces moved at identical requiredness — the parity ledger is genuinely unmoved, not accidentally silent. type-check exit 0 with the ledger compiling; parity vitest 32/32; no entry in UnmirroredDeclared / RuntimeOnlyDeclared / WiderThanDeclared / drift. Proven live by two ablations of the reviewer's own: deleting the calendar mirror's sort reds zod-mirror-parity.test.ts:2808; making it required reds the drift reconciliation at :2746. So zod-mirror-parity.test.ts (held by fix(types): arm AnyComponentSchema for seven registered node-slot renderers (objectui#8499) #8763) is genuinely not owed an edit.
    2. The finding(types): BaseSchema closes 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 PROCEED call holds — and is stronger than the PR claimed. finding(types): BaseSchema closes 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's own scope text names "removing the signature and declaring the real extension keys" as its removal route, so this declaration sits inside finding(types): BaseSchema closes 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's prerequisite set, not beside it. Under finding(types): BaseSchema closes 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's other route (keep and document) the value-dimension acquisition still stands. Robust to both outcomes.
    3. The value dimension is genuinely acquired, measured before/after: merge base → 1 error (positive control only); head → 4 errors at exactly the three wrong-typed nodes plus the control. Correctly authored nodes, the kanban node, and the misspelled filtr stay green in both states — i.e. the finding(types): BaseSchema closes 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 key-dimension ceiling is unchanged, exactly as the PR claims. Zod refuses at the key on both arm and union, paths filter, sort, sort.0.order.
    4. No vocabulary fork. any[] / SortConfig[] is what ObjectGanttSchema (objectql.ts:2725/2727), ObjectGridSchema (:679/690) and ObjectMapSchema (:2340/2342) already spell, on both faces — five object-bound views now agree. (ObjectGallerySchema unknown and ObjectDataTableSchema any are pre-existing outliers.)
    5. The kanban/calendar sort asymmetry survives a wider census than the pin's: case-insensitive sort token in ObjectKanban.tsx 0; bracket/destructuring forms 0; orderby 0; the 28 hits across plugin-kanban/src are all dnd-kit Sortable* plus one Array.prototype.sort(). Declaring sort on the board would be this repo inventing a key. Not a defect.
    6. The pin is not vacuous — all three PR legs reproduced independently plus a fourth of the reviewer's own, and membership confirmed to be on .shape (calendar shape carries filter+sort, kanban filter only, before-state neither).
    7. Changeset minor correct, with a planted major negative control confirming check-changeset-no-major actually bites.

    One overstatement recorded (does not change the verdict)

    The PR body's "nothing refusing it anywhere" is not literal: runtime emits one console.error per retired spelling (core/src/utils/sort-query.ts:112), and per #8221 the html tier already answers type-mismatch. The silent faces were exactly the two this PR fixes — TS, and zod → CLI validate/check. Substance stands.

    Left to the PM (follow-ups, ⛔ not riders on this PR)

    1. Stale docblock at packages/plugin-kanban/src/ObjectKanban.tsx:165-183 — still says filter is "declared by NEITHER face" with a | filter | — | — | — | row. False once this merges. Docs-only.
    2. order requiredness fork (pre-existing, now extended to calendar): SortConfig.order required (types :312, zod :136) vs core QuerySortEntry.order? honoured as ascending (sort-query.ts:64-67, :149) vs $orderby's order? (data.ts:118). So sort: [{ field: 'x' }] parsed green before and is refused at sort.0.order now — the changeset's "nothing that validated before is refused now" is true only under the package's own SortConfig. Same on Gantt/Grid/Map. A card, not a rider.
    3. Element-type tightening across the five any[] views — the docs already promise ViewFilterRule[] (plugin-kanban.mdx:108, plugin-calendar.mdx:247) and the spec exports FilterArray; QueryParams.$filter is additionally Record<string, any> | FilterArray (data.ts:108), so object-shaped filters the adapter accepts are refused at this authoring face. A five-view card.

    Disposition

    needs:contract-review cleared on both carriers (this card and PR #8788) — 双载体 agree. PR flips ready and goes to the merge queue.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpackage: typespriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions