You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Decision] A personal view's own conditions are invisible and cannot be edited: how should a reader change a saved view? #30073
The maintainer: 「等我决定的都要立卡,并且从业务的角度给我具体解释和我沟通」 (2026-11-04). Measured by the seat on main63b86b385d, on the example CRM unless noted. No domain:* label is set: triage routes this card.
The question
A rep saves 「本季度开放商机」 as My view: two conditions and a sort. Opening it later, the filter panel is empty, with no badge. The sort does show. To change one condition they must add filters on top and save a new view, then delete the old one. Separately, after saving, the source 全部商机 stays filtered and reads 已个性化, so the list they came from now shows fewer rows.
Governing text: #28971 (ruling C, personal views as a user-owned object) and ui/packages/app-shell/src/views/personalViews.ts:159-183. #28971's first-version exclusions do not name this case.
Measured
View Eval 1104 本季度开放商机: config.filter holds stage not_in closed_won, closed_lost and close_date between {quarter_start}, {quarter_end}. Five rows. The 筛选 panel reads 其中 / 添加筛选, with no conditions and no count.
Saving a personal view from a filtered 全部商机 left the source with the filter and 已个性化.
Options
A. Show and edit in the panel: a personal view's conditions appear as ordinary rows, and editing them offers 保存 to that view. Saving a view also clears the source's temporary conditions.
B. Show read-only: display the conditions and keep 另存为 as the only way to change them.
The maintainer, 2026-11-04: 「30079这种不是应该在元数据应用中自己去开发自定义操作吗?你的意见是批量换负责人放到内核吗?其他全部同意」.
Ruling: option A. A personal view's conditions appear as ordinary rows in the filter panel, and editing them offers 保存 to that view. Saving a view also clears the source view's temporary conditions. Conditions that cannot be folded keep today's refusal. needs-user-decision is removed; triage routes this card.
Claim: PM loop round 2
Session: session_01QSeKYpFBMr917PmVkG9up7
Account: os-justin
Branch: claude/issue-30073-personal-view-conditions
Worktree: objectstack-issue-30073
Domain: domain:ui
Seat: domain:ui#3
File surface: ui/packages/app-shell/src/views/personalViews.ts and its callers in ui/packages/app-shell/src/views/**, the list filter-panel seeding in ui/packages/plugin-list (filter-state region only, not the hidden-fields panel), and their tests (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; default tier), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6631338629
Premise: holds (personalViews.ts:159foldFilterGroupToSpecRules(state.url.filters) on origin/main31fe289fc)
Serial constraints cleared: #30034 (named by triage as same code) is closed. Seat 2's #30077 claims ui/packages/plugin-list/src/ListView.tsx in the hidden-fields region; this card's ListView edits, if any, stay in the filter-state region, and whichever lands second merges origin/main first.
Ruling: option A (6631079402), the maintainer's words 「其他全部同意」 (2026-11-04).
{
"issue": 30073,
"status": "done",
"branch": "claude/issue-30073-personal-view-conditions",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30242",
"session": "session_01QSeKYpFBMr917PmVkG9up7",
"premise_still_valid": true,
"summary": "Premise held on origin/main 31fe289fc: personalViews.ts `buildPersonalViewConfig` folded only `state.url.filters` and concatenated it after the stored `view.filter`, and the list renderer applied the stored filter under the list, so a personal view opened with an empty panel. Ruling A is implemented in ui/packages/app-shell/src/views/** and in the filter-state region of plugin-list ListView.tsx (hidden-fields panel untouched). Opening a personal view now seeds its conditions as Filter panel rows (`personalViewPanelFilters`, read through @object-ui/core `builderRulesOfSpecFilter`), and the renderer stops applying them under the list. `ListView` judges the filters personalization against `viewDefaults.filters`. The 已个性化 marker offers 保存 to a reader with update on sys_personal_view; 保存 replaces the row's `config` with the body Save as my view builds (same stored shape, no new key), then resets the reader's list state for the view. Save as my view clears the source view's stored list state, cached search and `uf_*` params. A nested-group panel keeps the `nested_group` refusal and writes nothing. A stored list the panel cannot hold (an any-of group beside other conditions), or one naming a refused field, stays applied under the list as before. Both saves read the panel as the list reports it (`readPanelFilters`), because an emptied panel leaves no `uf__filter` param, so the address alone cannot tell removed from unchanged.",
"tests": "Head 3ee4384a5 (after merging origin/main). plugin-list `vitest run --isolate --maxWorkers=2 packages/plugin-list/`: Test Files 189 passed (189), Tests 1799 passed (1799), lock VERDICT command-exit 0. app-shell, declared narrowing: the 128 test files that import a touched module, `--isolate --maxWorkers=2`: Test Files 128 passed (128), Tests 1125 passed (1125), lock VERDICT command-exit 0. The whole package is 2177 files. A full isolated run at 2 workers was stopped after about 70 minutes holding the lock, so it is NOT MEASURED locally and the full run belongs to CI Test UI. The narrowing is by import and cannot see a test that reaches these modules through another import. `vitest run scripts/` (after the seven @objectstack builds): 170 passed, 2 skipped files; 4985 passed, 2 skipped tests. turbo type-check of @object-ui/app-shell and @object-ui/plugin-list, each running tsc --noEmit plus tsconfig.test.json: Tasks 69 successful, 69 total. New pins: personalViews.test.ts 13/13; ObjectView.personalViews-29635.test.tsx 20/20, in a new top-level describe covering seeding, 保存 writing the stored shape, no 保存 without the update grant, the nested refusal, an unshowable list staying under the list, and the source cleared after Save as my view; ListView.personalization-28950.test.tsx 15/15. Ablations ran through scripts/ablation-replace.mjs, which confirmed each mutation landed and restored it; git status was clean afterwards. @object-ui resolves to src through the vitest alias, so no dist leg was needed. Three mutations went red: re-applying the base filter gave `expected [ 'Paris', 'Paris' ] to have a length of 1 but got 2`; deleting `clearSourceListState(activeView.id)` left the source's stored filters in place; ignoring `viewDefaults.filters` drew the marker.",
"gates": "`node scripts/pm/dispatch-gates.mjs --commands` on 3ee4384a5 derived 35 commands, and all 35 exited 0. `check:type-check-debt` first returned exit 3 (PREREQUISITE NOT MET) after the lock queue timed out. A re-run under the lock read `re-measure: OK`, exit 0. `dispatch-gates --ran` reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. Extra local runs, all exit 0 or clean: `pnpm check:changeset-fixed` (its roster sits under .changeset), `pnpm check:new-line-citations` (0 new), `check:nul-bytes`. PR #30242 check-runs were in_progress when this report was written: Check Changeset, Lint (ESLint, repo & tooling, docs & code guards, CI/release/engine, PM dispatch-gates), Type Check (workspace, source, consumer, debt ledger), Governed Surface Queue Guard and PR-body guards. Test UI, Type Check UI and Build & E2E UI had not been listed yet.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create, draft, no labels, no assignees, created #30242); POST /repos/objectstack-ai/objectstack/dispatches (post-stamped os-dev-report comment on #30073). git push is not counted.",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — personalViewPanelFilters builds builder rows the way the private `groupOfStoredRules` in metadata-admin/widgets.tsx does. That file is outside this card's surface, so the two are not merged here. This is duplication, not a defect.",
"carrier: none · noted, not filed — before this change, Save as my view read the panel from the address, and `serializeFilterGroup` drops a group it cannot fold. A nested group restored from stored list state was therefore saved silently without those conditions, and the nested_group refusal could not be reached. The FilterBuilder cannot author nested groups, so only legacy stored state could carry one. Both saves now read the panel the list reports, which closes this.",
"carrier: none · noted, not filed — local deviation: work happened in the harness worktree at .claude/worktrees/agent-acd6ac48dfe48691f on branch claude/issue-30073-personal-view-conditions, not in ../objectstack-issue-30073. The harness pins this agent to that worktree."
]
}
ACCEPT · PR #30242 · head b8a134840 · 2026-11-05T03:33Z
Seat review of the diff (GET /pulls/30242 diff, and fix commit 5fc9c61c8), against ruling 6631079402 (option A):
Shape:
Opening a personal view seeds its stored conditions as Filter panel rows (personalViewPanelFilters), and the renderer stops applying them under the list, so they are no longer applied twice.
ListView judges personalization against viewDefaults.filters.
The 已个性化 marker offers 保存 to a reader with update on sys_personal_view. It writes the same config shape Save as my view builds; no new key.
Save as my view clears the source view's stored list state, cached search and uf_* params.
A nested group keeps the nested_group refusal. An unshowable stored list stays applied under the list.
Patch round: CI shard 4/16 failed affordanceGrantMap-12082. The new 保存 write site was not entered, and the update grant was read directly. The fix reads the grant through the existing personalGrant helper and enters usePersonalViews > update as outOfFamily, with the reason that the write targets the caller's own sys_personal_view row. The existing save (create) and rename (update) entries for the same rows carry that same classification.
Tests:personalViews.test.ts 13/13, ObjectView.personalViews-29635 20/20 (new describe), and ListView.personalization-28950 15/15. Three ablations went red and were restored.
Gates: check-runs on b8a134840 show 55 runs: 44 success, 11 skipped, 0 red. check-governed-merges --pr 30242 says NOT governed. The PR's own diff is under 3000 lines (+508/−22 as GitHub reports it against the current base). mergeable_state reads clean.
Out of scope:
personalViewPanelFilters duplicates the private groupOfStoredRules in metadata-admin/widgets.tsx. This is duplication, not a defect → Acceptance notes.
The earlier silent loss of a nested group on Save as my view is closed by this PR → Acceptance notes.
Landing:pr_ready + automerge_enable by the seat. Fixes #30073 closes the card at MERGED.
{
"issue": 30073,
"status": "done",
"branch": "claude/issue-30073-personal-view-conditions",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30242",
"session": "session_01QSeKYpFBMr917PmVkG9up7",
"premise_still_valid": true,
"summary": "Patch round for the Test UI (4/16) red on 3ee4384a5. Both failures were in plugin-form affordanceGrantMap-12082.test.tsx, and both came from this PR. (1) The new write site `personalViews.ts :: usePersonalViews › update :: update` was unlisted. It now has a WRITE_SITES entry next to its two siblings, Save as my view (`save :: create`) and Rename (`rename :: update`), and with the same classification they have: `outOfFamily`, the caller's own owner-scoped sys_personal_view row. That classification is the row the existing personal-view saves already have, so it is reused; adding an AFFORDANCE_GRANTS row for the 保存 update alone would split the family in two. No handler was moved or renamed, and the census reports gone: []. (2) ObjectView read `perms.can(PERSONAL_VIEW_OBJECT, 'update')` directly. 保存 now reads the existing `personalGrant('update')`, the one read Rename already uses, moved up so 保存 can use it. No OUT_OF_FAMILY read entry was added and the test is not weakened. The original implementation is unchanged: a personal view's conditions are Filter panel rows, 保存 writes them to that view in the stored shape, Save as my view clears the source, and the nested refusal is kept. origin/main was merged (merge commit b8a134840, no force push). The merge touched objectViewListRenderer.tsx, objectViewListUrlState.ts, ListView.tsx and ListPreferencesProvider, so the overlap was re-checked on the new head.",
"tests": "Reproduced with `--isolate` by reverse verification through scripts/ablation-replace.mjs; each mutation was confirmed to land and was restored, and git status was clean afterwards. Renaming the new WRITE_SITES key turns the grant-map test red with 'A write call site has no WRITE_SITES entry (unlisted), or an entry no longer matches a site (gone)', 1 failed / 54 passed. Putting the direct `perms.can(PERSONAL_VIEW_OBJECT, 'update')` read back turns it red with 'A console source reads a CRUD grant without the affordance-to-grant map', 1 failed / 54 passed. On head b8a134840: the grant-map test plus the touched suites, `--isolate --maxWorkers=2` over 6 files, 140/140 passed. Declared narrowing for app-shell: the 129 files that import a touched module, plus the grant-map test, `--isolate --maxWorkers=2`: Test Files 129 passed (129), Tests 1186 passed (1186). The narrowing is by import and blind to tests that reach these modules another way; CI Test UI covers the full package and is green, see gates. plugin-list `--isolate --maxWorkers=2`: Test Files 189 passed (189), Tests 1811 passed (1811). `vitest run scripts/`: 170 passed, 2 skipped files; 4985 passed, 2 skipped tests. turbo type-check of @object-ui/app-shell, @object-ui/plugin-list and @object-ui/plugin-form: Tasks 70 successful, 70 total. Every lock VERDICT read command-exit 0. The first round's three behaviour ablations still stand (double-applied base filter, source not cleared, viewDefaults.filters ignored).",
"gates": "Re-derived on b8a134840 with `node scripts/pm/dispatch-gates.mjs --commands`: the same 35 commands as round 1, and all exited 0. That includes `check:type-check-debt` run under the lock (`re-measure: OK`, exit 0) and `vitest run scripts/` under the lock. `pnpm check:changeset-fixed` also exited 0. PR #30242 check-runs on head b8a134840: 45 success, 11 skipped, 0 failure, 0 pending. Among them: Test UI (1/16 through 16/16) and Test UI success, Type Check UI success, Lint UI success, Build & E2E UI success, Check Changeset success.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "3 in total across both rounds — POST /repos/objectstack-ai/objectstack/dispatches for fleet-write pr_create (draft #30242); POST .../dispatches for the round-1 post-stamped os-dev-report comment 6648571531 on #30073; POST .../dispatches for this round's os-dev-report comment on #30073. git push is not counted.",
"open_questions": [
{
"question": "The coordinator's patch order prescribed an AFFORDANCE_GRANTS row read through resolveAffordance and a `mapped` write-site entry for 保存, unless the existing personal-view save already had a row. The existing rows for Save as my view and Rename are `outOfFamily` WRITE_SITES entries behind `personalGrant`, and 保存 now follows them. Is that the intended reuse?",
"options": [
"A: keep the reuse. All three owner-row writes (save, rename, 保存) are classified alike as outOfFamily, read through the one personalGrant.",
"B: a follow-up card moves all three personal-view writes onto AFFORDANCE_GRANTS rows (create and update on the reader's own views) read through resolveAffordance, and enters all three as `mapped`."
],
"recommendation": "A for this PR, because mapping only 保存 would leave two dialects for one family of writes. If the seat wants B, it should be its own card that covers all three writes together."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — the PR body was written once at creation and its Verification section quotes head 3ee4384a5. The round-2 readings on b8a134840 are in this report. If the seat wants the body current, it can replace the first line of the Verification section with 'All readings on head b8a134840' and the numbers above. Per contract, the dev did not PATCH the body.",
"carrier: none · noted, not filed — personalViewPanelFilters builds builder rows the way the private `groupOfStoredRules` in metadata-admin/widgets.tsx does. That file is outside this card's surface, so the two are not merged here. This is duplication, not a defect.",
"carrier: none · noted, not filed — before this change, Save as my view read the panel from the address, and `serializeFilterGroup` drops a group it cannot fold. A nested group restored from stored list state was therefore saved silently without those conditions. Both saves now read the panel the list reports.",
"carrier: none · noted, not filed — local deviation: work happened in the harness worktree /home/user/objectstack/.claude/worktrees/agent-acd6ac48dfe48691f, not in ../objectstack-issue-30073. The harness pins the agent to that worktree, so it was neither removed nor had its node_modules deleted."
]
}
Activity
objectstack-fleet commentedon Nov 4, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
The maintainer, 2026-11-04: 「30079这种不是应该在元数据应用中自己去开发自定义操作吗?你的意见是批量换负责人放到内核吗?其他全部同意」.
Ruling: option A. A personal view's conditions appear as ordinary rows in the filter panel, and editing them offers 保存 to that view. Saving a view also clears the source view's temporary conditions. Conditions that cannot be folded keep today's refusal.
needs-user-decisionis removed; triage routes this card.objectstack-fleet commentedon Nov 4, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p2 kept ·
domain:ui· 2026-11-04T13:02Z⛔ Not a claim.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:ui#3 · os-justin · session_01QSeKYpFBMr917PmVkG9up7
Claim: PM loop round 2
Session:
session_01QSeKYpFBMr917PmVkG9up7Account:
os-justinBranch:
claude/issue-30073-personal-view-conditionsWorktree:
objectstack-issue-30073Domain:
domain:uiSeat:
domain:ui#3File surface:
ui/packages/app-shell/src/views/personalViews.tsand its callers inui/packages/app-shell/src/views/**, the list filter-panel seeding inui/packages/plugin-list(filter-state region only, not the hidden-fields panel), and their tests (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(dispatch-gates--tier: no path-derived mandate; default tier),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect cardThread-read: 6631338629
Premise: holds (
personalViews.ts:159foldFilterGroupToSpecRules(state.url.filters)onorigin/main31fe289fc)Serial constraints cleared: #30034 (named by triage as same code) is closed. Seat 2's #30077 claims
ui/packages/plugin-list/src/ListView.tsxin the hidden-fields region; this card's ListView edits, if any, stay in the filter-state region, and whichever lands second mergesorigin/mainfirst.Ruling: option A (6631079402), the maintainer's words 「其他全部同意」 (2026-11-04).
Generated by Claude Code
wip(app-shell): personal view conditions as Filter panel rows, save t…
test(app-shell): pin personal view conditions as panel rows, save bac…
test(plugin-list): pin viewDefaults.filters personalization; changeset (
objectstack-fleet commentedon Nov 5, 2026
domain:ui#3 · os-justin · session_01QSeKYpFBMr917PmVkG9up7
os-dev-report
{ "issue": 30073, "status": "done", "branch": "claude/issue-30073-personal-view-conditions", "pr": "https://github.com/objectstack-ai/objectstack/pull/30242", "session": "session_01QSeKYpFBMr917PmVkG9up7", "premise_still_valid": true, "summary": "Premise held on origin/main 31fe289fc: personalViews.ts `buildPersonalViewConfig` folded only `state.url.filters` and concatenated it after the stored `view.filter`, and the list renderer applied the stored filter under the list, so a personal view opened with an empty panel. Ruling A is implemented in ui/packages/app-shell/src/views/** and in the filter-state region of plugin-list ListView.tsx (hidden-fields panel untouched). Opening a personal view now seeds its conditions as Filter panel rows (`personalViewPanelFilters`, read through @object-ui/core `builderRulesOfSpecFilter`), and the renderer stops applying them under the list. `ListView` judges the filters personalization against `viewDefaults.filters`. The 已个性化 marker offers 保存 to a reader with update on sys_personal_view; 保存 replaces the row's `config` with the body Save as my view builds (same stored shape, no new key), then resets the reader's list state for the view. Save as my view clears the source view's stored list state, cached search and `uf_*` params. A nested-group panel keeps the `nested_group` refusal and writes nothing. A stored list the panel cannot hold (an any-of group beside other conditions), or one naming a refused field, stays applied under the list as before. Both saves read the panel as the list reports it (`readPanelFilters`), because an emptied panel leaves no `uf__filter` param, so the address alone cannot tell removed from unchanged.", "tests": "Head 3ee4384a5 (after merging origin/main). plugin-list `vitest run --isolate --maxWorkers=2 packages/plugin-list/`: Test Files 189 passed (189), Tests 1799 passed (1799), lock VERDICT command-exit 0. app-shell, declared narrowing: the 128 test files that import a touched module, `--isolate --maxWorkers=2`: Test Files 128 passed (128), Tests 1125 passed (1125), lock VERDICT command-exit 0. The whole package is 2177 files. A full isolated run at 2 workers was stopped after about 70 minutes holding the lock, so it is NOT MEASURED locally and the full run belongs to CI Test UI. The narrowing is by import and cannot see a test that reaches these modules through another import. `vitest run scripts/` (after the seven @objectstack builds): 170 passed, 2 skipped files; 4985 passed, 2 skipped tests. turbo type-check of @object-ui/app-shell and @object-ui/plugin-list, each running tsc --noEmit plus tsconfig.test.json: Tasks 69 successful, 69 total. New pins: personalViews.test.ts 13/13; ObjectView.personalViews-29635.test.tsx 20/20, in a new top-level describe covering seeding, 保存 writing the stored shape, no 保存 without the update grant, the nested refusal, an unshowable list staying under the list, and the source cleared after Save as my view; ListView.personalization-28950.test.tsx 15/15. Ablations ran through scripts/ablation-replace.mjs, which confirmed each mutation landed and restored it; git status was clean afterwards. @object-ui resolves to src through the vitest alias, so no dist leg was needed. Three mutations went red: re-applying the base filter gave `expected [ 'Paris', 'Paris' ] to have a length of 1 but got 2`; deleting `clearSourceListState(activeView.id)` left the source's stored filters in place; ignoring `viewDefaults.filters` drew the marker.", "gates": "`node scripts/pm/dispatch-gates.mjs --commands` on 3ee4384a5 derived 35 commands, and all 35 exited 0. `check:type-check-debt` first returned exit 3 (PREREQUISITE NOT MET) after the lock queue timed out. A re-run under the lock read `re-measure: OK`, exit 0. `dispatch-gates --ran` reconciliation: 35 derived, 35 run, 0 NOT-MEASURED, 0 UNRUN. Extra local runs, all exit 0 or clean: `pnpm check:changeset-fixed` (its roster sits under .changeset), `pnpm check:new-line-citations` (0 new), `check:nul-bytes`. PR #30242 check-runs were in_progress when this report was written: Check Changeset, Lint (ESLint, repo & tooling, docs & code guards, CI/release/engine, PM dispatch-gates), Type Check (workspace, source, consumer, debt ledger), Governed Surface Queue Guard and PR-body guards. Test UI, Type Check UI and Build & E2E UI had not been listed yet.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create, draft, no labels, no assignees, created #30242); POST /repos/objectstack-ai/objectstack/dispatches (post-stamped os-dev-report comment on #30073). git push is not counted.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — personalViewPanelFilters builds builder rows the way the private `groupOfStoredRules` in metadata-admin/widgets.tsx does. That file is outside this card's surface, so the two are not merged here. This is duplication, not a defect.", "carrier: none · noted, not filed — before this change, Save as my view read the panel from the address, and `serializeFilterGroup` drops a group it cannot fold. A nested group restored from stored list state was therefore saved silently without those conditions, and the nested_group refusal could not be reached. The FilterBuilder cannot author nested groups, so only legacy stored state could carry one. Both saves now read the panel the list reports, which closes this.", "carrier: none · noted, not filed — local deviation: work happened in the harness worktree at .claude/worktrees/agent-acd6ac48dfe48691f on branch claude/issue-30073-personal-view-conditions, not in ../objectstack-issue-30073. The harness pins this agent to that worktree." ] }fix(app-shell): read the save-to-view grant through personalGrant and…
objectstack-fleet commentedon Nov 5, 2026
domain:ui#3 · os-justin · session_01QSeKYpFBMr917PmVkG9up7
ACCEPT · PR #30242 · head
b8a134840· 2026-11-05T03:33ZSeat review of the diff (
GET /pulls/30242diff, and fix commit5fc9c61c8), against ruling 6631079402 (option A):personalViewPanelFilters), and the renderer stops applying them under the list, so they are no longer applied twice.ListViewjudges personalization againstviewDefaults.filters.sys_personal_view. It writes the sameconfigshape Save as my view builds; no new key.uf_*params.nested_grouprefusal. An unshowable stored list stays applied under the list.ListView.tsxedits stay in the filter-state region. Seat 2's [Decision] Readers can hide list columns but not add one: should a reader be able to add an object's field to their list? #30077 (now merged) was merged in without conflict.affordanceGrantMap-12082. The new 保存 write site was not entered, and the update grant was read directly. The fix reads the grant through the existingpersonalGranthelper and entersusePersonalViews > updateasoutOfFamily, with the reason that the write targets the caller's ownsys_personal_viewrow. The existingsave(create) andrename(update) entries for the same rows carry that same classification.personalViews.test.ts13/13,ObjectView.personalViews-2963520/20 (new describe), andListView.personalization-2895015/15. Three ablations went red and were restored.b8a134840show 55 runs: 44 success, 11 skipped, 0 red.check-governed-merges --pr 30242says NOT governed. The PR's own diff is under 3000 lines (+508/−22 as GitHub reports it against the current base).mergeable_statereadsclean.personalViewPanelFiltersduplicates the privategroupOfStoredRulesinmetadata-admin/widgets.tsx. This is duplication, not a defect →Acceptance notes.Acceptance notes.pr_ready+automerge_enableby the seat.Fixes #30073closes the card at MERGED.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:ui#3 · os-justin · session_01QSeKYpFBMr917PmVkG9up7
os-dev-report
{ "issue": 30073, "status": "done", "branch": "claude/issue-30073-personal-view-conditions", "pr": "https://github.com/objectstack-ai/objectstack/pull/30242", "session": "session_01QSeKYpFBMr917PmVkG9up7", "premise_still_valid": true, "summary": "Patch round for the Test UI (4/16) red on 3ee4384a5. Both failures were in plugin-form affordanceGrantMap-12082.test.tsx, and both came from this PR. (1) The new write site `personalViews.ts :: usePersonalViews › update :: update` was unlisted. It now has a WRITE_SITES entry next to its two siblings, Save as my view (`save :: create`) and Rename (`rename :: update`), and with the same classification they have: `outOfFamily`, the caller's own owner-scoped sys_personal_view row. That classification is the row the existing personal-view saves already have, so it is reused; adding an AFFORDANCE_GRANTS row for the 保存 update alone would split the family in two. No handler was moved or renamed, and the census reports gone: []. (2) ObjectView read `perms.can(PERSONAL_VIEW_OBJECT, 'update')` directly. 保存 now reads the existing `personalGrant('update')`, the one read Rename already uses, moved up so 保存 can use it. No OUT_OF_FAMILY read entry was added and the test is not weakened. The original implementation is unchanged: a personal view's conditions are Filter panel rows, 保存 writes them to that view in the stored shape, Save as my view clears the source, and the nested refusal is kept. origin/main was merged (merge commit b8a134840, no force push). The merge touched objectViewListRenderer.tsx, objectViewListUrlState.ts, ListView.tsx and ListPreferencesProvider, so the overlap was re-checked on the new head.", "tests": "Reproduced with `--isolate` by reverse verification through scripts/ablation-replace.mjs; each mutation was confirmed to land and was restored, and git status was clean afterwards. Renaming the new WRITE_SITES key turns the grant-map test red with 'A write call site has no WRITE_SITES entry (unlisted), or an entry no longer matches a site (gone)', 1 failed / 54 passed. Putting the direct `perms.can(PERSONAL_VIEW_OBJECT, 'update')` read back turns it red with 'A console source reads a CRUD grant without the affordance-to-grant map', 1 failed / 54 passed. On head b8a134840: the grant-map test plus the touched suites, `--isolate --maxWorkers=2` over 6 files, 140/140 passed. Declared narrowing for app-shell: the 129 files that import a touched module, plus the grant-map test, `--isolate --maxWorkers=2`: Test Files 129 passed (129), Tests 1186 passed (1186). The narrowing is by import and blind to tests that reach these modules another way; CI Test UI covers the full package and is green, see gates. plugin-list `--isolate --maxWorkers=2`: Test Files 189 passed (189), Tests 1811 passed (1811). `vitest run scripts/`: 170 passed, 2 skipped files; 4985 passed, 2 skipped tests. turbo type-check of @object-ui/app-shell, @object-ui/plugin-list and @object-ui/plugin-form: Tasks 70 successful, 70 total. Every lock VERDICT read command-exit 0. The first round's three behaviour ablations still stand (double-applied base filter, source not cleared, viewDefaults.filters ignored).", "gates": "Re-derived on b8a134840 with `node scripts/pm/dispatch-gates.mjs --commands`: the same 35 commands as round 1, and all exited 0. That includes `check:type-check-debt` run under the lock (`re-measure: OK`, exit 0) and `vitest run scripts/` under the lock. `pnpm check:changeset-fixed` also exited 0. PR #30242 check-runs on head b8a134840: 45 success, 11 skipped, 0 failure, 0 pending. Among them: Test UI (1/16 through 16/16) and Test UI success, Type Check UI success, Lint UI success, Build & E2E UI success, Check Changeset success.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "3 in total across both rounds — POST /repos/objectstack-ai/objectstack/dispatches for fleet-write pr_create (draft #30242); POST .../dispatches for the round-1 post-stamped os-dev-report comment 6648571531 on #30073; POST .../dispatches for this round's os-dev-report comment on #30073. git push is not counted.", "open_questions": [ { "question": "The coordinator's patch order prescribed an AFFORDANCE_GRANTS row read through resolveAffordance and a `mapped` write-site entry for 保存, unless the existing personal-view save already had a row. The existing rows for Save as my view and Rename are `outOfFamily` WRITE_SITES entries behind `personalGrant`, and 保存 now follows them. Is that the intended reuse?", "options": [ "A: keep the reuse. All three owner-row writes (save, rename, 保存) are classified alike as outOfFamily, read through the one personalGrant.", "B: a follow-up card moves all three personal-view writes onto AFFORDANCE_GRANTS rows (create and update on the reader's own views) read through resolveAffordance, and enters all three as `mapped`." ], "recommendation": "A for this PR, because mapping only 保存 would leave two dialects for one family of writes. If the seat wants B, it should be its own card that covers all three writes together." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — the PR body was written once at creation and its Verification section quotes head 3ee4384a5. The round-2 readings on b8a134840 are in this report. If the seat wants the body current, it can replace the first line of the Verification section with 'All readings on head b8a134840' and the numbers above. Per contract, the dev did not PATCH the body.", "carrier: none · noted, not filed — personalViewPanelFilters builds builder rows the way the private `groupOfStoredRules` in metadata-admin/widgets.tsx does. That file is outside this card's surface, so the two are not merged here. This is duplication, not a defect.", "carrier: none · noted, not filed — before this change, Save as my view read the panel from the address, and `serializeFilterGroup` drops a group it cannot fold. A nested group restored from stored list state was therefore saved silently without those conditions. Both saves now read the panel the list reports.", "carrier: none · noted, not filed — local deviation: work happened in the harness worktree /home/user/objectstack/.claude/worktrees/agent-acd6ac48dfe48691f, not in ../objectstack-issue-30073. The harness pins the agent to that worktree, so it was neither removed nor had its node_modules deleted." ] }objectstack-fleet commentedon Nov 5, 2026
domain:ui#3 · os-justin · session_01QSeKYpFBMr917PmVkG9up7
Landed · PR #30242 · 2026-11-05T04:29Z
eb6199b39at 2026-11-05T04:29Z, via the merge queue (re-queued once on the three facts, PR comment 6649383291);Fixes #30073closes this cardorigin/maingit grep -c "personalViewPanelFilters" origin/main -- ui/packages/app-shell/src/views/personalViews.ts→ ui/packages/app-shell/src/views/personalViews.ts:2State: delivered in full, per ruling 6631079402 (option A).
pm:dispatchedis removed in this stroke.Generated by Claude Code