- Sort imports and fix pyproject ruff config (per-file ignores for
intentional Qt/core exceptions)
- Mark Heirs.validate_* helpers as @staticmethod
- Clean up dead code, rename shadowing vars, use raise ... from
- Add AGENTS.md with env/lint/test/release guidance
Allow heirs to produce an OP_RETURN output instead of a regular BTC
payment. An address prefixed with OP_RETURN: carries hex data (max 80
bytes); such heirs always have amount 0 and are excluded from amount
calculations. GUI dialog shows a message field with the decoded text,
the heir list displays the decoded message, and the OP_RETURN output
is emitted with zero value in inheritance transactions.
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.
GUI / plugin lifecycle:
- Auto-close Electrum's native 'Electrum Plugins' manager dialog after the
BAL plugin is hot-enabled. Electrum 4.7.x no longer calls the old init_qt
hook, so the close is now triggered from the create_status_bar, init_menubar
and load_wallet hooks (fired when reload_windows() recreates the window).
- Robust dialog matching (isinstance / class name / localized window title) to
cope with zipimport module-identity mismatches.
- Robust dismissal of the modal dialog (reject()/done()/close()) with a retry
schedule [400, 800, 1500] ms; if it still cannot be closed, fall back to
bringing it to the front (showNormal/raise_/activateWindow) so it never
lingers hidden in the background. Counting only visible top-levels avoids
treating an already-closed dialog as still open.
Read-only field styling:
- Paint the locked Delivery time / Check Alive date editors and the mining-fee
spinbox with a light-grey background (#f0f0f0) so the user can see at a glance
that they are not editable outside the 'Build your will' wizard; the styling
is cleared when the fields are made editable again.
Pickle/RLock crash on 'Build will':
- heirs.save() now sanitises the heirs mapping via _json_safe() before handing
it to json_db.put(), which deep-copies the value. A live runtime object
(holding a threading.RLock) slipping into an heir value previously raised
'TypeError: cannot pickle _thread.RLock object' and aborted the task; such
values are now coerced to str and logged with their path.
- init_heirs_to_locktime() coerces the locktime to a plain serializable scalar.
- log_error() now accepts both a sys.exc_info() triple and a single exception
instance, fixing the secondary 'TypeError object is not subscriptable' that
masked the real error.