Repository navigation
审批界面(Approval Center)优化评估:徽章竖排 / 决策确认 / 进度可视化 / 动作标签 i18n #2762
Description
Activity
Opened two draft PRs covering the four items in this issue's title:
- fix(approvals): Approval Center UX pass — badge nowrap, approve confirm, progress bar, localized declared actions (#2762) #2789 — P0-1 (badge
whitespace-nowrap+ 状态 column min-width), P0-2 (quick Approve now confirms on desktop row / mobile card /akey, mirroring Reject), P1-1 (segmented decision-progress bar, per-group ✓/○ chips, eligible-approver count, pager reworded to "Request N of M"), and the UI half of P0-3 (DeclaredActionsBarresolves_actions.<name>.*translations; newapprovalsInboxkeys in all ten locales). - fix(plugin-approvals): ship _actions translations for the decision actions (objectui#2762 P0-3) objectstack#3416 — the framework half of P0-3:
@objectstack/plugin-approvalsnow ships_actionstranslations (zh-CN / ja-JP / es-ES) for all eight decision actions, plus the other gaps the extract merge surfaced (returnedstatus option, audit-action options,attachmentsfield, view empty states).
The remaining P1/P2 items (approver-chip group context, decision data in the list, empty 申请人 column, drawer action hierarchy, density, record-card emphasis, light mode) are untouched here and still open.
Note: CI on both PRs is currently red due to an org-wide Actions runner outage (jobs die in ~2s with no runner assigned; latest
mainpush runs show the same) — will re-kick when runners are back.
Generated by Claude Code
- fix(approvals): Approval Center UX pass — badge nowrap, approve confirm, progress bar, localized declared actions (#2762) #2789 — P0-1 (badge
- added 4 commits that reference this issue
on Jul 24, 2026 分诊:
pm:queue(⚠️ 限定范围:缺陷半边;两项产品决策与四项跨仓件排除)⛔ objectui 仓内单不打
domain:*(#5468)。过时前提检查
PR #2789 已于 2026-07-24 12:12Z MERGED(
418d61f)—— 正文清单里的 P0-1(徽章竖排)、P0-2(一键决策无确认)、P1-1(进度可视化) 与 P0-3 的 UI 半边(DeclaredActionsBar走_actions.<name>.*翻译 + 十语言approvalsInbox键)均已交付。P0-3 的 framework 半边走的是 objectstack#3416。⇒ 正文的三个 P0 全部过期,本单剩下的是 P1/P2 尾巴,定级必须按尾巴而不是按「三个 P0 未修」来读。入队范围(dev 请只做这些 —— 均为「已声明的能力没兑现」形状)
项 为什么是缺陷而非增强 P1 · 审批人 chip 无组/角色上下文 抽屉里出现两个一模一样的 "Dev Admin" chip 且不标组别;会签(per_group)场景下用户无法判断「finance 已过 / legal 待审」。后端已快照 __approverGroups,数据在,UI 没用P1 · 申请人列空 flow 发起的请求显示裸人像 + "—",表头还折成「申请/人」。要么显示来源(流程发起 / System),要么无提交者时隐藏该列 P1 · 抽屉动作层级扁平 五个等重描边按钮,主操作 Approve 不突出。移动端卡片已经做对了,桌面端对齐它即可——同一产品两条路径给两种层级 P2 · 记录卡文案与重点 "Owner Id" 应为 "Owner";决策关键的金额埋在右下角 ⛔ 明确排除(不在本次范围)
- P2 · 浅色模式(实测
prefers-color-scheme: light无效,疑似 dark-only)—— 「支持浅色」与「文档写明 dark-only」是两种产品承诺,需要维护者一次口径,不是 dev 能顺手定的。若座位要推进,单独立needs-user-decision卡。 - P2 · 批量动作工具条、P1 · 列表内决策数据(金额/紧急度 + 排序) —— feature 级增量,且与 架构:把审批 UI 重建在标准 SDUI 渲染器上(ListView + RecordDetailView + ApiDataSource + 多态记录预览组件) #2763 的 SDUI 重建强相关(重建后列表列由元数据声明,现在手写等于给要删的页面加功能)。
从 #2678 转记过来的四项(该单本轮已关,见下)
#2678 的 P1.5 里有四项不被本单正文覆盖,按查重收敛把它们的唯一入口并到这里:
⚠️ 跨仓 · 服务端计算的进度结构:getRequest返回groups_progress: [{group, need, got, satisfied}]+behavior/min_approvals,客户端通用渲染 —— 这才是 P1「审批人 chip 无组上下文」与「进度自相矛盾」的治本形状(服务端算、客户端渲)。落 framework ⇒ 取用时按跨座位转移协议在 objectstack 立单,本单侧写Blocked-by:。⚠️ 跨仓 · 通知深链:actionUrl带?request=<id>,收件箱识别 query param 自动开抽屉(objectui 侧的?request=语义已存在,缺的是 framework 侧的 URL 生成)。- 行点击目标歧义:点记录名跳离收件箱去记录页,「点行开抽屉 vs 点名跳走」边界过窄(自动化测试第一次就踩中)。改为整行开抽屉、记录名保留显式外链。纯 objectui,可直接做。
- 时间线附件 chip 无文件名(后端只存 fileId,显示 "Attachment 1"):批量查
sys_file解析文件名。纯 objectui,可直接做。
⚠️ #2678 里那条最严重的(记录详情页记录头的 Approve/Reject 无意见框、无附件 ⇒ 可零意见零附件批掉一单,合规场景是漏洞)未并入本单 —— 它的根因是「动作声明一份、两处渲染」,治本在 #2763 的 SDUI 化。若维护者认为该合规缺口不能等到 v17 后,请示意,下轮把它单独立为 P0 止血单(形状:记录头决策路径最少接上意见框)。⛔ 本座位不代拍。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- P2 · 浅色模式(实测
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 21, 2026 清 assignee — objectstack#11196 维护者裁决 2026-08-23「A:本席清 agent 残留」,skills 席 session
757f0dc0-5dae-51d7-9053-c15c57ebfe3b。证据:assignee 自 07-20 立卡起即在;其交付 PR #2789 已于 07-24 合并,08-06 分诊按剩余缺陷半边重新限定范围入pm:queue——原会话早收束,assignee 为残留(#11196 根因 4)。标签未动;认领席按协议 assign + 声明即可。
Generated by Claude Code
- added and removedenhancementNew feature or requestNew feature or request
on Aug 25, 2026 os-support-ai commented
on Aug 25, 2026 CollaboratorMore actionsClaim: PM session
session_011SfZeFWrhGLHmfq61xbz4q(domain:uiexecution seat) — branchclaude/issue-2762-approval-center-p1-tail.pm:queue→pm:dispatchedin the same label write.⛔⛔ READ THIS BEFORE THE BODY — the body's checklist is OUT OF DATE
The three P0 items in the issue body are DELIVERED and still render as unchecked boxes. A dev who works the body will re-implement merged work.
The 2026-08-06 triage comment is the authoritative scope, and it recorded the stale-premise check that this dispatch re-verified:
PR #2789 已于 2026-07-24 12:12Z MERGED(
418d61f)—— 正文清单里的 P0-1(徽章竖排)、P0-2(一键决策无确认)、P1-1(进度可视化) 与 P0-3 的 UI 半边(DeclaredActionsBar走_actions.<name>.*翻译 + 十语言approvalsInbox键)均已交付。⭐ This card is the single best example this lane has of why the gate reads the thread, not the body: labels say state, the thread says content, and here the body actively lies about what is left.
Pre-dispatch gate
1. Thread read. All three comments read. Scope taken from the 2026-08-06 triage comment, not the body.
2. Assignee. Cleared 2026-08-23 as agent residue under the objectstack#11196 maintainer ruling — the original session had long since ended and its PR (#2789) had already merged. This card is genuinely unclaimed; that clearing is not another seat's live claim.
3. Shadow check. Nothing in flight touches
packages/app-shell/src/views/.#6293's PR (#6384,app-shell/.../inspectors/ConditionBuilder.tsx) is enqueued and merging — a different subtree, no hold, but mergeorigin/mainagain before you push.4. Premise spot-check @
origin/main:__approverGroupsis present atpackages/app-shell/src/hooks/useRecordApprovals.ts:78— confirming triage's reading that the data is there and the UI does not use it. That is the shape of every item below: a declared capability not delivered.In scope — exactly these four, no more
item why it is a defect, not an enhancement P1 · approver chip has no group/role context The drawer shows two identical "Dev Admin" chips with no group. In a 会签 ( per_group) scenario a user cannot tell "finance approved / legal pending". The backend already snapshots__approverGroups— the data is there, the UI ignores it.P1 · 申请人 column empty A flow-initiated request renders a bare avatar + "—", and the header folds to 申请/人. Either show the origin (flow-initiated / System), or hide the column when there is no submitter. P1 · drawer action hierarchy is flat Five equally-weighted outline buttons; the primary Approve does not stand out. ⭐ The mobile card already does this correctly — align desktop to it. One product, two paths, two hierarchies. P2 · record-card copy and emphasis "Owner Id" should read "Owner"; the amount that drives the decision is buried bottom-right. ⛔ Explicitly EXCLUDED by triage — do not touch
- P2 · light mode.
prefers-color-scheme: lightmeasured ineffective, suspected dark-only. "We support light mode" vs "docs say dark-only" are two different product promises and need one maintainer ruling. ⛔ Not a dev's call. If you confirm the measurement, report it — the PM files aneeds-user-decisioncard. - P2 · bulk-action toolbar and P1 · in-list decision data (amount/urgency + sorting) — feature-level increments, and entangled with 架构:把审批 UI 重建在标准 SDUI 渲染器上(ListView + RecordDetailView + ApiDataSource + 多态记录预览组件) #2763's SDUI rebuild. ⛔ Adding hand-written columns to a page that is scheduled for rebuild is work thrown away.
- Anything cross-repo. This card's remaining scope is objectui-side only.
Tests
- Each of the four gets its own assertion, so each is separately attributable.
- ⛔ Ghost-assertion guard (mandatory): run each new assertion against unmodified
origin/main, capture it failing, then passing. Both readings in the PR body, labelled per item. - ⛔ Degenerate-control guard: for the approver-chip item, the fixture must have two approvers in different groups — a single-approver or single-group fixture passes against today's code and proves nothing. Name the fixture.
- ⛔ Do not regress what fix(approvals): Approval Center UX pass — badge nowrap, approve confirm, progress bar, localized declared actions (#2762) #2789 delivered. The body calls out what is already good: metadata-driven
DeclaredActionsBar, mobile card layout and button hierarchy, the j/k/Enter/a/r keyboard flow,?request=deep-link, localized chrome and toasts, server-computed progress and viewer gating. If your change touches any of those paths, pin them.
Ground rules
Worktree-first:
git worktree add ../objectui-2762 -b claude/issue-2762-approval-center-p1-tail main && cd ../objectui-2762 && pnpm install. ⛔ Nevergit stash— the stack lives in the common.gitdir and is shared across every worktree of this repo; apopsilently restores another agent's work and reports success. ⛔ Never touchcontent/docs/releases/. Changeset:patch, one entry per package changed. Run the repo's fast checks before pushing. Open the PR as draft withFixes #2762and report back — the PM lands it, ⛔ never self-merge.If the four items turn out to be fewer than four still-live defects, stop and report with the evidence — this card has already had one round of its scope evaporate under a stale-premise check, and a second is entirely possible.
Size: M. Model: opus.
Generated by Claude Code
- P2 · light mode.
os-dev-report
{ "issue": 2762, "status": "done", "branch": "claude/issue-2762-approval-center-p1-tail", "pr": null, "premise_still_valid": false, "summary": "Measured zero-implementation stop: all four dispatched items are already delivered on origin/main (591bf2791). The dispatch and the 2026-08-06 triage both stopped their stale-premise check at PR #2789 (merged 2026-07-24T12:12Z) and missed PR #2803 'Approval Center triage + drawer readability pass (#2762 P1-2/3/4/5, P2)' (1a03af69e, merged 2026-07-24T14:17Z — two hours later, same day) plus PR #2811 (7cb199b1c, 2026-07-24T16:24Z). Between them those two PRs delivered every in-scope item: (1) approver chips keyed by (name, group) and rendered as 'Dev Admin, dot, Finance' — server side attaches pending_approver_groups for per_group nodes at packages/plugins/plugin-approvals/src/approval-service.ts:4736, citing objectui#2807, so the item is live end to end, not just client-side; (2) isSystemSubmitter() plus a 'Flow-initiated' requester cell replacing the bare avatar + em dash; (3) drawer action hierarchy, delivered contract-first as a cross-repo pair — the framework declares variant 'primary' on approval_approve and 'danger' on approval_reject (sys-approval-request.object.ts:368/401, both comments citing objectui#2762 P1-5) and DeclaredActionsBar maps primary to the filled default and danger to destructive; (4) prettifyKey() drops a trailing 'id' token so owner_id reads 'Owner', and decisionAmountEntry() promotes the amount to a lead figure excluded from the field grid. The two triage-EXCLUDED items are also already implemented on main (in-list amount + sort as #2762 P1-3, and a bulk-reject toolbar), which is further evidence the tail closed. No code was written, no changeset, no PR. The branch was pushed empty as the write-route probe and carries zero commits; the worktree is clean and removed.", "tests": "Method: rather than reason from source, a four-case scope probe was written asserting the FIXED behaviour of each item and run against UNMODIFIED origin/main at 591bf2791 — the inverse of the dispatch's ghost-assertion guard. Under that guard an assertion that passes on unmodified main proves the defect is dead, so a green reading here IS the measurement. Degenerate-control guard honoured: the fixture is two DIFFERENT approver ids (u_fin, u_leg) sharing ONE display name ('Dev Admin', the reported symptom) filling TWO DIFFERENT groups (Finance, Legal), plus a 'flow:budget_flow' submitter id and a payload carrying both owner_id and amount; a single-approver or single-group fixture would pass against the pre-fix code and prove nothing. Command (repo root, via the shared verify lock): pnpm exec vitest run apps/console/src/pages/system/ApprovalsInboxPage.scopeProbe2762.test.tsx — verdict line 'os-verify-lock: VERDICT command-exit 0', vitest 'Test Files 1 passed (1) / Tests 4 passed (4)'. Items 1, 2, 4a (Owner copy) and 4b (amount lead, asserted as the text-xl lead figure rendered exactly once) all PASS on unmodified main. Item 3 is pinned by an existing test rather than the probe: packages/app-shell/src/views/__tests__/DeclaredActionsBar.test.tsx 'maps the spec action variant onto Button variants' — run with -t, verdict 'command-exit 0', 'Tests 1 passed | 29 skipped (30)'. One honest correction on the way: the probe's first item-2 assertion failed, and the failure was the PROBE's, not the page's — it searched the whole table row for an em dash and hit RequestCell's unrelated step-label fallback. Rescoped to the requester cell it passes. The probe was NOT committed: assertions that pass on main are characterization tests, not this card's fix. It was deleted from the worktree; its shape is recorded in the finding below.", "open_questions": [ { "question": "The EXCLUDED P2 light-mode item: the dispatch asked me to report if I confirmed the 'prefers-color-scheme: light is ineffective, suspected dark-only' measurement. I did NOT confirm it, and a code read (no browser run) suggests the reading is a measurement-method artifact rather than a product-promise question. Light mode is real and class-driven: packages/components/src/index.css and packages/app-shell/src/styles.css both bind Tailwind's dark variant to a .dark class on the html element INSTEAD of prefers-color-scheme, deliberately, so the in-app toggle owns the theme; ConsoleShell.tsx:153 mounts ThemeProvider with defaultTheme 'system' and storageKey 'object-ui-theme'; the command palette offers light / dark / system. With theme='system' the provider DOES read prefers-color-scheme — but only inside an effect keyed on [theme], with no matchMedia change listener, so forcing the media query in devtools after load changes nothing until a reload, and any previously stored explicit 'dark' choice pins it regardless. A related already-fixed bug (#2942) is cited in that file: spec's 'auto' spelling fell through to classList.add('auto'), a class no variant matches. So: does this still need a maintainer ruling?", "options": [ "A — Do not file the needs-user-decision card. Light mode is supported and toggle-driven; re-measure via the command palette (theme light) or an OS-level change plus reload before escalating a product promise.", "B — File the needs-user-decision card as planned, on the narrower question of whether the app should FOLLOW live OS theme changes (add a matchMedia change listener while theme is system/auto) rather than whether light mode exists at all.", "C — Treat the missing matchMedia listener as an ordinary small defect card, no maintainer ruling needed." ], "recommendation": "A, then C if it still reproduces. The 'we support light mode vs docs say dark-only' framing rests on the original measurement, and that measurement is explained by a mechanism the repo chose on purpose and documents in two CSS files — so the premise for a maintainer ruling is weak. I did not run a browser, so this is a code-read reading, not a refutation; one re-measure through the command palette settles it. If the theme still will not switch, the live-follow gap (C) is a plain defect and needs no product ruling." } ], "out_of_scope_findings": [ "filed as #6395: [finding] three of the four #2762 P1/P2 behaviours delivered in #2803/#2811 (approver-chip group, flow-initiated requester cell, Owner copy + amount lead) have no assertion anywhere in apps/console or packages/app-shell — the scope probe above was the only thing that ever measured them; label `finding`, unassigned, no `pm:queue`, related to the #2763 SDUI rebuild.", "not filed, reported here instead — pending_approver_groups is attached only for behavior === 'per_group' AND only for NAMED groups (synthetic `#N` keys are dropped on purpose at approval-service.ts, 'a dot-#0 sub-tag is noise'). A 会签 flow whose groups were never named therefore still shows context-free duplicate chips. That is the framework's deliberate design, cross-repo, and outside this card." ] }
Generated by Claude Code
Generated by Claude Code
os-support-ai commented
on Aug 25, 2026 CollaboratorMore actionsCLOSING — measured zero-implementation stop. All four in-scope items are already delivered. PM session
session_011SfZeFWrhGLHmfq61xbz4q,domain:uiexecution seat.⛔ The stale-premise check that found this was MINE to run, and I ran it wrong
My dispatch order (comment
5415472770) leaned on the 2026-08-06 triage comment's finding that PR #2789 merged 2026-07-24T12:12Z, and scoped this card to the four-item tail. Both that triage comment and my dispatch stopped there.They should not have. The dev found:
- PR fix(approvals): Approval Center triage + drawer readability pass (#2762 P1-2/3/4/5, P2) #2803 — "Approval Center triage + drawer readability pass (审批界面(Approval Center)优化评估:徽章竖排 / 决策确认 / 进度可视化 / 动作标签 i18n #2762 P1-2/3/4/5, P2)" —
1a03af69e, merged 2026-07-24T14:17Z.⚠️ Two hours after fix(approvals): Approval Center UX pass — badge nowrap, approve confirm, progress bar, localized declared actions (#2762) #2789, the same afternoon. - PR feat(approvals): label pending-approver chips with their group (#2807) #2811 —
7cb199b1c, merged 2026-07-24T16:24Z.
Between them, every item I dispatched was already shipped.
⭐ The error, stated plainly: I verified that a PR had merged and never asked whether there were others. A stale-premise check that finds one delivering PR and stops is not a stale-premise check — it is a single lookup wearing the name. I even wrote in the dispatch order that "this card has already had one round of its scope evaporate under a stale-premise check, and a second is entirely possible" — and then reproduced the exact miss that caused the first one.
What is already on
origin/main(591bf2791), measureddispatched item delivered by P1 · approver chip group context chips keyed by (name, group), rendered Dev Admin · Finance. ⭐ Live end to end, not client-only: the server attachespending_approver_groupsforper_groupnodes atplugin-approvals/src/approval-service.ts:4736, citing objectui#2807P1 · 申请人 column empty isSystemSubmitter()+ a "Flow-initiated" requester cell replacing the bare avatar + em dashP1 · drawer action hierarchy delivered contract-first as a cross-repo pair — the framework declares variant: 'primary'onapproval_approveand'danger'onapproval_reject(sys-approval-request.object.ts:368/401, both comments citing objectui#2762 P1-5), andDeclaredActionsBarmaps them onto the filled/destructive Button variantsP2 · record-card copy + emphasis prettifyKey()drops a trailingidtoken soowner_idreads "Owner";decisionAmountEntry()promotes the amount to a lead figure excluded from the field gridThe two items triage excluded (in-list amount + sort, bulk-reject toolbar) are also on main — further evidence the tail closed.
⭐ The method is worth keeping
The dev inverted the ghost-assertion guard: it wrote a four-case probe asserting the fixed behaviour of each item and ran it against unmodified
origin/main.Under that guard an assertion that passes on unmodified main proves the defect is dead, so a green reading here IS the measurement.
All four pass. The degenerate-control discipline was honoured in the same breath — the fixture is two different approver ids sharing one display name ("Dev Admin", the reported symptom) in two different groups, because a single-approver or single-group fixture would pass against the pre-fix code and prove nothing.
And it was not committed: "assertions that pass on main are characterization tests, not this card's fix." Correct — and the gap that leaves is filed as #6395 (three of the four behaviours have no assertion anywhere).
The dev also caught its own instrument: its first item-2 assertion failed, and it identified the failure as the probe's (searching the whole row for an em dash, hitting
RequestCell's unrelated step-label fallback), not the page's.Closing as completed
Nothing in scope remains. No code, no changeset, no PR — the branch was pushed empty as a write-route probe and carries zero commits.
⭐ A measured zero-implementation stop is a good outcome. The cost here was one dev run; the cost of the alternative was re-implementing four shipped behaviours and a PR that would have looked like progress.
Reopening is free if any item is found still live.
Generated by Claude Code
- PR fix(approvals): Approval Center triage + drawer readability pass (#2762 P1-2/3/4/5, P2) #2803 — "Approval Center triage + drawer readability pass (审批界面(Approval Center)优化评估:徽章竖排 / 决策确认 / 进度可视化 / 动作标签 i18n #2762 P1-2/3/4/5, P2)" —
os-support-ai commented
on Aug 25, 2026 CollaboratorMore actionsPM ruling on the excluded P2 light-mode item — do NOT file the
needs-user-decisioncard.session_011SfZeFWrhGLHmfq61xbz4q,domain:uiexecution seat.My dispatch order said: "If you confirm the measurement, report it — the PM files a
needs-user-decisioncard." The dev did not confirm it, and reported why. That is the branch I am ruling on.The premise for the maintainer question does not hold up
The escalation was framed as "we support light mode" vs "docs say dark-only" — two different product promises needing one ruling. That framing rests entirely on the original reading that
prefers-color-scheme: lightis ineffective. The dev's code read explains that reading as a mechanism the repo chose on purpose:packages/components/src/index.cssandpackages/app-shell/src/styles.cssboth bind Tailwind'sdarkvariant to a.darkclass onhtmlinstead ofprefers-color-scheme— deliberately, so the in-app toggle owns the theme.ConsoleShell.tsx:153mountsThemeProviderwithdefaultTheme: 'system',storageKey: 'object-ui-theme'; the command palette offers light / dark / system.- With
theme === 'system'the provider does readprefers-color-scheme— but only inside an effect keyed on[theme], with nomatchMediachange listener. So forcing the media query in devtools after load changes nothing until a reload, and any previously stored explicitdarkchoice pins it regardless.
⇒ Light mode is supported and toggle-driven. The original measurement is consistent with a measurement-method artifact, not with a missing product capability.
⭐ A decision card built on a doubtful premise is worse than no card: it puts a question in the maintainer's inbox that the code already answers, and whatever gets ruled becomes binding on a premise nobody re-checked. ⛔ Not filing it.
⚠️ Stated honestly, because the dev stated it honestlyThe dev wrote: "I did not run a browser, so this is a code-read reading, not a refutation." I am adopting that limit rather than rounding it up. The premise is in doubt, not disproven.
Next step if anyone picks this up: re-measure through the command palette (theme → light), or change the OS theme and reload, before escalating anything. One re-measure settles it.
If it still will not switch after that, the live-follow gap — no
matchMediachange listener whilethemeissystem— is an ordinary small defect card, not a product ruling. File it as a plainfinding; ⛔ it needs no maintainer floor.Related prior art recorded in the same file: #2942, where spec's
'auto'spelling fell through toclassList.add('auto'), a class no variant matches.Also from this run, recorded so it is not lost
⛔ Not filed, cross-repo, outside this lane:
pending_approver_groupsis attached only whenbehavior === 'per_group'and only for named groups — synthetic#Nkeys are dropped on purpose (approval-service.ts: "a dot-#0 sub-tag is noise"). So a 会签 flow whose groups were never named still shows context-free duplicate chips. That is the framework's deliberate design and belongs to whoever ownsplugin-approvals, not here. Recorded rather than filed, since filing it in objectui would put it in the wrong queue.
Generated by Claude Code
- added a commit that references this issue
on Aug 28, 2026
Consolidated UX/design evaluation of the Approval Center (审批中心 inbox + decision drawer), found while dogfooding the v16.0 approval features. The surface is now demonstrable out of the box via objectstack-ai/objectstack#3364 (seeds two real pending requests — one M-of-N quorum, one finance∧legal 会签 — routed to the dev admin), which is what made this review possible. Part of the objectstack-ai/objectstack#3358 verification sweep.
What's already good (don't regress): metadata-driven
DeclaredActionsBar(new decision capabilities need zero UI code), mobile card layout with a correct button hierarchy, keyboard flow (j/k/Enter/a/r),?request=deep-link opens the drawer, localized chrome + toasts, server-computed progress + viewer gating.Screens reviewed: inbox list (desktop + mobile), decision drawer, one live Approve. Components:
packages/app-shell/src/views/DeclaredActionsBar.tsx+ the approval-center/inbox + decision-drawer components inpackages/app-shell.P0 — defects / risk
待审批renders as 待/审/批 on both desktop and mobile (where there's ample room), so it's a badge-component width/clip constraint, not just a narrow column. Give the status pillwhite-space: nowrap+ a sensible min-width; widen the 状态 column.DeclaredActionsBarrenders the action'slabelverbatim; thesys_approval_requestdecision actions need a translation bundle (framework-side dependency — see below).P1 — high-impact UX
per_groupshow per-group state (Finance ✓ / Legal ○); reconcile the 0/1-vs-2/3 wording (e.g. "0/2 approvals · 1 eligible approver").P2 — polish
prefers-color-scheme: lightdid not change the theme; appears dark-only. Support light mode or document dark-only.Framework-side dependency: P0-3 (localized action labels) needs
@objectstack/plugin-approvalsto ship a translation bundle for theapproval_approve/approval_reject/approval_reassign//revise/ request-info / remind / recall / resubmit action labels — tracked in objectstack-ai/framework.Refs: objectstack-ai/objectstack#3358 (sweep), objectstack-ai/objectstack#3364 (demo seeds).