Date: 2026-06-21 · Mode: READ-ONLY recon (no code edits, no rebase).
Paused cycle design base SHA: 82e58674 (v0.60.0). Current origin/master: ddabf5e3 (v0.67.0).
Branch: feature/own-account-subset-search — 9 commits ahead of origin/master (ALL design/ docs; crates/mnemonic-toolkit/src/ UNTOUCHED on our lead), 66 behind in symmetric-difference terms (the "62 commits" figure; the true 82e58674..origin/master count is 66, not 62).
| Item | Value |
|---|---|
origin/master short SHA |
ddabf5e3 |
origin/master toolkit version |
0.67.0 (NOT 0.66.0 — the cycle-14/L22 secret-hygiene tail ALREADY shipped @ddabf5e3) |
| Our branch ahead | 9 commits, all design/ (brainstorm/spec/plan/followup); src/ clean |
| Our branch behind | 66 commits |
| Design-base → master commit count | 66 (git rev-list --count 82e58674..origin/master) |
| File | # commits | Cycles / findings (subject) |
|---|---|---|
permutation_search.rs |
0 | ENGINE 100% CLEAN — zero change. P1 lands on identical ground. |
synthesize.rs |
1 | 53787cbb cycle-2 H8 — thread run_language into template ms1 emit (in synthesize_template_descriptor + tests; NOT near is_order_independent_shape) |
cmd/restore.rs |
2 | 8c145f19 cycle-13b L8+L9 (multisig-template coin-type origin fix + early hardened/taproot refusals); db0bf583 cycle-14 L22 (stdin-secret locals → Zeroizing) |
cmd/verify_bundle.rs |
4 | 4ed3bbe9 cycle-1 H1 (md1_xpub_match → structural compare); c4b46624 cycle-1 H12 (descriptor-mode taproot 3' origin); 40c4db74 cycle-11b L24 (descriptor-mode OOB → typed); 7daf8c69 cycle-14 L22 (SlotInput.value: String → SecretString) |
The 66 commits are the constellation bug-hunt program (cycles 1–14, 57 of 58 findings fixed; the lone open is L16 WON'T-FIX). Version chain: v0.60.0 → 0.61.0 (cycle-1) → 0.62.0/.1 → 0.63.0 → 0.64.0 → 0.65.0/.1/.2 → 0.66.0 (cycle-13) → 0.67.0 (cycle-14, current).
Legend: ✅ ACCURATE ·
| Symbol / citation | Old (82e58674) |
New (ddabf5e3) |
Status |
|---|---|---|---|
fn complete_multisig_template |
:1416 | :1421 | |
--own-account-max refuse gate (ctx.own_account_max.is_some()) |
:1434 | :1439 | |
| pool.len supply gates | :1626 / :1635 | :1687 (< n) / :1696 (> n) |
|
realized_s = perm_count_u128(n,n) |
:1661 | :1722 | |
id/addr mode select (id_search/addr_search) |
:1665-1666 | :1726-1727 | |
reject_duplicate_keys whole-pool |
:1648 | :1709 | |
key65 field on CandidateKey |
:1647 | :1107 (struct def) / used :1708 | |
sorted_shape binding |
:1676 | :1737 | |
evaluator-filter (sorted_shape && assignment != identity → false) |
:1739 | :1800 (if sorted_shape && !assignment.iter()…all(i==v)) |
|
validate_prefix_strength call |
:1700 | :1761 | |
perm_count_u128 helper |
:1882 | :1943 | |
--account Vec<u32> default_value="0" |
:106 | :106 (pub account: Vec<u32>) |
✅ |
conflicts_with precedent |
:86 | :86 (conflicts_with = "passphrase") |
✅ |
run_multisig_template_completion |
:1321 | :1326 | |
#[arg(long="own-account-max")] clap def |
(n/a) | :133-134 (Option<u32>) |
✅ exists |
| Symbol / citation | Old | New | Status |
|---|---|---|---|
fn verify_multisig_template |
:808 | :808 | ✅ byte-unchanged |
complete_multisig_template call (in verify_multisig_template) |
:874 | :874 | ✅ |
own_account_max: None hardcoded |
:865 | :865 | ✅ |
--account SCALAR u32 default_value="0" |
:64 | :63-64 | ✅ |
The verify_multisig_template template-completion path is 100% byte-identical (808/865/874 all exact). The verify_bundle churn (+402 lines) is entirely in OTHER functions: descriptor_mode_verify_run (H12/L24/L22 at 1350–1638), resolve_env_sentinels (L22 at 1880), emit_multisig_checks/emit_verify_checks (H1 at 2715+).
| Symbol | Old | New | Status |
|---|---|---|---|
fn search |
:551 | :551 | ✅ |
fn unrank_permutation |
:494 | :494 | ✅ |
fn factorial |
:481 | :481 | ✅ |
fn total_candidates |
:509 | :509 | ✅ |
fn required_prefix_bytes |
:322 | :322 | ✅ |
fn validate_prefix_strength |
:342 | :342 | ✅ |
pinned test prefix_ladder_own_account_max_subset_space |
:740 | :740 | ✅ |
| full-scan/2nd-match doc | :530-548 | :528-548 | ✅ (intact) |
Note: SPEC/plan cite a
perm_count_u128atpermutation_search.rs:1882in §0/§4.1 — that symbol lives in restore.rs:1943 (a private fn), NOT in permutation_search.rs.permutation_search.rsexportsfactorial/total_candidates. (Pre-existing mislabel in the plan §0, not sweep-induced; mechanical.)
| Symbol | Old | New | Status |
|---|---|---|---|
fn is_order_independent_shape |
:335 | :335 | ✅ byte-identical (335-360 diff EMPTY) |
| Test | Old | New | Status |
|---|---|---|---|
own_account_max_flag_refuses_with_actionable_message (REWRITE) |
:677 | :677 | ✅ |
pool_larger_than_slots_refuses_with_actionable_message (UPDATE msg) |
:715 | :715 | ✅ |
multi_account_own_resolves_both_slots (stays byte-GREEN) |
:635 | :635 | ✅ |
#[test] count |
27 | 27 | ✅ |
Drift verdict: all restore.rs interior citations DRIFTED by a uniform +61 (below the L8/L9 insertion) or +5 (the clap-args block, above it). ZERO STRUCTURALLY-WRONG citations — every symbol still exists, all line shifts are mechanical re-greps. The math/test/engine surface (permutation_search, synthesize, the test file, verify_multisig_template) is byte-stable.
3.1 complete_multisig_template — TWO new early gates inserted into the exact region our design extends
The shared core gained a +57-line block at the SAME gate position our P2 design edits (right after the I-1 own-account-max refuse, before the --cosigner parse / pool assembly). Order at master:
restore.rs:1439 I-1 gate: if ctx.own_account_max.is_some() → REFUSE ← P2 REMOVES this
restore.rs:1465 L9 gate: if has_hardened_use_site(d) → ModeViolation ← NEW
restore.rs:1485 L9/#26: if taproot_override_card(d) && !restorable_taproot_override_card(d) → ModeViolation ← NEW
restore.rs:~1500 --cosigner parse → pool build → reject_duplicate_keys → pool.len gates → realized_s → search
- Signature /
MultisigCompletionCtx/ mode-precedence: UNCHANGED.own_accounts: &[u32],entropy,own_account_max: Option<u32>,expect_wallet_id/search_addressall intact.id_search/addr_searchprecedence (now :1726-1727) byte-identical. - The C1 carried-origin-never-loaded invariant + per-slot origin BUILD: STILL HOLDS — but the BUILD gained an L8 coin-type substitution (restore.rs:1586-1608): in the all-own / no-
--cosigner/ no---originfallback,canonical_origin(tree)(mainnet coin 0') is patched tonetwork.coin_type()atcomps[1]. This is the ONLY origin source in the pure-own path our subset-search runs in. Design-relevant: the over-supplied own pool derives every own candidate through this same fresh-origin closure; the L8 patch is in the path, identity on mainnet, coin-1 on testnet/signet/regtest. Our design's "per-slot origins built fresh; carried origin never loaded (C1)" claim is preserved AND must now note the L8 coin-type substitution as part of "built fresh." - NEW gates our design must COMPOSE with (not conflict): the L9 hardened-use-site refuse and the #26 taproot-override refuse now fire BEFORE pool assembly. For named-multisig templates (the subset-search domain) these are defense-in-depth (non-hardened, non-taproot-override today), so they pass through transparently — but our P2/P3 pool-build inserts AFTER them, and the design's "remove the I-1 refuse at :1434" edit now operates on a 3-gate prologue (I-1 → L9-hardened → L9-taproot), not a 1-gate one. Mechanical for the common case; the brainstorm should note the new prologue so the P2 edit-site description is correct and the premise-violation table (§5a) doesn't collide with the new refusals.
- No new floor on the SEARCH/realized_s side: the pool.len gates (:1687/:1696),
reject_duplicate_keys(:1709),realized_s(:1722),validate_prefix_strength(:1761) are byte-stable in logic — only line-shifted. The funds-safety floor sequence our SPEC §5 builds on is intact.
- H1 fix did NOT touch
verify_multisig_template. The fix (expected.tree == desc.tree && use_site_path == && tlv.use_site_path_overrides ==) lives entirely inemit_multisig_checks(restore.rs/verify_bundle.rs:2756-2865), the KEYEDmd1_xpub_matchchecks[]path (the seed-derivedrun_full/ supplied-vs-expected card-compare path). It widens apassedpredicate; wire-shape unchanged. verify_multisig_template(the TEMPLATE-completion core our P4 wires into) is byte-identical — still callscomplete_multisig_templateat :874 withown_account_max: Nonehardcoded at :865. Our SPEC's claim that "verify_multisig_template was already on the correct structural side" is CONFIRMED — but for a subtly different reason than the design implied: verify_multisig_template is correct because it RE-RUNS the shared completion engine (structural reconstruction), NOT because it shares the H1 comparator. H1 fixed a DIFFERENT verify path (md1_xpub_match, the card-multiset compare). Our subset-search verify story COMPOSES cleanly — P4 just exposes--own-account-max/--search-cosigner-subsetand replaces the hardcodedNoneat :865; it inherits the same completion engine restore uses, so verify==restore parity is structural and unaffected by H1. No conflict.
3.3 Secret-memory-hygiene is now a FIRST-CLASS bar (cycle-14 / L22) — a NEW contract our design inherits
- The sweep (per
feedback_secret_hygiene_first_class_bar.md, user "100% correct and more!", 2026-06-21) elevated secret-memory-hygiene to a non-deferrable bar: Zeroize-on-drop + redacting Debug + off-argv + (mlock ≠ scrub) for ALL owned secrets. - L22 already wrapped the seed-resolution path our subset-search uses:
TemplateSeed.passphrase: String → Zeroizing<String>;resolve_template_completion_seed'sfrom_value/passphrase→Zeroizing;SlotInput.value: String → SecretString. KEY LESSON the design must honor: rawZeroizing<String>derives a NON-redacting Debug that LEAKS into{:?}/panic — use theSecretStringnewtype for any secret String, NOT raw Zeroizing. - The over-supplied own pool's secret footprint: our subset-search derives K_own own candidates from
ctx.entropy(alreadyZeroizing<Vec<u8>>) viaderive_bip32_from_entropy_at_path, pushing only the PUBLICaccount_xpub→key65into the pool. So the HELD pool is public xpubs (not secret) — but an over-supplied pool means MORE BIP-32 derivations, each materializing a transientXpriv/derived secret. The design question: does deriving K_own own keys (vs the current--account-list count) leave more transient private material un-zeroized inderive_bip32_from_entropy_at_path, and does the new bar require the subset-search derivation loop to wrap/scrub those transients? This is a NEW contract the cycle now inherits that the old (pre-bar) design did not weigh.
is_order_independent_shapeat synthesize.rs:335 byte-identical. The evaluator-filter (sorted_shape && !assignment…all(i==v)) moved 1739→1800 but is mechanism-identical. Our SPEC §3 / R0-r1 I-1 enumeration-side mechanism (dropperm_rankfor sorted shapes) rests on an unchanged foundation. Mechanical re-cite only.
git diff --statis EMPTY.search/unrank_permutation/factorial/total_candidates/required_prefix_bytes/validate_prefix_strength/the pinned test — all byte-identical at their cited lines. The full-scan-with-2nd-match-ambiguity behavior ourearly_exitcontract (SPEC §4.4) gates is unchanged. P1 (the combinatorics engine work) builds on identical ground; the §7-P1 byte-invariance anchor remains valid.
- H12 (descriptor-mode BIP-48 taproot 3' origin): in
descriptor_mode_verify_run(verify_bundle:1397) +bundle::compute_default_origin_path(gained adefault_script_typearg). This is the descriptor-mode verify path, NOT template-mode. Our design builds origins fresh from supplied keys in the template path; we don't callcompute_default_origin_path. No overlap. (Caveat: if any future shared helper is touched —bip48_script_type_for_root_tagis new incrate::template— confirm our fresh-origin BUILD doesn't route through it; it does NOT today.) - H13 (hardened-multipath reject at toolkit lex): in the descriptor lex path, not our completion enumeration. No overlap.
- H8 (synthesize_template_descriptor run_language threading): in template DESCRIPTOR synthesis (emit side), not the restore COMPLETION (decode/search) side. No overlap.
- L24 (descriptor-mode slot-coverage OOB gate): in
descriptor_mode_verify_run, not template-mode. No overlap.
- The old plan targeted toolkit MINOR 0.60.0 → 0.61.0. CRITICAL: 0.61.0 ALREADY SHIPPED (cycle-1 CRITICAL funds-safety H12/H1/H13,
f9467cc5, CHANGELOG line 119). So have 0.62–0.67. - New likely target: MINOR
0.67.0 → 0.68.0(new clap surface =--search-cosigner-subseton restore +--own-account-max/--search-cosigner-subseton verify-bundle ⇒ MINOR). md-codec/mk-codec NO-BUMP unchanged. GUI MINOR paired unchanged. - Coordination note: if a cycle-14-tail follow-up (
phrase-overlay-secretstring,stdin-reader-transient-buf-zeroizing) ships first it claims 0.68.0 — confirm the next-free MINOR at execution time. The 7 version-sites (both READMEs +install.sh:32self-pin +fuzz/Cargo.lock+Cargo.lock+CHANGELOG.md+ Cargo.toml) and the pinned-test supersession all carry over mechanically. - Also note: FOLLOWUP
fuzz-build-broken-…(E0433, fuzz target set fails to compile) is OPEN on master — independent of our cycle but our P6 "run the fuzz gate before tag" step will hit it unless that lands first.
- Secret-footprint vs the new first-class hygiene bar (§3.3). Does the over-supplied own pool (K_own derivations vs the
--account-list count) materialize more transient private key material inderive_bip32_from_entropy_at_path, and does the now-mandatory Zeroize-on-drop bar require the subset-search derivation loop to scrub those transients / useSecretString-style wrappers? The pre-bar design never weighed this; it is now a gate. (The single biggest new design surface.) - Version retarget 0.61.0 → 0.68.0 (§3.7). 0.61.0 is consumed (cycle-1). Confirm 0.68.0 (or next-free MINOR after any cycle-14-tail follow-up), and that the only change is the renumber — verify no semver re-classification from the new surface.
DESIGN-CONFIRMING (verify the design's claims survived the sweep — mostly favorable, brief discussion)
- The new 2-gate L9 prologue inside
complete_multisig_template(§3.1). Our P2 "remove the I-1 refuse" edit now operates on a 3-gate prologue (I-1 → hardened → taproot-override). Confirm the §5a premise-violation table doesn't collide with the new ModeViolation refusals and that the pool-build insertion point is re-described against the new layout. Likely mechanical for named-multisig templates, but the edit-site description must be re-grounded. - L8 coin-type substitution in the fresh-origin BUILD (§3.1). The C1 "origins built fresh" invariant now includes a network-coin-type patch in the all-own path our subset-search runs in. Confirm the over-supplied own derivations all flow through it correctly (testnet/signet/regtest), and update the C1 statement to mention L8.
- H1 fix vs our verify story (§3.2). Our claim "verify_multisig_template is on the correct structural side" is CONFIRMED — but the reason is re-run-the-completion-engine, NOT shared-comparator-with-H1. Re-word the SPEC's verify rationale so it cites the right basis (and notes H1 touched a different verify path).
- Citation re-grep: all restore.rs interior cites +61 (below L9 insert) or +5 (clap block);
perm_count_u128is in restore.rs:1943 not permutation_search.rs (pre-existing mislabel). permutation_search / synthesize / verify_multisig_template / the test file: re-cite at the SAME lines (byte-stable). The plan §0 + SPEC body line numbers refresh againstddabf5e3. - Rebase
feature/own-account-subset-searchontoddabf5e3(our 9-commit lead is design-docs-only; src clean — a trivial rebase, no code conflicts expected). - P6 fuzz gate caveat: the open
fuzz-build-broken-…E0433 will fail the pre-tag fuzz build unless it lands first (not our cycle's bug; note it).
The engine, the sorted-collapse, verify_multisig_template, and the test file are byte-stable (P1 + the math + verify-parity land clean); restore.rs citations DRIFTED uniformly (+61/+5, zero structurally-wrong); the two genuinely NEW things the re-brainstorm must weigh are (a) the first-class secret-hygiene bar applied to the over-supplied own-pool derivation footprint, and (b) the 0.61.0→0.68.0 version retarget (0.61.0 is burned). The H1 fix CONFIRMS — does not threaten — our verify story; the new L9/L8 gates inside the shared core are compose-with (mechanical re-description), not conflict-with.