Skip to content

chore: add a justfile and AGENTS.md - #29

Merged
CameronBrooks11 merged 5 commits into
mainfrom
chore/justfile-and-agents-md
Sep 10, 2026
Merged

CameronBrooks11 merged 5 commits into
mainfrom
chore/justfile-and-agents-md

Conversation

@CameronBrooks11

Copy link
Copy Markdown
Member

Closes #28 (the first two boxes; the CONTRIBUTING.md box is left open deliberately -- see below).

The justfile

Every recipe inside check and test is a command .github/workflows/ci.yml runs, with CI's arguments rather than tidier ones. Derivation, job by job:

ci.yml recipe
uv sync --dev (all three jobs) setup
lint: uv run ruff check gerberdiff/ tests/ lint
lint: uv run ruff format --check gerberdiff/ tests/ fmt-check
lint: grep -rP '[^\x00-\x7F]' gerberdiff/ tests/ *.md *.toml ascii
typecheck: uv run mypy gerberdiff/ tests/ typecheck
test (Linux legs): pytest tests/ --cov=gerberdiff --cov-fail-under=90 -q test
test (Windows leg): pytest tests/ -q test-nocov

check is fmt-check lint ascii typecheck -- the lint and typecheck jobs in full. The paths are gerberdiff/ tests/ and not . because that is what CI passes; a recipe over . would check a different set of files and still be called the CI gate.

Deliberately not recipes:

  • --cov-report=xml. CI passes it on one leg only, to upload the report as an artifact. Locally it writes a file nobody reads.
  • pre-commit. Nothing in ci.yml runs it, so it is just hooks, outside check. A green check says nothing about the hooks -- the same gap netspec#30 and slicelab#24 record for their repos.
  • uv sync --locked. CI runs plain uv sync --dev, so setup does too. Making the local recipe stricter than CI would be a different claim than the one this file makes.

The ci.yml change

grep -rP descends into __pycache__, and .pyc files are full of non-ASCII bytes. CI never noticed because a fresh checkout has no caches; running the identical command locally after just test does:

$ just ascii
grep: gerberdiff/__pycache__/cli.cpython-312.pyc: binary file matches
grep: tests/__pycache__/test_gerber_parser.cpython-312-pytest-9.0.3.pyc: binary file matches
... 22 more
Non-ASCII characters found
error: Recipe `ascii` failed with exit code 1

--exclude-dir=__pycache__ is added to both the workflow step and the recipe, so they stay one command. It is a no-op on CI's checkout and changes nothing about which tracked files are checked -- git ls-files gerberdiff tests lists no binary file (py.typed and tests/__init__.py are empty).

Still red-capable after the change, checked by planting the character it exists to catch:

$ printf '\n<!-- \xe2\x80\x94 -->\n' >> AGENTS.md && just ascii
AGENTS.md:<!-- -- -->
Non-ASCII characters found
error: Recipe `ascii` failed with exit code 1
$ # restored
$ just ascii && echo ok
ok

The gate, run

$ just check
uv run ruff format --check gerberdiff/ tests/
66 files already formatted
uv run ruff check gerberdiff/ tests/
All checks passed!
uv run mypy gerberdiff/ tests/
Success: no issues found in 66 source files
CHECK EXIT: 0

(ascii is a shebang recipe, so just does not echo it; it ran between lint and typecheck and exited 0.)

$ just test
...
TOTAL                                    3266    138    96%
Required test coverage of 90% reached. Total coverage: 95.77%
487 passed in 346.42s (0:05:46)
TEST EXIT: 0

AGENTS.md

Names the org contract as the floor and carries only what is substantiated in this repo's code or docs:

  • The exit map settled in 0685cde (A3): 0 identical, 1 different, 2 indeterminate, 4 environment fault, 64 usage -- with 2 spelled as a third diff outcome rather than a verdict, different outranking indeterminate, and 2 returned regardless of --fail-on-diff. Each traced to the code that implements it (DiffOutcome and GeometryDiffResult.outcome in geometry/types.py, the geomdiff exit path in cli.py).
  • Only geomdiff can return 2. The raster diff verb exits 0, 1, 4 or 64 and has no third outcome, because it keeps no record of what it failed to model where layer_geometry.py counts an UNREPRESENTED_REASONS key. Recorded as the current state of the code, not as a design claim, and flagged as something not to add by inference.
  • import gerberdiff is Cairo-free -- render imports are lazy, which is why the Windows CI leg runs at all. Stated with the one-line command that checks it ('cairocffi' in sys.modules must be False, verified).
  • The ASCII rule, the 90% coverage gate and the cli.py coverage omission, the ok aggregator's explicit result != 'success' form and why the tolerant form is wrong for a workflow with no path filtering.
  • There is no docs/DECISIONS.md here, unlike every sibling. Said plainly, with where the reasoning actually lives instead.
  • planning/, artifacts/ and outputs/ are gitignored local scratch (.gitignore 221-223; git ls-files planning returns nothing), and planning/README.md records its own contents as historical. Flagged so nobody plans against it.

No Status claim is made that was not run. The verb list came from gerberdiff --help, the version from gerberdiff.__version__, and the Cairo-free claim from importing the package and reading sys.modules.

Left for #28

CONTRIBUTING.md still lists the four hand-typed commands that drift from CI. Pointing it at the recipes is a docs change with its own reviewable content and is kept out of this one.

grep -rP descends into compiled caches, whose .pyc bytes are not ASCII.
A clean CI checkout has none, so the step passed; anyone running the same
grep locally after a test run gets a failure on output nobody wrote.
Every recipe in check and test is a command ci.yml runs, with CI's arguments:
ruff format --check and ruff check over gerberdiff/ and tests/, the non-ASCII
grep, mypy, and pytest with --cov-fail-under=90. CONTRIBUTING.md's four
hand-typed commands were missing the format --check, the coverage gate and the
ASCII step, so passing them locally did not mean passing CI.

AGENTS.md names the org contract as the floor and records what is specific
here: the exit-code contract settled in 0685cde, that only geomdiff can return
2, that import gerberdiff stays Cairo-free, the ASCII rule, the coverage gate,
and that planning/ is gitignored scratch rather than a plan.
The lazy render import was credited with the Windows CI leg running. It is
not the mechanism: the raster tests skip through HAS_CAIRO in
tests/cairo_support.py, which exists because cairocffi raises OSError rather
than ImportError when the shared library is missing.
"Every sibling has one" is false: orlab has no docs/DECISIONS.md either.
The point stands without the comparison, so it is stated without one.
The exit table said `different` is exit 1. It is exit 1 only with
--fail-on-diff; without the flag a run with 28928 changes across 14 of 15
layers exits 0. Both existing records of this -- the EXIT_DIFFERENT docstring
and the 0.30.0 changelog entry -- carry the qualifier, and this file was the
copy that dropped it, which is exactly the gate it tells a reader to write:
`[ $? -eq 1 ]` never fires. Also note that 2 is unreachable once a change is
found, since `different` outranks `indeterminate`, and point a caller at
`summary.unrepresented` instead.

The ASCII rule claimed every tracked text file is enforced. The argv is
`gerberdiff/ tests/ *.md *.toml` with root-level globs, so 26 of 131 tracked
files are outside it -- all of docs/, all of .github/, and the justfile
carrying the rule. Scoped the sentence to what the check reads.

Name the test matrix as the largest deliberate gap between `just test` and
CI, and attribute kicad-happy to aklofas rather than implying it is a sibling.
@CameronBrooks11
CameronBrooks11 merged commit eb1eb14 into main Sep 10, 2026
7 checks passed
@CameronBrooks11
CameronBrooks11 deleted the chore/justfile-and-agents-md branch September 10, 2026 03:15
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.

No justfile and no AGENTS.md: the org's pre-commit gate cannot be run here

1 participant