Re-point paths for the ~ reorganisation (home-tidy)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
This commit is contained in:
Krzysztof Rudnicki 2026-09-11 18:46:28 +02:00
parent 0fb2da9c86
commit e98884b200
24 changed files with 77 additions and 77 deletions

View File

@ -277,7 +277,7 @@ repos:
# Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the # Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the
# rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may. # rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may.
# Shared gate in ~/utils owns the namespaces and the exemption list, so # Shared gate in ~/src/utils owns the namespaces and the exemption list, so
# one repo's idea of a valid name cannot drift from every other repo's. # one repo's idea of a valid name cannot drift from every other repo's.
- id: md-naming - id: md-naming
name: markdown naming convention name: markdown naming convention

View File

@ -71,7 +71,7 @@ an exempt extension, no `# noqa`-style suppressions.
A refactor without a gate silently regrows. Before this task is done: A refactor without a gate silently regrows. Before this task is done:
1. Wire the shared gate `~/utils/scripts/check_file_length.sh` into this repo's 1. Wire the shared gate `~/src/utils/scripts/check_file_length.sh` into this repo's
`.pre-commit-config.yaml` as a local hook. If the repo has no pre-commit `.pre-commit-config.yaml` as a local hook. If the repo has no pre-commit
config, add a minimal one. config, add a minimal one.
2. The hook checks **files in the commit** (not the whole tree), so unrelated 2. The hook checks **files in the commit** (not the whole tree), so unrelated
@ -82,7 +82,7 @@ A refactor without a gate silently regrows. Before this task is done:
## Done condition ## Done condition
- `bash ~/utils/scripts/check_file_length.sh --all` from this repo root exits 0. - `bash ~/src/utils/scripts/check_file_length.sh --all` from this repo root exits 0.
- The repo's own test suite and coverage bar are still green. - The repo's own test suite and coverage bar are still green.
- `pre-commit run --files <changed files>` passes. - `pre-commit run --files <changed files>` passes.
- A deliberately over-250-line test file, staged, makes `git commit` **fail**. - A deliberately over-250-line test file, staged, makes `git commit` **fail**.

View File

@ -7,7 +7,7 @@
## Goal ## Goal
`bash ~/utils/scripts/check_file_length.sh --all` from the repo root exits 0. `bash ~/src/utils/scripts/check_file_length.sh --all` from the repo root exits 0.
Everything else in `refactor_claude_todo.md` still applies (no gaming the cap, Everything else in `refactor_claude_todo.md` still applies (no gaming the cap,
no deleting tests, no suppressions). This file only adds what the previous no deleting tests, no suppressions). This file only adds what the previous
@ -16,7 +16,7 @@ session learned the hard way.
## Start here (do this first, it costs one command) ## Start here (do this first, it costs one command)
```bash ```bash
cd ~/screen-locker/stronglift_replacement/workout_app cd ~/src/screen-locker/stronglift_replacement/workout_app
env -u GIT_DIR flutter test 2>&1 | tail -1 # MUST print: +402: All tests passed! env -u GIT_DIR flutter test 2>&1 | tail -1 # MUST print: +402: All tests passed!
``` ```
@ -145,10 +145,10 @@ extension in a part still reaches `_privateField`.
## Verify (all of it, every batch) ## Verify (all of it, every batch)
```bash ```bash
cd ~/screen-locker/stronglift_replacement/workout_app cd ~/src/screen-locker/stronglift_replacement/workout_app
env -u GIT_DIR dart analyze lib/ # must be "No issues found!" env -u GIT_DIR dart analyze lib/ # must be "No issues found!"
env -u GIT_DIR flutter test 2>&1 | tail -1 # must be +402 env -u GIT_DIR flutter test 2>&1 | tail -1 # must be +402
cd ~/screen-locker && pre-commit run # includes the 100% Flutter coverage gate cd ~/src/screen-locker && pre-commit run # includes the 100% Flutter coverage gate
``` ```
`dart analyze` is a **no-op on test files** — `analysis_options.yaml` excludes `dart analyze` is a **no-op on test files** — `analysis_options.yaml` excludes
@ -157,7 +157,7 @@ signal.
## Done condition ## Done condition
- `bash ~/utils/scripts/check_file_length.sh --all` exits 0. - `bash ~/src/utils/scripts/check_file_length.sh --all` exits 0.
- 402 Dart tests pass; `flutter` coverage still 100%. - 402 Dart tests pass; `flutter` coverage still 100%.
- `pre-commit run --all-files` passes. - `pre-commit run --all-files` passes.
- The app still runs on the phone: use the `phone-deploy` skill - The app still runs on the phone: use the `phone-deploy` skill

View File

