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
258
Log entries
253
Days
5
Features
117
feature · 117 api · 39 ux · 29 note · 7 maintenance · 6 seo · 6

Sat, 25 Apr 2026 · 2 entries

  • note
    LIVE — almajlis.om in production. Cloudflare zone 7c387f3ba373780002dbb9968fd44aa6 (active, paul/robin NS), DNS records: A almajlis.om→147.93.20.54 (proxied) + A *.almajlis.om→147.93.20.54 (wildcard, proxied) + CNAME www→apex (proxied). SSL: Flexible (CF→nginx HTTP). nginx vhost at /www/server/panel/vhost/nginx/almajlis.om.conf serving almajlis.om + www + *.almajlis.om → port 4100 (fencing-om PM2 app). Tenant 'almajlis' registered in data/tenants.json with plan:enterprise. DialogueME font family (8 WOFF2 weights) + logo SVGs (mark/horizontal/stacked) + brand.css from Claude Design deliverable now under public/. Verified: https://almajlis.om/api/tenant returns slug=almajlis, title 'AlMajlis — المجلس', brand assets 200, fencing.om unaffected (96/96 smoke green). Next: install CF Origin Cert + nginx 443 block to upgrade to Full(strict); then integrate Claude Design's brand-system.html as the actual marketing site (currently the AlMajlis tenant renders the same shell as default tenant); then Phase 8 (API keys + webhooks + Paymob billing + onboarding wizard)
  • note
    SHIPPED — The Majlis product brand. Product identity doc at docs/brand/the-majlis-identity.md (positioning, voice, color tokens, typography, copy samples, messaging pillars). Logo SVGs at public/brand/ (mark, wordmark, AR-wordmark, reversed, favicon). DEFAULT_BRANDING updated to neutral Majlis shell (teal-green #1e6b55, brass gold accent, 'The Majlis' name, themajlis.om primary domain). OFC explicitly preserves fencing.om identity via data/tenants/ofc/branding.json (green #1e8439, 'Oman Fencing Committee', /logo.png, fencing.om). Sport-specific modules (athletes + selections) now defaultEnabled:false — OFC re-enables via data/tenants/ofc/features.json. src/pages/index.astro stopped hardcoding OFC title. Verified: fencing.om prod title + theme-color unchanged; default tenant lands on 'The Majlis — المجلس' shell. 96/96 smoke green. Domains to register: themajlis.om (10 OMR, available at GulfCyberTech), themajlis.com, themajlis.app

Fri, 24 Apr 2026 · 20 entries

  • note
    SHIPPED — per-tenant branding. Task 4.1 src/lib/branding.ts with Branding interface + DEFAULT_BRANDING (byte-for-byte match of current OFC hardcoded values) + readBranding/setBranding + brandingToCssVars + validators. Task 4.2 Base.astro swaps every hardcoded brand ref (title/meta author/RSS titles/og:site_name/JSON-LD/favicon/theme-color/siteUrl/fontColor etc) for branding-derived values; CSS vars injected via <style is:inline set:html={cssVars}> before global.css. Task 4.3 Branding editor section in SettingsTab (Identity/Logo&Favicon/Colors/Typography) + /api/board/branding/update POST endpoint with admin gate + hex/URL validation + atomic withFileLock writes. Commit 383a301. Runtime verified: <title> still 'Oman Fencing Committee — اللجنة العمانية للمبارزة' and theme-color=#1e8439 with NO branding.json written (defaults active). 96/96 smoke green. Task 4.4 (nginx+certbot custom-domain automation) deferred to Phase 4b — primaryDomain + emailFrom fields ship as data shape now
  • note
    SHIPPED — role+permission matrix. Task 3.1+3.2 src/lib/roles.ts with hasPermission() + legacy fallback + createTenant seeds admin/coach/member (56de008). Task 3.4 permission editor UI (modules x actions checkbox grid) + /api/board/roles/{update,create,delete} endpoints with seeded-role protection + admin wildcard safety net (fde24db). 12/12 permission cases pass, 96/96 smoke green. Reviewer SHIP. Tasks 3.3 (wire hasPermission into every tab ~20 tabs) + 3.5 (user migration) deferred — OFC runs on legacy fallback with zero behavior change until Phase 3.3. Phase 2 polish shipped earlier (bc6629f): withFileLock on features.ts, AccountsTab superadmin guard, err flash on home, AR error translations
  • note
    SHIPPED — module registry + per-tenant feature flags. Task 2.1+2.2 MODULE_REGISTRY (28 modules after settings added) + features.ts store (4797a2a + e460917). Task 2.3 board.astro swaps 27-entry hardcoded MODULE_GROUPS for enabledGroupsForRole(me.role), VALID_TABS now registry-derived, badges routed through moduleBadges.ts (7a8ff64). Task 2.4 /board?t=settings toggle grid + /api/board/features/toggle endpoint with self-lockout protection for settings+overview slugs (8473052). 96/96 smoke green. Follow-ups: wrap features.ts writes in withFileLock, AccountsTab role guard on line 239 doesn't include superadmin, err flash on board home, AR translation of err messages in SettingsTab
  • note
    SHIPPED — multi-tenant unlock live. Task 1.1 tenant registry (67d8e85), 1.2 middleware+AsyncLocalStorage (658dac6), 1.3 33 stores refactored to tenantReadPath/tenantWritePath (70bf41f), 1.4 migration script + 5 files copied to data/tenants/ofc/ (c9c049f), 1.6 /admin/tenants superadmin page + scripts/set-superadmin.mjs (220c75c). Ali promoted to superadmin on prod. /api/tenant returns {slug:ofc} for fencing.om. 96/96 smoke green. Phase 1b follow-ups: tenant-scope content/ dir before onboarding tenant #2, tenant-scope public/uploads/board/, tenant-scope src/lib/referees.ts. Task 1.5 (retire legacy data/*.json) intentionally deferred for 7-day soak
  • note
    SHIPPED — Phase 0 of productize-board-workspace master plan complete. Task 0.1 SurveysTab wired (eba1294), Task 0.2 ActionsTab+ActionDrawer on canonical commentStore (f9b7501), Task 0.3 ApprovalsTab+ApprovalDrawer (b0c45f9), Task 0.4 AccountsTab full CRUD invite/disable/enable/revoke + disabled-user session block (75a1f7a). Final code review SHIP verdict. Known follow-ups: ApprovalsList row openers not wired to drawers, overdueAll dead code in board.astro, CSRF hardening pass due. 96/96 smoke green
  • iter
    258
    phase0
    Phase 0 Task 0.1 — wire orphaned SurveysTab into /board MODULE_GROUPS Governance group (after Polls
    efore Decisions); 385-line tab was dead code until this commit. Badge counts open surveys
  • iter
    257
    ux
    /board home cmd-K palette now actually works — previously the kbd hint was decorative because ModuleHeader (which owned the keydown handler) only mounted on tab views. AppGrid now ships a self-contained palette (search input
    rrow-nav, enter, esc, click-outside); keybind auto-detects Mac (⌘K) vs Win/Linux (Ctrl K) via userAgentData.platform fallback to navigator.platform — the kbd label flips on mount. Typing-aware (ignored inside INPUT/TEXTAREA/contenteditable)
  • iter
    256
    maintenance
    smoke.sh 90->96 endpoints — covers iter 248 (/api/public/stats/birthyears)
    ter 255 (/api/public/stats/weapons), iter 250/251 (?ageCategory=U20, Senior, csv). 96/96 green on prod
  • iter
    255
    api
    /api/public/stats/weapons per-weapon aggregate — 3 rows (foil/epee/sabre) with athletes count
    ender split, and gold/silver/bronze tallied from results store. Foil=7 (4M/3F), epee+sabre follow. First SSH deploy stalled on the Bash tool; fresh retry succeeded. Registered in feeds/openapi/developers
  • iter
    254
    ux
    FIE age-category mini-chip on /athletes grid cards (top-right
    harcoal with 90% opacity, same title a11y pattern as iter 252/253); renders only when birthYear is known. Verified 3 U20 card chips live
  • iter
    253
    ux
    FIE age-category chip on athlete detail page — inline next to the Born/Age line
    harcoal-pill theme matching iter 252 filter row, title='FIE age category {X}' for a11y. Verified U20 chip renders on /athletes/israa-al-siyabi
  • iter
    252
    ux
    FIE age-category filter pills on /athletes (All/U11/U13/U15/U17/U20/Senior); charcoal-theme to visually distinguish from the green discipline pills above; combines with discipline+category+club+search filters in client-side applyFilters; reset button re-initialises both. Verified pills + data-age='U20' on cards live
  • iter
    251
    api
    /athletes.csv gains birth_year + age_eoy + age_category columns + ?ageCategory filter (parity with iter 250 JSON). Filename suffix reflects filter (ofc-athletes-u20.csv). Verified 3 U20 rows returned with age 18/18/17 and correct category
  • iter
    250
    api
    MILESTONE 250 iters. ?ageCategory=U11|U13|U15|U17|U20|Senior filter on /api/public/athletes (case-insensitive exact match against the iter 249 ageCategory field; athletes without birthYear never match). Verified U20=3 (all 3 seeded)
    enior=0. /api/public index endpoint descriptor updated
  • iter
    249
    api
    enrich /api/public/athletes rows with birthYear + ageEoY + ageCategory (FIE end-of-year rule); extract to shared lib/ageCategory.ts (DRY with /api/public/stats/birthyears from iter 248). Athletes without birthYear get null values. Verified weapon=foil row → age 18
    ategory U20, birthYear 2008
  • iter
    248
    api
    /api/public/stats/birthyears athlete age distribution — FIE categories (U11/U13/U15/U17/U20/Senior
    nd-of-year rule), dense per-year array (gaps kept as count=0), filtered to active athletes with birthYear. 3 active athletes seeded with birthYear, all U20 (2008 x2 + 2009 x1). Registered in feeds/openapi/developers
  • iter
    247
    maintenance
    smoke.sh 85->94 endpoints — adds /api/public/stats/clubs (iter 245)
    events/upcoming.ics (iter 246), /leaderboard.csv + 3 leaderboard?weapon permutations (iter 244). Spot-verified all 3 new endpoints return 200 on prod
  • iter
    246
    api
    /events/upcoming.ics future-only iCalendar feed (RFC 5545 VCALENDAR; filters date<todayUTC; sorts asc; X-WR-CALNAME 'Oman Fencing — Upcoming'); registered in feeds.json + /developers. First prod call 500'd (pm2 warm-up per feedback_pm2_restart_502_warmup); 200 on retry with empty VCALENDAR body since all seeded events are past-dated
    orrect behaviour
  • iter
    245
    api
    /api/public/stats/clubs per-club athlete aggregates (total/active + foil/epee/sabre + male/female)
    orted by active count desc. Uses tolerant club-name normalisation (Quriyat→Quryat) + NFD weapon/gender match. Top club Salalah (3 active: 1 foil + 2 epee, all female). 8 clubs. Registered in feeds/openapi/developers
  • iter
    244
    api
    ?weapon=foil|epee|sabre|mixed filter on /api/public/leaderboard + /leaderboard.csv (case-insensitive
    uns through medalLeaderboard signature change). JSON echoes weapon field, CSV filename concatenates year+weapon (e.g. ofc-leaderboard-2025-epee.csv). Verified prod

Thu, 23 Apr 2026 · 78 entries

  • iter
    243
    api
    /leaderboard.csv medal-leaderboard download — columns match /api/public/leaderboard JSON (rank
    lug/id, names, discipline, gold/silver/bronze/total/weight). Optional ?year=YYYY. Filename becomes ofc-leaderboard-YYYY.csv when year is set. Registered in feeds.json + /developers. Verified header-only CSV against empty results store
  • iter
    242
    maintenance
    smoke.sh 79->85 endpoints covering iter 237 (/api/public/venues)
    ter 239 (/api/public/venues/oman-club), iter 240 (/api/public/gallery + ?limit=2), iter 241 (/api/public/leaderboard + ?year=2025 + ?limit=5). 85/85 green on prod (first run had transient 502s on athletes.csv/events.csv, cleared on retry)
  • iter
    241
    api
    /api/public/leaderboard medal aggregate — uses lib/results.medalLeaderboard (weighted 3/2/1)
    oins with athlete names/slug, optional ?year=YYYY date-prefix + ?limit=1-500 (default 200). Registered in feeds/openapi/developers. 200 count=0 (results store empty) behaves correctly
  • iter
    240
    api
    /api/public/gallery JSON endpoint — ordered by display order
    rc rewritten to absolute https://fencing.om URLs, ?limit=1-500 (default 200). Registered in feeds.json + openapi + /developers. Verified count=2 with ?limit=2
  • iter
    239
    api
    /api/public/venues/{slug} single-venue detail + venueSlug()+findVenueBySlug() helpers in lib/venues.ts; list endpoint now includes slug field so consumers can link to detail; openapi + /developers updated. Verified oman-club=200
    asirah-club=200, no-such=404
  • iter
    238
    maintenance
    /venues.csv download (shares lib/venues.ts with /venues + /api/public/venues; 9 columns incl derived mapUrl) + smoke.sh extended to 79 endpoints covering iter 232 (/athletes/{slug}/results.csv+.json)
    ter 234 (/api/public/coaches), iter 236 (/api/public/coaches/{id}), iter 237 (/api/public/venues + /venues.csv). 79/79 green on prod
  • iter
    237
    api
    /api/public/venues JSON endpoint — extracted VENUES from venues.astro into src/lib/venues.ts (shared source of truth); exposes 8 training venues with clubEn/Ar
    ity, address, parking, mapQ + derived mapUrl. Registered in /api/public index, feeds.json, openapi.json, /developers. HTML /venues still green
  • iter
    236
    api
    /api/public/coaches/{id} single-coach detail (parity with committee/{id} + clubs/{slug}); returns 404 when the id exists but is not a coach (role /coach|مدرب/). Verified tm_hisham_karshod=200
    m_khalid_shuaibi=404, nope-xyz=404. Added to openapi.json + /developers
  • iter
    235
    api
    register iter-234 /api/public/coaches in 4 registry surfaces — /api/public index (endpoints.coaches)
    api/public/feeds.json (added alongside coaches.csv), /api/openapi.json (path entry), /developers (table row); verified all 4 live
  • iter
    234
    api
    /api/public/coaches JSON mirrors the existing coaches.csv — filters team by /coach|مدرب/i
    trips 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
    a-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
    mpty roster returns header-only CSV / count=0 JSON
  • iter
    231
    ux
    global '/' keybind focuses first search input on any public page (GitHub/Notion style
    kips when typing in input/textarea/contenteditable); focuses+selects
  • iter
    230
    maintenance
    smoke.sh extended with 12 filter permutations from iters 225-229 (weapon
    lub, 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
    efault 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
    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
    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
    pple 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
    TTP 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
    ame 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
    tar/users/file-csv icons, EN+AR)
  • iter
    214
    ux
    client-side filter on /clubs index (name+city search
    FD-normalised, shown/total counter); mirrors iter-213 committee filter
  • iter
    213
    ux
    client-side filter input above /committee board grid (NFD-normalised
    ive shown/total count pill, empty-state msg, no network roundtrip)
  • iter
    212
    export
    /coaches.csv (head coach + certified coaches filtered by coach|مدرب role pattern
    O 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
    chema 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
    50 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
    urfaces structured siblings)
  • iter
    207
    api
    /api/public/now JSON sibling to /now page (eventsThisWeek + upcomingNext5 with inDays + recentNews 7d + recentResults 30d
    00s 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
    irrors /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
    0s 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
    nknown 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
    FD-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+/
    o /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
    inks 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
    utodiscovered 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
    h cache, registered in /api/public
  • iter
    195
    feed
    /news.atom.xml Atom 1.0 sibling to RSS (per-entry xml:lang
    roper 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
    00wpm 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
    hmed-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
    eturns 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
    FD-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
    ow 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
    arget 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
    N+AR)
  • iter
    182
    ux
    share button on /news/[slug] (navigator.share native dialog on mobile/PWA
    avigator.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
    N+AR, hover green-ofc)
  • iter
    177
    ux
    'Add to calendar' button on /events/[slug] surfaces iter-176 per-event ICS endpoint (calendar-plus icon
    ownload 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
    -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
    dge 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
    TL-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
    TL-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
    lug/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
    ameEn, 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
    itleEn/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
    itleEn/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
    lug, 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
    6/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
    o widgets don't chain 3 API calls; registered in /api/public index
  • iter
    162
    api+seo
    /api/public/upcoming (future events only
    orted 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
    d/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
    y design)
  • iter
    158
    fix
    /athletes/[slug] 500 — missing import pctLast30Days from lib/training
    aught 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
    edal, 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
    egistered in /api/public index
  • iter
    153
    governance
    /code-of-conduct bilingual 8-section page (who
    alues, 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
    ow-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
    laimWizard 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
    io+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
    rabic 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
    pprovals: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
    oduleHeader, 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
    ategory-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
    y.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
    ert renewal banner)
  • multi-role
    SecretaryCard live (club-edit + announcement submit
    rite-through on approve)
  • multi-role
    ParentCard live (per-child AthleteCards
    dd-child CTA)
  • multi-role
    AthleteCard live (medical direct
    mergency 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
    rouped by day, kind filters, sprint-complete hero) · SPRINT 150/150 COMPLETE
  • iter
    149
    feature
    Activity timeline — /activity (unified news/events/results/media timeline
    time buckets, kind filter chips)
  • iter
    148
    feature
    Homepage video embed — VideoFeature block after Gallery (click-to-load youtube-nocookie
    azy poster, subscribe CTA)
  • iter
    147
    feature
    Camp registration backend — /camps/register form + /api/camps/register POST (JSON store + coaching@ email
    camps seeded)
  • iter
    146
    feature
    Share-card generator — /tools/share-card (1200x630 social card
    ive 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
    ive 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
    ive 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)
    ype filter chips, CTA
  • iter
    139
    feature
    Penalty tracker tool — /tools/penalty-tracker (cards by fencer
    uto-escalate 2nd yellow, CSV export)
  • iter
    138
    feature
    Training-load tool — /tools/training-load (sRPE
    eekly load, ACWR, monotony, strain; localStorage, seed data)
  • iter
    137
    feature
    Door-check QR tool — /tools/door-check-qr (A4 printable badge
    ive 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
    uttons 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
    imeline chips, cross-links)
  • iter
    128
    feature
    Printable calendar — /calendar-print (A4 portrait
    ear-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
    moji icons, bilingual descriptions)
  • iter
    124
    feature
    Strength program — /strength (4 blocks: lower/specific/core/weapon-arm
    eekly schedule, safety caveats)
  • iter
    123
    feature
    Training log tool — /tools/training-log (weekly 7-day table: session/min/RPE/notes/mood + reflections
    4 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
    eat, etiquette, dress, photo, kids, Qs + quick-links)
  • iter
    119
    feature
    Scoring explainer — /scoring (6 sections: electric box
    arget, priority, bout length, cards, spectator shortcut)
  • iter
    118
    feature
    Match-day kit checklist — /tools/kit-check (28 items
    categories: weapons/armour/clothing/hydration/paperwork/extras, A4 portrait print)
  • iter
    117
    feature
    Fencing glossary — /glossary (20 EN+AR terms
    ive search filter, letter-jump chips, empty-state)
  • iter
    116
    feature
    Mental game — /mental-game (pre-bout routine
    ox 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
    phases, A4 portrait print)
  • iter
    112
    feature
    Tournament registration — /register (auto-populated event dropdown
    thlete/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
    ick-boxes, sign-line, A4 portrait)
  • iter
    110
    feature
    Achievements feed — /achievements (unified newest-first stream of milestones + medals grouped per event
    ross-links to history/HoF/results)
  • iter
    109
    feature
    Risk assessment template — /tools/risk-assessment (8 pre-filled hazards
    ×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
    nline editable, A4 print, pre-filled template)
  • iter
    107
    feature
    Public statistics dashboard — /statistics (8 headline cards + medal-by-type/weapon/year charts
    ive-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
    nline editable rows, A4 landscape print, pre-filled template) + Tools hub Ready
  • iter
    104
    feature
    Search filter pills — per-type result counts
    uto-dim empty types, updates on every query
  • iter
    103
    feature
    Club detail pages — /clubs/[slug] dynamic routes
    portsTeam JSON-LD, filtered athlete grid, quick-links to training/venues/join
  • iter
    102
    feature
    Events tag filter — dynamic pill row above card grid
    ilters by event.tag, empty-state, client-side
  • iter
    101
    feature
    Sponsor application — /partners/apply (5-tier pricing
    ull 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)
    educed-motion safe, RTL-aware
  • iter
    99
    feature
    Homepage testimonial — rotating bilingual pull-quote (President/HeadCoach/Board)
    ark 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
    xtracted 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
    ide-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
    /S/B + athletes + intl + score cols, medal-coloured position chips)
  • iter
    89
    feature
    Bout slip generator — /tools/bout-slip (printable DE match slip
    ed/green score boxes, card cols, signatures, 2 per A4) + promote Ready
  • iter
    88
    feature
    Results filters — year + medal + search across athlete/event/discipline
    ount indicator, reset button, auto-hide empty year blocks
  • iter
    87
    feature
    Venue directory — /venues (8 club venues
    N+AR address, parking, Google/Waze/Apple maps links)
  • iter
    86
    feature
    Homepage partners strip — OOC/MCSY/FIE/FCA tiles above footer
    ilingual, links out + to /partners
  • iter
    85
    feature
    Sitemap index split — /sitemap.xml is now sitemapindex with 4 child sitemaps (pages/news/events/athletes)
    8 pages + dynamic entries, hreflang alternates
  • iter
    84
    feature
    Homepage next-event countdown — live days/hrs/min/sec to soonest upcoming event
    ilingual, calendar CTAs
  • iter
    83
    feature
    Advanced athlete filters — /athletes category + club selectors + name search
    atch count, reset button, empty-state
  • iter
    82
    feature
    News archive filters — /news year pill filters + EN/AR search
    ount indicator, empty-state reset link
  • iter
    81
    feature
    FAQ rich search — live client-side EN+AR filter
    atch 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
    rintable cards, CSV template) + promote Ready
  • iter
    79
    feature
    Poule sheet generator — /tools/poule-sheet (NxN printable grid
    anonical 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
    ed/green scoreboard, 10s chime, kbd shortcuts) + promote Ready on hub
  • iter
    77
    feature
    Tools hub — /tools (pool + bracket ready
    'soon' tools placeholders, tool-request CTA)
  • iter
    76
    feature
    DE bracket generator — /tools/bracket (cross-seed order
    uto 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
    SV/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
    ransparent 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
    eighted 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
    orm 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
    artnership CTA)
  • iter
    62
    feature
    Hall of Fame — /hall-of-fame (weighted medal score by level+medal
    op-24 cards w/ gold/silver/bronze, landmark firsts timeline)
  • iter
    61
    feature
    Accessibility statement — /accessibility (WCAG 2.1 AA commitment
    0 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
    hy, sharing, retention, rights, cookies, minors, security)
  • iter
    58
    feature
    Safeguarding policy — /safeguarding (7 sections
    tandards of behaviour, screening, reporting, ROP 9999 callout, Safeguarding Officer CTA)
  • iter
    57
    feature
    Anti-doping page — /anti-doping (WADA Code
    esting, TUE, whereabouts, education, sanctions, confidential reporting CTA)
  • iter
    56
    feature
    Press kit — /press (fact sheet
    N+AR boilerplate, 8 downloads inc RSS/ICS/JSON API, media contact)
  • iter
    55
    feature
    Public JSON API — /api/public + /events + /news + /athletes (CORS *
    -min cache) for press/ministry integration
  • iter
    54
    feature
    Events .ics calendar feed — /events.ics VEVENTs with RFC 5545 folding
    sia/Muscat TZ, subscribe CTA on /events
  • iter
    53
    feature
    News RSS feed — /news.rss.xml (40 items
    nclosures for cover photos, stripped-md descriptions) + autodiscovery <link> in Base
  • iter
    52
    feature
    Elections module — /elections mandate+countdown+eligibility+8-step timeline+secretariat CTA
    ilingual, JSON-LD breadcrumb
  • iter
    51
    feature
    AGM minutes archive — /agm (year-grouped completed meetings filtered by AGM/General Assembly keywords
    mpty-state + next-AGM call-out, cross-links bylaws/events)
  • iter
    50
    feature
    Constitution & bylaws — public /bylaws page with 10 EN+AR articles
    ticky ToC, JSON-LD breadcrumb, footer quick link
  • iter
    49
    feature
    Newsletter sign-up — footer form posting to /api/newsletter/subscribe (honeypot
    P 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
    ear 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
    ame 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
    efaults 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, contact form.

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