Skip to content

Fix broken-link audit: 4 real defects + CI gate - #386

Merged
pftg merged 24 commits into
masterfrom
fix/broken-link-audit
Jul 21, 2026
Merged

Fix broken-link audit: 4 real defects + CI gate#386
pftg merged 24 commits into
masterfrom
fix/broken-link-audit

Conversation

@pftg

@pftg pftg commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

GA showed a 404 spike. Wired lychee into the repo for broken-link
checking (Rakefile, lychee.toml, Brewfile, Gemfile), ran it
against a full production-mode build, and root-caused every finding
down to 4 real defects — all fixed here, plus a CI gate so this class
of regression is caught before merge.

  • Falcon slug mismatch: falcon-web-server-async-ruby-production
    had no slug: frontmatter, so Hugo derived a URL that didn't match
    its own canonical_url or the cross-link pointing at it. Added the
    missing slug:.
  • 3 missing cover images: three posts referenced a cover image in
    frontmatter that was never committed. Generated all three per the
    repo's cover-image pipeline (.stitch/design.md).
  • Duplicate cover images: single.html already renders the cover
    via Resources.GetMatch("cover.*") before the body. Two of the
    above posts also had a body-markdown ![alt](cover.*) line, so the
    same image rendered twice. Removed the redundant markdown line.
  • CI gate: added a link_check job to publish.yml running
    bundle exec rake test:links (offline lychee scan), matching local
    behavior exactly.

Two other findings were confirmed non-issues and excluded via
lychee.toml rather than "fixed": /contact-us/ returns 403 from an
intentional firewall/bot blocker, and jtway.co rate-limits scanners
(third-party, not actually broken). A third — the
falcon-web-server-production-tuning-benchmarks post and its
blog/tags/benchmarks tag page — 404s only because the post is
future-dated relative to today; it self-resolves once the publish
date passes, confirmed by the before/after evidence below.

Evidence

bundle exec rake test:links against a full production build:

  • Before: 5 raw errors (Falcon slug mismatch, 3 missing cover
    images, plus noise from the two documented exclusions before
    lychee.toml existed)
  • After: 1 remaining "error" — the future-dated
    blog/tags/benchmarks page, which is expected and self-resolving
    (144,416 links checked total, 143,932 excluded/OK)

Test plan

  • bundle exec rake test:links — 0 real errors (1 expected,
    self-resolving future-dated non-issue remains)
  • bin/hugo-build — clean production build
  • New CI link_check job added to publish.yml, will run on this
    PR itself as first real-world validation
  • Cover images visually verified via chrome-devtools on their post
    pages

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a “Friday report, in plain English” section to the homepage, highlighting shipped work, blockers, and next steps.
    • Updated the consultation offering to promote a free code audit, including a written assessment and clearer deliverables.
    • Added a customizable form submission label for the audit request.
  • Content Improvements

    • Refined article image handling and added a stable URL for the Falcon web server article.
    • Improved responsive styling for the new homepage report section.
  • Quality Improvements

    • Added automated validation for site links and generated HTML.

Paul Keen and others added 9 commits July 20, 2026 18:00
The homepage CTA "Get a Free Code Audit" landed on /free-consultation/, a
page that never mentioned an audit: H1 "Free Consultation", a generic lede
about a "technical strategy & planning session", and a five-item list of
company brags (years in business, talent network, cost structure).

Voice guide §4 requires a CTA to name what the reader receives, and ICP
90.10 §7 makes the named deliverable the answer to Alex's "all dev shops
are the same" objection. So the page now states the offer:

- H1 "Get a free code audit"; lede gives the shape - one senior developer,
  45 minutes, a one-page written assessment, no contract, no follow-up call.
  Wording follows the voice guide §4 example and the three live blog CTAs.
- The trust list becomes a deliverable list: what you get, plus "we read
  your repository, we do not change anything in it" for the burned founder.
- Submit label is now content-driven via `formSubmitLabel`, defaulting to
  "Request Consultation" so the shared /contact-us/ form is untouched.
  The audit page sets "Request my code audit".