@ -1,12 +1,12 @@
# Task: replace blind coordinate tapping with element-targeted Android UI automation # Task: replace blind coordinate tapping with element-targeted Android UI automation
> **STATUS 2026-08-10: BUILT.** The tool now exists at > **STATUS 2026-08-10: BUILT.** The tool now exists at
> `~/testsAndMisc/python_pkg/android_ui/` (see its README). It covers `dump` / > `~/src/testsAndMisc/python_pkg/android_ui/` (see its README). It covers `dump` /
> `find` / `tap` / `type` / `wait` / `focus` with verified typing, ambiguity as > `find` / `tap` / `type` / `wait` / `focus` with verified typing, ambiguity as
> an error, keyboard detection, and partial-tree retries — all exercised > an error, keyboard detection, and partial-tree retries — all exercised
> against the physical Pixel 6a. What remains from the spec below is the > against the physical Pixel 6a. What remains from the spec below is the
> optional MCP wrapper, `scroll_to()`, and rolling it out to `~/todo` and > optional MCP wrapper, `scroll_to()`, and rolling it out to `~/src/todo` and
> `~/dufs-cloud/app`. Everything after this line is the original brief, kept > `~/src/dufs-cloud/app`. Everything after this line is the original brief, kept
> because it records WHY each guarantee exists. > because it records WHY each guarantee exists.
## what ## what
@ -51,7 +51,7 @@ nodes by class and gave exact centers, and the retry worked first time.
New shared tool, not a per-repo copy. Two options, pick one: New shared tool, not a per-repo copy. Two options, pick one:
- `~/testsAndMisc/python_pkg/android_ui/` — a CLI (`android-ui find "Connect - `~/src/testsAndMisc/python_pkg/android_ui/` — a CLI (`android-ui find "Connect
Firebase"`, `android-ui tap "Connect Firebase"`, `android-ui dump`), matching Firebase"`, `android-ui tap "Connect Firebase"`, `android-ui dump`), matching
how `app_icons` is already shared across repos. how `app_icons` is already shared across repos.
- An MCP server exposing the same as tools, which is what makes it available to - An MCP server exposing the same as tools, which is what makes it available to
@ -59,9 +59,9 @@ New shared tool, not a per-repo copy. Two options, pick one:
asked for "some tool ... (mcp?) which also translates to UI". asked for "some tool ... (mcp?) which also translates to UI".
Consumers (every repo with an Android surface): Consumers (every repo with an Android surface):
- `~/screen-locker/stronglift_replacement/workout_app` - `~/src/screen-locker/stronglift_replacement/workout_app`
- `~/todo` - `~/src/todo`
- `~/dufs-cloud/app` - `~/src/dufs-cloud/app`
- any future Flutter app (this should be part of the app scaffold) - any future Flutter app (this should be part of the app scaffold)
## must ## must
@ -108,7 +108,7 @@ emulator and not a debug-only Flutter driver session.
- This session's working recovery: `uiautomator dump` + the bounds-parsing - This session's working recovery: `uiautomator dump` + the bounds-parsing
python in the screen-locker scratchpad (`ui.xml`, `ui2.xml`). python in the screen-locker scratchpad (`ui.xml`, `ui2.xml`).
- `~/screen-locker/CLAUDE.md` — "You are NOT done until you install the new - `~/src/screen-locker/CLAUDE.md` — "You are NOT done until you install the new
version on the phone itself", which is why agent-driven UI is on the critical version on the phone itself", which is why agent-driven UI is on the critical
path for every task in this repo. path for every task in this repo.
- Note `uiautomator dump` returns an empty/partial tree while an animation is - Note `uiautomator dump` returns an empty/partial tree while an animation is

View File

@ -1,13 +1,13 @@
## Shared gatelock library ## Shared gatelock library
`~/utils/gatelock` is **not** its own git repo — `git -C ~/utils/gatelock `~/src/utils/gatelock` is **not** its own git repo — `git -C ~/src/utils/gatelock
rev-parse --show-toplevel` resolves to `/home/kuhy/utils`, so it's a rev-parse --show-toplevel` resolves to `/home/kuhy/src/utils`, so it's a
subdirectory of the `~/utils` monorepo. Per audit scope, gatelock itself was subdirectory of the `~/src/utils` monorepo. Per audit scope, gatelock itself was
not walked against all 28 rules and no separate not walked against all 28 rules and no separate
`~/utils/gatelock/DESIGN_AUDIT_TODO.md` was written. `~/src/utils/gatelock/DESIGN_AUDIT_TODO.md` was written.
What's relevant here: gatelock defines exactly **one** real design token — What's relevant here: gatelock defines exactly **one** real design token —
`LockConfig.bg: str = "#1a1a1a"` (`~/utils/gatelock/gatelock/_window.py:71`), `LockConfig.bg: str = "#1a1a1a"` (`~/src/utils/gatelock/gatelock/_window.py:71`),
applied via `self.root.configure(bg=self._config.bg, ...)` in applied via `self.root.configure(bg=self._config.bg, ...)` in
`LockWindow.setup()` (`_window.py:143`). Both consumer repos re-hardcode `LockWindow.setup()` (`_window.py:143`). Both consumer repos re-hardcode
this same literal instead of reading it back off their own `LockConfig` this same literal instead of reading it back off their own `LockConfig`

View File

@ -14,7 +14,7 @@ independently, in 9 files. Evidence:
`_sick_dialog.py` (18), `_temperature_status_mixin.py` (5). `_sick_dialog.py` (18), `_temperature_status_mixin.py` (5).
`#1a1a1a` alone (the background) appears **31 times** — and it is *also* `#1a1a1a` alone (the background) appears **31 times** — and it is *also*
already defined once, correctly, as `LockConfig.bg = "#1a1a1a"` in already defined once, correctly, as `LockConfig.bg = "#1a1a1a"` in
`~/utils/gatelock/gatelock/_window.py:71`. None of the 31 in-repo literals `~/src/utils/gatelock/gatelock/_window.py:71`. None of the 31 in-repo literals
import or reference it; `LockWindow.setup()` in gatelock applies import or reference it; `LockWindow.setup()` in gatelock applies
`config.bg` to the root window (`_window.py:143`), and then every `config.bg` to the root window (`_window.py:143`), and then every
screen-locker dialog re-hardcodes the same string as if gatelock's value screen-locker dialog re-hardcodes the same string as if gatelock's value

View File

@ -47,7 +47,7 @@ production in this user's other repos — read both before designing this
one: one:
1. **diet-guard's Contents-API repo sync** 1. **diet-guard's Contents-API repo sync**
(`~/diet-guard/diet_guard/_sync.py` + `_sync_github.py` + (`~/src/diet-guard/diet_guard/_sync.py` + `_sync_github.py` +
`_sync_merge.py`, ~120/190/80 lines respectively). `GitHubSyncClient` `_sync_merge.py`, ~120/190/80 lines respectively). `GitHubSyncClient`
(`_sync_github.py:38`) wraps the GitHub Contents API with (`_sync_github.py:38`) wraps the GitHub Contents API with
`get_file_text(path)`, `put_file_text(path, text, message=...)`, `get_file_text(path)`, `put_file_text(path, text, message=...)`,
@ -59,7 +59,7 @@ one:
fine-grained PAT read from `~/.config/diet_guard/sync_token` (scoped to fine-grained PAT read from `~/.config/diet_guard/sync_token` (scoped to
just that one repo's contents). just that one repo's contents).
2. **wake-alarm's Gist-based sync** 2. **wake-alarm's Gist-based sync**
(`~/wake-alarm/shutdown-wrapper.sh`, phone app writes to a **private (`~/src/wake-alarm/shutdown-wrapper.sh`, phone app writes to a **private
Gist**, not a repo). Config at `~/.config/wake_alarm/gist_sync.json` Gist**, not a repo). Config at `~/.config/wake_alarm/gist_sync.json`
with `token`/`gist_id`. Much simpler (single JSON blob, no per-device with `token`/`gist_id`. Much simpler (single JSON blob, no per-device
paths, no merge step) — matches wake-alarm's much simpler data shape (one paths, no merge step) — matches wake-alarm's much simpler data shape (one
@ -148,7 +148,7 @@ sync code first.
- `screen_locker/_constants.py` (`WORKOUT_APP_JSON_REMOTES`, - `screen_locker/_constants.py` (`WORKOUT_APP_JSON_REMOTES`,
`WORKOUT_HTTP_PORT` — new `SYNC_*` constants go here too) `WORKOUT_HTTP_PORT` — new `SYNC_*` constants go here too)
- `stronglift_replacement/workout_app/lib/` (phone-side push, new work) - `stronglift_replacement/workout_app/lib/` (phone-side push, new work)
- Reference-only, do not modify: `~/diet-guard/diet_guard/_sync_github.py`, - Reference-only, do not modify: `~/src/diet-guard/diet_guard/_sync_github.py`,
`_sync.py`, `_sync_merge.py`; `~/wake-alarm/shutdown-wrapper.sh` `_sync.py`, `_sync_merge.py`; `~/src/wake-alarm/shutdown-wrapper.sh`
- **Do not touch**: `screen_locker/_runnerup_verification.py`, - **Do not touch**: `screen_locker/_runnerup_verification.py`,
`_runnerup_db.py` (hard boundary, see Context above) `_runnerup_db.py` (hard boundary, see Context above)

View File

@ -15,7 +15,7 @@ to have very different shareability:
`put_file_text` / `list_directory` against a private repo) is genuinely `put_file_text` / `list_directory` against a private repo) is genuinely
shared already — it's independently duplicated almost line-for-line shared already — it's independently duplicated almost line-for-line
between todo's `GitHubClient` and diet-guard's `GitHubSyncClient` between todo's `GitHubClient` and diet-guard's `GitHubSyncClient`
(`~/diet-guard/diet_guard/_sync_github.py`, 191 lines, `requests`-only). (`~/src/diet-guard/diet_guard/_sync_github.py`, 191 lines, `requests`-only).
Extracting this into a shared Python lib (and a shared Dart lib for Extracting this into a shared Python lib (and a shared Dart lib for
todo's/diet-guard's Dart-side client) is low-risk, small, and is the part todo's/diet-guard's Dart-side client) is low-risk, small, and is the part
Workstream C already committed to (Contents-API pattern, not Gist — Workstream C already committed to (Contents-API pattern, not Gist —
@ -70,7 +70,7 @@ library too:
## 3. Per-app migration cost (revised — the original doc's "todo = smallest ## 3. Per-app migration cost (revised — the original doc's "todo = smallest
lift" assumption does not hold) lift" assumption does not hold)
Checked `~/todo/lib/data/note_repository.dart`: notes are **mutated in Checked `~/src/todo/lib/data/note_repository.dart`: notes are **mutated in
place** — `upsert()` does `INSERT ... ON CONFLICT DO UPDATE SET text = ?, place** — `upsert()` does `INSERT ... ON CONFLICT DO UPDATE SET text = ?,
priority = ?, status = ?, updated_at = ?`, relying on `sqlite_crdt`'s priority = ?, status = ?, updated_at = ?`, relying on `sqlite_crdt`'s
per-column Hybrid-Logical-Clock last-writer-wins to reconcile concurrent per-column Hybrid-Logical-Clock last-writer-wins to reconcile concurrent
@ -138,8 +138,8 @@ off the shelf.
## Critical files referenced (read-only during this pass) ## Critical files referenced (read-only during this pass)
- `~/todo/lib/sync/sync_service.dart`, `~/todo/lib/data/note_repository.dart` - `~/src/todo/lib/sync/sync_service.dart`, `~/src/todo/lib/data/note_repository.dart`
- `~/diet-guard/diet_guard/_sync.py`, `_sync_github.py`, `_sync_merge.py` - `~/src/diet-guard/diet_guard/_sync.py`, `_sync_github.py`, `_sync_merge.py`
- `~/diet-guard/app/lib/services/sync_merge.dart` - `~/src/diet-guard/app/lib/services/sync_merge.dart`
- `~/wake-alarm/shutdown-wrapper.sh` - `~/src/wake-alarm/shutdown-wrapper.sh`
- `docs/todo/workstream-c-github-sync-workout-data.md` (this repo) - `docs/todo/workstream-c-github-sync-workout-data.md` (this repo)

View File

@ -8,9 +8,9 @@ fell out of it (both resolved by explicit user call, 2026-07-04).
**The two shared libraries are now implemented and locally verified **The two shared libraries are now implemented and locally verified
(2026-07-05):** (2026-07-05):**
- `~/crdt-sync` (Python) — 60 tests, 100% branch coverage, clean - `~/src/utils/crdt-sync` (Python) — 60 tests, 100% branch coverage, clean
ruff/mypy/pylint/bandit/pre-commit. Local git repo, one commit. ruff/mypy/pylint/bandit/pre-commit. Local git repo, one commit.
- `~/crdt_sync_dart` (pure Dart, no Flutter dep) — 59 tests, clean - `~/src/utils/crdt_sync_dart` (pure Dart, no Flutter dep) — 59 tests, clean
`dart analyze`. Local git repo, one commit. `dart analyze`. Local git repo, one commit.
Both implement the unified LWW-map-with-sticky-remove scheme (`Hlc`, Both implement the unified LWW-map-with-sticky-remove scheme (`Hlc`,
@ -62,7 +62,7 @@ from-scratch CRDT implementation.
**diet-guard and wake-alarm do NOT use CRDTs today.** Their existing **diet-guard and wake-alarm do NOT use CRDTs today.** Their existing
"merge" mechanisms are much simpler: "merge" mechanisms are much simpler:
- diet-guard: `_sync_merge.merge_logs` (`~/diet-guard/diet_guard/_sync_merge.py`, - diet-guard: `_sync_merge.merge_logs` (`~/src/diet-guard/diet_guard/_sync_merge.py`,
81 lines) is a tombstone-aware, entry-key-based merge — closer to 81 lines) is a tombstone-aware, entry-key-based merge — closer to
last-write-wins per entry than a general CRDT. Transport is last-write-wins per entry than a general CRDT. Transport is
`_sync_github.GitHubSyncClient` against a private repo's Contents API `_sync_github.GitHubSyncClient` against a private repo's Contents API
@ -151,9 +151,9 @@ time this is scoped for real.
## Critical files (reference only — read, do not modify, during scoping) ## Critical files (reference only — read, do not modify, during scoping)
- `~/todo/lib/sync/sync_service.dart`, `sync_settings.dart` (CRDT-via-package pattern) - `~/src/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` - `~/src/diet-guard/diet_guard/_sync.py`, `_sync_github.py`, `_sync_merge.py`
- `~/diet-guard/app/lib/services/sync_service.dart`, `sync_merge.dart` - `~/src/diet-guard/app/lib/services/sync_service.dart`, `sync_merge.dart`
- `~/wake-alarm/shutdown-wrapper.sh` - `~/src/wake-alarm/shutdown-wrapper.sh`
- `docs/todo/workstream-c-github-sync-workout-data.md` (this repo — resolve - `docs/todo/workstream-c-github-sync-workout-data.md` (this repo — resolve
its open questions jointly with this one, not independently) its open questions jointly with this one, not independently)

View File

@ -42,7 +42,7 @@ readonly REPO_DIR="$SCRIPT_DIR"
readonly SYSTEMD_USER_DIR="$HOME/.config/systemd/user" readonly SYSTEMD_USER_DIR="$HOME/.config/systemd/user"
readonly PROD_PYTHON="/usr/bin/python3" readonly PROD_PYTHON="/usr/bin/python3"
# The shared lock backend, developed in lockstep with this repo. # The shared lock backend, developed in lockstep with this repo.
readonly GATELOCK_DIR="$HOME/utils/gatelock" readonly GATELOCK_DIR="$HOME/src/utils/gatelock"
echo "=== Screen Locker Installer ===" echo "=== Screen Locker Installer ==="
@ -58,7 +58,7 @@ fi
# 2. Install the package ------------------------------------------------------ # 2. Install the package ------------------------------------------------------
# --break-system-packages is required on Arch (PEP 668). Editable so a later # --break-system-packages is required on Arch (PEP 668). Editable so a later
# `git pull` in THIS clone updates the running service with no reinstall. That # `git pull` in THIS clone updates the running service with no reinstall. That
# is also why the clone must live somewhere durable (~/screen-locker), never a # is also why the clone must live somewhere durable (~/src/screen-locker), never a
# scratch directory: a non-editable snapshot would freeze at this commit and # scratch directory: a non-editable snapshot would freeze at this commit and
# silently stop tracking the repo. # silently stop tracking the repo.
echo "[2/4] Installing screen_locker into user site-packages (editable)..." echo "[2/4] Installing screen_locker into user site-packages (editable)..."
@ -69,7 +69,7 @@ echo "[2/4] Installing screen_locker into user site-packages (editable)..."
# pyproject.toml pins `gatelock @ git+https://.../@gatelock-v0.2.1`, so the # pyproject.toml pins `gatelock @ git+https://.../@gatelock-v0.2.1`, so the
# install above resolves that pin and overwrites any local editable gatelock # install above resolves that pin and overwrites any local editable gatelock
# with a frozen wheel built from the tag. That silently reverts every # with a frozen wheel built from the tag. That silently reverts every
# unreleased change in ~/utils/gatelock and breaks the other lockers that # unreleased change in ~/src/utils/gatelock and breaks the other lockers that
# depend on them -- observed: it removed `ScrollableSurface` and took # depend on them -- observed: it removed `ScrollableSurface` and took
# diet_guard down with it. # diet_guard down with it.
# #
@ -122,7 +122,7 @@ if ! (cd /tmp && "$PROD_PYTHON" -c "import screen_locker" 2>/dev/null); then
fi fi
if ! (cd /tmp && "$PROD_PYTHON" -c "import gatelock" 2>/dev/null); then if ! (cd /tmp && "$PROD_PYTHON" -c "import gatelock" 2>/dev/null); then
echo "ERROR: $PROD_PYTHON cannot import gatelock (the shared lock" >&2 echo "ERROR: $PROD_PYTHON cannot import gatelock (the shared lock" >&2
echo " backend). Install it editable from ~/utils/gatelock." >&2 echo " backend). Install it editable from ~/src/utils/gatelock." >&2
exit 1 exit 1
fi fi
if [[ -d "$GATELOCK_DIR" ]]; then if [[ -d "$GATELOCK_DIR" ]]; then

View File

@ -2,8 +2,8 @@
> "do dopamine-ux-04-screen-locker". It is self-contained -- it needs no context from > "do dopamine-ux-04-screen-locker". It is self-contained -- it needs no context from
> any other session. > any other session.
> >
> Canonical copy: `~/utils/dopamine-ux/04-screen-locker.md` (source of truth; this is a > Canonical copy: `~/src/utils/dopamine-ux/04-screen-locker.md` (source of truth; this is a
> distributed copy). Running order and cross-prompt rules: `~/utils/dopamine-ux/00-INDEX.md`. > distributed copy). Running order and cross-prompt rules: `~/src/utils/dopamine-ux/00-INDEX.md`.
> Generated 2026-08-16 from a survey of this repo -- line numbers are accurate > Generated 2026-08-16 from a survey of this repo -- line numbers are accurate
> as of that date; **anchor on the symbol names, not the line numbers.** > as of that date; **anchor on the symbol names, not the line numbers.**
@ -19,10 +19,10 @@ Do **both**, in the same session that completes the work:
Do not leave a finished prompt lying in the repo -- a stale prompt is Do not leave a finished prompt lying in the repo -- a stale prompt is
indistinguishable from a pending one, and the next session will re-run it. indistinguishable from a pending one, and the next session will re-run it.
2. **Log completion in the canonical index**, in `~/utils` (a *different* repo, 2. **Log completion in the canonical index**, in `~/src/utils` (a *different* repo,
so it needs its own commit): so it needs its own commit):
```bash ```bash
cd ~/utils cd ~/src/utils
# append to the "Completion log" table in dopamine-ux/00-INDEX.md: # append to the "Completion log" table in dopamine-ux/00-INDEX.md:
# | 04-screen-locker.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> | # | 04-screen-locker.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> |
git add dopamine-ux/00-INDEX.md git add dopamine-ux/00-INDEX.md
@ -69,7 +69,7 @@ the data already exists, is already true, and needs no new state.
## where ## where
Repo: `~/screen-locker`. Repo: `~/src/screen-locker`.
Primary: Primary:
- `screen_locker/_status_sections.py` — the section renderers used by the status - `screen_locker/_status_sections.py` — the section renderers used by the status
@ -136,7 +136,7 @@ Primary:
- optional: a daily streak alongside the weekly one. Note this **would** be new - optional: a daily streak alongside the weekly one. Note this **would** be new
state (the existing streak is consecutive-*weeks*), so it triggers the conftest state (the existing streak is consecutive-*weeks*), so it triggers the conftest
requirement above. diet-guard's consecutive-*day* streak requirement above. diet-guard's consecutive-*day* streak
(`~/diet-guard/diet_guard/_daystatus.py` ~:144) is the precedent to mirror. (`~/src/diet-guard/diet_guard/_daystatus.py` ~:144) is the precedent to mirror.
## done ## done
@ -144,7 +144,7 @@ Primary:
the current streak, bonus hours and early-bird state — with the real values the current streak, bonus hours and early-bird state — with the real values
from `extra_benefits_state.json`, not placeholders. from `extra_benefits_state.json`, not placeholders.
2. A week-transition milestone reaches the UI instead of only `_logger.info`. 2. A week-transition milestone reaches the UI instead of only `_logger.info`.
3. `cd ~/screen-locker && python -m pytest` passes. 3. `cd ~/src/screen-locker && python -m pytest` passes.
4. `pre-commit run --files <changed files>` is clean — including the 250-line cap 4. `pre-commit run --files <changed files>` is clean — including the 250-line cap
(`scripts/check_file_length.py`) and the silent-failure check. (`scripts/check_file_length.py`) and the silent-failure check.
5. `git diff` touches no enforcement logic: no change to lock decisions, weekly 5. `git diff` touches no enforcement logic: no change to lock decisions, weekly
@ -155,7 +155,7 @@ Primary:
Desktop — this is a desktop Python app. Desktop — this is a desktop Python app.
``` ```
cd ~/screen-locker cd ~/src/screen-locker
python -m screen_locker --status # CLI: confirm the values it prints python -m screen_locker --status # CLI: confirm the values it prints
screen-locker-status # the Tk window: confirm they render screen-locker-status # the Tk window: confirm they render
``` ```
@ -176,7 +176,7 @@ Current real state for comparison: `screen_locker/extra_benefits_state.json` and
(the enforcement invariant) and the reward strings ~:130-139. (the enforcement invariant) and the reward strings ~:130-139.
- `screen_locker/_unlock_view.py` — the existing celebration, for tone. - `screen_locker/_unlock_view.py` — the existing celebration, for tone.
- `screen_locker/status_view.py` — check its line count **before** editing. - `screen_locker/status_view.py` — check its line count **before** editing.
- `~/screen-locker/CLAUDE.md` — repo rules (no silent failures, ruff `select=ALL`). - `~/src/screen-locker/CLAUDE.md` — repo rules (no silent failures, ruff `select=ALL`).
## context you would otherwise rediscover ## context you would otherwise rediscover
@ -196,7 +196,7 @@ Current real state for comparison: `screen_locker/extra_benefits_state.json` and
tunable is a module-level constant. An opt-out toggle has no existing home; tunable is a module-level constant. An opt-out toggle has no existing home;
for a self-restriction tool, requiring a code edit to disable is arguably for a self-restriction tool, requiring a code edit to disable is arguably
correct, so prefer a constant in `_constants.py` over new state. correct, so prefer a constant in `_constants.py` over new state.
- `~/gatelock` is **not** a repo — it is `~/utils/gatelock/gatelock/`, consumed as - `~/gatelock` is **not** a repo — it is `~/src/utils/gatelock/gatelock/`, consumed as
a pinned git dep, and it holds no workout state. a pinned git dep, and it holds no workout state.
- Manual workouts were **not** removed: `_manual_workout.py` implements a - Manual workouts were **not** removed: `_manual_workout.py` implements a
rate-limited, evidence-gated subsystem (budget 2 per 7 days, 10 per 30). rate-limited, evidence-gated subsystem (budget 2 per 7 days, 10 per 30).

View File

@ -2,8 +2,8 @@
> "do dopamine-ux-05-workout-app". It is self-contained -- it needs no context from > "do dopamine-ux-05-workout-app". It is self-contained -- it needs no context from
> any other session. > any other session.
> >
> Canonical copy: `~/utils/dopamine-ux/05-workout-app.md` (source of truth; this is a > Canonical copy: `~/src/utils/dopamine-ux/05-workout-app.md` (source of truth; this is a
> distributed copy). Running order and cross-prompt rules: `~/utils/dopamine-ux/00-INDEX.md`. > distributed copy). Running order and cross-prompt rules: `~/src/utils/dopamine-ux/00-INDEX.md`.
> Generated 2026-08-16 from a survey of this repo -- line numbers are accurate > Generated 2026-08-16 from a survey of this repo -- line numbers are accurate
> as of that date; **anchor on the symbol names, not the line numbers.** > as of that date; **anchor on the symbol names, not the line numbers.**
@ -19,10 +19,10 @@ Do **both**, in the same session that completes the work:
Do not leave a finished prompt lying in the repo -- a stale prompt is Do not leave a finished prompt lying in the repo -- a stale prompt is
indistinguishable from a pending one, and the next session will re-run it. indistinguishable from a pending one, and the next session will re-run it.
2. **Log completion in the canonical index**, in `~/utils` (a *different* repo, 2. **Log completion in the canonical index**, in `~/src/utils` (a *different* repo,
so it needs its own commit): so it needs its own commit):
```bash ```bash
cd ~/utils cd ~/src/utils
# append to the "Completion log" table in dopamine-ux/00-INDEX.md: # append to the "Completion log" table in dopamine-ux/00-INDEX.md:
# | 05-workout-app.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> | # | 05-workout-app.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> |
git add dopamine-ux/00-INDEX.md git add dopamine-ux/00-INDEX.md
@ -74,7 +74,7 @@ number with a motion token while you are in the file.
## where ## where
Repo: `~/screen-locker`. App: `~/screen-locker/stronglift_replacement/workout_app` Repo: `~/src/screen-locker`. App: `~/src/screen-locker/stronglift_replacement/workout_app`
(a Flutter app nested inside the screen-locker repo; that directory contains **no (a Flutter app nested inside the screen-locker repo; that directory contains **no
Python** — only the app and two design docs). Python** — only the app and two design docs).
@ -175,7 +175,7 @@ Theme: `lib/ui/theme.dart` — local hand-copy, **no `design_system` dependency*
2. Finishing a workout produces a haptic and a visible dialog arrival; step 2 2. Finishing a workout produces a haptic and a visible dialog arrival; step 2
adds a sound. adds a sound.
3. No inline animation duration remains in `exercise_tile_rows.dart`. 3. No inline animation duration remains in `exercise_tile_rows.dart`.
4. `cd ~/screen-locker/stronglift_replacement/workout_app && flutter analyze` clean. 4. `cd ~/src/screen-locker/stronglift_replacement/workout_app && flutter analyze` clean.
5. `flutter test` passes **and coverage stays at 100%**. 5. `flutter test` passes **and coverage stays at 100%**.
6. With OS "remove animations" on, the app works and durations are zero. 6. With OS "remove animations" on, the app works and durations are zero.
7. Step 2: the settings toggle flips, persists across restart, and silences both 7. Step 2: the settings toggle flips, persists across restart, and silences both
@ -187,7 +187,7 @@ Theme: `lib/ui/theme.dart` — local hand-copy, **no `design_system` dependency*
``` ```
adb devices # confirm 23181JEGR08034 adb devices # confirm 23181JEGR08034
cd ~/screen-locker/stronglift_replacement/workout_app cd ~/src/screen-locker/stronglift_replacement/workout_app
flutter build apk --release flutter build apk --release
adb install -r build/app/outputs/flutter-apk/app-release.apk adb install -r build/app/outputs/flutter-apk/app-release.apk
``` ```
@ -209,7 +209,7 @@ state what you felt and heard; do not infer them from the code.
`setLastWorkoutType` as the public-wrapper pattern. `setLastWorkoutType` as the public-wrapper pattern.
- `lib/screens/settings_screen_sections.dart` ~:1-11 — the coverage/line-cap - `lib/screens/settings_screen_sections.dart` ~:1-11 — the coverage/line-cap
comments, and `_SyncSection` as the section pattern. comments, and `_SyncSection` as the section pattern.
- `~/utils/unified-design-system/motion.md` — motion/haptic vocabulary from - `~/src/utils/unified-design-system/motion.md` — motion/haptic vocabulary from
prompt 01. **Prompt 01 must have run first.** prompt 01. **Prompt 01 must have run first.**
## context you would otherwise rediscover ## context you would otherwise rediscover

View File

@ -49,7 +49,7 @@ def is_scheduled_skip_today(today: date | None = None) -> bool:
"""Return True if *today* is in the shared free-day pool. """Return True if *today* is in the shared free-day pool.
The source moved from this app's own ``scheduled_skips.json`` to the The source moved from this app's own ``scheduled_skips.json`` to the
fleet-wide pool (``~/utils/freedays``), so one marked day now stands down fleet-wide pool (``~/src/utils/freedays``), so one marked day now stands down
every gate app at once instead of only this one. every gate app at once instead of only this one.
The date is **local**, not UTC. The file-backed check this replaces The date is **local**, not UTC. The file-backed check this replaces

View File

@ -135,7 +135,7 @@ def get_flags() -> dict[str, bool]:
``is_scheduled_skip_today`` keeps its name but no longer reads this app's ``is_scheduled_skip_today`` keeps its name but no longer reads this app's
``scheduled_skips.json``: it is now the shared, fleet-wide free-day pool ``scheduled_skips.json``: it is now the shared, fleet-wide free-day pool
(``~/utils/freedays``), so a true here also means diet-guard, wake-alarm, (``~/src/utils/freedays``), so a true here also means diet-guard, wake-alarm,
leetcode-guard and home-guard are standing down today. leetcode-guard and home-guard are standing down today.
""" """
history = load_history() history = load_history()

View File

@ -21,7 +21,7 @@
# whole ratchet. # whole ratchet.
# #
# Add to /etc/sudoers.d/workout-locker: # Add to /etc/sudoers.d/workout-locker:
# <username> ALL=(root) NOPASSWD: /home/kuhy/screen-locker/screen_locker/adjust_shutdown_schedule.sh # <username> ALL=(root) NOPASSWD: /home/kuhy/src/screen-locker/screen_locker/adjust_shutdown_schedule.sh
set -euo pipefail set -euo pipefail

View File

@ -3,7 +3,7 @@
This used to append to ``screen_locker/scheduled_skips.json``, which only This used to append to ``screen_locker/scheduled_skips.json``, which only
screen-locker read. That file is no longer consulted by anything: the lock screen-locker read. That file is no longer consulted by anything: the lock
chain now asks the shared pool (``~/utils/freedays``), so a day taken here chain now asks the shared pool (``~/src/utils/freedays``), so a day taken here
also stands down diet-guard, wake-alarm, leetcode-guard and home-guard. also stands down diet-guard, wake-alarm, leetcode-guard and home-guard.
Kept as a thin wrapper rather than deleted because ``arm.sh`` calls it, and Kept as a thin wrapper rather than deleted because ``arm.sh`` calls it, and

View File

@ -3,7 +3,7 @@
# ============================================================================ # ============================================================================
# Fail if any dependency in the commit is behind its ecosystem's newest stable. # Fail if any dependency in the commit is behind its ecosystem's newest stable.
# #
# Thin delegate to the shared gate in ~/utils, which owns the registry # Thin delegate to the shared gate in ~/src/utils, which owns the registry
# adapters, the pre-release rules and the allowlist semantics. Copying that # adapters, the pre-release rules and the allowlist semantics. Copying that
# logic here is what lets one repo's idea of "current" drift from every other # logic here is what lets one repo's idea of "current" drift from every other
# repo's -- so this script only locates the shared checker and forwards its # repo's -- so this script only locates the shared checker and forwards its
@ -18,12 +18,12 @@
set -euo pipefail set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_dependency_freshness.sh" readonly SHARED_GATE="${UTILS_ROOT:-$HOME/src/utils}/scripts/check_dependency_freshness.sh"
main() { main() {
if [[ ! -x "$SHARED_GATE" ]]; then if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared dependency-freshness gate not found at $SHARED_GATE" >&2 echo "Error: shared dependency-freshness gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2 echo " Clone github.com/kuhyx/utils to ~/src/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2 echo " UTILS_ROOT to where it lives." >&2
exit 1 exit 1
fi fi

View File

@ -3,7 +3,7 @@
# ============================================================================ # ============================================================================
# Fail if any markdown file breaks the shared naming convention. # Fail if any markdown file breaks the shared naming convention.
# #
# Thin delegate to the shared gate in ~/utils, which owns the four namespaces # Thin delegate to the shared gate in ~/src/utils, which owns the four namespaces
# (README / CLAUDE* / DOCS* / TODO*), the "REMOVE ME AFTER FINISH" marker rule # (README / CLAUDE* / DOCS* / TODO*), the "REMOVE ME AFTER FINISH" marker rule
# and the exemption list. Copying that logic here is what lets one repo's idea # and the exemption list. Copying that logic here is what lets one repo's idea
# of a valid name drift from every other repo's -- so this script only locates # of a valid name drift from every other repo's -- so this script only locates
@ -16,12 +16,12 @@
set -euo pipefail set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_md_naming.sh" readonly SHARED_GATE="${UTILS_ROOT:-$HOME/src/utils}/scripts/check_md_naming.sh"
main() { main() {
if [[ ! -x "$SHARED_GATE" ]]; then if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared markdown-naming gate not found at $SHARED_GATE" >&2 echo "Error: shared markdown-naming gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2 echo " Clone github.com/kuhyx/utils to ~/src/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2 echo " UTILS_ROOT to where it lives." >&2
exit 1 exit 1
fi fi

View File

@ -5,7 +5,7 @@
# #
# The apps depend on crdt_sync as a *git dependency pinned to a tag*, so what # The apps depend on crdt_sync as a *git dependency pinned to a tag*, so what
# CI builds is whatever that tag points at. A `dependency_overrides:` entry # CI builds is whatever that tag points at. A `dependency_overrides:` entry
# pointing at ~/utils/crdt_sync_dart is invaluable while developing an unreleased # pointing at ~/src/utils/crdt_sync_dart is invaluable while developing an unreleased
# library change -- and catastrophic if it survives the commit: local tests go # library change -- and catastrophic if it survives the commit: local tests go
# green against code CI has never seen, and the failure is silent in both # green against code CI has never seen, and the failure is silent in both
# directions. # directions.

View File

@ -18,7 +18,7 @@ whole ``tk`` module for every test so the suite can never reach a display, and
widget heights can only be measured by really rendering them. Like widget heights can only be measured by really rendering them. Like
``verify_lock_popup_safety.py``, this re-executes itself under Xvfb: ``verify_lock_popup_safety.py``, this re-executes itself under Xvfb:
cd ~/screen-locker && python3 -m scripts.verify_screen_fits cd ~/src/screen-locker && python3 -m scripts.verify_screen_fits
Run as a *module*, not a path: ``-m`` puts the repo root on ``sys.path`` so Run as a *module*, not a path: ``-m`` puts the repo root on ``sys.path`` so
``screen_locker`` resolves from the checkout. Running it as a path only works ``screen_locker`` resolves from the checkout. Running it as a path only works

View File

@ -55,7 +55,7 @@ flutter:
- assets/sounds/break_end.mp3 - assets/sounds/break_end.mp3
# Launcher icon generation. Source art is produced by the shared generator: # Launcher icon generation. Source art is produced by the shared generator:
# PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app <key> # PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app <key>
# then run: dart run flutter_launcher_icons # then run: dart run flutter_launcher_icons
flutter_launcher_icons: flutter_launcher_icons:
android: true android: true

View File

@ -1,5 +1,5 @@
/* /*
* Tokens from ~/utils/unified-design-system (DOCS-tokens.md). Values are copied * Tokens from ~/src/utils/unified-design-system (DOCS-tokens.md). Values are copied
* rather than imported because that system ships as documentation plus * rather than imported because that system ships as documentation plus
* per-stack generators, not an npm package -- keep these in step with it rather * per-stack generators, not an npm package -- keep these in step with it rather
* than inventing new ones. * than inventing new ones.

View File

@ -61,7 +61,7 @@ export function makeHealth(over: Partial<HealthPayload> = {}): HealthPayload {
armed: true, armed: true,
disarmed: false, disarmed: false,
disarm_marker: '/home/kuhy/.local/share/screen_locker/DISARMED', disarm_marker: '/home/kuhy/.local/share/screen_locker/DISARMED',
log_file: '/home/kuhy/screen-locker/screen_locker/log.json', log_file: '/home/kuhy/src/screen-locker/screen_locker/log.json',
log_age_seconds: 3600, log_age_seconds: 3600,
decision_log_file: '/home/kuhy/.local/share/screen_locker/decisions.jsonl', decision_log_file: '/home/kuhy/.local/share/screen_locker/decisions.jsonl',
last_decision_age_seconds: 120, last_decision_age_seconds: 120,

View File

@ -19,4 +19,4 @@ ExecStart=/usr/bin/python3 -m screen_locker.screen_lock --sync-only
# disabled early-bird-workout-check.timer and nothing noticed for 13 days). # disabled early-bird-workout-check.timer and nothing noticed for 13 days).
# ExecStartPost without "-" means a disarmed locker marks this unit failed, so # ExecStartPost without "-" means a disarmed locker marks this unit failed, so
# it surfaces in `systemctl --user --failed` instead of scrolling past. # it surfaces in `systemctl --user --failed` instead of scrolling past.
ExecStartPost=/usr/bin/python3 %h/screen-locker/scripts/check_enforcement_armed.py --quiet ExecStartPost=/usr/bin/python3 %h/src/screen-locker/scripts/check_enforcement_armed.py --quiet