Skip to content

v1.2.3 · StartOS package 1.2.3:0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 02:10

A maintenance release with no new features. The code behind the app was
reorganized (long functions divided into named steps, the web app's big
components split, filler comments and unused code removed) with one rule:
nothing it produces may change. Every figure, database row, Form 8949 field,
report PDF (byte for byte), CSV, import preview, API answer, AI tool output
and error message was compared with 1.2.2-1's on the golden ledger, the
seed ledger, 40 random ledgers in three timezones and about 2,200 bad
inputs; every page and form was recorded before and after. The only
differences are the two fixes below. Recalculation is about 7% faster.

Fixed

  • A CSV row with an unknown fee_usd_typed value (anything but yes, no
    or blank) crashed the import preview with a server error; it is now a row
    error, "Must be yes, no or blank." Found by the new equivalence check's
    bad inputs.
  • Ledger Review's fee check no longer hides errors. Looking for
    transfers whose fee value is off the day's price, it passed over a
    transfer on any error while getting that day's price, so a broken
    database or a bug looked like "nothing to fix". It now passes over only
    a day with no price; anything else is reported as an error.

Development

  • Code cleanup, no change in behaviour:
    the standards are written down in docs/CODE_STYLE.md, and CLAUDE.md
    points to them. scripts/equivalence_check.py compares everything the
    app produces (database rows, API answers, Form 8949 field values, report
    PDFs byte for byte, CSV files, import previews, MCP tool outputs, the
    error message of each of about 2,200 bad inputs) with v1.2.2-1's, and
    times recalculation (docs/TESTING.md).
  • Every Transaction column is in the CSV export or on a list saying why
    not
    (test_csv_export_columns.py), so a new column can't be left out of
    the export unnoticed, as three were before 1.2.1.
  • Comments and docstrings say what each module is for and why the code
    does what it does: no file-path headers, banner dividers, history notes
    or narration. The Transaction model's column descriptions no longer call
    its main fields "legacy". Two pieces of dead code went: main.py's empty
    __main__ block and an unused services.user.delete_user.
  • Modern type hints (list[str], dict, X | None) in the app code,
    and ruff's UP rules keep it that way (tests and migrations excepted).
  • The complete tax report is built one section at a time
    (complete_tax_report.py, from one 510-line function), with the paragraph
    styles and grid tables it shares with the transaction history in
    reports/pdf_layout.py. Its PDFs are byte-identical.
  • The transaction history and the Form 8949 field mapping are divided
    the same way: the history's columns and widths are one table, its PDF
    uses the shared layout; a Form 8949 page's checkbox, row fields and line 2
    totals are named steps. Same PDFs, CSVs and field values.
  • The CSV import guide's script keeps its tables as data and builds
    one section per method (backend/scripts/generate_csv_instructions_pdf.py,
    from one 395-line function); the PDF it writes is byte-identical, and the
    committed one is unchanged.
  • The CSV import checks a row one field at a time (csv_import.py:
    _validate_row was 276 lines), with the account rules per type as a
    table and each type's own rules in their own function; the template's
    sample rows are data. Two branches that could never run are gone.
    Checked on 40,728 generated rows: the same errors, warnings, previews and
    transactions.
  • The River import reads each kind of River row in its own step, finds
    duplicates in an exact and a rough pass, and the import endpoint checks,
    stubs and saves rows in named steps. Same proposals, warnings, matches and
    imports (3,000 random files and 3,000 random ledgers compared).
  • The dashboard's gains (get_gains_and_losses, 179 lines) add up
    realized gains, proceeds, income by source, fees and this year's gain in
    one step each. Same figures on every ledger compared, including ones with
    sales this year.
  • Ledger Review, the CSV export, the AI entry dry run, the public price
    download, the IRS form filler, the adoption of a pre-migration database
    and the AI connector's request and update_transaction
    are divided
    into named steps too; the connector's tools, their schemas and outputs
    are unchanged.
  • The ledger engine (transaction.py) reads as its steps: create and
    edit (check, complete, insert or apply, recalculate), the ledger lines per
    kind of transaction, FIFO disposal and transfer lot moves, the input checks
    in their fixed order and each type's account rules as a table. Two unused
    functions are gone. Same lines, lots, disposals, figures and messages on
    every ledger and bad input compared; recalculation no slower.
  • The transaction form (TransactionForm.tsx, 1,049 lines) keeps the
    form's state, saving and deleting; each transaction type's fields, the
    shared account and fee fields, the autofill and the save request are their
    own files in components/transactionForm/. Every form variant renders the
    same labels, roles and HTML (e2e/ui-snapshot.e2e.ts).
  • The Settings page (799 lines) is its sections: Account, Data
    Management (with the CSV import's preview) and Backup & Restore are their
    own components, next to the ones it already had; the page keeps the one
    action at a time and the message line they share. Same page, labels and
    messages.
  • The River import panel (486 lines) keeps the upload and the import;
    the preview, one row and its editable cells are their own components, and
    the row logic (the accounts a new type implies, the import request) is in
    utils/riverImport.ts with a unit test. The two import previews share
    one date format and one "server's reason, else…" message helper. Same
    preview after every edit a row allows (recorded before and after).
  • The Dashboard (470 lines) fetches its figures and lays out four
    cards, each its own component; the account totals and the gain/loss
    colour are in utils/dashboard.ts with a unit test. Same cards, figures
    and HTML.
  • The rest of the frontend over the limit: the transaction form's
    mapping to and from the API is one small step per transaction type (the
    same results on 40,000 random forms and transactions, compared), the
    Transactions page's sorting, paging and day groups are in
    utils/transactionList.ts, Privacy & network's "anything changed?" is
    one tested function, and the error message for a failed request is read
    in two steps (the same message for 20,000 random failures).
  • Frontend comments and unused code, as in the backend: no file-path
    headers, banner dividers, history notes ("NEW", "ADDED", "legacy") or
    comments that repeat the code. Gone because nothing used them: three hook
    files and four API hooks (src/hooks/ keeps the error message),
    formatTimestamp, two types, and five dashboard fields the server has
    never sent. The built app is byte-identical but for those five fields.
  • Lint limits each function's size, so the long functions can't grow
    back: Python (ruff: complexity 10, 12 branches, 50 statements, 6 returns;
    tests and migrations exempt) and the frontend (ESLint: complexity 15 per
    function, 400 lines per file; tests exempt).
  • The StartOS mirror stays current between releases: a docs-only change
    to startos/ goes to DigiMonk73/BTCTX-StartOS right away (CLAUDE.md),
    since Start9's build there ignores those files. Anything else waits for a
    release.
  • Roadmap: the AI connector will follow the app's version, chosen over
    uvx btctx-mcp@latest.

Downloads

  • StartOS: btctx.s9pk, package 1.2.3:0. In StartOS, open Sideload and upload it; installs of earlier versions update in place.
  • macOS: BitcoinTX-macOS.dmg (or the .zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway.
  • Docker: ghcr.io/digimonk73/btctx-mcp:v1.2.3 (amd64 and arm64).
  • AI connector: uvx btctx-mcp==1.2.3 (PyPI); Settings → Connect an AI Assistant has it ready to paste.