Skip to content

[Decision] Named personal list views: can a reader save their own named list, and what is the end state? #28971

Closed

Description

@objectstack-fleet

epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

Blocked-by: #28949
Blocked-by: #28948
Blocked-by: #28950
Blocked-by: #28912

Ruled C (see the ruling comment below). This card records the ruling. The first version gets its own execution card once all four blockers are closed.

Filed by the record-page epic seat on the maintainer's word: 「个人视图 立决策卡,并帮我综合评估」 (2026-11-02). Shortly before, the maintainer said 「命名个人视图暂不做」, so nothing is scheduled while this is open. An independent reviewer wrote the analysis and the seat checked its citations. Readings are on main ab085a0482. Analysis revised on the maintainer's correction, quoted below.

The question

A salesperson wants to keep "the leads I follow up this week" as a list with their own name on it. Today they can adjust a list and the system remembers it, but it keeps one unnamed copy per view, so a new filter overwrites the last one. Keeping a second set means a browser bookmark.

Background

  • Each user has one unnamed setting per view, in sys_user_preference (key ui.list. plus the object name; ui/packages/app-shell/src/context/ListPreferencesProvider.tsx).
  • Creating a view and 「保存为视图」 are admin-only: objectViewTitleSwitcher.tsx:449, ObjectDataPage.tsx:579. Saved views are environment metadata, visible to everyone who can read the object (ADR-0131 D6).
  • A sidebar favorite stores the view, not its current filter (ui/packages/app-shell/src/layout/listViewFavorites.ts).
  • Two declarations promise more than exists:
    • ViewScopeSchema still lists personal (packages/spec/src/ui/view.zod.ts:5789), and nothing restricts such a view to one user.
    • sharing.type: 'personal' only changes a read-only toolbar badge (ui/packages/plugin-list/src/ListView.tsx:6364).
  • content/docs/ui/audience-based-interfaces.mdx:51 promises "personal views".

Prior rulings. #7494 (2026-08-12): 「今天的合同就是 org 级;per-user scope 留在 #7611(v18),不提前造。」 #7611 parked per-user overlay scope and closed not planned on 2026-08-24; its per-user half later shipped through sys_user_preference (objectui#12418). It never ruled on named personal views.

Governing text

  • ADR-0131 D13: "a per-user 'personal view' scope stays the parked v18 direction … if pulled, it returns as a user-owned D3 object, not as this table." Re-check: git show origin/main:docs/adr/0131-total-organization-ownership-no-null-organization-id.md | grep -n "user-owned D3 object"
  • ADR-0017 §2: "personal — authored at runtime, visible only to its owner". Re-check: git show origin/main:docs/adr/0017-object-has-many-view.md | grep -n "visible only to its"
  • view.zod.ts, VIEW_ITEM_OWNER_RETIRED: "per-user view scoping is a parked direction". Re-check: git show origin/main:packages/spec/src/ui/view.zod.ts | grep -n "parked direction"

Protocol. A and B change no protocol. C adds a user-owned platform object and a merged read in the console, on its own execution card; its first version adds no view-item key. D changes the protocol and ADR-0131.

Premises to re-check

Premise Re-check
View creation is admin-only git show origin/main:ui/packages/app-shell/src/views/ObjectDataPage.tsx | grep -n "saveAsViewButton = isAdmin"
The metadata write gate has no user dimension git show origin/main:packages/rest/src/meta-item-write-routes.ts | grep -n "metaSaveVerdict"
Favorites carry no filter state git show origin/main:ui/packages/app-shell/src/layout/listViewFavorites.ts | grep -n "href:"
A query on an unreadable field is refused by the server, so a personal view's filter can't reveal it git show origin/main:packages/plugins/plugin-security/src/predicate-guard.ts | grep -n "not readable by the caller"
The engine resolves relative dates in a filter; whether the list filter panel offers them is still to be measured git show origin/main:packages/spec/src/data/date-range-presets.ts | grep -n "this_week"

Options

