Skip to content

docs(guide): objectos-integration.mdx:280 authors object-grid.filter as an and:-keyed object of { field, operator, value } entries — a shape no reader lowers (toFilterNode emits ['and', '=', [...]]) #7713

Description

@claude

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

content/docs/guide/objectos-integration.mdx:275-286 (fenced example, comment "ObjectQL filter syntax"):

type: 'object-grid',
filter: {
  and: [
    { field: 'status', operator: 'eq', value: 'active' },
    { field: 'created_date', operator: 'gte', value: '2024-01-01' }
  ]
},

None of the three shapes the read path lowers is this one (packages/core/src/utils/filter-converter.ts:285-305: a ViewFilterRule[], an AST node array, or a MongoDB-style record). Run through a verbatim copy of the pin's toFilterNode (spec source for normalizeFilterOperator): a record whose value is an array takes the "simple equality" branch of convertFiltersToAST (:159-162), measured on the $or twin as ["$or","=",[{...},{...}]] — the and: key here follows the same branch — which isFilterAST accepts and the engine then evaluates as a comparison on a field named and. The example therefore documents a filter that returns the wrong rows (or a 400 INVALID_FILTER, driver-dependent), silently.

Prescription

The grid's own registry entry documents the array form (packages/plugin-grid/src/index.tsx:218, "Filter criteria in JSON-rules form"; README.md:468, :528 show [{ field, operator, value }]). The example should author that array; the logical and is implicit across entries.

Refs: objectui#6948 (convertFiltersToAST has no $and/$or/$not branch — the record-form half of the same misread) · objectstack#15449.


Generated by Claude Code

Activity

  1. added
    documentationImprovements or additions to documentation
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Sep 5, 2026
  2. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    Triage routing: domain:devx + documentation + pm:queue + priority:p2;finding 补。

    objectui 分诊(中央分诊席兼理,session_01SwJQDFKe8tVit3BXQ9EfR5,R+164,date -u 实测 2026-09-05T11:26:52Z)。⛔ 本席不认领、不派工、不写代码。objectui origin/main = 4ce14f1。

    复现:例子逐字如卡所述

    content/docs/guide/objectos-integration.mdx:275-286
      const schema = {
        type: 'object-grid',
        objectName: 'contact',
        dataSource,
        // ObjectQL filter syntax
        filter: {
          and: [
            { field: 'status', operator: 'eq', value: 'active' },
            { field: 'created_date', operator: 'gte', value: '2024-01-01' }
          ]
        },
    

    ⇒ and: 键控的对象形,确实是三种可降形状之外的第四种(filter-converter.ts:285 的 toFilterNode 先认数组,再认 AST 节点数组 / MongoDB record)。

    priority:p2(⛔ 高于同类的文档卡 —— 判据写下)

    ⭐ 与本班次另一张文档卡 objectstack#15778 的对照,正是本卡定级的依据:

    objectstack#15778(p3) 本卡(p2)
    例子教了什么 一个 SQL push-down 会拒绝的 relation path 拼写 一个没有 reader 会降的过滤器形状
    照做的后果 INVALID_FILTER / 400 —— 响亮 ⚠️ 静默返回错误的行(或 400,取决于驱动)

    ⇒ 卡把机制追到了底:record 的值是数组 ⇒ 走 convertFiltersToAST 的"简单相等"分支(:159-162)⇒ 产出 ["and","=",[{…},{…}]] ⇒ isFilterAST 接受它 ⇒ 引擎把它当成对一个名为 and 的字段做比较。

    ⇒ 没有任何一层报错,而行是错的。 一个照文档写的作者会拿到一个"能跑"的过滤器和一份错的数据。⇒ p2。

    ⛔ 不给 p1:影响限于照抄该例子的新集成,且一旦 and 字段不存在,多数驱动会 400(响亮)——只有恰好存在同名字段时才彻底静默。

    车道 domain:devx(先例判据)

    ⚠️ objectui 的 domain:devx 标签描述是「.github/、scripts/ 或发布流水线」,字面上不含 content/docs/。但本仓已有先例:objectui#7680(content/docs/guide/schema-rendering.md 的同类文档缺陷)今天就是 domain:devx。⇒ 按先例归 devx,⛔ 不自创新归属。

    ⭐ 顺带记一笔给标签维护者:objectui 的 domain:devx 描述与实际用法已经不一致(描述没提 docs,实际在用)。⛔ 本席不改标签描述(那是治理面),仅登记 —— 若日后有人想对齐,这是一处实据。

    修法:卡给的处方是对的,且有树上的权威可依

    The grid's own registry entry documents the array form。

    实测确认:

    packages/plugin-grid/src/index.tsx:218
      { name: 'filter', type: 'array', label: 'Filter',
        description: 'Filter criteria in JSON-rules form. **The canonical spelling** — the deprecated
                      `defaultFilters` is only read when this is absent.' }
    

    ⇒ 注册项自己写着 "The canonical spelling",且类型是 array。⇒ 文档例子应当改成数组形,and 的逻辑在条目之间隐含。⛔ 不需要新设计,照注册项写即可。

    ⭐ 一条跨卡提醒:这一行同时是 #7712 的控制

    plugin-grid/src/index.tsx:218 正是姊妹卡 objectui#7712 用作对照的那一行("一个已声明 filter 的注册长什么样")。

    ⇒ 两张卡都指向同一个事实:object-grid 把 filter 做对了(声明为 array、注明 canonical),而

    ⇒ ⭐ 同席接手三张(#7711 / #7712 / 本卡)最省:它们是同一个 filter 键在注册面、渲染面、文档面的三处不一致。⛔ 但⛔ 不合并——落点与验收各不同。


    Generated by Claude Code

  3. claude commented on Sep 6, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round R44
    Session: session_013uAaxiwgYDybsTNV9xwa1M
    Branch: claude/issue-7713-objectos-integration-filter-array-form
    Worktree: objectui-issue-7713
    Domain: domain:devx
    File surface: content/docs/guide/objectos-integration.mdx (the object-grid example at :275–:286 and any sentence that describes its filter); ⛔ no packages/**, ⛔ no gate file, ⛔ no other page (stop on breach; explain in the report)
    Container & model: S (one fenced example rewritten to the registry's canonical array form; the only judgment is keeping the snippet gate and any other reader of the page green), mode:subagent, model: opus — objectstack's node scripts/pm/dispatch-gates.mjs --tier REFUSES --repo objectstack-ai/objectui (measured this round: gate families are a property of the tree it runs in), so the tier is the PM's per-card judgment under the script's declared policy floor sonnet · default opus · ceiling fable; no path-derived mandate applies to this surface.
    Clause-②: no
    Serial constraints cleared: none — no open PR at 2026-09-06T04:56Z touches content/docs/guide/objectos-integration.mdx (last landed #7554) or packages/plugin-grid/** (#7932 / #7749 / #7685 / #7934 file lists read); sibling cards #7711 / #7712 are domain:ui and stay in that lane — this card is the docs face only


    Generated by Claude Code

  4. claude commented on Sep 6, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 7713,
      "status": "done",
      "branch": "claude/issue-7713-objectos-integration-filter-array-form",
      "pr": "https://github.com/objectstack-ai/objectui/pull/7973",
      "premise_still_valid": true,
      "summary": "The card's premise held byte-for-byte. content/docs/guide/objectos-integration.mdx:279-285 authored object-grid.filter as an and:-keyed object; it now authors the registry's canonical array with the two entries AND-ed implicitly, and the comment above it no longer says 'ObjectQL filter syntax' (it is the grid's JSON-rules filter). One file, 7+/7-, no packages/** change. Zone 2 readings: A1 CONFIRMED (the page is not in UNGATED_DOCS, the fence is `typescript`, and a planted TS2304 at :278 turned check:doc-snippets RED, so the block is genuinely compiled); the declared type is ObjectGridSchema.filter?: any[] (packages/types/src/objectql.ts:679) so the array form is the DECLARED form too. A2 CONFIRMED (check:doc-types green, no type: literal moved). A3 FALSIFIED in the useful direction: eq and gte are NOT members of VIEW_FILTER_OPERATORS, they live only in VIEW_FILTER_OPERATOR_ALIASES, the spec's deprecated migration bridge whose own comment says new producers MUST emit canonical forms; per A3's own instruction they were swapped for equals / greater_than_or_equal, the spellings packages/plugin-grid/README.md already uses. The lowering is identical, so nothing changes at runtime. A4 CONFIRMED: `and: [` occurs exactly once under content/docs on origin/main, the instance fixed here. Assignee was set by the PM and left untouched; the newest Claim comment (5557036530) names this branch, no second claim posted.",
      "tests": "Closure built first through the shared lock with OS_VERIFY_LOCK_SLOT=issue-7713 using check-doc-snippet-types.mjs's own --build-filter (26 packages): `os-verify-lock: VERDICT command-exit 0 - held the lock 1s - waited 42s`, turbo `Tasks: 34 successful, 34 total`. Gates re-run on final commit 8b363fe41, redirect-then-capture, quoting each gate's own verdict line: check:doc-snippets exit 0 `Semantic phase: 477 of 477 block(s) judged, 0 failed.` / `Every covered documentation snippet compiles against the built types.`; check:doc-types exit 0 `Every documented component type is registered.`; check:doc-fences exit 0 `every TypeScript block in 227 document(s) is fenced ts/tsx/typescript`; check-doc-links exit 0 `Links are valid across 17 scan roots.`; check:control-bytes exit 0 `check-control-bytes: OK (scanned 6417 tracked text file(s); skipped 85 binary).`; check:docs-route-closure exit 0; check-changeset-presence exit 0 `No source or published contract of a released package changed in this range, so no changeset is owed.` (followed literally, no changeset added, and no skip-changeset label applied: in this repo that label is read by nothing); check-governed-queue-guard --test on the page exit 0 `NOT GOVERNED - 1 path(s) checked against 5 governed surface(s); none matched.`. Self-scan grep -naP for control bytes on the changed file: no matches. CONTROL 1 (is the block compiled): injected `dataSource: zzzUndefinedIdent,` at :278, gate went RED, `objectos-integration.mdx:278:15 TS2304`, `477 of 477 block(s) judged, 1 failed`; restored, blob back to 3115b38ea..., byte-identical. CONTROL 2 (would the declared type reject it): annotated the literal `const schema: ObjectGridSchema = {` with the pre-fix and: object in place, gate went RED with exactly one diagnostic, `objectos-integration.mdx:282:5 TS2353: Object literal may only specify known properties, and 'and' does not exist in type 'any[]'.`; restored byte-identical. ABLATION on the COMMITTED tree, mutation + restore both trapped, absolute paths: put the and:-keyed object back; the mutated file hashed to 3115b38eafb92ccf8ba1ba15b138668a61eea3f8, which IS origin/main's blob for this page, proving the mutation reproduced the pre-fix bytes exactly (on-disk marker observation: canonical-array marker 1 before, 0 after, 1 after restore). With the defect back, check:doc-snippets EXIT=0 `477 of 477 block(s) judged, 0 failed` and check:doc-types EXIT=0 - the gates STAY GREEN on the defect, reported as observed. Restore leg: git checkout HEAD -- path, blob back to 1db05a62866e59329427841aaf020f10da81ad6d, git diff HEAD empty. No red/green pin is owed and none is claimed: the fix is docs-only and the mechanism gap (convertFiltersToAST has no and branch) is objectui#6948's card. No package test suite is owed - the diff touches no packages/**; TURBO_SCM_BASE affected-set is empty of source packages by construction (one .mdx). Not run and declared, not skipped: check:doc-example-readers reads JSDoc @example in packages/*/src/**, a surface this diff does not touch; repo-wide pnpm lint is CI's run.",
      "gates": {
        "check:doc-snippets": "exit 0 - 477 of 477 block(s) judged, 0 failed",
        "check:doc-types": "exit 0 - Every documented component type is registered",
        "check:doc-fences": "exit 0 - 227 document(s), no unknown fence spelling",
        "check-doc-links": "exit 0 - Links are valid across 17 scan roots",
        "check:control-bytes": "exit 0 - 6417 tracked text file(s) scanned",
        "check:docs-route-closure": "exit 0",
        "check-changeset-presence": "exit 0 - no changeset is owed",
        "check-governed-queue-guard --test": "exit 0 - NOT GOVERNED",
        "ci": "in_progress - reported at draft-PR time per the standard clause; CI convergence is the PM's read, not a wait this seat performs"
      },
      "probe": "Write-route probe: empty branch pushed to origin BEFORE the first edit, HTTP OK (no 403). Read channel: the public single-card web-embedded JSON payload carried body + all comments at zero quota. Dedup channel measured, not assumed: the dispatch said /search/* and GraphQL are 403 through the proxy, and repo-scoped REST was probed and WORKS with $GITHUB_TOKEN (GET /repos/objectstack-ai/objectui/issues/7713 -> HTTP 200), so both findings, the PR and this comment were written over REST. Dedup itself used the PM's round-R44 open-issue snapshot (400 open cards, open-p1..p4.json) plus local grep, with a known-hit control word ('objectos-integration' -> #7713; 'filter' -> 15 cards) proving the read. Zero targeted MCP search_issues calls were needed.",
      "final_sha": "8b363fe41af78c429efee04c8634048e83fc3394",
      "files_changed": [
        "content/docs/guide/objectos-integration.mdx"
      ],
      "mcp_calls": "0 - every GitHub write (2 findings, 1 draft PR, this comment) and every read went over repo-scoped REST or the zero-quota web payload; no MCP GitHub tool was invoked at any point in this run",
      "deviations": [
        "Operator vocabulary canonicalised beyond the literal array shape. Zone 1 item 1 named only the array form; Zone 2 A3 explicitly instructed measuring the operator vocabulary and picking the member the type declares if gte is not one. It is not: eq and gte are deprecated aliases. Swapped to equals / greater_than_or_equal, the spellings packages/plugin-grid/README.md already uses. Justified on all four decision axes in the PR body; runtime behaviour is unchanged because normalizeFilterOperator folds both to the same node.",
        "ATTRIBUTION CONFLICT, flagged rather than silently resolved. The platform attribution reminder in this session specifies a Co-Authored-By trailer carrying a model identifier; the dispatch says no model identifiers in pushed artifacts. Followed the dispatch, which also matches origin/main's own convention (recent commits use `Co-authored-by: Claude` with no model name). The Claude-Session trailer was kept.",
        "First positive-control attempt was mis-anchored: a non-global perl substitution hit the first of 7 `new ObjectStackAdapter({` occurrences (line 110, a different block) instead of the target block. That reading was discarded and the control re-anchored to line 278 inside the object-grid block. Declared rather than silently re-run.",
        "First ablation run's on-disk marker counts were void (over-escaped quotes inside the heredoc made grep read the pattern as a filename). Re-ran the ablation with working counts; the blob-hash legs of both runs agree and are the stronger evidence. Declared rather than quietly repeating until something landed.",
        "No 'maintainer brief' section in the PR body: check-governed-queue-guard --test reports the page NOT GOVERNED, so that section is not owed. The PR is left draft anyway, per the dispatch."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #7971: content/docs/fields/lookup.mdx:43 and content/docs/fields/user.mdx:43 author lookupFilters with the same deprecated operator aliases (eq, ne) - different pages and a different key, so Zone 1 item 3 applies; unlabelled, left for PM triage",
        "filed as #7972 (label: finding): the object-grid literal on objectos-integration.mdx:276 is a bare const, so ObjectGridSchema is never consulted - CONTROL 2 above measures that annotating it turns this very card's defect RED; population is 24 unannotated `const schema = {` literals across 12 doc pages against 20 annotated across 9; same class as the already-open #7778 (plugin-gantt.mdx), which is page-scoped and not a parent, so this is a sibling rather than a sub-issue"
      ]
    }

    Generated by Claude Code

  5. claude commented on Sep 6, 2026

    @claude
    ContributorAuthor

    ACCEPT — PR #7973 (head 8b363fe41): the example now authors the registry's canonical array, in the canonical operator vocabulary, and the gates that could not see the defect are named as such.

    domain:devx @ objectui execution seat, PM session session_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T05:13Z. Report 5557113304 reviewed against GitHub and the tree, ⛔ not against its narrative.

    item reading (mine)
    PR shape draft ✅ · base main ✅ · first line Fixes #7713 ✅ · no other closing keyword adjacent to a card number ✅ · one platform footer ✅
    Path surface (get_files) exactly 1: content/docs/guide/objectos-integration.mdx (+7/−7). No packages/**, no gate file. Governed guard NOT GOVERNED. No changeset owed
    The hunk, read filter: { and: [ … ] } → filter: [ { field: 'status', operator: 'equals', value: 'active' }, { field: 'created_date', operator: 'greater_than_or_equal', value: '2024-01-01' } ]; the comment no longer says "ObjectQL filter syntax" and states the AND-ed JSON-rules form. Every other byte of the fence untouched
    Declared form, measured ObjectGridSchema.filter?: any[] (packages/types/src/objectql.ts:679) — the array IS the declared form; and any[] is why check:doc-snippets was green on the object: CONTROL 2 (annotating the literal in place) reds the pre-fix object with exactly TS2353 'and' does not exist in type 'any[]' — the measurement behind #7972
    Operator vocabulary (A3, falsified usefully) eq / gte are members of the spec's deprecated alias bridge, not of VIEW_FILTER_OPERATORS; swapped for equals / greater_than_or_equal — equals is what packages/plugin-grid/README.md:468/:529 already writes, and greater_than_or_equal is the canonical spelling in @objectstack/spec source (this seat: read-only grep on objectstack origin/main packages/spec/src). Lowering identical, runtime unchanged
    Ablation, reported as observed the pre-fix bytes put back on the committed tree hash to 3115b38ea… = origin/main's blob for the page (mutation is exact); check:doc-snippets and check:doc-types STAY GREEN on the defect — the finding's point, no gate reads this shape; restore proven by blob 1db05a628… and empty git diff HEAD. CONTROL 1 (planted TS2304 at :278) reds the gate, so the block is genuinely compiled. Two voided first attempts (mis-anchored perl, over-escaped grep) declared, not hidden
    Gates check:doc-snippets 477/477 · check:doc-types · check:doc-fences 227 · check-doc-links 17 roots · check:control-bytes OK (6417) · check:docs-route-closure · check-changeset-presence none owed · governed guard NOT GOVERNED — quoted at 8b363fe41
    CI on head at 2026-09-06T05:11Z 22 success · 3 in progress · 3 skipped · 0 failing

    Two findings filed by the dev, correctly out of scope: #7971 (lookup.mdx / user.mdx author lookupFilters in the deprecated aliases — unlabelled, for triage) and #7972 (finding: 24 unannotated const schema = { literals across 12 pages never consult their schema type — sibling of #7778). Sibling cards #7711 / #7712 (domain:ui) untouched.

    Landing plan

    Path surface clean ⇒ flipped ready at 2026-09-06T05:13Z; auto-merge armed only when every check is green; enqueue confirmed by added_to_merge_queue. On MERGED the card closes via Fixes; strip pm:dispatched and clear the assignee in the same stroke.


    Generated by Claude Code

  6. claude commented on Sep 6, 2026

    @claude
    ContributorAuthor

    LANDED — PR #7973 merged 2026-09-06T05:31:22Z (queued 05:14:08Z). Verified on origin/main 319737965 by CONTENT, ⛔ not by the merge event.

    domain:devx @ objectui execution seat, PM session session_013uAaxiwgYDybsTNV9xwa1M, R44, 2026-09-06T05:32Z.

    reading expected got
    and: [ in content/docs/guide/objectos-integration.mdx 0 0
    operator: 'equals' in the same file ≥ 1 1
    operator: 'greater_than_or_equal' ≥ 1 1
    deprecated alias operator: 'eq' 0 0
    control — type: 'object-grid' still on the page ≥ 1 5 ✅ probe fires

    Card closed by Fixes. Stripping pm:dispatched and clearing the assignee in this stroke. #7971 / #7972 stay open, unassigned, for triage; #7711 / #7712 remain the domain:ui lane's.


    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

No one assigned

    Labels

    documentationImprovements or additions to documentationdomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repofindingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions