Skip to content

Reframe weekly 404 monitor alert to lead with volume trend#268

Merged
rachaelrenk merged 6 commits into
mainfrom
rrenk/reframe-404-alert
Jun 30, 2026
Merged

Reframe weekly 404 monitor alert to lead with volume trend#268
rachaelrenk merged 6 commits into
mainfrom
rrenk/reframe-404-alert

Conversation

@rachaelrenk

@rachaelrenk rachaelrenk commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Why

This week's weekly-404-monitor Slack alert raised concern with "Uncovered broken URLs: 277 (+251 new this week)", but it wasn't a regression: total 404 volume actually dropped (483 → 395, −88). The monitor is only ~3 weeks old, and at ~1.4 hits per distinct URL the uncovered/new-gap counts are dominated by one-off bot/crawler/old-link traffic that churns ~90% week-over-week. No redirects were removed.

The report also led with a raw distinct-URL count (the noisiest metric for this dataset) and counted trailing-slash/case/host variants of the same page as separate gaps.

What

1. Reframe the alert (run_404_report.py, SKILL.md)

  • New REPORT_MIN_HITS env knob (default 5) splitting uncovered URLs into significant gaps (hits >= threshold) vs long-tail noise.
  • JSON summary adds trend_pct, report_min_hits, significant_uncovered_count, significant_new_gaps_count, top_significant_uncovered, long_tail_count. Legacy uncovered_count / new_gaps_count retained for continuity.
  • Slack format now leads with the volume trend (▼/▲ + %), lists only significant gaps, and rolls the long tail into a single line.

2. Collapse duplicate page variants in SQL (run_404_report.py, SKILL.md)

  • Previously the SQL grouped by the raw broken_url, so /foo, /foo/, /Foo, and https://host/foo arrived as separate rows. Because aggregate_by_norm() ran in Python after the query's LIMIT 500, a page whose hits split across variants could fall outside the top 500 and be dropped before its hits were summed (flagged in Oz review).
  • query_404_events now normalises and groups by path in SQL (mirroring normalise_url), so trailing-slash/case/host variants are summed into one row before the cap is applied. Each logical page is counted once and hit-splitting can no longer push a real gap below the threshold. aggregate_by_norm() is retained as an idempotent Python safety net.

This week's data under the new format

404 volume: 395 this week — ▼ 88 (-18.2%) vs 483 last week
Gaps worth fixing (≥5 hits): 2 (0 new)
  9  /~gitbook/image
  7  /privacy
+275 other uncovered URLs under 5 hits each (mostly bots/old links) — see CSV
225 resolved since last week

Known caveat / follow-up

Coverage is matched on the normalized key (trailing slash + case stripped), but Vercel matches redirect sources exactly (slash- and case-sensitive). So a variant like /foo/ can be marked "covered" when only /foo is a redirect source, even though /foo/ actually 404s live (verified: /getting-started 308s but /getting-started/ 404s). This can under-report a real slash-only gap. Not addressed here — the clean fix is a live HEAD check against docs.warp.dev for the top candidates, tracked as a follow-up.

Notes

  • Threshold is tunable via REPORT_MIN_HITS; revisit once more weeks of data accumulate.
  • Validated with python3 -m py_compile; verified normalise_url is idempotent and aggregate_by_norm collapses variants. End-to-end run needs METABASE_API_KEY and live BigQuery (cloud env only), so not run locally.
  • Out of scope: vercel.json on main carries ~1,954 redirects, but there are no true duplicates — the /x + /x/ pairs are intentional dual-form coverage (Vercel exact-matches). It's near Vercel's 2,048 build-failure ceiling, so bulk redirects is the path if it keeps growing.

Conversation: https://staging.warp.dev/conversation/908b9678-fe69-4c85-aba1-207134484cca

Co-Authored-By: Oz oz-agent@warp.dev

The weekly-404-monitor Slack report led with "Uncovered broken URLs: N
(+M new this week)" — a raw distinct-URL count. In the current low-sample
window (instrumentation is only weeks old) most broken URLs are one-off
bot/crawler/old-bookmark hits, so that count churns heavily week-over-week
and overstates the problem even when total 404 volume falls.

- run_404_report.py: add REPORT_MIN_HITS (default 5) splitting uncovered
  URLs into significant gaps (>= threshold) vs long-tail noise; add
  trend_pct, report_min_hits, significant_uncovered_count,
  significant_new_gaps_count, top_significant_uncovered, long_tail_count
  to the JSON summary. Legacy counts kept for continuity; CSV unchanged.
- SKILL.md: reframe Slack format to lead with the 404 volume trend, list
  only significant gaps, and roll the long tail into a single line.
  Clarify reporting threshold vs the Phase 2 automation threshold.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Jun 29, 2026
@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 30, 2026 5:58pm

Request Review

@oz-for-oss

oz-for-oss Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

@rachaelrenk

I'm starting a first review of this pull request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss 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.

Overview

This PR reframes the weekly 404 monitor around total 404 volume, adds a configurable reporting threshold for significant uncovered URLs, and updates the Slack guidance to roll long-tail traffic into a summary line. I did not find security-specific findings, and the provided spec context contains no approved or repository spec commitments for this PR.

Concerns

  • The new Slack trend template should define how to display weeks with no prior-week baseline so it does not render a null percentage.
  • The new REPORT_MIN_HITS environment knob would be more robust if it rejected non-positive or non-integer values explicitly.

Verdict

Found: 0 critical, 0 important, 2 suggestions

Approve with nits

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz


*Total 404s this week:* {N} ({+N / -N vs last week})
*Uncovered broken URLs:* {M} ({+N new this week})
*404 volume:* {total_404s_this_week} this week — {▼|▲} {abs(trend_delta)} ({trend_pct}%) vs {total_404s_last_week} last week

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.

💡 [SUGGESTION] trend_pct is null when the prior-week total is 0, so this template can render None%/null%; define a no-prior-week-baseline display before relying on the percentage placeholder.

# in the Slack headline. Anything below this is long-tail noise (one-off
# bot/crawler/old-bookmark traffic) and is rolled up into a single count so
# it doesn't dominate the report. Tunable via env; see SKILL.md.
report_min_hits = int(os.environ.get("REPORT_MIN_HITS", "5"))

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.

💡 [SUGGESTION] Validate REPORT_MIN_HITS as a positive integer before using it; 0, negatives, or non-numeric values either reintroduce long-tail noise or fail with a traceback.

The SQL groups broken URLs by their raw value (only ?/# stripped), so
trailing-slash, case, and scheme/host variants (/foo, /foo/, /Foo,
https://host/foo) came back as separate rows that were never re-merged after
normalise_url. The same page was counted multiple times in the uncovered /
new-gap counts and could appear twice in the top list, and its hits were split
across variants (sometimes dropping a real gap below the reporting threshold).

Add aggregate_by_norm() to collapse rows by normalise_url (summing hits) for
both current and prior week. report_rows, prior_gaps, and current_urls now
derive from the aggregated maps, and report_rows is re-sorted by hits.
SKILL.md steps 1/3/6 document the per-page aggregation.

Co-Authored-By: Oz <oz-agent@warp.dev>
- parse_min_hits(): treat REPORT_MIN_HITS as a positive integer; fall back to
  5 (with a stderr warning) for non-numeric, zero, or negative values instead
  of crashing or reintroducing long-tail noise.
- format_trend(): pre-format the headline volume line and return a
  "no prior-week baseline yet" string when last week had 0 events, so the
  Slack template ({trend_summary}) never renders a null percentage.
- SKILL.md: headline uses {trend_summary}; document the no-baseline fallback
  and the positive-integer requirement for REPORT_MIN_HITS.

Co-Authored-By: Oz <oz-agent@warp.dev>
@rachaelrenk

Copy link
Copy Markdown
Contributor Author

/oz-review

@oz-for-oss

oz-for-oss Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

@rachaelrenk

I'm re-reviewing this pull request in response to a review request.

You can view the conversation on Warp.

I completed the review and no human review was requested for this pull request.

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

@oz-for-oss oz-for-oss 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.

Overview

This PR reframes the weekly 404 monitor around total 404 volume trend, aggregates raw broken URL rows by normalized page, and splits uncovered gaps into significant vs long-tail buckets for Slack reporting. I did not find blocking correctness, security, or spec-alignment issues; the attached spec context contains no approved spec commitments.

Concerns

  • Non-blocking: aggregation currently runs after the existing raw query limit, so some low-hit variants can still be omitted before their hits are summed.

Verdict

Found: 0 critical, 0 important, 1 suggestions

Approve with nits

Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).

Powered by Oz

print(f" {len(current_week)} unique broken URLs found", file=sys.stderr)
# Collapse trailing-slash / case / host variants so each logical page is
# counted once and its hits are summed (see aggregate_by_norm).
current_agg = aggregate_by_norm(current_week)

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.

💡 [SUGGESTION] Because query_404_events still applies LIMIT 500 before this aggregation, variants outside the raw top 500 are dropped before their hits can be summed. If the goal is accurate thresholding for variant-split pages, aggregate by normalized path in SQL or remove the raw cap before applying any report limit.

Addresses the remaining Oz review nit on PR #268: LIMIT 500 was applied to raw broken_url variants before aggregate_by_norm summed them, so a page whose hits split across trailing-slash/case/host variants could fall outside the top 500 and be undercounted against the reporting threshold. Normalisation + aggregation now happen in SQL so the cap applies to normalised pages; the Python aggregate_by_norm pass remains as an idempotent safety net.

Co-Authored-By: Oz <oz-agent@warp.dev>
@rachaelrenk rachaelrenk merged commit e18a2c4 into main Jun 30, 2026
8 checks passed
@rachaelrenk rachaelrenk deleted the rrenk/reframe-404-alert branch June 30, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants