DaoConfig gains optional fields for Phase 4 (proposal_create) work:
- proposal_addr — proposal validator address (bech32)
- stake_st_policy — StakeST minting policy id (56 hex)
- proposal_st_policy — ProposalST minting policy id (56 hex)
- script_refs — cached reference UTxO refs for each Agora script
(governor / stake / proposal / treasury validators
+ stake_st / proposal_st minting policies)
All fields optional with serde defaults so existing configs keep loading.
Will be populated by upcoming `dao_discover_scripts` MCP tool that audits
on-chain state under a known governor_addr.
Test fixture also corrected: stakes_addr now uses Sulkta's real per-DAO
parameterized stake-validator address (`addr1w8msu7p...`) instead of the
shared MLabs deployer (`addr1w9gexmeunzsy...`) — matches audit findings.
aldabra-mcp dao_register tool initializes new optionals to None so
DaoConfig construction stays explicit.
Verified against Sulkta's live Proposal #0 datum 2026-05-05:
status field is bare BigInt(3), not Constr 3 []. Plutarch's
EnumIsData derive emits Integer-as-index in this Agora version.
Affected:
- ProposalStatus.{to,from}_plutus_data
- GovernorRedeemer.to_plutus_data (consistency; no on-chain
governor-redeemer evidence yet, but same EnumIsData derive)
ProposalDatum.to_plutus_data signature updated for the new fallible
ProposalStatus encoding (now returns DaoResult).
Added regression test `decodes_sulkta_live_proposal_zero` that decodes
Proposal #0's actual on-chain datum hex and asserts:
proposal_id=0, status=Finished, cosigners=[Sulkta's pkh],
thresholds=20/100/100/1/1, votes={0:0, 1:0} (zero votes ever cast),
starting_time=1772666551575ms.
Closes audit findings 1 + 2 from internal notes.
The Plutarch `ProductIsData` derive (used by every record datum in
Agora) emits a CBOR list of fields, NOT the generic Constr 0
encoding I assumed during Phase 0. Verified by decoding Sulkta's
live governor UTxO datum: outer bytes start `9f 9f` (indef array of
indef arrays), not `d8 79` (Constr tag 121).
Affected types:
- StakeDatum, ProposalLock (was Constr 0, now Array)
- ProposalDatum, ProposalThresholds, ProposalTimingConfig
- GovernorDatum
Sum types untouched — they keep Constr-encoding (makeIsDataIndexed
or EnumIsData both produce Constr i [...]):
- Credential, ProposalAction, StakeRedeemer, ProposalRedeemer,
GovernorRedeemer, ProposalStatus
New helpers in plutus_data.rs:
- `product(fields)` — emit indefinite-length CBOR Array
- `as_product(pd)` — decode (alias for as_array, named for intent)
Added end-to-end validation test `decodes_sulkta_live_governor_datum`
that decodes the real on-chain datum hex from Sulkta's governor UTxO
(7c8db14...221c47#1) and asserts the parsed struct matches README
parameters: thresholds [20/100/100/1/1], 7d draft, 7d vote, 48h lock,
24h exec, 30min ranges, max 20 proposals per stake.