Skip to content

Console: opening a list filter panel persists a view overlay that overrides the source-defined view filter (empties the list) #4155

Description

@baozhoutao

Summary

In Console, opening a list view's filter panel and interacting with it (add / clear a condition) persists the transient filter state as a view customization overlay in sys_metadata (type=view). The overlay overrides the source-defined view filter — e.g. a source view whose default filter is status not_in [archived, deleted] gets its filter replaced by a stray field equals "" (empty-value) condition. On the next visit the list is served with that empty-value filter → total: 0 → the list looks permanently empty.

Two things make this hard to diagnose:

  1. Clearing doesn't recover. "Clear all" / "Remove condition" in the filter panel do not remove the persisted overlay; the panel then shows no active conditions while the list request still carries the stale field equals "" filter.
  2. It persists across reload and across accounts (the overlay is stored server-side, not per-session), and the empty state renders the "no data yet" copy rather than "no records match your filter" — so it reads like data loss or a permission problem, not a filter artifact.

Recovery requires deleting the overlay row from sys_metadata and restarting the service.

Impact

  • A regular end-user (no admin rights, never explicitly clicked "save view") can silently overwrite the project's source-code view declaration just by using the filter panel.
  • The overwritten filter can hide all rows indefinitely for every user of that view.
  • Severity: high — silent, cross-user, and the misleading empty-state copy sends triage down the wrong path (data / permission) instead of the view layer.

