Commit Graph

75 Commits

Author SHA1 Message Date
a0c1938d8d Split notes on first non-blank line
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 14:14:16 +01:00
cbe3d9be59 One open note per repo, merge tool
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 14:11:21 +01:00
67212cd7ec One list: repo groups, repo search, compact filter
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 13:40:20 +01:00
9b42ff8ba2 Bump crdt_sync_dart, sync_settings_ui
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
crdt_sync_dart 0.11.3, sync_settings_ui 0.5.2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
2026-11-02 12:15:11 +01:00
dc547763fc Bump Flutter to 3.47.9, Gradle to 9.8.2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
2026-11-02 12:01:25 +01:00
4a30a52375 Move TODOs to todo app
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWZ24SUudEMNQAxdYb8
2026-11-02 11:20:34 +01:00
860d789864 Use google_account_token plugin
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Replace the app-local AccountManager channel with the shared plugin
(google_account_token-v0.1.0); kServerClientId stays put.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
2026-11-02 11:15:28 +01:00
d09978a338 Add one-command MCP go-live + live verify
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWVUTaKFW8m7YrvWbzV
2026-11-02 10:45:45 +01:00
cf01784241 TODO: flaky image_store_io attach test
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
2026-11-02 10:28:26 +01:00
0ff3d6f95d Sign in via AccountManager, drop google_sign_in
System account chooser plus getAuthToken with an
audience:server:client_id token; no Google client library.
Pins bumped to newest stable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWY1k4muZfdVJfb7YGJ
2026-11-02 10:24:59 +01:00
2a9332094a MCP: Dart task server replaces Python todo_mcp
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-11-02 09:36:44 +01:00
9e18821c7b Remove TODO-fit-to-screen-layout.md: implemented by the notes grid
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-02 09:28:27 +01:00
f7ea0ffb3d Notes list: 2-column tile grid on phones, whole rows fill the screen
Below 600dp the notes list is a compact tile grid (title + priority/status
cues). Tile height is derived so whole rows exactly fill the viewport between
the status and navigation bars; past a screenful it scrolls vertically (no
paging). Body and dates moved into the long-press sheet. Wider windows keep
the list. Not using design_system FitGrid: it is square-celled and pages
horizontally.

Pins: design_system v0.3.0 (newest tag); also bumped crdt_sync_dart 0.11.2,
sync_settings_ui 0.4.2, file_picker 13.3.1, idb_shim 2.11.1,
sqflite_common_ffi 2.4.5, web 1.1.2 because the freshness gate was already
red on them at HEAD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:28:18 +01:00
377ec17356 Design audit closed: 6px gaps → 8 on the 4/8 grid; track fit-to-screen TODO
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Filter-sheet tests now scroll Apply into view (tapApply): the sheet is
scrollable and Apply legitimately sits below the fold on a 600px viewport.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 09:18:40 +01:00
9349747629 Repo tasks: repo/done_check/by/evidence fields, filter chips, CLI close/list/search
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Four optional note fields (repo, done_check, by, evidence) in the crdt_sync
record, written only when set, so editing a plain idea stays wire-identical
to v3 (note_repository_sync_compat_test). The log is schemaless: no stored
schema version; the legacy sqlite reader stays at v3.

NoteFilter gains a sources facet (ideas / every repo task / one repo);
search also matches the repo. The notes list shows only open ideas by
default (no badge); the filter sheet adds Ideas, Repo tasks and one chip
per repo when repo tasks exist. The per-note sheet shows the task fields
read-only. BACKLOG.md markers carry the new attributes, entity-escaped.

CLI: --repo --done-check --by on add, --close <id> --evidence (refused
without), --list [--repo] [--status], --search, and every id flag takes a
unique 8-character prefix. --list prints id8, status, priority, repo, title.

install_hooks.sh: the wrapper finds pre-commit's generated hook next to
itself, so commits from a git worktree (where .git is a file) work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 08:26:26 +01:00
64bce89d87 Launcher: label Todo (Android, desktop entry, PKGBUILD), icon fitted to the shared 340px ink circle
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012kQ5PCKqBHAkAVkPSvp6dk
2026-10-23 14:38:14 +02:00
c71b98c08b Desktop: no stray browser window from extensions; keyboard stays down after backing out of a note
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:00:26 +02:00
341a943277 CLI: --set-status <id> <status> re-statuses a note and pushes
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 17:22:58 +02:00
41947463c5 Bump flutter 3.47.7, gradle 9.8.1, pub deps to latest
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 15:41:06 +02:00
56cb1b3594 Add headless note CLI (bin/todo_note.dart) reusing app CRDT sync
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 15:35:46 +02:00
f7df3cbbd1 Repo-contract rollout and dependency bumps (github_device_auth v0.3.2, url_launcher 6.3.3)
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
repo-contract / repo contract (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-04 18:23:25 +02:00
9f2ffdb8a9 Attach any file to a note, of any size, not only images
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
📎 now picks any file (Android: Camera / Gallery / File; desktop: an
unfiltered dialog) and the desktop page accepts any dropped file. Photo and
screenshot formats up to 64 MB are still re-encoded and shown inline;
everything else is stored byte for byte on dufs beside the images and linked
as [name](url), shown as a row with its size that opens in the platform's
app (Android) or a new browser tab (desktop). BACKLOG.md rewrites file links
to ~/data/cloud/todo-images/ like image links.

There is no size cap, so no layer holds a file in memory: uploads and
downloads stream through DufsImageClient, writes land in a .tmp renamed when
whole (an interrupted download no longer leaves a truncated file that looks
done), the desktop page POSTs the browser's own Blob with fetch, and Android's
"File" uses file_picker, because file_selector's Android side reads the whole
file into memory on the UI thread. Verified with a 1 GB file end to end on
desktop (peak RSS +36 MB) and on an API 36 emulator.

The wrapper serves attachments from the origin that holds the GitHub token
and the notes, so only script-free types open inline (nosniff), the rest
download; writes carrying another site's Origin are refused (this also
closes cross-site overwrites of the backup files); and attachment routes run
concurrently so a long upload or a playing video cannot stall the page.

Also: cupertino_icons 2.0.0 (unused, but the freshness gate blocks the
pubspec otherwise), open_file 4.0.0 and file_picker 13.1.0 pinned.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:17:06 +02:00
05056cf8c2 Rename ImageRef to AttachmentRef ahead of non-image attachments
A ref is about to name any file stored under todo-images/, not only a
re-encoded JPEG. Mechanical rename of the type and its file; no behaviour
change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:59:34 +02:00
2bbfe0b663 Desktop: launch Chrome with --password-store=basic so a locked keyring cannot blank the window
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 16:31:08 +02:00
8aa365919b Take Flutter 3.47.5 and Gradle 9.8.0
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:21:25 +02:00
0798f7b1d0 Attach images to notes, stored on dufs and kept offline
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-25 14:11:15 +02:00
9edcc76569 Bump Flutter to 3.47.4 and flutter_secure_storage to 11.1.1
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 17:19:48 +02:00
753e410fb4 Export the backlog to ~/src/todo, not the pre-reorganisation ~/todo
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 17:13:21 +02:00
3fd326516b Export the backlog to ~/src/todo, not ~/todo
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Both desktop writers build the directory by string interpolation
(`Directory('$home/todo')`), which the ~ reorganisation's reference
rewriter cannot see. The first export after the move recreated a stray
~/todo and tripped the home-tidy root allowlist.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 20:33:07 +02:00
a0d37c4df8 Re-point paths for the ~ reorganisation (home-tidy)
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Also bumps Flutter 3.47.3, flutter_secure_storage 11.1.0, idb_shim 2.9.9,
sqflite_common_ffi 2.4.3, very_good_analysis 11 and mcp 2.2.0 so the
freshness gate lets this through; v11 adds async_return_with_no_await, so
the 18 flagged return sites now await explicitly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 19:49:03 +02:00
951b00e263 Grade the todo MCP against 3.14 too, not just run on it
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
ruff and mypy were still grading `mcp/` against 3.10 while `requires-python`
had already moved to 3.14, so `datetime.timezone.utc` survived where
`datetime.UTC` is now correct (UP017). `pin_ci_python.py` rewrites the knobs;
ruff's `--select UP,I,F401 --fix` did the rest.

`mcp/` has no ruff hook in `.pre-commit-config.yaml`, so it also carries six
pre-existing CPY001 (missing copyright notice) findings. Those fire identically
at py310 and are left alone -- they are not this change's to fix.

24 tests pass at 100% line and branch coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr
2026-08-29 11:46:10 +02:00
f3facdd2ee One Python, always the newest
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 19:41:02 +02:00
6dbfb78f79 Refresh the gate workflow from the shared template
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 16:55:53 +02:00
4bc1ab6ef6 Pin the CI toolchain to the SDK this repo actually builds with
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
`.fvmrc` says 3.47.2 and nothing read it: `subosito/flutter-action` takes its
own `flutter-version:`, and the fleet had drifted to 3.44.6, 3.44.8, 3.44.9,
3.47.0 and 3.47.1 with no one tracking it. kuhylog is the proof it matters --
its runner on 3.44.9 predates the `@awaitNotRequired` annotation, so the same
`unawaited(Navigator.push(...))` is a violation on one SDK and required on the
other, and CI went red on a commit that is clean locally.

The freshness gate reads these pins now (utils@98213cb), so this cannot drift
again in silence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
2026-08-28 15:06:09 +02:00
5f29dfebf7 Latest stable dependencies, and the gate that keeps them there
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Three git-tag pins move (crdt_sync_dart v0.10.0 -> v0.11.1, design_system
v0.1.0 -> v0.2.1, github_device_auth v0.3.0 -> v0.3.1) and every caret in the
pubspec becomes an exact pin, flutter_secure_storage 10.3.1 -> 11.0.0 being
the one that matters. That version demands compileSdk 37, which needs AGP >
9.1.0, which needs Gradle >= 9.5.0, so those three move in this commit too.

**mcp 1.x -> 2.1.1 in the MCP subproject is a real breaking change.** FastMCP
was renamed MCPServer and moved to `mcp.server.mcpserver`; the decorator and
`run()` API are unchanged, so the migration is the import and the constructor.
Verified by driving the server over stdio: `initialize` and `tools/list` both
answer, all three tools still enumerate. 24 tests at 100% coverage.

`very_good_analysis` 10.3 adds a `cascade_invocations` finding on the
port-in-use message in bin/todo_desktop.dart. Fixed, not suppressed.

300 Dart tests green, `flutter analyze` clean, release APK builds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
2026-08-28 14:46:50 +02:00
KRZYSZTOF RUDNICKI
0a0fe88b09 Bump sync_settings_ui to v0.4.0, fixing Connect Firebase hanging forever
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
connectWithPassword() had no try/catch around firebaseFactory(), unlike
connectWithGoogle() right next to it. A thrown FirebaseAuthError (wrong
password, 400 from identitytoolkit) escaped uncaught out of the button's
async onPressed, so the screen sat on "Signing in..." forever with no
error shown -- reproduced live via the browser console. Fixed upstream in
kuhyx/utils (sync_settings_ui-v0.4.0), which also required moving
crdt_sync onto v0.11.0. Verified: wrong password now shows
"Firebase rejected that account: <reason>" and re-enables the button
instead of hanging.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-25 14:46:46 +02:00
59ee407d80 Adopt the four markdown namespaces and gate them
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
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:54:35 +02:00
fca5deebe6 Add dopamine-ux session prompt to prompts/
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Copy of the canonical prompt in ~/utils/dopamine-ux/, distributed here so the
repo can be opened and the prompt run directly. Derived from a survey of this
repo plus "The Dopamine Architecture" (london-post.co.uk).

Each prompt states its own completion protocol: when finished, the session
deletes this copy in the implementation commit and logs a DONE row in
~/utils/dopamine-ux/00-INDEX.md. Partial completion is never recorded as done.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdApz7EZ1zDfvtvvi1idDv
2026-08-16 21:12:52 +02:00
f46b65750e Use the shared github_device_auth package
Some checks failed
file-length / file length <= 250 lines (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
todo was the donor for both halves of the GitHub device flow -- its
`lib/sync/github_device_auth.dart` was byte-identical to home_inventory's,
and `lib/ui/device_code_dialog.dart` was the public version of the modal
diet-guard and wake-alarm each carried privately. All of it now lives in
`github_device_auth` in the kuhyx/utils monorepo.

The package is not a straight copy of what left here: it takes the more
capable variant of each divergence found across the four apps, so this app
gains two fixes it never had --

  * `pollForToken` retries a transient `ClientException` instead of
    failing, because GitHub can close the connection at the moment the
    user authorizes (wake-alarm's fix)
  * the dialog catches `on Object`, not `on Exception`, so an
    `ArgumentError` surfaces as an error message instead of leaving the
    dialog spinning forever

The local `test/github_device_auth_test.dart` is deleted; its cases live in
the package now, where they gate all four consumers. 300 tests pass and
`test/github_mirror_device_flow_test.dart` -- which drives the shared
dialog end to end -- is among them.

Deployed to the phone as versionCode 17 via phone_deploy.sh (signer
verified, `adb install -r`, no uninstall): launches with all 109 notes
intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U4Rj3s7K2qzrLNgJqqPhno
2026-08-15 16:15:10 +02:00
f4272d01b9 Route todo's snackbars through the shared showToast/showError
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
The package shipped feedback helpers while todo still had a private
_showSnack and a raw SnackBar in note_editor -- exactly the duplication
the extraction was meant to end, left live in a repo that had just
adopted the package.

Two deliberate behaviour changes come with it: the duration goes 2s ->
3s for toasts and 5s for errors (an error the user missed is an error
they will hit again), and messages now replace rather than queue, since
a queued snackbar describes an action the user moved on from seconds
ago. No test asserted on either, and the one snackbar assertion in the
suite checks that capture is *not* interrupted by one, which still holds.

Failures now read as failures: the mode-switch rejection in note_editor
and a failed sync in capture_screen use showError, while a successful
sync and the not-configured hint stay neutral toasts. Spending the danger
hue on routine confirmations is how a real failure becomes invisible.

Verified: flutter analyze clean, 308 tests pass at 100% coverage
(1964/1964). The release APK built and installed, but on-device
re-verification could not complete: the 23:00 night curfew removed the
package ~80ms after install, because dev.kuhy.todo is in the focus-mode
day WHITELIST but not NIGHT_WHITELIST. Enforcer working as designed, not
an app fault -- reverify after 05:00.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XS1m7b1piBtAL22USLW6jL
2026-08-14 23:09:24 +02:00
e6e1052c59 Adopt the shared design_system package, dropping theme.dart and confirm.dart
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Both files were donors for the new package rather than casualties of it:
theme.dart was byte-identical to untools' copy (md5 6cf8e82d), and
confirm.dart's confirmDestructive() was the ready-made pattern for the
~35 AlertDialogs inlined across the fleet. Keeping local copies after
promoting them upstream is exactly how the two would drift apart again,
so they are deleted and imported from design_system-v0.1.0 instead.

test/confirm_test.dart moves upstream with its subject -- the package
covers that behaviour (Escape and the barrier both refuse, Cancel is
autofocused so a reflexive Return cannot delete) as part of its own
100% gate. test/theme_test.dart stays as a consumer-side smoke check
that the dependency actually resolves to the frozen palette.

Verified: flutter analyze clean (bar one pre-existing bin/ style info),
308 tests pass at 100% coverage (1962/1962 -- the drop from 2005 is
exactly the promoted lines), and the release APK is installed and
running on the Pixel 6a with all 108 notes intact, the gold #B8862E
focus ring rendering from the package's inputDecorationTheme, and no
crashes in logcat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XS1m7b1piBtAL22USLW6jL
2026-08-14 22:59:59 +02:00
85a45daaee Share one note form between capture and the detail screen
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
Editing an existing note looked nothing like capturing a new one: it
always showed the template dropdown, the View/Guided/Raw pills and the
priority/status row. The cause was one missing argument -- both screens
already embedded the same NoteEditor, but NoteDetailScreen never passed
advancedMode, which defaults to true in the widget while the app-wide
AppSettings.advancedMode defaults to false.

Extract the shared form as three single-purpose components rather than
one widget owning several jobs:

- note_meta_row.dart renders the priority/status row (was
  capture_meta_row.dart; drops the private _MetaDropdown duplicate the
  detail screen carried, which used DropdownButtonFormField where the
  shared one uses DropdownButton).
- note_form_chrome_gate.dart owns chrome visibility and nothing else,
  replacing the _chromeVisible flag both screens copy-pasted.
- note_form.dart composes those two with NoteEditor.

appSettings is now a required parameter through NotesListScreen and
NoteDetailScreen. Required, not defaulted: a silent default is exactly
what caused this, so it must not be reintroducible.

Also in the capture app bar: drop the decorative Icons.list_alt_outlined
beside the note count, since the adjacent Icons.list button already names
what is counted, and move the count to the right of that button. The
finalize-and-clear action becomes Icons.check instead of Icons.add -- it
never saved anything (every keystroke is already persisted), so a plus
sign misdescribed it.

314 tests, 100% line coverage. Verified in a running web build that edit
and capture render identically with advanced mode both off and on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 17:48:39 +02:00
4e66de5467 Exclude platform dirs from analysis; refresh the lockfile
Some checks are pending
file-length / file length <= 250 lines (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
analysis_options.yaml: keep the analyzer out of android/ios/web/windows/
macos/linux. A no-op today — those directories hold no Dart (checked: 0
files each, and `flutter analyze` reports the same two pre-existing infos
either way) — but it stops generated platform Dart from ever entering the
lint surface.

pubspec.lock: transitive bumps (matcher 0.12.19->0.12.20, test_api,
leak_tracker and friends) picked up by the `flutter pub get` runs during
the file-cap refactor. No direct dependency changed.

Both had sat uncommitted through that refactor because I misattributed
them to kuhy and deliberately left them unstaged; they are this machine's
work, so they belong in the history.

Verified: flutter analyze clean (same 2 pre-existing infos), 313 tests
pass, file-length gate exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 17:20:51 +02:00
34b2c1c87c Fix three gate defects found reviewing the cap refactor
Moving bump_patch_version.sh into .pre-commit-config.yaml introduced three
bugs, all reproduced in clean clones of HEAD before fixing and re-verified
after:

1. DATA LOSS. pre-commit stashes unstaged changes and restores them with
   `git apply`. The bump rewrites pubspec.yaml inside that window, so an
   uncommitted edit near the `version:` line made the restore conflict:
   the commit aborted AND the developer's edit vanished from the working
   tree, recoverable only from $PRE_COMMIT_HOME. Only non-mutating checks
   belong in that file.

2. A commit rejected by the file-length gate still had its version bumped
   and staged, riding into the next successful commit. `always_run: true`
   with no `fail_fast` meant "ordered last" did not mean "runs last only
   on success" -- which is exactly what the comment above it claimed.

3. `files: ^pubspec\.yaml$` narrowed the crdt_sync override check from
   unconditional to staged-only, so a poisoned pubspec arriving by pull or
   merge was invisible to every later commit that did not touch pubspec.
   The hand-written hook this replaced checked it every time; that
   narrowing quietly widened the hole the check exists to close.

The fix keeps the framework for the read-only gates and puts the mutation
back outside it: install_hooks.sh now writes a wrapper that runs
pre-commit's generated hook first and bump_patch_version.sh only if it
passed. Plus fail_fast, and always_run on the crdt check.

Also fixes three stale references this refactor created: a load-bearing
layout comment citing the renamed _buildRaw, bin/todo_desktop.dart naming
two split-away test files, and CLAUDE.md giving two different suite
runtimes.

Verified: unstaged pubspec edit preserved through a commit; a rejected
commit leaves nothing staged; a merged-in override is caught on an
unrelated commit. 313 tests, 100% coverage, gate exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 17:13:31 +02:00
5856617cbf Mark refactor_claude_todo.md done for this repo
The survey counts at the top described a state that no longer exists, so the
file contradicted the repo it lives in. The counts stay as the record of what
was refactored; a banner above them states the outcome and how it was
verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:51:07 +02:00
25e2c9bb20 Split CLAUDE.md and the performance doc; the whole repo is under the cap
CLAUDE.md 925 -> 165. 789 of those lines were a verbatim paste of upstream's
flutter/flutter rules.md -- 85% of an always-loaded file given over to a
document nobody edits. It moves to five topic files under docs/, linked
rather than @-imported: inlining it back would pass the cap while keeping it
in context on every turn, which is the cost the cap exists to remove.

Every `###`/`####` heading in the upstream block survives the split; the two
that do not are its own wrapper headings, replaced by the per-file titles
(verified by diffing the sorted heading lists).

desktop-performance-findings.md 278 -> 73 + 216. It was already narratively
two-part; the author signposted "Part 2" explicitly at the seam.

Also updates two things that had drifted: the suite is 313 tests at 100%
coverage (not 238), and Conventions now documents the cap, where the gate
lives, and the two patterns used to split without breaking callers
(re-export barrels, and `part` files where a private State must stay in one
library).

`bash ~/utils/scripts/check_file_length.sh --all` now exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:46:54 +02:00
95c358cb5c Split sync_service_test.dart; all code and tests now under the cap
356 -> sync_service_test (133) + sync_service_revision_test (96) +
sync_service_fakes (163).

The underscores came off every shared fake: Dart privacy is library-scoped,
so `_FakeRemote` declared in one file is invisible to the next. `_peerLog`
became `buildPeerLog` rather than `peerLog`, because two tests declare a
local variable of that name and the helper would have been shadowed by its
own caller.

The `_revisionTrackingTests()` indirection is gone -- that function existed
only to keep main() readable, which is now the file boundary's job.

Verified: 313 tests pass, 100% coverage. Only CLAUDE.md and
docs/desktop-performance-findings.md remain over 250 lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:44:32 +02:00
10a546f6d6 Split nine oversized test files under the cap
Every split is by test-group/concern, with the shared setup moved to a
sibling helper the splits import. All 313 tests are preserved -- the count
is unchanged and `test('…')` names are byte-identical to the originals
(verified by diffing the sorted name lists).

  note_repository_test      677 -> 4 files + note_repository_helpers
  capture_screen_test       660 -> 4 files + capture_screen_harness
  note_editor_test          502 -> 3 files + note_editor_harness
  port_guard_test           405 -> 3 files + port_guard_fakes
  wrapper_server_test       402 -> 2 files + wrapper_server_harness
  settings_screen_test      391 -> 2 files + settings_screen_harness
  github_mirror_screen_test 365 -> 2 files + github_mirror_harness
  notes_list_screen_test    347 -> 2 files + notes_list_harness
  analytics_service_test    300 -> 2 files + analytics_helpers

Helpers are deliberately NOT named `*_test.dart`; the runner would collect
them and fail on the missing `main()`.

Three things did not survive a mechanical split and were handled explicitly:

  * wrapper_server's `enabledOrigin` closes over the `late root` that setUp
    assigns, so a top-level function cannot see it. It is now a
    WrapperHarness class with an install() that registers the setUp/tearDown
    pair -- the only structural change in this commit.
  * note_repository's splits each repeat `setUpAll(sqfliteFfiInit)`, and
    analytics' each repeat `@TestOn('vm')`. Omitting either fails quietly
    (uninitialised sqflite / wrong platform) rather than loudly.
  * capture_screen's `recordingMock` was declared mid-file between two
    tests, so it moved to the harness rather than being dropped.

Verified: 313 tests pass (same as before the refactor), 100% coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:42:13 +02:00
d145f92a07 Split note_repository.dart, taking coverage to 100%
429 -> 249 lines:

  note_query.dart   NoteSort, NoteFilter, and the pure predicates behind
                    them (matchesFilter, comparatorFor). All of it takes
                    notes and a filter and returns a decision, which is what
                    made it the natural seam.
  note_import.dart  ImportOutcome and the in-memory LogPersistence used by
                    openInMemory.

note_repository.dart re-exports both, so no call site changed.

Also drops ImportOutcome.total, which nothing called. It was uncovered
before this split too -- the larger file's percentage hid it. With it gone
the suite reports 100% line coverage (2005 lines), up from the 99.9%
baseline measured at the start of this refactor.

lib/ and bin/ are now fully under the cap. 313 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:32:06 +02:00
381afd1d77 Split the last three oversized lib/bin files
github_mirror_screen.dart  368 -> 232 + device_code_dialog.dart (109) and
                             a widget part
  wrapper_server.dart        336 -> 223 + wrapper_sync_routes.dart, the two
                             credential-provisioning routes
  bin/todo_desktop.dart      253 -> 199 + lib/desktop/browser_launcher.dart

The fixed port moved into browser_launcher.dart as kWrapperPort, with the
entry point aliasing it, so the "must not move" reason is stated once next
to the code that acts on it.

lib/ and bin/ are now entirely under the cap. The two remaining analyze
infos (bin/todo_desktop.dart cascade_invocations,
capture_screen_sync.dart unnecessary_unawaited) are pre-existing --
verified by analyzing e0eb8b9 in a worktree, which reports the same two.

Verified: 313 tests pass, coverage unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-14 16:29:35 +02:00