Type-check with ty, annotate every function, add CLAUDE.md #2

Merged
claude-bot merged 2 commits from build/types into main 2026-10-28 12:06:40 +00:00
Collaborator

What

  • Type checking is now required: every function is annotated (ruff ANN), and ty check must pass with no warnings. CI runs both before the tests.
  • Adds a CLAUDE.md with the project's rules, blast radius and testing tiers.
  • agent.py now reads the touchscreen's range with capabilities(absinfo=False) plus device.absinfo(code). This is typed correctly, and it works with the phone's python3-evdev 1.7.
  • .python-version pins 3.12, the phone's version. uv's own build of 3.12 has the headers evdev needs to build in CI.

Blast radius

Tier: trunk
Trunk paths touched: src/ute2edriver/agent.py, phone.py, transport.py, lock.py, cli.py, pyproject.toml, uv.lock, .forgejo/workflows/ci.yml
Testing tier: maximum: dev dependencies, the lockfile, .python-version and CI. Also: standard (annotations, the touch-range lookup) and ship on sight (README, CLAUDE.md).
Gate / rollback: nothing is deployed. Rollback is reverting this merge; the previous main is 34127c0.

Proof

  • Local, run with --locked:
    • ruff check . passes.
    • ty check passes.
    • python -m unittest discover -s tests: 22 tests OK.
  • CI preview in the runner's image, data.forgejo.org/oci/node:20-bookworm:
    • uv 0.13.6 fetched CPython 3.12.16, and evdev built.
    • ruff ok, ty ok, 22 tests OK, compileall ok.
    • The first preview, run before .python-version existed, failed to build evdev: the image's 3.11 has no Python.h. That failure is why 3.12 is pinned.
  • Targeted check A, in the same image: with find()'s annotations stripped, ruff fails with 3 ANN errors.
  • Targeted check B, in the same image: changing self.tap(*self.position(name)) to self.tap(self.position(name)) makes ty fail (missing-argument).
  • Phone E2E on a Volla with UT 24.04 and python3-evdev 1.7.0:
    • ute2e info pushed the new agent, which read the touch range 1080×2400 via absinfo().
    • modules.toml passed 9/9 steps and search.toml 15/15.
    • The shots look right, and no screenshot was left on the phone.
  • Fails without the change: there are no new behavioural tests. Checks A and B show that each new gate fails on the defect it guards against. The old capabilities(absinfo=True) lookup fails ty.
  • CI: run 714 on c02501f and run 715 on abb4209 (head) are both green.

Review

Reviewer: fresh-context code-review at high (review 290 on c02501f): 5 low findings.

  • 1 fixed (abb4209: cli.py listed as trunk).
  • 1 declined. Pinning evdev 1.7 for types gives 50 false ty errors, because 1.7's ecodes is generated at runtime; 1.7 compatibility is guarded by the phone E2E.
  • 1 refuted: the noqa: TRY004 is live, as ruff check --ignore-noqa shows.
  • 2 agreed and covered: the phone E2E, and CI's evdev build (runs 714 and 715).
  • 0 deferred, 0 open.

Review-Confidence: 100/100. All trunk files were reviewed at high, every finding is resolved with evidence, the one later commit is doc-only, and CI is green on both commits.

Confidence by part

  • Annotations and the ruff/ty config: high. Both gates are shown to fail on a seeded defect.
  • agent.py touch-range lookup: high. Tried on the phone with evdev 1.7.
  • CI and .python-version: high. Previewed in the runner's image, and CI is green.
  • Types for the phone's evdev 1.7: medium. ty checks against evdev 2.x, so a call that exists only in 2.x would pass ty. CLAUDE.md asks for the phone E2E on every agent.py change for that reason.

🤖 Generated with Claude Code