- Frontmatter title/description rewritten around the audit offer.
- Footer/company nav label follows the page: "Free Code Audit".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ICP 90.10 §5 weighs transparency proof at 25% of Alex's buying decision and
names the artifact he looks for: a sample weekly report. The homepage
promised "weekly plain-English reports" three times (meta description, hero
subtitle, services intro) and never showed one.

Adds a #weekly-report section between use-cases and testimonials - proof of
how we work, just before proof of what clients say. It renders the three
sections the voice guide already approves as canon phrasing: what shipped,
what's blocked, what's next.

Honesty note: no delivered client report exists in this repo, so this is
labelled as the format, not passed off as a redacted real artifact. The
caption says so explicitly. The Friday cadence follows the two live service
pages; the course chapter teaching founders to demand a Monday report from
their own team is a different audience and is left alone.

Implementation notes:
- Styles live in homepage-sections.css, the designated last-in-bundle layer
  for .jt-home-* sections, so plain single-class selectors win without
  !important.
- The CTA link class starts with "cta" on purpose: navigation.css exempts
  [class^="cta"] from the site-wide brand-blue link rule, which otherwise
  beats any reasonable specificity via its :not() chain. Verified ruby
  #cc342d renders.
- home-* prefix is already in the postcss greedy list, and the rules were
  confirmed present in the production bundle after PurgeCSS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…OS baselines

The two free_consultation tests asserted on "Free Consultation", the H1 that
the audit rewrite removed, so they would have failed. They now assert the
behaviour that matters: the destination names the deliverable.

- desktop + mobile free_consultation: assert "Get a free code audit" and
  "one-page written assessment" instead of the old page title.
- desktop homepage_sections: new verify_section_for("homepage",
  "weekly-report") with its baseline.
- Regenerated macOS baselines. The wide churn across 404, blog, course,
  contact_us and services shots is the footer/company nav label change
  ("Free Consultation" -> "Free Code Audit"), which renders on every page.

Linux baselines follow in the next commit; CI runs Linux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test:critical excludes the use-cases tests, so the first regeneration left
nav/use_cases and mobile/use_cases stale against the footer/company nav
label change ("Free Consultation" -> "Free Code Audit"), which renders on
every page. bin/qtest homepage caught it, and that failing run overwrote
the two baselines - restored with git checkout before regenerating.

Rule this confirms: a site-wide chrome change needs test:system, not
test:critical, before the baselines can be trusted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GA showed a 404 spike; wiring lychee in gives an offline internal-link
check (test:links) plus optional external/html-proofer checks, so this
class of regression is catchable locally and in CI.
Without a slug, Hugo derived the URL from the title
(.../falcon-web-server-async-ruby-in-production), which didn't match
the post's own canonical_url or the cross-link from
falcon-web-server-production-tuning-benchmarks — a real 404.
single.html already renders the cover via Resources.GetMatch("cover.*")
before the body. A body-markdown ![alt](cover.*) line for a post whose
cover file is literally named cover.<ext> renders the same image twice.
These posts referenced a cover image in frontmatter that was never
committed, producing broken image links. Generated per the repo's
cover-image pipeline (.stitch/design.md).
Runs bundle exec rake test:links (offline lychee scan against a
production-mode build) so link regressions are caught in CI, not just
via manual local runs.
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@pftg, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7f39a818-7db6-4972-90d9-d67106827422

📥 Commits

Reviewing files that changed from the base of the PR and between 9408db6 and 9fde42e.

📒 Files selected for processing (1)
  • .github/workflows/link-check.yml
📝 Walkthrough

Walkthrough

The PR adds generated-site link validation through Rake tasks and GitHub Actions, introduces a homepage weekly report, updates free code audit messaging and form labels, adjusts article metadata and cover images, and updates related system assertions.

Changes

Link validation

Layer / File(s) Summary
Generated HTML link-check tasks
Rakefile, lychee.toml
Production builds now feed generated HTML into blocking internal-link checks and non-blocking external-link and HTML validation tasks.
Link-check CI and tooling
Brewfile, Gemfile, .github/workflows/link-check.yml
Lychee and html-proofer tooling are declared, and a GitHub Actions workflow runs the blocking link check on configured triggers.

