Skip to content

Add tox for Python test/lint matrix + Rust MSRV verification - #31

Merged
BcKmini merged 6 commits into
feat/autonomy-lessons-mcpfrom
chore/tox-and-rust-msrv
Aug 10, 2026
Merged

Add tox for Python test/lint matrix + Rust MSRV verification#31
BcKmini merged 6 commits into
feat/autonomy-lessons-mcpfrom
chore/tox-and-rust-msrv

Conversation

@BcKmini

@BcKmini BcKmini commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #30. Stacked on #29 (feat/autonomy-lessons-mcp) — base branch is set to that branch, not main, since this adds tests for claude-lessons.py which only exists there. Once #29 merges, GitHub will retarget this to main automatically.

  • tox.ini + tests/: a real pytest suite for all 8 CLI tools (44 tests, subprocess-based against isolated $HOME/git repos — nothing touches the real ~/.claude/), run across Python 3.8–3.13 via tox, plus lint/fmt-check envs.
  • pyproject.toml: a ruff config scoped to E/F/I/UP — ruff's actual default ruleset turned out to be much broader (bandit-style security rules, blind-except, naive-datetime) and fighting this codebase's deliberate style (e.g. intentional broad except: pass around optional Windows VT-mode setup). E402 is ignored since every tool puts VERSION right after the docstring, before imports.
  • fmt-check is scoped to tools/claude-lessons.py + tests/ only — the older 7 tools use a deliberate hand-aligned style (aligned =, aligned dict values) that ruff format would flatten repo-wide. Asked in-session whether to force-reformat everything, format only new files, or drop the gate; went with formatting only new/touched-by-this-branch code and leaving the established style alone. scripts/fmt.sh scoped the same way so make fmt doesn't quietly undo it later.
  • Rust MSRV: rust-version = "1.75" added to claude-tools/Cargo.toml (matches the existing README badge, which wasn't enforced anywhere before), plus a cargo msrv verify CI job and make msrv target.
  • CI: new python-tests (matrix over 3.8–3.13) and python-lint jobs in .github/workflows/ci.yml; new msrv job.

Bugs the new tests actually caught (fixed in the second commit)

  • claude-cost.py set-budget crashed with FileNotFoundError on a fresh install — _save_budget never created ~/.claude/ first.
  • claude-remind.py / claude-review-diff.py use X | None (PEP 604) type annotations, which raise TypeError at import time on Python < 3.10 — every subcommand of both tools, including --help, was broken on 3.8/3.9 despite the "Python 3.8+" badge. Fixed with from __future__ import annotations; confirmed via tox -e py39 before/after (no py38/3.10/3.11 interpreter was available locally, but the failure mode is annotation-evaluation-at-import-time, identical on 3.8-3.9, and CI's matrix will confirm 3.8 directly).

Plus the ruff lint gate's small, low-risk findings (4 unused imports, 1 dead variable, 4 one-line if statements split for E701, one l -> line rename) — all mechanical, tests re-verified green after each.

Test plan

  • tox locally (py39/py312/py313 available in this environment; py38/py310/py311 skip cleanly via skip_missing_interpreters) — all green, including lint and fmt-check
  • tox -e py39 specifically re-run before/after the from __future__ import annotations fix to confirm it was the actual cause
  • make test (existing fast smoke suite) still passes after the lint cleanup
  • claude-harness check-all / bash scripts/validate-agents.sh unaffected, re-run to confirm
  • YAML-validated the edited ci.yml (parses, all expected jobs present)
  • Scripted relative-link check across touched docs — no broken links
  • cargo msrv verify / the new msrv CI job — not run locally, no Rust toolchain available in this environment; will be exercised by CI on push, same as this project's other Rust checks

Nothing verified tools/*.py across Python versions before this — no
pytest suite, no CI job, just a handful of ad-hoc --help/smoke commands
in Makefile's test-python target. Adds:

- tests/conftest.py: run_tool/home/git_repo fixtures — every test runs
  the real CLI as a subprocess against an isolated $HOME (and, for the
  two tools that shell out to git, an isolated git repo), so nothing
  touches the machine's real ~/.claude/
- tests/test_*.py: one file per tool, 44 tests total, functional not
  just --help (add/list/search/show roundtrips, error paths, exit codes)
- tox.ini: py38-py313 (skip_missing_interpreters), lint (ruff check),
  fmt-check (ruff format --check)
- pyproject.toml: scoped ruff config — E/F/I/UP only, not the full
  default ruleset (which includes bandit/blind-except/naive-datetime
  rules that fight this codebase's deliberate style), E402 ignored
  since every tool puts VERSION right after the docstring before imports
- fmt-check / scripts/fmt.sh scoped to tools/claude-lessons.py + tests/
  only, not all of tools/: the older tools use a deliberate hand-aligned
  style (aligned `=`, aligned dict values) that `ruff format` would
  flatten repo-wide — not forcing that as a side effect of adding tox
- make tox: runs the full matrix (requires: pip install tox ruff)

Verified locally: tox (py39/py312/py313 available here, py38/py310/py311
skip cleanly), lint, and fmt-check all pass.
Real, independently-reproduced bugs the new test suite caught:

- claude-cost.py: `set-budget` crashed with FileNotFoundError on a fresh
  install — `_save_budget` never created ~/.claude/ before writing
  cost-budget.json (snippet.py and claude-handoff.py already did this
  correctly; claude-cost.py's budget path was the one that didn't).
- claude-remind.py / claude-review-diff.py: both use `X | None` (PEP 604)
  type annotations, which raise TypeError at import time on Python < 3.10
  — every subcommand, including --help, was broken on 3.8/3.9 despite
  the "Python 3.8+" badge. Fixed with `from __future__ import
  annotations` (defers annotation evaluation, no behavior change).
  Confirmed via tox -e py39 before/after.

Also applies the (small, low-risk) findings from the new ruff lint gate
across tools/*.py: 4 unused imports, 1 dead local variable
(claude-handoff.py's `list`, left over from a removed date column), 4
`if x: y` one-liners split to satisfy E701, and one ambiguous single-
letter loop variable (`l` -> `line`). No behavior changes; tests still
pass after each.
- python-tests job: runs `tox -e py` across Python 3.8-3.13 via
  actions/setup-python (this is what actually caught the
  claude-remind.py/claude-review-diff.py 3.8/3.9 breakage in the
  previous commit — nothing exercised those versions before)
- python-lint job: `tox -e lint,fmt-check`
- rust-version = "1.75" declared in claude-tools/Cargo.toml, matching
  the README's existing "Rust 1.75+" badge (previously just a claim,
  not enforced anywhere)
- msrv job: `cargo msrv verify` — actually builds against the declared
  1.75 toolchain instead of only ever testing against `stable`

No cargo/rustc available in the environment this branch was authored
in, so the Rust-side change (rust-version field + msrv CI job) is
verified by CI on push, the same as the rest of this project's Rust
checks.
README.md/.ko.md: Makefile section (make tox, make msrv), repo layout
(tests/, tox.ini, pyproject.toml). Also fixes "make lint # clippy + ruff"
in both READMEs — the lint target only ever ran clippy; ruff wasn't
wired into it before this branch.

docs/CONTRIBUTING.md/.ko.md: tox usage in Development Setup, tests/
step added to "Adding a New Tool", tox/cargo-msrv checks added to the
PR checklist, cargo-msrv install/verify commands.

Closes #30
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5836f4a9-020f-4ed8-b984-bb4dc8656c51

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

CI caught this immediately: cargo-msrv installs rustc 1.75 per the
declared rust-version, but that toolchain's bundled cargo can't parse
Cargo.lock's `version = 4` format (stabilized in cargo 1.78) — a
structural incompatibility independent of whether the crate's actual
code needs 1.75 or newer. The pre-existing "Rust 1.75+" badge this
number was copied from was apparently never verified against the
checked-in lockfile either.

Bumped rust-version to 1.78 (README/README.ko badges updated to match)
and letting CI's msrv job re-verify — no cargo available locally to
confirm further.
1.78 also failed: a transitive dep (clap 4.6.1's Cargo.toml) requires
the `edition2024` cargo feature, stabilized in Cargo 1.85 — unrelated
to claude-tools' own code, just how new the pinned dependency tree is.

No local cargo to bisect this by hand, and guessing one version per
push-and-wait-5-minutes cycle is slow. `cargo msrv find` bisects the
actual minimum compatible version in one run instead of asserting a
guess. Once it reports the real number, Cargo.toml's rust-version will
be set to match and the job switched back to `verify`.
@BcKmini
BcKmini merged commit 52ab6b1 into feat/autonomy-lessons-mcp Aug 10, 2026
15 checks passed
@BcKmini

BcKmini commented Aug 10, 2026

Copy link
Copy Markdown
Owner Author

Closes #30.

BcKmini added a commit that referenced this pull request Aug 11, 2026
* fix: pre-existing bash 3.2 and Python syntax bugs found during testing

- scripts/validate-agents.sh used `declare -A`, which crashes on bash 3.2
  (the default /bin/bash on every stock macOS install, despite the repo's
  own "Platform: macOS" badge). Replaced with a portable space-delimited
  set so the script actually runs on macOS.
- tools/claude-pipeline.py `list` used a backslash-escaped quote inside an
  f-string nested in another f-string — a SyntaxError on every Python
  version, so `claude-pipeline list`/`--help` never worked at all. Hoisted
  the value into a local variable instead.
- Added __pycache__/*.pyc to .gitignore (generated while testing tools/).

Found while verifying the autonomy/lessons/MCP-guide changes in this branch.

* feat: add autonomy level (L0-L4) framework to harness design

Harness type (tight/loose/adaptive) controls output constraint; autonomy
level is a separate axis for how much human checking a task needs before
or after the AI acts. Adds the 5-level model (L0 human-only .. L4 fully
autonomous, L2 draft+review as the common default) from the AI Agent
autonomy article this branch is based on.

- autonomy: field added to all 11 agent frontmatters, assigned per role
- claude-harness.py: new required check (autonomy declared), `autonomy`
  subcommand printing the L0-L4 table, templates updated
- harness-designer (09): new design step + Autonomy Level output field
- docs/HARNESS-GUIDE.md(.ko): new Autonomy Levels section
- README Agent Roster table: new Autonomy column
- /harness command + cheatsheets: autonomy validate check + prompts

Closes #26

* feat: add claude-lessons failure/lessons-learned tool

claude-handoff captures session state; nothing in this project recorded
WHY something failed and HOW it was fixed, so the next session (or agent)
had no way to avoid repeating a past mistake. Unlike handoffs, lessons
accumulate indefinitely and are searchable by tag/keyword rather than
pruned by age.

  claude-lessons add       # symptom / root cause / fix / tags
  claude-lessons list       # recent, optionally filtered by tag
  claude-lessons search Q   # keyword search
  claude-lessons context    # pipeable into claude for session-start context

Stdlib-only, mirrors tools/claude-handoff.py conventions. Wired into
Makefile install-tools/status/test-python and install.sh's TOOLS array.

Closes #27

* feat: add MCP server guide with a working claude-lessons example

Documents when to convert a CLI tool into an MCP server (Claude calls it
mid-conversation) vs. keeping it a slash command or manual pipe (human
stays in control of when it runs) — including a guideline against wrapping
mutating/write actions as auto-callable MCP tools, tied to the autonomy
levels added earlier in this branch.

examples/mcp-lessons-server.py wraps tools/claude-lessons.py (add_lesson,
search_lessons, recent_lessons) via the `mcp` Python SDK's FastMCP API.
Lives under examples/, not tools/, since tools/ must stay dependency-free
per docs/CONTRIBUTING.md — documented there as the one exception.

Verified end-to-end against a real `mcp` install: `pip install mcp` now
pulls a 2.x release that reworked/moved FastMCP, so the guide and example
both pin `mcp>=1.2,<2`, confirmed working with 1.29.0.

Closes #28

* docs: sync README.md/.ko.md for autonomy levels, claude-lessons, MCP guide

- Agent Roster: new Autonomy column + explanation
- Tools: 7 -> 8, new Tool 8 (claude-lessons) section, /lessons row,
  repo layout tree, context-cost-tips row
- Nav bars + repo layout: MCP-GUIDE.md(.ko) link
- README.ko.md also gets the harness/pipeline Tool 6/7 detail sections and
  full 11-agent repo layout it was missing — it had fallen out of sync
  with README.md (only the slash-command table and top badges had been
  updated when those tools were added), which this branch's changes would
  otherwise have made worse

* Add tox for Python test/lint matrix + Rust MSRV verification (#31)

* test: add pytest suite + tox for tools/*.py

Nothing verified tools/*.py across Python versions before this — no
pytest suite, no CI job, just a handful of ad-hoc --help/smoke commands
in Makefile's test-python target. Adds:

- tests/conftest.py: run_tool/home/git_repo fixtures — every test runs
  the real CLI as a subprocess against an isolated $HOME (and, for the
  two tools that shell out to git, an isolated git repo), so nothing
  touches the machine's real ~/.claude/
- tests/test_*.py: one file per tool, 44 tests total, functional not
  just --help (add/list/search/show roundtrips, error paths, exit codes)
- tox.ini: py38-py313 (skip_missing_interpreters), lint (ruff check),
  fmt-check (ruff format --check)
- pyproject.toml: scoped ruff config — E/F/I/UP only, not the full
  default ruleset (which includes bandit/blind-except/naive-datetime
  rules that fight this codebase's deliberate style), E402 ignored
  since every tool puts VERSION right after the docstring before imports
- fmt-check / scripts/fmt.sh scoped to tools/claude-lessons.py + tests/
  only, not all of tools/: the older tools use a deliberate hand-aligned
  style (aligned `=`, aligned dict values) that `ruff format` would
  flatten repo-wide — not forcing that as a side effect of adding tox
- make tox: runs the full matrix (requires: pip install tox ruff)

Verified locally: tox (py39/py312/py313 available here, py38/py310/py311
skip cleanly), lint, and fmt-check all pass.

* fix: bugs found by the new pytest/tox matrix

Real, independently-reproduced bugs the new test suite caught:

- claude-cost.py: `set-budget` crashed with FileNotFoundError on a fresh
  install — `_save_budget` never created ~/.claude/ before writing
  cost-budget.json (snippet.py and claude-handoff.py already did this
  correctly; claude-cost.py's budget path was the one that didn't).
- claude-remind.py / claude-review-diff.py: both use `X | None` (PEP 604)
  type annotations, which raise TypeError at import time on Python < 3.10
  — every subcommand, including --help, was broken on 3.8/3.9 despite
  the "Python 3.8+" badge. Fixed with `from __future__ import
  annotations` (defers annotation evaluation, no behavior change).
  Confirmed via tox -e py39 before/after.

Also applies the (small, low-risk) findings from the new ruff lint gate
across tools/*.py: 4 unused imports, 1 dead local variable
(claude-handoff.py's `list`, left over from a removed date column), 4
`if x: y` one-liners split to satisfy E701, and one ambiguous single-
letter loop variable (`l` -> `line`). No behavior changes; tests still
pass after each.

* ci: add Python version matrix + Rust MSRV verification

- python-tests job: runs `tox -e py` across Python 3.8-3.13 via
  actions/setup-python (this is what actually caught the
  claude-remind.py/claude-review-diff.py 3.8/3.9 breakage in the
  previous commit — nothing exercised those versions before)
- python-lint job: `tox -e lint,fmt-check`
- rust-version = "1.75" declared in claude-tools/Cargo.toml, matching
  the README's existing "Rust 1.75+" badge (previously just a claim,
  not enforced anywhere)
- msrv job: `cargo msrv verify` — actually builds against the declared
  1.75 toolchain instead of only ever testing against `stable`

No cargo/rustc available in the environment this branch was authored
in, so the Rust-side change (rust-version field + msrv CI job) is
verified by CI on push, the same as the rest of this project's Rust
checks.

* docs: document tox/pytest and cargo-msrv workflow (EN/KO)

README.md/.ko.md: Makefile section (make tox, make msrv), repo layout
(tests/, tox.ini, pyproject.toml). Also fixes "make lint # clippy + ruff"
in both READMEs — the lint target only ever ran clippy; ruff wasn't
wired into it before this branch.

docs/CONTRIBUTING.md/.ko.md: tox usage in Development Setup, tests/
step added to "Adding a New Tool", tox/cargo-msrv checks added to the
PR checklist, cargo-msrv install/verify commands.

Closes #30

* fix: MSRV verify failed — Cargo.lock is lockfile v4, needs cargo 1.78+

CI caught this immediately: cargo-msrv installs rustc 1.75 per the
declared rust-version, but that toolchain's bundled cargo can't parse
Cargo.lock's `version = 4` format (stabilized in cargo 1.78) — a
structural incompatibility independent of whether the crate's actual
code needs 1.75 or newer. The pre-existing "Rust 1.75+" badge this
number was copied from was apparently never verified against the
checked-in lockfile either.

Bumped rust-version to 1.78 (README/README.ko badges updated to match)
and letting CI's msrv job re-verify — no cargo available locally to
confirm further.

* ci: use cargo-msrv find instead of verify — real MSRV is unknown

1.78 also failed: a transitive dep (clap 4.6.1's Cargo.toml) requires
the `edition2024` cargo feature, stabilized in Cargo 1.85 — unrelated
to claude-tools' own code, just how new the pinned dependency tree is.

No local cargo to bisect this by hand, and guessing one version per
push-and-wait-5-minutes cycle is slow. `cargo msrv find` bisects the
actual minimum compatible version in one run instead of asserting a
guess. Once it reports the real number, Cargo.toml's rust-version will
be set to match and the job switched back to `verify`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant