# Class BSV::Wallet::Fixtures::Rebuilder <a id="class-BSV-Wallet-Fixtures-Rebuilder"></a>

**Inherits:** `Object`

Rebuild dev-wallet databases from a clean slate. Operator plumbing — not a
runtime path.

The <code>fixtures:*</code> rake tasks are the operator surface; this class
carries the orchestration so the rake wrappers stay thin shims and the logic
is unit-testable with stubbed boot/sweep/network calls.

Four operations — schema lifecycle is intentionally separate from on-chain
mutation (no bundled "rebuild and fund" path —see #493 for the rationale):

    * +rebuild(name)+ — sweep current spendable UTXOs back to the
      wallet's own root, +DROP DATABASE+, +CREATE+, re-run
      migrations. Leaves the wallet in clean-schema state with
      zero rows. **Aborts if sweep fails** — an operator-visible
      signal that the wallet held funds that couldn't be moved
      (typically mid-convention-flip; investigation warranted).

    * +rebuild_all+ — iterate the registry; skip +:test+ (no WIF)
      and any wallet whose WIF is missing.

    * +fund(name, sats:)+ — send +sats+ from +:sdk+ to +name+'s
      root P2PKH. Explicit, opt-in; never bundled with rebuild.
      Rejects +:sdk+ (the funder cannot fund itself).

    * +verify+ — for each registered wallet, assert no stale
      spendable rows + non-zero root balance on chain. Returns
      the failing-wallets list (empty on success) — caller exits
      non-zero on any failure. The "non-zero root balance" check
      is post-fund expectation: a freshly-rebuilt-but-unfunded
      wallet will (correctly) fail verify.

Drop+create over +DELETE FROM+ for three reasons (Database Architect input on
#480):

    1. The per-wallet +outputs.spendable_recoverable+ CHECK literal
       embeds the WIF-derived root P2PKH script. Re-running migrations
       against a fresh DB rebakes the literal cleanly; a +DELETE+ leaves
       a stale CHECK against the previous WIF.
    2. ENUMs survive a +DELETE+ — adding/removing an ENUM value would
       require manual surgery.
    3. CASCADE FK ordering across promotions → spendable → outputs is
       non-trivial for a +DELETE+ sweep; +DROP DATABASE+ is atomic.

Wall time is chain-tip bound — sweep is an inline broadcast. Expect a minute
or two per wallet. One-shot pre-release operation; not a CI loop.

## Constants
### `DEFAULT_FUND_SATS` <a id="constant-DEFAULT_FUND_SATS"></a> <a id="DEFAULT_FUND_SATS-constant"></a>
Default amount to fund each rebuilt wallet from <code>:sdk</code> — matches
the CLAUDE.md convention that integration WIFs each carry >=1m sats on chain.

### `SKIP_NAMES` <a id="constant-SKIP_NAMES"></a> <a id="SKIP_NAMES-constant"></a>
Names that never get rebuilt by `rebuild_all`. <code>:test</code> has no WIF
(unit specs generate their own keys) and the test DB is truncated by the spec
suite's +before(:suite)+ hook — not by operator action.

## Public Instance Methods
### `fund(name, sats: = @fund_sats)` <a id="method-i-fund"></a> <a id="fund-instance_method"></a>
Fund a wallet by sending `sats` from <code>:sdk</code> to its root P2PKH.
Explicit, opt-in operation — never bundled with `rebuild`. Rejects
<code>:sdk</code> (the funder cannot fund itself). Side-effecting — returns
nil.
- **@param** `name` [Symbol, String] target wallet name (must be
registered, must carry a WIF, must not be +:sdk+).
- **@param** `sats` [Integer] satoshis to send.
- **@raise** [ArgumentError]

### `initialize(registry: = BSV::Wallet::Fixtures.registry, out: = $stderr, fund_sats: = DEFAULT_FUND_SATS)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
- **@param** `registry` [BSV::Wallet::Fixtures::Registry] usually
+BSV::Wallet::Fixtures.registry+.
- **@param** `out` [IO] status output. Defaults to +$stderr+ so the
operator sees progress.
- **@param** `fund_sats` [Integer] satoshis to send from +:sdk+ on
re-fund.
- **@return** [Rebuilder] a new instance of Rebuilder

### `rebuild(name)` <a id="method-i-rebuild"></a> <a id="rebuild-instance_method"></a>
Rebuild a single named wallet's database to clean-schema state: sweep current
spendable UTXOs back to the wallet's own root, +DROP DATABASE+, `CREATE`,
re-run migrations. Side-effecting — returns nil.

**Aborts on sweep failure.** If the wallet held spendable rows that the engine
couldn't sign or broadcast (typically: mid-convention-flip, where derived keys
can't be re-computed under the new convention), the exception is propagated up
— the drop + create + migrate never run. Operator should investigate the
failed sweep before blowing away the DB.
- **@raise** [ArgumentError]

### `rebuild_all()` <a id="method-i-rebuild_all"></a> <a id="rebuild_all-instance_method"></a>
Iterate the registry, rebuilding every entry except `SKIP_NAMES` and any
registered wallet whose WIF is missing. Each wallet's `rebuild` is independent
— no special ordering required (fund is a separate operation; no inter-wallet
dependency during rebuild).

A failing wallet does NOT abort the fleet: the exception is logged and caught,
the loop continues to the next wallet, and the final summary lists what
failed. Caller (the rake task) exits non-zero when the returned array is
non-empty. Rationale: an operator running the bulk variant after a wide change
typically wants the rest of the fleet attempted even if one wallet's sweep
refuses; the per-wallet failures surface at the end for triage.
- **@return** [Array<Array(Symbol, Exception)>] empty on success;
+[name, exception]+ pairs for each failed wallet otherwise.

### `verify()` <a id="method-i-verify"></a> <a id="verify-instance_method"></a>
Post-rebuild check. For each registered non-skipped wallet:

    * Boot the wallet, assert no spendable rows from the previous
      era survive (+spendable_count == 0+ — every spendable output
      must come from a fresh fund post-rebuild).
    * Probe the on-chain root P2PKH balance via the wallet's
      network provider — non-zero confirms re-funding landed
      and the wallet is recoverable from its WIF alone.
- **@return** [Array<Array(Symbol, String)>] empty on success;
+[name, reason]+ pairs for each failing wallet otherwise.
Caller (the rake task) translates an empty array to exit 0.
