fix(links): stop GitHub 503s from filing false broken-link issues - #83
Conversation
Particle renamed connectkit-aa-usage to connectkit-aa-demo, so the starter-repository link only resolved through a GitHub redirect. It keeps the same pinned commit, and #L117 still lands on executeTxNative. Co-authored-by: Cursor <cursoragent@cursor.com>
The five URLs flagged in #81 all return 200. As in #77, GitHub answered the weekly check with 503s, and the per-host throttle added for #77 did not stop them: 3 GitHub failures on Sep 14 without it, 5 on Sep 21 with it. lychee 0.24.2 retries a rejected 429 but never a 503, so a single 503 becomes a reported error. Every GitHub false positive so far was a rendered blob page, so remap github.com/<owner>/<repo>/blob/<ref>/<path> links to raw.githubusercontent.com, which still answers 404 for a missing file, ref, or repo. For every host, give failures a second look before filing an issue: if the first pass fails, wait two minutes and run lychee again. --cache lets the second pass replay passing links from .lycheecache and re-request only the failures, since lychee never caches a failure. Accepting 503 outright would have hidden learnweb3.io, which answered 502/503 for weeks (#52, #70, #73) before it was removed as dead. Verified with lychee 0.24.2 across the 142 tracked docs: 0 errors, 46 s for the first pass (57 s before the remap), and the second pass is served entirely from the cache. Against a mock server, a one-off 503 cleared on the re-check while a persistent 503 and a 404 were still reported. Closes #81 Co-authored-by: Cursor <cursoragent@cursor.com>
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
PR SummaryLow Risk Overview
Reviewed by Cursor Bugbot for commit 6bc51b5. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
This PR fixes the drifted Particle link, remaps GitHub blob links to raw.githubusercontent.com, and adds a cached second lychee pass before any issue is filed. The change is sound and I found no blockers. The main note is that the re-check pass also scans the report written by the first pass, because that report sits inside the glob.
Findings: 0 blocking | 7 non-blocking | 2 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- REVIEW_GUIDELINES.md is empty, so this review applies no repo-specific guidelines beyond AGENTS.md.
- The Cursor second-opinion pass (cursor-review.md) produced no output. Codex reported no material issues.
- The PR relies on lychee not caching failures, which is what lets the second pass re-request only the links that failed. I couldn't check this against the lychee 0.24.2 source here. The author says a mock-server test confirmed it. To make it explicit, consider setting
cache_exclude_statusfor 5xx codes in lychee.toml, so a cached 503 can never be replayed as a failure. - Remapped links now go to raw.githubusercontent.com, which is outside the
[hosts."github.com"]throttle. That is fine given the CDN's limits. Blob links that point at directories, or that rely on repo-rename redirects, will behave differently, as the new comment says. That is acceptable, but worth knowing if new false 404s show up. - Once
mint devregenerates, llms-full.txt will pick up the new Particle URL through the scheduled workflow. No hand edit is needed, which matches AGENTS.md. - 2 suggestion(s)/nit(s) flagged inline on specific lines.
| args: ${{ env.LYCHEE_ARGS }} | ||
| fail: false | ||
| format: markdown | ||
| output: ./lychee-report.md |
There was a problem hiding this comment.
[suggestion] The first pass writes ./lychee-report.md into the workspace. The second pass's '**/*.md' glob will then pick that report up as an input, because it isn't gitignored. Any URL that still fails will be checked again from the report and listed twice in the tracking issue, once from its real source and once from lychee-report.md. To avoid this, write the reports outside the checkout (for example ${{ runner.temp }}/lychee-report.md, and update content-filepath to match). Alternatively, add lychee-report.md to exclude_path.
| output: ./lychee-report.md | ||
|
|
||
| - name: Open or update tracking issue on broken links | ||
| if: steps.lychee.outputs.exit_code != 0 && steps.recheck.outputs.exit_code != 0 |
There was a problem hiding this comment.
[nit] If the re-check step fails before it sets its output (for example, the action errors out), steps.recheck.outputs.exit_code is ''. In an expression, '' != 0 evaluates to false, so no issue is filed and the failure goes unnoticed. Consider also checking steps.recheck.outcome == 'failure', or using steps.recheck.outputs.exit_code != '0', so the step fails closed.
The first pass wrote ./lychee-report.md into the checkout, where the re-check's '**/*.md' glob picks it up as a docs page, so every link that still failed would be listed twice in the tracking issue. Write the report to the runner's temp directory instead. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
This PR looks correct and well reasoned. The lychee remap sends blob links to raw.githubusercontent.com, the workflow now re-checks failures before filing an issue, and the Particle link is updated to the renamed repo. There are no blockers, only a few small robustness and maintainability notes.
Findings: 0 blocking | 5 non-blocking | 2 posted inline
Blockers
- None at the file/PR level.
Non-blocking
- The Cursor review file (
cursor-review.md) was empty, so that pass produced no output. Codex reported no material issues, and I agree there is nothing blocking. - The repository's
REVIEW_GUIDELINES.mdis empty, so this review applies only general standards plus AGENTS.md. - In lychee, a remap runs before the
excludecheck. Any futureexcludepattern aimed atgithub.com/.../blob/...would therefore never match; it would have to targetraw.githubusercontent.com. Consider adding a one-line note next to the remap. No such exclude exists today, and I found no blob links that point at directories. - 2 suggestion(s)/nit(s) flagged inline on specific lines.
| - name: Open or update tracking issue on broken links | ||
| if: steps.lychee.outputs.exit_code != 0 && steps.recheck.outputs.exit_code != 0 | ||
| uses: peter-evans/create-issue-from-file@v5 | ||
| with: |
There was a problem hiding this comment.
[nit] If the re-check step itself errors (the action fails to install or crashes) and never sets exit_code, steps.recheck.outputs.exit_code is ''. '' != 0 evaluates true, so this step files an issue from whatever report is on disk, possibly the first-pass report. That's arguably the safe default. If you'd rather make it explicit, gate on steps.recheck.outcome == 'success' && steps.recheck.outputs.exit_code != 0, or let a failed step fail the job.
| # 503s for files that exist. Reports show the raw URL. A blob link must point | ||
| # at a file; the CDN returns 404 for directories, so use /tree/ for those. | ||
| remap = [ | ||
| "^https://github\\.com/([^/]+)/([^/]+)/blob/([^?#]+).* https://raw.githubusercontent.com/$1/$2/$3", |
There was a problem hiding this comment.
[nit] The remap looks right: [^?#]+ drops ?plain=1 and #L117 anchors. One trade-off: a raw URL returns 200 for a renamed repo that only resolves through a redirect, which is exactly the drift fixed in particle.mdx here. Stale-but-redirecting blob links will stop surfacing. That's acceptable for a rot check, but worth knowing.
What is the purpose of the change?
Fixes the external link check behind #81. None of the links it flagged are dead: all five URLs return 200. As in #77, GitHub answered the weekly lychee run with 503s, and the throttle added for #77 didn't stop them. One of the links had drifted, so this also corrects that page.
Describe the changes to the documentation
evm/wallet-integrations/particle.mdx: Particle renamedconnectkit-aa-usagetoconnectkit-aa-demo, so the starter-repository link only worked through a redirect. It keeps the same pinned commit, and#L117still lands onexecuteTxNative.lychee.toml: GitHub file links (github.com/<owner>/<repo>/blob/<ref>/<path>) are now checked againstraw.githubusercontent.com. All 8 GitHub false positives across 🔗 Broken external links detected #77 and 🔗 Broken external links detected #81 were rendered blob pages. The raw CDN still returns 404 for a missing file, ref, or repo, so real rot is still caught. A failing file link is reported with its raw URL, alongside the usual source file and line..github/workflows/external-links.yml: if the first pass fails, the job waits two minutes and re-checks before opening the tracking issue. With--cache, the second pass replays passing links from.lycheecacheand re-requests only the failures, because lychee never caches a failure. The report now goes to the runner's temp directory, so the re-check's'**/*.md'glob doesn't read the first pass's report as a docs page.Notes
learnweb3.ioanswered 502/503 for weeks (🔗 Broken external links detected #52, 🔗 Broken external links detected #70, 🔗 Broken external links detected #73) and turned out to be dead (removed in 50054f0). Accepting 503 would have hidden it. With the re-check, anything that fails twice is still reported.lycheeverse/lychee-action@v2resolves to v2.9.0, which installs lychee v0.24.2, and lychee hasn't shipped a release since. The only newer build is nightly, which this PR doesn't switch to.actionlint,typos, and Vale are clean.llms-full.txtkeeps the old Particle URL until the weekly regeneration picks up the change.Closes #81