Upload files to "/"
Update HANDOFF and CHANGELOG for version-from-manifest
This commit is contained in:
181
CHANGELOG.md
181
CHANGELOG.md
@@ -2329,3 +2329,184 @@ The request was to make the failure message clearer (no timeout change).
|
|||||||
- `ruff`: no new errors.
|
- `ruff`: no new errors.
|
||||||
|
|
||||||
**Outcome:** DONE (delivered as test ZIP v0.5.18; commit only after confirmation).
|
**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, `<n>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.
|
||||||
|
|||||||
17
HANDOFF.md
17
HANDOFF.md
@@ -19,7 +19,7 @@
|
|||||||
services) can be paid a fee to broadcast the inheritance when due. The owner
|
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,
|
periodically proves they are alive ("check-alive"); if the deadline passes,
|
||||||
the inheritance becomes spendable.
|
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)
|
bal/ <- the plugin package (this is what ships in the ZIP)
|
||||||
__init__.py <- __version__ (one of 4 version files)
|
__init__.py <- package docstring (no version here anymore)
|
||||||
VERSION <- plain-text version (one of 4 version files)
|
manifest.json <- plugin manifest, "version" field (SINGLE SOURCE OF TRUTH for the version)
|
||||||
manifest.json <- plugin manifest, "version" field (one of 4)
|
|
||||||
core/
|
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,
|
heirs.py <- HEIRS + transaction building (prepare_lists,
|
||||||
prepare_transactions, buildTransactions). CORE LOGIC.
|
prepare_transactions, buildTransactions). CORE LOGIC.
|
||||||
will.py <- Will/WillItem, validation (check_amounts, check_will),
|
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
|
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",
|
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
|
**IMPORTANT for the owner when testing:** after installing a ZIP, the owner
|
||||||
must **fully restart Electrum** (not just reload the plugin) — Electrum's
|
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)
|
## 7. How to resume (checklist for the next AI)
|
||||||
|
|
||||||
1. Read this file, then `CHANGELOG.md` (last entries) and `.agent_memory_tasks.md`.
|
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).
|
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**.
|
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,
|
5. For any change: present a PLAN, wait for "OK" (R4), then implement, test,
|
||||||
|
|||||||
Reference in New Issue
Block a user