Loading module
Resolving locale, route permissions, and workspace projection.
GOVERNANCE_STATE_MACHINE.md
0. Purpose
This document defines protocol-level state machines for each domain enforced by packages/core/*.
It provides:
Visual diagrams (Mermaid)
Logical transition matrices (deterministic rules)
Authorization gates (ACCESS_DECISION requirements)
Traceability requirements (decision hash binding)
All rules here are ledger-driven and must remain deterministic.
- Common Concepts
1.1 Ledger-driven state
State is derived from scanning the append-only ledger:
LedgerEntry.payload.recordType
LedgerEntry.payload.action
LedgerEntry.payload.decision
LedgerEntry.payload.metadata
No external DB state is trusted.
1.2 Latest-decision-wins authorization
For any executable action X, authorization requires:
A matching ACCESS_DECISION for (subjectId, policyHash, action=X)
READ 2026-03-28T23:42:21.903Z
READ 2026-03-29T04:52:41.869Z
CORE STRICT SAFE DELETE AFTER RERUN REPORT
PUBLIC | DRAFT | v1.0.0
READ 2026-03-29T03:13:33.020Z
Latest matching decision wins
1.3 Cryptographic traceability
When an action is executed (record appended), the resulting record MUST embed:
So every execution can be proven to be authorized.
- Domain: Settlement
2.1 Settlement Records
SETTLEMENT_INTENT (action: SETTLEMENT.CREATE)
SETTLEMENT_EVENT (action: SETTLEMENT.<EVENT_TYPE>)
Settlement events (eventType):
RAIL_ERROR (informational / error signaling)
2.2 Visual State Machine (high-level)
Diagram
stateDiagram-v2
[*] --> IntentCreated: SETTLEMENT.CREATE (requires ALLOW)
IntentCreated --> EscrowOpened: SETTLEMENT.ESCROW_OPENED (requires ALLOW)
IntentCreated --> DepositConfirmed: SETTLEMENT.DEPOSIT_CONFIRMED (requires ALLOW) / if escrowRequired=false
EscrowOpened --> DepositConfirmed: SETTLEMENT.DEPOSIT_CONFIRMED (requires ALLOW)
DepositConfirmed --> ReleaseRequested: SETTLEMENT.RELEASE_REQUESTED (requires ALLOW)
ReleaseRequested --> ReleaseExecuted: SETTLEMENT.RELEASE_EXECUTED (requires ALLOW)
DepositConfirmed --> RefundExecuted: SETTLEMENT.REFUND_EXECUTED (requires ALLOW)
RefundExecuted --> [*]
IntentCreated --> RailError: SETTLEMENT.RAIL_ERROR (requires ALLOW)
EscrowOpened --> RailError: SETTLEMENT.RAIL_ERROR (requires ALLOW)
DepositConfirmed --> RailError: SETTLEMENT.RAIL_ERROR (requires ALLOW)
NOTE: Lifecycle constraints may invalidate some edges (see matrix).
2.3 Transition Matrix: SettlementIntent
From (derived) Action Required prior auth Emits recordType Notes
none SETTLEMENT.CREATE ACCESS_DECISION(ALLOW) for SETTLEMENT.CREATE SETTLEMENT_INTENT Intent is blocked unless latest decision is ALLOW
Hard rule
SettlementIntent must require ALLOW decision (already implemented)
2.4 Transition Matrix: SettlementEvent
Precondition Event Action Required prior auth Emits recordType Required metadata trace
intent exists SETTLEMENT.ESCROW_OPENED ALLOW for SETTLEMENT.ESCROW_OPENED SETTLEMENT_EVENT authDecisionEntryHash, authDecisionPayloadHash
escrowRequired=true AND escrowOpened=false SETTLEMENT.DEPOSIT_CONFIRMED ALLOW for SETTLEMENT.DEPOSIT_CONFIRMED SETTLEMENT_EVENT must be invalid at lifecycle verification
deposit confirmed SETTLEMENT.RELEASE_REQUESTED ALLOW for SETTLEMENT.RELEASE_REQUESTED SETTLEMENT_EVENT milestoneId required
release requested(milestone) SETTLEMENT.RELEASE_EXECUTED ALLOW for SETTLEMENT.RELEASE_EXECUTED SETTLEMENT_EVENT milestoneId required
any before refund SETTLEMENT.REFUND_EXECUTED ALLOW for SETTLEMENT.REFUND_EXECUTED SETTLEMENT_EVENT after refund, no releases allowed
any SETTLEMENT.RAIL_ERROR ALLOW for SETTLEMENT.RAIL_ERROR SETTLEMENT_EVENT requires error metadata
Lifecycle invariants (validator-level)
DEPOSIT_CONFIRMED cannot occur before ESCROW_OPENED when escrowRequired=true
RELEASE_EXECUTED requires prior RELEASE_REQUESTED for same milestone
RELEASE_* not allowed after REFUND_EXECUTED
- Domain: KES Contract Versioning
3.1 Records
KES_VERSION_PROPOSED (action: KES.PROPOSE_VERSION)
KES_VERSION_RATIFIED (action: KES.RATIFY_VERSION)
3.2 Visual State Machine (per contract)
Diagram
stateDiagram-v2
[*] --> NoVersions
NoVersions --> V1Proposed: KES.PROPOSE_VERSION(v=1) (requires ALLOW + chain)
V1Proposed --> V1Ratified: KES.RATIFY_VERSION(v=1) (requires ALLOW + stricter rule)
V1Ratified --> V2Proposed: KES.PROPOSE_VERSION(v=2) (requires ALLOW + strict chain + prev ratified)
V2Proposed --> V2Ratified: KES.RATIFY_VERSION(v=2) (requires ALLOW + stricter rule)
V2Ratified --> VnProposed: KES.PROPOSE_VERSION(v=n)
VnProposed --> VnRatified: KES.RATIFY_VERSION(v=n)
3.3 Transition Matrix: PROPOSE_VERSION (strict chain)
Condition Action Required prior auth Additional chain constraints Emits Trace
no versions exist KES.PROPOSE_VERSION v=1 ALLOW for KES.PROPOSE_VERSION v must be 1; no prevVersionHash KES_VERSION_PROPOSED decision hashes + chain expected/provided
versions exist KES.PROPOSE_VERSION v=n ALLOW for KES.PROPOSE_VERSION n must be contiguous; prevVersionHash must match last versionHash; prev must be ratified (strict+) KES_VERSION_PROPOSED decision hashes + chain expected/provided
Strict chain error codes (enforced):
V1_MUST_NOT_HAVE_PREV_HASH
3.4 Transition Matrix: RATIFY_VERSION (stricter rule)
Condition Action Required prior auth Stricter constraints Emits Trace
version proposed exists KES.RATIFY_VERSION(v, hash) ALLOW for KES.RATIFY_VERSION proposed version must exist; hash must match proposed; cannot ratify twice KES_VERSION_RATIFIED decision hashes
Ratify enforcement error codes:
DENIED_BY_LATEST_DECISION
(Fill-in section keep governance anchored. If you already have AUCTION_TRANSITION rules, map them here.)
AUCTION_TRANSITION (action: AUCTION.<STATE_CHANGE>)
4.2 Suggested Visual Skeleton
Diagram
stateDiagram-v2
[*] --> Draft
Draft --> Published: AUCTION.PUBLISH (requires ALLOW)
Published --> Bidding: AUCTION.OPEN_BIDDING (requires ALLOW)
Bidding --> Settling: AUCTION.CLOSE_BIDDING (requires ALLOW)
Settling --> Completed: AUCTION.COMPLETE (requires ALLOW)
Published --> Cancelled: AUCTION.CANCEL (requires ALLOW)
Bidding --> Cancelled: AUCTION.CANCEL (requires ALLOW)
4.3 Matrix Template
From Action Required auth action Emits Notes
Draft AUCTION.PUBLISH ACCESS_DECISION for AUCTION.PUBLISH AUCTION_TRANSITION
Published AUCTION.OPEN_BIDDING ... AUCTION_TRANSITION
Bidding AUCTION.CLOSE_BIDDING ... AUCTION_TRANSITION triggers SettlementIntent gate
5. Domain: Tender
5.1 Records
TENDER_TRANSITION (action: TENDER.<STATE_CHANGE>)
5.2 Visual Skeleton
Diagram
stateDiagram-v2
[*] --> Draft
Draft --> Open: TENDER.OPEN (requires ALLOW)
Open --> Evaluating: TENDER.CLOSE (requires ALLOW)
Evaluating --> Awarded: TENDER.AWARD (requires ALLOW)
Awarded --> Settling: TENDER.SETTLE (requires ALLOW)
Settling --> Completed: TENDER.COMPLETE (requires ALLOW)
Open --> Cancelled: TENDER.CANCEL (requires ALLOW)
- Domain: Policy Freeze
6.1 Records
POLICY_FREEZE (action: POLICY.FREEZE or your exact action naming)
6.2 Invariant (recommended)
Once a policyHash is frozen:
New ACCESS_DECISION for that policyHash should be rejected (or ignored) by governance modules
Execution must bind to frozen policyHash only
(If this rule is not yet enforced in code, keep it as a formal specification and implement enforcement in core validation.)
- Cross-Domain Couplings
7.1 Auction/Tender -> Settlement
When an Auction/Tender reaches an execution boundary (e.g., award or close):
it may create a SETTLEMENT_INTENT
but settlement is blocked unless:
ACCESS_DECISION(ALLOW) for SETTLEMENT.CREATE exists
7.2 KES Version -> Settlement Profile
KES version commitment includes settlementProfileHash.
Settlement execution referencing a KES contract should use the currently ratified version's settlementProfileHash (ledger-derived).
- Compliance Checklist (Protocol)
For each new domain transition:
Is action name stable and deterministic?
Is there an ACCESS_DECISION gate?
Is latest-decision-wins enforced?
Are decision hashes embedded for traceability?
Are lifecycle invariants verified by scanning the ledger?
Are error codes deterministic and stable?
- Appendix: Action Naming Convention