Skip to content

Prototype SV governance voter flow#5533

Open
ericmann wants to merge 31 commits into
canton-network:feature-sv-vote-dappfrom
Avro-Digital:eric-avro/prototype-sv-governance-voter-flow
Open

Prototype SV governance voter flow#5533
ericmann wants to merge 31 commits into
canton-network:feature-sv-vote-dappfrom
Avro-Digital:eric-avro/prototype-sv-governance-voter-flow

Conversation

@ericmann
Copy link
Copy Markdown
Contributor

Summary

This PR is a prototype for maintainer review and discussion, not necessarily a final upstream design. It makes the proposed Phase 1 SV governance-voter model concrete for discussion under canton-foundation/canton-dev-fund#223, especially Milestone 1: Governance-Voting Identity and CIP.

Phase 1 preserves the existing one-vote-per-SV semantics. A governance voter is modeled as an alternate signer for a represented SV's vote, not as a new voting unit or source of additional vote weight.

Changes included:

  • Add a hardcoded governance-voter action taxonomy via isGovernanceVoterAction, with new ActionRequiringConfirmation constructors rejected by default until explicitly reviewed.
  • Extend Vote with castBy and castByRole so vote records distinguish the represented SV from the party/authority path that signed the vote.
  • Add SvGovernanceVoter, an SV-declared binding template without a contract key; downstream paths fetch it by contract ID and validate the one-active-binding assumption at the workflow level.
  • Add DsoRules_CastGovernanceVote, which validates the binding, represented SV, governance-voter signer, signer role, and action allowlist before writing the vote into the represented SV's existing vote slot.
  • Add Daml tests for the action taxonomy, vote-slot/tally semantics, binding lifecycle and authorization, and governance-voter casting/overwrite scenarios.
  • Add an SV-operator documentation note describing the prototype scope, proposed allowlist, attribution semantics, binding invariant, and explicit contract-ID submission shape.

(Internal) Design IDs covered: GV-001, GV-002, GV-003, GV-004, GV-005, GV-006.

Notes For Reviewers

  • SRARC_OffboardSv is included in the proposed Phase 1 allowlist so reviewers can evaluate it explicitly; this should be validated through maintainer/CIP review because it is a high-impact membership action.
  • governanceVoter == sv is allowed for bootstrap/self-voting, while governanceVoter == dso is rejected.
  • The binding is unilateral/SV-declared in this prototype. If maintainers prefer bilateral consent, this can move toward a Propose-Accept shape in a follow-up revision.
  • The binding intentionally has no contract key in this prototype. The one-active-binding-per-SV rule is treated as an invariant to validate rather than a template-key constraint.

Test Plan

  • direnv exec . sbt "splice-dso-governance-test-daml/damlTest"
  • direnv exec . sbt damlDarsLockFileUpdate

ericmann added 6 commits May 13, 2026 16:39
Define the initial Phase 1 allowlist for governance-voter eligible actions and cover the proposed taxonomy plus represented-SV vote-slot semantics in Daml tests.

Signed-off-by: Eric Mann <eric@avrofi.com>
Add explicit vote-cast role metadata so the vote record can identify the represented SV and operator signing path without changing tally semantics.

Signed-off-by: Eric Mann <eric@avrofi.com>
Introduce the SV-declared governance-voter binding lifecycle without a contract key so the Phase 1 authority model can be reviewed independently of submit-path mechanics.

Signed-off-by: Eric Mann <eric@avrofi.com>
Use the SV governance-voter binding to authorize alternate signing while recording the vote in the represented SV's existing vote slot.

Signed-off-by: Eric Mann <eric@avrofi.com>
Keep the consolidated prototype to a single governance package version bump so intermediate stack-only DAR versions do not leak into the final review branch.

Signed-off-by: Eric Mann <eric@avrofi.com>
Use represented SV parties as vote map keys and prevent governance-voter submissions from replacing prior operator votes, making the shared vote-slot semantics explicit for review.

Signed-off-by: Eric Mann <eric@avrofi.com>
Comment thread daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml Outdated
Comment thread daml/splice-dso-governance-test/daml/Splice/Scripts/TestGovernance.daml Outdated
State that explicit disclosure is the supported submission path for unaffiliated governance voters, keep SV-hosted relay as an optional deployment choice, and flag CIP-0103 alignment as the remaining review item before this prototype is promoted out of draft.

