Skip to content

fix(public): guard the live contact page against the heading-hierarchy finding (closes 2026-08-09 dogfood item) - #119

Merged
nish3451 merged 6 commits into
mainfrom
fix/lane1-contact-heading-hierarchy-live-guard-20260812
Aug 12, 2026
Merged

fix(public): guard the live contact page against the heading-hierarchy finding (closes 2026-08-09 dogfood item)#119
nish3451 merged 6 commits into
mainfrom
fix/lane1-contact-heading-hierarchy-live-guard-20260812

Conversation

@nish3451

@nish3451 nish3451 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Why

The 2026-08-08 dogfood finding "Heading hierarchy needs cleanup on /contact" (finding 52753880dfc7) was repaired in source by PR #18 (card headings promoted from H3 to H2, outline H1 -> H2s -> H3s with no jumps), and scripts/test-public-heading-hierarchy.mjs guards the worktree HTML. But the live site still serves the June-20 bundle: the deployed /contact/ page jumps H1 -> H3 (live outline [1,3,3,3,2,3,3,3]) and the deployed stylesheet keeps the old .info-card h3-only rule — verified live on 2026-08-12. The finding silently stays open against tinystudio.in until the deployment refreshes.

This lands the live guard so the staleness is loud, following the fleet's live-guard pattern (like the soft-404 guard). Supersedes the stale, conflicting PR #78 (same intent; its branch never got past a conflict with the merged live-site-check.yml).

What

  • scripts/test-public-live-contact-heading-hierarchy.mjs (new): fetches the deployed /contact/ and /styles.css, then re-asserts the repaired outline (exactly one H1 first, three H2 card titles inside .info-card, flat H2 band before footer H3s, no heading-level jump greater than one) and the shared .info-card :is(h2, h3) rule at the former card scale.
  • Wired as npm run site:check-live-contact-heading-hierarchy and into the nightly live-site-check.yml staleness alarm (alongside the soft-404 check). Deliberately NOT part of npm run test / npm run ci — blocking chains stay green on repo state alone, same convention as the soft-404 guard.
  • scripts/check-public-live-deploy.mjs: the deploy lane's post-deploy acceptance now also asserts H2-after-H1 on /contact/, so the moment the Pages token is provisioned and the lane publishes, the live repair is verified as part of deploy acceptance.
  • Network-tolerant: skips when the site is unreachable; fails loudly when the deployment serves stale pages.

Current status

The guard intentionally fails against the live site right now (11 checks, 8 failures — all stale-deployment detections), keeping this item visibly open until the production deploy path is repaired (dormant release lane, PR #111) and main is published. Once the deployment refreshes, this guard goes green and the finding stays closed.

Verify

  • node scripts/test-public-heading-hierarchy.mjs → 62 checks, 0 failures (worktree HTML stays repaired)
  • node scripts/test-public-live-contact-heading-hierarchy.mjs → fails loudly on the stale live deployment (exit 1)
  • SKIP_LIVE_CHECKS=1 node scripts/test-public-live-contact-heading-hierarchy.mjs → exit 0
  • node scripts/check-public-live-deploy.mjs with SKIP_LIVE_CHECKS=1 → exit 0
  • All public test scripts → green; find scripts -name '*.mjs' | xargs node --check → clean; git diff --check → clean
  • npm test exit 1 is the pre-existing environment-specific retention-automation preflight (identical on clean origin/main, unrelated to this change)

Summary by CodeRabbit

  • Bug Fixes

    • Added automated monitoring for the live Contact page to detect unavailable pages, outdated heading structure, and missing shared heading styles.
    • The check now confirms the page loads successfully and maintains accessible heading hierarchy.
  • Chores

    • Added the Contact page validation to nightly live-site checks, with clear reporting when issues are detected.

…y finding

The 2026-08-08 dogfood finding "Heading hierarchy needs cleanup on
/contact" (finding 52753880dfc7) was repaired in source by PR #18
(card headings promoted from H3 to H2, outline H1 -> H2s -> H3s with
no jumps), and scripts/test-public-heading-hierarchy.mjs guards the
worktree HTML. But the live site still serves the June-20 bundle: the
deployed /contact/ page jumps H1 -> H3 (live outline [1,3,3,3,2,3,3,3])
and the deployed stylesheet keeps the old .info-card h3-only rule, so
the finding silently stays open against tinystudio.in.

Land the live guard following the fleet's live-guard pattern:
- scripts/test-public-live-contact-heading-hierarchy.mjs (new): fetches
  the deployed /contact/ and /styles.css, then re-asserts the repaired
  outline (exactly one H1 first, three H2 card titles inside .info-card,
  flat H2 band before footer H3s, no heading-level jump greater than one)
  and the shared .info-card :is(h2, h3) rule at the former card scale.
- Wired as npm run site:check-live-contact-heading-hierarchy and into
  the nightly live-site-check.yml staleness alarm. Deliberately NOT part
  of npm run test / npm run ci - blocking chains stay green on repo
  state alone, same convention as the soft-404 guard.
- scripts/check-public-live-deploy.mjs: the deploy lane's post-deploy
  acceptance now also asserts H2-after-H1 on /contact/, so the moment
  the Pages token is provisioned and the lane publishes, the live repair
  is verified as part of deploy acceptance.
- Network-tolerant: skips when the site is unreachable; fails loudly
  when the deployment serves stale pages.

verify: node scripts/test-public-heading-hierarchy.mjs green; guard
fails loudly on the current stale deployment; SKIP_LIVE_CHECKS=1 exit 0;
git diff --check clean

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds a live /contact/ heading-hierarchy and stylesheet guard. It extends public deployment checks, exposes the guard through an npm script, and runs it in the nightly live-site workflow.

Changes

Contact heading validation

Layer / File(s) Summary
Live contact guard
scripts/test-public-live-contact-heading-hierarchy.mjs
Fetches the live contact page and stylesheet, validates heading order and card counts, checks the shared .info-card rule, and reports failures.
Deployment proof integration
scripts/check-public-live-deploy.mjs
Adds documentation and a live /contact/ response check that verifies an H2 follows the first H1 before any H3.
Nightly check wiring
package.json, .github/workflows/live-site-check.yml
Adds the npm command and runs the live contact check in the nightly verification workflow.

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

Sequence Diagram(s)

sequenceDiagram
  participant NightlyWorkflow
  participant NpmScript
  participant ContactHeadingCheck
  participant LiveContactPage
  participant LiveContactStylesheet
  NightlyWorkflow->>NpmScript: run site:check-live-contact-heading-hierarchy
  NpmScript->>ContactHeadingCheck: execute test script
  ContactHeadingCheck->>LiveContactPage: fetch /contact/
  LiveContactPage-->>ContactHeadingCheck: return HTML
  ContactHeadingCheck->>ContactHeadingCheck: validate heading hierarchy
  ContactHeadingCheck->>LiveContactStylesheet: fetch stylesheet
  LiveContactStylesheet-->>ContactHeadingCheck: return CSS
  ContactHeadingCheck->>ContactHeadingCheck: validate card-heading CSS
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the live contact page heading-hierarchy safeguards added by the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lane1-contact-heading-hierarchy-live-guard-20260812

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@scripts/test-public-live-contact-heading-hierarchy.mjs`:
- Around line 77-81: Update the response handling around the fetch in the live
endpoint test so non-2xx responses record a failed assertion instead of logging
a skip and returning null. Keep skips limited to fetch errors and timeouts,
while preserving normal validation for successful responses.
- Around line 95-104: Update the heading validation loop in
scripts/test-public-live-contact-heading-hierarchy.mjs to track whether the
first H3 has been seen and count any subsequent H2 as a failure. Add an
assertion that no H2 appears after the first H3, while preserving the existing
jump and H2-count checks.
- Around line 132-137: Update the heading-hierarchy guard in the test script to
track failures separately for each target, so stale-stylesheet messaging only
reflects the stylesheet check itself rather than earlier page assertions. Use
the final target result to choose the concluding message, reporting the finding
as open only when the relevant live deployment check fails and reporting success
when all checks pass.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8cf20cc6-e98a-4a97-9aab-efb569f78427

📥 Commits

Reviewing files that changed from the base of the PR and between 6817d6c and aefca50.

📒 Files selected for processing (4)
  • .github/workflows/live-site-check.yml
  • package.json
  • scripts/check-public-live-deploy.mjs
  • scripts/test-public-live-contact-heading-hierarchy.mjs

Comment on lines +77 to +81
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) })
if (!res.ok) {
console.log(` ok skipped: ${url} answered ${res.status}, deployment not reachable - no assertions run for it`)
return null
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail when the live endpoint returns an HTTP error.

An HTTP response proves that the endpoint is reachable. Lines 78-80 convert a 404 or 500 response into a successful skip. The guard then exits successfully without validating a missing or failed deployment. Reserve skips for fetch errors and timeouts. Record a failed assertion for non-2xx responses.

🤖 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 `@scripts/test-public-live-contact-heading-hierarchy.mjs` around lines 77 - 81,
Update the response handling around the fetch in the live endpoint test so
non-2xx responses record a failed assertion instead of logging a skip and
returning null. Keep skips limited to fetch errors and timeouts, while
preserving normal validation for successful responses.

Comment on lines +95 to +104
const cardH2s = levels.filter((l) => l === 2).length
ok(cardH2s >= 4, `live ${name} page keeps the flat H2 band (card H2s plus the footer H2) before the footer H3s`)
let jumps = 0
for (let i = 1; i < levels.length; i++) {
if (levels[i] - levels[i - 1] > 1) {
jumps++
console.error(` bad transition H${levels[i - 1]} -> H${levels[i]} on ${name}`)
}
}
ok(jumps === 0, `live ${name} page has no heading-level jump greater than one (no H1 -> H3 skip)`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Verify that no H2 follows the first H3.

The current checks accept H1 -> H2 -> H3 -> H2. That sequence has no upward level jump and still meets the H2 count. It violates the required flat H2 band before the footer H3 headings. After the first H3, fail if a later H2 exists.

🤖 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 `@scripts/test-public-live-contact-heading-hierarchy.mjs` around lines 95 -
104, Update the heading validation loop in
scripts/test-public-live-contact-heading-hierarchy.mjs to track whether the
first H3 has been seen and count any subsequent H2 as a failure. Add an
assertion that no H2 appears after the first H3, while preserving the existing
jump and H2-count checks.

Comment on lines +132 to +137
if (failures > 0) {
console.error(" the deployed stylesheet is stale: it misses the card-heading pairing that public/styles.css already has. Refresh the live deployment from origin/main.")
}
}

console.log("\nLive /contact/ heading-hierarchy guard result: the finding stays open against tinystudio.in until a refresh of the live deployment lands on origin/main.")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report the result for the correct target.

The stylesheet message reports stale CSS when an earlier page assertion incremented global failures. Line 137 also reports that the finding remains open when all checks pass. Track failures per target, and select the final message from the final result.

🤖 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 `@scripts/test-public-live-contact-heading-hierarchy.mjs` around lines 132 -
137, Update the heading-hierarchy guard in the test script to track failures
separately for each target, so stale-stylesheet messaging only reflects the
stylesheet check itself rather than earlier page assertions. Use the final
target result to choose the concluding message, reporting the finding as open
only when the relevant live deployment check fails and reporting success when
all checks pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aefca50a33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +77 to +80
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) })
if (!res.ok) {
console.log(` ok skipped: ${url} answered ${res.status}, deployment not reachable - no assertions run for it`)
return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fail closed when the monitored route is not its own 200 response

In the nightly live-contact-heading-hierarchy job, a reachable /contact/ that returns 404/500 is logged as an “ok skipped” result, while the default redirect-following behavior also accepts another page’s response. For example, a redirect to /promptly/ would pass because that page has the same one-H1, three-card-H2 outline, so a broken contact route can leave this job green. Require a 200 response from the expected final URL; reserve skipping only for the intended network-unavailable case.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2aa7c007c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{
const { status, body } = await get("/contact/")
ok(status === 200, `/contact/ returns 200 (got ${status})`)
ok(h2AfterFirstH1(body), "H2 follows the first H1 before any H3")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Verify every contact card heading after deployment

In the deploy-public-site.yml publish path, this is the new post-upload acceptance assertion, but h2AfterFirstH1 only proves that some H2 precedes the first H3. A partially stale /contact/—for example, one with an earlier managed-service H2 or only the first card promoted while later card titles remain H3—passes this check even though the heading-hierarchy repair is not deployed, causing the release lane to report the publish as verified. Apply the three-card and no-jump assertions used by the nightly guard here as well.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7e287054c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


// Count <h2> used as .info-card card titles (inside <article class="info-card">).
const infoCardTitleCount = (html) =>
(html.match(/<article class="info-card[^"]*"[^>]*>[\s\S]*?<h2\b/gi) || []).length

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Bound each card-heading match to its article

In the nightly live-contact-heading-hierarchy job, this regex can cross the closing </article> while searching for an H2. If the third contact card regresses to H3, its match consumes the footer H2 instead, so infoCardTitleCount still returns 3; the current page also retains enough other H2s, and the H2→H3→H2 sequence creates no upward level jump, allowing every outline assertion to pass. Restrict each match to the contents of its individual .info-card article so a partially regressed deployment fails the guard.

Useful? React with 👍 / 👎.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nish3451 has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Your trial has ended. Reactivate Greptile to resume code reviews.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fad66f9cf6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +95 to +96
const cardH2s = levels.filter((l) => l === 2).length
ok(cardH2s >= 4, `live ${name} page keeps the flat H2 band (card H2s plus the footer H2) before the footer H3s`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scope the footer H2 check to the footer

When the unfiltered contact page is deployed—or after the snoozed sections return—this count no longer verifies the claimed footer H2: the current source contains two additional non-card H2s, so changing the footer heading from H2 to H3 still leaves at least four H2s, while the no-jump check also passes. Inspect the footer heading directly so the nightly guard actually enforces the documented H2 footer -> H3 columns outline.

Useful? React with 👍 / 👎.

@nish3451
nish3451 merged commit 45fd5a1 into main Aug 12, 2026
2 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.

1 participant