← Back to Core Domains

4.3 Warrant Lifecycle

Warrants are tenant-local, time-bounded records used by RBAC to validate grants that require formal authorization. They are normally requested in a roster and processed by the workflow engine.

Domain objects

Object Responsibility
Warrant One member/role authorization window, optionally tied to the originating MemberRole
WarrantRoster Batch request and approval subject
WarrantPeriod Administrative period used to organize warranting dates
WarrantRequest Service input value object for a requested warrant
DefaultWarrantManager Creates requests and cancels warrants consistently

Warrant uses the shared ActiveWindow behavior. Active status is therefore derived from approval/state and the start/expiry window; do not add a parallel “is active” rule in a plugin.

Request and approval flow

  1. A domain service builds one or more WarrantRequest values.
  2. WarrantManagerInterface::request() creates the roster and pending warrants in a transaction.
  3. The warrants-roster-approval workflow definition is triggered.
  4. The workflow engine resolves approvers and creates canonical WorkflowApproval records.
  5. Workflow actions apply the approval result to the roster and its warrants.
  6. Active-window processing and permission loading reflect the resulting dates and state.

The canonical approval UI and persistence are in core ApprovalsController, WorkflowApprovalsTable, and DefaultWorkflowApprovalManager. Do not create feature-specific approval tables for new warrant workflows.

Pending-request reconciliation

WarrantManagerInterface::request() reconciles in-flight warrants for every caller. Pending identity is the null-safe combination of entity type, entity ID, member ID, and linked member-role ID. Within that identity, an existing warrant is reused only when its name and normalized effective window also match. Repeating that request is therefore idempotent and does not create another roster or approval workflow. A different member-role ID is a different warrant identity and is not displaced implicitly.

If any of those requirements change, the manager marks the stale pending warrant as replaced and creates a new warrant in a fresh roster. The fresh roster always starts a new approval workflow; responses recorded against the old request are retained for audit but never authorize changed dates or roles. Replacing an item in a shared roster does not disturb the roster workflow while other pending warrants remain. When every pending item in an old roster has been replaced, the manager marks that roster as replaced and cancels its active approval workflow.

Reconciliation applies only to in-flight requests. A current issued warrant is preserved unless the calling domain explicitly shortens or terminates it under its own lifecycle rules.

WarrantManagerInterface::withdrawPendingRequests() is the universal counterpart for requirements that are no longer needed and therefore have no replacement warrant. It marks matching pending warrants as replaced, leaves unrelated items in a shared roster in flight, and marks an emptied roster as replaced while cancelling its active approval workflow. It never changes a current issued warrant.

Officer term changes

The ephemeral officer-assignment-update workflow keeps officer term editing customizable while delegating warrant persistence to WarrantManagerInterface. After the assignment mutation:

An assignment edit can shorten an issued warrant but never lengthens one. The current warrant remains intact while a separate extension request is pending; normal roster approval and activation own any later overlap adjustment.

Ephemeral execution provides per-action atomicity, not one transaction across the graph. The assignment action commits its officer, role, note, and warrant-shortening or retroactive-warrant changes together; a later extension or notification failure cannot roll that update back. The default graph routes expected follow-up failures to a successful updated: true result with warnings, continues to the member notification when possible, and tells the editor to handle the failed follow-up separately instead of resubmitting the assignment change.

A historical correction whose new end date is already past is different from an extension. The officer record keeps the corrected historical end date, but the linked role and current or pending warrants are terminated effective when the correction is recorded. The required officer-term note must state that permission and warrant authority remained effective through that recording date.

Cancellation

Use WarrantManagerInterface::cancel() or cancelByEntity(). These methods keep the warrant record, audit information, reason, revoker, and expiration behavior consistent. Direct deletion is not a valid revocation mechanism.

Authorization integration

A warrant does not independently grant permission. Roles define permissions; policy evaluation combines identity, active role assignment, required warrant state/window, branch scope, and entity rules. Always authorize at the controller boundary and scope list queries.

Multi-tenant and worker rules

Warrant rosters, workflow instances, approvals, roles, and members must all be loaded from the same tenant database. Scheduled or queued processing must carry a tenant identifier in platform job metadata, establish that tenant context, and clear it after the unit of work.

Primary code

When changing this lifecycle, test both the domain result and the workflow approval/decline path; a service-only test cannot prove the durable workflow is wired correctly.