"Assign number" as a workflow step — design spec
Goal: numbering becomes a first-class step inside a correspondence workflow, assigned to a
registrar (TU), so one chain can carry a letter from draft to sent without the creator manually
bridging between "approved" and "run the seal chain".
The flow question, answered
Recommended order: draft → approve → NUMBER → sign/e-Meterai/e-Stamp → mark sent.
Why this order and not number-first:
- Approval before numbering. Numbers are a gapless legal ledger. If drafts are numbered
before review, every rejected draft burns a number (void rows pollute the register). Standard
Indonesian tata-naskah practice numbers at the moment of official issuance — after the
content is approved. - Numbering before sealing — hard technical constraint. The number/date are merge fields
({{NOMOR}}, {{TANGGAL}}) rendered INTO the PDF that gets sealed. Sealing first would sign a
PDF without its number; re-rendering after numbering would break the signatures' ByteRange.
This is whyguardLetterWorkflowStartblocks seal chains on un-numbered letters today. - Send last, explicit (the terminal lock — already shipped).
Today the user must know this order and manually click "Assign number" between two separate
workflows. The guidance strip (shipped) tells them; the number step automates it. Both
orders stay possible for simple letters (a no-approval letter can still be numbered directly
from draft — unchanged).
Design
Engine
- New step kind
numberalongside approve/sign/meterai/stamp. Letter subjects only. - Assignee: a position or named user — the registrar. Their inbox task reads
"Assign the official number" with the letter deep-link. - Completing the task runs the existing AssignNumber path (allocation + merge-field render +
status→numbered) with task-holder authorization: holding the pending number task IS the
authority (a new engine-scoped completion, not the creator-gated public endpoint). The task UI
reuses the existing AssignNumberModal (scheme picker + live preview). - Scheme choice: the definition may pin a scheme_code; if unset, the registrar picks in the
modal at completion time (default). No new storage beyond the step-config JSON.
Validation (start-time, since definitions are subject-agnostic)
- A definition containing a
numberstep can only START over a letter subject. - Letter already numbered + chain contains
number→ 409 (remove the step or run seal-only). - Chain contains seal steps with NO preceding
numberstep → letter must already be numbered
(existing guard, unchanged). numberstep ordering: must come before any seal step in the chain (definition-save check).
Reject / cancel semantics
- Reject AT the number step → letter stays approved; nothing to void (no number allocated).
- Reject AFTER numbering (e.g. at a sign step) → seals void as today; the number stays —
numbers are never un-assigned (gapless ledger). The letter remainsnumbered.
Status reflection
- Approve steps still flip draft→in_review at start (shipped). The number step's completion
setsnumbered(via AssignNumber). ReflectApprovalState's in_review SQL guard naturally stops
touching the letter after that. No new reflection logic.
UI
- Builder palette gains "Assign number" (with a "letters only" hint; start-time validation
enforces it). - Inbox/task row + letter task banner render the number task with an "Assign number" button
→ AssignNumberModal → engine completion endpoint. - Preset: ship one built-in definition "Full correspondence flow" (Approve → Assign number →
Sign) surfaced in the letter Start-workflow picker, so the recommended order is one click.
Out of scope (v1)
- Un-numbering / renumbering. Number steps for documents. Per-step SLA changes.
Effort
Engine (kind + validation + completion) ~1.5d · FE (palette, task UI, preset) ~1d · e2e ~0.5d.