ParcelDraft Workflow and Audit¶
This guide covers operational checks for ParcelDraft workflow, approvals, and auditability.
Authorization model¶
Workflow authorization uses dataset role grants only:
VIEWEREDITORREVIEWERAPPROVER
No separate workflow ACL is used. Superusers are system/application administrators only and cannot participate in this dataset workflow, even if a stale grant exists.
Reads retain the Viewer-to-Approver hierarchy. Mutations use current exact direct/group grants. Services repeat the role, state, and actor-attribution decision after locking the item. Review and approval enforce distinct actors; cancellation remains role-based.
Action-domain boundary¶
Ordinary parcel drafts and topology change requests share exact-role, state, attribution, and separation-of-duties decisions, but they do not share the same contextual action set.
- Ordinary draft tasks in
DRAFTsupportupdate,submit, andcancel. - Draft topology requests support
update,validate,submit, andcancel. - Topology output mutation and import actions are service operations for the relevant topology workspace; they are not ordinary draft task actions.
createis a creation/audit action and is not an action on an existing task row.
Commit 7b20bfe (2026-07-25, exact-role authorization refactor) temporarily
regressed this boundary by deriving ordinary task-table actions from the full
shared DRAFT authorization vocabulary. This exposed disabled topology and
creation labels on ordinary editor rows. The correction retains the centralized
authorization decision but requires each item type to supply its supported
actions. Regression tests cover the pure authorization boundary, rendered
ordinary task labels, and topology API actions.
Transition matrix¶
Allowed transitions:
None -> DRAFTDRAFT -> SUBMITTEDSUBMITTED -> REVIEWEDSUBMITTED -> REJECTEDSUBMITTED -> CANCELLEDREVIEWED -> APPROVEDREVIEWED -> REJECTEDREVIEWED -> CANCELLED
Terminal states:
APPROVEDREJECTEDCANCELLED
No transition is permitted from terminal states.
Approval safety checks¶
Final approval is guarded by:
- role/state validation (
APPROVERonREVIEWED) - transaction-level locking
- stale-source guard:
draft.source_parcel_version == current_official_version - atomic creation of the new official parcel version
If any check fails, approval is rejected and no partial write is committed.
Submit, review, and approval also recompute overlap evidence under the dataset advisory lock. Unrelated authoritative parcels and submitted/reviewed proposals block transitions. Draft concurrency is advisory. Expected replacement sources and bounded active grandfather records are non-blocking. Approval evaluates the complete output validity interval, and the database trigger provides the final race-safe backstop.
Audit records¶
Each transition writes an immutable workflow event containing:
- draft id, dataset id, version chain id
- source state and target state
- action name (
submit,review_accept,final_approve,reject,cancel,create_official_version) - actor, timestamp, optional reason/comment
- per-draft monotonic
sequence_number
Review event order using sequence_number first, then timestamp.
Denied known-item authorization attempts are written by the public workflow
service after the attempted transaction rolls back. Ordinary and topology views
do not duplicate these records. The denial reason is a stable category such as
exact_role_required, actor_separation, attribution_incomplete, or
invalid_state; request payload, geometry, file contents, and credentials are
not copied into the audit record. The policy attaches this code to the raised
service exception, and the audit writer reads that code directly. It never
classifies a denial by matching exception-message text. Denials without a
workflow item use the structured application security log.
When a decision relies on grandfathering, ParcelOverlapExemptionUse records
SUBMIT, REVIEW, or APPROVE. Approval also produces an
AUTHORITATIVE_WRITE row from the trigger in the same database transaction.
Operational verification checklist¶
- Confirm at most one active draft exists for each dataset/version chain.
- Confirm rejected drafts include
rejection_reason. - Confirm final approval produced:
APPROVEDdraft state- new official parcel version
create_official_versionevent with resulting version reference- Confirm denied transitions are logged and do not mutate draft state.