Files
bal-electrum-plugin/HANDOFF.md
donkey-ai ed83af6be9 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.
2026-06-28 23:02:18 -04:00

222 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<changed files>
```
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 `<br>` (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 `<br>`).
- `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 R1R4 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.