Site presentation and content

Layer / File(s) Summary
Homepage weekly report section
themes/beaver/layouts/partials/homepage/weekly-report.html, themes/beaver/layouts/home.html, themes/beaver/assets/css/homepage-sections.css, test/system/desktop_site_test.rb
A responsive “Friday report” section is rendered on the homepage and verified in desktop system coverage.
Free code audit page and form messaging
content/pages/free-consultation/index.md, themes/beaver/layouts/page/free-consultation.html, themes/beaver/layouts/partials/page/contact-form.html, config/_default/hugo.toml, test/system/*_site_test.rb
Consultation content, navigation text, form submission labels, and desktop/mobile assertions now use free code audit messaging.
Article metadata and cover presentation
content/blog/*/index.md
Two inline article cover images are removed, and an explicit slug is added to the Falcon article.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant Rakefile
  participant HugoBuild
  participant Lychee
  GitHubActions->>Rakefile: run test:links
  Rakefile->>HugoBuild: build production site
  HugoBuild->>Rakefile: provide generated HTML
  Rakefile->>Lychee: validate internal links
  Lychee-->>GitHubActions: return check status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main changes: fixing broken-link defects and adding a CI gate.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/broken-link-audit

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Local bundle install on arm64-darwin-25 dropped the linux platform,
breaking bundle install on GitHub Actions ubuntu-latest runners.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/system/desktop_site_test.rb (1)

281-284: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the custom form submit button label.

Both the desktop and mobile system tests successfully verify the updated body text but miss checking the new custom submit button label introduced in this PR. Adding an explicit assertion for the button value ensures the front-matter configuration is rendering correctly and prevents future regressions.

  • test/system/desktop_site_test.rb#L281-L284: Add assert_button "Request my code audit" after the text assertions.
  • test/system/mobile_site_test.rb#L170-L171: Add assert_button "Request my code audit" after the text assertions.
🤖 Prompt for 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.

In `@test/system/desktop_site_test.rb` around lines 281 - 284, In
test/system/desktop_site_test.rb lines 281-284 and
test/system/mobile_site_test.rb lines 170-171, extend the existing audit
destination assertions by adding an explicit button assertion for “Request my
code audit” after the body-text assertions.
🤖 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/workflows/publish.yml:
- Line 57: Update the actions/checkout step to set persist-credentials to false,
preventing the GitHub token from being stored in the local git configuration
while preserving the existing checkout behavior.

In `@Rakefile`:
- Line 72: Update both lychee invocations in Rakefile, including the commands at
lines 72-72 and 84-84, to pass "#{dir}/**/*.html" as a single argument instead
of splatting html_files. Preserve the existing lychee options and scanning
behavior at both sites.

In `@themes/beaver/layouts/partials/page/contact-form.html`:
- Line 33: Update the submit input’s value expression to access the Hugo
parameter as .Params.formsubmitlabel, preserving the existing default "Request
Consultation" fallback so custom submit labels render correctly.

---

Nitpick comments:
In `@test/system/desktop_site_test.rb`:
- Around line 281-284: In test/system/desktop_site_test.rb lines 281-284 and
test/system/mobile_site_test.rb lines 170-171, extend the existing audit
destination assertions by adding an explicit button assertion for “Request my
code audit” after the body-text assertions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 157e4370-5f17-4712-88a7-4bb5dddda7ca

📥 Commits

Reviewing files that changed from the base of the PR and between 1f6d8a6 and 4a72f63.

⛔ Files ignored due to path filters (73)
  • Gemfile.lock is excluded by !**/*.lock
  • content/blog/ai-powered-code-reviews-transforming-development-workflows-2025/cover.jpeg is excluded by !**/*.jpeg
  • content/blog/rails-performance-at-scale-10k-to-1m-users-roadmap/rails-scaling-roadmap.jpg is excluded by !**/*.jpg
  • content/blog/ruby-on-rails-development-cost-guide-2025/cover.jpg is excluded by !**/*.jpg
  • test/fixtures/screenshots/macos/desktop/404.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_page/_achievements.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_page/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_page/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_page/_missions.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_page/_testimonials-header.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/about_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/index.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/index/_pagination.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/post.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/special/codeblocks/bare.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/special/codeblocks/html.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/special/codeblocks/indented.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/blog/special/youtube_post.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers/_newsletter.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers/_offers.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers/junior-ruby-on-rails-developer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/careers/junior-ruby-on-rails-developer/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/_technologies.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/_testimonials-header.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/_testimonials.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/agent-inbox/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/agent-inbox/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/agent-inbox/_overview.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/agent-inbox/_solution.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/clients/agent-inbox/_testimonial.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/course/chapter.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/course/landing.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/free_consultation.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/homepage/_clients.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/homepage/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/homepage/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/homepage/_weekly-report.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/nav/use_cases.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/privacy-policy.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/_testimonials-header.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/app_web_development.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/app_web_development_hero.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/fractional-cto/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/fractional-cto/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/services/fractional_cto.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/_overview.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/_technologies.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/_testimonials-header.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/startup-mvp-prototyping-development/_cta-contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/startup-mvp-prototyping-development/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/desktop/use-cases/startup-mvp-prototyping-development/_overview.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/404.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/about_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/blog/index.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/blog/index/_pagination.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/blog/post.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/careers.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/clients.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/contact_us.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/course/chapter.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/course/landing.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/free_consultation.png is excluded by !**/*.png
  • test/fixtures/screenshots/macos/mobile/use_cases.png is excluded by !**/*.png
📒 Files selected for processing (17)
  • .github/workflows/publish.yml
  • Brewfile
  • Gemfile
  • Rakefile
  • config/_default/hugo.toml
  • content/blog/ai-powered-code-reviews-transforming-development-workflows-2025/index.md
  • content/blog/falcon-web-server-async-ruby-production/index.md
  • content/blog/ruby-on-rails-development-cost-guide-2025/index.md
  • content/pages/free-consultation/index.md
  • lychee.toml
  • test/system/desktop_site_test.rb
  • test/system/mobile_site_test.rb
  • themes/beaver/assets/css/homepage-sections.css
  • themes/beaver/layouts/home.html
  • themes/beaver/layouts/page/free-consultation.html
  • themes/beaver/layouts/partials/homepage/weekly-report.html
  • themes/beaver/layouts/partials/page/contact-form.html
💤 Files with no reviewable changes (2)
  • content/blog/ai-powered-code-reviews-transforming-development-workflows-2025/index.md
  • content/blog/ruby-on-rails-development-cost-guide-2025/index.md

Comment thread .github/workflows/publish.yml Outdated
Comment thread Rakefile Outdated
Comment thread themes/beaver/layouts/partials/page/contact-form.html Outdated
Paul Keen and others added 13 commits July 21, 2026 19:30
- publish.yml: set persist-credentials: false on the link_check checkout
  so the GitHub token is not left in the runner's git config (zizmor artipacked).
- Rakefile: pass the glob string to lychee instead of splatting every matched
  path, avoiding an ARG_MAX/E2BIG crash as the page count grows (lychee expands
  the glob natively; verified same file set gets scanned).
- contact-form.html: Hugo lowercases front-matter keys, so .Params.formSubmitLabel
  was always nil and the button silently fell back to "Request Consultation".
  Use .Params.formsubmitlabel so the free-consultation page's "Request my code
  audit" label renders.
- desktop/mobile system tests: assert the submit button reads "Request my code
  audit", which catches exactly the case-sensitivity regression above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…platforms

The audit-page rework (492f034) rewrote the free-consultation page but only
macOS baselines were regenerated; the linux baselines still showed the old
"Free Consultation" copy. Regenerate desktop + mobile linux baselines via
bin/dtest so they match the shipped "Get a free code audit" page and the new
submit-button label.

Gemfile.lock: add aarch64-linux-musl (the bin/dtest Alpine/ARM container),
parallel to the earlier x86_64-linux addition, so bundle install is satisfied
on every platform this repo builds on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The nav-label + weekly-report + use-cases/clients section changes updated the
macOS baselines site-wide, but the matching linux baselines were never
regenerated. Ran the full system suite under bin/dtest and refreshed the seven
linux screenshots that actually differ; each has a committed macOS counterpart
in this PR, so these are real cross-platform changes, not renderer noise.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New quick_test job runs bin/qtest scoped to the branch's changed pages with a
~90s budget, so CI catches functional regressions (page loads, copy, buttons,
nav) on the pages a PR actually touches - without running the full visual suite.

Because the committed baselines are captured on the docker gate's OS/font stack
and CI runs on ubuntu, the pixel comparison is made configurable and loosened
in CI (SCREENSHOT_TOLERANCE, SCREENSHOT_PERCEPTUAL_THRESHOLD) to absorb
antialiasing while still flagging layout-level regressions. Both knobs default
to today's strict local values, so local/dtest runs are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
quick_test failed on its first run - the :vips comparison driver FFI-loads the
libvips shared library, which ubuntu-latest doesn't ship (macOS and the docker
gate already have it). Install libvips-dev before running qtest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Swap the raw apt-get for awalsh128/cache-apt-pkgs-action so libvips-dev is
restored from cache on subsequent runs instead of re-fetched every time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Temporary diagnostic step - libvips-dev installs but require "vips" still
fails; print apt state, ldconfig, and the real LoadError snap_diff swallows.
The screenshot-diff :vips driver dlopens libvips.so.42 at runtime, which
ships in the libvips42 runtime package - not libvips-dev (headers only).
The cache-apt action did not resolve the transitive runtime dependency,
so libvips-dev alone left the .so.42 target of its symlink missing
(apt list NONE, ldconfig NONE, LoadError on require "vips"). List
libvips42 explicitly, bump the cache salt, run ldconfig, and fail fast
with the real LoadError instead of 28 swallowed per-screenshot errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two Actions-minute reductions on PRs:

- link_check: the 144k-link crawl now runs only every 3rd PR (by PR
  number); non-PR events (nightly schedule, push to master, manual)
  still run so master stays fully covered. Gated per-step so the check
  still reports a real success status if it becomes required.

- quick_test: this branch's five themes/ files each mapped to :all and
  escalated qtest to the full bin/rake test:critical suite, which ignores
  --budget and blew the 10-min timeout (cancelled). Add --no-escalate so a
  site-wide change smoke-tests the homepage (densest shared chrome) within
  budget instead of running the big suite the user explicitly excluded from
  CI; full coverage stays with the docker/nightly gate. Map the shared
  contact-form.html partial to its only two consumers (free-consultation,
  contact-us) for precise scoping. Changed set now resolves to a bounded
  [blog, homepage, free-consultation, contact-us] run, no escalation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 3rd PR

Replace the every-3rd-PR modulo gate with a path gate: on PRs the crawl
runs only when a change could move an internal link - content (blog/
course/pages), templates (themes/*/layouts), menu/baseURL config, the
data records rendered into links, or the checker config (lychee.toml/
Rakefile). CSS/test/workflow-only PRs skip it. Stacking the modulo on
top would have skipped exactly the content-heavy PRs that most need the
check, so the count gate is dropped in favour of this precise trigger.

Non-PR events (nightly schedule, push to master, manual dispatch) always
run for full master coverage. The PR file list comes from the API via
the default token (pull-requests: read added) - cheaper than a
full-history checkout just to diff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cross-OS pixel diffing is unworkable here: baselines come from the docker
gate (Alpine/musl, aarch64) but CI runs Ubuntu (glibc, x86_64). Measured
divergence on c35d026 ran 3-28% (mobile code blocks 0.28, plain content
pages up to 0.21) - above any tolerance that still catches a real
regression. Set FORCE_SCREENSHOT_UPDATE so CI captures screenshots without
failing on the diff; the functional assertions in the same system tests
still gate (page renders, buttons/text present, no broken elements). True
visual regression stays with the same-OS bin/test + bin/dtest gates.

Revert the env-configurable tolerance/perceptual knobs added earlier for
the abandoned loosen-tolerance approach - their comments described a CI
behaviour that no longer exists, and defaults already equalled master.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With cross-OS pixel diffing unworkable (docker Alpine/aarch64 baselines vs
Ubuntu/glibc CI diverge 3-28%) and the functional-smoke fallback only
unevenly valuable, the job's cost isn't justified: visual regression is
already gated before merge by the mandatory same-OS bin/test (macOS) +
bin/dtest (docker-linux). Remove the job and revert bin/qtest to master
(the --no-escalate flag and contact-form mapping existed only to serve it).

CI keeps the cheap, high-signal gates: build, unit_tests, and the
path-scoped link_check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the hand-rolled gate (gh api pulls/files + grep + a run= output
guarding five steps, plus a pull-requests: read grant) with GitHub's
native on.pull_request.paths in a dedicated link-check.yml. Same
behaviour - PRs crawl only when content/templates/config/data/checker
files change; schedule/push/dispatch/workflow_run carry no paths filter
so they always run for full master coverage - with far less machinery.
Per-job path filtering was impossible inside publish.yml (it also builds/
deploys), which is the only reason the gate existed; a separate workflow
removes that constraint.

Trade-off: on a PR that touches no link-relevant files the workflow does
not run, so it reports no check (fine unless link_check becomes a required
status check - say so and I will add a skip-reporting companion).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 @.github/workflows/link-check.yml:
- Around line 10-16: Update the path filters for the link-check workflow to
include the runtime inputs used by the setup and build steps:
.github/actions/setup-hugo, Gemfile, and Gemfile.lock. Preserve the existing
content, theme, config, data, lychee.toml, and Rakefile entries.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ccfabfc1-7838-4c3c-ba7d-69eb7baf3545

📥 Commits

Reviewing files that changed from the base of the PR and between 4a72f63 and 9408db6.

⛔ Files ignored due to path filters (10)
  • Gemfile.lock is excluded by !**/*.lock
  • test/fixtures/screenshots/linux/desktop/clients/_technologies.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/free_consultation.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/homepage/_footer.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/homepage/_weekly-report.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/nav/use_cases.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/use-cases/_overview.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/desktop/use-cases/_technologies.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/mobile/free_consultation.png is excluded by !**/*.png
  • test/fixtures/screenshots/linux/mobile/use_cases.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • .github/workflows/link-check.yml
  • Rakefile
  • test/system/desktop_site_test.rb
  • test/system/mobile_site_test.rb
  • themes/beaver/layouts/partials/page/contact-form.html
🚧 Files skipped from review as they are similar to previous changes (3)
  • themes/beaver/layouts/partials/page/contact-form.html
  • test/system/mobile_site_test.rb
  • Rakefile

Comment thread .github/workflows/link-check.yml
…file)

Per CodeRabbit: the crawl runs against a Hugo-built site using bundled
tooling, so changes to the Hugo setup action or the Bundler runtime can
move generated links without the current content/template/config paths
firing. Add .github/actions/setup-hugo/**, Gemfile, Gemfile.lock so a
build/dependency change still validates internal links on the PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@pftg
pftg merged commit 372c480 into master Jul 21, 2026
4 checks passed
@pftg
pftg deleted the fix/broken-link-audit branch July 21, 2026 19:18
pftg pushed a commit that referenced this pull request Jul 21, 2026
Maintain the OKF bundle after PR #386: new build/ci-gates.md documents
what GitHub Actions enforces (build, unit, path-scoped lychee link check
in link-check.yml) and records that visual regression is intentionally
not a CI gate - cross-OS pixel diffs (Alpine/musl baselines vs Ubuntu/
glibc CI) diverge 3-28%, so bin/test + bin/dtest are the sole visual
coverage. Cross-link test-gates, refresh build/index, log the change.

CLAUDE.md: add @.okf/index.md to the mandatory session-start reads so the
bundle is loaded up front for build/test/CI/content context.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant