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
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (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.

Includes the patch bump this repo's bump-patch-version hook applies to
app/pubspec.yaml on every commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
This commit is contained in:
Krzysztof Rudnicki 2026-08-24 15:57:20 +02:00
parent 9bfc14684e
commit 03e14e17f3
20 changed files with 96 additions and 165 deletions

38
.github/workflows/md-naming.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: md-naming
# The four markdown namespaces, enforced on push as well as at commit time.
# The local pre-commit hook can be skipped (a fresh clone that never ran
# `pre-commit install` has no hooks at all), so the convention needs a second
# gate that no one can forget to install.
#
# Deliberately a SEPARATE workflow from any release job: a badly named doc
# should fail lint, not block publishing an artifact.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
md-naming:
name: markdown naming convention
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The gate and its exemption list live in the shared utils repo so every
# repo enforces the same namespaces. Checking it out beats vendoring a
# copy here, which is exactly how a gate and a survey start disagreeing.
- name: Check out the shared gate
uses: actions/checkout@v4
with:
repository: kuhyx/utils
path: .utils
- name: Enforce the naming convention
run: bash .utils/scripts/check_md_naming.sh --all
# Warns only: no machine can prove a task is finished, so an old TODO
# gets a nudge, never a failure.
- name: Warn about stale TODOs
run: PYTHONPATH=.utils python3 .utils/md_naming/stale.py

View File

@ -195,3 +195,13 @@ repos:
language: system
pass_filenames: false
files: ^diet_guard/
# Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the
# 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
# one repo's idea of a valid name cannot drift from every other repo's.
- id: md-naming
name: markdown naming convention
entry: scripts/check_md_naming.sh
language: system
files: \.(md|markdown)$

View File

@ -11,7 +11,7 @@ streaks, a YTD tally and weekly/monthly averages, all derived from
`_daystatus.py` / `day_status_service.dart` and `_averages.py` /
`average_service.dart`.
`docs/design.md` holds the original spec (slot timing, the Tue/Wed/Thu
`docs/DOCS-design.md` holds the original spec (slot timing, the Tue/Wed/Thu
catch-up rule).
## Commands
@ -75,7 +75,7 @@ every idle tick to approximate the two moments state actually changes here.
- **Interactive paths must not block, and stale peers cost real time.** The
threading rules, the interactive timeout, and `prune-peers` (including the
`revs/` marker trap that made pruning *slower*) are in
[docs/sync-latency.md](docs/sync-latency.md) — read it before touching
[docs/DOCS-sync-latency.md](docs/DOCS-sync-latency.md) — read it before touching
`_sync_refresh.py`, the fetch button, or the publish paths.
- Every test that logs a meal would otherwise hit the network through these new
call sites; `conftest._isolate_state` patches each one
@ -159,7 +159,7 @@ reach the running service.
`app/` builds for **Android** and **web** only. There is no `app/linux/` —
Flutter's GTK embedder manages ~20fps at 3840x2160 where the same Dart in
Chrome sustains ~144fps (`~/todo/docs/desktop-performance-findings.md`). The
Chrome sustains ~144fps (`~/todo/docs/DOCS-desktop-performance-findings.md`). The
desktop app is the web build served by `bin/diet_guard_desktop.dart` in a
Chrome `--app` window.
@ -184,7 +184,7 @@ many meals fall between them, evenly divided. Default `(8, 20, 4)` is the
old hardcoded 08/12/16/20. Four load-bearing rules (integer-only
arithmetic, whole-hour slots, the count clamp, the `last + 2h` cutoff), the
forward-only history, and the two edit surfaces are documented in
[docs/meal-schedule.md](docs/meal-schedule.md) — read it before touching
[docs/DOCS-meal-schedule.md](docs/DOCS-meal-schedule.md) — read it before touching
`_slots.py` / `slot.dart`.
## Averages (`_averages.py` / `average_service.dart`)
@ -207,7 +207,7 @@ phone show both.
## Do NOT
- Don't relax the meal-slot logic without re-reading `docs/design.md`. The
- Don't relax the meal-slot logic without re-reading `docs/DOCS-design.md`. The
off-hours clamp in `slot_for_log`/`slotForLog` (before the first slot → the
first slot, after the enforcement window → the last slot) must stay
byte-identical across Python and Dart; do NOT widen `elapsed_slots`, which
@ -228,7 +228,7 @@ phone show both.
- Don't let the catering import log unattended (delivered ≠ eaten), don't let
its Python and Dart halves drift (one shared fixture gates them), and don't
make the synced password machine-local — the phone could not then fetch.
[docs](docs/kuchnia-wikinga.md), [phone](docs/kuchnia-wikinga-phone.md).
[docs](docs/DOCS-kuchnia-wikinga.md), [phone](docs/DOCS-kuchnia-wikinga-phone.md).
- Don't add a dependency without the production install-path check above.
- Don't reintroduce a seal/`chattr +i` on the budget file.
- Don't re-add a Linux embedder target, and don't change the wrapper's port or
@ -243,8 +243,8 @@ _Vendored from [flutter/flutter docs/rules/rules.md](https://github.com/flutter/
via `~/.claude/CLAUDE.md` Flutter AI tooling setup. Re-fetch periodically.
Split across five files to stay under the 250-line cap._
- [Language, style and architecture](docs/flutter-rules-language.md)
- [Architecture, lint rules, state and data](docs/flutter-rules-state-and-data.md)
- [Code generation, testing and theming](docs/flutter-rules-testing-and-assets.md)
- [UI: theming, layout and overlays](docs/flutter-rules-ui.md)
- [Colour, type, documentation and accessibility](docs/flutter-rules-design.md)
- [Language, style and architecture](docs/DOCS-flutter-rules-language.md)
- [Architecture, lint rules, state and data](docs/DOCS-flutter-rules-state-and-data.md)
- [Code generation, testing and theming](docs/DOCS-flutter-rules-testing-and-assets.md)
- [UI: theming, layout and overlays](docs/DOCS-flutter-rules-ui.md)
- [Colour, type, documentation and accessibility](docs/DOCS-flutter-rules-design.md)

View File

@ -1,151 +0,0 @@
# Design audit — diet-guard
Generated against safe-design-rules (anthonyhobday.com/sideprojects/saferules).
Report only — nothing in this repo was changed by the audit itself.
This repo has two UI surfaces, audited separately below.
---
## Flutter app (`app/`)
Theme entry point: `app/lib/main.dart:52` —
`ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true)`. This is the
**entire** theme definition: no `darkTheme`, no `textTheme` override, no
component themes (`elevatedButtonTheme`, `cardTheme`, etc.), no
`ThemeExtension`. Everything downstream of this one line either inherits
Material 3's generated defaults or bypasses the theme with raw `Colors.*`
literals in individual widgets — sampled across `screens/` and `widgets/`
(11 of 21 files).
### Violations
- **Rule 1 (near-black/near-white)** — `app/lib/widgets/day_status_calendar.dart:79` uses `Colors.black` for the "not logged" cell fill, and `:87`/`:121` use pure `Colors.white` for text — both bypass the muted near-black/near-white the seeded `ColorScheme` would otherwise supply.
- **Rule 2 (saturate neutrals)** — `app/lib/widgets/day_status_calendar.dart:78,104` (`Colors.grey.shade800`) and `app/lib/widgets/slot_selector_row.dart:49` (`Colors.grey`) use Material's generic, zero-chroma grey instead of a grey tinted toward the teal seed.
- **Rule 4 (everything deliberate)** — no centralized token beyond the one-line `ThemeData` call; `day_status_calendar.dart` (8 raw `Colors.*` refs), `slot_selector_row.dart` (4 raw refs, lines 46-49), and `settings_screen.dart:567` each invent status/error colors independently instead of reading `Theme.of(context).colorScheme`.
- **Rule 4 (inconsistency, concrete pair)** — the same semantic "error" state is styled two different ways in the same app: `history_screen.dart:603` uses `Theme.of(context).colorScheme.error`, but `settings_screen.dart:567` hardcodes `Colors.red` for the same purpose.
- **Rule 9 (distinct brightness values)** — `day_status_calendar.dart:80-82` mixes `Colors.red.shade400` (lighter) with `Colors.amber.shade700` / `Colors.green.shade700` (darker) for three parallel status swatches meant to read as one family — inconsistent shade selection, not a deliberate brightness ramp.
- **Rule 11 (mathematically related measurements)** — corner radii are inconsistent across near-identical use cases: `BorderRadius.circular(8)` in `day_status_calendar.dart:105` and `photo_attach_field.dart:95` vs `BorderRadius.circular(6)` in `history_screen.dart:638` for the same "thumbnail clip" purpose, with no shared base unit.
- **Rule 20 (body text ≥16px)** — `day_status_calendar.dart:123` (`fontSize: 14`), `:146` (`fontSize: 11`), `:181` (`fontSize: 12`) are all below the 16px minimum, with no larger default applied elsewhere to compensate for this widget.
- **Rule 28 (icon contrast lower than text)** — `slot_selector_row.dart:54` (`Icon(..., color: color)`) and `:57` (`labelStyle: TextStyle(color: color)`) give the check icon and its adjacent label text the *same* full-strength color — the icon should read lighter/lower-contrast than the text, not equal.
### Passes worth recording
- **Rule 3** — clear hierarchy exists elsewhere: `history_screen.dart:603` correctly pulls the delete/error action from `colorScheme.error`.
- **Rule 18 (container brightness limits)** — only one nested-container case was found (`day_status_calendar.dart`'s `grey.shade800` panel on the default light scaffold); no evidence of a violation.
- **Rule 23 (two typefaces max)** — no `fontFamily` or `google_fonts` import anywhere in `app/lib/` — the whole app uses exactly one (system default) typeface.
- **Rule 26 / 27 (shadows/depth)** — no `BoxShadow`, `elevation:`, or `Card(` usage found in the sampled files; zero depth technique used, so it's trivially consistent (though see Notes — this also means rule 15/16/17 have nothing to evaluate).
- **Rule 28 (partial pass)** — `day_status_calendar.dart:113,127` (`Colors.white70` chevron icons) sit next to `:121` (`Colors.white` full-opacity month label) — here the icon *is* correctly lower-contrast than the text.
### Not applicable
- Rule 5 (optical alignment) — no asymmetric icon/shape widgets found requiring hand-tuned centering.
- Rule 6 (letter-spacing/line-height by size) — no manual `letterSpacing`/`height` found anywhere in `screens/`/`widgets/`; the app relies entirely on Material 3's built-in type scale, which already encodes this rule.
- Rule 7 (container border contrast) — the one border found (`day_status_calendar.dart:172-174`, `Colors.white70` on `Colors.black`) has adequate contrast against both surfaces; no counter-evidence.
- Rule 8 (everything aligns) — default `MainAxisAlignment.spaceBetween`/`spaceAround` usage throughout; no counter-evidence gathered.
- Rule 10 (warm or cool, not both) — the app doesn't saturate its neutrals at all (see Rule 2 violation above), so warm/cool consistency has nothing to check.
- Rule 12 (order by visual weight) — no clear violation or pass identified from the sampled files; would need a full-screen layout review.
- Rule 13 (12-column grid) — mobile-first single-column screens; no grid system in use.
- Rule 14 (space between high-contrast points) — insufficient evidence gathered from the sample.
- Rule 15 (closer = lighter) — no elevation/z-stacking found to evaluate (see Rule 26/27 pass note).
- Rule 16 (shadow blur ≈ 2× distance) — no `BoxShadow` in the codebase.
- Rule 17 (simple on complex) — flat design throughout; no complex background/foreground pairing to break the rule.
- Rule 19 (outer padding ≥ inner padding) — holds everywhere checked, e.g. `day_status_calendar.dart:102` (`EdgeInsets.all(12)` outer) vs `:168` (`EdgeInsets.symmetric(vertical: 2)` cell margin).
- Rule 21 (line length ~70 chars) — no long-form body text in the sampled screens (mostly labels/numbers).
- Rule 22 (button padding h=2×v) — no explicit `ButtonStyle.padding` override found anywhere; buttons run on Material 3 defaults, which already approximate this ratio.
- Rule 24 (nest corners) — no nested corner-radius container pairs found to check a concentric relationship.
- Rule 25 (avoid adjacent hard divides) — insufficient evidence gathered from the sample.
### Notes
- The theme is effectively unstyled beyond a seed color. Every rule above that reads "no evidence"/"insufficient evidence" is really a symptom of the same root cause: there is no `theme.dart`, no `ThemeExtension` for semantic colors (success/warn/danger), and no component themes, so status colors (green/red/amber/grey) get re-invented per-widget instead of being defined once. Centralizing those into a `ThemeExtension` (e.g. `DietGuardStatusColors`) would fix the Rule 4/9/28 violations above in one pass and make Rule 15/16/17/18/26/27 actually checkable against a real design system instead of "nothing to evaluate."
- `day_status_calendar.dart`'s status colors (`red.shade400`/`amber.shade700`/`green.shade700`) do not literally match the Python gatelock's calendar palette (`#ff5555`/`#e0c33c`/`#33cc66` — see Python section below) even though the widget's own doc comment (`day_status_calendar.dart:9`) says it "mirrors" that view. Worth reconciling into one shared value if visual parity between the phone app and the PC lock screen actually matters.
---
## Python/Tkinter gatelock UI (`diet_guard/`, not `app/`)
Files audited: `_gatelock_ui.py` (474 lines), `_gatelock_mealflow.py` (349),
`_gatelock_calendar.py` (466), `_gatelock_core.py` (196),
`_gatelock_nutrition.py` (230), `_gatelock.py` (211), `_gatelock_support.py`
(82), `_calendar_view.py` (151).
**Correction to prior research going in:** there *is* a color-constants block
(`_gatelock_ui.py:27-32`: `BG`, `FG`, `_ACCENT`, `ERR`, `_FIELD_BG`,
`_MUTED`), so "no color/theme constants module" is not quite right — the
real problem is worse than absence: it's **triplication**. `_gatelock_ui.py`
is the closest thing to a canonical module, but `_gatelock_calendar.py:58-60`
and `_calendar_view.py:25-33` each **redefine their own copies** of the same
values instead of importing them (see Violations below). `_gatelock_calendar.py:55-57`
even has a comment justifying this: *"BG/FG/ERR come from `_gatelock_ui`'s
public exports; the rest are private to that module, so equivalents are
defined locally rather than reaching across the boundary."* That's a
structural decision (avoid touching another module's private names) that
produces exactly the drift risk a shared module is supposed to prevent.
Confirmed **no `ttk.Style()` call anywhere** in the 8 files (0 grep hits).
Hex-literal count is also off from the prior estimate: **25 occurrences of
12 distinct hex values** (not ~4) across the 3 files that define palette
constants. Inline `font=` literal count is confirmed accurate: **35
occurrences**.
### The shared `LockConfig.bg` token
`~/utils/gatelock/gatelock/_window.py:71` — `LockConfig.bg: str = "#1a1a1a"`
is the one real shared design token in this family (used by `screen-locker`,
`wake_alarm`, and `diet_guard` alike via `GateRoot`/`LockWindow`).
- `diet_guard/_gatelock_ui.py:27` — `BG = "#1a1a1a"` **re-hardcodes the same
literal instead of importing `gatelock.LockConfig.bg`**. It happens to
match today, but nothing keeps it in sync — if `LockConfig`'s default ever
changes, this constant silently goes stale.
- `diet_guard/_gatelock.py:147` — `LockConfig(mode=..., bg=BG)` then passes
that duplicated literal back into `LockConfig` explicitly. This call is
redundant today (it matches `LockConfig`'s own default) and masks the
duplication: it *looks* like `diet_guard` is deliberately overriding the
background, when it's actually just echoing the default via a second,
unlinked constant.
- Fix direction (report-only, not applied): `_gatelock_ui.py` should import
`LockConfig.bg` (or a `LockConfig()` default instance's `.bg`) rather than
defining its own `BG` literal, and `_gatelock.py:147` should drop the
explicit `bg=BG` kwarg entirely once it's provably redundant.
### Violations
- **Rule 1 (near-black/near-white)** — `_calendar_view.py:25` `_NOT_LOGGED_FILL = "#000000"` is pure black; `_gatelock_ui.py:434` and `:459` (`fg="white"`) are pure white. This is inconsistent with the rest of the palette, which correctly uses `#1a1a1a`/`#e0e0e0` (near-black/near-white, not pure).
- **Rule 2 (saturate neutrals)** — every neutral in the shared palette is a zero-chroma grey (equal R/G/B hex pairs): `BG "#1a1a1a"`, `_FIELD_BG "#2a2a2a"`, `_MUTED "#9a9a9a"` (`_gatelock_ui.py:27,31,32`), plus the duplicate `LockConfig.bg = "#1a1a1a"` (`gatelock/_window.py:71`). None carry any tint — exactly the "generic gray" this rule warns against.
- **Rule 4 (everything deliberate / centralization)** — `_MUTED = "#9a9a9a"` is defined **three times** verbatim: `_gatelock_ui.py:32`, `_gatelock_calendar.py:58`, `_calendar_view.py:33`. `"#003322"` (status text color) is likewise defined 3×: `_gatelock_ui.py:191,421`, `_gatelock_calendar.py:154`, `_calendar_view.py:32`. `_FIELD_BG "#2a2a2a"` and `_ACCENT "#00ff88"`/`"#00cc66"` are each duplicated across `_gatelock_ui.py` and `_gatelock_calendar.py`. No `ttk.Style()` centralizes any of this.
- **Rule 11 (mathematically related measurements)** — font sizes in use: 10, 11, 12, 13, 14, 15, 16, 22, 30 (`_gatelock_ui.py` and `_gatelock_calendar.py`, e.g. `_gatelock_ui.py:130,145,161,285,371,378`) — no common ratio or base unit. Spacing is similarly mixed: `ipady=3` (`_gatelock_ui.py:147,216`) vs `ipady=2` (`:256,330`; `_gatelock_calendar.py:148`) used interchangeably for the same "entry field" role.
- **Rule 20 (body text ≥16px)** — the dominant font size is 12pt (9 occurrences in `_gatelock_ui.py` alone: lines 161,185,209,225,249,260,334,343,... ) with many labels at 11-13pt; only headline-style text (22, 30) clears a 16px-equivalent minimum.
### Passes worth recording
- **Rule 3 (high contrast for important elements)** — `_gatelock_ui.py:416-425` ("Log & Continue", the primary action) uses bright `_ACCENT #00ff88`; `:429-438` ("Fetch from sync", secondary) uses a muted `#334455`/`#445566` pair. Clear, deliberate hierarchy.
- **Rule 9 (distinct brightness values)** — `_calendar_view.py:28-30`: `GREEN #33cc66` / `YELLOW #e0c33c` / `RED #ff5555` are visually distinct hues at roughly matched brightness — better-executed than the Flutter app's equivalent (see Flutter Rule 9 violation above).
- **Rule 18 (container brightness limits)** — `BG "#1a1a1a"` (~10% luma) vs `_FIELD_BG "#2a2a2a"` (~16% luma), a ~6% delta — comfortably within the ~12% dark-interface guideline (`_gatelock_ui.py:27,31`).
- **Rule 19 (outer padding ≥ inner padding)** — outer frame padding (e.g. `padx=6` at `_gatelock_ui.py:144`) is consistently ≥ field-level `ipady=2-3`.
- **Rule 23 (two typefaces max)** — exactly two families used: "Arial" (34 occurrences) and "Courier" (1 occurrence, `_gatelock_ui.py:292`).
- **Rule 26 / 27 (no shadows / single depth technique)** — `relief=` (Tk's raised/sunken/groove bevel, its closest thing to a shadow) is never used in any of the 8 files (0 grep hits); zero depth technique applied, trivially consistent.
### Not applicable
- Rule 5 (optical alignment) — no asymmetric icon/shape widgets in this Tk UI.
- Rule 6 (letter-spacing/line-height by size) — Tk's `font=` tuples have no letter-spacing control and no independent line-height axis; not expressible in this toolkit.
- Rule 7 (container border contrast) — `highlightthickness` varies 0/1/2 (`_gatelock_ui.py:174,193,229`; `_gatelock_calendar.py:235`) but the one case with a matched-to-background `highlightbackground` (`_calendar_view.py:108-115`, future-day cells) is intentional per its own docstring (`day_status_calendar.dart:9`-equivalent design: blend future days into the background), not a rule violation.
- Rule 8 (everything aligns) — grid/pack-based layout; no counter-evidence gathered.
- Rule 10 (warm or cool, not both) — the neutrals aren't saturated at all (Rule 2 violation above), so there's no warm/cool choice to be consistent about.
- Rule 12 (order by visual weight) — not assessed from the sampled files.
- Rule 13 (12-column grid) — fixed-size fullscreen dialog, not a column-grid layout.
- Rule 14 (space between high-contrast points) — insufficient evidence gathered.
- Rule 15 (closer = lighter) — Tk has no z-stacking/elevation concept; nothing to evaluate.
- Rule 16 (shadow blur ≈ 2× distance) — Tk has no drop-shadow primitive; unused.
- Rule 17 (simple on complex) — flat single-color backgrounds throughout; no complex imagery to conflict with.
- Rule 21 (line length ~70 chars) — `wraplength=900` (pixels, e.g. `_gatelock_ui.py:410,446`) is a pixel wrap, not a character-count guarantee; can't verify without rendering.
- Rule 22 (button padding h=2×v) — no `tk.Button` in the sampled files sets explicit `ipadx`/`ipady`; only `Entry` widgets do (grep-confirmed). Buttons run on Tk's own default padding, unexamined by this rule.
- Rule 24 (nest corners) — Tk widgets here are flat-relief rectangles; no corner-radius concept applies.
- Rule 25 (avoid adjacent hard divides) — insufficient evidence gathered.
- Rule 28 (icon contrast lower than text) — this UI uses unicode glyphs embedded in button `text=` (e.g. `"⟳ Fetch from sync"` at `_gatelock_ui.py:431`, `"✕ Close Demo"` at `:456`) rather than a separately-styled icon widget; there's no independent icon-vs-text contrast axis to check.
### Notes
- The real fix for the Python surface's Rule 4 violations is structural: promote `_gatelock_ui.py`'s 6 constants (plus `_calendar_view.py`'s status-color dict) into one importable module that every gatelock file reads from, and have that module import `gatelock.LockConfig`'s `bg` default rather than re-literal-ing `"#1a1a1a"`. That single change collapses 3 duplicate `_MUTED`/`"#003322"` definitions and closes the `LockConfig.bg` drift risk in one pass.
- Because `LockConfig.bg = "#1a1a1a"` is shared by `screen-locker` and `wake_alarm` too, if those repos independently re-hardcode `"#1a1a1a"` the same way `diet_guard` does, this is a family-wide pattern, not a `diet_guard`-only one — worth a follow-up grep across those repos if/when this gets fixed.

View File

@ -96,4 +96,4 @@ python -m venv .venv && .venv/bin/pip install -r requirements.txt
```
See `CLAUDE.md` for scheduling details and production deployment gotchas,
and `docs/design.md` for the original feature spec.
and `docs/DOCS-design.md` for the original feature spec.

View File

@ -13,7 +13,7 @@ desktop, sharing one food log through GitHub-backed sync.
There is no `linux/` directory. Flutter's GTK embedder manages ~20fps at
3840x2160 -- measured on a stock `flutter create` counter app, so it is the
toolkit rather than this code -- where the same Dart rendered by Chrome
sustains ~144fps (`~/todo/docs/desktop-performance-findings.md`). Running
sustains ~144fps (`~/todo/docs/DOCS-desktop-performance-findings.md`). Running
`flutter create --platforms linux` here would silently restore the slow path.
## Commands

View File

@ -2,7 +2,7 @@ name: diet_guard_app
description: "Companion phone app for diet_guard: log meals on the go."
publish_to: 'none'
version: 1.0.69+1
version: 1.0.70+1
environment:
sdk: ^3.12.2

View File

@ -204,3 +204,5 @@ whitelist entry before it can be tested.
- diet-guard state lives under XDG (`~/.local/share/diet_guard`), unlike
screen-locker's in-repo JSON. Tests isolate it via
`~/diet-guard/diet_guard/tests/conftest.py:97` `_isolate_state`.
REMOVE ME AFTER FINISH

32
scripts/check_md_naming.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# ============================================================================
# Fail if any markdown file breaks the shared naming convention.
#
# Thin delegate to the shared gate in ~/utils, which owns the four namespaces
# (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
# of a valid name drift from every other repo's -- so this script only locates
# the shared checker and forwards its arguments.
#
# Usage:
# scripts/check_md_naming.sh <file> [<file> ...] # pre-commit passes these
# scripts/check_md_naming.sh --all # whole tree, from cwd
# ============================================================================
set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_md_naming.sh"
main() {
if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared markdown-naming gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2
exit 1
fi
exec bash "$SHARED_GATE" "$@"
}
main "$@"