Skip to content

Latest commit

 

History

History
422 lines (333 loc) · 22.4 KB

File metadata and controls

422 lines (333 loc) · 22.4 KB

o-sumo

DeepWiki

日本語 README

o-sumo is a static web app for publishing sumo banzuke, torikumi, and rikishi, gyoji, and yobidashi directories. It is built with React 19, TypeScript, and Vite, and serves both a static site and static JSON APIs from Cloudflare Pages.

Document Index

  • README: README.md / README_en.md
  • Development guide: DEVELOPMENT.md / DEVELOPMENT_en.md
  • Skills index: SKILLS.md / SKILLS_en.md
  • API spec: docs/api/v1.md / docs/api/v1.en.md
  • API policy: docs/api/policy.md / docs/api/policy.en.md
  • API changelog: docs/api/changelog.md / docs/api/changelog.en.md
  • Rikishi profile and matchup refresh runbook: docs/rikishi-profile-refresh-runbook.md
  • Gyoji and yobidashi refresh runbook: docs/official-profile-refresh-runbook.md

Overview

  • Web routes:
    • Homepage: /
    • Archives: /archives
    • Rikishi list: /rikishi
    • Rikishi profile: /rikishi/{id}
    • My Rikishi: /my-rikishi/
    • Compare Rikishi: /compare/?ids={id1},{id2}
    • Gyoji directory: /gyoji/
    • Gyoji profile: /gyoji/{id}/
    • Yobidashi directory: /yobidashi/
    • Yobidashi profile: /yobidashi/{id}/
    • Banzuke: /{YYYYMM}-banzuke/
    • Results hub: /{YYYYMM}-torikumi
    • Schedule hub: /{YYYYMM}-yotei
    • Daily result: /{YYYYMMDD}-torikumi
    • Daily schedule: /{YYYYMMDD}-yotei
    • Kimarite: /kimarite
    • Basho analytics: /analytics/
  • Current route examples:
    • /202609-banzuke/
    • /202609-torikumi/
    • /20260913-yotei/
    • /compare/?ids=3842,4227
    • /kimarite
  • Stories: https://blog.osada.us/
  • The legacy banzuke URL /{YYYYMM}-o-sumo/ redirects to the current banzuke URL.
  • Public APIs:
    • /api/v1/banzuke.json
    • /api/v1/torikumi.json
    • /api/v1/rikishi.json
    • /api/v1/rikishi/{id}.json
    • /api/v1/rikishi-matchups.json
    • /api/v1/gyoji.json
    • /api/v1/gyoji/{id}.json
    • /api/v1/yobidashi.json
    • /api/v1/yobidashi/{id}.json
    • /api/v1/news.json
    • /api/v1/blog.json

Related docs:

  • docs/api/v1.en.md
  • docs/api/v1.md
  • docs/api/policy.en.md
  • docs/api/policy.md
  • docs/api/changelog.en.md
  • docs/api/changelog.md
  • DEVELOPMENT_en.md
  • DEVELOPMENT.md

Skill publishing:

  • SKILLS_en.md
  • SKILLS.md
  • public/.well-known/agent-skills/osumo-content/SKILL.md
  • public/.well-known/agent-skills/osumo-discovery/SKILL.md
  • skills/osumo-api/SKILL.md

