diff --git a/CHANGELOG.md b/CHANGELOG.md index 484c438..fce3a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2329,3 +2329,184 @@ The request was to make the failure message clearer (no timeout change). - `ruff`: no new errors. **Outcome:** DONE (delivered as test ZIP v0.5.18; commit only after confirmation). + +--- + +## 43. v0.6.0 - Version bump for the official repository release + +**Date:** 2026-07-17 + +**Context:** the plugin content of this version is IDENTICAL to v0.5.18 - no code +changes. A release was published on the official repository +(bitcoinafterlife/bal-electrum-plugin) tagged "v0.6.0", but the internal version +files still read "0.5.18" (a human oversight: the release tag was not matched by +a version bump in the code), so Electrum displayed "0.5.18" after installing it. +This entry aligns the internal version with the intended "0.6.0" release tag by +bumping `bal/VERSION`, `bal/manifest.json`, `bal/__init__.py` and +`bal/core/plugin_base.py` from 0.5.18 to 0.6.0. No functional changes. + +**Verification:** full test suite against Electrum 4.7.2 and 4.8.0 (same 266 +passed / 2 pre-existing unrelated failures as v0.5.18); `ruff` clean. + +**Outcome:** DONE. + +--- + +## 44. Repo: inheritance-logic test suite + first BAL CLI (no plugin version change) + +**Date:** 2026-07-20 + +**Scope note:** this entry adds REPOSITORY files only (`tests/`, `bal_cli.py`). +The plugin package `bal/` is byte-for-byte unchanged, so the plugin version +stays **0.6.0** on purpose (no zip rebuild, no version bump - avoiding any new +tag/code mismatch). + +**1) New logic test suite - `tests/test_inheritance_scenarios.py` (23 tests):** +- Multi-heir distribution via `Heirs.prepare_lists`: 50/50 percent split of + (balance - fees); percentages normalized to their SUM (30+30 behaves like + 50/50); fixed heirs paid first with percents sharing the remainder; fixed + amounts exceeding the balance scaled down proportionally (`onlyfixed`). +- Dust rules: below-dust heirs marked `DUST:` while valid heirs keep building; + the leftover redistribution can LIFT dust fixed heirs above the threshold + (pinned: 100:200 on 1M -> 333333/666666); all-dust wills refused + (`HeirAmountIsDustException`); `BalanceTooLowException` when balance < fees. +- Will-executor fees: one pseudo-heir per distinct locktime; fees larger than + the balance raise `WillExecutorFeeException`. +- Expired-heir exclusion vs `from_locktime`; grouping of heirs by locktime. +- Heir add/remove/locktime-change all flag the will as changed + (`Will._same_heirs`); the `Heirs` mapping persists on add/remove. +- Will expiry (`Will.check_will_expired`): past locktime raises + `WillExpiredException`, future/boundary (== now) does not, non-VALID items + are ignored. +- Locktime parsing: int passthrough, `d` -> future midnight, `1y` == `365d`. + +**2) First BAL CLI - `bal_cli.py` (offline iteration):** +- Reuses `bal.core` directly with Electrum as a library (no Qt, headless). +- Commands: `heirs list/add/remove/export/import`, `status`. +- Safety: **testnet by default**, mainnet only with an explicit `--mainnet` + flag (active network always printed); encrypted wallets via `--password` or + `BAL_WALLET_PASSWORD` env var; address/locktime validation (rejects wrong + network and past locktimes); guaranteed process termination (electrum leaves + non-daemon threads even offline - the CLI stops the wallet and event loop, + then hard-exits with the proper exit code, so machine callers never hang). +- Planned next iteration: `will build/sign/push/check` with an explicit + `--yes` confirmation flag for automation. +- End-to-end smoke test `tests/test_cli_smoke.py`: creates a REAL testnet + wallet via electrum-as-library in a subprocess, then drives + add/list/reject-invalid-address/reject-past-locktime/export/remove/status. + +**Verification:** +- Full suite against **Electrum 4.7.2**: `290 passed`; against **4.8.0**: + `290 passed` (same 2 pre-existing, unrelated `baltx_fees` failures in both). +- `ruff`: no new errors on the three new files. + +**Outcome:** DONE. + +--- + +## 45. Repo: CLI will commands (build / sign / push / check) - no plugin version change + +**Date:** 2026-07-21 + +**Goal:** complete the headless workflow started in entry #44, so a machine can +run the whole inheritance cycle without the Qt GUI. + +**What was added (`bal_cli.py`):** +- `will build` - builds the will transactions offline through the same + `bal.core` pipeline the GUI uses (`Heirs.get_transactions` -> `WillItem` -> + `Will.update_will` / `normalize_will`), storing them as "New". +- `will sign` - signs the valid, incomplete transactions with the wallet + password (`--password` / `BAL_WALLET_PASSWORD`). Prints a summary and asks + for confirmation; `--yes` skips it for automation. Chained will inputs + (spending a previous will's change) are patched exactly as the GUI does. + Watching-only wallets and hardware keystores are rejected with a clear + message (hardware devices need physical confirmation). +- `will push` - sends signed transactions to the selected will-executors via + `Willexecutors.push_transactions_parallel`; `--yes` and `--force` supported; + updates PUSHED / PUSH_FAIL statuses. +- `will check` - read-only report with **exit codes for scripts**: 0 valid, + complete and pushed; 2 no will stored; 3 EXPIRED; 4 not fully signed; + 5 signed but not pushed; 6 heirs changed since the will was built. +- `_CliBalPlugin`: minimal stand-in exposing only what `bal.core` needs + headless (`WILLEXECUTORS`, `NO_WILLEXECUTOR`, `get_decimal_point`). + +**Three real bugs found and fixed while implementing this:** +1. `Will.only_valid()` returns a GENERATOR - `len()` on it raised + `TypeError`; now wrapped in `list()`. +2. `copy.deepcopy(tx)` fails on Electrum 4.8 (`cannot pickle '_thread.RLock'`); + signing now re-parses the transaction from its serialization instead. +3. **Silent will loss on save**: values loaded from the wallet DB are + `StoredDict`s carrying the DB lock, and `JsonDB.put` deep-copies its value + and returns False *silently* when that fails - the will appeared saved but + was never written. Persistence now normalizes through a + `json.dumps`/`loads` round-trip (which also validates serializability) and + exits with an explicit error if anything is non-serializable. + +Also added: pre-build validation of the selected executor addresses for the +active network (refreshed from the server when possible), and a guard that +catches exception sentinels embedded by `heirs.py` in a heir entry when an +output cannot be built. + +**Verification:** +- New `test_cli_will_cycle` in `tests/test_cli_smoke.py`: creates a REAL + testnet wallet, funds it offline with a handmade transaction, then drives + `build -> check(4) -> sign --yes -> check(5) -> push (aborted at the + confirmation prompt) -> status`. No network access. +- Full suite: **291 passed** against **Electrum 4.7.2** and **4.8.0** (same 2 + pre-existing, unrelated `baltx_fees` failures in both). +- `ruff`: all checks passed. + +**Note:** repository-only change (CLI + tests). The plugin version is +unchanged; the Qt plugin code is untouched. + +**Outcome:** DONE. + +--- + +## 46. v0.6.1 - Version read from manifest.json (single source of truth) - PR #4 + +**Date:** 2026-07-22 + +**Goal (Truman):** stop keeping the version in four places (`bal/VERSION`, +`bal/manifest.json`, `bal/__init__.py`, `bal/core/plugin_base.py`) synced by a +pre-commit hook. The version must be read at runtime from `manifest.json` only. + +**What changed:** +- `bal/core/plugin_base.py`: new module function `get_version()` reads the + `"version"` field from `bal/manifest.json` using `importlib.resources` + (zip-safe: works both extracted and from inside a zip, which is how Electrum + loads external plugins via `zipimport`; no hand-built paths, so no Windows + backslash-in-zip issue). The value is cached. `BalPlugin` now exposes a + `version` **property** returning `get_version()`; the hardcoded + `__version__` class attribute and the old `version()` method that read the + `VERSION` file are removed. +- The three call sites that printed the version now use the property + (`self.bal_window.bal_plugin.version` in `bal/gui/qt/widgets.py` and + `bal/gui/qt/dialogs.py`) or `get_version()` where no plugin instance is + available (`bal/core/willexecutors.py`, the HTTP user-agent, a static + method). +- **Removed the `bal/VERSION` file** (the plugin package now ships 36 files + instead of 37). +- `HANDOFF.md`: updated the four references that told maintainers to edit + `bal/VERSION` / keep four files in sync - now they point to + `manifest.json` as the single source of truth. (Historical version + references in `CHANGELOG.md` and `.agent_memory_tasks.md` are left intact on + purpose - they describe past events.) + +**Verification:** +- New `tests/test_version_source.py` (7 tests): version equals the manifest + field; the `version` property matches `get_version()`; no hardcoded + `__version__` remains; no `bal/VERSION` file remains; the value is cached; + and - the key one - the version is read correctly when `bal` is imported + FROM INSIDE A ZIP in a child interpreter (the real Electrum/Windows + scenario). +- Full suite: **298 passed** against **Electrum 4.7.2** and **4.8.0** (same 2 + pre-existing, unrelated `baltx_fees` failures in both). +- `ruff`: clean. +- Also verified the built distribution zip reports the manifest version via + `get_version()` (bumped to 0.6.1 in this change). + +**Note:** repository change targeting PR #4 (branch +`bitcoinafterlife-patch-5`). No functional change to inheritance behavior. + +**Outcome:** DONE. diff --git a/HANDOFF.md b/HANDOFF.md index 916fc56..397e0f1 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -19,7 +19,7 @@ services) can be paid a fee to broadcast the inheritance when due. The owner periodically proves they are alive ("check-alive"); if the deadline passes, the inheritance becomes spendable. -- **Current version:** see `bal/VERSION` (last shipped: **0.4.8**). +- **Current version:** see the `"version"` field of `bal/manifest.json` (the single source of truth; read at runtime via `get_version()` in `bal/core/plugin_base.py`). --- @@ -55,11 +55,10 @@ These are non-negotiable. They come from the owner directly. ``` bal/ <- the plugin package (this is what ships in the ZIP) - __init__.py <- __version__ (one of 4 version files) - VERSION <- plain-text version (one of 4 version files) - manifest.json <- plugin manifest, "version" field (one of 4) + __init__.py <- package docstring (no version here anymore) + manifest.json <- plugin manifest, "version" field (SINGLE SOURCE OF TRUTH for the version) core/ - plugin_base.py <- __version__ "AUTOMATICALLY GENERATED" (one of 4) + plugin_base.py <- get_version() reads the version from manifest.json (zip-safe) heirs.py <- HEIRS + transaction building (prepare_lists, prepare_transactions, buildTransactions). CORE LOGIC. will.py <- Will/WillItem, validation (check_amounts, check_will), @@ -115,13 +114,11 @@ find bal -name "__pycache__" -type d -exec rm -rf {} + ; find bal -name "*.pyc" python3 build_zip.py bal-electrum-plugin-vX.Y.Z.zip # produces 37 files ``` -**Bump version — there are FOUR files, keep them in sync:** +**Bump version — ONE file only (single source of truth):** ``` -bal/core/plugin_base.py -> __version__ = "X.Y.Z" # AUTOMATICALLY GENERATED DO NOT EDIT -bal/__init__.py -> __version__ = "X.Y.Z" -bal/VERSION -> X.Y.Z bal/manifest.json -> "version": "X.Y.Z", ``` +The code reads this at runtime via `get_version()` in `bal/core/plugin_base.py` (exposed as the `BalPlugin.version` property), so there is nothing else to keep in sync. There is no longer a `bal/VERSION` file nor a hardcoded `__version__`. **IMPORTANT for the owner when testing:** after installing a ZIP, the owner must **fully restart Electrum** (not just reload the plugin) — Electrum's @@ -292,7 +289,7 @@ must **fully restart Electrum** (not just reload the plugin) — Electrum's ## 7. How to resume (checklist for the next AI) 1. Read this file, then `CHANGELOG.md` (last entries) and `.agent_memory_tasks.md`. -2. Confirm the environment: `git status`, current branch, `bal/VERSION`. +2. Confirm the environment: `git status`, current branch, and the `"version"` field of `bal/manifest.json`. 3. Run the full test suite (Section 3) — expect all green (266 as of v0.4.8). 4. Talk to the owner in **Italian**, write everything else in **English**. 5. For any change: present a PLAN, wait for "OK" (R4), then implement, test,