Skip to content

fix: correct reprint threshold display and de-duplicate the formula - #124

Merged
Gioyik merged 4 commits into
paritytech:masterfrom
kirushik:fix/print-threshold-display
Aug 4, 2026
Merged

fix: correct reprint threshold display and de-duplicate the formula#124
Gioyik merged 4 commits into
paritytech:masterfrom
kirushik:fix/print-threshold-display

Conversation

@kirushik

Copy link
Copy Markdown
Contributor

Print.vue recomputed the recovery threshold as floor(total/2)+2 and stored the total in a field misleadingly named requiredShards. The reprint sheets therefore overstated how many more QR codes are needed (e.g. total=5 showed "need 4" instead of the correct 3) — misleading during recovery, though the QR payloads themselves were always untouched.

Root cause: the threshold policy was duplicated. Share.vue computes floor(total/2)+1; Print.vue re-implemented it and the formula drifted. Extract a single defaultThreshold() helper (src/util/shards.ts) and use it in both, so they can never disagree again. Rename Print.vue's field to totalShards to match what it actually holds.

Adds some unit tests for this new helper, including a (rather naïve) "defaultThreshold() should always be greater than n/2"

Copilot AI review requested due to automatic review settings May 29, 2026 21:10
@cla-bot-2021

cla-bot-2021 Bot commented May 29, 2026

Copy link
Copy Markdown

User @claude, please sign the CLA here.

@kirushik kirushik changed the title fix: correct reprint threshold display and de-duplicate the formula (F5) fix: correct reprint threshold display and de-duplicate the formula May 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR centralizes the shard reconstruction threshold logic and fixes the Print view so it uses the same threshold policy as Share, preventing UI/count mismatches.

Changes:

  • Added defaultThreshold(totalShards) utility to define the “majority required” policy in one place.
  • Updated Share.vue and Print.vue to use defaultThreshold (and corrected Print’s model/prop usage).
  • Added unit tests covering defaultThreshold across key values and the full UI range.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/unit/shards.spec.ts Adds unit tests validating the centralized threshold policy.
src/views/Share.vue Uses the shared defaultThreshold for required shard count.
src/views/Print.vue Fixes total-vs-threshold handling; uses shared threshold for required-shards.
src/util/shards.ts Introduces the single source of truth for threshold calculation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/views/Print.vue
Comment on lines 13 to 17
id="totalShards"
v-model.number="requiredShards"
v-model.number="totalShards"
type="number"
min="3"
max="255"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 19af5af

Comment thread src/views/Print.vue Outdated
Comment on lines 98 to 105
return this.totalShards !== undefined && this.shards.length !== this.totalShards;
},
remainingCodes(): number {
if (!this.requiredShards) {
if (!this.totalShards) {
return 0;
} else {
return this.requiredShards - this.shards.length;
return this.totalShards - this.shards.length;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 19af5af

Comment thread src/views/Print.vue Outdated
Comment on lines +107 to +108
threshold(): number {
return this.totalShards ? defaultThreshold(this.totalShards) : 0;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 19af5af

@Gioyik
Gioyik self-requested a review July 24, 2026 14:50
Gioyik
Gioyik previously approved these changes Jul 24, 2026
@Gioyik

Gioyik commented Jul 24, 2026

Copy link
Copy Markdown
Member

@kirushik could you please strip the claude signature of the commit, so it passes the cla check and I will merge it

kirushik added 4 commits July 27, 2026 16:31
Print.vue recomputed the recovery threshold as floor(total/2)+2 and stored
the *total* in a field misleadingly named `requiredShards`. The reprint
sheets therefore overstated how many more QR codes are needed (e.g. total=5
showed "need 4" instead of the correct 3) — misleading during recovery,
though the QR payloads themselves were always untouched.

Root cause: the threshold policy was duplicated. Share.vue computes
floor(total/2)+1; Print.vue re-implemented it and drifted. Extract a single
`defaultThreshold()` helper (src/util/shards.ts) and use it in both, so they
can never disagree again. Rename Print.vue's field to `totalShards` to match
what it actually holds.

Add unit tests for the helper, including the exact F5 case (total=5 -> 3) and
a strict-majority invariant across the whole 3..255 UI range.
Review follow-ups on the reprint view.

`<input type="number">` only constrains the spinner, not the value: typing
"3.5" (or clearing the box, which `v-model.number` leaves as "") sailed
through the old `>= 3 && <= 255` check and reached defaultThreshold(),
producing fractional totals and remaining counts. Add `step="1"` for the
spinner and gate on a shared `isValidShardCount()` predicate that also
requires a whole number, so the input and the threshold policy keep agreeing
about what a shard count is.

`needMoreShards` compared with `!==`, so scanning more codes than announced
kept the scanner open and drove `remainingCodes` negative. Compare with `<`
and clamp the remainder at 0.

`threshold` returned 0 for "nothing entered yet", a valid-looking value that
could reach ShardInfo's required-shards prop. Return `undefined` instead and
guard the consuming block on it, making the not-yet-entered state explicit.

The `threshold` and validation paths are covered by shards.spec.ts; the
scanner-overshoot clamp is not, as the repo has no component-test harness yet.
Share.vue's shard-count input had the defect Copilot flagged on Print.vue's:
no `step`, no integer check, and `totalShards` fed straight into
crypto.share() — a fractional count surfaced as an opaque secrets.js error
routed through the generic error hub, and an emptied box passed "" through.

Reuse `isValidShardCount()` and gate the generate button on it, matching the
existing `secretTooLong` pattern (disabled button plus an inline error span),
so both shard-count inputs now agree on what a shard count is.
Gating the generate button left the sentence above it still interpolating
defaultThreshold(totalShards) for values that had just been rejected: an
emptied field coerces to 0 and renders "Will require any 1 shards", and 3.5
renders 2. Give Share.vue's `requiredShards` the same contract as Print.vue's
`threshold` — `undefined` unless the count is usable — show an em dash in the
sentence, and guard both the generated-shards block and crypto.share() on it.

Also strengthen the threshold invariant test. Asserting only
`defaultThreshold(n) > n / 2` does not pin the policy: floor(n/2)+2, the very
formula this branch removed, satisfies it for every even n. Assert the
smallest strict majority instead, which floor(n/2)+1 alone satisfies.
@kirushik

Copy link
Copy Markdown
Contributor Author

@Gioyik done; also fixed Copilot's nits, and a couple of similar issues, just to keep the codebase tidy

@Gioyik
Gioyik self-requested a review August 4, 2026 13:58
@Gioyik
Gioyik merged commit 0c77cc4 into paritytech:master Aug 4, 2026
7 checks passed
@kirushik
kirushik deleted the fix/print-threshold-display branch August 4, 2026 15:49
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.

5 participants