feat(v0.4.7): report area 500px, heirs one-per-line, wizard line breaks, ALL-DUST guard

Owner-approved changes after testing v0.4.6, plus accumulated v0.4.x work,
task-tracking notes, and an updated project HANDOFF document.

The four v0.4.7 changes:

1. Report area (BalBuildWillDialog) opens 500px tall (min) up to 700px (max),
   then the scrollbar takes over. Previously it opened ~140px (too short).

2. Heirs are listed ONE per line again (green, bold) in _build_success_report,
   reverting the v0.4.6 single-line form. Heir names can be long and the report
   now scrolls, so compression is no longer needed.

3. Two wizard texts get an explicit line break: after "(or backup)" in the date
   hint and after "miner fees" in the fee note (widgets.py).

4. ALL-DUST guard: when EVERY heir's share is below the Bitcoin dust limit, the
   inheritance would pay nobody. Heirs.prepare_lists now raises
   HeirAmountIsDustException at the end (where all heirs across all locktimes
   are known with their final dust state), and dialogs.task_phase1 shows a clear
   RED message and stops without building/signing/checking. A mix of dust +
   valid heirs keeps building normally. The guard is intentionally in
   prepare_lists, NOT prepare_transactions (which only sees the lowest locktime
   and would false-positive). HeirAmountIsDustException is imported in common.py.

Tests: 3 new tests in test_core_heirs_extra.py pin the dust behaviour (all-dust
raises; mixed continues; multi-locktime continues). Full suite: 258 passed.
ruff: no new errors. Version bumped 0.4.6 -> 0.4.7 (4 files). CHANGELOG #23.

Also adds/updates HANDOFF.md so any future AI (Claude or another model) can
resume the project with full context (rules, layout, build/test/lint, dust
logic, git flow), and records the task-tracking notes in
.agent_memory_tasks.md.
This commit is contained in:
2026-06-28 23:02:25 -04:00
parent ed83af6be9
commit 646a33f2f5
19 changed files with 2803 additions and 323 deletions

View File

