Skip to content
OFC
Home Changelog

Changelog

سجل التغييرات

Built in public

Changelog

سجل التغييرات

Every single thing shipped on this site — OWASP headers, referee signals, poule sheets, scholarship forms, AI translation, Arabic RTL layout, door-check QR, 150 iterations of "what would a real federation website actually do?". The log is public because the work is.

Iterations
234
Log entries
222
Days
3
Features
117
feature · 117 ux · 25 api · 24 seo · 6 multi-role · 6 feed · 4

Thu, 23 Apr 2026 · 69 entries

  • iter
    234
    api
    /api/public/coaches JSON mirrors the existing coaches.csv
    filters team by /coach|مدرب/i, strips email/phone, exposes id/nameEn+Ar/roleEn+Ar/photo/bio/certificationLevel/joinedAt. Verified 200 count=1 (Hisham Karshod)
  • iter
    233
    ux
    surface iter-232 per-athlete Results CSV + JSON downloads as ghost-buttons next to Passport PDF on /athletes/{slug} (bilingual labels, fa-table + fa-brackets-curly icons)
    verified both links render on jana-al-sharji prod
  • iter
    232
    api
    /athletes/{slug}/results.csv + results.json per-athlete match-history endpoints (item #24 missing-features); slug→athleteId via readAthletes + resultsForAthlete + medalTally; unknown slug 404, empty roster returns header-only CSV / count=0 JSON
  • iter
    231
    ux
    global '/' keybind focuses first search input on any public page (GitHub/Notion style, skips when typing in input/textarea/contenteditable); focuses+selects
  • iter
    230
    maintenance
    smoke.sh extended with 12 filter permutations from iters 225-229 (weapon, club, year, tag, q, limit, medal, types=)
    61→73 endpoints, 73/73 green against prod
  • iter
    229
    api
    /api/public/results ?athleteSlug (or athleteId) + ?medal + ?year + ?weapon + ?limit (1-500, default 200) filters; store currently empty so all permutations return 200 count=0
  • iter
    228
    sync
    refresh content/loop-log.txt from 197 to 227
    footer 'Built in public · N iterations shipped' + /changelog + /changelog.rss.xml + /changelog.atom.xml all bump in one ship
  • iter
    227
    api
    /api/public/news ?year= date-prefix + ?q= NFD-normalised substring match + ?limit=1-200 (default 50). Verified 22 total → 5 from 2025 → 1 matching 'bahrain' → 3 capped by limit
  • iter
    226
    api
    /api/public/events ?year=YYYY date-prefix match + ?tag= case-insensitive exact match; verified 8 total → 3 from 2025, 2 international, 1 composed
  • iter
    225
    api
    /api/public/athletes ?weapon=foil|epee|sabre (NFD) + ?club=substring + ?active=0 filters; exposes discipline+gender on each row. Verified 13 total → 7 foil + 6 epee, 3 Salalah by club
  • iter
    224
    ux
    richer ShareButton titles on athlete/event/news/club detail pages (athlete: name+category+discipline+club; event: title+date+tag; news: title+date; club: name+city
    all suffixed 'Oman Fencing')
  • iter
    223
    api
    /api/public/search/suggest?types= mirrors iter-222 filter on the typeahead endpoint
    verified q=al types=athlete returns 8 athletes vs 4 types mixed unfiltered
  • iter
    222
    api
    /api/public/search?types=a,b,c filter (comma-separated allowlist of athlete/event/news/club/committee; unknown vals dropped; empty keeps all)
    verified q=jana types=athlete narrows from 5 mixed to 1 athlete
  • iter
    221
    discoverability
    /.well-known/change-password 302 to /login for W3C password-manager discovery (Bitwarden, Apple Passwords, 1Password, Chrome leaked-password flow); fencing.om is OTP so redirect to re-authentication page
  • iter
    220
    maintenance
    scripts/smoke.sh 61-endpoint smoke suite (discovery/feeds/registries/entities/aggregates/search/detail/pages, HTTP code + content-type substring check, exits non-zero on any mismatch, 61/61 green against prod now)
  • iter
    219
    refactor
    DRY spotlight picker into src/lib/spotlight.ts todaySpotlight(); /api/public/spotlight + SpotlightFeature both delegate
    guaranteed same pick (API and homepage both return Salma Al Daghishi today); -35 net lines
  • iter
    218
    ux
    SpotlightFeature on homepage surfaces iter-217 athlete-of-day (same FNV-1a hash ensures page pick matches API pick; renders between Stats and About with photo + discipline badge + category + club)
  • iter
    217
    api
    /api/public/spotlight athlete-of-the-day (FNV-1a(yyyy-mm-dd) mod active-roster, same pick all day, rotates at UTC midnight)
    today's pick: Salma Al Daghishi
  • iter
    216
    discoverability
    footer bottom bar surfaces /now (iter 206) + /developers (iter 170) alongside Privacy/Terms/CoC/Security
  • iter
    215
    ux
    'Jump to' anchor pills on /committee (President + board-members grid anchor + CSV download, star/users/file-csv icons, EN+AR)
  • iter
    214
    ux
    client-side filter on /clubs index (name+city search, NFD-normalised, shown/total counter); mirrors iter-213 committee filter
  • iter
    213
    ux
    client-side filter input above /committee board grid (NFD-normalised, live shown/total count pill, empty-state msg, no network roundtrip)
  • iter
    212
    export
    /coaches.csv (head coach + certified coaches filtered by coach|مدرب role pattern, NO email/phone, 600s cache) registered in feeds.json catalog and /developers docs
  • iter
    211
    seo
    /sitemap-images.xml Google image sitemap (event photos + news covers + athlete photos grouped by page URL with image:title context, schema 1.1, 1h cache); listed in sitemap.xml index
  • iter
    210
    ux
    /committee member cards get id={member.id} + scroll-mt-24 so /committee#tm_president deep-links work; matches iter-201 /api/public/committee/{id} URL contract
  • iter
    209
    api
    /api/public/sitemap.json (100 static pages + 51 dynamic news/events/athletes/clubs URLs deduped+sorted, 150 total, each with relative path + absolute URL, 600s cache)
  • iter
    208
    ux
    /now page gets JSON snapshot + .ics subscribe + News RSS pill row (mirrors iter-178 news index pattern, surfaces structured siblings)
  • iter
    207
    api
    /api/public/now JSON sibling to /now page (eventsThisWeek + upcomingNext5 with inDays + recentNews 7d + recentResults 30d, 300s cache, registered in /api/public)
  • iter
    206
    ux
    /now 'what's on this week' snapshot page (4 sections: This Week events / Recent News 7d / Coming Up Next 5 + day count / Recent Results 30d; empty-state pills with deep links; sitemap daily-changefreq)
  • iter
    205
    ux
    NewsFeature on homepage (3 latest news cards inserted after Events before Gallery, mirrors /news index card style with date badge + line-clamp-2 title + RTL-aware arrow, 'All news' link in header)
  • iter
    204
    docs
    /developers refreshed with all endpoints shipped since iter 170 (+committee/{id}, +disciplines, +changelog, +feeds.json, +new Search group with full+suggest, +atom feeds, +per-event ICS, +per-club CSV/JSON, +openapi.json)
  • iter
    203
    api
    /api/openapi.json refreshed to v1.1.0 (15→21 paths) covering committee/{id} disciplines changelog feeds.json search search/suggest shipped since iter 171
  • iter
    202
    api
    /api/public/search/suggest?q= typeahead endpoint (max 8 title-only results, 30s cache, NFD scoring); search.ts moved to search/index.ts to allow sibling; registered in /api/public
  • iter
    201
    api
    /api/public/committee/[id] per-member JSON (President's record returns full safe-fields, unknown 404, list endpoint moved committee.ts → committee/index.ts to allow [id].ts sibling); per-entity detail set complete across athletes (166), events (167), news+clubs (168), committee (201)
  • iter
    200
    MILESTONE
    /api/public/search?q=&limit=N cross-entity search (athletes+news+events+clubs+committee, NFD-normalised exact>prefix>substring scoring, news falls back to body scan, snippet+score per hit, 60s cache, registered in /api/public)
  • iter
    199
    a11y
    aria-current='page' + green tint on active Nav item (top + mobile drawer); active when pathname === target OR startsWith target+/, so /news/<slug> still highlights News
  • iter
    198
    ux
    'Built in public · N iterations shipped' centered footer line on every public page (reads max iter from content/loop-log.txt at module load, links to /changelog, EN+AR; +log refresh to keep number current)
  • iter
    197
    feed
    /changelog.atom.xml Atom 1.0 sibling to iter-188 RSS (100 entries, autodiscovered on /changelog via head slot, entry in /api/public/feeds.json catalog)
  • iter
    196
    api
    /api/public/feeds.json catalog (24 feeds across 8 MIME types: rss/atom/json/xml/calendar/csv/plain/opensearch)
    entity tag + format + description + absoluteUrl per entry, 1h cache, registered in /api/public
  • iter
    195
    feed
    /news.atom.xml Atom 1.0 sibling to RSS (per-entry xml:lang, proper id/published/updated, author block, 600s cache) + Base.astro adds <link rel=alternate type=application/atom+xml> alongside the RSS link
  • iter
    194
    ux
    reading-time badge on /news index cards (precomputed in frontmatter from readNewsArticle, 200wpm EN/180wpm AR, EN 'X min'/AR 'X د' next to date)
  • iter
    192
    ux+fix
    born/age badge on athlete profile when birthYear present (3 athletes today: israa-al-siyabi, ahmed-keskes, ali-al-busaidi); iter 192 IIFE pattern silently dropped by Astro template parser, iter 193 fixed by computing ageEoY in frontmatter and using plain {athlete.birthYear && (...)} expression
  • iter
    191
    api
    /clubs/<slug>/athletes.json sibling to iter-190 per-club CSV (same tolerant club-name match, returns club identity + roster array, 404 on unknown, 600s cache)
  • iter
    190
    export
    /clubs/<slug>/athletes.csv per-club roster CSV (tolerant club-name match handles 'Quriyat'/'Quryat' transliteration variants, NFD-normalised, strips 'club' word) + Roster CSV card surfaced on /clubs/[slug] alongside venue + how-to-join
  • iter
    189
    seo
    Base.astro gains <slot name='head'/> + /changelog adds <link rel=alternate rss> for iter-188 changelog.rss.xml so feed readers auto-detect it
  • iter
    188
    feed
    /changelog.rss.xml RSS 2.0 of last 100 shipped iterations (parses content/loop-log.txt) + RSS+JSON CTA pills under /changelog intro (mirrors iter 178 news pattern)
  • iter
    187
    api+sync
    refresh in-repo content/loop-log.txt (was 1 day stale, now reflects iter 186) + new /api/public/changelog JSON feed parsing same file (?limit=N, max 500) registered in /api/public; 175 entries indexed
  • iter
    186
    api
    /api/public/disciplines reference endpoint (3 weapons foil/épée/sabre with EN+AR names, target area, scoring rules, pageUrl) registered in /api/public, 24h cache
  • iter
    185
    ux
    ShareButton on /clubs/[slug] completes detail-page set (news + events + athletes + clubs all have prev/next nav AND share button)
  • iter
    184
    refactor+ux
    extract ShareButton.astro reusable component (data-attr + idempotent document-level handler binding via data-bound flag); /news/[slug] + /events/[slug] swap to component (-80 lines); /athletes/[slug] gains share button above prev/next nav
  • iter
    183
    ux
    share button on /events/[slug] mirrors iter-182 news (navigator.share native + clipboard fallback + transient flash, EN+AR)
  • iter
    182
    ux
    share button on /news/[slug] (navigator.share native dialog on mobile/PWA, navigator.clipboard fallback with 'Link copied'/'تم النسخ' transient flash, EN+AR)
  • iter
    181
    ux
    reading-time estimate badge in /news/[slug] meta strip ('X min read' EN @200wpm + 'قراءة X دقيقة' AR @180wpm; hidden when body empty; AR badge uses flex-ar helper for proper display:flex on lang=ar)
  • iter
    180
    ux
    /clubs index gets CSV + JSON API + Leaderboard pill row (completes listing-page export pattern across news 178 athletes+events 179 clubs 180)
  • iter
    179
    ux
    /athletes + /events index pages get JSON API export pill next to existing CSV/ICS pills (mirrors iter 178 news pattern)
  • iter
    178
    ux
    /news index gets RSS + Email + JSON API CTA strip surfacing /news.rss.xml + /#newsletter + /api/public/news (3 pills below search row, EN+AR, hover green-ofc)
  • iter
    177
    ux
    'Add to calendar' button on /events/[slug] surfaces iter-176 per-event ICS endpoint (calendar-plus icon, download attribute, EN+AR labels, leads the CTA strip above 'Full medal table' and 'All athletes')
  • iter
    176
    feed
    /events/<id>.ics per-event iCalendar export (RFC 5545 line-folding+escape, X-WR-CALNAME=event title, 600s cache, CORS *) sibling to /events.ics whole-calendar feed
  • iter
    175
    ux
    prev/next nav on /clubs/[slug] (alphabetical EN sort)
    completes detail-page nav set across news (172) events (173) athletes (174) clubs (175)
  • iter
    174
    ux
    prev/next nav on /athletes/[slug] completes the trio (news iter 172 + events iter 173 + athletes iter 174); active roster sorted alphabetically by EN name, edge entries single card, RTL-aware
  • iter
    173
    ux
    prev/next event navigation on /events/[slug] mirrors iter 172 news pattern (sorted by date newest-first, RTL-aware arrows, line-clamp-2 titles, edge events single card)
  • iter
    172
    ux
    prev/next article navigation on /news/[slug]
    grid 2-col older+newer cards, RTL-aware arrows, line-clamp-2 titles, hover green-ofc border; edge articles show single card
  • iter
    171
    api
    /api/openapi.json OpenAPI 3.1 descriptor (15 paths: entities+aggregates+ops, slug/id path params, envelope schema, Health schema, CC BY 4.0 license, contact [email protected]) registered in /api/public index, 1h cache
  • iter
    170
    docs
    /developers public API + feeds documentation page (bilingual; Registry/Entities/Feeds/Discovery tables; 14 JSON endpoints + 7 CSV/RSS/ICS + 6 discovery files; curl example; cache+CORS notes); added to sitemap-pages.xml
  • iter
    169
    export
    /committee.csv (id, nameEn, nameAr, roleEn, roleAr, isBoard, joinedAt
    NO email/phone; 7 active members) completing CSV export set with athletes/clubs/events/results; text/csv attachment, 600s cache, CORS *
  • iter
    168
    api
    /api/public/news/[slug] (slug, titleEn/Ar, date, coverPhoto, bodyEn/Ar, url) + /api/public/clubs/[slug] (slug, names, city, notes, public contact, url)
    both 404 on unknown, 600s cache, completing per-entity detail pattern with iters 166 (athletes) and 167 (events)
  • iter
    167
    api
    /api/public/events/[id] individual event JSON (id, titleEn/Ar, date, tag/Ar, photo, descEn/Ar, url)
    404 on unknown, 600s cache, for partner embeds and calendar integrations
  • iter
    166
    api
    /api/public/athletes/[slug] individual athlete JSON (id, slug, nameEn/Ar, discipline, category, gender, club, photo, bioEn/Ar, medals, highlights, FIE fields)
    matches by slug OR id, 404 on unknown/inactive, 600s cache, for partner embeds and share-card generators
  • iter
    165
    seo
    robots.txt rewritten: old blanket Disallow /api/ hid public JSON feeds from crawlers; now explicit Allow /api/public /api/health /.well-known/ and explicit Disallow /board /my /account /admin /api/admin /api/auth /api/board /api/my /api/ai /api/committee

Wed, 22 Apr 2026 · 30 entries

  • iter
    164
    seo
    /opensearch.xml OpenSearch 1.1 descriptor (EN + AR, 16/32/192 icons, search template /search?q=) + <link rel='search'> in Base.astro so browsers (Firefox/Chrome/Safari) can add fencing.om as a built-in search engine
  • iter
    163
    api
    /api/public/latest combined one-call feed (5 latest events + 5 latest news + 10 latest results) 300s cache, so widgets don't chain 3 API calls; registered in /api/public index
  • iter
    162
    api+seo
    /api/public/upcoming (future events only, sorted soonest first, 600s cache, registered in /api/public index) + sitemap-pages dedup /partners and /hall-of-fame were each listed twice
  • iter
    161
    api
    /api/public/committee (7 active members, id/nameEn/Ar/roleEn/Ar/photo/bioEn/Ar/isBoard/joinedAt sorted by order)
    email/phone deliberately omitted, private contact only via rate-limited /api/committee/contact; registered in /api/public index
  • iter
    160
    seo
    sitemap-pages.xml now indexes all 19 /tools/* routes (door-check-qr first-aid floor-captain kit-check penalty-tracker risk-assessment session-plan share-card training-load training-log training-plan weekly-digest added to the existing 7); 20 total tool entries
  • iter
    159
    fix
    /athletes/[slug] second missing import streakForAthlete (same file)
    all 13 athlete profile pages now return 200 (slugs that don't exist return 302 to /athletes, by design)
  • iter
    158
    fix
    /athletes/[slug] 500
    missing import pctLast30Days from lib/training, caught by module-audit agent
  • iter
    157
    seo
    sitemap-pages.xml now covers 24 previously-missing public routes (/activity /changelog /announcements /league /contact /coach-corner /beginner-pathway /lessons /glossary /rules-summary /scoring /spectator-info /referee-signals /target-areas /mental-game /strength /stretches /nutrition /statistics /achievements /medals /scholarship /security /code-of-conduct) so crawlers finally discover them
  • iter
    156
    api+footer
    /api/public/results endpoint (last 200 tournament results with athlete names, medal, placing, level, source) + footer now links /code-of-conduct + /security alongside /privacy + /terms in both EN and AR
  • iter
    155
    fix
    /api/public/stats breakdown zeros (discipline is 'Foil'/'Épée'/'Sabre' capitalized+accented; gender is 'Male'/'Female' capitalized; normalise to lowercase ASCII before matching) + sum athlete.medals.{gold,silver,bronze} to top-level medals block
  • iter
    154
    api
    /api/public/clubs (8 clubs) + /api/public/stats (counts + weapon/gender breakdown) CORS * 600s cache, registered in /api/public index
  • iter
    153
    governance
    /code-of-conduct bilingual 8-section page (who, values, expectations, prohibited, safeguarding, anti-doping, enforcement, agreement) linked to /safeguarding /anti-doping /decisions
  • iter
    152
    ops
    /humans.txt + /api/health (uptime probe with intentionally minimal surface: no PID/mem/SHA)
  • iter
    151
    security
    RFC 9116 /.well-known/security.txt + bilingual /security responsible-disclosure page (scope, how-to-report, in/out-of-scope, safe harbour, acknowledgments) thematic followup to pass-2 codex fixes
  • codex
    codex challenge mode found 4 real concurrency bugs (P1 role-requests lost-update race; P2 pending-changes same; P2 rate-limit bypass via cancelOwn splice; P2 athletes.json last-write-wins) + 2 bonus bugs (double-approval guard, ClaimWizard duplicate note fields) ALL FIXED with withFileLock wrappers, status='cancelled' marking, updateAthlete atomic helper, disabled hidden panels · 20 unit + 7+42+11 E2E all green on prod
  • crud
    11-case write-path CRUD suite added (claim+approve, bio+approve, medical direct, emergency direct, camp registration, newsletter, permission gates x3, rate limit, UI /my)
    snapshot+restore rig keeps prod data pristine · surfaced real API bug fixed: notify() was awaited and blocked redirects; switched to fire-and-forget across /api/my/claim + /api/board/approvals · 60 total E2E assertions passing on live prod
  • deep
    11-case depth-audit spec added (mobile overflow, Arabic CSS hide, identity menu, ⌘K switcher, sidebar active, app grid integrity)
    zero real bugs found; initial false-positive on Arabic h1 traced to Playwright :has-text matching hidden descendants · 49 total E2E assertions green
  • modules
    authenticated module sweep 31/31 green
    every /board module + /my + /admin pages verified (HTTP 200 + expected text + no console errors + no failed requests) using production admin session · test harness committed at tests/e2e/module-sweep.spec.ts + scripts/module-sweep.sh
  • icons
    6 broken Tabler refs replaced (polls:chart-bar, approvals:checks, contracts:contract, overview empty:circle-check, settings head-coach:user-star) audited 5699 glyphs · 20 unit + 7 E2E green
  • i18n
    admin Arabic labels were display:none because global.css never un-hid .ar-only
    added display:revert rules matching Base.astro parity; every Arabic label across /board /my sidebar topbar role cards approvals app grid module header now renders · 20 unit + 7 E2E green
  • admin-shell
    /my migrated onto Admin layout (user-heart icon, ModuleHeader, same topbar as /board) + full mobile pass (responsive topbar, chip collapse, tile sizing, welcome hero break-words) · 20 unit + 7 E2E green
  • board
    Odoo-style app launcher on /board home (no sidebar, category-tinted tile grid, staggered fade-in) + ModuleHeader with back-arrow and ⌘K-searchable app switcher sheet · 20 unit + 7 E2E green
  • board
    BoardMemberCard on /my (4-up KPIs + next-meeting hero + open-actions list) + tab-bar edge fades + auto-scroll active into view · 20 unit + 7 E2E green
  • ui-ux
    App Store polish across /board + /my: IdentityChip, My.astro shell, sidebar stripe, role-card unification, ApprovalsList field diff, design tokens + motion primitives · 20 unit + 7 E2E green
  • multi-role
    MyFamilyCard for staff-who-are-parents + Playwright E2E 7/7 green
  • multi-role
    RefereeCard live (availability direct, cert renewal banner)
  • multi-role
    SecretaryCard live (club-edit + announcement submit, write-through on approve)
  • multi-role
    ParentCard live (per-child AthleteCards, add-child CTA)
  • multi-role
    AthleteCard live (medical direct, emergency direct, profile via approvals)
  • multi-role
    claim+approve flow deployed (OTP dispatch, /my shell, ClaimWizard, Approvals tab, notifications, role-requests+pending-changes stores)

Tue, 21 Apr 2026 · 123 entries

  • iter
    150
    feature
    FINALE · Changelog
    /changelog (public log of all 150 sprint iters, grouped by day, kind filters, sprint-complete hero) · SPRINT 150/150 COMPLETE
  • iter
    149
    feature
    Activity timeline
    /activity (unified news/events/results/media timeline, 4 time buckets, kind filter chips)
  • iter
    148
    feature
    Homepage video embed
    VideoFeature block after Gallery (click-to-load youtube-nocookie, lazy poster, subscribe CTA)
  • iter
    147
    feature
    Camp registration backend
    /camps/register form + /api/camps/register POST (JSON store + coaching@ email, 5 camps seeded)
  • iter
    146
    feature
    Share-card generator
    /tools/share-card (1200x630 social card, live SVG preview, 5 accents, PNG+SVG download)
  • iter
    145
    feature
    Coach certification
    /coaches/certification (5-level pathway Assistant→FIE Master + directory of 8 certified coaches)
  • iter
    144
    feature
    Inter-club league
    /league (weighted points: medal value × event level multiplier, live from results)
  • iter
    143
    feature
    Personal training plan
    /tools/training-plan (4-week generator by weapon/level/days/goal; skill/strength/sparring/recovery)
  • iter
    142
    feature
    Alumni & legends page
    /athletes/alumni (6 curated pioneers + auto-merge with inactive athletes + add-your-name CTA)
  • iter
    141
    feature
    Weekly digest preview
    /tools/weekly-digest (email-ready template, live next event + YTD medals + latest 3 news, copy HTML button)
  • iter
    140
    feature
    Announcements page
    /announcements with 6 seeded notices (policy/club/competition/safeguarding/governance), type filter chips, CTA
  • iter
    139
    feature
    Penalty tracker tool
    /tools/penalty-tracker (cards by fencer, auto-escalate 2nd yellow, CSV export)
  • iter
    138
    feature
    Training-load tool
    /tools/training-load (sRPE, weekly load, ACWR, monotony, strain; localStorage, seed data)
  • iter
    137
    feature
    Door-check QR tool
    /tools/door-check-qr (A4 printable badge, live form, goqr.me render)
  • iter
    136
    feature
    Coach corner blog
    /coach-corner with 6 seeded posts (technique/mental/parents/career) + pitch CTA
  • iter
    135
    feature
    Contact page
    /contact with 6-department mailto routing (general/press/coaching/clubs/scholarship/safeguarding)
  • iter
    134
    feature
    News archive TOC
    /news/archive grouped by year+month with jump-to-year chips
  • iter
    133
    feature
    404 polish
    search form + Off-the-piste copy + sitemap link
  • iter
    132
    feature
    Human sitemap
    /sitemap (74 links across 12 themed sections)
  • iter
    131
    feature
    Homepage coach CTA block
    dark-gradient section between About and AthletesFeature, buttons to /lessons + /coaches + /beginner-pathway
  • iter
    130
    feature
    Rules summary infographic
    /rules-summary (9 emoji cards: hit/priority/length/3 cards/safety/piste/salute + cross-links)
  • iter
    129
    feature
    Beginner pathway
    /beginner-pathway (6-step visit→signup→basics→base→first-comp→squad, timeline chips, cross-links)
  • iter
    128
    feature
    Printable calendar
    /calendar-print (A4 portrait, year-grouped events with date tiles, past strike-through, tape-to-wall format)
  • iter
    127
    feature
    Scholarship programme
    /scholarship (commitment/coverage/eligibility/application/confidentiality/funding + confidential email CTA)
  • iter
    126
    feature
    Clubs CSV export
    /clubs.csv (slug/name/city/active/gold/silver/bronze/score/url) + link on /club-leaderboard
  • iter
    125
    feature
    Referee signals poster
    /referee-signals (10 signals across 5 categories, emoji icons, bilingual descriptions)
  • iter
    124
    feature
    Strength program
    /strength (4 blocks: lower/specific/core/weapon-arm, weekly schedule, safety caveats)
  • iter
    123
    feature
    Training log tool
    /tools/training-log (weekly 7-day table: session/min/RPE/notes/mood + reflections, A4 portrait)
  • iter
    122
    feature
    Target areas
    /target-areas (inline SVG silhouettes for foil/épée/sabre with green-shaded valid zones + off-target notes)
  • iter
    121
    feature
    Individual lessons
    /lessons (4 tiers trial/foundation/performance/group + request form posting to /api/contact)
  • iter
    120
    feature
    Spectator info
    /spectator-info (7 sections: what to expect, seat, etiquette, dress, photo, kids, Qs + quick-links)
  • iter
    119
    feature
    Scoring explainer
    /scoring (6 sections: electric box, target, priority, bout length, cards, spectator shortcut)
  • iter
    118
    feature
    Match-day kit checklist
    /tools/kit-check (28 items, 6 categories: weapons/armour/clothing/hydration/paperwork/extras, A4 portrait print)
  • iter
    117
    feature
    Fencing glossary
    /glossary (20 EN+AR terms, live search filter, letter-jump chips, empty-state)
  • iter
    116
    feature
    Mental game
    /mental-game (pre-bout routine, box breathing, reset after lost touch, focus cues, sleep, when to seek help) + cross-links
  • iter
    115
    feature
    Injury-prevention stretches
    /stretches (6 warm-up + 7 cool-down with reps/holds/focus + 5 red-flag signals)
  • iter
    114
    feature
    Athlete nutrition
    /nutrition (6 EN+AR sections: hydration/pre/match-day/recovery/Ramadan/supplements + medical disclaimer)
  • iter
    113
    feature
    Floor captain checklist
    /tools/floor-captain (22-point running order, 5 phases, A4 portrait print)
  • iter
    112
    feature
    Tournament registration
    /register (auto-populated event dropdown, athlete/contact/medical/consent fields, POST /api/contact, 48h confirmation)
  • iter
    111
    feature
    First-aid checklist
    /tools/first-aid (23-point medical readiness: personnel/equipment/venue/paperwork, tick-boxes, sign-line, A4 portrait)
  • iter
    110
    feature
    Achievements feed
    /achievements (unified newest-first stream of milestones + medals grouped per event, cross-links to history/HoF/results)
  • iter
    109
    feature
    Risk assessment template
    /tools/risk-assessment (8 pre-filled hazards, L×S scoring, L/M/H colour chips, inline editable, A4 landscape print)
  • iter
    108
    feature
    Session plan tool
    /tools/session-plan (6 phase blocks w/ minute totals, inline editable, A4 print, pre-filled template)
  • iter
    107
    feature
    Public statistics dashboard
    /statistics (8 headline cards + medal-by-type/weapon/year charts, live-computed from existing data)
  • iter
    106
    feature
    Sitemap catch-up
    14 new URLs in sitemap-pages.xml (8 club details + 3 tools + 7 pages added across iters 65-103)
  • iter
    105
    feature
    Event runsheet tool
    /tools/runsheet (day-of-show timeline builder, inline editable rows, A4 landscape print, pre-filled template) + Tools hub Ready
  • iter
    104
    feature
    Search filter pills
    per-type result counts, auto-dim empty types, updates on every query
  • iter
    103
    feature
    Club detail pages
    /clubs/[slug] dynamic routes, SportsTeam JSON-LD, filtered athlete grid, quick-links to training/venues/join
  • iter
    102
    feature
    Events tag filter
    dynamic pill row above card grid, filters by event.tag, empty-state, client-side
  • iter
    101
    feature
    Sponsor application
    /partners/apply (5-tier pricing, full form w/ tier+budget+goals, honeypot, POST to /api/contact) + /partners CTA updated
  • iter
    100
    feature
    MILESTONE Homepage medal ticker
    bilingual animated marquee of 20 recent medals (emoji + athlete + event + year), reduced-motion safe, RTL-aware
  • iter
    99
    feature
    Homepage testimonial
    rotating bilingual pull-quote (President/HeadCoach/Board), dark section between Gallery and Instagram
  • iter
    98
    feature
    Results medal chart
    stacked horizontal bars per year (G/S/B) with counts + totals + legend
  • iter
    97
    feature
    Newsletter archive
    /newsletter-archive (empty-state w/ live sub count + what-to-expect + inline subscribe form)
  • iter
    96
    feature
    Drills category filter
    6-pill client-side filter (Footwork/Point/Distance/Parry/Tactics/Conditioning) above card grid
  • iter
    95
    feature
    Events CSV export
    /events.csv (id/date/title/tag/photo/url) + CSV button on /events
  • iter
    94
    feature
    Athletes CSV export
    /athletes.csv (id/slug/name/weapon/category/club/photo/url cols) + link on /athletes filter bar
  • iter
    93
    feature
    Results CSV export
    /results.csv endpoint + Download CSV button on /results, extracted resultsData lib for reuse
  • iter
    92
    feature
    Parents FAQ
    /parents (10 EN+AR questions: age/safety/time/cost/academics/supervision/mixed/travel/quitting/talent + trial CTAs)
  • iter
    91
    feature
    Athlete comparison
    /tools/compare?a=&b= SSR, side-by-side G/S/B + intl + recent date + profile link, hub promoted Ready
  • iter
    90
    feature
    Club medal leaderboard
    /club-leaderboard (weighted rank by level+medal, G/S/B + athletes + intl + score cols, medal-coloured position chips)
  • iter
    89
    feature
    Bout slip generator
    /tools/bout-slip (printable DE match slip, red/green score boxes, card cols, signatures, 2 per A4) + promote Ready
  • iter
    88
    feature
    Results filters
    year + medal + search across athlete/event/discipline, count indicator, reset button, auto-hide empty year blocks
  • iter
    87
    feature
    Venue directory
    /venues (8 club venues, EN+AR address, parking, Google/Waze/Apple maps links)
  • iter
    86
    feature
    Homepage partners strip
    OOC/MCSY/FIE/FCA tiles above footer, bilingual, links out + to /partners
  • iter
    85
    feature
    Sitemap index split
    /sitemap.xml is now sitemapindex with 4 child sitemaps (pages/news/events/athletes), 48 pages + dynamic entries, hreflang alternates
  • iter
    84
    feature
    Homepage next-event countdown
    live days/hrs/min/sec to soonest upcoming event, bilingual, calendar CTAs
  • iter
    83
    feature
    Advanced athlete filters
    /athletes category + club selectors + name search, match count, reset button, empty-state
  • iter
    82
    feature
    News archive filters
    /news year pill filters + EN/AR search, count indicator, empty-state reset link
  • iter
    81
    feature
    FAQ rich search
    live client-side EN+AR filter, match counter, clear button, empty-state CTA, auto-expand matches
  • iter
    80
    feature
    Weapon check tool
    /tools/weapon-check (11-point FIE safety checklist per fencer, printable cards, CSV template) + promote Ready
  • iter
    79
    feature
    Poule sheet generator
    /tools/poule-sheet (NxN printable grid, canonical FIE bout order pools 4-10, V/M/TS/TR/Ind cols, A4 landscape) + promote Ready
  • iter
    78
    feature
    Bout timer
    /tools/bout-timer (FIE 3x3min + priority, red/green scoreboard, 10s chime, kbd shortcuts) + promote Ready on hub
  • iter
    77
    feature
    Tools hub
    /tools (pool + bracket ready, 4 'soon' tools placeholders, tool-request CTA)
  • iter
    76
    feature
    DE bracket generator
    /tools/bracket (cross-seed order, auto power-of-2 with BYEs, 8-128 tables, CSV/copy/print)
  • iter
    75
    feature
    Pool assignment tool
    /tools/pools (client-side snake/sequential/random seeding from paste roster, CSV/print/copy)
  • iter
    74
    feature
    Join OFC funnel
    /join (5-step onboarding with cross-links to clubs/fees/safeguarding/anti-doping/rankings)
  • iter
    73
    feature
    Training times
    /training (weekly grid across 9 club sessions with days/time/group)
  • iter
    72
    feature
    Equipment guide
    /equipment (3 weapons + 6-item FIE-standard safety kit + where-to-buy)
  • iter
    71
    feature
    Fees & licences
    /fees (6 tiers: athlete/club/coach/ref/event/hardship, transparent OMR pricing)
  • iter
    70
    feature
    Cookie notice
    /cookies (detailed table of 4 cookies + purpose + lifetime + type)
  • iter
    69
    feature
    National rankings
    /rankings (auto-computed by weapon from results, weighted score tables)
  • iter
    68
    feature
    Drill library
    /drills (6 drills across footwork/point/distance/parry/tactics/conditioning + submit-drill CTA)
  • iter
    67
    feature
    Camps & programmes
    /camps (6 tiers: U12/U15/U20/Sen/Ref/Coach + registration CTA)
  • iter
    66
    feature
    Referees & officials
    /referees licensed list + 4-step pathway + clinic CTA
  • iter
    65
    feature
    Coaches directory
    /coaches pulls coach-role team members + certification CTA
  • iter
    64
    feature
    Volunteer signup
    /volunteer (6 role cards, form with honeypot + checkbox interests + phone, POST to /api/contact, toast on success)
  • iter
    63
    feature
    Partners & sponsors
    /partners (4 tiers w/ accent gradients: governing/international/regional/academic, partnership CTA)
  • iter
    62
    feature
    Hall of Fame
    /hall-of-fame (weighted medal score by level+medal, top-24 cards w/ gold/silver/bronze, landmark firsts timeline)
  • iter
    61
    feature
    Accessibility statement
    /accessibility (WCAG 2.1 AA commitment, 10 features, 3 known limitations, testing/review process, feedback CTA)
  • iter
    60
    feature
    Terms of use
    /terms (10 EN+AR sections) + footer Privacy/Terms links in bottom bar
  • iter
    59
    feature
    Privacy policy
    /privacy (10 EN+AR sections: what, why, sharing, retention, rights, cookies, minors, security)
  • iter
    58
    feature
    Safeguarding policy
    /safeguarding (7 sections, standards of behaviour, screening, reporting, ROP 9999 callout, Safeguarding Officer CTA)
  • iter
    57
    feature
    Anti-doping page
    /anti-doping (WADA Code, testing, TUE, whereabouts, education, sanctions, confidential reporting CTA)
  • iter
    56
    feature
    Press kit
    /press (fact sheet, EN+AR boilerplate, 8 downloads inc RSS/ICS/JSON API, media contact)
  • iter
    55
    feature
    Public JSON API
    /api/public + /events + /news + /athletes (CORS *, 5-min cache) for press/ministry integration
  • iter
    54
    feature
    Events .ics calendar feed
    /events.ics VEVENTs with RFC 5545 folding, Asia/Muscat TZ, subscribe CTA on /events
  • iter
    53
    feature
    News RSS feed
    /news.rss.xml (40 items, enclosures for cover photos, stripped-md descriptions) + autodiscovery <link> in Base
  • iter
    52
    feature
    Elections module
    /elections mandate+countdown+eligibility+8-step timeline+secretariat CTA, bilingual, JSON-LD breadcrumb
  • iter
    51
    feature
    AGM minutes archive
    /agm (year-grouped completed meetings filtered by AGM/General Assembly keywords, empty-state + next-AGM call-out, cross-links bylaws/events)
  • iter
    50
    feature
    Constitution & bylaws
    public /bylaws page with 10 EN+AR articles, sticky ToC, JSON-LD breadcrumb, footer quick link
  • iter
    49
    feature
    Newsletter sign-up
    footer form posting to /api/newsletter/subscribe (honeypot, IP rate-limit, re-sub, /unsubscribe token) + /admin/newsletter list + CSV export
  • iter
    48
    feature
    Toast notifications
    window.toast(msg,kind) + auto-promote ?saved/?deleted/?error URL params into toast + scrub URL
  • iter
    47
    feature
    Keyboard shortcuts
    / search, ? help, Esc close, g+letter jump to Overview/Team/Meetings/Actions/Polls/Decisions/Updates/News/Events
  • iter
    46
    feature
    Site-wide search
    /search UI + /api/search across athletes/results/news/events/decisions/policies + / keyboard shortcut
  • iter
    45
    feature
    Public /decisions log
    ministry-transparency view of every board decision, year filter, outcome chips, cross-links governance + committee
  • BATCH 3 (10 parallel sub-agents)
    Time-box timer + Chair private notes + Action sub-tasks + Role delegation + Member-at-risk + Growth log + Training attendance + Injury tracker + Coach private notes + Athlete goals
  • iter
    34
    feature
    Public /org-chart page
    hierarchical board tree from team.json with Organization JSON-LD for SEO
  • iter
    33
    feature
    Board term tracker
    termStartsAt/EndsAt/Notes on TeamMember + Overview alert card when terms expire within 90d
  • iter
    32
    feature
    Action escalation 14d
    14+ days overdue fires WA+email to every admin + tags action as 'escalated'
  • iter
    31
    feature
    Athlete passport PDF
    GET /athletes/<slug>.passport — credit-card-sized ID card with photo, name EN+AR, weapon, born, ref, medals, QR
  • iter
    30
    feature
    Hybrid Zoom/Teams/Meet link
    videoUrl/videoProvider field + Join button on meeting page + WA reminder includes link
  • iter
    29
    feature
    Quorum checker ribbon
    green/amber/red status on scheduled meetings, defaults to majority of active board members
  • iter
    28
    feature
    Groups & sub-committees
    /board?t=sub-committees + 4 seed committees + admin CRUD
  • BATCH 2 (10 parallel sub-agents)
    Conflicts + Selections + Disciplinary + MoYC + Correspondence + Contracts + Letter generator + Insurance + Grants + Budget+Expenses · all merged + tabs wired
  • BATCH (10 parallel sub-agents)
    Decisions register + Agenda templates + Meeting RSVP + Read-receipts + AI board-chat + Discussions + Surveys + PWA + Governance hub + Motions
  • iter
    5
    feature
    Digital Board Packet PDF
    /admin/meetings/<id>.packet + buttons on both meeting views · plans/master.md + wave-a detail shipped
  • iter
    4
    feature
    eSignatures (Boardable parity #2)
    /admin/signature + /api/board/signature + draw canvas + stamp upload + signing event log
  • iter
    3
    feature
    Polls & async voting (board-committee Boardable parity)
    /board?t=polls + /api/board/poll + auto WA to eligible voters on create
  • iter
    2
    planning
    100 missing features catalogued in docs/100-missing-features.md · 9 clusters × 8-12 features each · loop will work through these
  • iter
    2
    feature
    Athlete Results & Medal Tracker
    /admin/results + /medals + Result type + medalLeaderboard
  • iter
    1
    security
    OWASP baseline headers (HSTS/CSP/XFO/XCTO/Referrer/Permissions) via sequence middleware · commit 9092e55
  • iter
    0
    cron 7,27,47 * * * *
    22-minute cadence · iteration 0 = kickoff

Sprint complete

150 iterations, one site. The next phase is whatever clubs, athletes, coaches and the board actually ask for. If something here is wrong or missing, tell us — [email protected].

Raw log also available as plain text on GitHub. Site source: fencing.om monorepo.