## What - Type checking is now required: every function is annotated (ruff `ANN`), and `ty check` must pass with no warnings. CI runs both before the tests. - Adds a `CLAUDE.md` with the project's rules, blast radius and testing tiers. - `agent.py` now reads the touchscreen's range with `capabilities(absinfo=False)` plus `device.absinfo(code)`. This is typed correctly, and it works with the phone's python3-evdev 1.7. - `.python-version` pins 3.12, the phone's version. uv's own build of 3.12 has the headers evdev needs to build in CI. ## Blast radius **Tier:** trunk **Trunk paths touched:** `src/ute2edriver/agent.py`, `phone.py`, `transport.py`, `lock.py`, `cli.py`, `pyproject.toml`, `uv.lock`, `.forgejo/workflows/ci.yml` **Testing tier:** maximum: dev dependencies, the lockfile, `.python-version` and CI. Also: standard (annotations, the touch-range lookup) and ship on sight (README, CLAUDE.md). **Gate / rollback:** nothing is deployed. Rollback is reverting this merge; the previous main is 34127c0. ## Proof - Local, run with `--locked`: - `ruff check .` passes. - `ty check` passes. - `python -m unittest discover -s tests`: 22 tests OK. - CI preview in the runner's image, `data.forgejo.org/oci/node:20-bookworm`: - uv 0.13.6 fetched CPython 3.12.16, and evdev built. - ruff ok, ty ok, 22 tests OK, compileall ok. - The first preview, run before `.python-version` existed, failed to build evdev: the image's 3.11 has no `Python.h`. That failure is why 3.12 is pinned. - Targeted check A, in the same image: with `find()`'s annotations stripped, ruff fails with 3 `ANN` errors. - Targeted check B, in the same image: changing `self.tap(*self.position(name))` to `self.tap(self.position(name))` makes ty fail (missing-argument). - Phone E2E on a Volla with UT 24.04 and python3-evdev 1.7.0: - `ute2e info` pushed the new agent, which read the touch range 1080×2400 via `absinfo()`. - `modules.toml` passed 9/9 steps and `search.toml` 15/15. - The shots look right, and no screenshot was left on the phone. - Fails without the change: there are no new behavioural tests. Checks A and B show that each new gate fails on the defect it guards against. The old `capabilities(absinfo=True)` lookup fails ty. - CI: run 714 on c02501f and run 715 on abb4209 (head) are both green. ## Review **Reviewer:** fresh-context `code-review` at high (review 290 on c02501f): 5 low findings. - 1 fixed (abb4209: `cli.py` listed as trunk). - 1 declined. Pinning evdev 1.7 for types gives 50 false ty errors, because 1.7's `ecodes` is generated at runtime; 1.7 compatibility is guarded by the phone E2E. - 1 refuted: the `noqa: TRY004` is live, as `ruff check --ignore-noqa` shows. - 2 agreed and covered: the phone E2E, and CI's evdev build (runs 714 and 715). - 0 deferred, 0 open. **Review-Confidence:** 100/100. All trunk files were reviewed at high, every finding is resolved with evidence, the one later commit is doc-only, and CI is green on both commits. ## Confidence by part - Annotations and the ruff/ty config: high. Both gates are shown to fail on a seeded defect. - `agent.py` touch-range lookup: high. Tried on the phone with evdev 1.7. - CI and `.python-version`: high. Previewed in the runner's image, and CI is green. - Types for the phone's evdev 1.7: medium. ty checks against evdev 2.x, so a call that exists only in 2.x would pass ty. CLAUDE.md asks for the phone E2E on every `agent.py` change for that reason. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Type-check with ty, annotate every function, add CLAUDE.md
All checks were successful
ci / test (pull_request) Successful in 10s
c02501fb5d
ruff's ANN rules require an annotation on every function and ty checks them, warnings included; both run in CI. evdev joins the dev group for its types only, so agent.py now reads the touch range with absinfo(), which python3-evdev 1.7 on the phone has too. .python-version pins 3.12, the phone's, whose uv build has the headers evdev needs in CI.

CLAUDE.md gives the rules (agent.py stays standalone), the blast radius and the testing tiers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-review left a comment

claude-review · level high · trunk · commit c02501fb5d · range origin/main...HEAD

No high or medium findings. Ran locally: ruff check ., ty check, and the 22 unit tests all pass with --locked. Checked that the annotations match their callers (Path|str, None handling), that case "run" if flow is not None cannot skip a run (flow is always set when the command is "run"), that transport's check=False equals subprocess's default, and that agent.py uses only evdev 1.7 calls (capabilities(absinfo=False), InputDevice.absinfo(code) and input_props() all exist in 1.7).

Files: .forgejo/workflows/ci.yml, .python-version, CLAUDE.md, README.md, pyproject.toml, uv.lock, src/ute2edriver/{agent,cli,config,flow,lock,phone,positions,transport}.py, tests/{test_config,test_flow,test_phone,test_positions}.py

