Skip to content

Drill "Open in list" and related-list "View all" are withheld when the drill holds a boolean, $ne, $in or text-operator condition: the list route's URL cannot carry them #30743

Closed

Description

@objectstack-fleet

domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

Filing gate: ③ an out-of-scope finding the #30654 card thread declared in draft PR #30735 (head 7519c053, os-dev-report 6672865626) and the seat's ACCEPT names. ⛔ Not a claim. Triage sets the grade and the lane.

What happens

The object list route carries its filter as filter[FIELD]=VALUE plus the range, is-null and is-empty params (ui/packages/app-shell/src/views/drillUrlFilters.ts). exactRecordListFilter (ui/packages/app-shell/src/views/useOpenRecordList.ts, objectstack#25888) offers a list destination only when every condition fits that grammar. It refuses a boolean, null or Date comparand, $ne, $in / $nin, the text operators, $or, and two different comparands on one field.

A related list's "View all" has followed that rule since #25888. After PR #30735 (#30654), every drill escape hatch follows it too: the drawer's "Open in list", drillDown.target: 'navigate', and the chart's own header button. That is the right call, because a wider list is no destination. The side effect is that a drill on a boolean bucket (a chart grouped by an is_active-style field), or under a widget filter or measure scope using $ne or $in, now stays in the drawer with no way to the full list, where before it opened a list that was sometimes the right one and sometimes wider.

Wanted

  • Measure which of these the list page (ObjectDataPage, parseUrlFilterTriples) can read back EXACTLY once written, and give the route a spelling for the ones it can: a boolean comparand and $in are the likely first two (a boolean bucket and a measure scope like status in (...) are common drills).
  • Each new spelling joins exactRecordListFilter's accepted set with its own round-trip proof (written alone, reads back as exactly one triple), so the drill exits and "View all" regain a destination together.
  • Nothing that cannot round-trip is accepted: no destination stays better than a wider list.

Reader: domain:ui (ui/packages/app-shell/src/views/drillUrlFilters.ts, useOpenRecordList.ts, ObjectDataPage's URL read).
Dedupe: exactRecordListFilter, list route URL filter $in, filter boolean list route (is:issue is:open) → only #30654 itself.

Activity

  1. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:ui · 2026-11-06T00:55Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:uiDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    Claim: PM loop round 8 · 2026-11-06T01:42Z
    Session: session_01L6bhgWn6zhNa4LL7VB1rtL
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: the card thread's designated claude/project-thread-* branch, named in a claim amendment once the thread reports it
    Worktree: objectstack-issue-30743
    Domain: domain:ui
    Seat: domain:ui#1 (the card's digit 3 is domain:ui#6's; that seat is offline since 01:34Z, and Sales left this seat as the lane's only one at 01:39Z)
    File surface: the object list route's URL filter grammar and its two readers in ui/packages/app-shell/src/views/: drillUrlFilters.ts (the write side serializeDrillFilterParams :361 and the read side parseUrlFilterTriples :254, on adf6ee094), useOpenRecordList.ts (exactRecordListFilter :80, whose accepted set grows only by spellings that round-trip), ObjectDataPage.tsx (where the parsed triples become the list's filter, :282, and its operator bridge to the spec's alias spelling), the filter chips that render those triples (groupFilterChips :502 and their i18n copy if a new operator needs a chip label), their tests; one root .changeset/ file if a published package moves. ⛔ packages/** (the server's filter grammar does not move), ⛔ ui/packages/react (the DrillNavigationValue contract landed in PR #30735 and does not change), ⛔ ui/packages/i18n/src/locales/*.ts (busy with PR #30640; a new chip label goes in a namespace the dev names or waits). Stop on breach and explain in the report.
    Container & model: M, mode:thread, model: default (opus) (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: high
    Clause-②: yes (widening) — the list route's URL, a surface users bookmark and share, gains spellings it did not accept, and exactRecordListFilter's accepted set grows; the seat's TIER_DEFAULT review is owed at ACCEPT. Re-read at ACCEPT.
    Metadata-protocol: none
    Responsibility: after a user drills into a boolean bucket or an $in scope, "Open in list" and a related list's "View all" open exactly those rows | both are withheld for those drills since PR #30735 (and "View all" since #25888), because the route cannot say the filter | every Console user drilling a dashboard chart grouped by a yes/no field, or a measure scoped to a set of statuses
    Thread-read: 6673676079
    Premise: holds on origin/main adf6ee094: the route's grammar is equality to a non-empty string or finite number, the four range bounds, [null] and [empty] (drillUrlFilters.ts:20-27, :64, :73); exactRecordListFilter (useOpenRecordList.ts:80) refuses a boolean, null or Date comparand, $ne, $in and the text operators, and proves every accepted condition by a one-triple round trip through parseUrlFilterTriples. The dev measures first, per candidate (boolean comparand, $in, then $ne and the text operators), whether a written param reads back in ObjectDataPage as the SAME comparison the drill made (type included: a boolean must not come back as the string 'true'), and adds a spelling only for those that do; each new spelling joins the accepted set with its own round-trip pin, and anything that cannot round-trip stays refused. The spelling must not collide with a field named like an operator suffix (the URL_FILTER_OPS prototype-free lookup, :64, stays).
    Out of scope: the server's query grammar; the chart's drill scopes (landed in #30615 / #30654).
    Serial constraints: the file lists of the 45 open PRs read at claim time: none touches drillUrlFilters.ts, useOpenRecordList.ts or ObjectDataPage.tsx. PR #30595 (#30511) adds ObjectDataPage.filterChipI18n-9159.test.tsx lines; whichever lands second merges the other.

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  5. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    席位核对 · Amendment · 2026-11-06T01:47Z · claim 6674571486 branch named

    Branch: claude/project-thread-7ywrg2 (the card thread's designated branch; session session_01L6bhgW9yrPaVhdtD84RWcR), worktree objectstack-issue-30743, cut from origin/main b3098661. Everything else in claim 6674571486 stands.

  6. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgW9yrPaVhdtD84RWcR
    os-dev-report

    {
      "issue": 30743,
      "status": "done",
      "branch": "claude/project-thread-7ywrg2",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30777",
      "head": "ed322918",
      "session": "session_01L6bhgW9yrPaVhdtD84RWcR",
      "premise_still_valid": true,
      "summary": "Measured first on b3098661 through toFilterNode, parseFilterAST, serializeDrillFilterParams and parseUrlFilterTriples against convertFiltersToAST: a bare boolean wrote filter[F]=true and read back as the STRING 'true'; $in, $nin, $ne and the five text operators wrote no param; all 18 probed cases answered exactRecordListFilter null. The list route now has filter[F][bool]=true|false (only those two spellings, read back as the boolean), filter[F][ne|contains|notcontains|startswith|endswith|icontains]=V and repeated filter[F][in|nin]=V gathered into one triple. exactRecordListFilter accepts them and proves each condition by a one-param-name round trip whose triple must equal convertFiltersToAST(condition), finite numbers allowed to read back as text; $or, $between, an empty set and a boolean $ne stay refused. Chips draw the filter builder's existing operator label beside the comparand; Save as view folds every new operator to its canonical word.",
      "tests": "Targeted, from ui/, 21 files: drillUrlFilters, exactRecordListFilter-25888, RelatedRecordActionsBridge viewAll-12741 and viewAllFilter-25888, useOpenRecordList.placeholderScope-9022, ObjectView.urlFilterSuffix-9196, ObjectView.filterParamsKeyEncoding-9287, every ObjectDataPage.* and drill* suite in app-shell views, studio-canvas-preview, plugin-dashboard DatasetWidget and ObjectPivotTable.drillFilterComposition-9024: Test Files 21 passed (21), Tests 383 passed (383) on ed322918. Before the pin moves: 7 failed | 339 passed, exactly the seven moved pins. type-check @object-ui/app-shell (both tsc runs) after building the app-shell^... closure: 0 error TS, VERDICT command-exit 0. Ablation via scripts/ablation-replace.mjs on the committed fix: [bool] read arm pushes the string 'true' (anchor 1 to 0, blob 35873c7f to 0120ef72): 3 failed | 116 passed (drillUrlFilters [bool] read-back, exactRecordListFilter new-spellings round trip and drawer-node equality); restored, blob == HEAD, git diff HEAD empty. Tests import source relatively, no dist leg. Declared narrowing: the remaining app-shell test files go to CI Test UI.",
      "gates": "dispatch-gates --commands on ed322918: 35 families, every one exit 0 (pnpm --dir ui exec vitest run scripts/: 4970 passed | 2 skipped; its PREREQUISITE NOT MET and chunk lines are fixture stdout inside that passing run). --ran with exit codes: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN (derived zero). check:nul-bytes exit 0. CI check-runs on ed322918 at report time: 44 success, 11 skipped, 0 failure, more still queued (in_progress).",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no GitHub MCP tool called; reads via gh api",
      "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 42150282723, body read back identical 11356 bytes) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/30743/comments) via post-stamped",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed: a boolean chip reads = true / = false (the raw comparand, the posture every equality chip has); a set of numbers reads back as text, as equality and the range bounds always have; both written in the PR Acceptance notes",
        "carrier: PR #30595 · serial: both PRs add lines to ObjectDataPage.filterChipI18n-9159.test.tsx (this one appends one describe at the end); whichever lands second merges the other"
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    ACCEPT → PR #30777 comment 6675455452 · head ed322918 · 2026-11-06T02:45Z — the list route gains [bool], [ne] and the five text suffixes, and repeated [in] / [nin], each read back as the node the drill drawer queries; "Open in list" and "View all" now offer the exact list for them. Contract review at TIER_DEFAULT (declaration face): PASS, 6675437187. pr_ready + automerge_enable relayed. Fixes: the card closes on the merge.

  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgWn6zhNa4LL7VB1rtL

    Landed 2026-11-06T03:28Z: PR #30777 merged as f8195bde6a (seat ACCEPT 6675455452 on head ed322918, contract review TIER_DEFAULT PASS 6675437187), 2026-11-06T03:27:33Z on the PR.

    probe reading
    git grep -n "export const BOOLEAN_FILTER" origin/main -- ui/packages/app-shell/src/views/drillUrlFilters.ts :265 (filter[F][bool]=true|false, read back as the boolean)
    git grep -n "export const URL_COMPARE_OPS" origin/main -- ui/packages/app-shell/src/views/drillUrlFilters.ts :285 ([ne] and the five text suffixes)
    git grep -n "export const URL_LIST_OPS" origin/main -- ui/packages/app-shell/src/views/drillUrlFilters.ts :313 (repeated [in] / [nin], one triple per field)
    git grep -n "function isSameComparison" origin/main -- ui/packages/app-shell/src/views/useOpenRecordList.ts :189 (every condition "Open in list" accepts must equal the node the drill drawer queries)

    State: the card closed on the merge (Fixes), and pm:dispatched is removed in the same stroke. Still refused by design: $or, $between, an empty set, a boolean $ne. Noted, not filed: the comment in ui/packages/plugin-detail/src/RelatedList.tsx (about lines 1705–1716) that calls URL_FILTER_OPS the one operator contract for this spelling is now a stale cross-reference; its conclusion still holds, because that raw-URL fallback never reads this grammar.

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

    bugSomething isn't workingdomain:uiDomain lane — seat card indexed by label:pm:seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions