Phase 4c + 4d. Closes the DAO write-path arc (excluding GAT minting,
which is Phase 4c-bis since Sulkta has never executed a proposal).
## proposal_advance (Phase 4c)
State-machine builder with 5 transitions:
- Draft → VotingReady (cosigner threshold met, all cosigner stakes
ref'd as txInfo.referenceInputs, sum staked_amount ≥ to_voting)
- Draft → Finished (drafting period elapsed without enough cosigners)
- VotingReady → Locked (winner outcome exists with votes ≥ execute,
no tie)
- VotingReady → Finished (locking period elapsed without winner)
- Locked → Finished (executing period elapsed; for InfoOnly proposals)
Validator (PAdvanceProposal in Proposal/Scripts.hs:657) requires
output proposal datum equals input with ONLY status mutated. Builder
mirrors exactly. Per-transition preflights match validator gates.
Cosigner stake refs go in as txInfo.referenceInputs (not regular
inputs) per witnessStakes pattern (Proposal/Scripts.hs:366) — sum
of staked_amount is computed from the ref-input set.
GAT-minting Locked→Finished path (effected proposals) deferred to
4c-bis. The pmintGATs governor redeemer is a separate tx that fires
ONLY when the executing period is in window AND the winner outcome
has effects to mint GATs for. Sulkta's first proposal was InfoOnly
so this path never exercised on chain yet.
11 unit tests covering every transition + every preflight reject.
## stake_destroy (Phase 4d)
Burns StakeST token + returns gov-tokens to owner. From
Stake/Redeemers.hs pdestroy (~L432): owner signs (no delegatees),
all locks empty, no stake output at stakes_addr. From stakePolicy
burn branch (~L161): burntST quantity = -spentST.
Tx shape: spend stake (Destroy redeemer) + maybe a funding utxo +
collateral; mint -1 StakeST; one wallet output carrying gov-tokens
+ (stake.lovelace + funding - fee). Funding optional — stake's own
lovelace usually covers fees.
4 unit tests including the funding-optional path.
## MCP tools
dao_proposal_advance_unsigned auto-picks the right transition from
proposal status + chain tip vs window boundaries. Mainnet-only gate.
Fetches cosigner stake refs by matching owner pkh against
proposal.cosigners.
dao_stake_destroy_unsigned fetches the wallet's stake (via owner
pkh match), pulls StakeST asset name from chain, burns it.