From 07e191be7bd1480f164b9b518a552fae079b67ce Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sat, 12 Sep 2026 20:13:53 +0200 Subject: [PATCH] Repair the two CI signals gatelock 0.8.x broke, and drop its finished pylint TODO The palette-drift check parsed LockConfig's colour defaults; they live on LockPalette in _theme.py since 0.8.0, so it read zero tokens and failed closed as designed. The two new LockWindow handles had no test reaching them, which CI's 100% bar caught and the local run did not. gatelock/prompts/TODO-pylint-to-ten.md described this job with the older design (raise the score, leave the gate); the gate is at 10. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HHCbkJWXHjtTUaqvnHRAqA --- gatelock/gatelock/tests/test_window.py | 7 ++ gatelock/prompts/TODO-pylint-to-ten.md | 113 ------------------ .../scripts/palette_check.py | 13 +- 3 files changed, 14 insertions(+), 119 deletions(-) delete mode 100644 gatelock/prompts/TODO-pylint-to-ten.md diff --git a/gatelock/gatelock/tests/test_window.py b/gatelock/gatelock/tests/test_window.py index 10b53be..3bfb62f 100644 --- a/gatelock/gatelock/tests/test_window.py +++ b/gatelock/gatelock/tests/test_window.py @@ -102,6 +102,13 @@ class TestSetup: topmost_on = True mock_root.attributes.assert_any_call("-topmost", topmost_on) + def test_suspend_handles_are_the_armed_collaborators(self, mock_root) -> None: + """An app that suspends the lock stops the very loop and watcher it arms.""" + window, _hooks = make_window(mock_root, config=LockConfig(mode="soft")) + + assert window.recovery is window._arming.recovery + assert window.detector is window._arming.detector + class TestGrabInput: """Tests for LockWindow.grab_input.""" diff --git a/gatelock/prompts/TODO-pylint-to-ten.md b/gatelock/prompts/TODO-pylint-to-ten.md deleted file mode 100644 index 1d9500f..0000000 --- a/gatelock/prompts/TODO-pylint-to-ten.md +++ /dev/null @@ -1,113 +0,0 @@ -# Session prompt: take gatelock's pylint from 9.06 to 10.00 - -Paste everything below the line into a fresh Claude Code session started in -`~/src/utils` (the monorepo root — **not** `~/src/utils/gatelock`; pre-commit runs -hooks from the git root and every path in the hook config is relative to it). - ---- - -Take `gatelock`'s pylint score from **9.06/10 to a clean 10.00/10**, without -weakening the linter. - -## Why - -The pylint hook runs with `--fail-under=8.0`, so 9.06 passes while carrying -~394 findings. The same job was done in the sibling package -`~/src/utils/crdt-sync` on 2026-08-21 (8.58 -> 10.00), so the conventions and the -gate are already proven one directory over — read that package's -`crdt_sync/tests/conftest.py` and the commits `1b8c99d` / `2005c40` in -`~/src/utils` for the worked example. - -Measured on this machine, 2026-08-21, with -`~/.cache/pre-commit/repo01lfw04p/py_env-python3/bin/pylint --rcfile=pyproject.toml gatelock` -run from `~/src/utils/gatelock`: - -| category | count | -|---|---| -| redefined-outer-name | 171 | -| protected-access | 141 | -| missing-function-docstring | 19 | -| use-implicit-booleaness-not-comparison-to-zero | 14 | -| use-implicit-booleaness-not-comparison | 12 | -| unbalanced-tuple-unpacking | 9 | - -**312 of the top two categories are in `gatelock/tests/`.** This repo is NOT -the docstring problem the other two are — only 19 docstrings are missing. -It is dominated by **pytest fixture idiom**, which makes it a different job: - -- `redefined-outer-name` (171) is the classic fixture-shadowing pattern — a - test parameter named the same as the module-level fixture function. -- `protected-access` (141) is tests reaching into the private members that - are the unit under test. - -Note for context: `~/src/screen-locker`'s tests hook already disables -`protected-access` / `unused-argument` / `duplicate-code` for exactly this -reason. gatelock does not, and that gap is most of the 0.94. - -Re-measure before you start and again at the end. - -## Scope - -**In scope** -1. Every pylint finding under `gatelock/`. - -**Out of scope — do not touch** -- `--fail-under` in `gatelock/.pre-commit-config.yaml`. Do NOT raise it to - hide a shortfall, and do NOT lower it. Leave it at 8.0. -- Any sibling package in the monorepo — `crdt-sync/`, `web_ui/`, - `design_system/`, `sync_settings_ui/`. Stage narrowly: `gatelock/` only. -- Note `crdt-sync/tool/seed_session.py` is modified and - `crdt-sync/firebase-debug.log` is untracked; both predate this task. Keep - them out of your commit. - -## The decision this task actually turns on - -The 312 fixture-idiom findings are not individually fixable in any -satisfying way, and this is the fork to bring to the user **before** doing -the work, not after: - -- **Option A — configure the checks off for tests only.** Add a - `[tool.pylint...]` per-path setting or a tests-scoped disable for - `redefined-outer-name` + `protected-access`. This is what screen-locker - already does, it is honest (the checks are wrong for pytest, not the code), - and it is one reviewable decision instead of 312 edits. It IS a config - change, which the user has previously refused when the alternative was - real work — so it must be asked, with the screen-locker precedent cited. -- **Option B — rename every shadowing parameter** and re-examine all 141 - protected accesses individually. No config change; a very large diff - touching most of the test suite. - -Ask which, in one question, with A recommended and the precedent named. Do -not start either until answered. - -## The rule for everything else - -**Fix the underlying issue; do not suppress it.** The repo blocks `noqa` and -`type: ignore` outright. Inline `# pylint: disable=` is permitted only for a -genuine false positive, scoped to the narrowest unit, placed on the line -that actually triggers it (verify — a `disable-next` one line off silently -does nothing and pylint reports `useless-suppression`), with a comment -saying why. - -`unbalanced-tuple-unpacking` (9) is worth reading carefully rather than -suppressing: it sometimes flags a real latent IndexError. - -## Gates - -- `python -m pytest -q` from `~/src/utils/gatelock` — **389 passing at 100% - branch coverage** (measured 2026-08-21). Both numbers must hold. -- `pre-commit run --config gatelock/.pre-commit-config.yaml --files ` - — run it from `~/src/utils`, and note the config's `files:` filter means paths - must be repo-root-relative (`gatelock/...`) or the hooks silently skip. -- `ruff format` will reformat what you touch; re-run the suite afterwards. - -## Done means - -1. pylint reports **10.00/10** for `gatelock`. -2. `--fail-under` still 8.0. -3. 389 tests still passing at 100% branch coverage. -4. No sibling package appears in your diff. -5. The final report states which option the user chose for the fixture-idiom - findings, and lists every suppression with its justification. - -REMOVE ME AFTER FINISH diff --git a/unified-design-system/scripts/palette_check.py b/unified-design-system/scripts/palette_check.py index bd0275b..b2d1e0b 100644 --- a/unified-design-system/scripts/palette_check.py +++ b/unified-design-system/scripts/palette_check.py @@ -39,9 +39,10 @@ ROOT = Path(__file__).resolve().parents[2] TOKENS_MD = ROOT / "unified-design-system" / "DOCS-tokens.md" TOKENS_CSS = ROOT / "web_ui" / "src" / "tokens.css" TOKENS_DART = ROOT / "design_system" / "lib" / "src" / "tokens.dart" -# LockConfig moved out of _window.py when that file was split for the -# 250-line cap; the palette lives with the dataclass, not the window. -LOCKCONFIG_PY = ROOT / "gatelock" / "gatelock" / "_config.py" +# The palette moved twice: out of _window.py in the 250-line split, then out +# of LockConfig into the LockPalette record (gatelock 0.8.0). The colours live +# with that record, not with the behavioural config. +LOCKCONFIG_PY = ROOT / "gatelock" / "gatelock" / "_theme.py" @dataclass @@ -121,16 +122,16 @@ def parse_dart(text: str) -> dict[str, str]: def parse_tk(text: str) -> dict[str, str]: - """Parse `field: str = "#RRGGBB"` defaults out of the LockConfig block.""" + """Parse `field: str = "#RRGGBB"` defaults out of the LockPalette block.""" found: dict[str, str] = {} - start = text.find("class LockConfig") + start = text.find("class LockPalette") if start == -1: # Fail closed. Returning {} here made every tk comparison vacuously # pass when LockConfig moved to _config.py during the 250-line split: # "0 parsed token(s)" read as success, and the drift guard was off # for the whole gatelock stack without anything going red. msg = ( - "LockConfig not found in the file palette_check reads. It has " + "LockPalette not found in the file palette_check reads. It has " "moved; update LOCKCONFIG_PY rather than letting the tk stack go " "unchecked." )