Repository navigation
Commit 5da2e88
fix(plugin-approvals): the approval request page's highlights strip names the current step by its label (#24374)
Fixes #24281
Clause-②: no
The served `sys_approval_request_detail` record page's highlights strip
now names the current step by its label. It lists `current_step_label`
(the "Approval Step" field) where it listed `current_step`, the Approval
node's machine id, so a request waiting on the `lead_review` node reads
`Lead Review` there. The approvals get's row builder now serves
`current_step_label`, the member `ApprovalRequestRow` declares for this
(`370833a303`, #24309), with the value `step_label` carries. The
object's `highlightFields` makes the same swap.
Responsibility: `plugin-approvals`' own row builder and page declaration
| the platform path is the declared column `current_step_label` and its
contract member, both on `main`, which the row builder did not serve
under that name | reached by every user who opens a pending request's
served page, whose Current Step chip showed the node's machine id.
Readings are against objectstack `c4526a705` (this branch's base) and
objectui at the console pin `.objectui-sha` = `0707b4ae3`, by `git show`
and `git grep`, plus one wire reading on a booted showcase (below). The
page was not driven in a browser.
## Which of the card's two routes, and why
The card asks the fix to say which: **the get now serves the label under
a field name the page can list.** The page names its object's declared
fields, and `step_label` is not one. `current_step_label` is a declared
field of `sys_approval_request` and a declared member of
`ApprovalRequestRow`, so `rowFromRequest` serves it from the same
`requestDisplayLabels` value as `step_label`. No new column, field,
formula or contract member, and no `packages/spec` edit.
## Which doors gain the member
`rowFromRequest` is the only builder of a served `ApprovalRequestRow`.
It has three call sites, and no other site builds a row:
- `openNodeRequest`'s return;
- `listRequests`, behind `GET /api/v1/approvals/requests`;
- `loadRequest`, behind `getRequest` (`GET
/api/v1/approvals/requests/ID`) and `readBackRequest`. `readBackRequest`
builds the `request` that `decideNode`, `recall`, `sendBack`,
`resubmit`, `reassign`, `remind`, `requestInfo` and `comment` echo back.
The action-token chain behind the emailed links also reads through
`getRequest`.
The two REST routes pass the rows through unchanged (`res.json`,
`packages/rest/src/approvals-routes.ts`). So every one of these doors
now serves `current_step_label` beside `step_label`.
## Readers of `step_label`, measured
`step_label` stays served, with the same value. No reader moves.
- This repo: `action-link-pages.ts:66` (`req.step_label ||
req.current_step`), the emailed confirm and result pages. Unchanged.
- objectui at the pin: the inbox
(`apps/console/src/pages/system/ApprovalsInboxPage.tsx:314`, `:1525`;
the type in `approvalsApi.ts:68`) and the record page's approvals panel
(`packages/app-shell/src/views/RecordApprovalsPanel.tsx:372`; the type
in `useRecordApprovals.ts:97`). Unchanged.
- No objectui source reads `current_step_label` at the pin, so the added
key changes nothing for an existing reader.
## The strip and the object's list change together
The rule in the page header still holds at the pin. The Details tab is a
bare `record:details` node. objectui's `record-details.tsx` draws the
object's `highlightFields` minus the mounted strip when a node authors
neither `sections` nor `fields` (`bodyFieldsOmitted`, objectui#12433). A
strip-only swap would have put a "Current Step: lead_review" row into
that tab. So both lists swap `current_step` for `current_step_label`,
and the object's list minus the strip is still exactly `record_id`.
Other readers of the object's `highlightFields`, re-read at the pin (the
same census PR #24279 took):
- `ObjectView` and `ObjectGrid` default columns: only for a list view
that declares no `columns`. All four `sys_approval_request` list views
declare columns, and three already list `current_step_label`. Not
reached.
- `InterfaceListPage` default columns, `ObjectKanban` card fields,
`RelatedList` default columns: no interface page, kanban view or related
list of requests is declared in this repository. Not reached.
- `deriveLookupColumns` (lookup picker preview): the two lookups to a
request, `sys_approval_action.request_id` and
`sys_approval_approver.request_id`, sit on objects whose
`enable.apiMethods` is `['get', 'list']`, so no create or edit form
opens that picker. Not reached.
- `RecordDetailView`'s synthesized default page: not used, since this
page is assigned (`isDefault: true`).
- `RecordPreviewCard`, the card a `referenceVia` pointer pair draws:
reached only where a pointer pair names a request row. Whether such rows
exist is NOT MEASURED. Where they do, the card shows the step's label
where it showed the node id.
- Studio's object settings panel shows the list as authored.
- Backend readers (`@objectstack/lint` field references and semantic
roles, `metadata-core`'s field-security fold, `metadata-protocol`'s
build probes): none changes behaviour for a list of existing fields.
`current_step_label` is the fifth entry, outside the first four that the
field-group shadow rule reads. The page test's build-rule leg stays
green.
## The null case: what the chip shows
The console reads a request through the approvals get
(`routeApprovalRecordReads`). The get serves `current_step_label` by the
one chain in `display-labels.ts`:
- **A label is stored** (every request opened since the column, and
every row the boot backfill filled): the stored label. `openNodeRequest`
stores the authored node label, else the humanized node id.
- **No label stored, the snapshot carries one** (a row opened before the
column that the backfill has not reached): the snapshot's `__nodeLabel`.
- **No label stored and none in the snapshot**: the humanized node id
(`approve_step` becomes `Approve Step`), never the raw id.
- **No step at all** (`current_step` null and no label anywhere):
neither field is served. The strip draws no step chip, as it drew no
`current_step` chip before.
- **The same record read through the generic data API** (a host without
the console's routing): the chip reads the stored column. It is null
only on a row the boot fill has not labelled, and the chip is then
absent where it used to show the raw id.
## The `object_name` chip: measured, the same family, not fixed here
- **What it draws.** The get serves `object_name` as stored, the target
object's API name. On the booted showcase below it answers
`showcase_announcement`. At the pin, `HeaderHighlight` draws a `text`
field through the text cell renderer, which prints the stored string.
The pointer-pair handling (`resolveRecordPointer`) applies only to the
`record_id` id half, and only in the details grid and the list grid. So
the Object chip reads `showcase_announcement`. That is a static reading
of the renderer plus a wire reading of the value, not a screen reading.
- **Why it is not fixed here.** The get also serves `object_label`
(`Announcement` on the same request), but `sys_approval_request`
declares no field that holds the target object's label (its 23 declared
fields were read). A page bound to the object names declared fields
only: this page's own pin, "every field the page names exists on its
object", holds that. So a label chip needs a new member, which this
card's claim excludes. It is reported to the seat with its reach and is
not built.
## Tests (at `2ff7f0350`)
- `pnpm --filter '@objectstack/plugin-approvals^...' build`: VERDICT
command-exit 0. `pnpm --filter @objectstack/plugin-approvals build`:
exit 0, at `b62923f9d`. The one commit after it changes only a test
file.
- `pnpm --filter @objectstack/plugin-approvals test`: `Test Files 81
passed (81)`, `Tests 1111 passed (1111)` (1108 before, plus the 3
below).
- `pnpm --filter @objectstack/plugin-approvals typecheck`: exit 0,
`check:test-typecheck: OK — … 8 file(s) / 324 error(s) / 27 pinned
signature(s) held`. The first spelling of the new test read `.id` off
the open result, a union with the auto-outcome, and grew
`approval-service.test.ts`'s debt by 7 (`189 type error(s), ledger
records 182`). The second commit narrows it with a typed id helper, as
the file's other helpers do.
- Three new cases, named and green (`--reporter=verbose -t
current_step_label`):
- `approval-service.test.ts` › the get serves the step label as
current_step_label, beside step_label › a labelled row serves its stored
label. It also asserts that the list door serves the same three fields
as the get.
- › a row opened before the column serves the snapshot label, else the
humanized machine id.
- `sys-approval-request-page.test.ts` › names the step by its label,
current_step_label, in the strip and in the object's list alike.
- Control in each: `current_step` is still served raw (`approve_step`)
and is still a declared field.
**Ablation (once, then restored; repeated on the final head).** `node
scripts/ablation-replace.mjs --delete` dropped the served line
`current_step_label: labels.current_step_label,` from `rowFromRequest`,
inside a script with its own `trap` restore. The test imports the
service by relative path, so `src/` is what runs and no `dist/` is
involved. On-disk evidence: anchor x1 → x0, blob `b137eedb4067` →
`2739baafe590`. Expected direction: red. Observed: `Tests 2 failed | 1
passed`. Both get cases failed with `expected { current_step_label:
undefined, …(2) } to deeply equal { …(3) }`, and the page pin stayed
green (it does not read the get). Restore leg: blob after restore ==
HEAD (`b137eedb4067`), `git diff HEAD` empty, `git status` clean. No
test file is left behind.
**Wire reading (booted showcase, this branch's build).** `pnpm dev --
--fresh -p 38241`, signed in as the seeded admin. Retitling a
`showcase_announcement` (the `showcase_dynamic_approval` flow's trigger)
opened a pending request at the `lead_review` node. `GET
/api/v1/approvals/requests/ID` answered `current_step_label: "Lead
Review"`, `step_label: "Lead Review"`, `current_step: "lead_review"`,
`object_name: "showcase_announcement"`, `object_label: "Announcement"`.
`GET /api/v1/approvals/requests` answered the same step fields. The
served metadata lists the strip
`["process_label","object_name","status","current_step_label","submitter_id","updated_at"]`
and the object's `highlightFields`
`["process_label","object_name","record_id","status","current_step_label","submitter_id","updated_at"]`.
The server was stopped after the reading.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `2ff7f0350` derived 69 commands for this
diff, the same 69 the seat derived at `83dfbc046`. All 69 ran locally at
`2ff7f0350`, each exit code captured before any pipe. All 69 exited 0,
and none printed a PREREQUISITE NOT MET refusal of its own (the CLI and
the showcase closure had been built for the wire reading).
`node scripts/pm/dispatch-gates.mjs --ran` with the exit-coded record:
`69 derived, 69 run, 0 NOT-MEASURED, 0 UNRUN` ("a DERIVED zero — all 69
recorded an exit code and none of them is 3").
The longest local runs were `check:query-options-erasure` (235 s),
`check:slot-lookup` (123 s) and `check-comment-mask-corpus` (107 s).
Not run locally, declared to CI: `test:repo` (the vitest `repo` project;
the diff touches neither its tests nor a list naming it), and the
repo-wide scans (`pnpm lint` and the rest of the battery). The PR's
check-runs are the reading. `check:dev-prereqs` and
`check:skill-frame-freshness` are not derived and not owed (no
`packages/spec` build input and no frame file changed).
## Acceptance notes
- **The Object chip still shows the target object's API name** (section
above). It is the same family as this card, and a label needs a declared
field the claim excludes. It is reported to the seat, not built.
- **objectui's served-page pin copies**
(`apps/console/src/services/approvalRequestsDataSource.servedPage*.test.tsx`,
8 files at the pin) copy the strip and the object's list with
`current_step`. Their field maps declare no `current_step_label`, and
their row fixtures are shaped as the get served a request before this
PR, without it. objectstack-ai/objectui#12484 carries them. They are not
touched here.
- `ApprovalRequestRow.current_step_label`'s TSDoc says the member was
"declared ahead of its producer", which this PR is. The sentence names
this card as the producer's, so it stays true. It is a `packages/spec`
file and is not touched here.
- `origin/main` moved one commit past this branch's base (`a2a3777bf`,
docs-only, none of the touched paths). The branch is not merged with it.
CI's merge ref and the queue read the merged tree.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8d3dd6f commit 5da2e88
7 files changed
Lines changed: 99 additions & 9 deletions
File tree
- .changeset
- packages/plugins/plugin-approvals/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1271 | 1271 | | |
1272 | 1272 | | |
1273 | 1273 | | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
| 1300 | + | |
| 1301 | + | |
| 1302 | + | |
| 1303 | + | |
| 1304 | + | |
| 1305 | + | |
| 1306 | + | |
| 1307 | + | |
| 1308 | + | |
| 1309 | + | |
| 1310 | + | |
| 1311 | + | |
| 1312 | + | |
| 1313 | + | |
| 1314 | + | |
1274 | 1315 | | |
1275 | 1316 | | |
1276 | 1317 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1181 | 1181 | | |
1182 | 1182 | | |
1183 | 1183 | | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
1184 | 1189 | | |
1185 | 1190 | | |
1186 | 1191 | | |
| |||
3519 | 3524 | | |
3520 | 3525 | | |
3521 | 3526 | | |
3522 | | - | |
| 3527 | + | |
| 3528 | + | |
3523 | 3529 | | |
3524 | 3530 | | |
3525 | 3531 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
165 | 165 | | |
166 | 166 | | |
167 | 167 | | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
168 | 186 | | |
169 | 187 | | |
170 | 188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
62 | 62 | | |
63 | 63 | | |
64 | 64 | | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
70 | 72 | | |
71 | 73 | | |
72 | 74 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
47 | 55 | | |
48 | 56 | | |
49 | 57 | | |
| |||
80 | 88 | | |
81 | 89 | | |
82 | 90 | | |
83 | | - | |
| 91 | + | |
84 | 92 | | |
85 | 93 | | |
86 | 94 | | |
| |||
0 commit comments