crapkit 0.4.11
Every README and handbook link is absolute
PyPI publishes the README verbatim as the long description, so its 36 repo-relative
links (docs/lanes.md, LICENSE, action.yml, ...) resolved against pypi.org and
answered nothing there. The handbook linked its five deep-reference pages as bare
lanes.md, which GitHub Pages serves as text/markdown, so the browser downloaded a
file where the reader expected a page. Both now link out by full URL, the README's
handbook link opens the rendered page on the project site, and two contracts hold
the relative form out.
The README pins uses: to the release it documents
The Action snippets in the README still said @v0.4.8 two releases later: the release
bump touched crapkit X.Y.Z and rev: vX.Y.Z and nothing else, and no test read the
third pin. A contract now holds every uses: pin in the README to crapkit.__version__,
so a bump that forgets it fails before the tag.
The 60-second start says when init writes a lane and when it writes a template
The comment on the crapkit init line promised "scopes, a coverage lane, .gitignore
lines" with no condition attached, so a reader whose repo carries neither a pytest marker
file nor a JS test setup expected a lane, got a commented template, and ran crapkit coverage into a config that measures nothing. The line now names what init recognizes
(pyproject.toml, pytest.ini or setup.cfg for pytest; a test script or vitest/jest in
package.json for the JS side) and what happens without one: the lane comes commented
out, init says to declare one, and docs/lanes.md is how to fill it in.
The formula says who coined the metric
The README printed CRAP = ccn^2 * (1 - cov)^3 + ccn with nothing under it about where
the score came from, which reads as if crapkit invented it. C.R.A.P., Change Risk
Anti-Patterns, was coined for crap4j by Alberto Savoia and Bob Evans in 2007, and the
handbook has said so from its first draft. The README now carries the same credit
directly under the formula, and a contract holds the four names in the paragraph that
formula sits in.
The sample worklist explains its own risk 0.0
The 60-second start prints a worklist row scoring risk 0.0, which a first-time reader
takes as a broken ranking rather than as arithmetic. Churn weight is position in the
commit log, so a one-commit repo weights every file the same and the ranking falls back to
ccn order. The sample now says that in a clause and points at the Risk section, which has
carried the full explanation all along.
The Action's whole-job snippet sets an interpreter up before installing into it
The snippet showed pip install -e ".[dev]" as the step before the action, with no
actions/setup-python in front of it. The action's own first step is
actions/setup-python, so a team copying that job installed their dependencies into
whatever interpreter the runner defaulted to and the lanes then ran on a different one:
the packages are on the machine and the lane still cannot import them. The snippet now
mirrors this repo's own dogfood job, actions/setup-python@v5 with python-version: "3.12" ahead of the install, and the python-version row of the inputs table says to
match the two. A contract holds the order in the snippet.
The plugin section says which skills Claude reaches on its own
The section listed three skills as one set, so a reader waited for Claude to pick up
crapkit-onboard and it never did. plugin/skills/crapkit-onboard/SKILL.md carries
disable-model-invocation: true: wiring a repo up happens once, and its description has
no business in every turn's window. The section now splits them: crapkit and
crapkit-recover are the two Claude reaches by itself, and the third is
/crapkit:crapkit-onboard, which you type.
The Install section says nothing leaves the machine
Nothing on the page told a reader evaluating crapkit for a private repo where their source
goes. It goes nowhere: scoring runs the reader's own test command locally and reads the
artifact it writes, and src/crapkit makes no network call of any kind. The Install
section now says so and links SECURITY.md, which has carried the same claim under
"It never phones home".
A fork's read-only token no longer fails the whole action
A pull request from a fork carries a read-only token, so the gh api call that posts
the comment came back 403. Composite run steps use bash's -e, and that 403 failed
the step and the job: the check went red on a pull request whose scoring had all
passed, and the verdict the steps above computed was never explained anywhere. The step
now opens code=0 and records what each gh api call got, the way the scoring steps
above it already did, and closes with a line naming the exit code and, when it is not
zero, the token as the likely cause. The comment lookup keeps a status of its own, so a
lookup that died on a closed pipe cannot blame the token for a comment that posted. No
step but the gate's now exits on a status it chose, and a contract test holds it there.
A run with no surviving lane prints each failure once
coverage printed every failed lane's refusal and then raised
every lane failed: <the same texts, joined>, which the CLI printed again. On the
screen most first-time users meet, a vitest lane with no coverage provider installed,
that was one eight-line block twice over, with the same absolute paths in both copies,
and nothing in the second copy that was not in the first. The closing line is now a
count and a pointer, every lane failed (1 of 1); the errors are above. README.md,
docs/lanes.md and the crapkit-recover skill show the new line.
doctor counts one file as one file
The per-scope line read ok scope 'calc': 1 files. It is the first proof a reader
gets that a scope path matches anything, and the quickstart publishes it, so the first
crapkit output a new user saw was ungrammatical. The noun now follows the count, and
zero keeps the plural, which is the FAIL case the line exists for.
The onboarding transcript names no machine and no release
The worked crapkit doctor --plugin-root example in plugin/skills/crapkit-onboard
was pasted off one machine: it printed that machine's home directory, spelled with the
name of whoever ran it, ending in an install six releases old. A reader matched their
own output against a path nobody else has and a version they were not meant to have.
It now reads <home>\.claude\plugins\cache\crapkit\crapkit\<version>, and
tests/unit/test_skills_contract.py holds every shipped skill page to it: no home
directory on any of them, and no release number on that line.