Skip to content

A stored list view filter is AND-only: no OR group #28020

Closed

Description

@objectstack-fleet

domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe

Filing gate: ③ triage-directed. The order on #27386 said: take what is ui-only, card the rest. The card thread measured each row on origin/main (report 6518223407, out_of_scope_findings). Rows 2 and 4 are taken in PR #28017.
Reach: any list view whose natural filter is an any-of, such as HotCRM's "open, or completed in the last 7 days" (#27386 row 1).
Reader who acts: domain:spec first (a decision on the stored shape), then domain:ui (the list query build).
Duplicate search: search_issues "list view stored filter OR group any-of ViewFilterRule" → #19751, #24129 and #20456, all closed and about other parts of the view filter, not OR groups.

Observed

A list view's filter is an array of flat ViewFilterRuleSchema rules (field, operator, value) joined by AND. The $or grouping exists only in FilterConditionSchema, which the view's filter key does not take. So an any-of cannot be authored on a view, although the console filter builder already offers groups.

Wanted

  • Spec: decide the stored shape: either a filter-condition tree on views, or a group member on the rule list. Landing: packages/spec ui/view.zod.ts ListViewSchema.filter and ViewFilterRuleSchema.
  • Console: ui/packages/plugin-list's query build reads it.

Dedupe words: view filter OR group, ViewFilterRule or, list view filter any-of.

