Repository navigation
Commit e68669a
docs(qa): wave-2 accuracy slice C — approvals drawer items, queue leg, decision-only pin gap, record-edit-undo premise (#27112)
Part of #25807 (wave-2 accuracy, slice C)
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:审批与记录表单两个清单区共 7 个测试项按 `origin/main` 重写:4
个仍描述已退役请求抽屉的审批项改为请求记录页;queue 审批人一项改为"展开为队列成员";decision-only 与
record-edit-undo 两项复核后保留缺口/阻塞。
- 为什么改:抽屉已被删除,请求改在记录页打开;queue 审批人已能解析成员。旧条目会让 QA 跑出假 FAIL,或把已删除的行为当作缺陷。
- 风险与回滚:只改 QA 台账 JSON,不碰产品代码,也不进 CI 门禁;回滚即 revert 本 PR。被删的子句(抽屉 ←/→
漫游、抽屉打开时的键盘让位)随抽屉一起消失,history 行写明了出处。
- 先看:`docs/qa/platform-checklist/areas/approvals.json` 中的
`inbox-keyboard-flow`(删除子句)、`approver-resolution-matrix`(queue
子句改义)、`notification-deep-link`(未知 id 的落点改义)。
- 两分钟验证:`node scripts/check-platform-checklist.mjs`,预期输出 `OK — 16 areas,
329 items`。
- 需要你做的:无,席位落地。
## What changes
Seven items, each at `revision` +1 from main, main's `history` kept as
an exact prefix, and exactly one appended row citing its source. Every
edit was read against the tree at base `f2fdac5d`. Leads come from slice
B's report (comment 6457925443, `out_of_scope_findings` items 2, 3 and
5).
**What replaced the drawer.** The inbox opens a request by navigating to
its served record page, `/apps/APP/sys_approval_request/record/ID`. This
covers a row click, a mobile card, Enter on the focused row, and the
`?request=ID` deep link, which replaces itself in history.
- Code: `ui/apps/console/src/pages/system/ApprovalsInboxPage.tsx`,
`requestPagePath` and `ApprovalsInboxPage`.
- The page itself is `sys_approval_request_detail`
(`packages/plugins/plugin-approvals/src/sys-approval-request.page.ts`).
Its decision panel draws `sys_approval_request`'s declared actions by
their `visible` predicates over `record.viewer`
(`sys-approval-request.object.ts`).
- The side sheet was deleted in objectui#12484 (ruling C on
objectui#12438). It came into this tree with the ui/ import, PR #25825.
| item | rev | change |
|:--|:--|:--|
| `approvals.viewer-gating-submitter-side` | 6→7 | steps[2] and
acceptance[0] read the decision panel. The levers are named by their
declared labels: Send reminder and Recall for the submitter, none of the
five approver levers. The assertion is unchanged. |
| `approvals.notification-deep-link` | 7→8 | The deep link lands on the
request's record page on both routes (title, steps[3], [4], [7],
acceptance[1]–[4], negative[0], source[3]). Two readings change:
acceptance[1] may read the landing URL, which keeps the id, and
acceptance[4]'s unknown id lands on that id's record page in its
not-found state instead of the inbox. A prior verdict on
acceptance[1]–[4] does not carry. acceptance[0] (the `actionUrl`) is
unchanged. |
| `approvals.dynamic-approver-routing` | 3→4 | steps[3] opens Approve in
the request page's decision panel. `decisionOutputParams.ts` is added as
a source. No clause changed. |
| `approvals.inbox-keyboard-flow` | 2→3 | **Dropped, behaviour removed
with the drawer:** the ←/→ drawer-to-drawer walk (revision 2's steps[2]
and acceptance[4]'s first half) and the list handler's yield to an open
drawer sheet (in steps[5] and acceptance[2]). The keyboard effect on
main has no ArrowLeft/ArrowRight branch. It yields only to a decide
dialog, a focused input and an alertdialog. Enter now opens the request
page. acceptance[0] and [4] changed meaning. |
| `approvals.approver-resolution-matrix` | 8→9 | The queue leg follows
ADR-0142 D6 (PR #26337): see the next section. Also touched: steps[6],
acceptance[5] (oracle `log` → `api`), variants[9], and the queue notes
in source[0] and source[1]. |
| `approvals.decision-only-via-service` | 4→5 | No pin found, so
`automated` stays absent. knownGaps[0] now names the layer pins the
search found and where a pin could land. No clause changed. |
| `records-forms.record-edit-undo` | 4→5 | The BLOCKED premise holds and
`blocked` is unchanged. requires[0] says the reading is current.
knownGaps[1] records the re-check and the two `undoable` shapes the spec
now accepts. No clause changed. |
**Queue (ADR-0142 D6).**
- A `queue` approver expands to the queue's members at node entry,
through the `ownership-queue` service (`approval-service.ts`
`resolveApproverSpec`, `expandQueueUsers`).
- A queue that expands to nobody warns "approver 'queue:VALUE' expanded
to nobody" and adds no slot.
- The `queue:VALUE` literal is never written. So on an empty queue, the
admin_rescue request's `__resolvedFrom['admin_rescue:abandoned']` is
`[]`, with the reason 'no approver on the node resolved to a person'.
- The leg gains a staffed arm: planted `sys_queue` and
`sys_queue_member` rows (both `managedBy: 'config'`, admin-authored).
- The designer note is dropped, because `queue` is no longer
`xEnumDeprecated`.
**decision-only-via-service: why there is no pin.** The tree has three
kinds of layer pin. None of them drives the generic resume route and the
approvals API on a booted stack:
- `resume-authority-gate.test.ts`: an engine unit over a stand-in gated
node.
- `approval-node.test.ts`: the real ApprovalService and AutomationEngine
in process, with no route.
- `http-dispatcher.test.ts` and `automation-resume-caller-gate.test.ts`:
the route over a mocked automation service.
Every clause's oracle is the api, so under RUNNER rule 6 none of these
scores a clause.
**record-edit-undo: why it stays BLOCKED.**
- `examples/` declares zero `undoable` keys, and no packaged action in
`packages/` declares `undoable: true`.
- Inline edits still push no Undo op.
`ui/packages/core/src/actions/ActionRunner.ts` pushes only an action
result's `undo`.
- Since the semantic migration
`ui-action-undoable-unfulfillable-refused`, `undoable: true` parses only
with `operation: 'update'` plus a patch, or with `type: 'api'`. Either
shape can be the fixture that lifts the block.
## Tests
No product code changed, so no package test is owed. Two service-layer
pins were run as re-measurement evidence for the queue and decision-only
rows (built closure: `pnpm --filter '@objectstack/plugin-approvals^...'
build`, lock `VERDICT command-exit 0`):
- `pnpm --filter @objectstack/plugin-approvals exec vitest run
--maxWorkers=2 src/approval-service.test.ts -t 'queue approver routes'`:
`Tests 4 passed | 382 skipped`, lock `VERDICT command-exit 0`.
- `pnpm --filter @objectstack/plugin-approvals exec vitest run
--maxWorkers=2 src/approval-node.test.ts -t 'resume|owning service'`:
`Tests 5 passed | 13 skipped`, lock `VERDICT command-exit 0`.
The edits were made by a span editor. It re-parses each area file and
asserts four things: the document equals the base with the declared
edits applied, every other item and top-level key is value-equal, each
touched item is at base revision + 1, and each touched item has exactly
one appended history row with every earlier row equal.
## Acceptance notes
- Nothing here was driven live. Each rewritten clause is read from
source at `f2fdac5d`, and its history row says so. Where a clause
changed meaning, the row says a prior verdict does not carry.
- Not a ledger edit, noted only: the docblock of
`ui/apps/console/src/registerApprovalsComponents.tsx` still says
`?request=ID` reaches the page's "drawer opener". That is a stale code
comment, with no behaviour behind it.
- Scope: within `approver-resolution-matrix`, variants[9] and the two
source notes changed beside steps[6] and acceptance[5]. Left alone, they
would contradict the rewritten clause in the same item.
## Verification
At head `b2a41393f` (after merging `origin/main` `a27f889a0`; the
incoming commits touched no checklist file):
<details><summary>checklist gate and selectors</summary>
- `node scripts/check-platform-checklist.mjs`: exit 0,
`check-platform-checklist: OK — 16 areas, 329 items (324 active, 1
planned)`
- `node scripts/check-platform-checklist.mjs --self-test`: exit 0, `265
assertions`
- `node scripts/checklist-select.mjs area:approvals`: exit 0, `20
runnable item(s)`
- `node scripts/checklist-select.mjs area:records-forms`: exit 0, `44
runnable item(s) (1 blocked, hidden …)`. The hidden one is
`records-forms.record-edit-undo`.
</details>
<details><summary>derived gates (dispatch-gates --commands,
16)</summary>
Each was run with its exit captured before any pipe, and each exited 0:
`check-adr-links` (+ `--self-test`), `check-closing-keyword-parity` (+
`--self-test`), `check-comment-mask-corpus`, `plan-test-core-shards
--self-test`, `check:doc-formula-expressions`, `check:ci-filter-parity`,
`check:cross-package-test-inputs`, `check:doc-authoring`,
`check:driver-memory-census`, `check:gitlink-declared`,
`check:nul-bytes`, `check:platform-checklist`, `check:refd-timer-probe`,
`check:watch-hint-literal`.
`dispatch-gates --ran`: `16 derived famil(ies) accounted for — 16 run, 0
NOT-MEASURED (a DERIVED zero — all 16 recorded an exit code and none of
them is 3)`.
</details>
This PR was opened by cloud session `session_01QSeKYmtLvHnqZSNJgjpKRv`,
dispatched under claim 6461679818.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QSeKYmtLvHnqZSNJgjpKRv)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 685d9f3 commit e68669a
2 files changed
Lines changed: 83 additions & 61 deletions
0 commit comments