GovernanceDecisionReceipt Authority + Grant-Minting — Design
Status: draft for review (design-only). No code in this PR. Issue: #1868 (governance:write decomposition / mandate layer). Companion docs: `governance-write-decomposition.md` (§6, §10, §12 Q4), `mandate-gate-design.md`; ADR-0014 (AuthorityGrant non-conflation).
0. Non-claims
- This is a design draft, not an implementation. No receipt is emitted, no grant is minted, no handler is wired by this PR.
- No
GovernanceDecisionReceiptV2production emission. NoMandateGate::require()handler wiring. No grant-minting code. NoTypedScopeFederation/Role binding code. Nogovernance:writeretirement. - No production-readiness, live-federation, or demo-ready claim.
1. Problem
Low-blast membership-standing receipt emission is live — MeetingAttendanceReceiptV2
(#1934) and ActionItemCompletionReceiptV2 (#1935), both
NoMandateRequired { MembershipStandingOnly }, persisted via the fail-closed
put_opaque route. The next dormant receipt is GovernanceDecisionReceiptV2 (schema from
#1929), but its honest production emission is blocked on undecided authority semantics,
and MandateGate handler wiring is blocked on a missing grant/target substrate. This doc
decides the authority model so the follow-up code lands honestly — no fake
NoMandateRequired, no always-reject theater.
2. Current facts (verified against ae7fc00a)
- Decision-producing paths are the proposal close/accept path: standalone manager
close (
icn/apps/governance/src/manager.rs), actor-backed close (icn/apps/governance/src/actor.rs), and the forced-accept path (sameactor.rs, with forced provenance inicn/apps/governance/src/lib.rs). - Forced-accept is an explicit administrative override of vote thresholds
(
VoteTally::empty(), no votes); the receipt still verifies because its hash is derived from those exact inputs. - Production grants are minted only for
AppointSteward/ReinstateSteward/ReconfirmSteward(grant_minting.rs), allAuthorityClass::Attestation.mandate_gate.rs::expected_classmaps every wiredMandateActtoExecution(orCastVote→Representation);Attestation"never authorizes acts gated here". No minted grant authorizes any gated act today. MandateGatetargets:Domain(actor-first) andProposalvalidate;Federation(rawString; noFederationIdtype) andRole(StructureId+ holder) always returnWrongTarget—TypedScopecannot bind them.- Taxonomy versioning:
NoMandateReasonandReceiptMandateAttestationare#[non_exhaustive];NoMandateReason's own note says adding a variant "requires a new domain-separation tag on the receipt that consumes it (:v3)". The existing v2 taxonomy is exactly{ NoMandateRequired { MembershipStandingOnly | Bootstrap }, Grant }.
3. The three authority modes
A receipt's ReceiptMandateAttestation must name the actual authority mode. There are at
least three; they must not be collapsed.
- Bootstrap override — forced-accept / administrative override that bypasses vote
thresholds. Attestation:
NoMandateRequired { Bootstrap }. Fits the current v2 taxonomy. - Process / tally authority — a normal proposal close where the governance process itself authorizes the outcome: eligible voters, voting period, quorum/threshold, tally, proposal scope, and outcome rules. This is not membership standing — membership standing authorizes participation and low-blast record-keeping (attendance, action-item completion), it does not by itself authorize an institutional decision. It is also not a personal grant. The current taxonomy has no honest variant for this mode (§4).
- Grant-backed execution — a discrete actor authorized to perform a high/medium-blast
act (charter/federation/steward execution). Attestation:
Grant { grant_ref }. Blocked until Execution-class grants exist and the Federation/Role target-binding gap is solved.
3.1 Decision closure is not payload execution
A GovernanceDecisionReceipt records a decision outcome (closure / tally
certification). That outcome can be process-authorized with no personal grant.
Executing the accepted payload (e.g. activating a charter, joining a federation,
seating a steward) is a separate authority path that may require a grant-backed mandate.
Do not conflate the two: a decision receipt may carry a process-authorized attestation
while a downstream execution receipt/act carries Grant { grant_ref }.
4. Recommendation: a process-authorized attestation, on a v3 decision receipt
Add a positive "process-authorized" attestation mode and emit it on a new
GovernanceDecisionReceiptV3.
- Shape (recommended): a distinct
ReceiptMandateAttestationvariant — e.g.ProcessAuthorized— rather than a newNoMandateReason. Rationale: a democratic decision is positively authorized by process, so labelling itNoMandateRequired("no authority needed") mislabels it — exactly the conflation this doc forbids. The decision receipt already binds the vote tally and votes in its base hash, so the attestation only needs to name the mode (it need not re-encode the tally).- Alternative (lower-churn):
NoMandateReason::ProcessAuthorizedunder the existingNoMandateRequiredarm. Rejected as primary because it reuses the "no mandate" framing for a positively-authorized act. Either way the choice is an ADR-level commitment.
- Alternative (lower-churn):
- Versioning: extending either taxonomy for a decision triggers the
:v3rule — the decision receipt that uses the new mode must beGovernanceDecisionReceiptV3(icn:gov:decision:v3), fresh-hashed over its base fields + the new attestation, never derived from v2. Existing v2 receipts (meeting/action-item, and decision-v2 if/when used forBootstrap) are unaffected because they never emit the new ordinal.
5. Per-path answers
| Decision path | Authority mode | Honest attestation | Emit when |
|---|---|---|---|
| Forced-accept (admin override) | Bootstrap | NoMandateRequired { Bootstrap } |
Now — fits v2 taxonomy |
| Normal democratic close (vote authorizes outcome) | Process/tally | ProcessAuthorized (new) |
After §4 lands → v3 |
| Close whose outcome triggers/represents execution authority (charter/federation/steward) | Grant-backed execution | Grant { grant_ref } on the execution receipt/act |
Blocked on Execution grants + target binding |
Explicit answers to §12 Q4 and the review questions:
- Which v2 paths emit immediately: only forced-accept →
Bootstrap. - Which need a new process-authorized mode first: all normal democratic closes —
they must not be emitted as
MembershipStandingOnly, and the current taxonomy has no honest variant, so they wait for §4 (→ v3). - Which wait for
Grant: the execution of accepted high/medium-blast payloads (charter/federation/steward) — a separate receipt/act, not the decision closure itself. - Must
NoMandateReason/attestation be extended before normal-close v2 emission? Yes — and extending it for a decision means a v3 decision receipt, not v2. - Mixed attestation across the chain? Yes: the decision receipt may carry
ProcessAuthorizedwhile the downstream execution receipt carriesGrant.
6. Grant-minting + target-binding gaps (for the execution path)
Before any Grant-backed decision/execution attestation or MandateGate handler wiring is
honest:
- Execution-class grant minting (decomposition Q7–Q10): decide which proposal classes
mint
AuthorityClass::Executiongrants, the grantee (named person/role), a truthful term/expiry (fail-closed on overflow, per theAppointStewardprecedent ingrant_minting.rs), and the target.derive_grants_for_accepted_proposalis the extension point. None exist today. - Federation/Role target binding (Q11–Q12): a
TypedScopebinding surface (or a(domain, act, target) → mandate_idindex) soFederation/Rolestop returningWrongTarget. Federation also lacks aFederationIdtype (rawString).
Until both land, MandateGate::require() handler wiring is always-reject theater and is
deferred.
7. Next-code sequencing (after this design RFCs)
- Extend the attestation taxonomy per §4 (new
ProcessAuthorizedmode) — the smallest typed change inicn-governance/src/proof.rs, plusGovernanceDecisionReceiptV3if the:v3rule is confirmed. - Emit
GovernanceDecisionReceiptV3(and v2-Bootstrap) for the honestly-attestable paths: forced-accept →Bootstrap; normal democratic close →ProcessAuthorized. Reuse the fail-closedput_opaquerouting pattern (#1934/#1935); no typed gateway import. - Only then the execution-authority track: Execution-grant minting (§6), Federation/Role
target binding (§6), and finally
MandateGate::require()handler wiring.
8. Open questions for the ADR
ProcessAuthorizedas a distinct attestation variant (recommended) vs a newNoMandateReason— freeze the shape.- Confirm the
:v3rule applies (decision receipt must bump) vs a documented exception. - Whether
ProcessAuthorizedshould carry any process reference (it need not — the tally is already in the base hash) or stay a bare discriminator likeBootstrap. - The Execution-grant grantee/term/target shape (§6) — likely its own design slice.