Key Features

  • Direct navigation from the homepage to Banzuke / Schedule / Results / Rikishi Directory / Gyoji Directory / Yobidashi Directory / My Rikishi / Compare Rikishi / Basho Analytics
  • Today's Highlights (Featured Matchups):
    • Prominently featured right below the Hero on the homepage with 1-2 curated matchups for the day
    • Displays East vs West bout cards, shikona, rank badges, deterministic avatars, Aikuchi (head-to-head record and two-tone win rate bar), insightful bout preview commentary, and one-tap direct links to the Rikishi Comparison page
    • Generated automatically from the official musubi bout on days with published torikumi data; hand-written static matchups, fixed descriptions, and hand-curated head-to-head records have been removed
    • On days without an official torikumi, renders only a neutral "Today's Highlights / Awaiting Official Bouts / Featured bouts, aikuchi, and comparison links will appear after the official torikumi is published." notice — no rikishi names, articles, aikuchi, comparison/bout links, or commentary box
    • Aikuchi is sourced exclusively from /api/v1/rikishi-matchups.json (pre-aggregated from official profile history) and is only shown when the JSON loads successfully. On failure the aikuchi block is hidden while comparison and bout links remain available
  • Revamped Rikishi Comparison (/compare/?ids={id1},{id2}):
    • Highlights Aikuchi (Head-to-Head Record / Compatibility) as the primary hero scoreboard with score, win rate, contextual lead badge, and two-tone ratio meter
    • East vs West graphical faceoff card (rank, avatar, current basho win-loss record)
    • Physical stats visual comparison bars (height, weight, career win rate) with advantage indicators
    • Top winning techniques (Kimarite) side-by-side ranking dynamically aggregated from tournament archives
    • Quick-pick matchup presets (Yokozuna, Ozeki, Sekiwake, Sanyaku rivalries) and one-tap selection for saved My Rikishi pairs
  • Bilingual lists and profiles for 42 active gyoji and 45 active yobidashi, sourced from the official Japan Sumo Association website. Official numeric IDs are used in page and JSON API URLs, and no photographs are published
  • Banzuke pages for makuuchi and juryo rankings and records, with MiniMax I2I Generation processed rikishi profile images and links to rikishi profiles
  • My Rikishi (/my-rikishi/) stores up to 10 wrestlers by shikona with navigation count badges, URL synchronisation, and localStorage persistence across devices
  • Basho analytics (/analytics/) shows makuuchi yusho, jun-yusho, kanto-sho, gin-o-sho, and juryo yusho for the current basho
  • The ShareCurrentLink button copies the current URL to the clipboard (with a manual fallback for non-secure contexts)
  • Monthly hub pages listing all 15 daily pages
  • Daily pages for makuuchi and juryo torikumi, with profile links on wrestler names
  • ascending / descending sorting on banzuke, hub, and daily pages
  • Unpublished days remain available as pending pages with empty-state messaging
  • The month key is derived dynamically from generated data in app/lib/torikumi-data.ts
  • Homepage Latest News section split into two sub-sections: Japan Sumo Association announcements and Sumo World News (latest 5 from dmenu Sports)
  • Homepage Kimarite card links to the /kimarite index page that lists all 82 winning techniques, grouped by category with bilingual Japanese/English commentary
  • News JSON is regenerated automatically by the news-feed-update GitHub Actions workflow via the Python scraper (/api/v1/news.json)
  • Stories are managed as Japanese Markdown posts in blog/posts/*.md. Run npm run blog:build to generate the standalone blog.osada.us output in dist-blog/ and refresh /api/v1/blog.json for homepage integration
  • Four WebMCP tools (search_rikishi, list_basho, get_banzuke_for_month, get_torikumi_for_day) are exposed for AI agents running in supported browsers (prefers document.modelContext.registerTool from the W3C Draft, falls back to navigator.modelContext.registerTool)

Tech Stack

  • Frontend: React 19, TypeScript, React Router, Vite
  • Testing: Vitest, Testing Library, jsdom
  • Data generation: Python (scripts/update_sumo_data.py, scripts/update_official_profiles.py)
  • Hosting: Cloudflare Pages
  • Data sources: Japan Sumo Association Ajax endpoints and the official gyoji/yobidashi member list and profile pages
  • Rikishi imagery: local PNG illustrations processed with MiniMax I2I Generation from Japan Sumo Association profile photos
  • News sources: Japan Sumo Association announcements page and dmenu Sports (https://sumo.sports.smt.docomo.ne.jp/news/)

Local Development

Requirements:

  • Node.js 20.19+ or 22.12+
  • npm 9+
  • Python 3.10+

Setup:

git clone https://github.com/dai/o-sumo.git
cd o-sumo
npm install

Notes:

  • package-lock.json is committed
  • Use npm install for the initial setup
  • Use npm ci for reproducible reinstalls and CI

Main commands:

# Dev server
npm run dev

# Type checking
npm run typecheck

# Tests
npm test

# Production build
npm run build

# Read-only current-data switch preflight
npm run preflight:current-data

# Local preview of the built app
npm run preview

# Generate the Stories site output
npm run blog:generate

Useful local URLs:

  • http://localhost:3001/
  • http://localhost:3001/archives
  • http://localhost:3001/rikishi
  • http://localhost:3001/rikishi/{id}
  • http://localhost:3001/my-rikishi/
  • http://localhost:3001/compare/?ids={id1},{id2}
  • http://localhost:3001/gyoji/
  • http://localhost:3001/gyoji/{id}/
  • http://localhost:3001/yobidashi/
  • http://localhost:3001/yobidashi/{id}/
  • http://localhost:3001/{YYYYMM}-banzuke/
  • http://localhost:3001/{YYYYMM}-torikumi
  • http://localhost:3001/{YYYYMM}-yotei
  • http://localhost:3001/{YYYYMMDD}-torikumi
  • http://localhost:3001/{YYYYMMDD}-yotei
  • http://localhost:3001/kimarite
  • http://localhost:3001/analytics/
  • http://localhost:3001/api/v1/rikishi-matchups.json
  • http://localhost:3001/api/v1/news.json

Data Updates

Full refresh (banzuke + torikumi + rikishi profiles):

python scripts/update_sumo_data.py

Rikishi profiles only:

python scripts/update_sumo_data.py --rikishi-only

This full-profile refresh also generates rikishi-matchups.json, resolving shikona before and after name changes to official rikishi IDs. The file is replaced only after all target profiles pass retrieval, ID resolution, and cross-profile consistency checks; partial fetches and parse failures preserve the last valid file. See docs/rikishi-profile-refresh-runbook.md for pre-publish checks.

Rikishi profiles only, limited to the first 10 profiles (for testing):

python scripts/update_sumo_data.py --rikishi-only --profile-limit 10

Torikumi-only refresh:

python scripts/update_sumo_data.py --torikumi-only

Results-only or schedule-only refresh:

python scripts/update_sumo_data.py --torikumi-scope result
python scripts/update_sumo_data.py --torikumi-scope schedule

Torikumi-only plus scoped refresh:

python scripts/update_sumo_data.py --torikumi-only --torikumi-scope result
python scripts/update_sumo_data.py --torikumi-only --torikumi-scope schedule

News feed only (Japan Sumo Association announcements + dmenu Sports):

python scripts/update_news_feed.py

Gyoji and yobidashi directories only, sourced from the official Japan Sumo Association website:

python scripts/update_official_profiles.py
python scripts/update_official_profiles_test.py

See docs/official-profile-refresh-runbook.md for generated-file and pre-publish integrity checks.

The July basho is final and retained as immutable snapshots in app/lib/july2026-data.ts and app/lib/july2026-banzuke-data.ts. The current /api/v1/banzuke.json and /api/v1/torikumi.json serve September; July remains available as an archive.

Run npm run preflight:current-data before switching current data. It fetches the official annual schedule and banzuke, then checks current banzuke/torikumi, archives, routes, sitemap, and workflow consistency without writing files. The September 2026 switch used --current-month 202607 --target-month 202609. It prints READY and exits 0 only when every gate is [OK]; official publication/fetch failures and contract mismatches print BLOCKED and exit 1. The generator is not run, and data, routes, sitemap, redirects, and workflow schedules are not changed.

To check a different basho month, pass arguments after -- so npm forwards them to the script: npm run preflight:current-data -- --current-month YYYYMM --target-month YYYYMM (npm 8+). You can also invoke the script directly: python scripts/preflight_current_basho.py --current-month YYYYMM --target-month YYYYMM. The npm script keeps the hard-coded defaults, so subsequent switches must specify the months explicitly.

For the next basho switch, preserve the current September data and the immutable July snapshots, and switch banzuke, torikumi, public JSON, monthly routes, and sitemap together only after validating the new official data.

Generated outputs:

  • app/lib/sumo-data.ts
  • app/lib/torikumi-data.ts
  • app/lib/news-data.ts
  • public/api/v1/banzuke.json
  • public/api/v1/torikumi.json
  • public/api/v1/rikishi.json
  • public/api/v1/rikishi/{id}.json (one file per rikishi, including name, yomi, currentRank, sourceUrl, and updatedAt)
  • public/api/v1/rikishi-matchups.json (unique official ID pairs and both career win totals derived from profile history)
  • public/api/v1/news.json
  • public/api/v1/gyoji.json / public/api/v1/gyoji/{id}.json (42 gyoji)
  • public/api/v1/yobidashi.json / public/api/v1/yobidashi/{id}.json (45 yobidashi)
  • public/images/rikishi/{id}.png (processed profile illustrations for all rikishi, used permanently on both profile and banzuke pages)

Key validations:

  • 42 makuuchi rikishi
  • 28 juryo rikishi
  • 15-day archives for both results and schedules
  • Published days are populated from source data when available
  • Unpublished days remain as pending placeholders
  • Gyoji and yobidashi list/detail JSON counts match and contain no image fields

Automated Updates

GitHub Actions separates torikumi schedules, results, and news. Daily schedule updates open a PR and request auto-merge after checks; realtime result updates validate and push directly to main.

Shared helpers under scripts/ci/:

  • run_torikumi_generator.sh — runs update_sumo_data.py with up to two retries
  • validate_torikumi.py — schema check for public/api/v1/torikumi.json
  • validate_news.py — schema check for public/api/v1/news.json
  • notify_discord.sh — posts a Discord notification when DISCORD_WEBHOOK_URL is configured

Workflows:

  • Daily update: .github/workflows/daily-data-update.yml
    • trigger: JST 13:00, 15:00, 17:00, and 19:00 plus workflow_dispatch
    • updates torikumi schedules only (--torikumi-only --torikumi-scope schedule)
  • Realtime results update: .github/workflows/realtime-torikumi-direct-update.yml
    • trigger: every 10 minutes from JST 13:00 through 18:50 plus workflow_dispatch
    • updates torikumi results only (--torikumi-only --torikumi-scope result --skip-rikishi-fetch --strict-torikumi-fetch)
    • logs: GitHub Actions job summary aggregates step results, commit state, and the run URL
  • News update: .github/workflows/news-feed-update.yml
    • schedule: every 2 hours from JST 09:05 through 19:05
    • updates Japan Sumo Association and dmenu Sports news (python scripts/update_news_feed.py)
    • skips rewriting news.json when fetched items and source states are unchanged
    • One PR per day: the PR is opened on the JST-date-keyed branch automation/news-updates-<YYYY-MM-DD>; multiple same-day runs accumulate commits on the same PR
    • Auto-merge: enabled only on the JST 19:xx run via gh pr merge --auto --squash --delete-branch; the branch is removed after merge

Discord notifications (optional)

Set DISCORD_WEBHOOK_URL as a repository secret to send failure notifications to a Discord channel from the three automated workflows. The workflows continue to work normally when the secret is absent.

Setup:

  1. In Discord open the target channel → Edit channel → Integrations → Webhooks → New webhook, then copy the URL.
  2. Open the GitHub repository → Settings → Secrets and variables → Actions → New repository secret.
  3. Name: DISCORD_WEBHOOK_URL, Value: the webhook URL from step 1 → Add secret.

See scripts/ci/notify_discord.sh for the embedded payload structure.

Testing

  • test runner: Vitest
  • component testing: Testing Library
  • workflow: .github/workflows/test.yml

Current main coverage:

  • routing helpers in app/lib/torikumi-routes.ts
  • sorting helpers in app/lib/sorting.ts
  • kimarite 82-entry master in app/lib/kimarite-data.ts
  • homepage navigation
  • homepage news section (Japan Sumo Association + Sumo World News split sections, empty states, see-all links)
  • banzuke sorting
  • 15-day hub rendering and sorting
  • daily torikumi sorting and pending-state rendering
  • gyoji/yobidashi lists and profiles, bilingual ranks, official numeric IDs, API paths, dynamic metadata, and sitemap entries
  • My Rikishi toggle, list, IME input correction, and URL synchronisation
  • Compare Rikishi (exactly two wrestlers) search, URL sync, seven metrics, and head-to-head results from both display perspectives
  • Basho analytics (/analytics/) makuuchi awards, special prizes, and juryo yusho
  • WebMcpProvider four-tool registration with document.modelContext.registerTool / navigator.modelContext.registerTool

GitHub Actions runs the following on pull requests and pushes to main, codex/**, and automation/data-updates:

  • npm ci
  • npm run typecheck
  • npm test
  • npm run build

Cloudflare Pages

  • Production URL: https://osada.us
  • SPA fallback file: public/_redirects (app routes only; /api/v1/* serves static JSON as-is)
  • Direct access to date-based URLs falls back to index.html

September Basho Operations

  • The current APIs have switched to September; keep the July TypeScript snapshots immutable as archives.
  • Scheduled triggers activate only after this workflow change reaches the default main branch. Runs may be delayed or skipped, and the shared concurrency lock does not cover an outstanding Daily PR merge; there is no guaranteed ten-minute publication SLA.
  • After the official September 12 torikumi publication, run gh workflow run daily-data-update.yml -R dai/o-sumo --ref main, watch the run, then inspect PR checks and merge, the JSON on main, and production JSON. Empty data for both divisions before publication is a no-op, not successful publication.
  • After the September 27 final results and next-day verification, remove both cron schedules in a separate PR. Summaries are point-in-time snapshots, not merge or deployment proof.
  • preflight:current-data is a transition-only safety gate that requires paused workflows, not an in-tournament readiness check. The validator permits only the precise fusen exception and generation preserves old data on partial fetch failure.
  • The news workflow remains scheduled every two hours from JST 09:05 through 19:05.
  • Manual runs can be started with gh workflow run daily-data-update.yml -R dai/o-sumo --ref main, gh workflow run realtime-torikumi-direct-update.yml -R dai/o-sumo --ref main, or gh workflow run news-feed-update.yml -R dai/o-sumo --ref main.
  • The realtime workflow uses --torikumi-only --torikumi-scope result --skip-rikishi-fetch, so it is limited to torikumi results.
  • If results still look stale, triage in this order: run history -> run logs (event.schedule, JST time, updatedAt fields) -> upstream judge values.
  • Keep the cache policy in public/_headers unchanged to control Cloudflare usage.
  • Keep the PWA Service Worker on registerType: "autoUpdate" so updates are applied automatically.

Important Files

  • app/main.tsx: route definitions
  • app/page.tsx: homepage
  • app/archives/page.tsx: archives page
  • app/banzuke/page.tsx: banzuke page
  • app/kimarite/page.tsx: all 82 kimarite listing page
  • app/officials/page.tsx: gyoji and yobidashi lists and profiles
  • app/torikumi/page.tsx: monthly hubs for results and schedules
  • app/analytics/page.tsx: basho analytics page
  • app/rikishi/MyRikishiPage.tsx: My Rikishi page
  • app/rikishi/CompareRikishiPage.tsx: Compare Rikishi page
  • app/components/TorikumiDayPage.tsx: daily result and schedule pages
  • app/components/BanzukeTable.tsx: banzuke table component
  • app/components/MyRikishiToggle.tsx: My Rikishi toggle
  • app/components/ShareCurrentLink.tsx: copy current URL to clipboard
  • app/components/NewsSection.tsx: homepage news section (Japan Sumo Association + Sumo World News split sections)
  • app/components/GreetingSection.tsx: homepage "From the Editor" callout (h3, uses getLatestBlogPost(), nested inside MonomosuSection)
  • app/components/MonomosuSection.tsx: standalone "Have Your Say" section on homepage (visible h2 + zabuton reaction + textarea drawer + nested GreetingSection)
  • app/components/KimariteCard.tsx: homepage kimarite entry card
  • app/components/WebMcpProvider.tsx: WebMCP tool registration (prefers document.modelContext.registerTool)
  • app/components/PrimaryNavigation.tsx: primary navigation
  • app/lib/archives-data.ts: past basho dataset
  • app/lib/torikumi-routes.ts: month-key URL resolution and navigation
  • app/lib/sumo-data.ts: banzuke data (includes rikishi type definitions)
  • app/lib/torikumi-data.ts: torikumi archive data
  • app/lib/news-data.ts: static news feed data
  • app/lib/kimarite-data.ts: master list of all 82 winning techniques
  • app/lib/official-profile.ts: gyoji/yobidashi types, API fetching, and numeric-ID paths
  • app/lib/my-rikishi.ts: My Rikishi registration state (localStorage sync)
  • app/lib/basho-status.ts: basho status (live / upcoming / final) classification
  • app/lib/webmcp.ts: WebMCP tool definitions (4 tools)
  • app/lib/july2026-data.ts: immutable July 2026 (Nagoya) basho snapshot
  • app/lib/july2026-banzuke-data.ts: immutable July 2026 banzuke snapshot
  • app/lib/archive-basho-data.ts: aggregated past + current basho data
  • app/lib/blog-build.ts: static HTML, RSS, and sitemap generation for Stories
  • app/lib/agent-skills.ts: Agent Skills Index metadata
  • scripts/update_sumo_data.py: data generation script for banzuke, torikumi, and rikishi profiles
  • scripts/update_news_feed.py: news feed generation script
  • scripts/update_official_profiles.py: gyoji and yobidashi data generator
  • scripts/build_blog.ts: Stories output and blog.json generator

Contact

License

MIT

Download