Check the example report fiches the way the figures are checked - #471
Conversation
Five committed fiches no longer matched the code that writes them, and one had never been committed at all. Nothing in CI looked: the conformance report, the generated API reference, the evidence pages and the documentation figures each have a staleness gate, and .github/reports had none, so a fiche could sit a release behind for weeks with nobody the wiser. iso1999_nipts and iso1996_tone_audibility still carried their shaded regions as they were drawn before fills started being derived from the page they sit on: the fractile band as the old 50 % composite #ffddbb rather than the #ffe7d2 wash, and the decisive tone's critical band as the hard-coded #eaf0f8 rather than #c8deed. The printed values and the verdicts were already right, so nothing there could be misread, but the two fiches the documentation offers as examples of what the library prints were showing a paler band than the library prints. ebu_r128_loudness and the two iec61260_filter fiches had the same kind of drift a shade smaller. All five are regenerated. iec61043_intensity was registered in the generator and documented in the intensity guide, and its PDF and preview were never committed. The example is a good one, matching the worked class verification the guide already walks through, so it is generated here and embedded in the guide in both languages like every other worked example. The gate itself is scripts/check_reports.py, run by a new Example report fiches up to date job. A byte diff is not an option, for the reason it was abandoned for the figures: every fiche embeds its plot as vector geometry, and GitHub's heterogeneous runners compute a few of those coordinates a last bit apart, which rewrites the page for no visible reason. Each fiche is compared instead on what it says and on what it looks like: the extracted text exactly, page by page, and the committed preview within the same two-criteria pixel tolerance the figures use. The tolerance was measured, not assumed. matplotlib writes SVG coordinates through %f, so one unit in the last emitted decimal is the largest text change a last-bit difference can make; re-rendering five fiches with every plotted coordinate moved by that much, in a random direction, moved at most 8 pixels past the level threshold and the whole-page RMS by 0.074. The two stale fiches moved 28710 and 27629 pixels, and the subtler restyled fills moved the RMS to 0.79 and 1.33. The thresholds sit between the two populations, at 64 pixels and an RMS of 0.5: about an order of magnitude clear of the noise, and a factor of two under the smallest real change. They are tighter than the figures' on purpose, since a fiche preview is a flat document page rasterized by a pinned binary rasterizer rather than a plot raster, and the figures' looser bound would have let the restyled fills through. Around it: requirements-reports.txt pins the svglib/reportlab/pypdfium2 half of the chain the way requirements-figures.txt pins the matplotlib half, make reports clears the directory first so a fiche that is no longer produced is actually removed, and the generator pins the numerical thread pools like the figure generator so a runner with a different core count cannot reorder a sum into the rendered page. The staleness helpers the two checks share move to scripts/generated_assets.py. A unit test also asserts every registered fiche has a committed render, which fails in the same second as the commit that forgets one and needs no regeneration to do it.
The guide embeds it on the site in both languages; docs/intensity.md is the copy GitHub renders with no build step, and every other guide with a fiche carries the same preview there.
Three places still described the committed previews as unchecked. The module docstring, the Makefile and the workflow comment were brought into line with the new job; the generator's own preview writer, the Astro component that embeds the preview and the labeler's file lists were not. The labeler entry matters on its own: scripts/generated_assets.py is what the figures and the fiches now share, so a change to it belongs to both groups and was landing in neither.
The calibration note generalised from five fiches to all 67. Measured across the whole set, the upper-bound displacement pushes five of them past the thresholds, all plots whose bar edges land on a pixel boundary, and one character of a printed field moves 53 to 102 pixels rather than the 89 quoted, so most single-character edits are caught by the RMS alone. The thresholds stay: the bound is not what a real machine does, raising it would stop catching the restyled fills, and the exact text comparison covers every printed value regardless.
|
You've reached the fair usage limit for the Bito Code Review Agent trial. Upgrade to a paid plan or contact support@bito.in for assistance. Learn more |
Reviewer's GuideAdds a tolerance-aware CI staleness gate for generated report fiches, refactors shared raster comparison helpers, tightens figure/raster tolerance configuration, regenerates and embeds the missing/updated fiches, and adds tests to ensure both the checker and fiche generation stay in sync. Sequence diagram for the new example report fiches CI staleness checksequenceDiagram
actor Dev
participant GitHubCI as GitHubActions
participant Make as make_reports
participant Gen as scripts_generate_reports_py
participant Check as scripts_check_reports_py
participant Git as git_HEAD
Dev->>GitHubCI: push / open PR
GitHubCI->>Make: run `make reports`
Make->>Gen: python scripts/generate_reports.py
Gen-->>Make: regenerate .github_reports PDFs/WebPs
GitHubCI->>Check: python scripts/check_reports.py
Check->>Git: committed_bytes(path)
Check->>Git: tracked_files(REPORT_DIR)
Check->>Check: pdf_problem(old_pdf, new_pdf)
Check->>Check: raster_problem(old_webp, new_webp, RASTER_TOL)
alt any fiche differs beyond tolerance or is added/removed
Check-->>GitHubCI: exit 1 (reports stale)
GitHubCI-->>Dev: job "Example report fiches up to date" fails
else all fiches match within tolerance
Check-->>GitHubCI: exit 0
GitHubCI-->>Dev: job succeeds
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe pull request adds tolerance-aware validation for generated report PDFs and WebP previews. It centralizes raster checks, adds deterministic report generation and CI enforcement, regenerates report assets, and documents the IEC 61043 report in English and Spanish. ChangesReport validation and publication
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CI as GitHub Actions
participant Make as Makefile reports target
participant Generator as generate_reports.py
participant Checker as check_reports.py
CI->>Make: run report generation
Make->>Generator: generate PDFs and WebP previews
CI->>Checker: validate committed outputs
Checker-->>CI: return success or stale-report failure
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds a CI “staleness gate” for the committed example report fiches under .github/reports, bringing them to parity with existing figure/API/conformance drift checks. It also embeds a newly committed intensity example fiche into the docs (EN/ES + docs/ mirror) and factors shared asset-compare utilities into a common script module.
Changes:
- Add
scripts/check_reports.py+ pinned render dependencies to regenerate and compare.github/reports(PDF text exact + preview raster within tolerance) in a new CI job. - Refactor shared “committed vs regenerated” helpers into
scripts/generated_assets.pyand reuse them fromscripts/check_figures.py. - Add tests to prevent missing/unstored fiche renders and embed the intensity example fiche in documentation.
Reviewed changes
Copilot reviewed 15 out of 27 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
tests/test_generate_reports.py |
Adds a test to assert every registered fiche has committed PDF + preview files. |
tests/test_check_reports.py |
New unit tests for report staleness tolerances (PDF text + raster tolerance). |
site/src/content/docs/guides/intensity.mdx |
Embeds the new intensity example fiche preview/link in the EN guide. |
site/src/content/docs/es/guides/intensity.mdx |
Embeds the new intensity example fiche preview/link in the ES guide. |
site/src/components/ReportPreview.astro |
Updates component docs to reflect tolerance-based CI checking for previews. |
scripts/generated_assets.py |
Introduces shared git/asset helpers + raster tolerance comparator. |
scripts/generate_reports.py |
Pins threadpool env vars early for deterministic report rendering; updates docs. |
scripts/check_reports.py |
Implements tolerance-aware staleness check for .github/reports PDFs + previews. |
scripts/check_figures.py |
Refactors to use shared helpers for committed bytes/tracked files/raster compare. |
requirements-reports.txt |
Adds a pinned rendering stack for the reports CI job. |
Makefile |
Makes make reports delete stale outputs first; runs generator under FIGURE_ENV. |
docs/intensity.md |
Embeds the intensity example fiche preview/link in the docs/ mirror. |
CHANGELOG.md |
Documents the new fiche staleness gate and regenerated/added example fiche(s). |
.github/workflows/python-app.yml |
Adds the new “Example report fiches up to date” CI job. |
.github/labeler.yml |
Labels report/figure changes to include new scripts and requirements file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pytest.importorskip("PIL") | ||
| from PIL import Image | ||
|
|
There was a problem hiding this comment.
Correct, and it failed rather than skipped. Blocking pypdfium2 with a meta-path finder that raises ModuleNotFoundError, which is what an absent package looks like, produced two failures: pdf_problem catches the ImportError and returns "could not extract text", so both tests then assert against text that was never read. With the skip added beside Pillow's it is one clean skip, and eight passing with the package present. Fixed in a0d12cb.
|
The requested test file |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/labeler.yml:
- Around line 45-47: Add requirements-figures.txt to the existing reports label
rule alongside scripts/check_reports.py, scripts/generated_assets.py, and
requirements-reports.txt.
In @.github/workflows/python-app.yml:
- Around line 260-264: Pin the actions used by the workflow, including checkout
and setup-python, to their full immutable commit SHAs instead of version tags.
Update every other action reference in the workflow similarly, preserving each
action’s current version and behavior.
In `@Makefile`:
- Around line 141-145: Update the report-generation recipe around
scripts/generate_reports.py so it writes generated PDFs and WebPs to a temporary
output directory first, preserving the existing .github/reports files while
generation runs. Only after the generator succeeds should the recipe replace the
committed renders, including removal of stale files; ensure failure leaves the
existing generated examples intact.
In `@scripts/generate_reports.py`:
- Around line 29-38: The module-level loop in scripts/generate_reports.py does
not reliably enforce deterministic threading because it preserves existing
values and runs after process initialization. Remove this runtime fallback and
ensure all listed thread-limit variables are explicitly set to 1 in FIGURE_ENV
and the CI environment before launching Python or pytest workers.
In `@tests/test_generate_reports.py`:
- Around line 52-65: Update test_every_registered_fiche_is_committed and the
_EXAMPLES registry so the test obtains each report filename from metadata or a
name-only registry without invoking factory(). Preserve the existing
committed-path and preview-path existence checks while keeping report generation
and plot setup out of this test.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a61ef17f-af85-4161-94bb-ad9437869ee3
⛔ Files ignored due to path filters (6)
.github/reports/ebu_r128_loudness_example.pdfis excluded by!**/*.pdf.github/reports/iec61043_intensity_example.pdfis excluded by!**/*.pdf.github/reports/iec61260_filter_1995_example.pdfis excluded by!**/*.pdf.github/reports/iec61260_filter_example.pdfis excluded by!**/*.pdf.github/reports/iso1996_tone_audibility_example.pdfis excluded by!**/*.pdf.github/reports/iso1999_nipts_example.pdfis excluded by!**/*.pdf
📒 Files selected for processing (21)
.github/labeler.yml.github/reports/ebu_r128_loudness_example.webp.github/reports/iec61043_intensity_example.webp.github/reports/iec61260_filter_1995_example.webp.github/reports/iec61260_filter_example.webp.github/reports/iso1996_tone_audibility_example.webp.github/reports/iso1999_nipts_example.webp.github/workflows/python-app.ymlCHANGELOG.mdMakefiledocs/intensity.mdrequirements-reports.txtscripts/check_figures.pyscripts/check_reports.pyscripts/generate_reports.pyscripts/generated_assets.pysite/src/components/ReportPreview.astrosite/src/content/docs/es/guides/intensity.mdxsite/src/content/docs/guides/intensity.mdxtests/test_check_reports.pytests/test_generate_reports.py
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #471 +/- ##
=======================================
Coverage 96.97% 96.97%
=======================================
Files 249 249
Lines 38433 38433
=======================================
Hits 37271 37271
Misses 1162 1162 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Numerical conformance report✅ 533/533 conformance checks pass across 57 domains and 362 standards - filters class 1 - weightings within IEC 61672-1 class 1. Each row pins a standard clause to its expected normative value and the value the library computes. Every section below is collapsible and stays collapsed while all of its rows pass; a section with any failing row opens automatically. ✅ Numerical validation - filters & weightings: class showcase (IEC 61260-1 · IEC 61672-1 · ISO 7196)IEC 61260-1:2014 class per filter architecture (order 6, one-third-octave, 100 Hz-10 kHz, fs = 48 kHz). For each architecture the table shows, at its binding band, the measured relative attenuation and the class-1 limit it must clear, so the number and the range it must sit in are both visible. A positive margin means the acceptance limits are met with that much room.
Only Butterworth (the library default) and Chebyshev-II are class-compliant architectures. Chebyshev-I and elliptic trade the mask for passband ripple, and Bessel for a maximally-flat group delay (soft rolloff); they cannot satisfy the IEC 61260-1 Class 1/2 attenuation mask by construction, so they are labelled By design - this is expected, not a failure or regression. Frequency-weighting conformance (A/C: IEC 61672-1 Table 3; G: ISO 7196 A.3). The max deviation from nominal is informational (it falls at a frequency extreme where the tolerance is widest and asymmetric); compliance is judged at the binding frequency - the one with the least headroom - where the deviation, the applicable tolerance band and the headroom are shown together.
✅ Filters & weightings: 100% (10/10)
✅ Levels & dosimetry: 100% (9/9)
✅ Room & building acoustics: 100% (72/72)
✅ Room acoustics: 100% (16/16)
✅ Psychoacoustics: 100% (14/14)
✅ Speech transmission (IEC 60268-16): 100% (10/10)
✅ System measurement (Golay / Kirkeby / Mueller-Massarani): 100% (5/5)
✅ Intensity & sound power: 100% (10/10)
✅ Building prediction & uncertainty: 100% (15/15)
✅ Outdoor propagation & occupational exposure: 100% (10/10)
✅ Materials: absorption, airflow & impedance: 100% (6/6)
✅ Scattering & diffusion (ISO 17497): 100% (14/14)
✅ In-situ road absorption (ISO 13472): 100% (3/3)
✅ Precision sound power (ISO 3745 / 9614-3): 100% (4/4)
✅ Human vibration (ISO 8041 / 2631 / 5349): 100% (15/15)
✅ Speech intelligibility (ANSI S3.5-1997): 100% (24/24)
✅ Objective intelligibility (STOI / ESTOI): 100% (3/3)
✅ Impulsive-sound prominence (NT ACOU 112): 100% (2/2)
✅ Impulsive-sound prominence (ISO/PAS 1996-3): 100% (2/2)
✅ Room noise (ANSI S12.2-2019): 100% (3/3)
✅ Hearing threshold (ISO 7029 / ISO 389-7): 100% (3/3)
✅ Measurement uncertainty (GUM / Supplement 1): 100% (7/7)
✅ Noise-induced hearing loss (ISO 1999): 100% (6/6)
✅ Multiple-shock whole-body vibration (ISO 2631-5): 100% (6/6)
✅ Sound absorption in enclosed spaces (EN 12354-6): 100% (2/2)
✅ Prominent discrete tones (ECMA-418-1): 100% (2/2)
✅ Tonal audibility (ISO/PAS 20065): 100% (11/11)
✅ Psychoacoustic annoyance & fluctuation strength (Fastl & Zwicker): 100% (3/3)
✅ Electroacoustics: distortion & frequency response: 100% (20/20)
✅ Calibrated spectral analysis (Bendat & Piersol): 100% (12/12)
✅ Multiple-input coherence (Bendat & Piersol): 100% (5/5)
✅ Time-frequency analysis (Bendat & Piersol): 100% (3/3)
✅ Correlation, time delay and envelope (B&P / Knapp & Carter): 100% (7/7)
✅ Cepstrum, liftering and envelope spectrum (Havelock / B&P): 100% (3/3)
✅ Time synchronous averaging (McFadden 1987): 100% (5/5)
✅ Data qualification and Rice statistics (Bendat & Piersol): 100% (8/8)
✅ Underwater acoustics (ISO 18405/17208/18406): 100% (6/6)
✅ Underwater sound propagation (transmission loss): 100% (16/16)
✅ Underwater propagation regimes (Weston flux theory): 100% (3/3)
✅ Marine-mammal auditory weighting (NMFS / Southall): 100% (4/4)
✅ Underwater numerical propagation (modes / rays / PE): 100% (4/4)
✅ Aircraft noise (ICAO Annex 16 / IEC 61265): 100% (15/15)
✅ Rotorcraft noise (ECAC Doc 32 / NORAH2): 100% (12/12)
✅ CNOSSOS-EU road source (Directive 2002/49/EC Annex II): 100% (6/6)
✅ Wind-turbine noise (IEC 61400-11): 100% (3/3)
✅ Porous & multilayer absorbers (Mechel / Bies / Cox & D'Antonio): 100% (20/20)
✅ Slow-sound perfect absorbers (Jimenez et al. Appl. Sci. 2017): 100% (3/3)
✅ Program loudness (ITU-R BS.1770 / EBU R 128): 100% (8/8)
✅ 2D FDTD wave simulation (Attenborough & Van Renterghem 2021, Ch. 4): 100% (4/4)
✅ Swept-sine distortion & phase utilities (Farina / Novak): 100% (7/7)
✅ Spherical ground & barriers (Attenborough / Salomons / Bies): 100% (7/7)
✅ Panel & aperture sound insulation (Bies / Hopkins / Cremer): 100% (17/17)
✅ Bending-wave plate-junction transmission (Cremer / Craik / Hopkins): 100% (6/6)
✅ Atmospheric refraction (Salomons rays / GFPE): 100% (3/3)
✅ Electroacoustics: 100% (9/9)
✅ Industrial noise control: 100% (22/22)
✅ CNOSSOS-EU railway source (Directive 2002/49/EC Annex II): 100% (8/8)
Tests & coverage — 47040 tests, 0 failures (✅ all green)
Conformance harness: |
The module already skips itself without Pillow, but the three tests that drive the PDF half of the gate go through pypdfium2, and nothing said so. Without it installed they do not skip: pdf_problem catches the ImportError and reports "could not extract text", which is the right answer for a corrupt fiche and the wrong one here, so two of the three fail on an assertion about text that was never read. Blocking the import turns them from two failures into a module-level skip.
The reports label listed requirements-reports.txt but not requirements-figures.txt, and the fiche job installs both: the plot the fiche embeds is drawn by the matplotlib and NumPy pinned in the figures file, so a bump there can move the rendered PDF and its preview. Such a pull request was going out with the figures label alone.
`make reports` deleted every committed PDF and preview before rendering a single one, so a generator that died halfway left the working tree stripped of the examples it had not reached yet. On CI that only fails the job, but locally it is a real loss: 116 of the 134 committed files were gone twenty seconds into an interrupted run, and the only way back is `git checkout`. The set is now rendered into a scratch directory and moved into place once it is complete. Clearing the output stays, because the generator overwrites and never deletes and a fiche that is no longer produced has to disappear from the tree; it just happens after a successful run rather than before an attempted one. A trap removes the scratch directory on every exit path, interrupted or not, and an empty one fails the target rather than emptying the committed directory.
The test that checks every registered fiche has a committed render only needed the name of each file, and it got it by calling all 67 factories for the third element of the tuple they return, computing and plotting every worked example to learn where they were meant to land. The registry is now keyed by the file each factory writes, so the check is a path lookup: 1.58 s of factory calls down to under the 5 ms pytest bothers to report. The per-example render test asserts the factory still writes the file it is registered under, so the keys cannot drift away from what is produced.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Makefile`:
- Around line 149-156: Update the reports target’s replacement flow around the
existing tmp cleanup trap, find deletion, and mv commands: preserve the current
.github/reports directory as a backup, rename the generated scratch directory
into .github/reports, and restore the backup from the trap if any replacement
step fails or is interrupted. Remove the destructive per-file deletion and
ensure successful replacement cleans up the backup.
In `@tests/test_check_reports.py`:
- Around line 21-25: Move the module-level pytest.importorskip("pypdfium2") call
into the PDF-specific setup used by _fiche_pdf and cr.pdf_problem tests, so
raster tests continue running when pypdfium2 is unavailable. Keep the skip
scoped only to tests that require PDF page extraction.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 80c1de26-da0c-44ae-bf23-cf0b7c7dc947
📒 Files selected for processing (6)
.github/labeler.ymlCHANGELOG.mdMakefilescripts/generate_reports.pytests/test_check_reports.pytests/test_generate_reports.py
Deleting the committed renders and then moving the new ones in left a window where an interrupted move emptied the directory. The recipe now renames the current directory aside, renames the fresh one into place and only then discards the copy it kept, and the trap restores that copy whenever the swap started but did not finish. It restores nothing when the generator failed first, which is when the directory was never touched. The pypdfium2 skip also moves off the module and onto the three tests that read a PDF, so the raster comparisons still run without it.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Makefile`:
- Around line 152-158: Update the reports target’s trap and swap sequence to
track completion explicitly: initialize a swap-complete flag, set it immediately
after moving "$tmp" into .github/reports, and make the trap restore
"$old"/current only when that flag is still unset. Preserve cleanup of "$tmp"
and "$old" in all cases so interrupted generation restores the committed
reports, while post-swap cleanup cannot replace the new directory with a
partially deleted backup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 0fa1aaa2-9b2a-4d7a-9353-6fd41afaca30
📒 Files selected for processing (3)
CHANGELOG.mdMakefiletests/test_check_reports.py
The backup still looked pending while the copy was being deleted, so an interrupt in that stretch restored the previous renders over the ones just generated. Renaming the copy aside first settles the question in a single atomic rename, which leaves only two consecutive renames between the new renders landing and the copy being disowned, rather than the removal of 134 files.
|
You've reached the fair usage limit for the Bito Code Review Agent trial. Upgrade to a paid plan or contact support@bito.in for assistance. Learn more |
A composite assertion reports only that the pair was false, so a check that returned None and one that returned the wrong reason failed the same way.
|
|
You've reached the fair usage limit for the Bito Code Review Agent trial. Upgrade to a paid plan or contact support@bito.in for assistance. Learn more |



What and why
.github/reportshad gone stale and nothing said so. The conformance report and the generated API reference each have a CI job asserting they match the code; the fiches never did, so two of them had been publishing an old rendering since PR #422 without anyone noticing.They were stale for a reason worth naming: two merged commits changed how shaded regions are filled, and the fiches were never regenerated.
iso1999_niptsstill carried#ffbb78at 50 percent with the PDF transparency operators that #428 deleted when_fractile_bandmoved totheme_fill, andiso1996_tone_audibilitystill carried the#eaf0f8literal that same commit removed.ebu_r128_loudnessand the twoiec61260_filterfiches carried the corridor fill that #461 migrated. Every printed value, verdict and margin was already correct: the extracted text is identical before and after in all five, so this was stale presentation, not a wrong answer. Incidentally #461 said the filter fiche "comes out the same" when rendered before and after, and it does not.iec61043_intensity_examplewas registered by the generator but its render was never committed, and nothing embedded it either, so the only report kind in the library without a rendered example was the one the intensity guide walks through step by step. It is committed now and embedded in the guide, in both languages and in thedocs/mirror.The new job runs
make reportsand thenscripts/check_reports.py, which compares each fiche on what it says and on what it looks like: the extracted PDF text exactly, and the committed preview within the same two-criteria pixel tolerance the figures use. A byte comparison would not do, for the same reason the figures job does not use one: the embedded plot is vector geometry and a last-bit coordinate difference rewrites the page stream for no visible reason. The script documents what the tolerance measurement actually showed, including where the noise bound and the real changes overlap and why the thresholds stay where they are anyway. Shared helpers moved toscripts/generated_assets.pyso the two checks do not carry a copy of each other.Validation
The check was verified in both directions. Green on the committed tree, twice, and
make reportsreproduces all 134 files byte for byte. Failing: reverting_fractile_bandto its pre-#428 fill is caught at 29535 pixels; one character changed in a printed field is caught by both criteria; an added fiche and a removed one are each named. Run against the branch before the fix, it found all six defects unprompted.tests/test_check_reports.pydrives both sides of every criterion, andtests/test_generate_reports.pynow asserts every registered fiche has a committed render, which would have caught the missing one at the moment it was forgotten.Checklist
Ran locally, same as CI:
ruff check .,mypy src scripts(275 files),bandit -r srcpytest -q: 7817 passed, 23 skippedmake conformance: 533/533, no driftscripts/check_reports.pyandscripts/check_figures.py: both greenAlways:
[Unreleased]Summary by CodeRabbit
New Features
Improvements
Documentation