You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
A preview of the 2026 tax forms. The IRS's final 2026 Form 8949 and
Schedule D usually come out in December or January; until then Reports
offers 2026 as "2026 – IRS draft (preview, not for filing)", filled from
the IRS's draft forms, which say "DRAFT — DO NOT FILE" on every page,
with a warning not to file them. On January 1, 2027 the preview stops by
itself, so nobody files a draft from a version they never updated: the
final forms come in an update, which replaces it. Reports now also says,
for a year without forms yet, that they come in an update (#53).
A full test ledger on any server, and 2026's forms from the IRS
drafts, for testing only.scripts/seed_ledger.py loads 103
transactions (2023 to 2026) into a running BitcoinTX over its API: every
year has every kind of transaction, and every report has entries in each
part, every Form 8949 box of 2024 (A–F), 2025 and 2026 (G–L) included, in
any tax timezone.
Every USD value a price lookup would fill is given, so it loads with the
price source Off. It refuses a ledger that has
transactions and the Mac app's address. On a test install, python -m backend.cli install-draft-forms puts the IRS's draft 2026
Form 8949 and Schedule D in the data folder; Reports then offers 2026,
labelled as the IRS draft, and prints them with the IRS's
"DRAFT — DO NOT FILE" on every page. The drafts are checked like a final
form (year, marking, every field the app writes), and are ignored once a
version has that year's final forms. The StartOS test lab's vmtest seed uses both, so
every report can be clicked through on the VM without the owner's real
ledger (#51).
Changed
A professional Complete Tax Report. Redesigned from the ground up,
with the same figures:
a cover with the year's key figures (net capital gain, income, bitcoin
held and its value at year end), how the report was made (FIFO per
account, tax timezone, prices, when it was generated) and contents
with page numbers;
a one-page summary: gains and losses by term, each Form 8949 box with
its Schedule D line and totals (the same as the IRS forms BitcoinTX
fills), income by source, and holdings on January 1 and December 31;
the detail, each table with a totals row and its heading repeated on
every page it runs onto: every disposal (with its kind, Form 8949 box
and term, in the IRS column order: proceeds, cost basis, gain), income,
gifts, donations and lost coins, and the holdings at year end by
account and lot by lot (they listed every lot as a "balance" before,
with the same price note on each row);
losses in parentheses as on the IRS forms, a running header and "Page X
of Y" on every page, and the notes at the end.
Fixed on the way: the number of disposals sat in the Short Term column
though it counted both; two column headings overlapped; and a table that
ran onto another page lost its heading there.
The Transaction History PDF fits the page. Its table was wider than
the page (columns were cut off). It is now landscape, in the same look,
with dates in the tax timezone, each type with its source or purpose
("Withdrawal (Spent)"), amounts and fees with their currency, and page
numbers. Reports now offers it: with Transaction History selected,
Format is CSV spreadsheet (as before) or PDF; before, the PDF could only
be had through the API. The CSV is unchanged.
Fixed
The Complete Tax Report no longer has an "Expenses" section. It
listed withdrawals with the purpose "Expenses", which the app's forms
never offered (a BTC withdrawal is Spent, Gift, Donation or Lost), so it
almost always said "No transactions". Rows that reached it: a BTC
"Expenses" row from an old ledger, and a cash withdrawal sent by API or
CSV with that purpose, which it showed as BTC.
Spending is in the capital gains section, as before, and so is a BTC
"Expenses" row an old ledger still has. No figure changes (#58).
The Mac app no longer carries the test files. It bundled all of backend/, its tests and test ledgers included (harmless, never used);
now it leaves out backend/tests/ and what a dev run leaves in the
build's checkout (a database, its backups, the session key, the setup
code). Docker and StartOS never had the tests; their build now also
ignores those dev files. Release builds start from a clean checkout and
were never affected.
On a phone, the transaction panel can be closed. Up to 460 px wide the
Add or Edit Transaction panel covers the whole page, so there was nothing
outside it to tap and no Escape key: Save was the only button, and the
phone's Back button left the page and dropped what was typed without
asking. A close button (✕) in the panel's header now closes it, on every
screen, like a click outside: at once when nothing was changed, else after
"Discard changes?". Found by the full test run of 1.2.4.
StartOS package
Package 1.2.5:0. Release notes in five languages; 1.2.4:1's
one-time cleanup of the retired Show Credentials task moved to its own
version file, so installs from before it still get it.
Start9's review, taken back (package 1.2.4:1, on Community Beta).
Start9 reviewed the package on their fork
(Start9-Community/BTCTX-StartOS#1) and merged it on 2026-10-05; startos/
now has it as they wrote it. One Set Login Credentials action
replaces Show Credentials and Reset Login Credentials: it makes a new
random password and shows it once, at install (a critical task; install
no longer creates the database) and whenever the owner has lost it, only
while the service is stopped. A login StartOS already made stays; an
older install whose password was set in the app gets the critical task,
and running it replaces that login. The update clears any leftover Show
Credentials task. The package repository is now Start9's fork; their
workflows and template updates came too. Our docs, comments, the agent
release tests and the box test name the new action (#29).
Development
The Start9 check survives squash merges.scripts/start9-pull.sh --apply records the Start9 commit it took in scripts/start9-taken
(committed with the take-back); the release's check then counts only
what Start9 changed since. Before, it looked for the take-back commit in
the history, which GitHub's squash merge of #48 removed: the 1.2.5
release would have stopped at its first step. Set to Start9's 8c030d8,
the review #48 took.
How changes flow, on one page.docs/HOW-CHANGES-FLOW.md maps the
whole path in plain steps: an everyday change, a release, sending it to
Start9, taking Start9's own changes back, and keeping this computer and
GitHub the same. Written for the owner and every AI session; AGENTS.md
points to it.
An issue for each yearly IRS forms step. The weekly IRS forms watch
now runs all year and opens an issue (label claude) when the IRS
publishes the next year's draft forms or revises them (ship them as the
preview) and when it publishes the final forms (add them, in a release).
Before, it only ran November to March and only failed, with an email, for
the final forms (scripts/irs_new_year.py --due).
One command says whether everything is on GitHub.make sync-check
(scripts/sync-check.sh) fetches, brings local main and develop down
when they're only behind, and lists anything that exists only on this
computer: work not committed in any working folder, branches with
commits GitHub doesn't have (a merged pull request counts as on GitHub),
stashes. It ends with IN SYNC or NOT IN SYNC. --tidy also deletes the
local branches already on GitHub. Every session runs it at its start and
end, and ends its last message to the owner with "Git: in sync" or what
is only here (owner's request).
The mirror sync builds on Start9's fork. scripts/sync-startos-mirror.sh first fast-forwards the mirror's main
to Start9's fork (or, if the two have diverged, records it as merged,
keeping startos/), so each pull request to Start9 shows only our
changes, not theirs undone. It runs start9-pull.sh --check first, also
for a docs-only sync by hand, and stops if startos/ lacks theirs.
The check (also the release's first step) now accepts their changes
taken in a commit since our last sync and edited afterwards; before, our
edits to two lines Start9 had rewritten would have stopped the next
release (the release's mirror step now fetches the whole history for
it). It also passes when Start9's branch is already in the mirror's
history: what differs then is ours, waiting for them to merge. And it
now stops, instead of warning, when Start9 changed their fork while a
pull request of ours is open there: theirs can't be told from ours then,
and a sync could record their commits without their content. startos/'s UPDATING.md, TODO.md and AGENTS.md now say that develop takes pull requests and that to-dos
are issues (#29).
What to do when a dependency update breaks something. docs/MAINTENANCE.md now says that an update to the app's or the Mac
app's Python packages or to the GitHub Actions merges only after the Mac
build has passed (it isn't
one of the required checks), that updates which could move a tax figure
get the before-and-after comparison, how a breaking update is fixed or
skipped (an ignore rule, a "Deferred upgrades" row and an issue), and
how a skipped one is taken later.
Comments tidied where the linters don't look. The Dockerfile, Makefile and scripts/backup-db.sh lost their banners and the comments
that only restated the next line; the ones left say why. No change in
behaviour.
Everything we name to build with is pinned exactly. The Docker base
images are pinned by digest and the GitHub Actions by commit (a tag can be
moved later; the publishing step for the AI connector followed a branch),
the Mac app's build tools and anyio by version; Dependabot now also
proposes updates for desktop/ and the base images (the Dockerfile lost
its indentation, which hid its FROM lines from Dependabot). The packages
those pull in themselves aren't locked yet (#37). The AI connector
keeps version ranges, now capped below the next major version of httpx
and pydantic. The rules are in docs/MAINTENANCE.md, "Updating a
dependency".
Every Python package that ships is locked, with its hash. Each shipped
requirements file is now a requirements.in (what we name, pinned) with a
lock beside it, requirements.txt, made by make lock (uv pip compile): every package, the indirect ones too (32 of them were pinned
nowhere and resolved at build time), at a fixed version with its hashes,
for Linux (both chip types), macOS and Python 3.10 and up. The Docker
image installs its lock with --require-hashes and ready-built wheels
only; the Mac app installs the backend's and desktop/'s locks the same
way, and builds its one source-only package with the locked setuptools;
the connector's PyPI build uses a lock of build and setuptools
(.github/release-tools/) and no longer fetches them unpinned. The Mac
build no longer upgrades pip or installs wheel, and CI's container
smoke test installs the lock rather than an unpinned httpx. Dependabot
keeps the locks current (uv entries); the dependency audit now covers
all three locks, on Linux and macOS. test_pinning.py checks that each
lock has every hash and matches its pins, and that whatever ships installs
with --require-hashes. The indirect packages are refreshed once a month
(make lock LOCK_ARGS=--upgrade), when the first Monday's weekly check
reports newer versions (#37).
The pre-push check can't break the repository any more. Run from a
worktree, git hands the hook GIT_DIR; the tests that build throwaway
repositories inherited it, and their git init set core.bare = true in
the shared .git/config, so git refused to work in every checkout until
it was set back. The hook now unsets those variables first (git's advice
for hooks), and a test keeps that line there.
The weekly StartOS check also watches start-cli. Besides a newer @start9labs/start-sdk on npm, startos-sdk-check.yml opens an issue
when Start9 releases a newer start-cli (the tool that packs btctx.s9pk)
than CI's START_CLI_VERSION. Each issue links the release notes and
says to look at Start9's fork first, since Start9 may send the bump
there themselves.
One rules file and one to-do list for every AI. The rules moved
from CLAUDE.md to AGENTS.md, the file other AI tools read too
(CLAUDE.md now only points to it, which a test checks); it now says who
does what (the owner decides, Claude Code develops, Grok brainstorms) and where each kind of information lives. To-dos are GitHub
issues, labelled claude, owner, ask-grok or start9 (the workflows
label theirs), instead of docs/temp/TODO.md: its open items went to #29–#32 (checking that start9-pull.sh --fork finds Start9's fork was
done), the roadmap's two specific to-dos to #38 and #39, and test_todo_lists.py keeps a to-do file from coming back.
Every change arrives by reviewed pull request.develop takes changes
only by pull request (GitHub rulesets now protect main and develop),
from .github/pull_request_template.md. Each one is reviewed by a fresh
Claude agent before it merges, as thoroughly as the change needs. Claude
merges once the required checks pass and nothing is open, except a change
in tax figures or what the app computes, the database, the StartOS
package or anything that reaches Start9, and releases, which wait for the
owner. The pre-push hook now fast-forwards main only to GitHub's develop, and scripts/start9-pull.sh --apply runs on a branch for a
pull request.
Sessions start and end at one pinned issue.#41, "Where we are", says
what's in flight and what's next; each session reads it first and rewrites
it last (AGENTS.md, "Starting a session"). Reviewed pull requests now
merge through GitHub's auto-merge once the required checks pass, and a
branch behind develop is updated by merging develop in, not rebasing.
AGENTS.md says what's at stake and how Start9 works. Two new
sections: the owner's real tax records and ledger, other people's
installs, privacy; and Start9's fork, Community Beta, promotion, and their
own changes coming back.
Downloads
StartOS:btctx.s9pk, package 1.2.5: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.5 (amd64 and arm64).
AI connector:uvx btctx-mcp==1.2.5 (PyPI); Settings → Connect an AI Assistant has it ready to paste.