Skip to content

feat(quality): untimed output quality pass with Lighthouse and SEO signals (spec 009) - #25

Merged
matbrgz merged 15 commits into
masterfrom
feat/009-output-quality
Oct 10, 2026
Merged

matbrgz merged 15 commits into
masterfrom
feat/009-output-quality

Conversation

@matbrgz

@matbrgz matbrgz commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

Implements spec 009 (closes #22). --quality adds an untimed pass that audits what each generator ships. It runs Lighthouse, collects SEO signals, validates the HTML, checks accessibility and broken links, and measures weight and JS. It does not change how builds are timed.

How it works

  • Export: after a successful build, DockerHelper.benchmark copies <WorkingDir>/<output_folder> out of the generator container before removing it.

  • Collection:

    • A pinned image, quality/, runs quality/run.mjs with network_mode="none" and no mounts.
    • The site goes in with put_archive, and raw.json comes out with get_archive. The toolset itself runs in a container, so bind mounts would need host paths.
    • It audits three pages: the index, the first post and 404.html. It runs Lighthouse with the mobile and desktop presets, 3 runs each, plus axe-core, html-validate, lychee (offline) and gzip/brotli sizes.
  • Reduction: pure Python in toolset/quality/ (site, seo, reduce, runner) turns raw.json into quality.json. That covers the medians and ranges, 12 per-page SEO signals plus site-wide ones, HTML/axe/link counts, weight, jsClass, worksWithoutJs and extra output.

  • Results:

    • per generator: results/<ts>/quality/<fw>/quality.json;
    • in results.json: quality.<fw> and environment.quality (the tool versions and the image id);
    • a "Qualidade" section in summary.md and a quality-summary.csv.

    There is no combined score and no ranking. --parse keeps quality.

  • Isolation: the pass runs only after an ok build. With --suite it runs only in the nf50-cs5 cell. A failing check is recorded as status: error and never changes a timing, a status or a ranking. Tests pin this.

Pinned versions: lighthouse 13.5.0, chrome-launcher 1.2.2, playwright 1.64.0 (Chromium 156.0.8078.4), axe-core 4.14.0, html-validate 11.16.2, lychee 0.24.2, Node 24.21.0.

Spec changes found while planning

  • The collector container takes the site through put_archive instead of a bind mount.
  • Chromium comes from Playwright, because Chrome for Testing has no Linux arm64 build.

Testing

  • pytest: 341 passed, 1 skipped. ruff is clean.
  • Locally, the collector image passed quality/test/check-shape.mjs on the fixture site, and quality/test/bad-url.mjs passed: a malformed URL does not crash the collector.
  • New CI jobs:
    • quality-image runs the collector on the fixture for amd64 and arm64.
    • quality runs ./ssgberk --test hugo -nf 50 -cs 5 -mr 1 --quality and checks every section, the 6 Lighthouse entries, scores in [0, 1], environment.quality and the summaries.
  • benchmark-round.yml gets a quality input.

Each of the 8 tasks had its own review, followed by a whole-branch review. Its findings were fixed in 5a712be:

  • the collector's output directory collided with the site of generators whose output folder is out;
  • the pass's I/O errors could escape;
  • the bad-url check was not in CI.

🤖 Generated with Claude Code

matbrgz and others added 14 commits October 10, 2026 14:17
…a11y, links, weight)

Untimed quality pass over the output of one build per generator, as
proposed in #22.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t mounts on Playwright's Chromium

The toolset runs in a container, so the site goes in with put_archive
instead of a bind mount; Chrome for Testing has no Linux arm64 build, so
Chromium comes from the pinned Playwright.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Join text nodes without a separator, as textContent does, so
"<p>See <a>x</a>.</p>" yields "See x." instead of "See x .".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ork (spec 009 R-3..R-5)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ld (spec 009 R-1, R-2, R-6, R-17)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…v (spec 009 R-18, R-19)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…round input (spec 009 goal 4)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…-validate, lychee (spec 009 R-4, R-5)

Pinned: lighthouse 13.5.0, chrome-launcher 1.2.2, playwright 1.64.0 (Chromium 156.0.8078.4),
axe-core 4.14.0, html-validate 11.16.2, lychee 0.24.2, Node 24.21.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…(spec R-16)

A malformed percent-escape or a read error now answers 404/500 instead of an
unhandled rejection that killed the process before raw.json was written.
Adds quality/test/bad-url.mjs as a regression check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ct the build, CI checks

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… reference site

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…c 009 R-7)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@matbrgz
matbrgz merged commit 4c6e185 into master Oct 10, 2026
6 checks passed
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.

Quality pass after the benchmark: Lighthouse and output audits per generator

1 participant