Environment

  • @objectstack/*@17.0.0-rc.5
  • Console list view with a source-defined default filter (status not_in [...]).
  • Observed independently twice on the same platform version (a QA run and a dev run), different objects, different accounts.

Steps to reproduce

  1. Define a list view in metadata with a non-empty default filter (e.g. status not_in ['archived','deleted']).
  2. Open that list in Console as a normal user.
  3. Open the filter panel, add a condition, then use Clear all / Remove condition.
  4. Leave and re-open the list (reload, or log in as a different user).

Expected: the source-defined filter is intact; the list shows the same rows as before.
Actual: the list is served with a persisted field equals "" overlay filter → total: 0 → empty list with "no data yet" copy; the filter panel shows no conditions; the source filter is gone until the sys_metadata view overlay row is deleted and the service restarted.

Notes / questions

  • Is a view customization overlay meant to be written on mere filter-panel interaction (vs. only on an explicit "save view" action)? The trigger threshold appears far lower than expected.
  • When an overlay carries an empty-value condition, could Console treat it as "no filter" rather than field equals ""? And surface a "clear customization / reset to default" affordance for non-admins?

Reproduced while building an ObjectStack 17 app. Detailed screen captures are available on request (currently in a private project repo).

Activity

  1. baozhoutao commented on Aug 10, 2026

    @baozhoutao
    ContributorAuthor

    Additional field report from downstream acceptance testing (private app repo steedos-labs/os-project-titanwind-ehr, S06 batch, 2026-08-10, console @objectstack/*@17.0.0-rc.5):

    • Filter conditions set via the list filter panel on a source-defined shared view ("全部派工单" / all dispatch orders) survive logout + re-login for the same account — the persisted overlay keeps applying, conditions stack across sessions, and the list eventually shows "no matching records" until the user clicks "Clear all" in the filter panel.
    • During the acceptance run this produced repeated false "empty list" states; the run had to adopt a "always Clear-all before each filter change" discipline to get stable results.

    Evidence screenshot lives in the downstream private repo (evidence branch 20260810-s06-work-order/PROBE-001-筛选面板.png) — happy to re-capture on a public reproduction if useful. Behavior matches the persisted view-overlay described in this issue; adding the cross-session persistence angle as extra repro signal.

  2. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Triage: pm:queue — concrete high-severity defect with a repro and two independent observations (a QA run and a dev run, different objects/accounts, plus the downstream S06 acceptance field report on this thread: overlays stack across sessions and survive logout/re-login).

    • Substance: interacting with the list filter panel persists a view-customization overlay in sys_metadata that replaces the source-defined view filter (a stray field equals "" condition), cross-user and unrecoverable from the panel's own Clear-all. Silent overwrite of a source declaration by a non-admin用户 path + misleading "no data yet" empty state — this is a dispatchable defect, not an observation.
    • Dup check: no open objectui/objectstack card covers filter-panel overlay persistence (Studio 在代码定义的包里把「只读」一刀切到所有类型:运行时实测接受其中 15 类的 overlay 写入,同屏还并存「可写」徽标与全部禁用的控件 #4036 is Studio read-only badge vs overlay writes — different surface; local filter over cached open lists). Server-side overlay semantics (which rows may be persisted, whether an end-user interaction should ever write a view overlay) may grow an objectstack-side card once the landing is diagnosed; nothing to split pre-diagnosis.
    • Premise: report is from live rc.5 runs today; nothing on origin/main @ 521a37b claims a fix in this area (no overlay/filter-panel entries in recent history touching the persist path).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. self-assigned this
    on Aug 10, 2026
  4. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Collaborator

    CLAIM (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching a dev agent now.

    • Branch claude/issue-4155-filter-panel-overlay, worktree objectui-issue-4155 (M container, diagnosis-first)
    • Delegated product ruling for the client half (veto window open), answering the card's own question: an end-user's filter-panel interaction must NOT persist a view-customization overlay — transient filter state is session/URL state; overlay writes belong to an explicit save-view action only. Additionally: an empty-value condition must never be persisted or applied as field equals "", and Clear-all must actually clear any overlay the panel wrote (recovery for already-poisoned installs).
    • Lane split: if diagnosis lands the cross-user scoping half server-side (a per-user customization stored org-wide in sys_metadata), the dev stops at evidence and the seat files the objectstack-side card — this lane never lands platform code.
    • Surface constraints: TabBar.tsx is train-held (unmerged PR fix(i18n): render I18nLabel objects at the 13 remaining sites #4208); packages/data-objectstack/src/index.ts is held by updateView reads the base view without preview=draft, so renaming a freshly-created draft view is silently lost (404 → create-equivalent partial write → 422) #4139's agent right now — both read-only for this task.

    Generated by Claude Code

  5. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    Collaborator

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4214, delivered across two host restarts (gen-1/2 diagnosis + source inherited and verified, gen-3 added the missing changesets, corrected a measurably-wrong prediction header, and ran the full evidence ladder).

    The diagnosis is complete and mechanistic: onFilterChange → persistViewFilter → persistViewPatch → updateViewConfig wrote an overlay on every panel emission; "Add filter" emits an incomplete equals '' row immediately, the key-wise merge made that one row REPLACE the declared filter, and the live grid's own AST conversion skips empty values — so the only condition the screen ignored was the only condition that reached storage, which is why nothing looked wrong until the next load. Clear-all's filter: [] was still an override with an opinion. All four rulings landed: R1 the panel persist bindings are deleted (explicit save-view remains the only overlay write, pinned as a control, with the objectstack#5159 fold surviving on that path under a retargeted ratchet); R2 two-layer (fold drops valueless rows matching the live query's own predicate; sanitizeViewOverride strips them in both at-rest shapes on read, dropping the filter KEY so the source declaration wins — poisoned installs self-heal with no metadata surgery); R3 from both ends; R4 via the existing list.noMatches keys (all ten locales verified, zero new keys). The deliberate expressiveness loss — an overlay can no longer say "NO filter" over a declaring source — is recorded in code, changeset and PR, and I endorse it: that shape was the eraser.

    The corrected prediction header deserves note: the inherited claim that the control block stays green under reversion was measured false (module dies at import when the export vanishes — a missing export cannot discriminate), rewritten to record the real 24/13 split and name the genuinely discriminating cases. That is the reverse-verification discipline applied to inherited work.

    CI converged 18 success / 2 skipped / 0 failures on b4800fd4b. Cross-user verdict accepted as platform-side and NOT closed here: the seat takes the handoff — an objectstack-side card will be filed for updateViewConfig writing "per-view personal config" as one org-wide metadata item (evidence at data-objectstack index.ts:2818), folding in the related observation that sort/columnState overlays pin the filter as-of-write. Flipping ready + auto-merge.


    Generated by Claude Code

  6. added a commit that references this issue on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions