Renames this repo's markdown onto README.md / CLAUDE*.md / DOCS*.md / TODO*.md so a future session can tell a live task from a finished record without reading the file to find out. TODO files carry the literal "REMOVE ME AFTER FINISH" and are deleted when the work lands. Enforced by the md-naming pre-commit hook and CI workflow, both delegating to the shared gate in kuhyx/utils so this repo's idea of a valid name cannot drift from every other repo's. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
8.7 KiB
Workstream D — Shared CRDT transport library across 4 repos
Status
Scoping pass complete — see workstream-d-scoping-findings.md (2026-07-04)
for the answers to the four questions below and the two sub-decisions that
fell out of it (both resolved by explicit user call, 2026-07-04).
The two shared libraries are now implemented and locally verified (2026-07-05):
~/crdt-sync(Python) — 60 tests, 100% branch coverage, clean ruff/mypy/pylint/bandit/pre-commit. Local git repo, one commit.~/crdt_sync_dart(pure Dart, no Flutter dep) — 59 tests, cleandart analyze. Local git repo, one commit.
Both implement the unified LWW-map-with-sticky-remove scheme (Hlc,
Record/merge_record, Log/merge_logs) plus a GitHub Contents-API
transport client and a domain-agnostic sync_log/syncLog orchestrator.
Both were originally pushed as standalone public repos (2026-07-05, matching
gatelock's convention), then consolidated (2026-07-10) into the shared
github.com/kuhyx/utils monorepo alongside gatelock and guard-lib, each
as its own subdirectory. Consume via
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.1.0#subdirectory=crdt-sync
(Python) or a pubspec git:/path: dependency pointing at
crdt_sync_dart-v0.1.0/crdt_sync_dart (Dart).
Not yet done — none of the four apps have been migrated onto it yet:
todo (off sqlite_crdt), diet-guard (Python + Dart merge code), wake-alarm
(shutdown-wrapper.sh rewrite), and screen-locker's own Workstream C
adoption. Each is its own separate, production-code-touching task per the
scoping doc's per-app cost estimates.
Split out from the original status-view plan
(~/.claude/plans/screen-locker-status-streamed-feigenbaum.md, Workstream A).
The decision this workstream implements
Per explicit prior user decision: build a shared Python CRDT transport
library (used by screen-locker, diet-guard, wake-alarm PC-sides) and a
shared Dart CRDT transport library (used by todo, diet-guard app,
wake-alarm phone_app, screen-locker's workout_app), with all four
apps adopting CRDT-based merge — not just todo's existing use of it. This
was flagged during the original planning conversation as more complexity
than diet-guard/wake-alarm/screen-locker's simpler log data strictly needs,
and the user chose it anyway as a deliberate future-proofing call. That
tradeoff decision stands; this document doesn't re-litigate it, only
records what's actually true about the current state so a future scoping
pass starts from facts, not assumptions.
What's actually true today (verified by reading the code, not assumed)
todo's "CRDT approach" is the sqlite_crdt package, not hand-rolled
logic: todo/lib/sync/sync_service.dart imports
package:sqlite_crdt/sqlite_crdt.dart and syncs by "pulling every other
device's full CRDT changeset" (per that file's own doc comment) — merge
conflict resolution is handled entirely inside that library, not in
app code. This matters: Workstream D's Dart-side library is not "extract
what todo already built," it's "wrap/standardize how todo already uses a
third-party package," which is a smaller and clearer task than a
from-scratch CRDT implementation.
diet-guard and wake-alarm do NOT use CRDTs today. Their existing "merge" mechanisms are much simpler:
- diet-guard:
_sync_merge.merge_logs(~/diet-guard/diet_guard/_sync_merge.py, 81 lines) is a tombstone-aware, entry-key-based merge — closer to last-write-wins per entry than a general CRDT. Transport is_sync_github.GitHubSyncClientagainst a private repo's Contents API (see Workstream C's doc for the concrete interface). - wake-alarm:
shutdown-wrapper.shreads a private GitHub Gist directly in bash — no merge logic at all, because it's a single current value (next wake time), not a growing log. This is the piece the original plan flags as needing a rewrite ("wake-alarm's current bash-based PC-side gist reader... into something that can participate in a CRDT merge") — but note its data model (one current value, no history) may not even need CRDT semantics; forcing it into the same abstraction as diet-guard's growing log is itself a design choice to confirm, not a given.
No existing Python CRDT-backed storage layer exists in any of these
repos. Unlike the Dart side (where sqlite_crdt is a mature, adopted
package), the Python PC-side transport library has no equivalent starting
point. This is the biggest unknown in scoping this workstream: does a
suitable Python CRDT library exist and fit sqlite/JSON-log use cases, or
does "shared Python CRDT transport library" mean writing CRDT merge logic
from scratch in Python? That question needs an answer before any
time/complexity estimate for this workstream means anything.
Why this is a separate, large task (not a natural extension of A/B/C)
- Spans 4 repositories (todo, diet-guard, wake-alarm, screen-locker) and 2 languages (Dart, Python).
- Touches production sync code in apps that are already shipping and
working (diet-guard's Contents-API sync, wake-alarm's Gist reader) —
regressions here have real user-facing consequences (diet-guard's own
memory notes record a prior 3-day silent outage from a similar
deployment-path mismatch — see
feedback-verify-real-deployment-path.mdin this user's memory). - Requires picking one canonical transport primitive (Gist vs. Contents-API repo, per Workstream C's open question) that all four apps then share — a decision this document deliberately does not make, since it depends on Workstream C's resolution.
- The Python-side CRDT gap (previous section) may turn "build a shared library" into "evaluate/adopt a third-party Python CRDT library" or "design a minimal CRDT scheme from scratch" — materially different scopes that need to be distinguished before estimating.
Scoping pass — what actually needs to happen before coding starts
This is not an implementation plan. Before any code is written for this workstream, produce (as a separate, dedicated planning task):
- Python CRDT landscape check: does a maintained Python package exist that plays well with SQLite or flat JSON logs (the data shapes diet-guard/wake-alarm/screen-locker actually use today)? If none fits, decide: adopt something not-quite-right, or hand-roll a minimal CRDT (e.g. a simple LWW-register or OR-set scheme, since the data here is genuinely simple — daily log entries, not concurrent rich-text editing).
- Transport primitive decision: Gist vs. Contents-API repo (see Workstream C) — must be settled first since the shared library wraps whichever one is chosen, for all 4 apps, not per-app.
- Per-app migration cost, assessed individually:
- todo: likely smallest — already on
sqlite_crdt, mainly a question of whether it also needs to move onto the shared library's transport wrapper, or keeps its current transport untouched. - diet-guard: rewrite
_sync_merge.py's tombstone-LWW merge onto a real CRDT scheme, both Python (_sync.py) and Dart (app/lib/services/sync_merge.dart) sides. - wake-alarm: full rewrite of
shutdown-wrapper.sh's bash Gist reader into something CRDT-capable — the single biggest unknown, since it's currently the simplest and most different data model of the four (single value, no log, no merge today at all). - screen-locker: net-new — no existing sync of any kind yet (Workstream C is what introduces sync here at all; if C is built with "local, throwaway sync code" per its own doc, D's arrival means replacing that code, not extending it).
- todo: likely smallest — already on
- Sequencing relative to Workstream C: confirm whether C should be (a) built on local throwaway sync code now and migrated onto D's library later, or (b) blocked until D's library exists. This is Workstream C's open question too — resolve it once, here or there, not independently in both places.
Explicitly out of scope for the scoping pass itself
Do not write any CRDT library code, do not touch any of the 4 repos' existing sync mechanisms, and do not commit to the Python-CRDT-library question's answer without actually researching current (as of when this task is picked up) Python package options — the answer may be stale by the time this is scoped for real.
Critical files (reference only — read, do not modify, during scoping)
~/todo/lib/sync/sync_service.dart,sync_settings.dart(CRDT-via-package pattern)~/diet-guard/diet_guard/_sync.py,_sync_github.py,_sync_merge.py~/diet-guard/app/lib/services/sync_service.dart,sync_merge.dart~/wake-alarm/shutdown-wrapper.shdocs/todo/workstream-c-github-sync-workout-data.md(this repo — resolve its open questions jointly with this one, not independently)