Skip to content

Wire the PDF output, and stop ignoring ReportConfig - #28

Merged
Tippel-AI merged 2 commits into
mainfrom
feat/wire-pdf
Aug 31, 2026
Merged

Wire the PDF output, and stop ignoring ReportConfig#28
Tippel-AI merged 2 commits into
mainfrom
feat/wire-pdf

Conversation

@Tippel-AI

Copy link
Copy Markdown
Owner

Closes #21. Sixth step of the audit remediation — and the last item in step 3 of
the plan.

The gap

354 lines of PDF renderer, 91% and 90% covered, unreachable:

  • ReportConfig.formats was Literal["json", "summary"]
  • cli.py imported neither renderer
  • markproof run --help mentioned no PDF

…while the README said "PDF is opt-in (pip install "markproof[pdf]")", which
reads as install the extra and you get PDFs. You did not.

The PDF is the artefact you hand an auditor — the one output whose whole purpose
is to be read by someone who will never run the tool. Existing only in the source
tree made it the gap least likely to be noticed and most likely to matter.

Wired

report:
  formats: [json, summary, pdf]     # needs: pipx install "markproof[pdf]"

Imported at the point of use, never at module scope: the default output path must
not depend on a package the base install does not ship. When a renderer is
unavailable, its own message is passed through — WeasyPrint's covers the case
that actually bites people, where the wheel is installed but ctypes cannot find
Pango or cairo, and nothing reconstructed in the CLI would say it better.

Two things it was sitting next to

report.sign_key was validated and then ignored. An operator writing
sign_key: env:CI_SIGNING_KEY got an unsigned report and no hint why.
MARKPROOF_SIGNING_KEY stays the fallback, because that is what CI documentation
everywhere assumes.

report.output_dir was ignored too. Asking for a PDF in the config and
getting nothing because --report-dir was not also passed would discard the
request silently, so a non-default format is now sufficient on its own.

And the same bug as #22, one layer further on

Report writing sat outside the guarded block, so a missing extra propagated as a
traceback and exit 1 — which in this tool means a rule failed. Exit 2 now,
with a sentence. Worth noting that #22's fix did not generalise on its own; this
needed finding separately, which is what the test for it now prevents.

Tests

Six, including: a real PDF is written (%PDF-1.4, two pages); formats: [json]
alone does not also write a summary — proving the field is honoured rather
than decorative; and an unavailable renderer yields the install command, not a
stack trace.

437 tests, ruff + mypy strict clean.

🤖 Generated with Claude Code

Tippel-AI and others added 2 commits August 31, 2026 21:33
Closes #21.

`ReportConfig.formats` was typed to accept only json and summary, the CLI never
imported either PDF renderer, and `run --help` said nothing about PDF. So 354
lines of renderer — 282 statements at 91% coverage, 72 at 90% — could not be
produced by any invocation of the shipped tool, while the README advertised
`pip install "markproof[pdf]"` as though installing the extra got you one.

The PDF is the artefact you hand to an auditor: the one output whose entire
purpose is to be read by someone who will never run the tool. Having it exist only
in the source tree was the gap least likely to be noticed and most likely to
matter.

`formats` now accepts `pdf` and `pdf-html`, and the renderer is imported at the
point of use rather than at module scope — the default output path must never
depend on a package the base install does not ship. When a renderer is
unavailable, its own message is passed through: WeasyPrint's covers the case that
actually bites people, where the wheel is installed but ctypes cannot find Pango
or cairo, and nothing reconstructed here would say it better.

Two neighbouring things the audit found, fixed with it:

`report.sign_key` was validated and then ignored, so an operator who wrote
`sign_key: env:CI_SIGNING_KEY` got an unsigned report and no hint why.
MARKPROOF_SIGNING_KEY stays the fallback, because that is what CI documentation
everywhere assumes.

`report.output_dir` was likewise ignored. Asking for a PDF in the config and
getting nothing because `--report-dir` was not also passed would discard the
request silently, so a non-default format is now enough on its own.

Writing the report was also outside the guarded block — the same defect as #22 one
layer further on. A missing extra propagated as a traceback and exit 1, which in
this tool means "a rule failed". It is exit 2 now, with a sentence.

Six CLI tests, including that a real PDF is written (`%PDF-` and two pages), that
asking for `[json]` alone does not also write a summary, and that a missing
renderer produces the install command rather than a stack trace.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pytest jobs installed only the dev extra, so the newly wired PDF output was
untested on every runner — the same shape of gap as the one this PR closes. CI now
installs [pdf] as well, and a contributor without it gets a skip rather than a
puzzling failure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Tippel-AI
Tippel-AI merged commit 90be371 into main Aug 31, 2026
8 checks passed
@Tippel-AI
Tippel-AI deleted the feat/wire-pdf branch August 31, 2026 19:38
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.

The PDF output tier is unreachable from the CLI

1 participant