Repository navigation
feat(sdk): multiple choice decision proposals in rs-sdk, wasm-sdk and js-evo-sdk - #5903
Conversation
… js-evo-sdk Multiple choice proposals (PV15) had no client helpers: an app assembled the proposal, its choices and the vote from generic document and contested vote calls, and had to know that a vote for a choice carries the choice's document id where a contest among identities carries an identity id. rs-dpp names the contest a proposal's choices run in from the proposal's id alone and maps a multiple choice answer (a choice, abstain, none of the choices) to the resource vote the contest takes. rs-sdk builds a proposal, its edit and a choice, gives the proposal's processing fee and the fund a choice pays, lists proposals, and reads the vote, the kept choice and a masternode's answer. wasm-sdk and js-evo-sdk (sdk.decisionProposals.multipleChoice) bind them. The contested vote state paging loop of get_contested_dpns_vote_state and the contender count of prefunded_voting_balance_to_join are now shared functions taking the poll; both keep their behaviour. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 29 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
|
✅ Final review complete — no blockers (commit 966c1a5) · triage: normal |
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
Static verification at b8cdb7f found no blocking issues and three non-blocking suggestions concerning finalized-result limits, unnecessary cloning, and WASM result coverage. The preliminary kept-choice concern is incorrect: active choices live in contest storage, and only the awarded choice enters ordinary document storage. Current Rust, wasm32, JavaScript build/lint, Evo SDK, and platform test-suite checks pass; Dashmate Local network E2E fails while synchronizing Core blocks to height 1392, with no demonstrated connection to these client changes.
🟡 3 suggestion(s)
Approved: nothing here blocks the merge. The non-blocking findings are optional: fix them forward, in this PR or a follow-up, without waiting for another review round.
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
normalbygpt-6.1-sol(effort low) — This is a large cross-cutting SDK, WASM, and DPP helper implementation involving proposal, voting, paging, and fee-related logic, but it does not directly change consensus, cryptography, signatures, storage, or other critical surfaces. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Second Phase-2 pass: on this first full review every Phase-2 reviewer also ran a second, independent time, and the verifier weighed both sets
- Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— ffi-engineer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— ffi-engineer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review (PastaClaw). How to work through them:
- Verify each finding against the code; fix only what is real, as narrowly as the finding.
- If a finding is wrong, decline it on its thread in one or two sentences; the bot answers once and stops.
- To defer one, link a tracking issue on its thread (or reply asking the bot to file one).
- Suggestions don't block the merge, but most are worth fixing in this PR; judge each on its merits.
- Keep the PR's scope; don't grow it to satisfy hardening suggestions.
- Carried items were raised in an earlier round: read the thread first, and don't re-fix what you already fixed, declined or deferred there. Disputed and deferred items are already handled and are not listed.
In `packages/rs-sdk/src/platform/documents/contest_vote_state.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/documents/contest_vote_state.rs:60-63: Enforce the contender limit for finalized contests
The helper documents `limit` as the maximum number of contenders returned, but it accepts the entire decoded page without enforcing that bound. For a finalized contest, `verify_vote_poll_vote_state_proof_v0` reconstructs every final contender tally from the single stored-info item, irrespective of the requested page limit, and `Contenders::FromProof` preserves that complete map. A finalized contest with three choices therefore returns all three for `Some(1)`; the next iteration only notices that the limit has already been exceeded. This behavior is carried into the new public helper by the moved DPNS implementation. Bound the accumulated contender map while retaining the winner and aggregate tallies, and add a regression test with a finalized response larger than the requested limit.
In `packages/rs-sdk/src/platform/decision_proposals/multiple_choice.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/decision_proposals/multiple_choice.rs:272-273: Move each owned contender document instead of cloning it
The preceding `into_iter()` transfers ownership of each decoded contender into this closure, but `contender.document().clone()` deep-copies its document immediately before the original is dropped. That repeats property and string allocations for every running choice on each vote-state read. Use the existing `take_document()` accessor to move the document into `Choice::from_document`. The same conversion also clones the document-type schema through `to_owned_document_type()`; iterating the serialized contenders with `try_to_contender(document_type, platform_version)` would additionally avoid that schema copy.
In `packages/wasm-sdk/src/decision_proposals/multiple_choice.rs`:
- [SUGGESTION] packages/wasm-sdk/src/decision_proposals/multiple_choice.rs:272-282: Exercise the vote-state JavaScript result shape offline
The added tests do not exercise `vote_to_js`: native tests stop at `MultipleChoiceProposalVote::from_contenders`, the WASM JavaScript tests cover edits and option parsing, and facade tests stub `getMultipleChoiceProposalVote`. Consequently, the new public result shape—base58 map keys, wrapped choice documents, optional tallies, and awarded-versus-locked finalization results—has no regression coverage across the JavaScript boundary. Add offline WASM fixtures for empty, running, awarded, and locked votes and assert the converted result fields; this does not require a protocol-15 network.
|
Bots are done — your move: post |
A contest that ended answers a vote state page with every contender of its record, whatever limit the page asked for, so fetch_contested_resource_vote_state (and get_contested_dpns_vote_state, which calls it) returned more contenders than `limit`. The contenders are now cut to the limit, the first in id order kept; the winner and the abstain and lock tallies are returned whole. The multiple choice vote reader moves each decoded choice document instead of cloning it, and no longer copies the document type. New wasm binding tests cover the JavaScript shape of the vote state (no choice, running, kept a choice, kept none) and the vote choice round trip. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
Two blocking correctness defects remain in the new SDK read helpers. Finalized contests can cause the shared pagination loop to repeat indefinitely, and fetching a kept choice does not consult the finalized vote winner, so it can return an active choice or fail when multiple choices exist. CI reports no completed code-check failures, although the Rust and platform test jobs are still in progress.
🔴 2 blocking
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
normalbygpt-6.1-sol(effort low) — This is a large cross-language SDK and WASM feature spanning proposal construction, document queries, voting, fee calculations, and shared paging behavior, but it does not modify consensus, funds movement, cryptography, key handling, network deserialization, or storage migrations. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— ffi-engineer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review (PastaClaw). How to work through them:
- Verify each finding against the code; fix only what is real, as narrowly as the finding.
- If a finding is wrong, decline it on its thread in one or two sentences; the bot answers once and stops.
- To defer one, link a tracking issue on its thread (or reply asking the bot to file one).
- Suggestions don't block the merge, but most are worth fixing in this PR; judge each on its merits.
- Keep the PR's scope; don't grow it to satisfy hardening suggestions.
- Carried items were raised in an earlier round: read the thread first, and don't re-fix what you already fixed, declined or deferred there. Disputed and deferred items are already handled and are not listed.
In `packages/rs-sdk/src/platform/documents/contest_vote_state.rs`:
- [BLOCKING] packages/rs-sdk/src/platform/documents/contest_vote_state.rs:67-72: Stop pagination when a finalized contest returns its full record
The loop decides whether to issue another page solely from the returned page length. For a finalized contest, the proof verifier reconstructs the complete contender map even when the query includes a limit and start position, and `page.winner.is_some()` identifies that finalized response. With `limit = None`, `fetch_multiple_choice_proposal_vote` therefore receives the full record, sets `start_at` to the same last contender, receives the same record again, and loops indefinitely for contests with at least `DEFAULT_QUERY_LIMIT` contenders. Stop pagination as soon as the page contains finalized winner information; this also avoids the unnecessary extra proved query for a finalized contest.
In `packages/rs-sdk/src/platform/decision_proposals/multiple_choice.rs`:
- [BLOCKING] packages/rs-sdk/src/platform/decision_proposals/multiple_choice.rs:614-622: Use the finalized vote winner to fetch the kept choice
`fetch_kept_choice` queries the `byProposal` contested index directly and never reads the contest's winner. While voting is still active, that index contains the active choices: with one choice this returns an active choice even though the method documents `None` until finalization, while multiple choices make a single-document fetch non-unique. Read the bounded contest state first, return `None` unless the winner is `WonByIdentity(choice_id)`, and then fetch the choice by that exact document id. Return `None` for a running vote, a locked vote, and a proposal with no choices.
|
Your move: thepastaclaw requested changes on this head; dismiss the review or push a fix; thepastaclaw left review threads unresolved; resolve them. |
A contest that ended answers its first vote state page with its whole record. The read loop took a full page as a sign of more and asked for the next one, which a finished contest answers empty: one proved query for nothing. The loop now stops at a page that names a winner. fetch_kept_choice stays one document query through the byProposal index, and its comment says why that is enough: the choices a vote holds are in the vote's own storage, so the index holds a choice only once a vote has kept it. The consensus tests now assert it: a document query of a proposal's choices finds none while the vote runs, none after a lock, and only the kept choice after an award. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
Waiting for bot review — thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
The current head resolves all five prior findings: finalized contest reads now stop after the complete record and enforce limits, vote-state conversion moves decoded data without cloning, and the WASM result shape has offline coverage. One remaining interop mismatch is present in the new multiple-choice listing APIs: the Rust helper emits null for missing documents while both WASM and facade declarations advertise undefined; Rust CI passed, while JavaScript and WASM checks were still queued in the supplied CI snapshot.
🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)
Approved: nothing here blocks the merge. The non-blocking findings are optional: fix them forward, in this PR or a follow-up, without waiting for another review round.
Follow-up candidates (optional) (1)
New non-blocking observations on code unchanged since the last full review. No inline threads are opened for them and later reviews do not repeat them: fix forward or track separately if they are worth it.
🟡 Suggestion: Declare null values in multiple-choice proposal listing maps
packages/wasm-sdk/src/decision_proposals/multiple_choice.rs:482-485
The new getMultipleChoiceProposalsByOwner and getMultipleChoiceProposalsByCreatedAt bindings declare Map<string, Document | undefined>, but documents_map converts a missing Rust document to JsValue::NULL. The JavaScript facade also repeats the Document | undefined return type at multiple-choice-facade.ts:69-80, so consumers can receive null while TypeScript says only undefined is possible. Change both WASM return annotations and both facade return types to Document | null, or change documents_map to emit undefined consistently.
source: gpt-6.1-sol (phase2-reviewer: ffi-engineer)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)
- Triage:
normalbygpt-6.1-sol(effort low) — This is a large, cross-cutting client-library change spanning Rust, WASM, and JavaScript with voting and paging behavior changes, but it does not itself modify consensus, cryptography, signatures, or other critical surfaces. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity quota lookup failed),glm-5.3-flash(not used above high effort; tier asks max) - Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Code unchanged since the last final review (
f0621c6c): 1 new suggestion(s) listed as follow-up candidates, not posted inline - Fresh verifier:
gpt-6.1-sol— final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— ffi-engineer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— ffi-engineer (completed, effort high); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort high); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.
No unresolved findings remain from the prior review on this head.
|
Bots are done — your move: post |
Basic explanation
What this does: A multiple choice proposal is a question put to the masternodes whose answers ("choices") anyone, or only its author, can enter; the masternodes vote and the choice with the most votes is kept. The chain already supports this (protocol version 15), but an app had to assemble every piece by hand from generic document and vote calls. This adds ready-made helpers to the Rust SDK and the JavaScript SDKs: build a proposal, edit it, enter a choice, list proposals, read the vote, and cast a masternode's vote.
Value: Yes/no proposals already had these helpers; multiple choice proposals had none. The detail apps were most likely to get wrong is now handled in one place: a vote for a choice must carry the choice's document id, not the id of the identity that entered it (the chain refuses the latter). Apps can also show the cost before signing: 0.2 Dash for a proposal, 0.1 Dash for a choice.
Risks: Low. No consensus code changes: this is client code, plus constants and two small pure functions in
rs-dppthat nothing on the block execution path calls. One existing SDK function (get_contested_dpns_vote_state) now delegates to a shared paging function; its existing tests pass unchanged. One behaviour of it changes: for a contest that has ended it used to return every contender whateverlimitwas passed, and it now returns at mostlimit. It also stops reading at a finished contest's record, where it used to ask for one more, empty, page. Nothing here was exercised against a live network, because no protocol version 15 network exists yet, and the JavaScript specs were not run locally.Issue being fixed or feature implemented
Part of #5679 (decision proposals). The multiple choice kind (#5848, #5895, #5897) shipped with no client helpers:
docs/protocol/decision-proposals.mdsaid "They have no helpers of their own yet."What was done?
Before and after
Before, voting for a choice from JavaScript:
After:
rs-dpp(dpp::decision_proposal::multiple_choice, new)CHOICE_DOCUMENT_TYPE_NAME,CHOICE_CONTEST_INDEX_NAME,proposal_properties,choice_properties,is_choice. A test pins each to the contract crate's constant.multiple_choice_proposal_vote_poll(proposal_id): the contest a proposal's choices run in, from the proposal's id alone. A test pins it to the poll achoicecreate derives from itsproposalId, in each form the id can be written.MultipleChoiceVoteChoice { Choice(id), Abstain, NoneOfTheChoices }, its conversion toResourceVoteChoice(a choice's document id travels asTowardsIdentity, none of the choices isLock), andmultiple_choice_proposal_vote(proposal_id, choice) -> Vote.rs-sdk(dash_sdk::platform::decision_proposals::multiple_choice, new)Sdk::build_multiple_choice_proposal,build_multiple_choice_proposal_edit,Sdk::build_choice(and the*_documentforms that take the contract and entropy).Sdk::multiple_choice_proposal_processing_fee,Sdk::choice_fund_to_join(proposal_id).Sdk::fetch_multiple_choice_proposal,..._by_owner,..._by_created_at,MultipleChoiceProposal::from_document,Choice::from_document.Sdk::fetch_multiple_choice_proposal_vote(every choice with its tally while the vote runs; the outcome with every choice's final tally once it ended),Sdk::fetch_kept_choice,Sdk::fetch_masternode_multiple_choice_vote.Sdk::fetch_contested_resource_vote_state(vote_poll, limit)(the paging loopget_contested_dpns_vote_statehad, which now calls it) andSdk::contest_fund_to_join_vote_poll(vote_poll)(the counting half ofprefunded_voting_balance_to_join).wasm-sdkandjs-evo-sdkwasm-sdk:buildMultipleChoiceProposal,buildMultipleChoiceProposalEdit,buildMultipleChoiceProposalChoice,getMultipleChoiceProposalProcessingFee,getMultipleChoiceProposalChoiceFund,getMultipleChoiceProposal,getMultipleChoiceProposalsByOwner,getMultipleChoiceProposalsByCreatedAt,getMultipleChoiceProposalVotePoll,getMultipleChoiceProposalVote,getMultipleChoiceProposalKeptChoice,getMasternodeMultipleChoiceProposalVote,multipleChoiceProposalVote. They read options and callrs-sdk.js-evo-sdk:sdk.decisionProposals.multipleChoice(build,buildEdit,buildChoice,processingFee,choiceFund,get,byOwner,byCreatedAt,votePoll,voteState,keptChoice,masternodeVote,vote).What the helpers do not check
The builders write what they are given. Which descriptions, lengths and choices Platform takes is the contract's to say and is checked on chain, so a proposal with neither a description document nor a url is built and then refused by Platform (it pays its gas). The three properties that name a description document travel together as one
DescriptionDocument, so a partial reference cannot be built. The only refusals made client-side are the ones the yes/no helpers already make: an edit of a document that is not a multiple choice proposal, and an edit that changes nothing (Platform would accept it and charge for it).Docs
docs/protocol/decision-proposals.md("From the SDKs") and thejs-evo-sdkREADME.In-place changes to shipped generations
None. No versioned function is touched.
get_contested_dpns_vote_stateandprefunded_voting_balance_to_joininrs-sdk(client code, not consensus) now call the shared functions. The only behaviour change is thelimiton a finished contest described under Risks.How Has This Been Tested?
Run locally:
cargo test -p dpp --features all-system_contracts,fixtures-and-mocks,validation --lib -- decision_proposal: 14 passed (4 new: the names against the contract crate, the poll against the one achoicecreate derives, the vote choice mapping,is_choice).cargo test -p dash-sdk --features mocks,decision-proposals-contract --lib -- decision_proposals contest_fund contest_vote_state contested: 39 passed (10 new). The new tests build a proposal and a choice and validate both against the contract's schema, check that a choice joins the contest its proposal's id names and states that contest's fund, cover the edit (title, url, description document set and removed, no-op and wrong-type refusals), read a running and a finished vote, check every listing is served through the index the schema declares, and read the choice fund and the vote through a mock SDK. The existing DPNS vote state paging tests pass through the shared function, and a new one serves a finished contest of three contenders to pages of 1 and 2, and reads a finished record of 150 contenders in one page.RUST_MIN_STACK=16777216 cargo test -p drive-abci --lib -- multiple_choice_proposal: 14 passed. Three of them now also assert whatfetch_kept_choicerelies on: a document query of a proposal's choices finds none while the vote runs, none after a lock, and only the kept choice after an award.CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER=wasm-bindgen-test-runner cargo test -p wasm-sdk --target wasm32-unknown-unknown --lib -- multiple_choice: 5 passed. They cover the JavaScript shape of the vote state (no choice, running, kept a choice, kept none) and the vote choice round trip.cargo check -p wasm-sdk --target wasm32-unknown-unknown, andcargo clippyondpp,dash-sdk(all targets) andwasm-sdk(wasm target): clean.cargo fmt --all.Not run:
packages/js-evo-sdk/tests/unit/facades/multiple-choice-proposals.spec.ts,packages/wasm-sdk/tests/unit/multiple-choice-proposals.spec.ts) and ESLint: this checkout has no built wasm bundle ornode_modules. CI runs them.Sdk::fetch_multiple_choice_proposal_vote,fetch_kept_choiceandfetch_masternode_multiple_choice_voteend to end: the mock SDK holds no system contract, so their parts are tested separately (the query, and the reading of its result).Breaking Changes
None.
Checklist:
structure.rs, regeneratedgrovedb-structure.json, and checked the structure viewer link posted on this pull requestFor repository code-owners and collaborators only
🤖 Generated with Claude Code
PR Hygiene ·
966c1a5/self-reviewedjs-wasm-sdk(packages/js-evo-sdk/README.md,packages/js-evo-sdk/src/decision-proposals/facade.ts,packages/js-evo-sdk/src/decision-proposals/multiple-choice-facade.tsand 6 more) — shumkovdpp— you own itrs-drive-abci— you own itrust-sdk(packages/rs-sdk/src/platform/decision_proposals.rs,packages/rs-sdk/src/platform/decision_proposals/multiple_choice.rs,packages/rs-sdk/src/platform/documents/contest_fund.rsand 3 more) — lklimek or shumkovWhen every merge requirement is met, the
PR Hygienecheck passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.