ci(skills): lint, type-check and test the Python under skills/ - #1054
Open
potiuk wants to merge 1 commit into
Open
ci(skills): lint, type-check and test the Python under skills/#1054potiuk wants to merge 1 commit into
potiuk wants to merge 1 commit into
Conversation
Closes apache#1053. Nothing checked the Python in `skills/`. The workspace checks iterate over `[tool.uv.workspace] members` and every member lived under `tools/`, so ruff, mypy, and pytest never saw `skills/**`. Eight helper scripts and guards sat there unchecked, and apache#1049 added the first test file under the tree — nine tests that CI would never have run. Reverting `pr_link.py` would have left the checks green. Declares `skills/` as one workspace member whose `pyproject.toml` is a config carrier rather than a package (`package = false`). The existing machinery does the rest: `run-workspace-check.sh` discovers which checks apply from the sections present, and `tests.yml` derives its matrix from the members list, so this adds a `pytest (skills)` job with no workflow edit. Per-skill packaging was the alternative and is worse: skills are symlinked into adopter repos one directory at a time, so build metadata inside each would leak into every adopter. `skills/pyproject.toml` is not symlinked — the relays point at `skills/<name>`, never at `skills/` itself. Turning the checks on found seven real things, all fixed here: - `scan_ci_runners.py` — `zip()` without `strict=`; two same-named `futures`/`future` bindings of different types in one function, which is why the second needed renaming before mypy could annotate it; an unannotated `rows`; and `step.get("with")` called twice so the isinstance narrowing did not stick. - `collect_status.py` — `l` as a variable name, and a mixed-value dict literal inferred as `dict[str, object]`, which made the later indexed assignment invalid. - `test_pr_link.py` — a stale `# noqa: E402` that no longer suppressed anything. None of these change behaviour; they are the annotations and renames the checks require, which is the point — they were invisible until now. `ruff format` is skipped for this member via `[tool.magpie.checks]`. It wants to reflow ~280 lines across six pre-existing scripts, none of them touched here, and that churn would bury the change behind a mechanical reformat. Lint, types and tests all run; formatting can land separately and the skip removed. Verified the coverage is real rather than nominal: breaking the OSC 8 emission in `pr_link.py` now fails three tests under the workspace runner, where before the same break produced no signal at all. Generated-by: Claude Code (Opus 5)
21 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
skills/. The workspace checks iterate over[tool.uv.workspace] members, every member lived undertools/, soruff,mypy, andpytestnever sawskills/**— eight helper scripts and guards, plus the nine tests fix(pr-management-triage): centralize terminal PR links #1049 had just added, none of them run.skills/as one workspace member whosepyproject.tomlis a config carrier, not a package (package = false). The existing machinery does the rest:run-workspace-check.shdiscovers applicable checks from the sections present, andtests.ymlderives its matrix from the members list, so this adds apytest (skills)job with no workflow edit.Closes #1053.
Type of change
.claude/skills/<name>/) — eval fixtures updated belowtools/<system>/*.md)tools/*/withpyproject.toml)docs/,README.md,CONTRIBUTING.md)projects/_template/)prek, workflows, validators)Test plan
prek run --all-filespasses (23 hooks)ruff check,mypy, andpytestall run and pass through the workspace runner for the new member —uv run --project . python -m …fromskills/pr_link.pynow fails three tests under the workspace runner. Before this change the same break produced no signal at all — that is the whole point of the PR, so it seemed worth proving rather than asserting.check-workspace-memberspasses — it already scans top-level directories (widened in fix(ai-tutors): stop nested code fences inverting the fence state #1011), soskillsis discoverable and not reported as drift.RFC-AI-0004 compliance
(None apply — CI wiring plus type/lint fixes.)
Linked issues
Closes #1053. Follow-up to #1049, which added the first tests under
skills/, and to #1011, which fixed the same class of gap forai-tutors/.Notes for reviewers (optional)
Why one member and not per-skill packaging. Skills are symlinked into adopter repos one directory at a time (
.claude/skills/magpie-<name>→skills/<name>), so apyproject.tomlinside each skill would leak build metadata into every adopter.skills/pyproject.tomlis never symlinked — the relays point atskills/<name>, never atskills/itself. I checked that before choosing this shape.What the checks found. None of it changes behaviour; these are the annotations and renames the checks require, which is rather the point — they were invisible until now.
scan_ci_runners.py—zip()withoutstrict=; two same-namedfutures/futurebindings of different types in one function, which is why the second pair needed renaming before mypy could annotate them; an unannotatedrows; andstep.get("with")called twice, so theisinstancenarrowing never stuck.collect_status.py—las a variable name, and a mixed-value dict literal inferred asdict[str, object], which made a later indexed assignment invalid.test_pr_link.py— a stale# noqa: E402suppressing nothing.One deliberate omission:
ruff formatis skipped for this member via[tool.magpie.checks] skip = ["ruff-format"]. The formatter wants to reflow ~280 lines across six pre-existing scripts — long set literals and regex constants — none of which this change touches. Folding that in would bury the substance behind a mechanical reformat nobody can review line by line. Lint, types and tests all run; formatting can land as its own commit and the skip be deleted then. Happy to do that immediately after if you would rather have it in one go.