Add a ruff-config hook and publish ops as a two-hook repository - #6
Merged
Conversation
Every pyproject.toml claims 'see offworldlabs/ops for the canonical copy'. ops has never held one. Same false claim as 86cb417ty's dead-code script. ruff's extend takes only a local path, so there is no rev:-style sharing for pyproject.toml. But pre-commit clones the hook repo locally before running it, so a checker shipped from ops can compare against a canonical file sitting beside it — one rev pins the standard and the checker together. Comparison is semantic: select, ignore and per-file-ignores compare as sets, so comment whitespace and ordering do not matter. target-version is ignored because it tracks requires-python and is legitimately per-repo. Verified: all six repos pass today.
python exits 2 when it cannot open the script, the same code the checker uses for a missing pyproject.toml, so the bare rc check passed even with check-ruff-config.py absent — it would have kept passing if the script were later renamed or deleted.
Adds the ruff-config hook to the manifest, renames the workflow now that it covers more than one hook, and gives both hooks a try-repo positive control. Switches to repo-level hooks-v<MAJOR>.<MINOR> tags: pre-commit pins the whole repo at one rev, so per-hook tags were incoherent — pinning dead-code-v1.1 also silently decided which ruff-config you got. Also reworks the drift message printed by check-ruff-config.py: it now leads with "copy the entries above into this repo's pyproject.toml" (the common case) and lists editing ruff-shared.toml as the secondary path, since the prior wording read as though the standard itself should usually change.
The quickstart still showed rev: dead-code-v1.1 with a single hook, sitting above the paragraph that explains per-hook tags are superseded. A reader skimming top-to-bottom would copy the old scheme and get only one of the two hooks. Also updates check-dead-code.sh's header comment, which pointed the same old dead-code-v*.* scheme, to hooks-v*.* — kept version-agnostic (a placeholder, not a literal MAJOR.MINOR), since this file ships frozen inside whatever tag a consumer pins.
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.
First half of 86cb43xya — the deferred half of 86cb417ty. The six consumer PRs follow once
hooks-v1.0is tagged.Why
Every consumer's
pyproject.tomlcarries this:opshas never held a ruff config. It is the same false canonical-copy claim as the dead-code script, in a different file — and "keep in sync across repos" is an instruction to humans with no mechanism behind it.Why a checker rather than a shared file
Sharing by reference is impossible here: ruff's
extendaccepts only a local filesystem path (verified — given a URL it treats it as a relative filename and fails), and there is norev:-style sharing forpyproject.toml.But pre-commit clones the hook repo locally before running it. So a checker shipped from
opscan compare a consumer's config against a canonical file sitting beside it in the hook checkout — onerev:pins the standard and the checker together.What it enforces
line-lengthlint.select,lint.ignorelint.per-file-ignorestarget-versionrequires-python, legitimately per-repoComparison is semantic, not textual: values compare as sets. That is load-bearing — Tower-Finder is semantically identical to the baseline but pads its comments differently, so a text diff would falsely fail the largest repo in the fleet.
All six repos pass today, so adoption requires reconciling nobody's config.
Tag scheme changes
Per-hook tags stop making sense with two hooks: pre-commit pins the repo at one
rev, sodead-code-v1.1also silently decided whichruff-configyou got. Moving to repo-levelhooks-v<MAJOR>.<MINOR>. The olderdead-code-v1.0/v1.1remain valid.The dot is mandatory, not cosmetic — pre-commit warns "appears to be a mutable reference" for any rev containing neither a
.nor pure hex.Verification
check-ruff-config.py: 10 tests.check-dead-code.sh: 14 tests. Both green.pre-commit try-repopositive controls for both hooks: each plants a deliberate failure, requires the hook to catch it, then requires a clean version to pass. Review independently proved these are load-bearing by replacing the hooks with/bin/trueand with a checker that unconditionally exits 0 — both producedFAIL: hook did not catch....rc -eq 2for a missingpyproject.toml. Python exits 2 when it cannot open the script at all, so it passed whether or not the checker existed. Now asserts the message too: with the script hidden the suite goes from 1 passing to 0 passing, 10 failing.🤖 Generated with Claude Code
https://claude.ai/code/session_013ZcazeseXVpu8XrYA2tE1V