Summary
Every guard on the delegation's split lives in one governable action's executeImpl (SetupCouponReassignmentDelegation.daml): non-empty beneficiaries, ≤20, percentages in (0,1] summing to exactly 1.0, uniqueness, non-empty assigners, unique assigners, and the decparty/dso exclusions.
CouponReassignmentDelegation itself has no ensure clause. So the invariants hold for the one creation path that exists today, and for no other.
Why it matters
Any future creation path bypasses all of them: a second governable action (a migration action, a differently-shaped setup), or a direct threshold submit by the decparty. Such a path could create a delegation whose split is empty, sums to 0.7, or repeats a beneficiary — and the last of those is not merely wrong but permanently un-exerciseable, because splice's RewardCoupon_AssignBeneficiaries requires unique beneficiaries. With a failed chunk ending the tick, that coupon then heads the most-urgent-first order forever and assignment stalls behind it.
The guards are also duplicated against Rust's validate_reward_beneficiaries, so there are already two copies of the rules and a third creation path would need a fourth.
Requirements
- Move the split invariants onto
CouponReassignmentDelegation as an ensure clause, so they hold for every creation path rather than one.
executeImpl keeps whatever is genuinely about the action rather than the contract — the priorDelegation revoke, and any check that needs the proposer's context.
- The existing execute-time tests must keep passing unchanged; add a direct-create negative proving the template itself rejects a bad split (there is currently no such test —
given_delegation only ever creates a valid one).
- Note that
ensure cannot express the dso/decparty exclusions if they need the governance party's identity beyond what the template already carries; keep those in executeImpl and say why in a comment.
References
daml/governance-rewards/daml/Governance/Rewards/SetupCouponReassignmentDelegation.daml — executeImpl, all guards.
daml/governance-rewards/daml/Governance/Rewards/CouponReassignmentDelegation.daml — the template, no ensure.
crates/decman/src/server/types.rs — validate_reward_beneficiaries, the API-boundary copy.
Summary
Every guard on the delegation's split lives in one governable action's
executeImpl(SetupCouponReassignmentDelegation.daml): non-empty beneficiaries, ≤20, percentages in (0,1] summing to exactly 1.0, uniqueness, non-empty assigners, unique assigners, and the decparty/dso exclusions.CouponReassignmentDelegationitself has noensureclause. So the invariants hold for the one creation path that exists today, and for no other.Why it matters
Any future creation path bypasses all of them: a second governable action (a migration action, a differently-shaped setup), or a direct threshold submit by the decparty. Such a path could create a delegation whose split is empty, sums to 0.7, or repeats a beneficiary — and the last of those is not merely wrong but permanently un-exerciseable, because splice's
RewardCoupon_AssignBeneficiariesrequires unique beneficiaries. With a failed chunk ending the tick, that coupon then heads the most-urgent-first order forever and assignment stalls behind it.The guards are also duplicated against Rust's
validate_reward_beneficiaries, so there are already two copies of the rules and a third creation path would need a fourth.Requirements
CouponReassignmentDelegationas anensureclause, so they hold for every creation path rather than one.executeImplkeeps whatever is genuinely about the action rather than the contract — thepriorDelegationrevoke, and any check that needs the proposer's context.given_delegationonly ever creates a valid one).ensurecannot express the dso/decparty exclusions if they need the governance party's identity beyond what the template already carries; keep those inexecuteImpland say why in a comment.References
daml/governance-rewards/daml/Governance/Rewards/SetupCouponReassignmentDelegation.daml—executeImpl, all guards.daml/governance-rewards/daml/Governance/Rewards/CouponReassignmentDelegation.daml— the template, noensure.crates/decman/src/server/types.rs—validate_reward_beneficiaries, the API-boundary copy.