Option What it does Where the data lives, and who sees it What the customer notices Effort
A. Don't build Keep the one unnamed setting, URL bookmarks, and author-written "My …" views Unchanged A second set of conditions lives only in a browser bookmark Zero
B. Named favorites with state Save the view plus its current filter and sort as a named sidebar favorite The favorites preference row, self only A named bookmark. It is not in the view switcher, has no columns of its own, and breaks quietly when the view changes ui lane, 1–2 PRs. No direct precedent; #27943 (+2206/−560, the whole switcher) is the upper bound
C. True personal views A user-owned object per ADR-0131 D13. The switcher merges package, environment and "my" views, and any reader can save as "my view" User data, row scope self, not exported with packages Same model as the major CRMs. Works in walled environments, because it is data and not metadata Two lanes, at least 2 PRs. Nearest precedents: objectui#12471 (+2609/−480), #27943 (+2206/−560)
D. Let readers create shared views Give readers the metadata write, or a new permission Environment metadata, visible to everyone One person's list appears for the whole company, and the switcher grows with headcount. Walled environments can't write it (#23330) Spec, ui, and an ADR-0131 change. No precedent

业务含义

  • A:列表是公司的,个人只能临时调;要固定一套条件,就找管理员加视图。
  • B:把一次筛选结果存成带名字的书签挂在侧栏,底层视图一改就可能失效。
  • C:每人有自己的视图抽屉,和公司视图并列,别人看不见。Salesforce、Dynamics 365、HubSpot、Airtable 都是这种模型(经验判断,未查官方文档)。
  • D:人人都是视图管理员,很快没人知道哪个视图是谁的。

按场景评估(维护者口径)

维护者更正:「我们是创业项目,要评估需求的合理性,而不是现在没有拉动」。所以 issue 零命中只记作「无反馈数据」,不作为推迟理由。以下角色和频率是经验判断,不是本项目实测。

角色 工作 现有能力覆盖 覆盖不了 判断
销售 每天按复合条件过线索,同时维持 2–4 份工作集 "My …" 视图覆盖「我负责」,无名设置留最近一份 同一视图上多份工作集互相覆盖;条件因人因时变化,作者写不全 真实、中频、代价中等
主管 团队切片,随月、随人变化 预写的团队视图覆盖骨架 组合写不全 需求更强,但要的是团队共享,C 第一版只是前置层
客服 / 运营 按队列工作 组织定义的队列视图 基本没有 基本不需要
财务 / 后台 周期清单 预写视图、报表、导出 临时核对 不需要

AI 生成应用的影响。 岗位级的静态条件应该由 AI 写进包,这压掉了一半需求。因人因时的条件作者无法预知,把 9 个视图写成 20 个只会让切换器更挤。另外(推断):用户让助手「给我一个本周跟进列表」时,今天只能落到全组织可见的定制包,个人层是这种操作的合理落点。

最强反方: 无名设置加多个基础视图,已经相当于多份工作集,小团队今天基本够用。它不成立的地方是:管理员改基础视图时个人那份会乱,同一视图也放不下两份。这条决定了时序,C 排在卫生卡之后。

四轴分析

① 项目长远合理性。 项目已三次写下终态:ADR-0017 的三层作用域、#7611 的 "architecturally right end-state"、ADR-0131 D13 的用户所有对象。C 是这条线的落点,并给个人列表一个不污染组织视图的归宿。B 形成第二套视图定义方言,以后做 C 要迁移。D 和 ADR-0131、ADR-0146 的来源模型相反。A 不增生,但终态空缺。读数:C > A > B > D。

② 实际业务拉动(按场景)。 见上表。销售每天发生、单次代价小;主管更强但要共享;其他角色不需要。无反馈数据,如实记录,不作权重。

③ 防 AI 犯错。 今天 AI 写 sharing.type: 'personal' 或 scope: 'personal',保存成功,所有人照样看得见,徽标却写「个人」,这是静默的可见性谎言。C 用 spec 的 ListView 形状校验,坏值保存时响亮拒绝;筛选不可读字段由服务端拒绝。B 的偏好 JSON 结构自由,坏了只表现为列表空着或被拒。D 会让个人口味随定制包静默部署到下一个环境。

④ 创业阶段不扩散。 C 的场景合理,义务是一张用户数据表,不进包,也不是作者可写的协议键;第一版压到最小。D 代价最大。四个选项都不需要新门禁,荐「不加」。

推荐:C,第一版排在个性化卫生卡之后

时序: #28949 → #28948 → #28950 → #28912 → C 第一版。第一版把当前个性化状态快照成视图,这四张卡没落地时快照源是脏的:可能含已删字段,可能把旧默认当成个人选择。#28915 与 C 没有耦合,不参与排序。

第一版包含: 另存为我的视图、切换器里的「我的视图」分组、改名、删除、设为我的默认(复用已有的置顶)。

第一版不包含: 分享和团队视图、管理员代管和离职转移、字段选择器、进包和导入导出。个人视图之上也不再叠无名偏好层。

工作量: 两个车道,至少 2 个 PR。先例:objectui#12471(+2609/−480,用户所有列表状态)、#27943(+2206/−560,切换器,C 只加一个分组)、objectui#11582(+674/−18,保存被拒的提示)。平台对象照 sys_user_preference 的模板,没有独立 PR 先例。

终态: 两年后切换器里有三层:包作者的视图、环境管理员发布的视图、用户自己命名的「我的视图」。「我的视图」是一行属于该用户的数据,行级仅本人,不随包导出,配置就是 spec 的 ListView。

自检:只看①选 C;②③④ 是否翻转:否。②把时序从「不排期」改成「卫生卡之后」,④把第一版压到最小,都不翻字母;不新增门禁。

其余选项不取的理由: A 的代价是销售每天重设、主管找管理员,可以承受,但留下终态空缺,是回退项。B 只解决命名,不解决覆盖和漂移,还多一套方言。D 对主管最对症,但墙内不成立,且要改 ADR-0131。

置信缺口:

  • 角色和频率是经验判断,没有使用数据;第一版落地后以「多少用户建了至少一个」作为要不要做分享的依据。
  • 助手代建列表的落点是按 ADR-0146 推断的,未看控制台助手的实际写路径。
  • 列表筛选面板能否选「本周」这类相对日期未实测。引擎解析日期宏({week_start}),spec 有 this_week 预设,但如果面板不提供,「我的本周跟进」存下的就是会过期的绝对日期。

裁后执行

裁 C:

  1. 席位在 ADR-0131 D13 旁记日期注和本卡编号:命名个人视图是用户所有对象,不走 view item,不复用 scope: 'personal'。
  2. 同笔子裁决,不另立决策卡:
    • 退役 ViewScopeSchema.personal 和 sharing.type: 'personal',开一张收口卡。
    • audience-based-interfaces.mdx:51 先改成 per-user list preferences,C 落地后再改回。
    • 个人视图的 id 不用视图的 object.key 语法,避免撞包视图名。
    • 个人视图之上不叠无名偏好层。
  3. 开工前先核实相对日期在列表筛选里能否选择和保存。
  4. 四张卫生卡合入后,席位重跑上面的 re-check,再立第一版执行卡,卡上写明「不包含」清单。
  5. 分享留到第一版落地之后,以使用情况为依据,并先裁非管理员能否产出环境元数据。

裁 A: 同样执行第 2 步的退役和文档修改,并把 ADR-0131 D13 和 view.zod.ts 里的 "parked direction" 改成 not planned。

裁 B 或 D: 维护者在卡上另裁。B 要同时定筛选状态的存法和底层视图被删时的表现。D 要先改 ADR-0131。

os-decision-facets

  • ① 项目长远合理性:C 落实 ADR-0017 和 ADR-0131 D13 的终态,用一个用户数据对象和一套 spec 形状,并给个人列表一个不污染组织视图的归宿,缩小特例;B 造第二套方言,D 下放环境元数据权属且墙内不成立;A 让终态空缺。
  • ② 实际业务拉动(按场景):销售每天维持多份工作集,无名设置只留一份,绕行单次代价小但每日发生;主管更强但要共享,C 第一版只是前置层;客服、财务、后台不需要;无反馈数据,不作权重。
  • ③ 防 AI 犯错:今天写 personal 保存成功而所有人可见,是静默谎言;C 保存时响亮拒绝坏值,不可读字段由服务端拒绝;B 静默失效;D 让个人口味随包静默部署。
  • ④ 创业阶段不扩散:C 场景合理,第一版只做另存、切换、改名、删除、设默认,不做分享和代管;两处悬空声明现在退役;四个选项都不加新门禁。

Prior rulings read: personal view / 个人视图 / per-user scope / sys_view_definition / view.owner → 19 + 282 + 7 + 6 + 5 hits (first 15 read, the rest by title); ADR-0017 §2, ADR-0131 D6 and D13, ADR-0146 D2, ADR-0049; thread: #7494, #7611, #20085, #20230, objectui#12418, objectui#12474, #27594.

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

    Ruling: C · 2026-11-02T06:21Z

    The seat asked: 「选 C 还是 A?我推荐 C。」 C was stated as: 做个人视图,第一版排在四张卫生卡之后,范围按卡上写的. The maintainer: 「同意」.

    Ruled, with the sub-questions in the same stroke:

    Executed now:

    When all four blockers are closed, the seat re-runs the re-checks above, measures whether the list filter panel can select a relative date such as 本周, and files the first-version execution card with the 「不包含」 list. An early unlock from any single blocker does not start it.

    Sharing stays out until the first version has landed. It then needs usage evidence, and a ruling on whether non-admins may produce environment metadata.

  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

    Premise measured · 2026-11-02T06:43Z. The maintainer asked the seat to finish what was unverified: 「没验证的 继续验证」. Preview: bdb19ca6d9, examples/app-crm, signed in as the dev admin.

    Relative dates: the engine works, and a reader has no way to choose one.

    Step Result
    REST close_date < {today} / >= {today} 8 and 5 of 13 rows, correct dates, not inverted
    REST close_date < {week_end} / < {current_month_end} 8 and 9 rows, as the dates predict
    REST {not_a_macro} 400, "Unresolvable filter placeholder"
    Console link with uf__filter value {current_month_end} (operator before) The list requests the macro and shows 9 rows
    Filter panel operators on a date field 等于, 不等于, 为空, 不为空, 早于, 晚于, 介于. No relative option
    Calendar 「今天」 Stores 2026-11-02, an absolute date, and it comes back from the stored preference on the next open
    Typing {today} into the date input Not applied. The filter keeps its previous value

    So the HotCRM comment at src/sales/views/event.view.ts:17-23 ("date macros ship to the server as literal strings") doesn't hold on this build, while the one at task.view.ts:298 does.

    What this means here. A personal view saved today could only hold absolute dates, so 「我的本周跟进」 would go stale. The same is true of today's unnamed setting. Spec already has the presets (DATE_RANGE_PRESETS, including this_week) and the dashboard filter bar offers them. The list filter panel doesn't. The seat will bring this to the maintainer before the first-version card is filed.

    Blocked-by with four targets. h19BlockOutlivedBlocker in scripts/pm/check-half-states.mjs reports a row when any body target has closed. An open target "withholds a full discharge". references/state-machine.md says a closed upstream means re-deriving the block, never a reflex release. The "all targets closed" rule there applies only to the scheduled full-run card refresh. So one closure can raise a patrol row, and the remaining open targets keep this card blocked. The ruling comment's line, that an early unlock doesn't start the work, stands.

    The seat's test filter was removed with 「重置视图」.

  3. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: domain:server · p2 · 2026-11-02T07:36Z

    ⛔ Not a claim.

    • Read fresh. This decision card is ruled C and already carries pm:blocked, so that state is kept and only the domain and priority are added. The first version is a user-owned platform object modelled on sys_user_preference plus a console switcher group, and the rule to change the platform first puts it in domain:server (platform-objects), with a ui follow-up. The ruling says it adds no author-writable protocol key, so it is a user-visible feature, p2.

    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

    Relative dates ruled · 2026-11-02T08:00Z. The maintainer: 「三点都按 A:相对日期立独立卡,在 #28971 第一版立卡前合入,包含早于/晚于的相对点;仪表盘拼法另立低优先级卡;两只钟另立 finding。」

    This answers the open point in the previous comment, that the seat would bring the relative-date gap to the maintainer.

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Seat correction · 2026-11-02T15:00Z

    At 2026-11-02T14:41Z this seat moved this card from pm:queue to needs-user-decision. That was the seat's mistake: it read the [Decision] title and did not read the comments. The card was already ruled C (6570568131), the relative-date sub-question was ruled A (6572083158), and triage unblocked it to pm:queue (6577893396) once #28949, #28948, #28950 and #28912 had closed. #29115 has closed too. No maintainer ruling is owed. needs-user-decision → pm:queue. Seat 1 claims the card's execution step in the next comment.

  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY

    Execution step 4 · 2026-11-02T15:04Z

    The four hygiene cards and #29115 are all closed, and every re-check in the body still holds on origin/main a6441636. The first-version execution card is filed as #29443, with the 「不包含」 list. It runs in two steps: the server step, a user-owned platform object, comes first, and the ui-lane step after it. Seat 1 claims the server step on #29443. This card's work continues there. #29037 and #29038 remain the ruling's side cards.

  8. objectstack-fleet commented on Nov 3, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-warren · session_01JQwxbXsK9j25sd7eVwW96G

    Closed: ruling C executed in both halves · 2026-11-03T03:39Z

    ⛔ Not a claim. This decision card came back into the domain:server queue, but no work is left on it. The coordinator assigned it to seat 1 by mod-5, and the seat read the thread before claiming.

    Step Card Reading
    Ruling C plus the relative-date sub-ruling this card (6570568131, 6572083158) ruled; #29115, #29116 and #29117 filed by the epic seat
    Ruling side cards #29037, #29038 filed in the same stroke as the ruling
    Execution, server half (sys_personal_view) #29443 → PR #29575 MERGED 4008e6290f; #29443 closed completed
    Execution, ui half (「我的视图」 switcher) #29635 → PR #29699 MERGED 0b2b309cb4; #29635 closed completed

    The execution card filed under step 4 (6580498444) has carried this card's work since then, and both of its halves have landed. Closed as completed. If the maintainer wants a step from the 「不包含」 list (sharing, team views, admin management, a field picker, packaging), it is a new card, not a reopening of this one.

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

    area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions