ParcelDraft Workflow and Audit

This guide covers operational checks for ParcelDraft workflow, approvals, and auditability.

Authorization model

Workflow authorization uses dataset role grants only:

  • VIEWER
  • EDITOR
  • REVIEWER
  • APPROVER

No separate workflow ACL is used.

Transition matrix

Allowed transitions:

  • None -> DRAFT
  • DRAFT -> SUBMITTED
  • SUBMITTED -> REVIEWED
  • SUBMITTED -> REJECTED
  • SUBMITTED -> CANCELLED
  • REVIEWED -> APPROVED
  • REVIEWED -> REJECTED
  • REVIEWED -> CANCELLED

Terminal states:

  • APPROVED
  • REJECTED
  • CANCELLED

No transition is permitted from terminal states.

Approval safety checks

Final approval is guarded by:

  • role/state validation (APPROVER on REVIEWED)
  • 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.

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

  1. Confirm at most one active draft exists for each dataset/version chain.
  2. Confirm rejected drafts include rejection_reason.
  3. Confirm final approval produced:
  4. APPROVED draft state
  5. new official parcel version
  6. create_official_version event with resulting version reference
  7. Confirm denied transitions are logged and do not mutate draft state.