Repository navigation
Description
Filing gate: ① product defect, reproduced, landing site named.
Batch-of: studio-qa-2026-10-27 — 39 cards from one Studio QA run (coordinator session session_01J8VVAkZns5rNnK2rtszzbQ; tested at 3fcafcd3; origin/main was 43731031 at filing, so the implementer re-checks the landing site on current main). The run record is the qa-run issue filed with this batch.
Symptom
The filter section is visible and the button responds, but nothing is added, so list views can only be filtered through the API.
Reproduction
/_console/studio/<pkg>/interfaces?surface=object:<obj>&nav=<nav>→ list-view inspector › Filter "+ Add filter…" → "Add filter" → no row (twice). Draft unchanged.- The same filter written via
PUT /api/v1/meta/view/…works end to end (L3 pass).
Expected
"Add filter" adds an editable condition row, held until complete.
Suspected landing site (the implementer verifies)
FilterBuilderField in ui/packages/app-shell/src/views/studio-design/widgets.tsx; ui/packages/app-shell/src/views/viewFilterFold.ts (~210) foldFilterGroupToSpecRules drops the blank row before it can be edited. Possibly a regression around the filter-fold work in closed #26306 / #26348.
Filing facts
- reach: Studio UI, measured twice by the view gap-fill card.
- Business reason: Filtering a list view is one of the most common authoring tasks; a designer that cannot do it fails the "designer must author what the protocol declares" rule (p1).
- Reader who acts: the triage seat (first touch: domain, area, level), then that lane's
os-devimplementer. QA suggestion only:domain:ui,area:studio, p1. - Dedupe: semantic search
list view inspector Add filter adds no row, filter cannot be authored in Studioon objectstack-ai/objectstack → 2 hits (open Page designer: a list-view block has no inspector fields, so its row cap (dataSource.limit) can only be set in JSON #26703 page-designer list block has no inspector fields — different surface; closed spec(ui): ListViewSchema.filterableFields is described as the userFilters.fields shorthand, but its one renderer reads it as the Filter panel whitelist; retire or redefine (objectui#13047) #24640). - QA id: U10
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
priority:p2·domain:ui·area:studio·pm:queue(findingremoved) · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T16:16Z⛔ Not a claim, ⛔ not a dispatch.
main.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 1
Session:
session_01QsR2jTfvjNTaR29ehLk26TAccount:
zhuangjianguoBranch:
claude/project-thread-jlfl08(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record)Worktree:
objectstack-issue-26738Domain:
domain:uiSeat:
domain:ui#4File surface:
ui/packages/app-shell/src/views/metadata-admin/widgets.tsx(FilterBuilderField),ui/packages/app-shell/src/views/viewFilterFold.ts, tests beside, one root changeset; ⛔packages/spec/**, server packages, any new key (stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate; reason: the maintainer's standing rule 「只认领不需要 fable 的卡」, 2026-10-27T10:39Z),effort: highClause-②: no
Metadata-protocol: none
Responsibility: the Studio list-view inspector's filter builder (app-shell, this lane) drops the blank row "Add filter" inserts · the platform path that may cover it: a filter written by API, which works · reached by every builder filtering a list view in Studio
Thread-read: 6447230679
Premise: holds (
git grep -n "export function foldFilterGroupToSpecRules" origin/main -- ui/packages/app-shell/src/views/viewFilterFold.ts→ line 197 on origin/mainc8583f80at 2026-10-27T16:51Z)Serial constraints cleared: no open PR touches the surface (scan of the 80 most recently updated open PRs 2026-10-27T16:51Z)
Dispatched at 2026-10-27T16:58Z to the card thread on that branch; the order lives in the thread's opening message.
Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
os-dev-report
{ "issue": 26738, "status": "done", "branch": "claude/project-thread-jlfl08", "pr": "https://github.com/objectstack-ai/objectstack/pull/26821", "session": "session_01QsR2jV9H4DGK4phGzhzdZ4", "premise_still_valid": true, "summary": "FilterBuilderField (metadata-admin widgets.tsx) re-derived the FilterBuilder row group from the stored rules on every render; foldFilterGroupToSpecRules correctly drops the blank row Add filter inserts, so the next host re-render erased it. The widget now holds the row group in its own state, writes only when the folded rules differ from the stored ones, and re-seeds from the stored rules when they change from outside (keyed on JSON, not identity). viewFilterFold.ts is unchanged, so incomplete rules still never reach the saved view. Regression origin from #26306/#26348 NOT MEASURED (shallow checkout); those touched builder value types, not this feed.", "tests": "head 5221c34d, under os-verify-lock: new widgets.filterBuilderHeldRow-26738.test.tsx Test Files 1 passed (1), Tests 4 passed (4). Reverse verification (fix committed, widgets.tsx restored to base c8583f80 in the tree only, groupOfStoredRules 3 to 0): Tests 3 failed | 1 passed (4), each red the vanished row; restored via git checkout HEAD (count back to 3, git diff HEAD empty). app-shell type-check (tsc --noEmit + tsc -p tsconfig.test.json) VERDICT command-exit 0, 0 TS errors. Narrowed app-shell run (52 files naming widgets / filter-builder-trigger / FilterBuilderWidget / viewFilterFold / ObjectListViewInspector / FilterModeWidget / filter-mode): 52 passed, 754 tests passed; rest of app-shell declared to CI Test UI.", "gates": "dispatch-gates --commands derived 34 at 5221c34d; all 34 run with exit captured before any pipe, all exit 0, plus pnpm check:changeset-fixed exit 0; --ran: 34 derived accounted for, 34 run, 0 NOT-MEASURED. vitest run scripts/ 178 passed | 2 skipped files, 5065 tests passed. check:new-line-citations 0 new. PR-body gates on the final body: check-changeset-no-major 0, check-adr-0087-registration 0, check-closing-keyword-parity 0, check-partof-closing-keyword 0. PR check-runs: in_progress at report time.", "line_budget": "215 changed lines (+201 / -14), 3 files, under the 3000 threshold", "files_changed": [ ".changeset/26738-filter-builder-held-row.md", "ui/packages/app-shell/src/views/metadata-admin/widgets.tsx", "ui/packages/app-shell/src/views/metadata-admin/widgets.filterBuilderHeldRow-26738.test.tsx" ], "deviations": [ "viewFilterFold.ts (in the claimed surface) left unchanged: the fix is entirely in the widget, and the order forbids loosening the fold.", "Reverse verification restored the whole base file with git restore --source rather than ablation-replace.mjs on one anchor; landing proved by occurrence counts 3 to 0 to 3 and git diff HEAD empty." ], "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "3 — issue_read get #26738, issue_read get_comments #26738, issue_read get #26348 (reads only)", "api_writes": "2 — fleet-write pr_create (POST pulls + POST issues/26821/assignees), fleet-write comment on #26738 (this report); git push is not a REST write", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed: the inspector's onChange always rebuilds the view body, so before this fix any non-changing builder emit wrote an unchanged body to the draft; the widget no longer emits in that case (Acceptance notes of PR #26821)." ] }Generated by Claude Code