- Rename index page title and heading to 'Missing Link'
- Give a clear message for 429 rate limit errors
- Include response body snippet in other unexpected API errors
- Log warning when falling back to unauthenticated session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Detect redirect targets (e.g. "handling stolen goods" → "Possession of
stolen goods") and use piped links [[target|title]] in edits; exclude
articles already linking to the redirect target from candidates
- Remove candidates from the list in real time as they are checked and
found invalid, with live count update in the summary
- Track and display per-article save count in the stats line
- Rename "Find Link" to "Missing Link" throughout
- Show redirect target in the article heading
- Report save errors to the user via error page instead of crashing
- Filter self-links using case-insensitive first-letter comparison
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Record skips, saves, and no-match results in session["skipped"] so
revisiting an article resumes past already-checked candidates
- Filter self-links (case-insensitive first letter) from hit list
- Use OAuth session for all API reads when logged in for higher rate limits
- Add "for" template to exclusion list to avoid bad edits
- Improve API error handling with HTTP status codes logged to stderr
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Search candidates client-side with JS, showing "Checking X..." spinner
instead of leaving user waiting on a blank page
- Fix broken api_valid_hit endpoint (get_diff returns dict, not tuple)
- Remove server-side get_best_hit; article_page now returns candidate list
immediately and JS iterates via /api/1/valid_hit
- URL now reflects current article via history.replaceState (?title=X),
Skip navigates to ?after=X to advance past it
- Track saves in session; show count as green badge in navbar
- Add session counter incremented on each successful save
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add navbar with login/logout, search form, and Find Link branding
- Clean up index page: search-only, examples behind ?debug=1
- Improve article page: remove debug clutter, named Wikipedia links, collapsible candidates
- Add SVG favicon (🔗 emoji)
- Fix diff CSS: compact layout, auto table layout to eliminate wide marker column gap
- Catch TokenRequestDenied in OAuth start and show error page
- Store username in session at login; clear bad session on API failure
- Raise NoMatch when diff is empty (edit already applied)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Skip no-parameter templates (navboxes) and add annotated link,
excerpt, main, see to the list of skipped parameterised templates
- Preserve sentence-initial capitalisation when replacement is lowercase
- Skip matches that sit entirely inside an existing [[link]] destination
- Treat link destinations that start with q as more specific links to
preserve, in both find_link_in_chunk and find_link_and_section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parse_cite: extend to skip {{cite}}/{{citation}}, {{short description}},
{{gli}}, {{defn}}, external links [https://...], italic text ''...'',
and bullet-point lines containing bare URLs (unformatted bibliography
entries). Uses brace-counting to handle nested templates correctly.
parse_links: yield [[Category:...]] links as 'category' tokens so they
are never modified.
add_link: handle three new boundary cases where the match spans an
existing [[link]]:
- match ends exactly at the link boundary: replace the whole thing with
a single clean link (e.g. surface [[runoff (hydrology)|runoff]] →
[[surface runoff]])
- match starts right after [[: absorb the stray [[ (e.g.
[[anti-globalization]] movement → [[anti-globalization movement]])
- match starts partway inside a link: skip (would produce broken wikitext)
- match spans into but not through a link: use a piped prefix link
(e.g. cross-platform [[interchange station]] →
[[cross-platform interchange|cross-platform]] [[interchange station]])
Fallback search: mask [[Category:...]] spans with spaces so the pattern
cannot match inside them. Guard against matches that are part of a
longer named entity (title-case phrase followed by extra words then an
abbreviation in parentheses, e.g. "Anti-Globalization Movement of
Russia (AGMR)").
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mediawiki_oauth: set User-Agent on all OAuth1Session instances so
Wikimedia doesn't reject token and API requests with 403; add timeout
parameter to api_post_request (default 4s).
mediawiki_api: add APIError exception; wrap .json() in call() to raise
APIError with status code and response body on decode failure; raise
timeout to 30s for edit POSTs.
api: wrap call_get_diff .json() with the same JSONDecodeError guard,
raising MediawikiError with HTTP status and body on failure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Set User-Agent on OAuth1Session during token fetch and access token
exchange so Wikimedia doesn't reject the requests with 403
- Extract handle_post() from article_page() for clarity
- Catch api.MediawikiError in get_best_hit() to skip bad API responses
rather than crashing the page
- Catch mediawiki_api.APIError on save and return a 502 with the error
text instead of a 500 traceback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>