diff --git a/TODO-deps-01-mechanical-bumps.md b/TODO-deps-01-mechanical-bumps.md new file mode 100644 index 0000000..33a6067 --- /dev/null +++ b/TODO-deps-01-mechanical-bumps.md @@ -0,0 +1,127 @@ +# TODO — utils deps, session 1 of 2: the mechanical bumps + +REMOVE ME AFTER FINISH + +Running order: **this file first**, then `TODO-deps-02-major-upgrades.md`. +Session 2 does not depend on this one having run, but doing this first shrinks +the gate output to just the three majors, so a failure in session 2 is +unambiguous. + +## Why this exists + +`~/src/utils` has been red on the **dependency freshness** CI workflow since +2026-09-11 — every run on `main` since commit `001670f` has failed. It is not +a broken build: upstream moved and the exact pins did not. Measured +2026-09-12 with `./scripts/check_dependency_freshness.sh --all` (exit 1, +29 findings across 9 sub-packages). + +This session does the **16 low-risk bumps only**. The three majors +(`very_good_analysis 11`, `vitest`/`@vitest/coverage-v8 5`, `pnpm 12`) are +session 2's job — do not touch them here, they need hand edits and a +separate blast-radius check. + +`~/src/utils` is a shared monorepo: 8 Flutter apps consume its Dart packages +(`home_inventory`, `kuhylog`, `lyricanki`, `punchme`, `todo`, `untools`, +`restaurant-rater`, `epopeja_karta`) and 4 TS repos consume `@kuhyx/ts-core` +/ `@kuhyx/web-ui` (`europe-county-map`, `iron-and-anvil`, +`awesome-mcp-explorer`, `konbini-67`). Breaking a pin here breaks twelve +downstream repos, which is why every sub-package's suite must go green before +you commit. + +## Read first + +- `rules/dependency-freshness.instructions.md` — exact pins, stable only, the + two allowlist classes, and **never `--no-verify`**. +- `dependency-freshness.allowlist.yaml` — top-of-file comment explains the two + entry classes. It already holds ONE active entry (`npm:typescript` pinned + 6.0.3, `blocked_by: transitive:typescript-eslint@8.70.0`). **Leave it + alone.** It is fleet-wide and correct; touching TypeScript is out of scope + for both sessions. +- `scripts/run_subproject_tests.sh` — pre-commit runs hooks from the git root, + but each sub-package's pytest config is relative to its own directory. This + wrapper supplies the missing `cd`. Use it; do not hand-roll one. + +## The 16 bumps + +Exact, with the file and line the gate named: + +| Ecosystem | Package | From → To | File | +|---|---|---|---| +| toolchain | node | 24.20.0 → 24.21.0 | `.github/workflows/ts-core-tests.yml:27` | +| toolchain | node | 24.20.0 → 24.21.0 | `.github/workflows/web-ui-tests.yml:27` | +| pypi | ruff | 0.16.5 → 0.16.7 | `crdt-sync/requirements.txt:11` | +| pypi | types-requests | 2.33.0.20260712 → 2.33.0.20260906 | `crdt-sync/requirements.txt:12` | +| pypi | ruff | 0.16.6 → 0.16.7 | `freedays/requirements.txt:10` | +| pypi | ruff | 0.16.5 → 0.16.7 | `gatelock/requirements.txt:10` | +| pub | test | 1.31.2 → 1.32.0 | `crdt_sync_dart/pubspec.yaml:17` | +| pub | flutter_secure_storage | 11.0.0 → 11.1.1 | `crdt_sync_flutter/pubspec.yaml:21` | +| npm | eslint | 10.9.1 → 10.10.0 | `ts_core/package.json:34` | +| npm | typescript-eslint | 8.68.0 → 8.70.0 | `ts_core/package.json:36` | +| npm | eslint | 10.9.1 → 10.10.0 | `web_ui/package.json:43` | +| npm | typescript-eslint | 8.68.0 → 8.70.0 | `web_ui/package.json:48` | +| npm | @testing-library/user-event | 14.6.6 → 14.6.7 | `web_ui/package.json:39` | +| npm | @types/react | 19.2.18 → 19.3.0 | `web_ui/package.json:40` | +| npm | @types/react-dom | 19.2.5 → 19.3.0 | `web_ui/package.json:41` | +| npm | react + react-dom | 19.2.8 → 19.3.0 | `web_ui/package.json:33,46` | + +`react` and `react-dom` must move together — a split version pair is a +runtime error, not a lint finding. + +Note `typescript-eslint 8.70.0` is already the version the allowlist entry +names as the TS-7 blocker. Bumping to it is expected and does **not** +invalidate that entry; do not edit the allowlist. + +## Constraints you would otherwise rediscover painfully + +- **pnpm only, never npm.** `@kuhyx/web-ui` is consumed as `&path:/web_ui`; + npm cannot install a monorepo subdirectory and running it locks the repo + into a broken state. Local pnpm is 11.26.0 (leave it — session 2 owns the + pnpm 12 move), node is v24.18.0 via nvm. The node bumps above are **CI + workflow pins only**; your local node does not need to match. +- **250-line cap** on every file, code and prose, enforced by + `scripts/check_file_length.sh`. +- **No `# noqa`, no `type: ignore`, no per-file-ignores** without asking + first, every time. Fix the underlying issue. +- **Never `--no-verify`.** If a hook fails, fix the failure. +- Heavy commands go through `~/.claude/scripts/capped.sh` (2 GiB / 10% CPU; + raise per-invocation with `CAP_MEM=4G CAP_CPU_PCT=20`). +- Work on `main`; branch creation is blocked by a hook. Stage narrowly — no + blanket `git add .`. + +## Procedure + +1. Apply the 16 edits above. Exact pins, no carets (the `very_good_analysis` + carets stay as-is — they are session 2's). +2. Regenerate lockfiles where the ecosystem has one: + - `crdt_sync_dart`, `crdt_sync_flutter`: `dart pub get` / `flutter pub get` + - `ts_core`, `web_ui`: `pnpm install` (in each package directory) + - Python requirements have no lockfile here. +3. Run each touched sub-package's suite and fix what breaks: + - `scripts/run_subproject_tests.sh crdt-sync` + - `scripts/run_subproject_tests.sh freedays` + - `scripts/run_subproject_tests.sh gatelock` + - `cd crdt_sync_dart && dart test` + - `cd crdt_sync_flutter && flutter test` + - `cd ts_core && pnpm lint && pnpm test` + - `cd web_ui && pnpm lint && pnpm test` + The React 19.2→19.3 bump is the one most likely to surface a test change; + `eslint 10.10.0` may add a rule that fires. Fix the code, not the config. +4. `./scripts/check_dependency_freshness.sh --all` — expect exit 1 still, but + **only** the three majors remaining (very_good_analysis ×4, vitest ×2, + @vitest/coverage-v8 ×2, pnpm ×2). If anything else is listed, you missed a + bump. +5. `pre-commit run --files `. +6. Commit and push with `~/.claude/scripts/finish_auto.sh` (it runs the gate, + stages narrowly, commits, pushes and watches CI with no LLM turns). Pass + the changed files explicitly — it does not stage untracked files on its + own. + +## Exit condition + +- `./scripts/check_dependency_freshness.sh --all` lists **only** the three + major upgrades named above; every other finding is gone. +- Every sub-package suite listed in step 3 passes. +- Pushed to `main`, and the **dependency freshness** workflow is the only red + CI check left on the repo (it stays red until session 2 lands — that is + expected and is why session 2 exists). +- Delete this file as part of the final commit. diff --git a/TODO-deps-02-major-upgrades.md b/TODO-deps-02-major-upgrades.md new file mode 100644 index 0000000..a38593e --- /dev/null +++ b/TODO-deps-02-major-upgrades.md @@ -0,0 +1,141 @@ +# TODO — utils deps, session 2 of 2: the three major upgrades + +REMOVE ME AFTER FINISH + +Running order: `TODO-deps-01-mechanical-bumps.md` first, then **this file**. +If session 1 has not run, `./scripts/check_dependency_freshness.sh --all` will +list ~16 extra low-risk findings alongside the three below — ignore them here +and do session 1 first, or the gate can never reach exit 0. + +## Why this exists + +`~/src/utils` has been red on the **dependency freshness** CI workflow since +2026-09-11. Session 1 cleared the mechanical bumps. What is left are three +major-version moves, each with real breaking-change surface, measured +2026-09-12: + +| Ecosystem | Package | From → To | Files | +|---|---|---|---| +| pub | very_good_analysis | `^10.3.0` → `11.0.0` | `crdt_sync_flutter/pubspec.yaml:37`, `design_system/pubspec.yaml:20`, `github_device_auth/pubspec.yaml:21`, `sync_settings_ui/pubspec.yaml:25` | +| npm | vitest | 4.1.11 → 5.0.0 | `ts_core/package.json:37`, `web_ui/package.json:49` | +| npm | @vitest/coverage-v8 | 4.1.11 → 5.0.0 | `ts_core/package.json:33`, `web_ui/package.json:42` | +| npm | pnpm | 11.24.0 → 12.4.1 | `ts_core/package.json:12`, `web_ui/package.json:12` | + +`very_good_analysis` keeps a caret by an explicit carve-out in +`rules/dependency-freshness.instructions.md` (SDK-coupled packages may keep a +range), but **its range floor is still checked** — `^10.3.0` fails because the +floor is behind latest. `^11.0.0` is the target, not a bare `11.0.0`. + +`~/src/utils` is shared: 8 Flutter apps consume its Dart packages +(`home_inventory`, `kuhylog`, `lyricanki`, `punchme`, `todo`, `untools`, +`restaurant-rater`, `epopeja_karta`) and 4 TS repos consume `@kuhyx/ts-core` / +`@kuhyx/web-ui` (`europe-county-map`, `iron-and-anvil`, +`awesome-mcp-explorer`, `konbini-67`). Every one of those repos pins its own +`very_good_analysis`, so lint changes here are a preview of work they will +each need — **do not fix them in this session**, just note what you hit. + +## Read first + +- `rules/dependency-freshness.instructions.md` — the Dart caret carve-out, the + "range floor is still checked" rule, and **never `--no-verify`**. +- `dependency-freshness.allowlist.yaml` — ONE active entry (`npm:typescript` + pinned 6.0.3, `blocked_by: transitive:typescript-eslint@8.70.0`, held + fleet-wide). **Leave it alone.** TypeScript stays on 6.0.3 in both sessions. +- `design_system/analysis_options.yaml` — line 1 is + `include: package:very_good_analysis/analysis_options.yaml`, so the lint set + changes the moment the pin moves. The other three Dart packages follow the + same pattern. + +## What `very_good_analysis 11` actually changes + +Recorded when the fleet first hit this (2026-09-11), so expect these three +and confirm against the package's own changelog before assuming the list is +complete: + +- `async_return_with_no_await` — **needs hand edits.** Not auto-fixable. +- `unnecessary_type_name_in_constructor` — `dart fix --apply` handles it. +- `unnecessary_const_in_enum_constructor` — `dart fix --apply` handles it. + +For scale: in `betting-sim` the two auto-fixable rules alone hit 164 sites. +The four utils packages are much smaller, but run `dart fix --dry-run` first +so you know what you are about to change. + +## Constraints you would otherwise rediscover painfully + +- **pnpm only, never npm.** `@kuhyx/web-ui` is consumed as `&path:/web_ui`; + npm cannot install a monorepo subdirectory. +- **The pnpm 11 → 12 move is the riskiest item here.** Local pnpm is 11.26.0 + and `corepack` is present at `~/.nvm/versions/node/v24.18.0/bin/corepack`. + The `packageManager` field carries a full `+sha512...` integrity hash — + regenerate it, do not hand-edit the hash. A past "Failed to switch pnpm to + vX" was a **stale shim in `$PNPM_HOME`**, not a pnpm bug; if you see it, + look for a stale binary on `PATH` and never delete `PNPM_HOME` itself. +- **`vitest 5` is a major** — check its migration notes for config and + coverage-provider changes before editing. `vitest` and + `@vitest/coverage-v8` must move together; a version-skewed pair fails at + coverage time, not at install time. +- **Coverage bars are not negotiable.** `scripts/check_coverage.sh` is the + adjudicator; if vitest 5 reports coverage differently, fix the config, do + not lower the bar. +- **No `# noqa` / `type: ignore` / per-file-ignores / lint disables** without + asking first, every time. All 7 "unavoidable" disables in a past konbini-67 + session were reverted — exhaust refactors first. +- **250-line cap** on every file, enforced by `scripts/check_file_length.sh`. +- Heavy commands through `~/.claude/scripts/capped.sh` + (`CAP_MEM=4G CAP_CPU_PCT=20` to raise). +- Work on `main`; branching is hook-blocked. Stage narrowly. + +## Procedure + +Do these as **three separate commits**, in this order — each is independently +revertable, and mixing them makes a CI failure impossible to attribute. + +**A. `very_good_analysis` → `^11.0.0`** (4 pubspecs) +1. Bump all four, then `flutter pub get` / `dart pub get` in each. +2. `dart fix --dry-run` in each; review, then `dart fix --apply`. +3. Hand-fix every `async_return_with_no_await` finding. +4. `flutter analyze` / `dart analyze` clean in all four. +5. Suites — all four are Flutter packages, so `flutter test` in each (not + `dart test`; only `crdt_sync_dart`, which this session does not touch, is + pure Dart): `crdt_sync_flutter`, `design_system`, `github_device_auth`, + `sync_settings_ui`. + +**B. `vitest` + `@vitest/coverage-v8` → 5.0.0** (`ts_core`, `web_ui`) +1. Bump both packages in both manifests together. `pnpm install` in each. +2. `pnpm lint && pnpm test && pnpm coverage` in each. +3. If coverage reporting changed shape, fix the config so + `scripts/check_coverage.sh` still adjudicates — do not relax the bar. + +**C. `pnpm` → 12.4.1** (`ts_core`, `web_ui`) +1. `corepack use pnpm@12.4.1` in each package so the `packageManager` field + and its integrity hash are regenerated, not hand-written. +2. `pnpm install` in each; commit the updated lockfiles. +3. `pnpm lint && pnpm test` in each. +4. Sanity-check one downstream consumer still resolves — e.g. + `cd ~/src/konbini-67 && pnpm install` — but **do not commit** in that repo. + +After all three: `./scripts/check_dependency_freshness.sh --all` → **exit 0** +(the one `npm:typescript` allowlist entry will print as an active exception; +that is expected and is not a failure). + +Then `pre-commit run --files ` and push each commit with +`~/.claude/scripts/finish_auto.sh`, passing changed files explicitly. + +## Exit condition + +- `./scripts/check_dependency_freshness.sh --all` exits **0**, with only the + pre-existing `npm:typescript` exception reported. +- All four Dart packages analyze clean; `ts_core` and `web_ui` lint, test and + meet their coverage bars. +- No new allowlist entries, no new lint suppressions, `typescript` still 6.0.3. +- The **dependency freshness** workflow is green on `main` — the whole point; + it has been red since 2026-09-11. +- Delete this file (and `TODO-deps-01-*` if it is still present) in the final + commit. + +## If a transitive conflict blocks a bump + +Stop and ask, per conflict. Exact pins plus latest-everywhere is provably +unsatisfiable when a dependency internally constrains a shared transitive. +**Never auto-add an allowlist entry** — that is the user's call, and a +discretionary entry needs an `expires` date capped at 90 days.