Signed-off-by: Eric Mann <eric@avrofi.com>
Comment thread daml/splice-dso-governance/daml/Splice/DSO/GovernanceVoter.daml Outdated
CIP-0103 governs the dApp/Wallet API rather than on-ledger contract shape; the cast choice is already compatible with an external-party prepareExecute flow with disclosed contracts. The remaining alignment work lives in the governance-voter dApp client, not in this PR.

Signed-off-by: Eric Mann <eric@avrofi.com>
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
ericmann added 7 commits May 14, 2026 16:44
Address review feedback: drop the operator override on
governance-voter eligible actions, route their vote requests through
the governance-voter path, and replace clearing with rotation back to
the represented SV (with onboarding default of self-voting).

- Remove the ClearGovernanceVoter choice from SvGovernanceVoter; the
  represented SV always has a binding, and "return to operator" is
  RotateGovernanceVoter back to self.
- Add DsoRules_RequestGovernanceVote (governance-voter controlled) and
  reject governance-voter eligible actions on DsoRules_RequestVote /
  DsoRules_CastVote. DsoRules_CastGovernanceVote no longer needs the
  operator-overwrite guard.
- Update DsoTestUtils helpers to dispatch by action type and to
  auto-create self-bindings; rewrite the prototype lifecycle/cast tests
  for the new policy; port existing direct request/cast call sites to
  the appropriate path.
- Refresh the bundled splice-dso-governance 0.1.25 DAR, dars.lock, and
  the SV governance-voter doc.

Signed-off-by: Eric Mann <eric@avrofi.com>
Phase 1 does not enforce a one-active-binding-per-SV invariant: the
represented SV may keep more than one SvGovernanceVoter contract alive at
the same time, and any active binding can authorize a cast for the
represented SV. All such casts land in the represented SV's single vote
slot under the same per-SV cooldown, so additional bindings broaden the
set of authorized signers without changing the one-vote-per-SV tally.

- Document the multi-binding semantics in the SV governance-voter doc.
- Add testGovernanceVoterMultipleBindings exercising two concurrent
  bindings for sv1 (delegateA, delegateB), validating that both can cast
  into the same represented-SV slot, that the second cast overwrites the
  first via the cooldown path, and that a delegate cannot cast against
  the other delegate's binding (signer-must-match-binding check).

Signed-off-by: Eric Mann <eric@avrofi.com>
DsoRules_CastVote and DsoRules_CastGovernanceVote previously accepted
votes regardless of how much time had elapsed since the request was
opened. The voting period is meaningful only if both the request
choices and the cast choices enforce it.

Add a deadline check on both cast paths that mirrors the existing
close-vote semantics: when targetEffectiveAt is set the voting period
extends to the effective time (matching the documented behavior that
SVs can keep voting between voteBefore and targetEffectiveAt); when
it is not set the deadline is voteBefore.

Add testCastDeadlineExpiry to lock down both branches.

Signed-off-by: Eric Mann <eric@avrofi.com>
The operator cast path tells the caller which choice to use when the
action does not belong on that path ("use DsoRules_CastGovernanceVote").
The governance-voter cast path was missing the equivalent hint and read
as a flat statement of fact. Match the operator path's wording so the
two messages are symmetric and point the caller at the right choice.

Signed-off-by: Eric Mann <eric@avrofi.com>
DsoRules_CastVote previously overwrote the caller-supplied castBy and
castByRole silently, mirroring the operator authority but masking any
caller-side mismatch. DsoRules_CastGovernanceVote instead validates the
caller-supplied values against the binding before overwriting; the
operator path should follow the same fail-loud pattern so caller bugs
surface immediately.

Add two require checks on the operator cast path: castBy must equal
vote.sv, and castByRole must be VCR_Operator. The silent overwrite
remains for forward-compatibility with future timestamping but no
longer hides incorrect callers.

Add testOperatorCastAttributionGuards to exercise both guards.

Signed-off-by: Eric Mann <eric@avrofi.com>
The binding template's module comment claims "there is intentionally no
Clear choice," but the implicit per-signatory Archive choice still lets
the represented SV unilaterally archive any of its bindings (the SV is
the sole signatory). Reviewers reading the no-Clear claim shouldn't
mistake it for a hard invariant.