@@ -853,3 +853,574 @@ commit).
**Outcome:** DONE (delivered as test ZIP v0.3.9, confirmed OK by the user before
commit).
---
## 16. v0.4.0 - Unified invalidate, clearer CHECK message, will-executor auto-select, SIMPLE/ADVANCED mode
**Date:** 2026-06-24
This release bundles five related improvements (Groups 1-3 of the user's
to-do list). Version bumped 0.3.9 -> 0.4.0.
### Group 1 - Invalidate / messages
**#01b - Clearer "Checking your will" message.**
The two misleading outcomes "Heir not found" and "New" were both replaced with a
single, accurate two-line message, because in practice that branch is most often
reached when the delivery date was anticipated, not when an heir is missing:
Found CHANGES to the DATE or the HEIRS,
a NEW WILL must be prepared.
- `bal/gui/qt/dialogs.py`: `task_phase1` - HeirNotFoundException branch and the
no-subtype fallback (previously "New").
- `bal/gui/qt/window.py`: `build_inheritance_transaction` - HeirNotFoundException
branch (kept consistent with the CHECK window).
- The other specific messages (Heirs changed / Will-Executor not present /
Will-Executor changed / Txfees changed) are unchanged.
**UNIFY invalidate procedure + #03 - Same behaviour for CHECK and WIZARD, and the
history label is now always written.**
When a will is expired (CHECK on an expired will, or an heir added to an expired
will), the plugin now ALWAYS:
1. shows a warning popup (no more "use Tools -> Invalidate" wording);
2. automatically opens Electrum's classic transaction window via
`BalWalletWindow.invalidate_will()` so the user signs and broadcasts the
invalidation - this path already sets the "BAL Invalidate transaction" history
label, which fixes #03 (the label was missing when invalidating from the
automatically-opened window).
- `bal/gui/qt/dialogs.py`: the first `WillExpiredException` handler in
`task_phase1` now returns the `"invalidate_classic"` signal (instead of routing
to the label-less automatic path); `on_success_phase1` shows the approved popup
text and opens the classic window with `QTimer.singleShot(0, ...)` AFTER closing
the wizard, so the transaction window stays in front.
### Group 2 - Will-executor auto-select (#02)
In the wizard's "Automatically download and select willexecutors" flow, the green
SELECTED tick now follows the green ping dot: only servers that actually answered
the ping (status == 200) are selected, and every non-responding server is
explicitly DESELECTED. This discards dead/slow servers at the source (no more
getting stuck broadcasting to them) and is re-evaluated on every download, so a
server that failed before but now answers is selected again.
- `bal/gui/qt/dialogs.py`: `ping_on_done` in `BalWizardWEDownloadWidget._on_next`
(robust `str(status) == "200"` comparison + safe `.get`).
### Group 3 - SIMPLE / ADVANCED mode (global)
A new global "USER TYPE" setting lets the user pick a simpler interface.
- New global config `USER_TYPE` (default `"basic"`) and helper
`BalPlugin.is_basic_mode()` in `bal/core/plugin_base.py`. Stored in Electrum's
global config, so it does not affect existing wallet files; an old wallet opens
with the global value and its owner can switch to ADVANCED at will.
- `bal/gui/qt/plugin.py`: new "USER TYPE" row in the settings dialog - a two-choice
combo [BASIC, ADVANCED] with a HelpButton, added at the top (row 0) and to the
"Reset setting" list (reset -> BASIC).
- In BASIC mode:
- the Raw/Date selector is hidden and every date field is forced to the calendar
("Date") editor, so the user never sees or uses RAW
(`bal/gui/qt/widgets.py`, `BalTimeEditWidget`);
- the whole "Check Alive" (threshold) row and its icon are hidden, while the
"Delivery time" (locktime) row stays visible
(`bal/gui/qt/widgets.py`, `WillSettingsWidget`);
- the "check alive" postpone behaviour is disabled: `init_class_variables`
skips raising `CheckAliveError`, so a passed check-alive date never forces a
postpone/rewrite (`bal/gui/qt/window.py`). The delivery time is unaffected.
### Verification
- `ruff`: no new errors in the changed lines (only the usual pre-existing
F401/F403/F405 star-import noise and one pre-existing F841).
- Full test suite: `239 passed`.
**Outcome:** DONE (delivered as test ZIP v0.4.0; commit only after the user
confirms the ZIP works).
---
## 17. v0.4.1 - Heir-change rebuild fix, all heirs in green, UI tidy-up, unified 20s deadline
**Date:** 2026-06-24
**Goal:** Address the user's v0.4.0 test feedback (points A-K), with the most
important items being two functional bugs surfaced via a log:
- **(E + F + K) Heir-change full rebuild (the core fix):** After deleting or
changing an heir, pressing CHECK said "Found CHANGES... a NEW WILL must be
prepared" but then "Signing: Nothing to do" / "Broadcasting: Nothing to do",
and the rebuilt transactions were never signed or broadcast. The log showed
that, right after `build_will()`, a second coherence check raised
`HeirNotFoundException(<heir name>)`, which was (1) shown in RED with the heir
name (bug E) and (2) made the dialog return `have_to_sign=False` (bugs F/K).
Root cause: `Will.update_will` reused the OLD (already signed/COMPLETE)
`WillItem` whenever a rebuilt transaction kept the same txid, copying only the
new heirs onto it - so a stale heir set survived and the item stayed COMPLETE.
Fix (user-approved "Option A"): a new helper `Will._same_heirs` compares the
real heirs (address/amount/locktime, ignoring the reserved `w!ll3x3c"`
will-executor pseudo-heirs); the old item is reused ONLY when the heirs are
identical, otherwise the freshly built (unsigned) item is kept so it is
correctly detected as needing signing and broadcasting. This also fixes K:
once the rebuilt tx is not COMPLETE, the existing auto-sign + auto-push path
(have_to_push when a will-executor is attached) fires automatically.
- **(E) Show all heirs in GREEN:** On a successful build, every heir is now
listed on its own line in green ("Ok" colour) in the Building Will window,
instead of a heir name ever appearing in red as exception text.
- **(A)** Delivery-time help popup: added a "(ONLY IN ADVANCED MODE)" line
before the Raw-suffix explanation.
- **(B)** Plugin settings: added a blank gap below the red warning, above the
first setting row.
- **(C)** Renamed the "USER TYPE" settings label to "User Type".
- **(D)** Renamed "Editable dates" to "Panel editable Date and Fee".
- **(G + K-label)** Moved the will-executor backup-tx checkbox up (now right
below "Panel editable Date and Fee", above "Number of reminders") and renamed
it from "No will-executor TX" to "Add transaction without willexecutor".
- **(H)** Wizard date-field layout: date rows are now a compact fixed width
(no longer the inflated 40-char minimum), the calendar button is left-aligned
and no longer stretches, and the mining-fee field is sized to ~5 characters.
ADVANCED mode (with the extra Check-Alive row) stays aligned.
- **(J)** Network waits unified into a single shared
`Willexecutors.NETWORK_DEADLINE = 20` constant (was 30s push/check and 45s
download); push/check/ping/download all derive from it.
**Files changed:**
- `bal/core/will.py` - new `_same_heirs` helper; `update_will` reuses old item
only when heirs are identical (Option A).
- `bal/gui/qt/dialogs.py` - list all heirs in green on successful build.
- `bal/gui/qt/widgets.py` - help-text line (A); compact, left-aligned wizard
date/fee layout (H).
- `bal/gui/qt/plugin.py` - settings labels/spacing/row order (B, C, D, G, K-label).
- `bal/core/willexecutors.py` - single `NETWORK_DEADLINE = 20` (J).
- `bal/gui/qt/window.py` - download deadline derives from `NETWORK_DEADLINE` (J).
- `tests/test_group_f_heir_change_rebuild.py` - new unit tests for `_same_heirs`.
- Version bumped 0.4.0 -> 0.4.1 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `ruff`: no new errors (only the pre-existing F401/F403/F405 star-import noise
and two pre-existing F841 at `will.py:151` and `dialogs.py:645`).
- Full test suite: `248 passed` (239 existing + 9 new Group F tests).
**Outcome:** PARTIAL (delivered as test ZIP v0.4.1). User testing showed that
the CORE bug (E/F/K) was STILL present: the `_same_heirs`/`update_will` fix was
misdirected - on a real heir change the rebuilt transactions get COMPLETELY NEW
txids, so `update_will`'s "reuse the old item when the txid matches" branch never
runs. The real cause was found and fixed in entry 18. The `_same_heirs` helper is
kept as a correct safety improvement. Items A, B, C, D, G, J, I were confirmed OK.
---
## 18. v0.4.2 - Real fix for the heir-change rebuild bug (E/F/K), wizard layout (H), tooltip
**Date:** 2026-06-24
**Goal:** Fix the bugs the user found while testing v0.4.1: after adding or
deleting an heir (or building from the wizard), the heir name was shown in RED
and the will was reported as "Nothing to do" (never signed/broadcast); the
green heir list disappeared; the wizard icons/fields were still misaligned and
the fee box was too small; and a tooltip needed rewording.
**Root cause of E/F/K (confirmed from the user's Electrum log):** In
`task_phase1` (dialogs.py), after `build_will()` rebuilds the whole will, a
SECOND `check_will()` re-validates it. When the heirs (or date) changed, this
re-validation legitimately raises a `NotCompleteWillException` subclass
(`HeirNotFoundException`) - the freshly rebuilt, still-unsigned transactions do
not yet "cover" the new heir set. That exception was caught by the GENERIC
`except Exception`, which printed the heir name in RED and returned
`have_to_sign=False`, so the rebuilt will was never signed ("Nothing to do").
**What changed:**
- **(E/F/K)** `bal/gui/qt/dialogs.py`: added a dedicated
`except NotCompleteWillException` handler BEFORE the generic `except Exception`
(and after the existing `WillExecutorNotPresent` / `WillExpiredException`
handlers, whose order matters). It treats the post-build re-validation failure
as what it really is - "the will was rebuilt and now needs signing" - instead
of an error. It shows the green "Ok" result and the full heir list, then falls
through to the existing `have_to_sign` detection, so the new (status "New")
transactions are correctly signed and (with a will-executor attached)
auto-broadcast. The heir-green-listing was extracted into a new
`_build_success_report()` helper, used on BOTH the clean and the rebuilt
paths, so the green heir list is always shown and the heir name never appears
in red again.
- **(H)** `bal/gui/qt/widgets.py`: reworked the wizard's vertical layout. Every
leading icon (delivery time, check-alive, calendar, fee) is forced to the same
fixed width and the composites are stacked left-aligned, so the icons line up
one under the other and every field starts at the same x just to their right.
The fee field is WIDENED (~8 chars) so the spin-box arrows no longer cover the
digits. The composites are kept INTACT (not split apart) because the
delivery-time / check-alive widgets hold two editors plus a runtime Raw/Date
selector in ADVANCED mode; splitting them would break that mode.
- **Tooltip** `bal/gui/qt/widgets.py`: the delivery-time icon tooltip now reads
exactly "Delivery Time, click for more information".
**Files changed:**
- `bal/gui/qt/dialogs.py` - `NotCompleteWillException` handler + `_build_success_report`.
- `bal/gui/qt/widgets.py` - wizard layout (H) and tooltip wording.
- Version bumped 0.4.1 -> 0.4.2 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `ruff`: no new errors (only pre-existing star-import noise and the two
pre-existing F841 at `widgets.py:563` and `dialogs.py:645`).
- Full test suite: `248 passed`.
**Outcome:** DONE (delivered as test ZIP v0.4.2; commit only after the user
confirms the ZIP works).
---
## 19. v0.4.3 - Sync delivery date after auto-anticipation; BASIC calendar reminders; sign-reason message
**Date:** 2026-06-24
**Goal:** Fix a follow-up bug reported by the owner after testing v0.4.2, add a
clearer message when signing is requested, and make the calendar export work in
BASIC mode.
**(1) Delivery date out of sync after an automatic anticipation (main bug).**
Root cause (confirmed from the code + the owner's report): when the will is
rebuilt while it still spends the same coins as a previous one (e.g. after
deleting an heir WITHOUT changing the date), the core engine AUTOMATICALLY
anticipates the transaction locktime by one day (Will.check_anticipate /
Util.anticipate_locktime) so the new transaction can be mined before the old
one. However the plugin's stored delivery date (WILL_SETTINGS["locktime"]) was
NOT updated, so on the next Check the plugin compared the stored date (original)
with the transaction locktime (original minus one day), mistook the automatic
anticipation for a user POSTPONE, and wrongly asked to invalidate the will.
Fix: after a (re)build, `BalBuildWillDialog._sync_locktime_to_built_txs` sets the
stored delivery date to the MINIMUM locktime among the valid built transactions
(via `Will.get_min_locktime`). The date is only ever moved EARLIER
(anticipation); a genuine user postpone is never overwritten. The update is
routed through `BalWindow.update_setting_widgets`, which stores the value,
persists it and refreshes the date widgets in every panel/wizard, so the visible
delivery date reflects the anticipated date and the calendar (.ics) export uses
it too (owner-confirmed behaviour; the minimum is used when several
transactions carry different locktimes).
**(2) Explain WHY signing is requested after an anticipation.**
When the date was auto-anticipated, the wizard now shows an orange note on the
"Building your will" row before the sign prompt: "The delivery date was
automatically moved one day earlier so the updated will can correctly replace
the previous one. Please sign (and broadcast) to confirm the change." (A
`_date_was_anticipated` flag set by the sync step drives this.)
**(3) Calendar reminders in BASIC mode.**
In BASIC mode the check-alive parameter is hidden and not managed by the user,
so spreading reminders over the check-alive period (the ADVANCED behaviour) is
meaningless and produced wrong/garbage dates. The calendar now uses three FIXED
reminders in BASIC - 30, 10 and 1 day before the inheritance delivery date -
dropping any offset that would fall in the past. ADVANCED mode is unchanged. The
logic is a pure helper `basic_reminder_offsets()` with unit tests.
**Files changed:**
- `bal/gui/qt/dialogs.py` - `_sync_locktime_to_built_txs` + `_date_was_anticipated`
flag + sign-reason note.
- `bal/gui/qt/widgets.py` - BASIC calendar reminders (`basic_reminder_offsets`,
`BASIC_REMINDER_OFFSETS`); the .ics export now uses the anticipated delivery
date.
- `tests/test_group_g_basic_calendar.py` - new unit tests for the BASIC
reminder offsets.
- Version bumped 0.4.2 -> 0.4.3 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `ruff`: no new errors (only pre-existing star-import noise and the two
pre-existing F841 at `widgets.py:563` and `dialogs.py:645`).
- Full test suite: `255 passed` (248 + 7 new BASIC-calendar tests).
**Outcome:** DONE (delivered as test ZIP v0.4.3; commit only after the user
confirms the ZIP works).
---
## 20. v0.4.4 - Wizard hints, BASIC/ADVANCED help text, Check tooltip, ADVANCED check-alive visibility, backup-tx default OFF
**Date:** 2026-06-24
**Goal:** Apply a batch of owner-requested UI/wording fixes and fix a visibility
bug for the Check-Alive field.
**What changed:**
- **(P1)** Plugin settings, "Number of reminders" help text: rewritten to
document BASIC and ADVANCED separately. BASIC: "Calendar reminder 30, 10 and
1 days before."; ADVANCED: the previous "spread across the check-alive period"
explanation (range 1 to 5, default 3).
- **(P2)** Build-your-will WIZARD only: added an explanatory label ABOVE the
date field - "Enter the date on which you want the inheritance (or backup) of
your Electrum wallet to take effect." - and a cautionary label BELOW the
miner-fee field - "Please note: Do not reduce the miner fees unless you know
what you're doing". These appear only in the wizard (vertical layout), not on
the WILL/HEIR toolbars.
- **(P3)** WILL tab: the Check button tooltip changed from "Check" to
"Check Inheritance" so the icon's purpose is clear.
- **(P4 - bug fix)** Check-Alive visibility on USER TYPE change: the WILL/HEIR
toolbar settings widgets are created once and reused for the whole session, so
switching from BASIC to ADVANCED previously did NOT re-show the Check-Alive
field there (it reappeared only in the freshly-created wizard). Added
`WillSettingsWidget.apply_user_type_visibility()`, called from
`BalWindow.update_all()` (which the USER TYPE combo triggers), so the
Check-Alive field is shown/hidden immediately on the existing WILL and HEIR
tabs too, without restarting Electrum.
- **(P5)** "Add transaction without willexecutor" now defaults to OFF
(`NO_WILLEXECUTOR` default False). A fresh wallet therefore does NOT create the
extra no-will-executor backup transaction unless the user enables it from the
wizard. The value is persisted in Electrum's configuration (as before), so the
plugin always follows the saved choice; the new default only applies when no
value has been stored yet.
**Files changed:**
- `bal/gui/qt/plugin.py` - reminders help text (P1).
- `bal/gui/qt/widgets.py` - wizard date hint + miner-fee note (P2);
`apply_user_type_visibility` (P4).
- `bal/gui/qt/lists.py` - Check tooltip -> "Check Inheritance" (P3).
- `bal/gui/qt/window.py` - call `apply_user_type_visibility` from `update_all` (P4).
- `bal/core/plugin_base.py` - `NO_WILLEXECUTOR` default -> False (P5).
- Version bumped 0.4.3 -> 0.4.4 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `ruff`: no new errors (only pre-existing star-import noise and pre-existing
F841 at `widgets.py:591`, `lists.py:185` and `lists.py:272`).
- Full test suite: `255 passed`.
**Outcome:** DONE (delivered as test ZIP v0.4.4; commit only after the user
confirms the ZIP works).
---
## 21. v0.4.5 - Fix invalidation loop (10s pause + stop-on-persistent-postpone), add "BAL Invalidate transaction" label on the automatic path, fix wizard text truncation
**Date:** 2026-06-24
**Reported issues (after testing v0.4.4):**
- **Issue 1 (wizard text truncated):** the explanatory hint above the delivery
date and the miner-fee note below the fee field were displayed cut in half.
- **Issue 2 (invalidation loop):** when the user postpones the delivery date,
the "Invalidate your old will" window appears; after signing, an IDENTICAL
invalidation window immediately appears again (endless loop) while Electrum
is still broadcasting the transaction. In addition, the
"BAL Invalidate transaction" label did not appear in Electrum's on-chain
history for this automatic ("postpone") path.
**Root causes:**
- Issue 1: the wizard QLabels had `setWordWrap(True)` but were added to the
vertical layout with `AlignLeft`, so each label took its narrow `sizeHint`
width. Word-wrap then computed line breaks against an almost-zero width and
the text appeared truncated.
- Issue 2a (loop): after broadcasting the automatic invalidation,
`on_success_invalidate` re-ran phase 1 immediately. Electrum had not yet seen
the invalidation transaction, so phase 1 still detected a postpone and the
wizard re-prompted to invalidate - forever.
- Issue 2b (missing label): the automatic broadcast in
`loop_broadcast_invalidating` did not set any history label (unlike the
Tools -> Invalidate menu path, which does).
**What changed (user-approved solution):**
- `bal/gui/qt/dialogs.py`
- `loop_broadcast_invalidating`: set the `"BAL Invalidate transaction"`
history label (fixes Issue 2b), matching the Tools -> Invalidate menu.
IMPORTANT follow-up fix: the first attempt did not work because it took the
txid from `Network.broadcast_transaction()`'s return value - but that method
is declared `-> None` and ALWAYS returns None, so the `set_label()` call
sat in an `else` branch that was never reached. The label is now taken from
`tx.txid()` (the transaction is already signed and complete here, so this is
the stable, correct id - exactly what the working Tools -> Invalidate path
uses) and is set BEFORE broadcasting (set_label is local-only, no network).
- `invalidate_task`: the post-broadcast pause is now 10 seconds (was 5) so
Electrum has time to register the new transaction before phase 1 re-runs;
a new `self._invalidation_broadcast` flag is set after the broadcast.
- `on_success_phase1` (the `have_to_sign is None` branch): if
`_invalidation_broadcast` is set and a postpone is STILL detected, STOP with
a clear message ("Your old will has been invalidated and the transaction was
broadcast... please wait until it is confirmed, then press Check again")
instead of re-prompting to invalidate (fixes Issue 2a - no more loop).
- `__init__`: initialise `self._invalidation_broadcast = False`.
- `bal/gui/qt/widgets.py`
- Wizard branch: give the two explanatory QLabels (delivery-date hint and
miner-fee note) a `setMinimumWidth(30 * char_width_in_lineedit())` so
word-wrap uses the full dialog width and the whole text is visible
(fixes Issue 1).
- Version bumped 0.4.4 -> 0.4.5 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `py_compile`: `dialogs.py` and `widgets.py` compile OK.
- Full test suite: `255 passed`.
- `ruff`: no new errors (only pre-existing star-import F401/F403/F405 noise and
the pre-existing F841 `e` warnings).
- Headless wizard label check: with the 270px minimum width, both labels wrap
to multiple lines (3 lines and 2 lines) instead of being truncated.
**Outcome:** DONE (delivered as test ZIP v0.4.5; commit only after the user
confirms the ZIP works).
---
## 22. v0.4.6 - DUST one-line-per-heir, heirs on one line, scrollable report, wizard final check, wizard text truncation fix, anticipated-date notice styling
**Date:** 2026-06-24
**Reported issues (after testing v0.4.5):**
- **Issue 1 (allegato13 - DUST):** when the wallet balance is below the dust
limit the inheritance is not feasible. The dialog printed the "is DUST"
exclusion ONCE PER (will-executor x heir): with 20 will-executors and 10
heirs that is 200 identical rows. There should be one row PER HEIR.
- **Issue 2 (allegato18 - heirs):** heirs were listed one per line, wasting
vertical space. They should be on a single line: "Heirs: a, b, c".
- **Issue 3 (allegato14 - overflow):** with many will-executors the "Building
Will" window kept growing taller for every line until it ran off-screen and
the bottom buttons became unreachable. It needs a scrollable area.
- **Issue 4 (allegato15 - wizard final check, case B):** the wizard did not run
the final will-executor verification, so the user always had to press "Check"
manually afterwards.
- **Issue 5 (allegato16 - wizard text truncated):** the wizard hint and fee
note were still cut in half.
- **Issue 6 (allegato17 - notice styling):** the yellow "delivery date was
moved" notice should be black bold and split onto two lines.
**Root causes:**
- Issue 1: a double loop over every valid will AND every heir printed the dust
row N_executors x N_heirs times.
- Issue 4: the wizard's `on_next_we` only called `build_will_task()` and, unlike
the "Check" button (`lists.py:check()`), never called
`check_transactions()`.
- Issue 5: the two QLabels were added to the vertical layout WITH an
`alignment=AlignLeft` flag. A widget added with an alignment flag is NOT
stretched to the layout width, so the word-wrapped label used a narrow
sizeHint width and the reserved height was too small, cutting the text.
`setMinimumWidth` alone could not fix it because the alignment flag still
blocked horizontal stretch.
**What changed:**
- `bal/gui/qt/dialogs.py`
- `task_phase1` (DUST report): collect dust heirs in a de-duplicated dict and
print ONE row per heir, without the will-executor reference (Issue 1).
- `_build_success_report`: list all heirs on a SINGLE green/bold line
("Heirs: a, b, c") instead of one row each (Issue 2).
- `__init__` + `msg_update`: wrap the report label in a `QScrollArea` with a
capped maximum height (400px) and auto-scroll to the bottom, so the dialog
no longer grows off-screen and the buttons stay reachable (Issue 3).
- `BalWizardDialog.on_next_we`: after building, run the SAME final
`check_transactions()` as the Check button (`Will.needs_server_check`),
so the wizard performs the will-executor verification automatically
(Issue 4).
- `on_success_phase1`: the anticipated-date notice is now black bold and split
onto two lines after "...previous one." (Issue 6).
- `bal/gui/qt/widgets.py`
- Wizard branch: add the two explanatory QLabels WITHOUT an alignment flag and
with an Expanding/Minimum size policy (plus a minimum width on the widget),
so they stretch to the full width and word-wrap correctly instead of being
truncated (Issue 5).
- Version bumped 0.4.5 -> 0.4.6 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `py_compile`: `dialogs.py` and `widgets.py` compile OK.
- Full test suite: `255 passed`.
- `ruff`: no new errors (only pre-existing star-import noise and the
pre-existing F841 `e` warnings).
- Headless wizard label check (real wizard layout, 780px dialog): both labels
expand to the full width (740px) and the full text fits - NOT truncated.
**Outcome:** DONE (delivered as test ZIP v0.4.6; commit only after the user
confirms the ZIP works).
---
## 23. v0.4.7 - Report area opens 500px tall, heirs back to one-per-line, explicit wizard line breaks, ALL-DUST guard (block only when EVERY heir is dust)
**Date:** 2026-06-24
**Goal (4 owner-approved changes from testing v0.4.6):**
1. (allegato1) The scrollable "Building Will" report opened far too short
(~140px). Open it already 500px tall, growing up to 700px before the
scrollbar takes over.
2. Revert the v0.4.6 "all heirs on one line" form back to ONE heir per line
(green, bold). Heir names can be long and, now that the report scrolls,
there is no need to compress them.
3. (allegato2) Add an explicit line break in two wizard texts at the exact
spots the owner marked: after "(or backup)" in the date hint and after
"miner fees" in the fee note.
4. (LOG analysis) When EVERY heir's share is below the dust limit the will was
still built, signed, checked and listed - an "empty" inheritance that pays
nobody. Block it with a clear message, but ONLY when ALL heirs are dust; a
mix of dust + valid heirs must keep building normally.
**What changed:**
- `bal/gui/qt/dialogs.py`
- `BalBuildWillDialog.__init__`: report `QScrollArea` now
`setMinimumHeight(500)` / `setMaximumHeight(700)` (change 1).
- `_build_success_report`: list each heir on its own line again, green +
bold, de-duplicated and skipping the internal will-executor pseudo-heirs
(change 2, revert of v0.4.6).
- `task_phase1`: add a dedicated `except HeirAmountIsDustException` handler
BEFORE the generic `except Exception`, showing a clear RED message
("All heirs' shares are below the dust limit: the inheritance cannot be
created. Increase the amounts or reduce the number of heirs.") and stopping
without signing/checking, so no empty will is created (change 4).
- `bal/gui/qt/widgets.py`
- Wizard date hint: explicit `\n` after "(or backup)".
- Wizard fee note: explicit `\n` after "miner fees" (change 3).
- `bal/core/heirs.py`
- `prepare_lists`: NEW all-dust guard added at the END of the function, where
the `locktimes` dict already contains EVERY heir of EVERY locktime with the
final dust marking. It counts the real heirs (excluding the `w!ll3x3c"`
will-executor pseudo-heirs) and how many have a valid, non-dust amount; if
there are real heirs but none is payable it raises
`HeirAmountIsDustException` (change 4).
- WHY here and NOT in `prepare_transactions`: `prepare_transactions` only
ever processes the single lowest locktime, so a guard there would wrongly
block a will whose later locktimes still have valid heirs (false positive).
`prepare_lists` is the only place that sees all heirs/locktimes AND the
final dust state of both fixed and percentage heirs.
- The `HeirAmountIsDustException` raised here propagates cleanly: it is not a
`WillExecutorFeeException`, so it skips that handler in `buildTransactions`
and reaches the GUI without the misleading "error preparing transactions"
log.
- `bal/gui/qt/common.py`
- Import `HeirAmountIsDustException` from `...core.heirs` so it is available
to `dialogs.py` via `from .common import *`.
- `tests/test_core_heirs_extra.py`
- Add 3 tests pinning the dust logic:
`test_prepare_lists_all_dust_raises` (tiny balance + percentages -> raises),
`test_prepare_lists_mixed_dust_continues` (dust + valid -> no raise),
`test_prepare_lists_multi_locktime_continues` (dust on early date, valid on
later date -> no raise; guards against the false positive).
- Version bumped 0.4.6 -> 0.4.7 (`plugin_base.py`, `__init__.py`, `VERSION`,
`manifest.json`).
**Verification:**
- `py_compile`: `heirs.py`, `common.py`, `dialogs.py` and the test file compile OK.
- Full test suite: `258 passed` (255 previous + 3 new dust tests).
- `ruff`: no new errors (only pre-existing star-import noise and the
pre-existing F841 `e` warnings).
- Manual dust trace (real `prepare_lists`, mocked wallet): all-dust raises;
mixed and multi-locktime continue and keep the valid heir.
**Outcome:** DONE (delivered as test ZIP v0.4.7; commit only after the user
confirms the ZIP works).