The PC push aborted before it ever tried Firebase whenever the GitHub PAT
was missing ("Workouts NOT synced: no sync token"), so once the PAT was lost
this machine published nothing although Firebase was configured and live.
The phone has not written the GitHub repo since 2026-08-21.
- sync_client() / sync_client_or_reason(): firebase_client_for("screen_locker")
only, proven with can_access_remote(), healed once from a sibling app's
session, else a warning naming what will not sync (push AND pull) plus a
recorded degraded source. No config -> warning, no fallback.
- _manual_push and scripts/tombstone_sync_record.py take their client from
sync_client_or_reason(); the push result carries the reason.
- Deleted: read_sync_token, remote_client, the mirror client, _push_outcome
(the half-landed-push case no longer exists), SYNC_TOKEN_FILE and
SYNC_REPO_OWNER/NAME, the sync_token test fixture. Status backend is
"firebase" or "none".
- crdt-sync pinned to v0.9.2 in pyproject, requirements.txt and
install_systemd.sh (the installer still said v0.9.0).
kuhyx/syncs/screen-locker-sync is left untouched as an archive; no code
reads it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RHwVPyiiQMPh4M3JQwQku
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint
were still grading against 3.10 -- three tools judging an interpreter nothing
runs any more, and ruff withholding the pyupgrade fixes that only become safe
at the newer target. `pin_ci_python.py` rewrites exactly these three knobs.
What the stale target was hiding: 118 sites on `datetime.timezone.utc` instead
of `datetime.UTC` (UP017), two classes still written with `Generic[...]` rather
than PEP 695 syntax, and ruff's stable formatter applying PEP 758 --
`except (A, B):` becomes `except A, B:`. That last one makes the source
3.14-only at the syntax level, which is what `requires-python = ">=3.14"`
already promised; kuhy chose it deliberately over holding ruff a release back
(2026-08-29).
The PEP 695 fix needed a hand: ruff rewrote `class WidgetGroup(Generic[_W])`
to `class WidgetGroup[W: tk.Misc]` but left the body annotated with the
module-level `_W`, a *different* type variable -- mypy caught it as "a function
returning TypeVar should receive at least one argument containing the same
TypeVar". Renamed the body to the new parameter, gave `FrameGroup.__init__`
the concrete `list[tk.Frame]` it always wrapped, and deleted the two now-dead
module-level TypeVars.
ruff, ruff-format, mypy --strict and both pylint passes clean at the new target.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
The status window is the PC's surface for workout data, but it said nothing
about the sync layer that data now arrives through -- so a machine that had
silently fallen back to the GitHub mirror, or was configured but had never
completed a push, looked identical to a healthy one. That is exactly how
todo's desktop app sat on GitHub unnoticed while every other surface moved to
Firebase.
Adds a "Sync backend" section and a --sync CLI flag (for i3blocks, alongside
--summary/--state), both rendering one line: which backend this machine is
configured for, when it last pushed, and how many peers it has merged.
Deliberately on-disk only -- no RTDB read, no Firebase sign-in -- because
gather_status() runs on every i3blocks tick and every window open; a network
round-trip there would be rate-limited nonsense. Everything shown is
knowable from the revision cache plus which credential files exist.
Coloured by `healthy` (configured AND has pushed) rather than by backend,
because "configured but never pushed" is the failure that looks fine in every
settings screen and produces no data.
Named _section_sync_backend, not _section_sync: another session is adding a
_section_sync that reports the outcome of a background sync *tick*. The two
are complementary -- did the tick run, versus what is this machine wired to
-- so they coexist rather than one overwriting the other.
Verified by running it, not just the suite: the real window shows
"firebase · pushed 2026-08-09 21:06 · 2 peer(s)" against this machine's live
state, and `--sync` prints the same line.
924 tests, 100% branch coverage. Every except logs at warning per the repo's
no-silent-failures hook; no suppressions added.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177xwzcpfpWG2qca8Mr39q9