Acknowledge the escape hatch in the template's module comment and in
the SV governance-voter doc, and frame it as self-harm only that is
trivially recoverable via a new self-binding. Hard enforcement of "an
SV always has at least one active binding" is deferred.

Signed-off-by: Eric Mann <eric@avrofi.com>
The SV dApp PoC discussion landed on a single governance-voter party
per represented SV: accountability is hard enough with one voting
party to declare, and multi-user assignment lives at the dApp/UI layer
rather than via multiple ledger bindings. This reverses the earlier
prototype note that explicitly permitted multiple concurrent bindings.

- Update the SV governance-voter doc to state the single-binding
  intent and flag that Phase 1 enforces it through the workflow
  (RotateGovernanceVoter exclusively) rather than at the template
  level. Promotion to a contract key or registry is left as an open
  question for Splice maintainers.
- Drop testGovernanceVoterMultipleBindings, which exercised behavior
  the design no longer endorses.
- Add an explicit "exactly one active binding for sv1" assertion at
  each step of testSvGovernanceVoterBindingLifecycle so the invariant
  is visible in the test, not just implied by the consuming choice.

Signed-off-by: Eric Mann <eric@avrofi.com>
ericmann added 3 commits May 14, 2026 21:19
Code review on the prototype draft pointed out that the
single-active-binding-per-SV invariant is workflow-only (onboarding
default plus consuming RotateGovernanceVoter), not enforced by the
SvGovernanceVoter template itself. If a represented SV does bare-create
parallel bindings, both delegates can cast against the same represented-
SV slot under last-writer-wins.

This commit captures that boundary as a test so future work
(contract key, single-binding registry, or onboarding-side
enforcement) has a concrete behavior to compare against:

- testGovernanceVoterDuplicateBindingsAmbiguity exercises sv1 bare-
  creating two SvGovernanceVoter contracts in parallel, has each
  delegate cast against the same represented-SV slot, and asserts
  the slot count stays at one with last-writer-wins on castBy and
  castByRole.

No template or choice changes; the test pins existing behavior the CIP
also flags as an open review question.

Signed-off-by: Eric Mann <eric@avrofi.com>
DsoRules_CastGovernanceVote used fetchChecked + archive because it needs
to validate that the request action is governance-voter eligible before
consuming it (unlike DsoRules_CastVote, which uses fetchAndArchive after
the operator-path eligibility check). The cooldown check sat after the
explicit archive, which is functionally correct because the transaction
aborts on failure, but is non-idiomatic: validation should precede
destructive operations.

Move enforceCooldown above archive requestCid in
DsoRules_CastGovernanceVote so all gate checks (binding validity, role,
SV membership, action eligibility, deadline, cooldown) run before the
request is consumed. The operator path already follows this order via
fetchAndArchive; this aligns the governance-voter path stylistically.

Bump splice-dso-governance to 0.1.26 and refresh dars.lock.

Signed-off-by: Eric Mann <eric@avrofi.com>
A re-review pointed out that the prototype design doc described the
single-active-binding shape as preserved "by construction" through the
consuming RotateGovernanceVoter lifecycle, and as "enforced through the
workflow", both of which overstate what the template actually
guarantees. The represented SV is the sole signatory and can bare-
create additional bindings; the consuming rotation only enforces that
one specific binding is consumed when a rotation happens, not that no
parallel binding ever exists.

Soften the wording in docs/src/sv_operator/sv_governance_voter.rst to
match the language in the companion CIP draft:

- The intended Phase 1 workflow has one active governance-voter party
  per represented SV, shaped by the consuming rotation and self-
  binding onboarding default, rather than preserved as a contract-
  level invariant.
- Spell out the residual behavior: if two bindings do coexist, both
  delegates can cast under last-writer-wins; the tally is still one
  vote per represented SV, but the cast log becomes ambiguous.
- Reference testGovernanceVoterDuplicateBindingsAmbiguity, which pins
  that behavior so any future tightening has a baseline.

No Daml code or test changes; the template comments and the new test
already use the workflow-intent framing.

Signed-off-by: Eric Mann <eric@avrofi.com>
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DSO/GovernanceVoter.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DSO/GovernanceVoter.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DSO/GovernanceVoter.daml
Comment thread daml/splice-dso-governance/daml/Splice/DSO/GovernanceVoter.daml Outdated
Comment thread daml/splice-dso-governance/daml/Splice/DsoRules.daml Outdated
ericmann added 13 commits May 19, 2026 15:56
Bring two user-facing labels in `buildAmuletConfigChanges.ts` into line
with the `no_illegal_daml_references` allow-list, which permits the
phrase "Amulet to Issue" (matching the camel-case identifiers
`amuletToIssuePerYear` / `AmuletToIssuePerYear` already used in the
file) but not the lowercase "Amulet to issue". Purely cosmetic; no
behavior change. Unblocks the pre-commit hook for follow-up commits.

Signed-off-by: Eric Mann <eric@avrofi.com>
Address review comments canton-network#10 and canton-network#11 by making the DSO party the sole
signatory of SvGovernanceVoter and moving the rotation flow off the
template and into DsoRules:

- SvGovernanceVoter: signatory dso, observer sv only (governanceVoter
  is no longer a ledger observer; it discovers the binding via Scan or
  explicit CIP-0103 disclosure, so a participant hosting that party
  does not need to vet the splice-dso-governance DAR). On-template
  RotateGovernanceVoter choice removed.
- DsoRules: new SRARC_RotateGovernanceVoter action and internal
  DsoRules_RotateGovernanceVoter choice. Rotation runs through the
  standard confirmation-quorum flow, so a single SV operator cannot
  unilaterally rotate their own binding. Adds HasCheckedFetch
  SvGovernanceVoter ForDso to support the fetchAndArchive in the
  choice body.
- DsoRules_AddSv: createPerSvPartyContracts now atomically establishes
  the (sv, sv) self-binding at onboarding, preserving the
  one-binding-per-represented-SV invariant from contract creation.
- Tests rewritten against the new flow: strict ensureSelfBinding
  lookup, new rotateGovernanceVoterByConfirmation helper, lifecycle
  and cast-path tests exercise the confirmation-quorum rotation, and
  the SRARC taxonomy test treats SRARC_RotateGovernanceVoter as a
  non-governance-eligible operational action. The
  duplicate-bindings-ambiguity test is removed since SV bare-create is
  no longer authorized.
- Docs: sv_governance_voter.rst rewritten to describe the
  DSO-signatory model, atomic onboarding self-binding, and
  confirmation-quorum rotation.
- splice-dso-governance bumped to 0.1.27; dars.lock and the versioned
  DAR refreshed accordingly.

Signed-off-by: Eric Mann <eric@avrofi.com>
The `sv /= dso && governanceVoter /= dso` invariant is already enforced
by the only paths that create the contract:

- onboarding (`DsoRules_AddSv` / `createPerSvPartyContracts`) installs a
  self-binding for a newly added SV party, which by construction is not
  the DSO party, and
- rotation (`DsoRules_RotateGovernanceVoter`) explicitly requires
  `newGovernanceVoter /= dso`.

No other contract template in this package uses an `ensure` clause for
cross-field party-distinctness, so the assertion was both redundant and
inconsistent with the rest of the codebase.

Signed-off-by: Eric Mann <eric@avrofi.com>
Replace the SRARC_* wildcard fall-through in `isGovernanceVoterAction`
with an explicit case per constructor (preserving current True/False
truth values byte-for-byte). New SRARC_* additions will now fail to
compile here, forcing the author to make an explicit eligibility
decision rather than silently inheriting a default.

The CRARC_* (AmuletRules) branch keeps its wildcard intentionally:
AmuletRules actions are owned by a separate codebase area and changing
their default of non-eligibility should be driven from there, not from
this SV-side taxonomy. The docstring records the asymmetry.

Signed-off-by: Eric Mann <eric@avrofi.com>
Two upgrade-rule violations introduced by the governance-voter work,
flagged on review:

1) VoteRequest.votes had its key changed from `Map.Map Text Vote` to
   `Map.Map Party Vote`. Existing on-ledger VoteRequest contracts have
   Text (SV name) keys; changing the key type is a breaking change.
   Revert to Text keys. The represented SV's party is still recoverable
   from `vote.sv`.