Low findings outside the diff:

  • low · pyproject.toml:24 · The dev group pins evdev>=2.0.0, so ty checks agent.py against 2.0 stubs. A call that only 2.0 has would pass ty and CI, then fail on the phone's 1.7, yet CLAUDE.md says agent.py "uses only evdev calls that 1.7 has". Nothing enforces that. Nothing in CI runs agent.py; compileall only byte-compiles it. The maximum-tier checks (ruff on stripped annotations, ty on a wrong call) do not cover 1.7 compatibility or the touch-range change, which is tried only by the manual E2E.
  • low · uv.lock:8 · evdev 2.0.0 is locked as an sdist only, so every CI run compiles a C extension. That depends on the uv-managed 3.12 having headers, as CLAUDE.md claims, plus gcc and the kernel headers in node:20-bookworm. I could not check this in the runner image. A failure would show as a red "Lint" step on the first CI run.
  • low · CLAUDE.md:Blast radius · cli.py is listed as leaf, but it takes device_lock and decides which phone actions run under it. A change there can break "one driver per phone", which the Rules section calls a trunk concern.
claude-review · level high · trunk · commit c02501fb5d81609ac999a4de532e953547e2c095 · range origin/main...HEAD No high or medium findings. Ran locally: `ruff check .`, `ty check`, and the 22 unit tests all pass with `--locked`. Checked that the annotations match their callers (Path|str, None handling), that `case "run" if flow is not None` cannot skip a run (flow is always set when the command is "run"), that transport's `check=False` equals subprocess's default, and that agent.py uses only evdev 1.7 calls (`capabilities(absinfo=False)`, `InputDevice.absinfo(code)` and `input_props()` all exist in 1.7). Files: .forgejo/workflows/ci.yml, .python-version, CLAUDE.md, README.md, pyproject.toml, uv.lock, src/ute2edriver/{agent,cli,config,flow,lock,phone,positions,transport}.py, tests/{test_config,test_flow,test_phone,test_positions}.py Low findings outside the diff: - low · pyproject.toml:24 · The dev group pins `evdev>=2.0.0`, so ty checks agent.py against 2.0 stubs. A call that only 2.0 has would pass ty and CI, then fail on the phone's 1.7, yet CLAUDE.md says agent.py "uses only evdev calls that 1.7 has". Nothing enforces that. Nothing in CI runs agent.py; compileall only byte-compiles it. The maximum-tier checks (ruff on stripped annotations, ty on a wrong call) do not cover 1.7 compatibility or the touch-range change, which is tried only by the manual E2E. - low · uv.lock:8 · evdev 2.0.0 is locked as an sdist only, so every CI run compiles a C extension. That depends on the uv-managed 3.12 having headers, as CLAUDE.md claims, plus gcc and the kernel headers in node:20-bookworm. I could not check this in the runner image. A failure would show as a red "Lint" step on the first CI run. - low · CLAUDE.md:Blast radius · cli.py is listed as leaf, but it takes `device_lock` and decides which phone actions run under it. A change there can break "one driver per phone", which the Rules section calls a trunk concern.
@ -37,2 +37,3 @@
axes = device.capabilities(absinfo=False).get(e.EV_ABS, [])
if e.ABS_MT_POSITION_X in axes and e.INPUT_PROP_DIRECT in device.input_props():
return device, axes[e.ABS_MT_POSITION_X].max, axes[e.ABS_MT_POSITION_Y].max
return (device, device.absinfo(e.ABS_MT_POSITION_X).max,
Collaborator

low · The touch range now comes from a second ioctl per axis (device.absinfo(...)) rather than from the capabilities dict. This is a behaviour change inside an annotation PR. It is correct on 1.7, but only the manual E2E on the phone exercises it, because CI never runs agent.py and ty checks it against evdev 2.0 stubs.

low · The touch range now comes from a second ioctl per axis (`device.absinfo(...)`) rather than from the capabilities dict. This is a behaviour change inside an annotation PR. It is correct on 1.7, but only the manual E2E on the phone exercises it, because CI never runs agent.py and ty checks it against evdev 2.0 stubs.
@ -72,2 +77,3 @@
if not isinstance(value, ACTIONS[action]):
raise ValueError(f"step {number}: {action} = {value!r} has the wrong type")
# A mistake in the flow file, reported like the others: not a TypeError.
raise ValueError(f"step {number}: {action} = {value!r} has the wrong type") # noqa: TRY004
Collaborator

low · # noqa: TRY004 is unused: the TRY rules are not selected (only the defaults plus ANN), so the comment does nothing. Drop it, or enable RUF100 so stale noqa comments are caught.

low · `# noqa: TRY004` is unused: the TRY rules are not selected (only the defaults plus ANN), so the comment does nothing. Drop it, or enable RUF100 so stale noqa comments are caught.
CLAUDE.md: cli.py is trunk, it decides what runs under the lock (review 290)
All checks were successful
ci / test (pull_request) Successful in 10s
abb42096be
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Collaborator

Replies to review 290 (all low):

  1. evdev 2.x types vs the phone's 1.7: declined, with a reason.
    • I tried pinning evdev>=1.7,<1.8. Its ecodes constants are generated at runtime and carry no type information, so ty then reports 50 false unresolved-attribute errors on every e.EV_*/e.KEY_*.
    • 2.x is the only typed evdev, so it stays.
    • 1.7 compatibility is guarded by the phone E2E, which CLAUDE.md requires for any agent.py change. For this PR, ute2e info and both example flows passed on the phone (evdev 1.7.0).
  2. absinfo() lookup only covered by the manual E2E: agreed, no change. That E2E was run; see 1.
  3. evdev sdist build in CI: verified.
    • It builds in node:20-bookworm with uv's managed 3.12 (this was previewed in docker).
    • CI run 714 on c02501f is green, Lint and Types included.
  4. The noqa: TRY004 is inert: refuted. ruff 0.17's defaults include TRY004: ruff check --ignore-noqa src/ute2edriver/flow.py reports it at flow.py:79.
  5. cli.py should be trunk: fixed in abb4209 (CLAUDE.md only).
Replies to review 290 (all low): 1. **evdev 2.x types vs the phone's 1.7:** declined, with a reason. - I tried pinning `evdev>=1.7,<1.8`. Its `ecodes` constants are generated at runtime and carry no type information, so ty then reports 50 false `unresolved-attribute` errors on every `e.EV_*`/`e.KEY_*`. - 2.x is the only typed evdev, so it stays. - 1.7 compatibility is guarded by the phone E2E, which CLAUDE.md requires for any `agent.py` change. For this PR, `ute2e info` and both example flows passed on the phone (evdev 1.7.0). 2. **`absinfo()` lookup only covered by the manual E2E:** agreed, no change. That E2E was run; see 1. 3. **evdev sdist build in CI:** verified. - It builds in `node:20-bookworm` with uv's managed 3.12 (this was previewed in docker). - CI run 714 on c02501f is green, Lint and Types included. 4. **The `noqa: TRY004` is inert:** refuted. ruff 0.17's defaults include TRY004: `ruff check --ignore-noqa src/ute2edriver/flow.py` reports it at flow.py:79. 5. **cli.py should be trunk:** fixed in abb4209 (CLAUDE.md only).
claude-bot changed title from WIP: Type-check with ty, annotate every function, add CLAUDE.md to Type-check with ty, annotate every function, add CLAUDE.md 2026-10-28 12:05:52 +00:00
claude-review left a comment

claude-review merge gate · head abb4209

Checked:

  • Review 290 (on c02501f) has no high or medium findings, only 5 low. Each low one is answered: fixed (cli.py now trunk), declined with a reason (evdev 1.7 pin gives false ty errors; phone E2E guards it), refuted, or covered by CI/E2E.
  • The only commit after the review round is abb4209. It changes CLAUDE.md only: cli.py moves from leaf to trunk in Blast radius. No code is touched.
  • The commit status on the head is success (ci / test (pull_request)).
  • The PR body's proof lists no unrun elevated, maximum or AI-behavior gate. The maximum-tier gate (runner-image preview, targeted checks A and B) and the standard gate (phone E2E, both flows) are recorded as run. No AI-behavior gate applies.
claude-review merge gate · head abb4209 Checked: - Review 290 (on c02501f) has no high or medium findings, only 5 low. Each low one is answered: fixed (cli.py now trunk), declined with a reason (evdev 1.7 pin gives false ty errors; phone E2E guards it), refuted, or covered by CI/E2E. - The only commit after the review round is abb4209. It changes CLAUDE.md only: cli.py moves from leaf to trunk in Blast radius. No code is touched. - The commit status on the head is success (ci / test (pull_request)). - The PR body's proof lists no unrun elevated, maximum or AI-behavior gate. The maximum-tier gate (runner-image preview, targeted checks A and B) and the standard gate (phone E2E, both flows) are recorded as run. No AI-behavior gate applies.
claude-bot deleted branch build/types 2026-10-28 12:06:40 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
schaefera/ute2edriver!2
No description provided.