From ed83af6be9d23ee8b7bdde837e80bffc1ec59ff7 Mon Sep 17 00:00:00 2001 From: donkey-ai Date: Sun, 28 Jun 2026 23:02:18 -0400 Subject: [PATCH] docs(bal): add HANDOFF.md to resume the project in a new chat/model Adds a single handoff document summarizing the mandatory rules (R1-R4, zip-first), project overview, build/test/release commands, current state (v0.3.9 released), the pending 'unify invalidate procedure' task with the user-approved warning popup text, and a key file/line map. Lets any new chat or AI model resume work without losing context. Documentation only; no plugin code changed. --- HANDOFF.md | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 HANDOFF.md diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 0000000..2a6204a --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,221 @@ +# HANDOFF — BAL Electrum Plugin (Bitcoin After Life) + +> **Purpose of this file:** allow ANY new chat / AI model to resume this project +> WITHOUT losing context. If you are a new assistant, READ THIS FILE FIRST, +> then read `CHANGELOG.md` and `.agent_memory_tasks.md`. +> +> **Chat language is Italian, but ALL output (code, comments, UI text, docs, +> CHANGELOG, commit messages) MUST be in ENGLISH.** The user is NOT a programmer. + +--- + +## 1. MANDATORY STANDING RULES (apply to EVERY task, never skip) + +- **R1 — LANGUAGE:** Italian is ONLY for chatting. ALL deliverables in ENGLISH + (code, docstrings, comments, UI strings, docs, CHANGELOG, commit messages). +- **R2 — DOCUMENTED CODE:** every method/class needs a docstring + explanatory + comments. When a non-obvious design choice is made, explain WHY in a comment. +- **R3 — NEVER INVENT:** if anything is missing or unclear, STOP and ask the user + clear, simple questions (he is not a programmer). Be "100% sure" before acting. +- **R4 — HUMAN CHECKPOINT:** before writing/modifying code, show the PLAN and WAIT + for the user's explicit "OK". +- **METHOD per task:** DISCOVER → PLAN (wait OK) → EXECUTE → VERIFY → ITERATE + (max 8 attempts, then declare "UNRESOLVED"). +- **LOG:** a single `CHANGELOG.md` in English, one numbered entry per task. +- **ZIP-FIRST:** always deliver a test ZIP for the user to try BEFORE committing + plugin code. Commit ONLY after explicit user confirmation. +- Always run `ruff` + the official test suite before committing/reporting/zipping. + +--- + +## 2. PROJECT OVERVIEW + +- Electrum **4.7.2** Qt (PyQt6) inheritance plugin. +- External zip plugin id: `electrum_external_plugins.bal`. +- **zipimport caches the plugin → the user MUST fully restart Electrum after + installing a new ZIP** (always remind him). +- Repo: `Bitcoin-after-life/test` (GitHub). Working branch: `genspark_ai_developer`. +- Main code lives under `bal/`. Tests under `tests/`. Electrum source vendored in + `electrum-src/` (read-only reference). + +### Version files (keep ALL FOUR in sync on every release) +- `bal/manifest.json` → `"version"` +- `bal/__init__.py` → `__version__` +- `bal/core/plugin_base.py` → `__version__ = "..." # AUTOMATICALLY GENERATED DO NOT EDIT` +- `bal/VERSION` +- **Current released version: 0.3.9** + +--- + +## 3. BUILD / TEST / RELEASE COMMANDS + +### Build the ZIP (clear caches first) +```bash +cd /home/user/webapp +find bal -name "__pycache__" -type d -exec rm -rf {} + ; find bal -name "*.pyc" -delete +python3 build_zip.py bal-electrum-plugin-vX.Y.Z.zip # builds 37 files +``` + +### Run the full test suite (must stay GREEN: 239 passed) +```bash +cd /home/user/webapp +QT_QPA_PLATFORM=offscreen PYTHONPATH=electrum-src python3 -m pytest \ + tests/test_core_*.py tests/test_gui_*.py \ + tests/test_anticipate_past_locktime.py tests/test_anticipate_manual_locktime.py \ + tests/test_group_b_auto_sign.py tests/test_group_c_settings.py \ + tests/test_group_d_alarms.py tests/test_group_e_mock_giovanna7.py -q +``` + +### Ruff (only PRE-EXISTING noise is acceptable) +```bash +cd /home/user/webapp && ruff check bal/ +``` +Pre-existing warnings that are NOT your fault and can be ignored: +- `F401/F403/F405` star-import noise (`from .common import *`). +- `F841` at: `will.py:151`, `dialogs.py:615` (`except NoHeirsException as e`), + `lists.py:185`, `lists.py:272`. +- `E501` long lines in several pre-existing spots. + +### Git / PR / Release workflow +- `setup_github_environment` first. If `git push` fails with + "Invalid username or token", CALL `setup_github_environment` AGAIN, then retry. +- Token for API calls: + `TOKEN=$(sed -n 's#https://\([^:]*\):\([^@]*\)@.*#\2#p' ~/.git-credentials | head -1)` +- Repo for API: `Bitcoin-after-life/test`. +- Releases published so far: v0.3.6, v0.3.7, v0.3.8, **v0.3.9 (latest)**. +- Attach the ZIP as a release asset via the uploads API. + +--- + +## 4. CURRENT STATE (as of v0.3.9, COMMITTED + RELEASED) + +v0.3.9 is merged to `main` (PR #11) and released with the ZIP attached. +sha256 of the released ZIP: `cd52b6f5e6276fb707ea4bf477a00bd0469dfd88960282fb74c219ee0f5f4292`. + +What shipped in v0.3.9 (TASK A/B/C/D + regression fix A3): +- **A** clearer "Will expired" message: shortened will id (8+8 chars via + `Will._short_will_id`) + readable UTC date (`Will._format_locktime`) instead of + raw UNIX timestamp; shown ORANGE (warning) not RED (error) in the wizard. +- **A2** message split on two lines via `
` (rendered as HTML by `msg_warning`). +- **B** History labels (text only): inheritance tx → `BAL Inheritance transaction`; + invalidate tx → `BAL Invalidate transaction`. Colours = Electrum defaults + (Electrum colours outgoing tx descriptions red by itself, history_list.py:193-196). +- **C** "No will-executor TX" checkbox in plugin Settings (`plugin.py`), bound to + existing `NO_WILLEXECUTOR` config (default ON, line plugin_base.py:193), with help + text "Create a will that does not require a Will-executor; it can be saved, for + example, on a USB stick, and a copy can be given to the heirs." Included in reset. +- **D** wizard button "Create your will" → "Build Your Will" (lists.py:473). +- **A3 regression fix:** adding an heir to an expired will via the wizard no longer + failed to invalidate. Implemented a `"invalidate_classic"` signal returned from + `task_phase1` and handled in `on_success_phase1` (dialogs.py) that closes the + wizard and shows a popup telling the user to use `Tools → Invalidate`. + **NOTE: this popup text is about to be CHANGED — see the pending task below.** + +--- + +## 5. PENDING TASK (NOT STARTED) — UNIFY THE INVALIDATE PROCEDURE + +### Problem the user reported +There are currently TWO different "invalidate" procedures, and the user wants ONE +identical behaviour for BOTH the **CHECK button** and the **WIZARD**. + +- **PROCEDURE 1 "classic/manual"** = `window.py::invalidate_will` (line ~695): + waiting dialog → "please sign and broadcast" popup → CLASSIC Electrum tx window + (Sign/Broadcast buttons) → **SETS** history label "BAL Invalidate transaction" + (line ~704). Used by: Tools→Invalidate menu (lists.py:468→571), a dialog button + (dialogs.py:1356), the on-close/postpone paths (window.py:539,576,614). + **This window opens correctly IN FRONT** (user confirmed) because nothing else + is closing at the same time. +- **PROCEDURE 2 "automatic"** = `dialogs.py::invalidate_task` (line ~902): + password prompt inside the wizard → sign + auto-broadcast + (`loop_broadcast_invalidating`, line ~729) → **does NOT set the history label**. + Used by the CHECK button and the FIRST `WillExpiredException` handler in + `task_phase1` (dialogs.py:594 → `return None, Will.invalidate_will` at ~598, + which makes `on_success_phase1` see `have_to_sign is None` → password prompt). + +### CHECK button flow (important) +`lists.py:545 check()` → `BalBuildWillDialog(...).build_will_task()` → +`task_phase1` → `on_success_phase1`. **It is the SAME engine as the wizard.** + +### FINAL REQUIREMENT (user-approved, OPTION A refined) +Make CHECK and WIZARD behave IDENTICALLY for an expired will: +1. First show a **WARNING popup** (REMOVE the old "use the top-right menu + Tools → Invalidate" wording). +2. Then **AUTOMATICALLY open the CLASSIC Electrum sign window** (PROCEDURE 1, + `window.py::invalidate_will`) so the label is set and the user can Sign + Broadcast. +3. Sequence: warning popup → user clicks OK → classic sign window opens BY ITSELF, + IN FRONT. + +### APPROVED WARNING POPUP TEXT (verbatim, English per R1) +``` +Your will has expired and must be invalidated before it can be rebuilt. +A transaction window will now open: +please SIGN and then BROADCAST it to invalidate your old will. +After the invalidation is confirmed, press the Check button to finish the will. +``` +(The user wrote "SIGN and then BROADCAST" with a double space — normalize to a +single space unless he objects.) + +### Implementation approach (agreed in principle; still needs final PLAN + OK) +- Route the expired cases (FIRST handler at ~594, the `invalidate_classic` block, + and therefore the CHECK button) through ONE shared helper that: + (a) closes the CHECK/wizard dialog FIRST, + (b) shows the warning popup, + (c) then calls `self.bal_window.invalidate_will()` (PROCEDURE 1) LAST, so the + classic window is the last thing opened and stays in front. +- Drop the use of PROCEDURE 2 (`invalidate_task`) for the expired case. +- **KNOWN RISK / why this is delicate:** earlier attempts to auto-open the classic + window *while the wizard was closing* put it BEHIND the main wallet window on the + user's machine (Windows focus/stacking). The fix is to make sure NOTHING closes + AFTER the classic window opens (close the dialog first, open the tx window last). + `Tools → Invalidate` works perfectly precisely because no other window is closing. +- The user has hinted he may add MORE requirements before this is implemented, so + CONFIRM the full scope before coding. + +### Status: WAITING. Do NOT code yet. Build full PLAN → wait OK (R4) → zip-first. + +--- + +## 6. KEY FILE / LINE REFERENCES (verify line numbers, they drift) + +- `bal/core/will.py` + - `check_will()` order (line ~561): `check_invalidated` → `check_will_expired` + (raises `WillExpiredException`) → `search_rai` (raises `HeirNotFoundException`). + - `invalidate_will()` static (line ~394) builds the invalidation PartialTransaction. + - `_short_will_id` / `_format_locktime` helpers + the expired message (with `
`). +- `bal/gui/qt/dialogs.py` + - `BalBuildWillDialog` is the CHECK + wizard engine. + - `build_will_task()` (~530) starts `task_phase1`. + - `task_phase1()` (~542): first `check_will()`; first `WillExpiredException` + handler (~594) → `return None, Will.invalidate_will(...)`; `NotCompleteWill`/ + `HeirNotFound` → `have_to_build`; inner `check_will()`; inner `WillExpiredException` + (~659) → currently returns `"invalidate_classic", None`. + - `on_success_phase1()` (~924): unpacks `(have_to_sign, tx)`. If + `have_to_sign == "invalidate_classic"` → shows popup. If `have_to_sign is None` + → password prompt "Invalidate your old will" → `invalidate_task` (PROCEDURE 2). + - `invalidate_task()` (~902) + `loop_broadcast_invalidating()` (~729): PROCEDURE 2. + - `QTimer` is available via `from .common import *` (defined in common.py:53). +- `bal/gui/qt/window.py` + - `invalidate_will()` (~695): PROCEDURE 1 (the "good" one). Sets label at ~704. + - `show_transaction_real()` (~656) uses `show_on_top(d, modal_to_window=False)`. + - on-close/postpone expired handling at ~539, ~576, ~614. +- `bal/gui/qt/lists.py` + - `check()` (~545): the CHECK button. `invalidate_will()` (~571). Menu actions + "Check"/"Invalidate" at ~467/468. Wizard button "Build Your Will" at ~473. +- `bal/gui/qt/plugin.py`: settings dialog; "No will-executor TX" checkbox + reset. +- `bal/gui/qt/window_utils.py`: `show_on_top` (~100), `bring_to_front` (~52), + `show_modal` (~86). +- `bal/gui/qt/common.py`: `add_widget` helper (~98); imports `QTimer`, `Qt`, etc. + +--- + +## 7. HOW TO RESUME IN A NEW CHAT (any model) + +Tell the new assistant: +> "Read `/home/user/webapp/HANDOFF.md`, then `CHANGELOG.md` and +> `.agent_memory_tasks.md`. Follow rules R1–R4 and zip-first. The next task is the +> 'unify invalidate procedure' task in HANDOFF.md section 5 — present the PLAN and +> wait for my OK before coding." + +Everything needed (rules, state, pending task, build/test commands, file map) is in +this file. The real work is safe in Git (PR #11, release v0.3.9) and in CHANGELOG.md.