2) The Vote record gained two required fields (`castBy : Party`,
   `castByRole : VoteCastRole`) inserted in the middle of the record.
   Daml upgrade rules require new fields to be optional and appended
   at the end so existing contracts can be lifted with `None` values.
   Move both fields to the end and wrap in `Optional`.

Knock-on changes:

- `DsoRules_CastGovernanceVote` lifts `castBy` to a top-level choice
  argument so it can serve as a (non-Optional) controller. The
  `Vote.castBy` / `Vote.castByRole` fields remain as attribution
  metadata: server-set to `Some _` for new votes, `None` for legacy
  contracts. The choice no longer validates `vote.castByRole`; the
  role is implicit in the choice itself and is recorded on the
  resulting Vote.
- `DsoRules_CastVote` drops its input-validation requires for
  `vote.castBy` / `vote.castByRole`. Those fields were overwritten on
  recording so the validation was cosmetic; the controller `vote.sv`
  remains the real authorization. The `testOperatorCastAttributionGuards`
  test that exercised the dropped validations is removed.
- All vote-construction sites (in DsoRules choices, DsoTestUtils, and
  TestGovernance) set `castBy = Some <party>` and `castByRole = Some
  <role>` explicitly for new votes.
- All map operations on `votes` use SV names (`Text`) as keys: cast
  paths look up the voter's name via the `svs` map and key insertions
  on that name; `CloseVoteRequest` iterates `(_, vote)` and uses
  `vote.sv` to determine represented-SV activity. `offboardedVoters`
  continues to wire-encode the offboarded SV's party via
  `partyToText vote.sv`, preserving the existing field semantics.
- Tests updated accordingly: `Map.lookup "sv1" request.votes`,
  `vote.castBy === Some <party>`, etc.

No Scala touched: codegen consumers of `request.votes` only iterate
`.values()`, and no Scala references `castBy` / `castByRole` today.

splice-dso-governance bumped to 0.1.30; dars.lock and the versioned
DAR refreshed accordingly.

Signed-off-by: Eric Mann <eric@avrofi.com>
…me binding

In the governance-voter request/cast choices, replace the bare `fetch`
of the SvGovernanceVoter binding with a checked fetch against
`ForOwner with dso; owner = <controller>`. The new `HasCheckedFetch
SvGovernanceVoter ForOwner` instance validates both the DSO and that
the caller is the binding's authoritative governance voter in a single
step, so the manual `require` checks for those invariants are dropped.
Also rename the local variable `binding` to `svGovernanceVoter` so it
matches the template name and is unambiguous next to the
`governanceVoter` party field.

Signed-off-by: Eric Mann <eric@avrofi.com>
…al binding

`DsoRules_RequestVote` now takes an Optional `bindingCid :
ContractId SvGovernanceVoter` appended at the end and branches in
the body:
* `bindingCid = None`  -> operator path (existing behavior). The
  controller `requester` is the represented SV; action must NOT be
  governance-voter eligible; the initial vote is recorded with
  VCR_Operator.
* `bindingCid = Some _` -> governance-voter path. The controller
  `requester` is the governance voter named on the binding; the
  binding is checked-fetched via `ForOwner with owner = requester`;
  action must be governance-voter eligible; the represented SV is
  taken from the binding and the initial vote is recorded with
  VCR_GovernanceVoter.
`DsoRules_RequestGovernanceVote` and its result type are removed;
all Daml callers (test helper and tests) migrate to the unified
choice. Docs updated to describe the optional-binding shape.

Signed-off-by: Eric Mann <eric@avrofi.com>
…ding

`DsoRules_CastVote` now takes two Optional fields appended at the end:
* `bindingCid : Optional (ContractId SvGovernanceVoter)` — when `Some`,
  selects the governance-voter path.
* `castBy : Optional Party` — the governance-voter party signing the
  cast on the governance-voter path; serves as the choice controller.
  Must be present iff `bindingCid` is.
The controller expression is `fromOptional vote.sv castBy`, so on the
operator path (both fields `None`) the controller remains `vote.sv` as
before. On the governance-voter path the controller is the `castBy`
party, the binding is checked-fetched via `ForOwner with owner = castBy`,
the recorded vote's SV is taken from the binding (cannot drift from
`vote.sv`), and the vote is attributed with `VCR_GovernanceVoter`. The
two paths are otherwise unified: action-eligibility check, deadline
check, per-SV cooldown, and request archival are shared.
`DsoRules_CastGovernanceVote` and its result type are removed; all Daml
callers (test helper, tests, docs) migrate to the unified choice.

Signed-off-by: Eric Mann <eric@avrofi.com>
…e shapes

The Daml-side schema changes in this PR add Optional fields to several
records and choices that the Scala apps construct directly via Java
codegen. This commit updates every such call site so the apps continue
to compile against the new codegen output:

* Vote record (now 6 fields): added trailing `Optional.empty()` for
  `castBy` and `castByRole` (server-set attribution metadata; `None`
  on legacy contracts) at every `new Vote(...)` site.
* DsoRules_RequestVote choice (now takes optional `bindingCid`):
  added trailing `Optional.empty()` to the SvApp caller; this caller
  is the operator path, so `bindingCid = None` is the correct value.
* DsoRules_CastVote choice (now takes optional `bindingCid` and
  `castBy`): added two trailing `Optional.empty()` to both production
  callers (SvApp and CopyVotesTrigger), keeping them on the operator
  path.

No semantic change. Governance-voter routing for these app paths is
not wired up here; that's deferred to a follow-up.

Signed-off-by: Eric Mann <eric@avrofi.com>
Records the SvGovernanceVoter binding under which each governance-voter
cast was made (new Vote.bindingCid : Optional, appended at the end for
upgrade). At close, DsoRules_CloseVoteRequest now takes an optional
currentBindings : Optional [ContractId SvGovernanceVoter] argument; when
supplied, the choice fetchCheckeds each (forcing the caller to provide
only live bindings), builds an SV -> live-binding map, and drops any
governance-voter-cast vote whose recorded bindingCid is no longer the
live binding for its SV. Dropped voters are reported via a new
DsoRules_CloseVoteRequestResult.staleBindingVoters : Optional [Text]
(None when no check ran, Some [...] otherwise) -- mirroring the existing
offboardedVoters channel. currentBindings = None preserves pre-staleness
behavior for callers that have not yet adopted the new arg.
Why pass the live set instead of trying to detect archived bindings
intrinsically: SDK 3.4 cannot catch fetch-on-archived in the Update
monad, so the close choice cannot independently distinguish "still
authoritative" from "rotated". Passing the live set is the same trust
shape as already used for amuletRulesCid and the controller sv.
Tests in TestGovernance.daml exercise both branches:
 - testStaleBindingDropsVote: open a gov-voter-eligible request via a
   delegate, collect 4 accepts, rotate the represented SV's binding,
   close with currentBindings = [...live...] and assert the rotated-out
   voter shows in staleBindingVoters and the remaining 3 still satisfy
   the 3-of-4 threshold (VRO_Accepted).
 - testStalenessCheckOptIn: same setup but close with
   currentBindings = None; staleBindingVoters = None, all 4 votes count.
Scala/Java codegen consumers (SvApp, CopyVotesTrigger,
CloseVoteRequestTrigger, SvDsoStoreTest, ScanStoreTest, StoreTestBase)
are updated to thread the new Optional fields with empty defaults.

Signed-off-by: Eric Mann <eric@avrofi.com>
Use the SvGovernanceVoter self-binding for governance-voter-eligible request and cast submissions from the SV app, copy votes via the governance-voter path when required, and pass live bindings into automated close-vote submissions so staleness is actually checked.

Signed-off-by: Eric Mann <eric@avrofi.com>
Require close-vote staleness checks to supply exactly one live binding for each active SV, and add DsoRules/SV automation cleanup for orphaned or duplicate SvGovernanceVoter bindings left by offboarding, re-onboarding, or older package versions.

Signed-off-by: Eric Mann <eric@avrofi.com>
Keep the PR to a single splice-dso-governance package version bump from
0.1.24 to 0.1.25. Rebuild the final DAR contents under 0.1.25, remove the
intermediate 0.1.26-0.1.35 DARs, and update dars.lock accordingly.

Signed-off-by: Eric Mann <eric@avrofi.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants