|
19 | 19 | import type { SharingExecutionContext } from './sharing-service.js'; |
20 | 20 |
|
21 | 21 | /** |
22 | | - * Lifecycle state of an approval request. |
| 22 | + * Lifecycle states of an approval request, in the order the |
| 23 | + * `sys_approval_request.status` select presents them. |
| 24 | + * |
| 25 | + * A VALUE, not only a type, so `plugin-approvals` can spread it into that select |
| 26 | + * rather than re-typing the list (#3786). It used to be a bare union under a |
| 27 | + * "keep in sync with the `sys_approval_request` status select" comment, with the |
| 28 | + * plugin holding the second copy. The two agreed — but nothing made them, and |
| 29 | + * both directions of drift fail quietly: a status the column accepts and the |
| 30 | + * contract omits is invisible to every consumer typed against the contract, |
| 31 | + * while one the contract declares and the column rejects surfaces only at write |
| 32 | + * time, on whichever tenant first reaches that transition. |
23 | 33 | * |
24 | 34 | * `returned` (ADR-0044): the approver sent the request back for revision — |
25 | 35 | * terminal for THIS request/round; the flow walks the `revise` edge to a wait |
26 | 36 | * point, and a later resubmit opens a fresh `pending` request (next round). |
27 | 37 | * Distinct from `recalled` (submitter-initiated withdrawal). |
28 | | - * |
29 | | - * Dual-source: keep in sync with the `sys_approval_request` status select. |
30 | 38 | */ |
31 | | -export type ApprovalStatus = 'pending' | 'approved' | 'rejected' | 'recalled' | 'returned'; |
| 39 | +export const APPROVAL_STATUSES = [ |
| 40 | + 'pending', |
| 41 | + 'approved', |
| 42 | + 'rejected', |
| 43 | + 'recalled', |
| 44 | + 'returned', |
| 45 | +] as const; |
| 46 | + |
| 47 | +/** Lifecycle state of an approval request — derived from {@link APPROVAL_STATUSES}. */ |
| 48 | +export type ApprovalStatus = (typeof APPROVAL_STATUSES)[number]; |
32 | 49 |
|
33 | 50 | /** Live request row. */ |
34 | 51 | export interface ApprovalRequestRow { |
@@ -211,27 +228,49 @@ export interface ApprovalRequestRow { |
211 | 228 | }; |
212 | 229 | } |
213 | 230 |
|
214 | | -/** Kinds of entries on a request's audit trail. */ |
215 | | -export type ApprovalActionKind = |
216 | | - | 'submit' |
217 | | - | 'approve' |
218 | | - | 'reject' |
219 | | - | 'recall' |
220 | | - | 'escalate' |
221 | | - /** A pending approver handed their slot to someone else. */ |
222 | | - | 'reassign' |
223 | | - /** The submitter nudged the pending approvers. */ |
224 | | - | 'remind' |
225 | | - /** An approver asked the submitter for more information (request stays pending). */ |
226 | | - | 'request_info' |
227 | | - /** A free-form reply on the thread (submitter or approver). */ |
228 | | - | 'comment' |
229 | | - /** ADR-0044: an approver sent the request back for revision (request finalizes `returned`). */ |
230 | | - | 'revise' |
231 | | - /** ADR-0044: the submitter resubmitted after rework (the next round's request opens with its own `submit`). */ |
232 | | - | 'resubmit' |
233 | | - /** #1322 M1: an out-of-office approver's slot was auto-rerouted to their delegate at resolution time. */ |
234 | | - | 'ooo_substitute'; |
| 231 | +/** |
| 232 | + * Kinds of entries on a request's audit trail, in the order the |
| 233 | + * `sys_approval_action.action` select presents them. |
| 234 | + * |
| 235 | + * A VALUE for the same reason as {@link APPROVAL_STATUSES}: `plugin-approvals` |
| 236 | + * spreads it into that select instead of holding a twelfth-entry copy of it |
| 237 | + * (#3786). Twelve hand-matched strings across a package boundary is the widest |
| 238 | + * of the sweep's remaining copies, and an audit vocabulary is the worst place |
| 239 | + * for a silent gap — a kind the column accepts but the contract omits produces |
| 240 | + * rows no typed consumer can narrow, and the audit trail is exactly what gets |
| 241 | + * read back when someone asks what happened. |
| 242 | + * |
| 243 | + * Only some entries need explaining; the first five are self-describing: |
| 244 | + * reassign a pending approver handed their slot to someone else |
| 245 | + * remind the submitter nudged the pending approvers |
| 246 | + * request_info an approver asked for more information (request stays pending) |
| 247 | + * comment a free-form reply on the thread (submitter or approver) |
| 248 | + * revise ADR-0044: sent back for revision (request finalizes `returned`) |
| 249 | + * resubmit ADR-0044: resubmitted after rework (the next round opens with |
| 250 | + * its own `submit`) |
| 251 | + * ooo_substitute #1322 M1: an out-of-office approver's slot was auto-rerouted |
| 252 | + * to their delegate at resolution time |
| 253 | + * |
| 254 | + * `reassign` / `remind` / `request_info` / `comment` / `ooo_substitute` are |
| 255 | + * thread interactions and never move the flow; `revise` / `resubmit` do. |
| 256 | + */ |
| 257 | +export const APPROVAL_ACTION_KINDS = [ |
| 258 | + 'submit', |
| 259 | + 'approve', |
| 260 | + 'reject', |
| 261 | + 'recall', |
| 262 | + 'escalate', |
| 263 | + 'reassign', |
| 264 | + 'remind', |
| 265 | + 'request_info', |
| 266 | + 'comment', |
| 267 | + 'revise', |
| 268 | + 'resubmit', |
| 269 | + 'ooo_substitute', |
| 270 | +] as const; |
| 271 | + |
| 272 | +/** Kinds of entries on a request's audit trail — derived from {@link APPROVAL_ACTION_KINDS}. */ |
| 273 | +export type ApprovalActionKind = (typeof APPROVAL_ACTION_KINDS)[number]; |
235 | 274 |
|
236 | 275 | /** |
237 | 276 | * A file attached to a decision action (#3266) — the READ shape of one |
|
0 commit comments