Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Changed

- trivy: use 0.76.0.1
- `check:deptry` and `check:trivy` ignore `.claude/`, so a git worktree created
there - a complete second checkout inside the repository - no longer fails
`task check` with findings from its own copy of the tests and its own lock
file (#109)
- `ARG002` on an argument whose presence the framework fixes - `inputs` in a
workflow task with no input ports - is a settled suppression in the shipped
rules, beside `PLR0913` and `PLR0917`, so it no longer needs a human decision
per project; `plugin-implementation` says why renaming it to `_inputs` is the
wrong answer, and the example plugin now carries the reason above its
`# noqa` (#108)
- the example files say to replace them with the first real task in the same
change, rather than to remove them after bootstrapping: an emptied package
fails `task check` three times over - deptry on the now unused
`cmem-plugin-base`, pytest on collecting no tests, and the coverage badge on
a package with no statements (#107)


## [9.9.0] 2026-10-13
Expand Down
20 changes: 20 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,23 @@ added.
A project that wants the check can add a `check:links` task to its own
`TaskfileCustom.yaml`, which is exactly what that file is for, and run it on a
schedule rather than on every commit — which is the cadence link rot deserves.

### The coverage badge is not made to tolerate a package with no code

`check:pytest` ends by generating the coverage badge, and on a package with
zero statements that step fails:
`ValueError: Computed line rate (0) is different from the one in the file
(1.0)` — `coverage.xml` records `line-rate="1.0"` for nothing at all, genbadge
recomputes it as 0 and refuses the file. Report #107 asked for the badge step
to be skipped when the package has no lines, so that an emptied package fails
on deptry alone, which is the check that is actually right.

Declined, because the state it would rescue is one the template now tells you
not to create. The example files say to replace them with the first real task
in the same change, precisely because a package emptied beforehand has no green
baseline to start from. And the badge is not the first thing to go: `pytest`
exits 5 on having collected no tests before genbadge runs at all, and deptry
reports `DEP002 'cmem-plugin-base' defined as a dependency but not used`. Three
correct signals say the same true thing, so silencing the third one changes
nothing a user would notice — while the shell conditional it needs would have
to hold on the Windows branch of that task too.
11 changes: 10 additions & 1 deletion src/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,14 @@ tasks:
deps:
- prepare
cmds:
# .claude/ is skipped because a git worktree created there is a second
# checkout inside the repository, whose lock file is scanned as if it
# were this one - on an older branch, with older findings.
- >
poetry run trivy fs
--include-dev-deps
--scanners vuln
--skip-dirs .claude
--skip-files .poetry/plugins/poetry.lock
--exit-code 1
.
Expand All @@ -177,7 +181,12 @@ tasks:
deps:
- prepare
cmds:
- poetry run deptry .
# deptry's default excludes cover a tests directory at the root but not
# a nested copy of one, so the worktree described above would be read as
# source and its test imports reported.
- >
poetry run deptry .
--extend-exclude '\.claude'

check:ruff:
desc: Complain about everything else
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Example tests.

Remove this and other example files after bootstrapping your project.
Replace these with tests for your first real module and delete the example
files in the same change - a package emptied beforehand fails `task check` on
deptry and on the coverage badge.
"""

from {{ package_dir }}.example import double
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Plugin tests.

Remove this and other example files after bootstrapping your project.
Replace these with tests for your first real task and delete the example
files in the same change - a package emptied beforehand fails `task check` on
deptry and on the coverage badge.
"""

import os
Expand Down
8 changes: 8 additions & 0 deletions src/{{ '.claude' }}/rules/copier-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ reason above it. Neither escape ruff suggests is available, since keyword-only
arguments answer `PLR0917` but not `PLR0913`, and folding the arguments into
one object breaks the one-argument-per-option mapping the framework relies on.

`ARG002` is settled on the same grounds, one argument further in: a method the
framework calls has the signature the framework declares, so an argument the
implementation does not need cannot be dropped - a workflow task with no input
ports never reads `inputs`. Suppress it on the argument, again with the reason
above it. Renaming it to `_inputs` silences ruff as well, and is wrong here:
the name belongs to an API the caller may use by keyword, as this project's own
tests do.

## Every user visible change gets a changelog entry

`CHANGELOG.md` follows [Keep a Changelog](https://keepachangelog.com/). Add an
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,33 @@ start failing on it without anything in the task changing. Suppress it on the
constructor only - it stays a real finding on an ordinary function, which is
why it is not in the `ignore` list in `pyproject.toml`.

## An argument the task never reads

`ARG002` complains about an unused method argument, and a task declaring
`FixedNumberOfInputs([])` generates its own entities - so `execute()` never
reads `inputs`, while `WorkflowPlugin` decides the signature all the same. A
`TransformPlugin` or a `PluginAction` ignoring an argument the framework
supplies is the same case.

This is the standing exception the lint rules describe, so it needs no fresh
decision either. Suppress the code on the argument, with the reason above it:

```python
def execute(
self,
# This task has no input ports, so it never reads inputs - but the
# signature is the one WorkflowPlugin declares.
inputs: Sequence[Entities], # noqa: ARG002
context: ExecutionContext,
) -> Entities:
```

Do not rename it to `_inputs` instead. Ruff accepts the underscore and mypy
permits the rename, but the name is part of the API the caller uses:
`execute(inputs=(), context=TestExecutionContext())` is a keyword call, which is
how `tests/test_example.py` and the `plugin-testing` skill invoke a task, and it
raises `TypeError` against a renamed argument.

## Parameters that carry secrets

A password, token or API key is typed, never a plain string:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Example module

Remove this and other example files after bootstrapping your project.
Replace this with your first real module and delete the example files in the
same change - a package emptied beforehand fails `task check` on deptry and on
the coverage badge.
"""


Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""lifetime(age) transform plugin module

Remove this and other example files after bootstrapping your project.
Replace this with your first real task and delete the example files in the
same change - a package emptied beforehand fails `task check` on deptry and on
the coverage badge.
"""

import datetime
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Random values workflow plugin module

Remove this and other example files after bootstrapping your project.
Replace this with your first real task and delete the example files in the
same change - a package emptied beforehand fails `task check` on deptry and on
the coverage badge.
"""

import uuid
Expand Down Expand Up @@ -79,6 +81,8 @@ class DollyPlugin(WorkflowPlugin):

def execute(
self,
# This task has no input ports, so it never reads inputs - but the
# signature is the one WorkflowPlugin declares.
inputs: Sequence[Entities], # noqa: ARG002
context: ExecutionContext,
) -> Entities:
Expand Down
Loading