Repository navigation
feat(dpns): operator-first masternode voting and usernames in identities #901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 45 commits
Commits
Show all changes
225 commits
Select commit
Hold shift + click to select a range
382efc5
docs(dpns): design unified voting experience
lklimek 36cd3e2
feat(dpns): unify voting operations and UX
lklimek 0c012cc
fix(dpns): harden voting operation recovery
lklimek ad30809
feat(dpns): preserve voting center route choices
lklimek a3c67c3
fix(dpns): make voting review safe and explicit
lklimek 0c0eed0
fix(dpns): make scheduled operations authoritative
lklimek 64e0962
fix(dpns): close voting operation safety gaps
lklimek fa0cb10
fix(dpns): explain empty voting workspace
lklimek b933f92
fix(dpns): close voting operation review gaps
lklimek 05133b6
fix(dpns): always release failed sweep dispatches
lklimek 5e0532e
fix(dpns): keep preflight failures retryable
lklimek dd2af66
fix(dpns): keep voting views authoritative
lklimek d6db6cd
fix(dpns): preserve scheduled vote recovery
lklimek 4b50878
fix(dpns): retry schedules with missing voters
lklimek 2936e6d
fix(dpns): bound vote operation journal growth
lklimek 3a281b7
fix(dpns): cache voting operation views
lklimek 14d3aca
fix(dpns): recover failed direct vote dispatches
lklimek 44e0726
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 071cc16
fix(dpns): contextualize vote diagnostics for DAPI exhaustion
lklimek f69db4e
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek 25b89d2
fix(dpns-voting): address 5 review findings marked resolved but still…
lklimek 79b5c00
fix(dpns-voting): address remaining PR 901 review findings
lklimek 10a3090
fix(dpns-voting): avoid field_reassign_with_default lint error
lklimek 815c19c
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek eda79f9
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 8335bf9
fix(dpns-voting): move broadcast phase boundary earlier, fix immediat…
lklimek 040c518
feat(dpns): move voting into active contests
lklimek 4d04570
docs: update changelog for DPNS voting redesign
lklimek c7e6ac4
fix(dpns): restore Scheduled-votes nav and rebuild Review-and-cast sheet
lklimek cf61d47
fix(dpns): address active voting review gaps
lklimek dd81c46
fix: refresh proved DPNS vote state after updates
lklimek b575672
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek 8ca7635
fix(dpns-voting): quarantine poisoned legacy rows and unblock strande…
lklimek 76c0ed7
fix(dpns-voting): retain quarantined legacy rows instead of dropping …
lklimek 8abca99
perf(dpns-voting): batch masternode-card current-vote reads into one …
lklimek 991e5f9
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek 07f2a29
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek f60b7ae
fix(dpns): surface masternode vote limit
lklimek aff18b2
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek 4119610
fix(dpns): rename misleading is_open_for_voter to reflect contest-lev…
lklimek 9b45fba
fix(dpns): route scheduled-vote sweep completion to hidden Active-con…
lklimek 7de526b
fix(dpns): guard scheduled-vote journal cleanup and removal
lklimek f7f3c62
fix(dpns): make Scheduled Votes journal-first and fix Clear All feedback
lklimek d7ae529
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek fcbc1dc
fix(dpns): unstick the Review and cast window after a failed submission
lklimek 32032a7
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek ecd04b0
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek cbb4afa
fix(dpns): make the vote review honest, Remove final, and keyless nod…
lklimek a9d5a2c
refactor(dpns): drop the vote-change note from the review sheet
lklimek 513af26
chore(deps): bump platform pin to PR #3968 latest tip
lklimek 8f3dde1
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek f6d15df
chore: merge v1.0-dev into DPNS voting branch
lklimek 28948b1
fix(dpns): preserve vote proof freshness and ambiguous outcomes
lklimek c5c0e50
fix(dpns): bound vote history and preserve scheduled vote lifecycle
lklimek 04699b6
fix(dpns): preserve vote review intent and complete scheduled voting …
lklimek f0dd759
fix(identity): cancel stale loads and preserve scoped key updates
lklimek 4d05711
docs(dpns): describe reviewed voting and scheduling behavior
lklimek 5277eeb
fix(dpns): preserve scheduled vote eligibility and validate new sched…
lklimek 0101218
docs(dpns): clarify recovery visibility and schedule validation
lklimek b22cc75
fix(dpns): keep unresolved voting activity and failed state visible
lklimek 29b9a57
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek 9dc1843
fix(dpns): explain missed votes and bound completed history
lklimek 9c50554
fix(dpns): preserve vote intent and fresh preflight review
lklimek ed8bdc3
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek ed34bc6
docs(dpns): fix duplicate story ID and close design-doc drift
lklimek a504b8a
fix(app): route every DPNS result to hidden screens and fix vote copy
lklimek e9b7f38
fix(dpns): give vote reconciliation a negative terminal verdict
lklimek 6df93f3
fix(dpns): unify the authoritative-operation rule across the vote jou…
lklimek b397392
fix(dpns): disclose the vote recipient and stop the per-frame contest…
lklimek 1c985fa
fix(masternodes): correct the DPNS card contract and gate the voting-…
lklimek 4c0d80c
refactor(dpns): move dpns_voting test module to the bottom of the file
lklimek 1cf9164
fix(dpns): dedupe kv test-support latch and fix doc placement
lklimek 39edf1f
perf(masternodes): batch the DPNS contest summary read in reload()
lklimek 6ae22e6
test(dpns): drop the review-round prefix from one contested_names_db …
lklimek c9fd8b7
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 75cba19
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 237b37b
fix(dpns-voting): update RNG usage for rand 0.10 API
lklimek c007461
fix(dpns): clarify the vote decision before confirmation
lklimek e566b5c
Merge remote-tracking branch 'origin/v1.0-dev' into mb/pr-901
lklimek d9bda15
test(mcp): use the fragment-based put-failure helper after base merge
lklimek 43ff39e
chore: merge v1.0-dev into feat/dpns-voting-unified
lklimek 76f2350
docs(dpns): redesign voting for operators and move usernames to ident…
lklimek 47249d4
feat(dpns): contest timing, contested rule and vote fee in model
lklimek 201e3f5
feat(identity): username availability and request status ops
lklimek c9cba79
feat(dpns): move voting into Masternodes ▸ Votes
lklimek d4e7a13
feat(identity): rework username registration flow
lklimek 1d511d6
style(identity): drop redundant format calls in username confirm rows
lklimek b314550
feat(dpns): signal votes needed from every screen
lklimek 53ccd3f
feat(identity): usernames card and request status page
lklimek 5643f16
feat(dpns): contest cards with weighted tallies and a node set
lklimek 2c8f44b
feat(identity): home request cards, outcome banners and main name
lklimek 26424f5
feat(identity): signing key choice for usernames and user stories
lklimek e77b50d
feat(dpns): aggregate confirm step for staged votes
lklimek fac41fc
feat(dpns): non-blocking vote progress drawer
lklimek b1402b9
feat(dpns): keyboard shortcuts and bulk decisions in Votes
lklimek 9821f10
fix(identity): close username QA gaps before and after payment
lklimek db87d63
feat(masternodes): node votes and "Vote with this node"
lklimek 6aba4a5
feat(dpns): group scheduled votes by decision
lklimek ad3bbde
feat(dpns): history shows how your nodes voted
lklimek 6bccd91
feat(dpns): open Votes on one contest from other screens
lklimek 303571a
docs(identity): mark DPN-017 implemented and name the filtered votes …
lklimek 7b67d62
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek cb9cf95
Merge branch 'redesign/pr901-voting' into feat/dpns-voting-unified
lklimek 638c9c8
Merge branch 'redesign/pr901-usernames' into feat/dpns-voting-unified
lklimek 8929336
fix(masternodes): key list membership by the masternode identity id
lklimek 93a5012
fix(identity): keep outcome banners once-only and show the main name …
lklimek 05621ed
fix(dpns): tie lines for Lock ties and empty contests
lklimek e082ff5
fix(dpns): clear "not cast" once a vote is recast or dismissed
lklimek 2ac5b0d
fix(identity): bound outcome seen-mark retries
lklimek 1efeac0
feat(dpns): vote now on contests already inside the lead time
lklimek e076952
refactor(dpns): persist voting preferences through backend tasks
lklimek ebcb70f
fix(dpns): reopen the confirm when preflight finds a vote change
lklimek ea5f95a
fix(dpns): QA follow-ups for executor coverage, wording and refresh
lklimek b0a8fd0
test(dpns): pin Enter in the confirm; drop list focus on segment switch
lklimek a4d5332
Merge branch 'redesign/pr901-voting' into feat/dpns-voting-unified
lklimek 26d71e7
chore: merge v1.0-dev (#1038) into feat/dpns-voting-unified
lklimek 05f5864
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 61c674c
fix(dpns): do not treat an unknown contest as closed
lklimek 8774488
fix(dpns): use singular and plural vote feedback sentences
lklimek 6fb00e1
fix(masternodes): singular status line for one active contest
lklimek 8b17094
refactor(dpns): drop dead transition_hash and pin the journal key set
lklimek 5ceed0b
refactor(dpns): derive vote confirmation from the classified status
lklimek 604acc4
refactor(dpns): name the SubmitDpnsVoteOperation fields
lklimek 7f14b27
perf(dpns): detect lock-index changes without cloning the index
lklimek 8d8f462
fix(dpns): rank scheduled rows by the shared vote authority rule
lklimek 668d89c
refactor(dpns): name the reviewable-failure lifecycle on the status
lklimek ac884e2
perf(dpns): clone voter identities only when submitting
lklimek 2768f70
perf(dpns): scope submission preflight to the operation's voters
lklimek ab99fa5
docs(dpns): note deferred shared preflight concurrency bound
lklimek a1c0d42
Merge remote-tracking branch 'origin/v1.0-dev' into feat/dpns-voting-…
lklimek 4f121ce
docs(dpns): align voting and username documentation with current UI
lklimek 26ed875
docs(dpns): document registration and voting review fixes
lklimek 718bf6b
fix(dpns): preserve registration and vote state across concurrent tasks
lklimek 9e67385
test(dpns): route registration errors through dispatch context
lklimek 9788852
Merge branch 'v1.0-dev' into feat/dpns-voting-unified
lklimek ca9af7e
chore: merge v1.0-dev into username and voting redesign
lklimek eb94b0a
fix(dpns): preserve identity edits and isolate vote recovery
lklimek 4bb1ec0
chore: merge v1.0-dev into dpns voting branch
lklimek 8cc95c6
fix(dpns): address voting review blockers
lklimek 9e3ffdb
fix(ui): keep voting refresh bursts from starving frames
lklimek 4902efd
fix(dpns): refresh voting state and align review feedback
lklimek f9ef2a7
fix(dpns): refresh saved username outcomes before contest discovery
lklimek ed897e7
fix(dpns): replace vote history column with explorer links
lklimek 919dfaf
fix(dpns): move explorer links to final details column
lklimek f1124e4
chore: merge v1.0-dev into DPNS voting branch
lklimek 18cd9c5
fix(dpns): preserve uncertain votes and concurrent username updates
lklimek d9c5787
perf(dpns): count authoritative outcomes in one journal pass
lklimek 281bfd9
refactor(dpns)!: use only the vote journal for schedules
lklimek 72b4744
fix(dpns): retry won names and bind payment consent to review
lklimek bbc9e52
chore: merge v1.0-dev into dpns voting branch
lklimek b0b7d2e
test(dpns): prepare payment review before overlay error dispatch
lklimek 939316a
fix(dpns): preserve saved requests and address voting review feedback
lklimek 8da0f69
fix(dpns): publish live vote progress and preserve uncertain outcomes
lklimek e4aca67
fix(dpns): preserve final requests and retry identity cleanup
lklimek 718d127
test(dpns): discard unused drawer texture deltas
lklimek e6c0840
fix(dpns): keep the voting attention signal alive between refreshes
lklimek ab6271f
fix(dpns): stop re-raising the unconfirmed-vote banner on every sweep
lklimek aee07f3
fix(dpns): name the real cause when no vote of a batch was submitted
lklimek f281b93
fix(dpns): make the old-schedule startup notice truthful and self-exp…
lklimek 1bc4dda
docs(dpns): align the voting spec with the shipped behaviour
lklimek e9d7731
fix(dpns): keep a card's changes-left known when its vote confirms live
lklimek deeb4a7
fix(dpns): surface a failed vote that was scheduled in an earlier ses…
lklimek b6c600a
test(dpns): make the lock-retention and rebroadcast tests exercise th…
lklimek fe4d72e
fix(dpns): retry an incomplete background refresh soon, not an interv…
lklimek 71fa342
refactor(dpns): drop the confirm step's unreachable in-flight state
lklimek c2000ba
fix(dpns): bound the contest page query retries
lklimek 3515fc0
fix(dpns): keep the Scheduled view in a fixed order
lklimek 02d8a06
fix(dpns): skip a contest with an unknown deadline instead of failing…
lklimek eab8e62
fix(dpns): review a failed vote again without dropping staged decisions
lklimek 7434f7e
fix(dpns): make a per-node "before the end" timing follow the lead time
lklimek 2c7a316
fix(dpns): validate schedule times in the UI through the model validator
lklimek 7e459dd
fix(dpns): do not send long-queued votes by themselves after a restart
lklimek e143a3e
fix(dpns): refuse to overwrite a stored vote operation on insert
lklimek cf3e9b2
fix(dpns): check the proved vote before recording a broadcast rejection
lklimek 64d3ae5
refactor(dpns): drop comments and names left over from removed designs
lklimek 6cbc38c
fix(dpns): return TaskError from the new retry and proved-vote helpers
lklimek 115fd2b
docs(dpns): describe failure recency and the Scheduled view order
lklimek 4820dab
fix(migration): stop reading legacy scheduled votes in the app-data pass
lklimek cd084b9
fix(dpns): make the Identities breadcrumb leave the request status page
lklimek 4ca8bd5
fix(dpns): use identity-side tally labels and gate the voter link on …
lklimek 788203e
fix(dpns): check the approved contest fee against the broadcast document
lklimek 2c81802
test(dpns): cover the pay-time availability guard before the contest fee
lklimek 86a3cf0
fix(dpns): never report a possibly paid username request as a plain f…
lklimek 000fc0c
fix(dpns): keep a just-registered username when the follow-up read lags
lklimek 61092df
fix(identity): delete identity keys even when the vote journal is unr…
lklimek 898407d
fix(dpns): keep refreshing owned names past a removed or failing iden…
lklimek b9a78d3
refactor(dpns): drop the unreachable DPNS source of the registration …
lklimek 701ee43
test(dpns): pin contest durations across the versions the SDK can report
lklimek 4df6f20
docs(dpns): align username model docs with the code they describe
lklimek 1183e58
docs(dpns): document the vote journal and username keys, retire sched…
lklimek 3fd1584
docs(changelog): describe DPNS voting and usernames by what users obs…
lklimek 9abb108
docs(dpns): make platform-facts portable and mark resolved contradict…
lklimek fb8f3e3
docs(dpns): align the usernames spec and user stories with the shippe…
lklimek 2cd51ff
docs(migration): describe the app-data step as top-up history only
lklimek 9a54a4a
docs(migration): stop describing unreadable scheduled votes as an upg…
lklimek 8a9b9d2
docs(dpns): document when the old-schedule startup notice stops
lklimek a44a15d
fix(dpns): show an immediate vote that restart recovery stopped
lklimek ff16151
fix(dpns): do not skip a vote as already cast on proof too old to decide
lklimek 9130b06
fix(dpns): release immediate votes left queued by a failed executor
lklimek 3c2e924
fix(dpns): date every immediate vote restart recovery picks up
lklimek 1b20894
fix(dpns): cancel votes stored after their voter was removed
lklimek 84c38c5
fix(dpns): count a vote once when its confirmation is recorded twice
lklimek 2bda135
test(dpns): build the vote-state snapshot fixture with its proof times
lklimek f778d94
fix(dpns): check the network before believing a refused username request
lklimek bbdb04c
docs(dpns): describe the old-schedule notice as non-listing and self-…
lklimek aa468f4
refactor(migration): remove the unreadable-votes report the upgrade c…
lklimek cb12ded
fix(dpns): stop listing a won username after it has left the identity
lklimek f908ee3
docs(dpns): list the vote-count key in the k/v reference
lklimek 772750d
docs(dpns): list the recovery-time and counted-operation keys
lklimek baf1739
test(identity): schedule the vote before the interrupted removal it m…
lklimek fba85f9
fix(dpns): show a vote that is only checked again as cast, not as a f…
lklimek 267c6d9
refactor(dpns): build a voting node and map its kind in one place
lklimek 45cce5c
refactor(dpns): carry the composer's error instead of restating it
lklimek 92f655b
refactor(dpns): let the votes panel say which results it handles
lklimek 0dbacba
refactor(dpns): remove small restatements in the voting task and copy
lklimek f218785
test(identity): share one bare user identity fixture
lklimek 4bb9406
refactor(identity): keep the username sentences in the copy module
lklimek 9214ac2
fix(dpns): do not announce a vote that is only checked again as sent now
lklimek 0bfcb2e
fix(dpns): skip a node with no changes left instead of checking it again
lklimek c35813a
fix(dpns): drop the transaction sentence when nothing is sent
lklimek bde6a00
fix(dpns): keep a node loaded after staging out of the confirm step
lklimek 78581f7
fix(identity): report a refused name request as unconfirmed when it c…
lklimek 34d5ba3
perf(dpns): share published vote operations instead of copying them all
lklimek d5f7387
fix(dpns): cancel reviewed votes when their node is removed and loade…
lklimek cc0848f
fix(dpns): retry a contest refresh soon when a contest query failed
lklimek File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
206 changes: 206 additions & 0 deletions
206
docs/ai-design/2026-07-16-dpns-voting-experience/01-requirements.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| # DPNS Voting Experience — Requirements | ||
|
|
||
| ## Status | ||
|
|
||
| Planning specification. No implementation is authorized by this document. | ||
|
lklimek marked this conversation as resolved.
Resolved
|
||
|
|
||
| ## Problem statement | ||
|
|
||
| DET previously exposed DPNS voting through disconnected per-node and bulk | ||
| experiences. The adopted design consolidates them on DPNS Active contests. | ||
|
|
||
| Both call the same backend, but neither owns a complete, authoritative model of | ||
| the operation. The result is unsafe ambiguity: a vote can be accepted while DET | ||
| shows no confirmation, the same choice can immediately be offered again, bulk | ||
| results cannot identify the affected node, and a second click can submit another | ||
| state transition while the first is unresolved. | ||
|
|
||
| Voting consumes Platform credits and each node may vote only five times on a | ||
| contest in total (the initial vote plus up to four changes). Duplicate or | ||
| ambiguous submissions therefore have a real cost. | ||
|
|
||
| ## Primary persona | ||
|
|
||
| Priya, the power-user masternode operator, manages one or more nodes and expects: | ||
|
|
||
| - current vote state to be accurate; | ||
| - quick actions for one node; | ||
| - bulk and scheduled operations for many nodes; | ||
| - progress that survives navigation and restart; | ||
| - exact per-node results; and | ||
| - safe recovery when Platform accepted a request but DET could not confirm it. | ||
|
|
||
| The voting workspace remains hidden below Detailed interface mode. It is not an | ||
| Everyday User workflow. | ||
|
|
||
| ## Current-state audit | ||
|
|
||
| | Finding | Source behavior | User impact | | ||
| |---|---|---| | ||
| | Vote ownership is never populated | Contest cache reconstructs `ContestedName::my_votes` as empty and no task fills it | A successful vote is offered again after refresh | | ||
| | Existing votes are treated as closed | `is_open_for_voter` excludes any contest where the node already voted | Operators cannot inspect or change an existing vote | | ||
| | Same-node bulk execution races | `VoteOnDPNSNames` runs contests with `join_all`; each task independently fetches the same voter nonce | Multiple votes for one node can conflict | | ||
| | Bulk results lose node identity | `DPNSVoteResults` contains only name, choice, and result | Partial results cannot say which node succeeded | | ||
| | Scheduled casts can report false success | Scheduled paths treat an outer `Ok(DPNSVoteResults)` as success without inspecting inner failures | A failed or unconfirmed scheduled vote is marked executed | | ||
| | Progress is screen-owned | App results are routed to the currently visible screen | Navigation can strand controls in progress or deliver feedback to the wrong page | | ||
| | Duplicate prevention is local or absent | Quick and bulk submit buttons do not share an operation lock | Repeated clicks or cross-screen actions can submit duplicates | | ||
| | Post-broadcast wait failure is ambiguous | A cause-less `StateTransitionBroadcastError` can follow successful broadcast | DET must not label it rejected or invite immediate retry | | ||
| | Voting entry points were disconnected | Per-node and bulk flows used different UI ownership | Operators could miss capabilities or assume they were removed | | ||
|
|
||
| ## Product decisions | ||
|
|
||
| 1. DPNS Active contests is the single home for immediate, bulk, and scheduled | ||
| vote composition. | ||
| 2. Masternode detail links plainly to Active contests without carrying a node | ||
| filter or draft. | ||
| 3. Single and bulk/scheduled voting use one review sheet and one shared | ||
| operation coordinator. | ||
| 4. Current vote state is authoritative Platform data, not UI-local memory. | ||
| 5. A vote row remains visible after voting and shows the current choice. Voting | ||
| again is presented as a change, not a new missing vote. | ||
| 6. Operation state is global, correlated, and durable enough to survive | ||
| navigation and process restart. | ||
| 7. Exact targets, not whole screens, are locked. Unrelated nodes and contests | ||
| remain usable. | ||
|
|
||
| ## Functional requirements | ||
|
|
||
| ### Information architecture | ||
|
|
||
| - **VOTE-FR-001** — Masternodes provides only the node list and node detail. | ||
| - **VOTE-FR-002** — DPNS Active contests owns the single vote composer and | ||
| review sheet. | ||
| - **VOTE-FR-003** — A node detail page offers one `DPNS Voting` action that | ||
| navigates plainly to Active contests. | ||
|
|
||
| ### Authoritative state | ||
|
|
||
| - **VOTE-FR-010** — DET queries each loaded node's proved Platform votes and | ||
| joins them with active contests. | ||
| - **VOTE-FR-011** — Each active contest shows the node's current choice, or | ||
| `Not voted`. | ||
| - **VOTE-FR-012** — Existing votes remain actionable while the contest is | ||
| votable, allowing a deliberate vote change. | ||
| - **VOTE-FR-013** — Selecting the already-current choice is a no-op and cannot | ||
| create a state transition. | ||
| - **VOTE-FR-014** — Refresh updates contests, tallies, and node vote state as one | ||
| coherent snapshot. | ||
| - **VOTE-FR-015** — A change to an existing vote is labeled as a limited vote | ||
| change in Review. DET does not claim to know the remaining count. | ||
| - **VOTE-FR-016** — If current vote state cannot be proved, DET shows it as | ||
| unavailable and disables submission for that node instead of assuming | ||
| `Not voted`. | ||
| - **VOTE-FR-017** — Node summaries distinguish active contests from contests | ||
| where the node has not voted yet. | ||
|
|
||
| ### Vote composition | ||
|
|
||
| - **VOTE-FR-020** — Quick voting supports one node across one or more contests. | ||
| - **VOTE-FR-021** — Bulk voting supports one or more contests across one or more | ||
| nodes. | ||
| - **VOTE-FR-022** — The operator can apply one timing choice to all selected | ||
| nodes and override individual nodes. | ||
| - **VOTE-FR-023** — Timing choices are `Cast now`, `Schedule`, and `Do not use | ||
| this node`. | ||
| - **VOTE-FR-024** — Before submission, a review step lists every target as | ||
| node × contest, including current choice, requested choice, and timing. | ||
| - **VOTE-FR-025** — The review step removes no-op targets and explains why. | ||
|
|
||
| ### Operation lifecycle | ||
|
|
||
| - **VOTE-FR-030** — Every submitted batch has a stable operation ID. | ||
| - **VOTE-FR-031** — Every target result includes operation ID, node ID, contest | ||
| ID/name, requested choice, and typed status. | ||
| - **VOTE-FR-032** — Target statuses are `Scheduled`, `Queued`, `Submitting`, | ||
| `Confirming`, `Confirmed`, `Unconfirmed`, `Rejected`, and | ||
| `Failed before submission`, plus `Not applied` after definitive | ||
| post-broadcast reconciliation. | ||
| - **VOTE-FR-033** — Same-node targets execute sequentially to preserve nonce | ||
| order. Different nodes may execute concurrently with a fixed bound. | ||
| - **VOTE-FR-034** — A target lock prevents a second operation for the same | ||
| network + node + contest while the first is unresolved. | ||
| - **VOTE-FR-035** — Button state derives from the shared coordinator. A click | ||
| disables the affected action immediately and shows progress text. | ||
| - **VOTE-FR-036** — Navigation does not cancel an operation or lose its state. | ||
| - **VOTE-FR-037** — Restart restores scheduled and unresolved operations before | ||
| enabling conflicting actions. | ||
|
|
||
| ### Confirmation and recovery | ||
|
|
||
| - **VOTE-FR-040** — Structured Platform consensus causes are treated as | ||
| confirmed rejection. | ||
| - **VOTE-FR-041** — A cause-less post-broadcast wait failure is treated as | ||
| `Unconfirmed`, never as rejection. | ||
| - **VOTE-FR-042** — Unconfirmed targets are reconciled against the proved | ||
| current vote and, when available, retried by transition hash through the | ||
| Platform SDK. | ||
| - **VOTE-FR-043** — A target becomes `Confirmed` when authoritative state | ||
| matches the requested choice. | ||
| - **VOTE-FR-044** — DET never offers `Submit again` while the result remains | ||
| ambiguous. It offers `Check again`. | ||
| - **VOTE-FR-045** — A retry becomes available only after authoritative | ||
| reconciliation proves the requested change was not applied. | ||
|
|
||
| ### Scheduled votes | ||
|
|
||
| - **VOTE-FR-050** — Scheduled votes use the same target model, result model, | ||
| locking, execution order, and reconciliation as immediate votes. | ||
| - **VOTE-FR-051** — A scheduled target is marked executed only after confirmed | ||
| application. | ||
| - **VOTE-FR-052** — Rejected and failed-before-submission targets remain visible | ||
| with an actionable status. | ||
| - **VOTE-FR-053** — Unconfirmed scheduled targets are not automatically | ||
| rebroadcast. | ||
| - **VOTE-FR-054** — Existing scheduled-vote records migrate without losing node, | ||
| contest, choice, time, or executed state. | ||
| - **VOTE-FR-055** — A scheduled target can be edited or cancelled until | ||
| execution begins. Once submitting, it follows normal operation locking. | ||
|
|
||
| ### Feedback | ||
|
|
||
| - **VOTE-FR-060** — One confirmed target shows a concise success banner. | ||
| - **VOTE-FR-061** — Batch feedback summarizes confirmed, unconfirmed, rejected, | ||
| and failed counts and links to per-target details. | ||
| - **VOTE-FR-062** — Messages name node aliases and contested names where useful. | ||
| - **VOTE-FR-063** — Technical errors stay in banner details. | ||
| - **VOTE-FR-064** — Unconfirmed copy explicitly says DET will keep checking and | ||
| warns against resubmission. | ||
|
|
||
| ## Non-functional requirements | ||
|
|
||
| - **VOTE-NFR-001 Safety** — No UI path can bypass target locking. | ||
| - **VOTE-NFR-002 Correctness** — Same-voter transitions are serialized. | ||
| - **VOTE-NFR-003 Durability** — A crash after broadcast cannot erase the only | ||
| record that the outcome is unresolved. | ||
| - **VOTE-NFR-004 Proofs** — Current vote state is obtained through proved SDK | ||
| queries. | ||
| - **VOTE-NFR-005 Accessibility** — Disabled actions explain why; progress is not | ||
| color-only; keyboard focus follows the composer step order. | ||
| - **VOTE-NFR-006 Localization** — User-facing strings are complete translation | ||
| units with no parsed error text. | ||
| - **VOTE-NFR-007 Performance** — Refresh queries votes once per node, not once | ||
| per contest, and bounds cross-node concurrency. | ||
| - **VOTE-NFR-008 Network isolation** — Drafts, schedules, operations, locks, and | ||
| results are network-scoped. | ||
| - **VOTE-NFR-009 Secret handling** — The coordinator stores identifiers and | ||
| choices, never private keys. | ||
|
|
||
| ## Platform dependency | ||
|
|
||
| The preferred recovery contract is | ||
| [dashpay/platform#4137](https://github.com/dashpay/platform/issues/4137), which | ||
| tracks phase-specific, retryable post-broadcast wait errors. The SDK should | ||
| expose the transition hash after broadcast so DET can persist it and resume | ||
| waiting. | ||
|
|
||
| DET must still support fallback reconciliation by fetching the node's proved | ||
| vote for the target poll. Until either method proves the result, the operation | ||
| remains unconfirmed and locked against duplicate submission. | ||
|
|
||
| ## Out of scope | ||
|
|
||
| - Changing Platform's five-vote protocol limit. | ||
| - Showing a remaining-change count before Platform exposes it in the proved | ||
| identity-votes response. | ||
| - Embedding Platform Explorer. | ||
| - Allowing arbitrary cancellation after broadcast. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.