Use when looking up USAW weightlifting event information — national events, local meets, registration, qualifying totals, schedules, results, live stream, tickets, or event policies. Knows the URL patterns, data locations, and info types across usaweightlifting.org and Sport80.
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Use when looking up USAW weightlifting event information — national events, local meets, registration, qualifying totals, schedules, results, live stream, tickets, or event policies. Knows the URL patterns, data locations, and info types across usaweightlifting.org and Sport80.
How to find any type of USA Weightlifting event information: national events, local meets, registration, schedules, results, qualifying totals, tickets, live streams, policies, and hotels.
When to Use
"Where do I find info about [USAW event]?"
"How do I register for [national championship]?"
"What are the qualifying totals for [event]?"
"Where is the schedule / start list / results for [event]?"
"How do I find a local meet near me?"
"What's the live stream / ticket link for [event]?"
All registration links from usaweightlifting.org event pages deep-link into Sport80. Athletes must have an active Sport80 account + USAW membership to register.
Full reference: See wiki page usaw-event-info-sources for the complete URL catalog, and usaw-event-info-schema for the persistent schema (lifecycle, age groups, scoring, bodyweight categories, synonyms).
Information Type → Where to Find It
The skill classifies links into 23 info types. The full mapping of info types to URL patterns is in INFO_TYPES in usaw_event_extractor.py and on the wiki page usaw-event-info-sources. Key categories:
Sport80 URL patterns:/v/808740/e/meets/{ID}/overview (NCW, VWS2, Finals), /public/wizard/e/{ID} (VWS1, Masters/Uni, WZA), /public/events/{ID}/entries/{ID} (entry lists). See wiki usaw-event-info-sources for the full pattern catalog.
National Event Lifecycle
August (prior year) → Schedule announced (news article)
January (event year) → Registration opens (2:00 p.m. MT; VWS1 at 12:01 a.m. MT)
~6 weeks before → Early Bird closes
~4 weeks before → Regular closes / Preliminary schedule
~2 weeks before → Late closes / VFE (10:00-10:30 a.m. MT)
~10 days before → Final schedule + Start list
During event → Live stream + Results (Google Drive, after "A" sessions)
After event → Full results archived
Full lifecycle reference: See wiki page usaw-event-info-schema for registration tiers, VFE details, schedule release phases, scoring systems, and bodyweight category changes (pre/post Aug 1, 2026).
2026 National Events Calendar
Event
Dates
Venue
Location
VWS1
Mar 5–8
Greater Columbus Convention Center
Columbus, OH
Masters Nationals / University Nationals
Apr 9–12
Salt Palace Convention Center
Salt Lake City, UT
National Championships Week
Jun 20–28
Ed Robson Arena
Colorado Springs, CO
VIRUS Weightlifting Series 2
Sep 10–13
Fort Worth Convention Center
Fort Worth, TX
USAW x Gymreapers Wodapalooza SoCal
Sep 25–27
—
Huntington Beach, CA
VIRUS Weightlifting Finals
Dec 3–6
Alameda County Fairgrounds
Pleasanton, CA
Live-extracted calendar with info-type coverage per event: See wiki page usaw-competitions-events.
How to Look Up Event Info (procedure)
Identify the event and year. If the user names a specific event (e.g. "2026 Nationals"), go to its dedicated event page at usaweightlifting.org/{YEAR}-{slug}. If unsure which event, start at /national-events.
Extract the event page with web_extract. The page contains: dates, venue, location, registration links (Sport80 deep-links), qualifying totals link, schedule PDFs, start list, results, hotel links, tickets, live stream, media info, and policy links.
For registration details, follow the Sport80 deep-links from the event page. Each division (Senior, U25, Junior, Youth, Adaptive) has its own Sport80 meet ID.
For qualifying totals, go to /{YEAR}-usa-weightlifting-national-event-qualifying-totals.
For schedules/results, check the event page for PDF links. If the event is in progress or past, check for a Google Drive results folder link.
For local meets, use the Sport80 calendar widget at usaweightlifting.sport80.com/public/widget/1 or the /events page.
For policies/rules, go to /about-us/governance-and-financial/bylaws-technical-rules-and-policies/rules.
Check the wiki (/opt/data/wiki) for Jim's existing knowledge — search for usaw-event-info-sources, usaw-competitions-events, usaw-weightlifting, or ncw-2026-to-logistics.
Wiki cross-references
usaw-event-info-sources — wiki page with full URL catalog and info-type mapping (companion to this skill)
usaw-weightlifting — USAW governing body entity page
usaw-competitions-events — competition calendar and event participation data
ncw-2026-to-logistics — NCW 2026 TO logistics (event-specific)
Google Drive Results Folders
Each national event publishes results to a Google Drive folder (linked from the event page as "Full Results"). The /results archive page lists all historical folders (2012–present).
Full reference: See wiki page usaw-event-info-sources for confirmed Drive folder IDs (2025-2026), file naming conventions, document type classification, division codes, and the owlcms PDF data structure.
Auto-Extraction Script
scripts/usaw_event_extractor.py — fetches any USAW event page and extracts all structured info using fuzzy header matching + URL pattern classification.
# Markdown output (human-readable, grouped by info type)
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/usaw_event_extractor.py https://www.usaweightlifting.org/2026-national-championships
# JSON output (for programmatic use)
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/usaw_event_extractor.py https://www.usaweightlifting.org/2026-national-championships --json
# Verbose (show unclassified links for debugging)
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/usaw_event_extractor.py https://www.usaweightlifting.org/2026-national-championships -v
URL pattern matching first (most reliable — e.g. sport80.com/v/meets/ → registration)
H3 header fuzzy matching via rapidfuzz.fuzz.partial_ratio (threshold: 60)
Link text as fallback context when H3 is empty (VWS1 inline style)
Navigation/footer link filtering via _is_nav_link() denylist
Handles layout differences:
Chakra UI (NCW, VWS2, Finals): H2 and UL in sibling divs inside .content-tile-block
Inline (VWS1): Links in <p> tags without structured H3 sections
Minimal (WZA): Fewer sections, no hotels/schedules (TBA)
Combined (Masters/Uni): Multiple registration divisions with different Sport80 patterns
Test results across all 6 event pages (2026): 100% classification rate, 0 unclassified, 0 noise.
Fuzzy Matching Techniques
The extractor uses a header_priority disambiguation pattern when multiple
info types match the same URL pattern. This is the core technique for handling
layout differences across events.
How it works:
URL pattern matching runs first — some patterns match multiple types
(e.g. assets.contentstack.io.*schedule matches both preliminary_schedule
and final_schedule)
Types with header_priority: True require the H3 header text to fuzzy-match
their aliases at ≥80% threshold (higher than the 60% default)
If only one priority type passes, it wins
If multiple pass, header fuzzy matching picks the best among priority types
If no priority types pass, falls back to header matching among all URL matches
If URL matching fails entirely, pure header fuzzy matching at 60% threshold
When to add header_priority: True: Any info type whose URL pattern
overlaps with another type's URL pattern. Without it, the first dict iteration
order wins arbitrarily.
See references/usaw-event-page-layouts.md for the full DOM layout reference
(Chakra UI, inline, minimal, combined-event patterns) and nav link filter list.
scripts/test_extractor_mock.py — same 11 tests using saved HTML fixtures
(tests/fixtures/*.html). No network dependency.
# Run offline tests
python scripts/test_extractor_mock.py -v
# Re-fetch fixtures from live pages (when event pages change)
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/test_extractor_mock.py --refresh
Unit tests (offline, <1 second)
scripts/test_extractor_units.py — 22 tests for individual functions: URL
classification, Sport80 URL parsing, nav link filtering, info type matching,
header priority disambiguation, and edge cases.
scripts/test_results_parser.py — 11 tests for PDF parsing: full results,
best lifters, start list, final schedule (3 formats), DNF layouts (3),
classify_file_name, format_summary, and edge cases.
scripts/test_page_health.py — 5 tests for page-health monitoring: section
extraction, link count thresholds, classified link presence, and per-event
health warnings.
Tests validate: required info types per event, zero unclassified, title/dates/venue
extraction, Sport80 URL patterns, Google Drive results links, TBA/TBD status,
fee/milestone metadata, edge cases (WZA minimal, Masters/Uni combined, prior-year slugs),
DNF parsing (3 layouts), final schedule parsing (3 formats), page health monitoring,
and all previously untested utility functions.
Coverage gaps: See references/usaw-test-coverage-gap-analysis.md for the
exhaustive mapping of every untested branch, edge case, and boundary condition
across all three source files. Key gaps: main() CLI paths, Google Drive
download functions, network error paths, _find_section_containers() edge cases,
format_markdown() rendering, and the full daily-sync orchestration loop.
Results PDF Parser Script
scripts/usaw_results_parser.py — downloads and parses owlcms-generated results PDFs from Google Drive folders into structured JSON.
# Parse a local PDF
uv run --with pymupdf python scripts/usaw_results_parser.py /path/to/results.pdf --json
# Download + parse a specific Drive file
uv run --with pymupdf --with google-api-python-client --with google-auth-oauthlib --with google-auth-httplib2 \
python scripts/usaw_results_parser.py --drive-file-id 1V9-fFSa4C2GrPB-4hIqD7G-4qzg_Mf2Z --json
# List files in a Drive results folder
uv run --with pymupdf --with google-api-python-client --with google-auth-oauthlib --with google-auth-httplib2 \
python scripts/usaw_results_parser.py --folder-id 14ncrwEnqErUKGomckAdG_LOT0qEbRomI
# Download + parse ALL PDFs in a Drive results folder
uv run --with pymupdf --with google-api-python-client --with google-auth-oauthlib --with google-auth-httplib2 \
python scripts/usaw_results_parser.py --folder-id 14ncrwEnqErUKGomckAdG_LOT0qEbRomI --all --json
Four PDF types parsed:
Full Results (Results.pdf): owlcms export — all athletes, all attempts, all categories. Extracts: lot, name, team, bodyweight, age, snatch attempts (3), CJ attempts (3), total, score, age_group, gender, weight_category.
Best Lifters ({DIV} Best Lifters.pdf): ranked list by Sinclair/QPoints. Extracts: division, gender, name, team, bodyweight, age, snatch, CJ, total, score.
Final Schedule (Final Schedule.pdf): session grid with platform, weigh-in time, start time, gender, age group, weight category, qualifying totals, entry count. Three distinct session formats: (a) Full — all 8 fields present (31 sessions), (b) Condensed — platform + times + gender + age_group only, no weight category or entry count (63 youth sessions), (c) WSO/ADAP — age_group + weight_cat + qualifying_totals + entry_count, no platform or times (28 adaptive/masters sessions). Total: 122 sessions from NCW 2026. See references/usaw-final-schedule-parsing.md for the complete format reference.
Test results (2026 NCW PDFs):
Full Results: 1,341 athletes, 45 DNF, 0 false parses
Best Lifters: 20 athletes (U11 division), all with snatch/CJ/total/score
Start List: 1,456 athletes
Final Schedule: 122 sessions (31 full, 63 condensed, 28 WSO/ADAP)
Reference files:
references/usaw-sport80-meet-ids.md — all confirmed 2026 Sport80 meet IDs (25 divisions across 6 events)
references/usaw-results-folder-ids.md — all confirmed Google Drive folder IDs (2024–2026)
references/usaw-final-schedule-parsing.md — final schedule 3-format reference (full, condensed, WSO/ADAP)
Daily Sync Cron
scripts/usaw_event_info_sync.py — re-runs the extractor daily on all 2026 event pages, compares to the previous snapshot, runs the test suite, and reports any new/changed info.
# Manual dry run
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/usaw_event_info_sync.py --dry-run
# Run and update snapshot (used by cron)
uv run --with beautifulsoup4 --with requests --with rapidfuzz \
python scripts/usaw_event_info_sync.py
Cron job:usaw-event-info-sync runs daily at 4:00 a.m. PT. It is a
script-only cron (no_agent: true) and stays silent when no changes are found.
What it monitors:
Event title, dates, venue, status
New/removed info types
New URLs added to the event page
Test suite pass/fail status
Page health (advisory, non-blocking): sections extracted, link count (≥5), classified links present. Warnings surface in the sync report as health_warnings per event. Detects CMS changes, broken pages, or pages that changed structure enough to break the extractor. See scripts/test_page_health.py for the 5 health-check tests.
USAW Event Information Schema
For the persistent schema behind this skill — the metadata fields, competition lifecycle, age groups, bodyweight categories, scoring systems, and historical synonyms — see the wiki page [[usaw-event-info-schema]].
Event naming evolution (American Open → North American Open → VIRUS Weightlifting Series)
Synonyms and alternate terms (Sport:80/BARS, VFE, NCW, WSO, TO, etc.)
owlcms data model and document types
Common Pitfalls
Event pages change yearly. The {YEAR}-{slug} pattern is stable for recurring events, but new events (e.g. Wodapalooza SoCal) or combined events (Masters + University Nationals 2026) may have unexpected slugs. Always verify via /national-events.
Sport80 meet IDs are per-division. A single national event has 5–10 separate Sport80 meet IDs (one per championship division). Don't assume one registration link covers all divisions.
Qualifying totals change mid-year. USAW adopted new IWF bodyweight categories effective Aug 1, 2026. Events before Aug 1 use pre-change totals; events after use new totals. Always check the date on the qualifying totals page.
Schedule PDFs are on Contentstack CDN. These URLs are long assets.contentstack.io links that change when updated. Don't hardcode them — always extract from the event page.
Results are often in Google Drive folders, not on the USAW site. The event page links to a shared Drive folder that gets updated after each session.
Local meets are NOT on usaweightlifting.org event pages. They're on the Sport80 calendar widget only. The /national-events page only lists national-level events.
Times are always Mountain Time (MT) unless otherwise noted. USAW is headquartered in Colorado Springs and defaults to MT for all announcements and deadlines.
Registration deadlines are at 2:00 p.m. MT, not midnight. Missing a deadline by a few hours means paying the next tier ($145 → $175 → $375).
Event page layouts differ. The extractor uses fuzzy matching (rapidfuzz) + URL patterns to handle Chakra UI (NCW/VWS2/Finals), inline-paragraph (VWS1), minimal (WZA), and combined-event (Masters/Uni) layouts. If a new event page uses a different CMS layout, add new nav patterns to _is_nav_link() and new info type aliases to INFO_TYPES. Run with -v to see unclassified links.
Sport80 URL patterns vary by event. NCW uses /v/808740/e/meets/{ID}/overview, VWS1 uses /public/wizard/e/{ID}, Masters/Uni uses /public/wizard/e/{ID}/home. The extractor's classify_sport80_url() handles all three and extracts the meet_id.
header_priority threshold must be 80%, not 60%. The default fuzzy threshold is 60, but "wso championships" matches "National Championships Registration" at 82 via partial_ratio — both contain "championships". Using 60 would misclassify every standard registration as WSO registration. The 80% threshold catches this. When adding new header_priority types, always test the false-positive rate with rapidfuzz.fuzz.partial_ratio against known headers.
Prior-year pages use different URL slugs. The {YEAR}-{event-slug} pattern is NOT stable across years. 2025 NCW is /2025-usaw-national-championships (with "usaw" prefix), while 2026 is /2026-national-championships (without). VWS1 was /2025-north-american-open-series-1 in 2025 but /2026-virus-weightlifting-series-1 in 2026. Always verify event URLs via /national-events rather than guessing the slug.
Tests must match current function signatures (2026-07-07). When you refactor function signatures — add/remove/rename parameters, change return types — the unit tests in test_extractor_units.py will break because they call functions directly. In one session, 11 of 22 tests failed after _is_nav_link() gained link_text and h3_header parameters, classify_info_type() changed its signature, and classify_sport80_url() changed its return type. Prevention: after any signature change, immediately run uv run --with pytest --with beautifulsoup4 --with rapidfuzz python3 -m pytest test_extractor_units.py test_extractor_mock.py test_results_parser.py -q. Fix all failures before committing. Also remove return True statements from test functions — pytest warns on non-None returns and they mask assertion failures.
DNF parsing in owlcms PDFs has three distinct layouts (2026-07-08, resolved). The string "DNF" appears in owlcms results PDFs in three different layouts: (a) all-DNF on one line ("DNF DNF DNF" — all attempts failed), (b) all-DNF on three lines (3 consecutive "DNF" lines — same as (a) but split), (c) partial DNF ("DNF DNF" for cj_rank+total_rank, then 1 numeric rank + lot + name — athlete had a valid snatch but DNF'd cj). The parser detects all three: 0 false parses, 1,341 athletes parsed, 45 DNF (total=0). DNF means total=0 (not "DNF" string or None). If both snatch_best and cj_best exist, total is computed from them. See references/usaw-owlcms-dnf-parsing.md for the complete pattern reference. Three isolated unit tests in test_results_parser.py cover each layout. Also: standalone "DNF" and "DNF DNF" lines are now checked for a lot number within 5 lines — if found, they're treated as DNF athlete starts (catches 26 additional DNF athletes that were previously skipped as noise).
parse_athlete_lines returns (athlete, lines_consumed) tuple (2026-07-08). The parser now returns the number of lines consumed so callers skip exactly that far — no more fragile fixed-line skip distances. All 3 call sites in parse_full_results use athlete, consumed = parse_athlete_lines(...) and set i = start_position + consumed. If you add a new call site, always unpack the tuple. The old approach (fixed skip of 16/12/13) took 4 iterations to tune and was still wrong for edge cases.
Final Schedule PDF detection must precede full_results (2026-07-08). The Final Schedule PDF contains "owlcms" in its header, so detect_pdf_type was misidentifying it as full_results and parsing 4 garbage athletes. The fix: check for Session + Platform + Weigh-In keywords BEFORE the owlcms/Age Group check. Detection order matters — always add new PDF type checks before the broad full_results fallback.
Python for...else trap in _find_section_containers (2026-07-08). The walk-up loop used for _ in range(5): ... else: containers.append(...). When container became None (walked past root), the loop hit break — but break prevents the else clause from running. Result: H2 sections with no <ul> ancestor were silently dropped instead of falling back to strategy 2. Fixed with an explicit found_ul flag. Rule: never use for...else when the loop body contains a break that isn't the success path — use a flag variable instead.
Nav link filter trailing-slash mismatch (2026-07-08)._is_nav_link used /coaching/ (with trailing slash) but the actual URL was /coaching (no slash). The regex didn't match, so coaching links leaked through as classified content. Rule: when adding nav patterns, test both /path and /path/ variants — or use patterns without trailing slashes to match both.
check_page_health crashes on sections=None (2026-07-08). When event_data.get("sections") returns None, the sum(len(s.get("links", [])) for s in sections) call fails with TypeError: 'NoneType' object is not iterable. Fixed with sections = event_data.get("sections") or []. Rule: always guard against None when iterating over dict values that may be absent — use or [] / or {} fallbacks.
Subagent-introduced typos can break production code (2026-07-08). A Kimi subagent introduced iflot_idx is not None: (missing space) into usaw_results_parser.py, which silently broke the DNF handler. The parser still ran but missed 26 DNF athletes. Rule: after any subagent modifies source code, always run the full test suite AND verify the 4 fixture PDFs before committing. Subagents don't have the full context and can introduce subtle syntax errors.
FILE_NAME_PATTERNS ordering: specific patterns must precede generic (2026-07-08).full_results matches Results.pdf$ — this catches Glen Middleton Award Results.pdf before the glen_middleton pattern gets a chance. Similarly, 2026 NCW Results - Open Teams.pdf should match teams but full_results wins because it's first in the dict. Rule: when adding new file-name patterns, put specific patterns (glen_middleton, teams, best_lifters) BEFORE the generic full_results pattern. Or make full_results more specific (e.g. Results\\.pdf$ → require no "Glen Middleton" or "Teams" or "Best" in the filename).
Tests must use FIXTURE_DIR for PDF paths, not relative paths (2026-07-09).test_final_schedule_3_formats used parse_pdf("../tests/fixtures/pdfs/2026-ncw-final-schedule.pdf") — a relative path that only works when the CWD is scripts/. When run from the skill root or via pytest with a different CWD, the path resolves wrong and the test fails. Fix: use parse_pdf(str(FIXTURE_DIR / "2026-ncw-final-schedule.pdf")) — FIXTURE_DIR is defined at the top of the test file as Path(__file__).parent.parent / "tests" / "fixtures" / "pdfs" and works regardless of CWD. All other tests already use this pattern; this was the only straggler.
Verification Checklist
Correct event year and slug identified
Event page extracted and parsed for all available info types
Qualifying totals URL checked for the correct period (pre/post Aug 1)
Schedule milestone timeline checked (what's been published vs what's pending)
Wiki searched for existing Jim-specific knowledge about the event
All URLs verified as live (not 404) before presenting to user
If extractor was modified: run uv run --with pytest --with beautifulsoup4 --with rapidfuzz --with pymupdf python3 -m pytest test_extractor_units.py test_extractor_mock.py test_results_parser.py test_page_health.py test_coverage_expansion.py -q — all 134 tests must pass
If results parser was modified: run parse_pdf() against ALL 4 fixture PDFs (2026-ncw-results.pdf, 2026-ncw-start-list.pdf, 2026-ncw-u11-best-lifters.pdf, 2026-ncw-final-schedule.pdf) — not just the one being modified. Expected: full_results=1,341 athletes/0 false parses, start_list=1,456 athletes, best_lifters=20 lifters, final_schedule=122 sessions (31 full, 63 condensed, 28 WSO/ADAP).
If PDF type detection was modified: verify all 4 types are correctly detected (full_results, start_list, best_lifters, final_schedule) — the owlcms header appears in ALL types, so specific checks must come before the generic owlcms/Age Group fallback.