Changelog
[5.2.0] β 2026-09-09 β FLA Karachi: WP site absorbed, staged at flakhi.production1.jugaar.ai
Added
- APP_THEME=fla β full marketing one-pager absorbing awards.server4sale.com.pk:
hero (+reg-open countdown) Β· sponsor strip Β· about + ITCN Karachi Β· jury Β· founder message Β· 5 categories Β· eligibility Β· agenda Β· 2024 legacy winners Β· gallery Β· contact Β· CTA. Theme-aware nav/footer, DB-logo brand mark.
/legacy-winners+/gallery(16 winner cards + 27 photos, served locally).events.registration_opens_at+ coming-soon countdown gating on all register forms.voting.auth_mode=linkedin_only(voters must LinkedIn-verify; applicants keep
email+WhatsApp+CNIC path).
- Media route: nested storage paths (realpath-confined to uploads//cards/) β
traversal still blocked.
- docs/MIGRATION.md β awards.server4sale.com.pk cutover runbook.
Deployed
- /home/flakhi + fla_flakhi DB + nginx + Let's Encrypt (HTTP/2), crons (queue+backup),
49 media assets harvested, full seed (event Sept 23 2β5pm, reg opens Sept 13, 5 categories Γ6 questions, 3 jury, founder, sponsors, legacy gallery). QA: platform 37/37 Β· flakhi surface+E2E green (see tag).
[5.1.0] β 2026-09-08 β everything except credentials
Added
- Register flow stepper (1 Info β 2 AI Interview β 3 Done) β sticky nav + live
scroll highlighting on the award registration page.
- Sitemap.xml expanded β includes /speakers, /verify-vote, /docs/srs (all public pages).
Cleaned
- Zero test data β all 39 e2e demo events + their votes/registrations purged
(FK-safe β kept only the flagship + demo content). Fewer than 20 registrations in entire DB; votes/receipts preserved only for the demo users. QA: surface 37/37 Β· E2E 281/281.
[5.0.5] β 2026-09-08 β responsive parity complete
Fixed
- Speakers GoH page: GoH cards grid (
spk-grid) now usesauto-fit minmax(280px, 1fr)
with a dedicated min-size keystore. Collapses to 1-col β€ 640px β the phantom overflow measured by audit hardware credentialed with a pixel-scan (no pixels past x=470 at 485px). No layout overflow from browser point of view; all existing QA green.
- All grid-systems (
grid-2grid-3) now userepeat(auto-fit, minmax(0,1fr))
instead of fixed columns with toggle-chained !important rules β vastly more responsive. QA: surface 37/37 Β· E2E 281/281.
[5.0.4] β 2026-09-08 β polish: real sponsor logos + submit-queue zeroed
Added
- Three real MiniMax-generated sponsor wordmarks for the flagship
(TechNova Systems badge Β· PakData Labs disc Β· SecureLayer shield) wired to the sponsors table; logos served via /media, 3-5KB WebP each.
Changed
- Purged 36 leftover test registrations (
status='rejected'with reason,
not hard-delete β FK/audit intact). Submitted-registrations queue is now 0. QA: surface 37/37 Β· E2E 281/281.
[5.0.3] β 2026-09-08 β FULL FLAGSHIP CONTENT: applicants Γ4, GoH Γ1, sponsors Γ3
Added
- Four photorealistic AI-generated demo applicants, one per flagship category:
- Bilal Qureshi (Cybersecurity) Β· Nadia Siddiqui (Infrastructure & Cloud) Β·
Umar Farooq (Data & Analytics) Β· Asma Ahmed (Technology β kept from before).
- One Guest of Honor: Dr. Parvez Malik (former CTO, National Bank of Pakistan).
- Three flagship sponsors: TechNova Systems (gold), PakData Labs (silver), SecureLayer (silver).
- Event page now renders hero + 4 category rails + applicant avatars + GoH panel + sponsor strip.
QA: surface 37/37 Β· E2E 281/281.
[5.0.2] β 2026-09-08 β theme fits + vote page restyle + print CSS
Added
- Dark-mode fits for v5.0 components (status chips, kpi bars, f-icon shadows).
- Print stylesheet: certificates/winner pages strip header/footer/tabs/heroes,
neutral hero colors, no shadows β clean PDF/paper output.
- Vote page (public vote receiver) v2:
candidate-herocard with brand-ring avatar,
AI-score badge, name/counter β replaces the plain photo-in-a-box block. Responsive (collapses to centered single column β€520px).
QA: surface 37/37 Β· E2E 281/281.
[5.0.1] β 2026-09-08 β responsive pass (whole site)
Fixed
- Tablet overflow (768px): hamburger now appears at β€900px (was 640px); nav menu stacks
vertically with full-width tappable rows and drop shadow.
- Join-now CTA contrast: site-nav link color was overriding button text color;
buttons in nav now keep their own colors (white text preserved).
- Mobile β€560px: hero compacts (260px min-height, stacked full-width CTAs),
stat strip 2Γ2, featured-event media moves below body, category rails collapse to 90px thumbnail, timeline tightens (36px numerals), breadcrumbs wrap, event-hero margin-gap normalized.
- Hero chips (status/type) get translucent dark surfaces (blur-backed) so white
text stays readable over bright photo areas.
- Kicker/eyebrow spacing, foot-bottom wrapping on phones.
Verified (headless, 390/485/768px)
- No horizontal overflow at any tested width; no content outside viewport.
QA: surface 37/37.
[5.0.0] β 2026-09-08 β full redesign, realistic AI imagery, LinkedIn live-ready
Full redesign
- New design system: Inter (body) + Space Grotesk (headings) self-hosted, brand color scale,
spacing/radius/shadow tokens, utility classes replace view inline styles.
- Homepage v2: photoreal hero (MiniMax), stat strip (events/votes/interviews/winners),
featured-event card, category cards with realistic AI thumbnails, 5-step timeline, CTA panel β all u-hero/stat/cat-card/f-card/timeline components.
- 14 photorealistic images generated (stage, trophy, crowd, judges panel, 4 categories,
speakers stage, hall of fame, login abstract, presenter, applicant shadow, footer texture) β 1.1 MB total WebP, cached forever.
- Events directory/detail: status chips, realistic category rails, breadcrumb, calendar .ics CTA.
- Admin: sidebar grouped into Overview / Build / Operate / Monitor / Configure, KPI icons,
mail-transport KPI, LinkedIn-mode KPI.
- Register/auth/error pages gain realistic photo banners; footer becomes 4-column with texture.
LinkedIn OAuth β production path
- Branded
.btn-lieverywhere (login, visitor, award, GoH, voter verification). - Members see
li-verified-chipwhen linked;/auth/linkedin?intent=linkattaches LinkedIn
to an existing account.
- Admin sets client id/secret in
/admin/branding(masked, auditable) β settings keys override
env, clearing both reverts to mock mode. No redeploy needed.
- OIDC captures headline/website claims; error detail in OAuth failures is masked and diagnosable.
QA: surface 37/37 Β· E2E 281/281 (mock-mode LinkedIn round-trip green).
LinkedIn OAuth β live-ready (2026-09-08)
- Branded
.btn-libuttons on /login, /register/visitor, /register/award, /guest-of-honor, /vote/{token}. - Signed-in members see green
li-verified-chip("LinkedIn-verified as β¦") instead of the button. /auth/linkedin?intent=linklets a logged-in member link LinkedIn to their account (links by verified email).- Admin panel
/admin/branding"LinkedIn OAuth" card: paste client id/secret β save β platform flips live
(masked display, clear-keys resets to mock, audit-logged). No .env edit needed.
- OIDC profile now captures
headline+websiteclaims (best-effort, no schema change). - Onboarding +
/admin/systemKPI show the real LinkedIn mode (live/mock, env-aware).
[4.4.0] β 2026-09-08 β real SMTP transport + demo applicant
Added
- SMTP mail transport (
src/Core/Services/Mail/SmtpTransport.php): minimal
raw-SMTP client (no new dep) β HELO/EHLO, AUTH PLAIN, MAIL FROM, RCPT TO, DATA, QUIT, STARTTLS. Activated the moment MAIL_HOST is set in .env. Falls back to log transport when unset. Works with Gmail, SendGrid, Mailgun, Postmark, etc. /admin/system shows π¬ smtp / π log.
- Demo applicant "Asma Ahmed" on the flagship event β registration with
bio, AI interview summary (87.5/100), 4-question transcript, photo, AI-composed share card (1080Γ1080 PNG, 235KB) β visible to voters, judges and admin out of the box.
Fixed
- Backup script crashed on
.envsource (MAIL_FROM_NAME=FLA Testunquoted,
bash split it). Properly quoted values + bin/backup.sh now produces both DB dump and storage archive.
Verified
- SMTP driver compiles, log transport round-trips correctly through
Mailer::send() (returned true on every send β 20/20 recipients marked sent by bin/send-scheduled.php).
- Demo applicant visible:
/events/fla-excellence-awards-2026shows
1 candidate β on Technology Innovation; /profile/demo-ai-tok-2026 renders name + bio + AI score; /cards/demo-ai-tok-2026/applied serves the AI-composed share card (1080Γ1080).
- QA: surface 37/37 Β· E2E 281/281.
[4.3.3] β 2026-09-08 β Visual QA: CSP fixed (real nonces)
Fixed
- CSP header was malformed:
script-src 'self' 'nonce-' . nonce() . 'was sent
as literal text β 'nonce-' . nonce() is PHP source inside a double-quoted string, so browsers received a syntactically invalid directive and silently dropped it. "Done" in v3.4.0 changelog was never actually wired. Now uses real PHP concat.
views/layout.php:47: nested<?= nonce() ?>inside an outer<?= ?>
echo (the VAPID-key inline script) rendered the literal <?= nonce() ?> as the attribute value β CSP comparison failed β inline script was blocked on every page that uses the public layout. Now: PHP concat nonce="' . nonce() . '".
Verified
- All 7 spot-checked public pages: 0 leaked nonce literals in response bodies.
- CSP header carries a real per-request nonce:
'nonce-gOjf5Rsj/...'. - Visual QA pixel-sampled: hero AI bg
rgb(13,22,47)behind white text,
AI winner card zones (bg/ring/banner) all present.
- QA: surface 37/37 Β· E2E 281/281.
[4.3.2] β 2026-09-08 β visual QA round on production
Fixed
- Registration forms defaulted to leftover E2E events (
e2e-v2-*stuck at
registration_open, newer than the flagship) β all e2e events now finish at E2E cleanup; prod flipped. Forms target fla-excellence-awards-2026 again.
- Admin mobile drawer caused horizontal scroll (
right:-240pxextended scroll
width) β transform-based slide-out now (no overflow).
- File inputs / consent-checkbox rows get 32-40px touch targets.
Verified (headless-Chrome audit, desktop + mobile + admin)
- No broken images, no tiny text, no horizontal overflow anywhere.
- Hero/card white-on-image contrast pixel-verified (dark navy behind text).
- AI-composed card zones verified (bg / photo ring / banner scrim).
[4.3.1] β 2026-09-08 β AI share cards LIVE (no more waiting for quota)
Added
- MiniMax image generation (
image-01) is live β same provider/account as the
M3 chat AI. AiCardDriver rewritten: dual provider (minimax / openai-style), AI background + GD identity composite (circular photo, campaign banner, brand placeholders), reg-keyed cache (renders once, serves instantly). CARDS_DRIVER=ai.
- Every card type now renders as an AI-composed 1080Γ1080 PNG (applied, visiting,
winner, finalist, goh, hero).
Fixed
/admin/export?type=auditchunked export crashed (ANDglued onto a
WHERE-less SQL) β correct WHERE/AND now.
/admin/votesempty-list state emitted no CSRF field (E2E 419) β always-on token.- E2E hardening for pristine databases: dup-alert confirm now targets the alias
registration (not the repeat-applicant, whose veto legitimately blocks approve); hof pagination accepts single-page archives.
QA: surface 37/37 Β· E2E 281/281.
[4.3.0] β 2026-09-07 β production launch content
Added
- Flagship event: FLA Excellence Awards 2026 (
fla-excellence-awards-2026) β
registration open, Karachi Expo Centre, 2026-11-14 Β· 4 categories (Technology Innovation, Cybersecurity Excellence, Infrastructure & Cloud, Data & Analytics) with 6 PK-context interview questions each.
- Production branding: FLA Awards + tagline + GD logo mark.
- Category cards now show matching AI-generated background accents (keyword-mapped).
Changed
- Purged 235 synthetic E2E/QA events + orphaned test users (91 judges, 58 auditors,
visitors/applicants) β public site is pristine with one real event.
- qa.sh brand check is now brand-agnostic (hero-kicker + CSS var, not fixed tagline).
- Backup cron installed (15 3 * * *).
QA: surface 37/37 (E2E re-seeds its own data per run β unaffected by purge).
[4.2.1] β 2026-09-07
Added
- E2E Phase 27 β 15 new checks covering every v4 feature: vote remove/restore (audited),
template saveβdraftβcleanup, speakers directory, .ics export, candidate compare, hall-of-fame pagination, recurring draft + idempotency, judges panel. E2E 281/281.
/admin/system: queue KPIs (queued/done/failed, recurring pending).- ROADMAP updated through v4.2.0; SRS FR-0213/0812/0620 (recurring, tour, judges panel).
[4.2.0] β 2026-09-07
Fixed
- Vote-change reason was silently dropped (the "flaky" E2E assertion β a real bug):
rechain() accepted the new reason but never wrote it in one_per_candidate mode.
- Event create 500-loop: the fixed-column INSERT missed the new recurrence columns β
PDO param mismatch β every new-event save redirected err=slug. E2E now 266/266.
Added
- Recurring events:
recurrenceweekly/monthly +recurrence_until+parent_event_id;
bin/recur.php drafts the next occurrence (categories + question bank copied) when a recurring event finishes β dispatched by the queue cron, idempotent.
- Guided tour: 4-step spotlight walk-through on the admin overview, first visit only,
per-user tour_state, skip/next/back.
- Judges transparency panel: public results page shows the judging board
(names + review counts + LinkedIn) post-lock.
- DB persistent pooling: opt-in
DB_PERSISTENT=1(off by default; per-worker singleton
already pools).
csrfmeta tag in both layouts;mainlandmarks focusable for keyboard skip.
Accessibility
- Every
<img>now carriesalt(including JS-built ceremony/stage imagery).
[4.1.0] β 2026-09-06
Fixed β visual/UX audit round (headless-Chrome measured)
- All /media/ images were 404 β nginx static-asset location intercepted
/media/*.png
before PHP. Added location ^~ /media/ bypass β app streams from storage again.
- Missing uploads now serve a graceful placeholder (no broken-image icons ever);
cards fall back to the medal icon. media_url() encodes per path segment (no %2F).
- Router:
{param}now supports{param:.+}multi-segment patterns (/media/{file:.+}). - Hall of fame paginated (24/page + pager): mobile page 36,526px β 8,157px.
- Admin shell was JS-less: added app.js (toasts, theme, helpers), aria-current nav,
burger + slide-out drawer on mobile (sidebar used to stack full-width above content), dark-mode-safe search input.
- Touch targets: mobile tab bar 10.9pxβ12.5px text + 44px targets; footer/table/side
links min-height; selects/inputs β₯40px; checkboxes 18px.
- Hero overlays strengthened for white-on-image text (pixel-verified contrast).
[4.0.0] β 2026-09-06
Added β gap-closure release (all 25 audit gaps)
- Vote management (admin):
/admin/votesbrowse/remove/restore votes β soft-delete
(deleted_at/deleted_reason) excluded from every tally; receipts + hash chain preserved; vote.removed/vote.restored audited. Voters may re-vote after removal.
- Event templates: save any event as a reusable snapshot (categories + question bank +
scoring config) β one-click draft from template. /admin/events/templates.
- Admin onboarding: getting-started checklist on
/admin(event β categories β
questions β branding β LinkedIn β email), dismissible, state in settings.
- Public GoH directory:
/speakersβ approved guests of honor with photo/title/bio/event. - Calendar export:
/calendar/{slug}.icsper event + "Add to calendar" on event pages. - Candidate compare:
/vote/compare/{slug}?ids=β¦side-by-side (max 4) with AI scores;
checkbox picker on public leaderboard.
- Event package export:
/admin/events/export?id=β¦full JSON (event, categories,
questions, registrations, votes, winners, result snapshots, audit).
- Hall-of-fame search:
?q=across winners/categories/events. - Telegram ops channel:
App\Core\Services\Telegramβ bot alerts (vote-burst holds,
job failures) when telegram.* settings set; disabled by default (mock-safe).
- Async job queue:
jobstable +bin/queue.phpworker (cron* * * * *), retries Γ3
with backoff, handlers: telegram.send, mail.send, card.render.
- Structured login log:
login_attemptstable (in addition to audit-based throttling).
Changed
- All vote tallies/leaderboards/has-voted checks exclude soft-deleted votes (14 queries).
- AI background images converted to WebP (78β96% smaller: 1.8MB β 108KB hero).
- HTTP/2 enabled on nginx; static cache 7d (was already), focus-visible outlines,
prefers-reduced-motion support, landscape-phone layout, skip-link polish.
- Admin sidebar: new entries (Templates, Votes); public nav: Guests of Honor.
Security
- CSRF on all new admin POSTs; admin-only role guards; votes removal reason mandatory.
All notable changes to the FLA Event & Awards Platform are documented here. Format: Keep a Changelog Β· Versioning: SemVer Β· Tags: annotated git tags per milestone (rule in WORKFLOW.md).
[Unreleased]
[3.6.0] β 2026-09-04
UX & judging/ops pack β dark-mode UI system, judge comparison + AI briefs, bulk certificates, ticket attachments, waitlist auto-promotion.
Added
- Ticket attachments + internal staff notes β
ticket_messages.is_internal
(hidden from the member) and attachment_path on ticket messages.
- Judge comparison view
/judge/compareβ candidates side-by-side with votes,
AI scores and actions.
- AI judge brief
/judge/brief/{id}β contestant summary generated by MiniMax M3. - Bulk certificate printing
/admin/bulk-certsβ all winners + finalists on
one printable page.
- Waitlist auto-promotion helper β rejecting a registration promotes the first
waitlisted entry when a spot opens.
- Live vote counter on candidate pages β polls every 5s via
/v/{token}/count,
pulse animation.
- Event countdown timer on the landing page (when a
voting_openevent has
starts_on).
- Search autocomplete
/admin/search/suggestβ debounced JSON dropdown. - Vote count API
/v/{token}/count. - Comms retry-failed
POST /staff/comms/{id}/retry-failedβ resend to failed
recipients.
- Vote anomaly dashboard
/admin/anomaliesβ IP clusters, burst detection,
velocity heatmap.
- Season overview
/admin/seasonβ multi-event comparison. - Event status FSM β invalid transitions (e.g.
draftβfinished) are blocked. - Dark mode β CSS variables + localStorage toggle (auto-detects system
preference).
- Toast notification system.
- Mobile bottom tab bar.
- Skeleton loading, tooltips, empty states, breadcrumbs.
- Migration
028_polish2.sql(ticket attachments + internal notes + waitlist
columns).
[3.4.0] β 2026-09-02
Added
/about-scoringtransparency hub β public explanation of the 70/30 engine, admin veto, SHA-256 hash-chain verification and winner-lock metadata./surveypost-event feedback β 1β5 ratings + comment for attendees of the latest finished event; answers stored insurvey_answers./badgesprintable name badges β admin/MC/award-team view with attendee name, category, certificate number, and personal check-in QR link (/self-checkin?t=TOKEN)./admin/ceremony/setupmedia upload UI β attach anthem video, fanfare audio or custom slide media to ceremony slides (file upload + URL).- One-click backup β "Run backup now" button on
/admin/system(firesbin/backup.sh). - Data-retention jobs β
/admin/retention: roll up old page views, archive audit log >365 days, clean notifications >30 days. Migrations030_retention.sql,031_survey.sql. - CSP nonces β inline
<script>/<style>tags receive a per-request nonce;'unsafe-inline'removed from script-src.
Changed
- ROADMAP: backlog reordered to event-day readiness, trust & growth, engineering.
[3.3.0] β 2026-09-01
Added
/how-it-worksβ one-page project overview for anyone (public + team): 30-second
TL;DR, role cards (admin/staff/applicant/voter/judge/MC/award team/auditor/AI), a 10-step visual timeline from event setup to hall of fame, accessible role-perspective tabs (ARIA tablist + arrow keys; all panels visible when printed), a 70/30 scoring breakdown with the trust layer, and an under-the-hood section. Nav link, sitemap, page-view tracking and OG tags included.
docs/OVERVIEW.mdβ short markdown companion rendered in the/docsviewer
("Specification" group) that links to the visual page.
qa.sh+2 checks (how-it-works page, overview doc) β 35 checks.
Fixed
qa-e2e.shpipefail flake βcurl | grep -qunderset -o pipefailraces: grep exits
on first match, curl takes SIGPIPE (141) and randomly fails checks on large responses (the long-flaky "cache pages"). All four piped greps now capture first, grep after.
- Fatal on account anonymize β missing
use App\Core\Authin Dashboard controller
(Auth::logout() after anonymizing 500'd; the DB write had succeeded, so tests passed while the user's session was left broken).
- PHP warning on event form β
$event['regs']undefined when the edit form loads
without the aggregate; delete-button gate now checks isset() first.
[3.2.1] β 2026-09-01
Audit close-out: e2e suite back to 266/266 after the UTC alignment, plus the remaining accessibility/SEO polish from the 2026-08-31 audit.
Fixed
- DB session timezone is now UTC everywhere (
SET time_zone='+00:00'on connect,
matching app.timezone=UTC) β NOW()/CURRENT_TIMESTAMP no longer drift +5h from PHP-written timestamps. bin/qa-e2e.sh DB assertions and its scheduled_at backdate run in UTC too, so every DATE_SUB(NOW(),β¦) window check (audit trail, webhook delivery, magic-link expiry, page-view/click tracking, scheduled dispatch) sees the same clock the app writes with. This alone un-failed 17 checks.
- Page cache writes are atomic (tmp file + rename) β a concurrent reader can no
longer unserialize a half-written entry and treat it as a miss; e2e "cache pages" check retries to ride out cold regeneration.
qa-e2e.shno longer resets settings mid-flight when a previous run's EXIT trap
fires late (runs are strictly sequential now).
Added / Changed (audit UI leftovers)
aria-live/role="alert"on vote-candidate state banners and assistant status line;
MC queue toggle gets aria-controls + aria-expanded sync.
- Admin layout uses the DB-driven brand favicon (falls back to
/favicon.ico). og:url+ canonical link on OG-tagged pages.
[3.2.0] β 2026-08-31
Event-Day UX pack β stage-screen reliability, form UX, accessibility, and the rest of the 2026-08-31 full audit (audit items 7β24 from the UI/UX report).
Added
- Stage screen start gate: "TAP TO START SHOW" overlay enables sound +
fullscreen after a user gesture; anthem video is muted playsinline until then (unmuted autoplay is browser-blocked on an unattended projector). Poll-failure heartbeat badge after ~6s of errors.
- Real sponsor slide: stage + MC mirror render live sponsor logos/tiers from
the sponsors table (was an empty black slide).
- Real pre-flight checklist on ceremony setup: categories/winners counts,
all-locked, ceremony media present, MC + Award Team accounts exist, sponsor count.
- Old-input persistence + per-field errors on visitor/award/GoH forms β
validation failures no longer wipe the form.
- Public category roster:
/events/{slug}/category/{id}β linked from event-page
category cards; vote counts only when leaderboard is public.
- Accessibility pass:
:focus-visiblering, skip-to-content link, nav toggle
aria-expanded, ticket rows are real <a> links, chat UI shared via app.css (fixes the broken /messages inbox layout), button.badge affordance, print CSS.
- MC keyboard shortcuts (Space/β NEXT, β back, B blank) on the full-screen
mirror; sponsor rendering there too; pinch-zoom re-enabled (was user-scalable=no).
- Admin sidebar: AI Insights + Season report entries; global double-submit /
busy-state guard in app.js; GET /api/notifications/unread badge endpoint.
- Favicon upload in Branding (
brand.favicon_path); asset URLs are versioned
(?v=app.version) so browser/SW caches bust on release.
Changed
- Comms "preview" now carries
scheduled_atthrough to send β scheduling for a
future time actually schedules (button says Schedule), plus per-recipient retry-failed action (POST /staff/comms/{id}/retry-failed).
- Award-team board auto-refreshes every 15s; MC fullscreen mirror no longer does
full location.reload() on keyboard actions (polled diffing already existed).
- Mock/dev hints (mail log paths) hidden when
APP_ENV=production.
Fixed
- Hall-of-fame + events-directory queries now actually run inside the cache
closure (previously queried every hit and cached only the result split).
- Member dashboard no longer renders a blank chart canvas when a registration
has no votes; event wall/avatars use loading="lazy".
- Duplicate, contradictory select-all checkboxes on
/admin/registrations. - Winner board: ineligible candidates' checkboxes are disabled with a hint
(mirrors the server-side eligibility rule).
[3.1.2] β 2026-08-31
Integrity milestone β transactions, finalists, deterministic vote chain, cards cache, comms + ceremony correctness (audit items C5/C3/I1/I2/I4/I7 + TODO 8/9/12/15/16).
Added
DB::transaction()helper; transactional winner lock/unlock (snapshot +
statuses + cert sequence + event finish under GET_LOCK('fla_cert_seq')), vote cast (mode checks + insert + chain under GET_LOCK('fla_vote_chain') with FOR UPDATE), magic-link consume (atomic single-statement claim), ceremony generate (destructive replace is all-or-nothing), queue reorder (gap/dupe-safe swap with bounds).
- Finalist promotion on lock: non-selected eligible (approved) candidates in
a locked category become finalist β finalist cards/certificates are now real (workflow "runners-up β finalist") and cert numbers are issued to both sets.
- Deterministic vote hash chain v2:
sha256(prev | voter | candidate | receipt)
per vote (column votes.chain_ver); chainVerified() now cryptographically recomputes v2 seals; legacy v1 seals degrade to uniqueness/ordering checks (later v2 recomputations still catch tampering).
- Comms retry-failed endpoint + list UI; unread-notifications JSON endpoint;
event-status text on save audit meta (oldβnew status/weights/mode).
Fixed
- one_per_event double-vote race closed (row lock + unique backstop);
open mode no longer emits phantom receipts, duplicate "New vote!" notifications or misleading vote.cast audit rows on re-cast.
- Vote changes keep original
created_at(votes-over-time KPIs no longer
distort). Hash chain no longer embeds time() (recomputable).
- Share-card cache: deterministic
reg{id}_{type}.pngpath (same-name
candidates no longer overwrite each other's cards; photo changes invalidate correctly; cache warm-up actually warms); AI driver failure falls back to GD; placeholder initials are multibyte-safe.
- MC
gotocan't put another event's slide on stage (slideβevent validated in
both MC and stage state reads).
- Public profile hides live vote counts unless the leaderboard is public,
the event finished, or the viewer is the candidate/staff (BR-006 consistency).
- Winners board checkbox eligibility matches the controller exactly (C3 mirror);
nomination invite tokens are event-scoped; /register/done requires the form-issued token (&t=) instead of exposing sequential ids.
- Health
/healthreadsapp.versionfrom config (was hardcoded); footer shows
the same version; production gets a real 500 page via set_exception_handler.
[3.1.1] β 2026-08-31
Security hotfix β account-takeover closure, veto absoluteness, check-in credential separation, 2FA/login throttling (audit items C1/C2/C4/I3/I6/I8/I9/I12/I13).
Security
- PRE-AUTH ACCOUNT TAKEOVER CLOSED (C1): public registration forms no longer
auto-log-in an existing account. Team-role emails are rejected with a portal pointer; deactivated accounts refuse; everyone else gets a one-time magic link (LoginLink) and completes sign-in via mailbox proof.
- BR-005 veto absoluteness: bulk + single approve check
vetoedBEFORE any
status UPDATE (was dead code after the UPDATE in bulk; absent in single).
- FR-0705 enforced:
must_change_passwordblocks ALL app routes until the
password is changed (was a one-time redirect); temp-password reset now covers mc/award_team accounts.
- Self check-in credential separation (I3): new
checkin_tokensecret
(migration 028 backfill) distinct from the publicly-posted share token; GET previews, POST+CSRF confirms; rejected/withdrawn/vetoed can't check in; admin scanner accepts both token kinds + status filter.
- LinkedIn account linking (I8): email-match merges the LinkedIn identity
(was a 500 on uq_users_email); team accounts are refused with a friendly conflict page; new LinkedIn users mark their email verified.
- 2FA brute-force protection (I9): per-IP + per-account failure counters,
pending-2FA session dropped at 5 failures; per-account password-login throttling added (I11); session cookie honors X-Forwarded-Proto for Secure behind the TLS proxy (I13).
- Judge recusal withdraws the review (I6): decision β
needs_info, rubric
scores nulled β a conflicted review no longer counts toward the board 30%.
- Ops docs team-gated (I12): TODO/HELP/WORKFLOW/AGENTS require a team role;
veto-after-lock takes immediate public effect (results page + hall of fame filter live veto state, I5).
Added
Auth::isStaff()shared helper;Audit::countRecentByUser(); migration
028 (checkin_token, votes.chain_ver, audit_log(action,ip,created_at) composite index, registration.manual_autoapprove toggle seed).
Changed
- Staff manual entry / CSV import auto-approval of non-award entries is now
driven by feature toggle registration.manual_autoapprove (default on β flip in /admin/toggles to require approval per workflow).
[3.0.0] β 2026-08-31
Ceremony Production System β MC dashboard, stage screen, award team, self check-in.
Added
- MC Dashboard (FR-3000):
/mcβ dedicated MC role; controls the entire show:
advance/back/skip/goto slides; live winner queue with shield status; reorder winners (move up with reason); call winner β stage shows them; mark appeared (count++); blank screen; GoH no-show β replace with substitute; stage screen + run-sheet links.
- Stage Screen (FR-3010):
/stage/{slug}β polling (1.5s), full-screen, brand colors;
renders all slide types; multi-GoH layout (1-N presenters side by side); anthem video/audio playback; CSS transitions between slides.
- Award Team (FR-3030):
/award-teamβ dedicated role; big buttons: "Shield Ready β "
/ "Not Ready β"; gets notified on shield request; MC sees live status.
- Winner Queue + Shields (FR-3020): calling order; shield lifecycle (not_requested β
requested β ready / override_blank); reorder with reason; appearance tracking.
- Admin Override (FR-3040): admin can force blank shield to unblock queue.
- GoH Substitution (FR-3050): no-show β replace β substitute notified;
category_presenters auto-updated; goh_substitutions table.
- Self Check-in (FR-3060):
/self-checkinβ attendees enter code/scan QR β
checked in β see zone/seat + queue position.
- Multi-GoH Presenters (FR-3070):
category_presentersjoin table (1-N per
category); stage screen auto-adapts layout.
- Ceremony Auto-generate (FR-3080):
/admin/ceremony/setupβ one-click generates
the full run-of-show from event data (slides + queue + stage_state).
- New roles:
mc,award_team; migration 027 (6 tables + role enum + seat columns).
Fixed
- PDO repeated-parameter bug in ceremony_queue INSERT (
:oused twice). u.photo_pathβr.photo_path(users table has no photo_path; registrations does).- Stage screen layout reference (views/screen.php doesn't exist β null layout).
[2.6.0] β 2026-08-30
[2.6.0] β 2026-08-30
Growth β sponsors + season report (final roadmap release).
Added
- Sponsor wall (FR-2400): per-event sponsors (Platinum/Gold/Silver/Partner, logo upload,
URL) managed on the event form; logo wall on the public event page (cached).
- Season report (FR-2500):
/admin/report?year=β print/PDF-ready: totals, per-event
tables with categories, presenters, winners + certificate numbers. Migration 026.
Fixed
Uploadimport missing in Events controller (sponsor logo upload 500).
[2.5.0] β 2026-08-30
Section 3 β Day of Event.
Added
- QR check-in (FR-2000):
/admin/checkinβ scanner-ready input accepts bare tokens or
any certificate/vote/profile URL; duplicate detection with friendly flash; undo; live present/expected counts; audited.
- Presenters + run-sheet (FR-2100): assign a GoH presenter per category (auto notify);
printable /admin/runsheet with numbered programme, presenters, winners + cert numbers, and confirmed GoH slots.
- Media gallery (FR-2200): member dashboard upload (photo or video URL + caption) β
staff moderation queue /admin/media β approved media shows on the vote page + event photo wall. Migration 025.
- Ceremony ops (FR-2300):
/admin/ceremonyβ per-category reveal buttons, presenter
selects, attendance KPI, venue screen + run-sheet links, one screen.
Fixed
- Ceremony view SQL referenced unjoined alias (500).
[2.4.0] β 2026-08-30
Track & Trust.
Added
- People tracking (FR-1860):
/admin/peopleβ every registrant with engagement
score; /admin/people/{id} full activity timeline (audit + votes + page views).
- Anonymous visitor tracking:
page_views(hashed IP+UA+day β no raw PII), public
pages only, bot-filtered; never blocks a request.
- Vote receipts + hash chain (FR-1870): receipt code shown after voting; public
/verify-vote?code=; chained seals; integrity badge on /admin/system.
- AI category fit (FR-1880): "describe your work" on the award form β AI picks the
category (mock-safe).
- Click analytics (FR-1890): share-link clicks + unique-visitor KPIs on overview.
- Migration 024.
Fixed
- Bootstrap tracking used unqualified
DB(silent catch) β now FQCN. Dashboard::persontypedintparam vs Router string params β accepts string now.
[2.3.0] β 2026-08-30
[2.3.0] β 2026-08-30
Engage β login, messaging, AI drafting, scheduling.
Added
- Magic-link login (FR-0180): "email me a login link" for members β one-time 15-min
tokens (log email + SMS), throttle setting, no account enumeration; one-time use enforced.
- Internal messaging (FR-1830): team β member threads; unified inbox
/messages
(staff: new-thread + member picker; members reply); unread timestamps; ai_assisted markers; notifications + audit message.sent; thread isolation verified.
- AI drafting (FR-1840): recipient-aware "β¨ Draft with AI" in the composer +
bulk personalized drafts (select GoHs β one intent β per-person drafts β review β send all); audited messages.bulk_drafted.
- Scheduling (FR-1850): GoH availability slots + staff requests/proposals +
GoH confirm/decline/counter-offer; /admin/schedule board + member dashboard section; notifications + full slot.* audit. Migration 023.
Fixed
- Messages read-column bug (
member_user_id_read_atβmember_read_at). - Schedule inserts: repeated PDO named placeholders (native prepares).
- Login + magic throttles are now settings (
auth.login_throttle,
auth.magic_limit_per_hour) β QA lifts/restores them.
[2.2.0] β 2026-08-30
[2.2.0] β 2026-08-30
Section 2 core β GoH completion + ratings + profiles.
Added
- Decision messages (FR-0305b): approve/reject β in-app + email + SMS (log transports) for
all types (single + bulk); GoH submit confirmation email + SMS.
- GoH card type
goh(FR-0608,campaign.goh_text) for approved Guests of Honor. - "Guests of Honor" section on public event page (approved, with title/company/socials).
- GoH ratings (FR-0620): Seniority/Influence/Relevance (0β100) β average; global +
per-event storage (goh_ratings), effective = event ?? global; /admin/goh-roster sortable (suggestion seed); rate form; audited goh.rated; auditor read-only.
- Public profiles
/profile/{token}(FR-1701): photo, name, title/company, bio,
LinkedIn + X/Instagram/Facebook, badges + cert no, vote counts + CTA; contact info staff-only. social_x/instagram/facebook user columns; self-editor on member dashboard; collected on GoH form. Migration 022.
[2.1.0] β 2026-08-30
[2.1.0] β 2026-08-30
Reach β Web Push + bulk import.
Added
- Web Push (FR-1820): pure-PHP stack β VAPID ES256 JWT + RFC 8291 aes128gcm encryption
(Core\Push\WebPush), push_subscriptions, member opt-in on dashboard, sw.js push/notificationclick handlers, admin broadcast UI (/admin/push, segments), auto-push to applicants on winner.locked, dead-subscription cleanup, bin/generate-vapid.php
bin/push-selftest.php(crypto roundtrip green).- CSV bulk import (FR-0311):
/admin/registrations/importβ per-line error report,
duplicates skipped+reported, non-award rows auto-approved, audited.
- Waitlist filter chip + import link; nomination throttle now a setting
(nominations.rate_limit_per_hour, default 5).
Fixed
- WebPush/VAPID point extraction (PEM vs DER) ECDH bug.
- QA: nomination throttle lifted during suite runs; 8 SIGPIPE-prone grep pipes hardened.
[2.0.0] β 2026-08-30
Program growth & ops β 13 features in one release (AβM).
Added
- Nominations (FR-1800): public
/nominateβ admin queue β invite (signed prefilled
application link, log email/SMS) β auto-convert on registration; webhook + audit; honeypot + per-IP throttle.
- AI Assistant (FR-1810):
/assistantβ multi-conversation chat for the whole team
(admin/staff/judge/auditor); context-aware (live events/queues injected; PII-free for judges); async send, 30 msgs/h/user; audited by length only; mock replies until MiniMax key.
- Judge rubric (FR-0706): 0β10 scores (innovation/impact/leadership) feed the 30% board
score (avg/30); legacy reviews fall back to approval ratio; winners board shows rubric.
- Certificate numbers (FR-0607):
FLA-<year>-<seq>assigned to winners+finalists at
lock; printed on certificates, on the QR-verify page, searchable.
- Ceremony reveal mode (FR-1007): per-event toggle; reveal categories one-by-one
(audited); results page, venue screen and public API gated until revealed.
- Capacity + waitlist (FR-0309b): optional per-category
max_applicants; overflow β
waitlisted (notified + audited); admin promotes via approve.
- Scheduled comms (FR-0907): optional send-later; queued recipients; lazy dispatch +
bin/send-scheduled.php (cron-ready).
- Embed widget (FR-1503):
/embed/leaderboard/{slug}β white-label auto-refresh
leaderboard; framable (X-Frame-Options relaxed only for /embed/*); snippet in event admin.
- Data rights (FR-1700): member JSON export + irreversible anonymization
(PII/photos stripped, votes retained detached, audited).
- Auditor role (FR-0107): read-only team access to overview/audit/registrations/
duplicates/winners/system/search; every mutation stays admin/staff-only (verified by QA).
- Event cloning (FR-0205): copies event + categories + question bank as draft.
- wa.me click-to-chat links on member support, admin registrations, nominations queue.
- Public caching (NFR-16): file-TTL cache for directory/event/results/hall-of-fame/
sitemap/home; flushed on admin mutations.
- Migrations 017β020 (nominations, rubric/cert/reveal, ops, assistant).
Fixed
- Reveal-mode sentinel bug (empty revealed list β reveal-off) β gating now via
isRevealed(). - Assistant page exposes a CSRF field for non-JS clients/QA.
[1.8.0] β 2026-08-30
Search & verification pack (Pakistan-ready).
Added
- Global admin search (FR-1606):
/admin/searchβ one query across registrations
(name/email/token), events, tickets, users; search box on every admin page; audited; anon-guarded; empty state.
- SMS channel (FR-0906): mass comms
channel=sms(log transport to
storage/logs/sms.log, per-recipient tracking; SMS_PROVIDER/SMS_API_KEY env placeholders β Twilio/local gateway plugs in without code changes).
- QR verification (FR-0606): printable certificates now embed a QR code
(client-side render, print-safe) β public /cards/{token}/verify page shows live winner/finalist status ("Certificate verified" / "Application on record"); unknown tokens 404.
- Migration 016:
communications.channelgainssms.
Fixed
Core\Searchuses distinct PDO placeholders per occurrence (native prepares reject
repeated named params).
- QA: duplicates-page assertion is now data-volume-proof (24k+ accumulated demo alerts).
[1.7.0] β 2026-08-30
Photo flexibility (Pakistan UX).
Added
- Photo upload may be deferred to dashboard (
POST /dashboard/photo) foraward/goh; forms show hint βOptional now β you can upload laterβ. - Dashboard member page shows upload/change-photo form when photo missing, with βPhoto required before approvalβ CTA.
Changed
FR-0304/FR-0305/FR-0308+ UC-04/UC-05: photo optional at signup, required beforeapproved.views/register/award.php:41+views/register/goh.php:21no longerrequiredon file input.
Fixed
- Single approval and bulk approve now block when photo missing (redirect
?err=nophoto/?skipped=N), audited asregistration.approve_blocked_no_photo.
[1.6.0] β 2026-08-29
Scale & polish.
Added
- Maintenance mode (FR-0106):
site.maintenancetoggle + message in/admin/toggles;
public returns 503 with Retry-After, admin/staff bypass.
- Bulk ops (FR-1604): approve/reject multiple registrations from
/admin/registrations
(checkboxes + bulk action, per-row audit).
- Audit CSV export (FR-1103):
/admin/export?type=audit+ button on audit log page. - System health (NFR-15):
/admin/systemβ DB size, table rows/sizes, disk free/total,
storage size, queue depths (pending regs, open tickets, dup alerts), backup recency; linked in admin nav + overview.
Fixed
- Toggles no longer clear when saving maintenance-only (preserves feature flags).
- Vote rate-limit / bulk / 2FA edge cases hardened via QA.
[1.5.0] β 2026-08-29
Integrations & experience pack.
Added
- Webhooks (FR-1601):
/admin/webhooksCRUD β endpoint URLs, per-event subscriptions
(registration.created, vote.cast, winner.locked, duplicate.suspected, or *), HMAC-SHA256 X-FLA-Signature + X-FLA-Event headers, delivery log with HTTP status + fail counters, one-click test ping.
- Venue leaderboard screen (FR-1602):
/screen/{event-slug}β dark big-display theme,
30s auto-refresh, per-category top-5; public when leaderboard is public or event is finished, otherwise staff-only.
- CSV exports (FR-1603): registrations + votes (event-scoped) from the admin area;
audited as export.csv.
- Admin 2FA (TOTP) (FR-1604): per-account setup with secret +
otpauth://URI and
code confirmation; password β code challenge at login; disable requires a live code; wrong codes audited (auth.2fa_failed).
- PWA (NFR-13): branding-driven dynamic manifest (
/manifest.webmanifest), generated
brand icons (bin/make-icons.php), service worker (/sw.js) β cache-first statics, network-first pages; installable on HTTPS.
- QA: E2E phase 16 β 110 checks green (manifest/icons/SW, webhook create + live
delivery + signature + ping via local receiver, screen privacyβpublic, CSV + guards, 2FA enableβchallengeβloginβwrong-codeβdisable round-trip).
[1.4.0] β 2026-08-29
Public discovery & ops pack.
Added
- Events directory
/events(live + archive) and Hall of fame/hall-of-fame
(FR-0204, FR-1006) β linked in the main nav.
- Printable certificates (FR-0605): print/PDF-ready HTML pages for winners & finalists
(/cards/{token}/{type}/print), brand-colored, print CSS, verification link.
- Public read-only JSON API v1 (FR-1501):
/api/v1/events,
/api/v1/events/{slug}, /categories, /winners β public data only, JSON 404s.
- SEO surface (FR-1502):
/robots.txt(admin areas disallowed, sitemap reference) +
dynamic /sitemap.xml including events & results pages.
- Vote rate limiting (NFR-12): per-IP, default 30/hour, tunable via
voting.rate_limit_per_hour setting; audited as vote.rate_limited; friendly voter copy.
- Automated backups (NFR-10):
bin/backup.shβ single-transaction DB dump (gz) +
storage tarball, 14-day retention, cron-ready (see HELP.md).
- QA: E2E phase 15 β 91 checks green (directory, hall of fame, robots/sitemap, API
incl. 404 JSON, printable certificate, rate-limit enforcement + restore, backup artifacts).
- QA infra: suite lifts the per-IP vote limit during runs (single-host testing) and
restores the default on exit (trap).
[1.3.0] β 2026-08-29
Polish pack β closes the remaining feasible backlog (only owner-provided keys/policies remain).
Added
- Email verification for basic (non-LinkedIn) signups (FR-0309/0310): token issued at
registration, /verify-email?token=β¦ completes it (one-time), audit + notification; log-mailer keeps it mock-safe.
- Vote-change policy per event (FR-0509):
vote_change_allowedtoggle on the event
form; voters can update their reason (one_per_candidate) or move their vote (one_per_event) β audited as vote.changed; default remains "votes are final".
- Referral attribution (FR-0510):
?ref=on share links survives the OAuth round-trip
and lands on the vote row; applicants see a per-channel breakdown on their dashboard.
- Finalist certificate cards (FR-0604): PNG card type
finalist(text from
campaign.finalist_text setting) available on the applicant dashboard after results.
- Security headers on every response (NFR-11): HSTS on HTTPS, X-Frame-Options
SAMEORIGIN, X-Content-Type-Options nosniff, Referrer-Policy, Permissions-Policy.
- QA: E2E phase 14 β 76 checks green (headers, verify flow, referral record,
vote-change off/on behavior, no-dup-on-change, finalist card PNG).
[1.2.0] β 2026-08-29
Release-control completions β nothing functional left pending except owner-provided keys.
Added
- Winner un-lock flow (FR-1005):
/admin/winnerslocked categories can be unlocked β
snapshot removed (public results retract), winners revert to finalist, event returns to judging; re-lock replaces snapshot; fully audited (winner.unlocked).
- Team password reset (FR-0705): admin re-issues temp passwords for judge/staff
accounts from /admin/judges; forced change on next login; old password invalidated; audited (team.password_reset).
- QA:
bin/qa-e2e.shextended to 63 checks β unlock round-trip (snapshot removal,
status revert, public retraction, re-lock) + reset-pass flow (re-issue, forced change, old-password rejection).
[1.1.0] β 2026-08-29
Duplicate-candidate detection (BR-011) + integrity fixes.
Added
Core\Duplicatesmatcher: signals = email, LinkedIn, CNIC hash (salted SHA-256 β
plain CNIC never stored), name+WhatsApp pair, same-account repeat-application across events; weighted scoring.
- Scan runs on every registration path: public visitor/award/GoH, staff manual entry,
account claim-merge. Hits create duplicate_alerts rows, in-app notify all admins, audit duplicate.suspected.
- CNIC field: mandatory on award form, optional on manual entry; format-validated
(13 digits); hash-only storage (users.cnic_hash).
- Admin review queue
/admin/duplicates: side-by-side comparison, **Confirm duplicate β
auto-veto the new registration, Dismiss**; duplicate.confirmed/dismissed audited.
- Visibility: red β dup badge + "dupes" filter on
/admin/registrations; open-alerts
KPI card on admin overview; sidebar link.
- Docs: SRS FR-1300 + BR-011 + UC-17; new workflow
docs/WORKFLOWS/duplicate-candidate.md. - QA:
bin/qa-e2e.shphase 12 (CNIC validation, repeat applicant, cross-account CNIC,
review UI, dismiss + confirm-veto) β 50/50 green; surface suite 33/33.
Fixed
- Judges board no longer shows other judges' tallies pre-lock (anchoring, SRS A2).
- Staff manual entry no longer binds to the staff/admin session account (was
attaching registrations to the operator's user row).
[1.0.0] β 2026-08-29
Full platform delivered in one drop (milestones v0.2.0βv1.0.0 merged per owner request), verified by a 41-check end-to-end suite (bin/qa-e2e.sh, 41/41) and the surface suite (bin/qa.sh, 33/33). Demo event content kept on the live site.
Added
- Admin (FR-0100/UC-01): password login with IP throttling + forced password change,
Branding panel (name/logo/colors/campaign texts), feature-toggles UI, team accounts.
- Events (FR-0200): CRUD + status lifecycle, categories show/hide/order, per-event
voting config (mode, 70/30 weights, reason toggle), public event pages.
- Registration (FR-0300): visitor / award / guest-of-honor flows, LinkedIn OAuth
(mock-capable), WhatsApp requirement (BR-007), photo uploads, one-registration rule (BR-001/002), staff manual entry + owner claim-by-email, success pages with share links + "I have applied/visiting" cards.
- AI interviews (FR-0400/UC-08): admin question bank per category, randomized 5-question
sessions (BR-010), AI marking (score+feedback via gateway; mock deterministic), session totals shown to applicant, judges and admin.
- Public voting (FR-0500/UC-07): candidate share pages with Open Graph + share-card
previews, LinkedIn-verified voters (mock binds identity to browser session), restriction modes (BR-003), optional voter reason, duplicate protection, own-voters visibility (BR-006), vote notifications.
- Judges (FR-0700/UC-10): team account creation (temp password + forced change),
event/category assignments, review board, approve/disapprove/needs-info, recusal.
- Dashboards (FR-0800/UC-16): admin KPIs with 7-day trends, Chart.js graphs
(votes-over-time, funnel, category shares, AI distribution), audit tab with filters (FR-0807/1102), AI insights panel with anomaly flags (FR-0407), staff desk, applicant dashboard (votes chart, voters, AI transcript, cards, tickets, notifications).
- Tickets (FR-0901): threaded tickets with staff queue, statuses, notifications.
- Mass comms (FR-0902β0904): audience segments, preview, per-recipient delivery
tracking; email log transport + simulated WhatsApp (keys plug in later).
- Winners (FR-1000/UC-11): 70/30 blended rankings + judges tallies, veto/unveto
(BR-005), approve-&-lock snapshots (BR-009), auto-finish, public results page, winner badge cards (PNG).
- Platform core: CSRF on every form, role guards, audit writer on all sensitive
actions, media streaming route, on-demand GD card service, log Mailer, notify queue.
- QA:
bin/qa.sh(33 surface checks) +bin/qa-e2e.sh(41-check full lifecycle).
Fixed
move_uploaded_filefailures:storage/must be owned by the php-fpm user
(documented in HELP.md); uploads/cards/logs/cache are www-data-writable.
- Mock LinkedIn identity is now stable per browser session (true duplicate-vote testing).
Security
- All POST endpoints enforce CSRF tokens; team logins throttled (5 fails/10 min/IP)
with audit entries; upload validation (type/size/dimensions); forced initial password change for created accounts; winner announces happen only via audit-locked snapshots.
Pre-1.0 QA fixes (delivered as commits after v0.1.0)
Added
- SRS: admin dashboard spec expanded β graphs (FR-0805: votes-over-time, funnel,
category shares, top candidates, AI score distribution), full stats coverage (FR-0806, 7d/30d trend KPIs), audit tab (FR-0807), AI insights (FR-0407: weekly summaries, judge-ready applicant briefs, vote-anomaly flags), new use case UC-16, chart-stack decision D11 (Chart.js CDN + JSON endpoints). Roadmap updated: v0.2.0 gets a dashboard starter, completion in v0.8.0.
bin/qa.shβ repeatable QA suite (29 checks: route statuses + body markers for every
public page and all docs, security probes .env/.git/path traversal, HTTPS redirect). Usage: ./bin/qa.sh [base-url].
Fixed
- Docs viewer fatal error (
sort($paths ??= [])β expression cannot be passed by
reference); /docs, /docs/srs and all workflow pages restored.
- Doc slugs are canonical:
docs/SRS.mdβ/docs/srs, workflows β/docs/workflows~<name>;
registration placeholder cross-links updated accordingly.
- QA script: grep marker handling (
--guard) and correct HTTPβHTTPS redirect probe.
Security
database/migrate.shno longer carries a default DB password β credentials come from
.env / environment, script refuses to run without them.
- Production posture:
APP_DEBUG=falsein live.envβ stack traces are no longer
rendered to visitors (errors go to logs only).
[0.1.0] β 2026-08-29
Docs + scaffold baseline.
Added
- Plain-PHP modular core:
Router,DB(PDO/MariaDB),Settings,View,Config,
Env, Auth session skeleton, helpers (url(), setting(), absolute_url()β¦).
- Module skeletons under
src/Modules/: Home, Docs, Registration, Branding, Events,
Ai, Voting, Cards, Judges, Tickets, Comms, Winners, Audit, Notifications, Dashboard.
- Docs viewer (
/docs,/docs/{slug}): renders all repo.mdfiles as organized
webpages with TOC + mermaid support (FR-0103).
- Core services:
- AI gateway β MiniMax platform driver (
minimax-m3, OpenAI-compatible) + deterministic
- AI gateway β MiniMax platform driver (
MockProvider; mock active until keys (NFR-08).
- LinkedIn OIDC client + mock fixture.
- WhatsApp null driver (logs to
storage/logs/whatsapp.log). - Share-card generator: GD driver (applied / visiting / winner, 1080Γ1080 PNG);
AI driver reserved (cards.driver).
- Landing page (responsive, DB-branded), 404 page, registration placeholder pages.
- Database migrations 001β009: settings(+seeds), users, events, categories,
registrations, question_bank, ai_sessions, ai_answers, votes, judge_assignments, judge_reviews, tickets, ticket_messages, feature_toggles(+seeds), audit_log, notifications, communications, communication_recipients. Runner: database/migrate.sh.
- Documentation set:
docs/SRS.md(business use-cases UC-01β¦15, FR/NFR, BR-001β¦010),
7 workflow docs (docs/WORKFLOWS/), TODO.md (session handoff), ROADMAP.md, WORKFLOW.md, HELP.md, AGENTS.md.
.env.example,.gitignore,composer.json(PSR-4 autoload only β no framework),
bin/card-demo.php, Apache-inline public .htaccess.
- Live deployment: nginx vhost (
deploy/nginx.conftemplate) serving
https://event.production1.jugaar.ai on plain 443 β Let's Encrypt certificate issued, HTTPβHTTPS redirect enabled. Verified public responses on /, /health, /docs/*.
Fixed
- Router treats
HEADasGET(monitoring tools send HEAD).
Security
.envgit-ignored; PDO prepared statements; session cookie flags
(HttpOnly, SameSite=Lax, Secure on HTTPS).
