screen-locker/docs/todo/DOCS-workstream-d-shared-crdt-transport.md
Krzysztof kuhy Rudnicki a889341979 Adopt the four markdown namespaces and gate them
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
2026-08-24 15:53:42 +02:00

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, clean dart 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.GitHubSyncClient against a private repo's Contents API (see Workstream C's doc for the concrete interface).
  • wake-alarm: shutdown-wrapper.sh reads 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.md in 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):

  1. 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).
  2. 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.
  3. 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).
  4. 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.sh
  • docs/todo/workstream-c-github-sync-workout-data.md (this repo — resolve its open questions jointly with this one, not independently)