Repository navigation
userActions.edit.visibleWhen 对详情页页头内建【编辑】不生效 —— 同一份声明里 delete 生效、edit 不生效(17.0.0-rc.6) #8499
Description
Activity
- addedbugSomething isn't workingSomething isn't working
on Aug 13, 2026 Triage: lands in objectui's detail-page header (the built-in Edit button's visibility wiring) ⇒ seam card stays here with
repo:objectui, for the objectui whole-repo seat. Queued as Bug: same-shape predicate honoured bydelete(list row + overflow menu) and ignored by the built-in headeredit— an A/B-verified UI-entry convergence defect on rc.6. Not p0: the server-side permission/RLS backstop holds (submit is rejected, no data-integrity exposure); the cost is UX + repeated QA findings.Adjacent, not duplicate: #3076 (the per-record CEL capability this is a gap in), #7692/#7758 (the
createface, resolved). Sibling of #8500 — same page surface, different mechanism (declaredvisibleWhennot consumed vs CEL evaluation fail-open); serial-check the file surface if both are in flight at once.Size/model suggestion: S–M.
本评论来自分诊座位 Routine。
Generated by Claude Code
Claim: PM loop round 4
Session:session_01RnQd8iMMUwXQEV1crFmQiQ
Branch:claude/issue-8499-builtin-edit-visiblewhen
Worktree:objectui-issue-8499
Domain:repo:objectui
File surface:packages/app-shell/src/views/RecordDetailView.tsx(built-in edit/delete predicate wiring region ~1040-1070 + the header button consumption site) + its tests (stop on breach; explain in the report)
Container & model: S–M,mode:subagent,model: opus
Serial constraints cleared: sibling #8500 is CLOSED (its PR #4641 added tests for DECLARED header actions only — the built-in path this card is about was deliberately untouched there); in-flight #8018 declares plugin-grid +useConsoleActionRuntime+ projection builders (disjoint files); no open dev PR touches RecordDetailView (checked this round). Premise caution in the dispatch: current main already carrieseditVisible = useRowPredicate(editPredicates?.visibleWhen ?? true, …)— the card was measured on rc.6 and may be already fixed; closed-PR search mandatory before writing.
Generated by Claude Code
{ "issue": 8499, "status": "done", "branch": "claude/issue-8499-builtin-edit-visiblewhen", "pr": "https://github.com/objectstack-ai/objectui/pull/4666", "premise_still_valid": false, "summary": "The defect is NOT live on main. The card was measured on 17.0.0-rc.6; the wiring landed between rc.6 and 17.5.0 on both detail surfaces — objectui PR #4524 (objectui#4213) for the console record page (RecordDetailView.tsx) and PR #4515 (objectui#4419) for plugin-detail's DetailView — and current main computes editVisible AND consumes it as a fourth conjunct of the sys_edit affordance gate. The PM's fallback hypothesis (the consumption seam is unwired) is falsified too: the seam reads editVisible correctly, and the mobile twin sys_edit_mobile converges via the same schema.showEdit. What WAS missing is coverage of the card's own predicate shape: 'os.user.id != record.executor' reads the SESSION USER against a RELATION field, and the two existing pins bracket that shape from opposite sides without covering it — the #4213 built-in pin uses only self-contained record.status compares and mounts no ExpressionProvider (so os.user was bound nowhere on the built-in path), while the #4641 lookup pin drives os.user.id against a relation but only through DECLARED record_header actions. Delivered that missing pin: 8 cases, both relation payload shapes (bare FK and $expand'd), each as a discriminating PAIR because visibleWhen fails CLOSED — the executor gets no Edit, any other session user keeps it, so an absence-only assertion could not pass for the wrong reason.", "tests": "All at final head 3fcc3311f (quoted from `git rev-parse --short HEAD` after the last commit; main moved to 6d4f5bd59 during the run but only touched content/docs/components/disclosure/toggle-group.mdx, so the run still covers the tree). Build closure first: `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` => EXIT=0. New pin alone: 8 passed. Dispatch-named gate `pnpm exec vitest run packages/app-shell/src/views` => 243 files, 2311 passed | 1 skipped, EXIT=0. `pnpm --filter @object-ui/app-shell type-check` => EXIT=0, and it genuinely RAN (banner '> @object-ui/app-shell@17.5.0 type-check' + 'tsc --noEmit && tsc -p tsconfig.test.json', so the new test file is type-checked — not a zero-match silent pass). REVERSE VERIFICATION, direction predicted before running: ablating the `&& editVisible` conjunct (the rc.6 state) was predicted to redden the five 'Edit absent' assertions and leave the three 'Edit present' controls green; observed exactly that — 'Tests 5 failed | 3 passed (8)'. Restored via `git checkout <branch> -- <path>` from the branch's own commit (the pin was committed BEFORE the ablation); tree clean afterwards, RecordDetailView.tsx byte-unchanged vs main. Changeset: tests-only, so the gate's own prescribed exemption — check-changeset-presence.mjs first ran RED (exit 1, 'no changeset'), and after adding an EMPTY-frontmatter changeset it returned exit 0 with 'Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.' `pnpm changeset:check` => exit 0 (fixed group, no major). `pnpm check:control-bytes` => OK (4192 tracked text files); targeted control-byte self-scan of the new file => no hits. PR labels read back after bots settled: ['tests'].", "open_questions": [], "out_of_scope_findings": [] }Plain-language outcome: the reported behaviour already works on
main— this is a premise falsification, not a re-implementation. The card still closes because the one thing genuinely missing (a regression pin for the exactos.user+ relation predicate shape on the built-in Edit) is delivered in objectui#4666, verified to go red without the fix.No file-surface breach: only the new test file and its changeset were added;
RecordDetailView.tsxis unchanged. The sibling in flight (#8018, plugin-grid +useConsoleActionRuntime) stayed disjoint.
Generated by Claude Code
Generated by Claude Code
ACCEPT (premise-falsified + coverage delivered) — PR objectstack-ai/objectui#4666 (reviewer of record: seat session
session_01RnQd8iMMUwFupVRt… correction:session_01RnQd8iMMUwXQEV1crFmQiQ).The defect is not live on objectui main: the wiring landed between rc.6 and 17.5.0 on both detail surfaces (PR #4524 for the console record page, PR #4515 for plugin-detail), and the dispatch's fallback hypothesis (unwired consumption seam) was also falsified —
editVisibleis consumed as a conjunct of thesys_editgate. What the card actually exposed is a coverage hole at the intersection of two existing pins: session-user (os.user) × relation field × the built-in path had never been evaluated under test. Delivered as an 8-case pin where every case is a discriminating pair (executor loses Edit, any other user keeps it — the "present" half is what a fail-closed fault cannot fake), both payload shapes, plus a no-fault-warning assertion. Reverse verification restored the rc.6 state and reddened exactly the predicted five.Fixesline is correct: with behavior verified and the pin added, this card closes on merge.@baozhoutao — same disposition as #8500/#8282: your rc.6 measurement was accurate for rc.6; on ≥17.5.0
userActions.edit.visibleWhengoverns the header's built-in Edit exactly as delete. Please re-test on a refreshed image; reopen with the build hash if it still reproduces.Landing: ready + auto-merge once gates conclude green on head
3fcc3311f.
Generated by Claude Code
现象
平台版本
@objectstack/*@17.0.0-rc.6。同一份对象级
userActions声明中,两个动作用完全同形的谓词,收敛结果却不一致:delete.visibleWhen生效,edit.visibleWhen在记录详情页页头的内建【编辑】按钮上不生效。逐字的对象声明:
复现与实测
以目标角色账号(即该记录
executor本人)登录 Console,打开该记录的详情页:点开页头【编辑】得到的是可编辑表单;提交时被服务端权限 / RLS 拒绝,数据未发生变更 —— 所以这不是数据完整性问题,是纯粹的 UI 入口收敛问题。
A/B 对照(用于排除项目侧写法问题):同一构建、同一账号、同一会话下,两个不同业务对象使用同一形态的
userActions声明,表现完全一致 —— 均为「delete生效、页头edit不生效」。因此不是单个对象的声明写错。影响
业务上「某角色对该记录只读」这类需求,UI 入口收不干净:只能依赖服务端拦截,用户会先看到入口、点进去、填完再被拒。体验之外,合规审计也会把「能看到编辑入口」记为缺陷 —— 每轮 QA 都会重复报同一条。
期望
userActions.edit.visibleWhen对详情页头的内建【编辑】按钮同样生效,与delete(以及列表行入口)行为一致;相邻单(非重复)
edit/delete加了 per-record CEL 谓词 —— 本单是该能力在详情页头这一渲染面上的缺口;create根本不接受谓词),已由 feat(spec): userActions.create/import accept the edit/delete CEL predicate union #7758 合并解决,与本单不同面。