Related: #27386 (row 1).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Claim: PM loop round 3 · 2026-10-31T06:29Z
    Session: session_017ihL2fi9zfJhUTubyJYb6V
    Account: marchtian
    Branch: claude/issue-28020-view-filter-or-group
    Worktree: objectstack-issue-28020
    Domain: domain:spec
    Seat: domain:spec#4
    Ruling-ref: maintainer 「可以开始派发 p3」 (2026-10-30T12:23Z) and 「当前任务处理完,合并后改为并发1」 (2026-10-31T05:08Z); the card's filing gate ③ (triage-directed, order on #27386, report 6518223407). The stored-shape fork the card names is put to the maintainer; this claim builds the seat's recommended shape (A below) and the PR does not land before the maintainer rules on it.
    File surface: packages/spec/src/ui/view.zod.ts (ViewFilterRuleSchema and its carriers gain one group element beside the flat rule: shape A, an any-of group of flat rules inside the existing AND list, so the list stays the one filter vocabulary on every carrier and every stored filter parses unchanged), packages/spec/src/ui/filter-rule-array.ts, the readers that walk a rule array (packages/lint/src/filter-walk.ts, validate-field-consumers.ts, packages/metadata-protocol/src/protocol.ts, packages/spec/src/conversions/registry.ts) only where a group element would be misread, describes, tests, regenerated spec artefacts and reference docs, the ui/packages/types mirrors only where they are exhaustive copies, changeset (@objectstack/spec minor). The console query build (ui/packages/plugin-list) is filed as a domain:ui follow-up card by this PR's dev, not taken here (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: TIER_DEFAULT (opus) — the quota-exhaustion exemption (claude-fable-5-1 answered HTTP 429; maintainer 「你没有fable了,使用 opus开发完之后等契约复审」), effort: medium
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: a stored list view filter is AND-only, so an any-of view ("open, or completed in the last 7 days") cannot be authored | an any-of group element in the view filter rule list, judged at parse like a rule | every list view, tab, related list, page base filter and picker filter whose natural filter is an any-of (HotCRM, #27386 row 1)
    Thread-read: none
    Premise: holds — on 22ce3d13fe ListView.filter (view.zod.ts:3072) is z.array(ViewFilterRuleSchema), and ViewFilterRuleSchema (view.zod.ts:1003) is a strict { field, operator, value } object with no group member
    Serial constraints cleared: open PRs touching view.zod.ts are #28343 (the kanban block, L2094) and #28334 (the calendar block, L2278); neither touches ViewFilterRuleSchema or a filter carrier. #28334 and #28315 touch plugin-list, which this card does not take

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
    os-dev-report

    {
     "issue": 28020,
     "status": "done",
     "pr": 28380,
     "head_sha": "68b811655830245edb343e45ed08b7b2590d131a",
     "branch": "claude/issue-28020-view-filter-or-group",
     "gates": {
      "derived_at": "68b8116558",
      "derived": 110,
      "run": 110,
      "exit_0": 110,
      "not_measured": 0,
      "unrun": 0,
      "ran_verdict_exit": 0,
      "first_run_exit_3": [
       {
        "command": "pnpm --filter @objectstack/spec run check:skill-examples",
        "cause": "packages/client-react/dist absent",
        "rerun_exit": 0,
        "after": "pnpm --filter @objectstack/client-react... build"
       },
       {
        "command": "pnpm check:dual-build-cjs-loads",
        "cause": "34 package dist/ absent",
        "rerun_exit": 0,
        "after": "turbo run build --filter=!@objectstack/docs (116/116)"
       }
      ],
      "local_only": {
       "check:dev-prereqs": 0,
       "check:nul-bytes": 0
      },
      "tests_post_merge": {
       "@objectstack/spec": {
        "files": 773,
        "tests": 22857,
        "failed": 0
       },
       "@objectstack/lint (targeted)": {
        "files": 4,
        "tests": 263,
        "failed": 0
       },
       "@objectstack/metadata-protocol (targeted)": {
        "files": 1,
        "tests": 3,
        "failed": 0
       }
      },
      "tests_pre_merge": {
       "@objectstack/lint": {
        "files": 183,
        "tests": 7693,
        "failed": 0,
        "typecheck": 0
       },
       "@objectstack/metadata-protocol": {
        "files": 285,
        "failed": 0,
        "typecheck": 0
       },
       "ui type-check (spec dependents)": "green after pin moves",
       "ui pin tests": {
        "files": 6,
        "tests": 184,
        "failed": 0
       }
      },
      "regen": {
       "gen:schema": 0,
       "gen:docs": 0,
       "check:generated --fix": 0,
       "check:liveness": 0,
       "check:authorable-surface": 0
      }
     },
     "line_budget": {
      "stated": null,
      "total": {
       "files": 38,
       "added": 1243,
       "removed": 122
      },
      "handwritten_excl_generated": {
       "added": 881,
       "removed": 46
      },
      "tests": {
       "added": 491,
       "removed": 14
      }
     },
     "deviations": [
      {
       "what": "ui/packages/types/src/__tests__ type-parity pins edited (6 files)",
       "why": "widening ViewFilterRuleSchema carriers flips Equal<> pins; mirror source record-components.ts left flat"
      },
      {
       "what": "os-regen-merge.sh step 3 finished by hand",
       "why": "its commit refused on stale docs after merge 669502d4fc; rebuilt spec, gen:docs, check:generated --fix, committed 68b8116558"
      },
      {
       "what": "full ui type-check ran pre-merge only",
       "why": "post-merge reran spec/lint/metadata-protocol tests and the 110 gates; ui pin files untouched by the merge"
      },
      {
       "what": "BlueprintViewSchema.filters not widened",
       "why": "strict mirror cannot carry a union; applier is outside the repo"
      },
      {
       "what": "origin/main advanced 7 commits after the head; not re-merged",
       "why": "git merge-tree clean; packages/spec/src/ui/view.zod.ts and packages/lint/src/validate-list-view-field-refs.ts touched upstream"
      }
     ],
     "key_spelling": {
      "key": "any",
      "mirrored_existing": false
     },
     "readers": {
      "updated": [
       "packages/lint/src/filter-walk.ts",
       "packages/lint/src/validate-preset-comparands.ts",
       "packages/lint/src/validate-page-field-bindings.ts",
       "packages/metadata-protocol/src/metadata-diagnostics.ts"
      ],
      "unchanged_verified": [
       "packages/metadata-protocol/src/protocol.ts#graftNormalizedOperators",
       "packages/lint/src/validate-field-consumers.ts",
       "packages/lint/src/validate-empty-combinators.ts"
      ],
      "planned_not_rendered": {
       "follow_up": 28370,
       "readers": [
        "ui/packages/plugin-list ListView buildEffectiveFilter",
        "ui/packages/core filter-converter convertFiltersToAST",
        "ui/packages/data-objectstack objectFilterEntriesToAST",
        "ui/packages/app-shell viewFilterFold",
        "ui/packages/plugin-view parseSpecFilter"
       ]
      }
     },
     "files_changed": [
      ".changeset/28020-view-filter-any-group.md",
      "content/docs/references/api/protocol.mdx",
      "content/docs/references/data/object.mdx",
      "content/docs/references/index.mdx",
      "content/docs/references/ui/component.mdx",
      "content/docs/references/ui/page.mdx",
      "content/docs/references/ui/view.mdx",
      "docs/audits/2026-07-unknown-key-strictness-ledger.counts/ui.md",
      "packages/lint/src/filter-walk-any-group.test.ts",
      "packages/lint/src/filter-walk.ts",
      "packages/lint/src/validate-page-field-bindings.test.ts",
      "packages/lint/src/validate-page-field-bindings.ts",
      "packages/lint/src/validate-preset-comparands.test.ts",
      "packages/lint/src/validate-preset-comparands.ts",
      "packages/metadata-protocol/src/metadata-diagnostics.ts",
      "packages/metadata-protocol/src/metadata-diagnostics.view-filter-any-group.test.ts",
      "packages/spec/api-surface/ui.json",
      "packages/spec/authorable-surface/ui.json",
      "packages/spec/declaration-map/ui.json",
      "packages/spec/dropped-refinements.baseline.json",
      "packages/spec/export-origins/ui.json",
      "packages/spec/json-schema.manifest/ui.json",
      "packages/spec/liveness/view.json",
      "packages/spec/src/ai/solution-blueprint.zod.ts",
      "packages/spec/src/conversions/registry.ts",
      "packages/spec/src/ui/component.zod.ts",
      "packages/spec/src/ui/filter-rule-array-guidance.test.ts",
      "packages/spec/src/ui/filter-rule-array.ts",
      "packages/spec/src/ui/page.test.ts",
      "packages/spec/src/ui/page.zod.ts",
      "packages/spec/src/ui/view-filter-any-group.test.ts",
      "packages/spec/src/ui/view.zod.ts",
      "ui/packages/types/src/__tests__/gantt-flat-config-declared-keys.test.ts",
      "ui/packages/types/src/__tests__/grid-default-filters-gantt-map-filter-round10-6152.test.ts",
      "ui/packages/types/src/__tests__/grid-kanban-calendar-members-round8-6152.test.ts",
      "ui/packages/types/src/__tests__/kanban-calendar-filter-sort-8174.test.ts",
      "ui/packages/types/src/__tests__/related-list-add-picker-filter-mirror-9964.test.ts",
      "ui/packages/types/src/__tests__/related-list-filter-rule-array-10199.test.ts"
     ],
     "mcp_calls": 0,
     "api_writes": [
      {
       "op": "issue_create",
       "via": "scripts/pm/issue-create.mjs",
       "target": 28370
      },
      {
       "op": "pr_create",
       "via": "scripts/pm/fleet-write/dispatch.mjs",
       "target": 28380,
       "readback": "8186/8186 bytes identical"
      },
      {
       "op": "comment",
       "via": "scripts/pm/post-stamped.mjs",
       "target": 28020
      }
     ],
     "out_of_scope_findings": [
      {
       "class": "consumer-gap",
       "reach": "objectstack-ai/objectstack ui/packages (console list query and filter builder)",
       "evidence": "#28370: no console reader applies { any: [...] }; filed as the consumer half"
      },
      {
       "class": "conversion-widening",
       "reach": "packages/spec/src/conversions/registry.ts",
       "evidence": "a stored $or record is not converted to an any-of group; doing so widens the conversion"
      },
      {
       "class": "mirror-lag",
       "reach": "ui/packages/types/src/record-components.ts + plugin-detail props",
       "evidence": "related-list mirror stays flat ViewFilterRule[]; pinned as Exclude<contract element, ViewFilterAnyGroup>"
      }
     ],
     "seat_decisions": [
      "ratify stored shape { any: [rule, ...] } one level and key name any (maintainer)",
      "BlueprintViewSchema.filters stays flat rules",
      "related-list ui mirror stays flat until #28370 vs widening with plugin-detail props"
     ]
    }
  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Landed · 2026-10-31T16:43Z · PR #28380 merged by the queue at 2026-10-31T16:43Z as f4a8eede37; the card closed by Fixes. This seat strips pm:dispatched in the same act. Shape A, key or by the maintainer's ruling (decision card 2026-10-31T14:23:54Z, recorded again 6545957296): a view filter list may hold { or: [rule, ...] } beside plain rules, one level, never empty. Contract review: PASS 6541345546, delta PASSes 6542065823, 6543859741 and 6545981353 on the landed head. Until the console half #28370 lands, a view that stores an or group fails closed (the list reports a malformed filter and shows no rows). #28370 is unblocked by this landing and goes to pm:queue.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions