Skip to content

release: 1.0 - #34

Merged
M1RK02 merged 7 commits into
mainfrom
develop
Jul 13, 2026
Merged

release: 1.0#34
M1RK02 merged 7 commits into
mainfrom
develop

Conversation

@M1RK02

@M1RK02 M1RK02 commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Promotes develop to main for the 1.0 release — the final phase gate.

Merge as a standard merge commit — do NOT squash. This keeps main containing develop's exact history. Tag the merge commit v1.0 after merging.

Included since last main (phase-6)

  • Field-test fixes (C): QR check-in, dashboard, leaderboards, map, navigation, and friends flows hardened from field testing.
  • Doubles / 2v2 (A): full doubles integration — match flow, team leaderboard, and team profile.
  • Achievements (B): client-side achievement evaluation, badge grid, and profile achievements grid.
  • Arena Assistant (C): LLM chatbot answering over live app data, including the mandated availability disclaimer copy (brain/project_description.md §3.10).
  • Field-test hardening & testing campaign: additional robustness fixes plus documentation and seed-data polish.
  • Documentation: design & test document, user manual, and final presentation (docs/presentation.pdf).
  • App icon refresh: new launcher icon source and regenerated Android/iOS assets.

Shared Contracts / schema

  • No new cross-vertical Shared Contracts or Firestore schema changes in this window beyond what was already locked at phase-6.
  • Achievement evaluation and match-timeout transitions remain client-side, lazy, no Cloud Functions (Spark-tier trust boundary).

M1RK02 and others added 7 commits July 8, 2026 16:02
* feat: add doubles and team-profile localization keys

* feat: doubles data layer — side-join, pair rating and match queries

* feat: 2v2 match creation, side picker and team-aware result UI

* feat: team (pair) profile screen with per-sport stats

* feat: global doubles leaderboard with pair rows and search

* fix: move leaderboard singles/doubles toggle to its own row

The mode toggle shared a row with the scrolling sport chips and collided
with them on a long chip list. Give it a full-width row at the top (like
the scope selector), with sport chips and scope each on their own rows.

* feat: move singles/doubles + side selection to a dedicated setup screen

Split the mode/team picker out of the dark QR viewfinder onto a plain
themed MatchSetupView. The match flow routes readyToConfirm/checkingIn
there whenever an explicit setup is needed (deep-link entry or a far-table
scan); a nearby blind scan still auto-confirms as singles on the
viewfinder. QrScanView sheds the now-dead inline controls and GPS banner.
Adds matchSetupTitle/matchSetupSubtitle ARB keys; setup-screen tests carry
the check-in/GPS/doubles assertions.

* chore: expand doubles seed data around the two real users

Christian+Mirko now carry pair ratings across all three sports, plus each
real user gets a rated real+synth alternate team and filler opponent pairs
so every doubles board has depth. New doubles() helper seeds the C+M pair
across states/sports (win, loss, awaiting, disputed, live) so their team
profile stats and history populate fully.

* fix: lead leaderboard filters with sport, group the segmented selectors

Sport chips now come first as the primary axis; the two like-styled
segmented controls (mode, then scope) sit together below, so the differing
control styles no longer read as three scattered rows.

* feat: give the doubles leaderboard a podium like singles

Make LeaderboardPodium pair-aware via optional label/initials resolvers,
and render doubles with the same podium-plus-rows shape as singles — a pair
shows both members joined ("Anna & Bruno") with combined initials ("AB").
* fix: bump workaholic achievement threshold to 15 matches/month

Data-only edit to the achievementDefs seed (was 10) and the matching
threshold documented in the firestore schema, ahead of the Phase 7A
achievements client work.

* feat: add achievement data layer (models, evaluator, repository)

New client-side-only achievements slice, mirroring the elo_calculator
pattern:
- AchievementDefModel — the achievementDefs/{id} schema shape.
- AchievementEvaluator — pure, side-effect-free evaluation of all 6
  seeded conditions (first_victory, win_streak, giant_slayer, explorer,
  monthly_count) against a per-match context, write-once by construction
  (never re-emits an id already in currentBadges). tournament_champion
  is intentionally never evaluated here.
- AchievementRepository — bounded reads over achievementDefs.
- ARB keys for all 6 badge names/descriptions plus the profile grid and
  unlock-reveal strings (EN + IT), the assets/badges/ block in
  pubspec.yaml, and the 6 provided badge PNGs.

Each piece ships with its own unit test (evaluator covers every
condition's boundaries and the write-once rule).

* feat: evaluate and persist achievements in the match confirmation transaction

confirmResult now returns the newly-earned achievementIds per player
(Map<uid, Set<achievementId>>) instead of void. MatchRepository builds
the AchievementEvaluator's context in _prepareConfirmation (reusing the
per-player confirmed-history read already done for streak/monthly/
explorer — no new query) and writes badges.{id} = Timestamp for every
player inside the SAME atomic transaction as the match/ELO/facility
writes (Phase 4 Transaction Rule), via SetOptions(merge: true).

Write-once is enforced end-to-end: the evaluator never returns an id
already present in the user's badges map, so the merge write can never
clobber an existing unlock timestamp. Achievement reads are cosmetic
(Spark-Tier Trust Boundary) and degrade to "no badges" on any failure
rather than failing the confirmation.

* feat: award tournament_champion badge on tournament completion

completeIfDecided now awards the winner the tournament_champion badge
in the same commit as the completed/winnerUid write — the only place
this condition is evaluated (never at match confirm). Write-once: the
winner's badges map is read before any write, and the merge write is
skipped when the id is already present, so a re-derived champion never
overwrites an existing unlock timestamp.

* feat: show client-local achievement unlock reveal on match summary

ResultCubit.confirm() resolves the confirming user's own newly-earned
ids (from confirmResult's new return value) to their AchievementDefModels
and carries them as ResultState.newBadges. ResultSummaryView renders an
inline reveal (badge PNG(s) + localized name, scale-in animation, medium
haptic) when non-empty — no FCM, no local-notifications dependency,
mirroring the established Spark-tier client-local alert pattern (queue-
ready, challenge notification). Resolution is decorative: a failed defs
read just skips the reveal, since the badge is already persisted.

* feat: add tappable achievement badge grid to the profile screen

ProfileCubit loads the achievementDefs list (best-effort, decorative —
a failed read just hides the section) and ProfileScreen renders it as a
grid: one art file per badge, earned full-color vs locked desaturated
(ColorFilter greyscale matrix), driven by the seed's iconAsset field.
Works unchanged for a foreign profile.

Each tile is tappable and surfaces its detail as a system-message
SnackBar (the reset-password idiom) instead of a permanent on-grid
caption — the unlock date for an earned badge, or how to earn it for a
locked one — keeping the grid compact.

AchievementRepository is threaded through createRouter/ProfileScreen
alongside the other per-tab repositories.

* fix: remove empty space under profile achievement badges

GridView.count's childAspectRatio forces every row to a fixed height —
once the on-grid caption was replaced by the tap-to-reveal SnackBar, the
row height no longer matched the (now shorter) content, leaving a gap
under every badge. Swapped to a Wrap of fixed-width tiles, which sizes
each row to its actual content instead of a computed aspect ratio.

* fix: give Christian's profile fun-stats enough matchups to clear the gate

Favorite Victim and Black Beast only render once a head-to-head hits
kFunStatMinMatchups (3). Rework the seeded win-streak matches to be all
vs. the same opponent (Favorite Victim) and add two more losses vs.
another (Black Beast, on top of the existing confirmed loss), so both
fun-stat cards have seed data to demo on the profile screen.

* fix: restore full-width 3-column layout for the achievement grid

The Wrap from the previous commit fixed the empty-space bug but
regressed the grid to a centered cluster of fixed-width tiles instead
of stretching across the card. Switched to GridView.builder with a
SliverGridDelegateWithFixedCrossAxisCount: crossAxisCount keeps the 3
columns stretching to fill the full width like before, while a fixed
mainAxisExtent (not childAspectRatio) sizes rows to the tile's actual
content height, so the empty-space fix is kept.

* fix: stream the user doc instead of fetching it once in ProfileCubit

ProfileCubit._loadUser() fetched users/{uid} one-shot at cubit creation.
Because StatefulShellRoute keeps each tab's branch (and its cubit)
alive across tab switches rather than rebuilding it, that fetch never
re-ran — so a newly-earned achievement badge (or any other user-doc
change) only appeared after a full app restart, when a fresh
ProfileCubit finally re-read the doc.

Switched to UserRepository.watchUser(uid), the same live-stream pattern
already used by FriendsCubit/AuthCubit, so state.user (and its badges
map) stays current for as long as the cubit is alive.

* chore: mark Phase 7A achievements tasks complete on the task board

Models/conditions, client-side confirm-transaction evaluation, and the
profile badge display all ship in this branch. Drops the "push
notification" line — the client-local in-app reveal (no FCM) is the
shipped behavior, called out as a deviation in the PR description.

* fix: write concrete Timestamp for tournament_champion badge

Address PR 29 review:
- completeIfDecided writes Timestamp.fromDate(now) instead of
  serverTimestamp(), keeping the optimistic snapshot parseable so the
  champion's Profile tab doesn't flip to an error state mid-write.
- UserModel.fromMap tolerates an unresolved/null badge value defensively.
- Document the match-path non-transactional write-once read asymmetry.
- Sort achievement_repository import before challenge_repository.
* feat(assistant): add chatbot tools service and repository queries

- FacilityRepository.getFacilitiesByStatus, TournamentRepository.getTournamentsInWindow/getParticipantCount, RatingRepository.getUserRank, QueueRepository.getQueueLength/getUserQueueEntry - all one-shot, bounded (limit/count()), no listeners, for Spark-tier read discipline.
- ChatbotToolsService: read-only tool layer over the existing repositories (getEmptyTables, getFacilitiesByStatus, getTournamentsToday, getUserQueuePosition, getUserEloAndRank, explainHowEloWorks). Typed tool_results.dart result objects so the UI can render cards without re-parsing strings.

* feat(assistant): add multi-provider LLM client and settings

Deviation from the original chatbot plan (which assumed a single Gemini key via --dart-define): a runtime-configurable multi-provider LLM layer instead, so the API key/provider/model are user-supplied and persisted on-device rather than baked into the client binary.

- AssistantProvider enum (anthropic, openai, openrouter, gemini) with per-provider default models, all user-editable.
- AssistantSettingsModel + AssistantSettingsRepository, persisted via shared_preferences (added to pubspec.yaml alongside http, already present).
- LlmClient (package:http): OpenAI-compatible /chat/completions for openai + openrouter, Anthropic Messages API (x-api-key + anthropic-version), Gemini generateContent (key as query param). Low temperature throughout. testConnection() fires a minimal request and maps common HTTP failures (401/403 invalid key, 429 rate limit, network error) to short reason codes for localized UI feedback.

* feat(assistant): add chatbot orchestrator, intent matcher, and system prompt

- RuleBasedIntentMatcher: keyword classification (EN+IT) into empty tables / tournaments today / ELO explainer / queue position / general.
- ChatbotSystemPrompt: distills static app facts (ELO rules, not-a-booking-app framing) plus the current turn's tool results as the only source of live data, explicitly forbidding invented facilities/data.
- ChatbotRepository: orchestrates classify -> run tool(s) -> LLM-grounded answer if a key is configured, else deterministic rule-based fallback (limited mode) on missing key or LlmException. Hard rule enforced here in code (never trusted to the LLM or the templates): the localized availabilityDisclaimerShort is appended unconditionally whenever an availability tool fired this turn.
- ChatMessageModel: in-memory (non-persisted) chat message, optionally carrying FacilityToolResult/TournamentToolResult for rich card rendering.

* feat(assistant): localization for Arena Assistant

Adds 42 new ARB keys (EN + IT) covering the chat screen, settings screen, suggested-question chips, limited-mode/error strings, and the four exact disclaimer strings from the plan (availabilityDisclaimerFull/Short, chatbotIntroDisclaimer, plus a one-line tutorialSlide3Disclaimer). Hand-edited app_localizations.dart / app_localizations_en.dart / app_localizations_it.dart to match — flutter gen-l10n is unavailable in this environment, so these were generated by script from the arb content and matched to the existing doc-comment/getter style key-for-key; not verified to compile.

Also appends the short disclaimer as a muted caption on the onboarding mini-tutorial's map/queue slide (Task C4 placement 4).

* feat(assistant): add chat UI and cubit

- ChatCubit/ChatState: initial/sending/answered/error lifecycle, in-memory-only messages (no persistence across restarts), limited-mode tracking driven by whether an API key is configured.
- ChatScreen (route /chatbot, full-screen, pushed outside the shell): app bar with settings gear + overflow 'Clear chat', limited-mode pill linking to settings, dismissible first-open disclaimer banner (chatbotIntroDisclaimer), suggested-question chips, message list with typing indicator, rounded input + 48px circular send button disabled while sending.
- Widgets: MessageBubble (user -> primaryContainer right-aligned, assistant -> surfaceContainer left-aligned, error bubble with retry), FacilityStatusCard (status dot, sport icon, queue count, Open Map, short-disclaimer footer), TournamentCard (name/sport/time, x/max participants, status chip, View), SuggestedChips, DisclaimerBanner, TypingIndicator (animated 3-dot).
- AssistantSettingsCubit/State + AssistantSettingsScreen (route /chatbot/settings): provider dropdown, obscured API key field with show/hide toggle, user-editable model field (prompts to reset to the new provider's default on provider change), Test Connection with loading/success/error feedback, 48px Save button. Works fully with no key saved (chat stays in limited/rule-based mode) with a banner linking here.
- Wiring: route_constants.dart + app_router.dart (both routes registered, /chatbot auth-guarded the same way courtPath/challengePath already are), main.dart provides ChatbotToolsService/AssistantSettingsRepository/ChatbotRepository via MultiRepositoryProvider, Home screen gets a minimal FAB entry point (small surgical diff, dashboard rebuild is a separate in-progress phase).

* test(assistant): unit and widget tests for the Arena Assistant

- RuleBasedIntentMatcher: classifies EN+IT sample phrases into each of the 4 intents plus the general fallback.
- ChatbotToolsService: mocked-repository tests for getEmptyTables, getTournamentsToday, getUserQueuePosition, getUserEloAndRank, and the static explainHowEloWorks (asserts zero repository interactions).
- ChatbotRepository: no-key -> rule-based, LLM-configured-but-errors -> rule-based fallback, LLM-configured-and-succeeds -> LLM text used; hard disclaimer-rule tests proving availabilityDisclaimerShort is appended on both the rule-based path and the LLM path (even when the LLM's own text omits it), and NOT appended when no availability tool fired.
- ChatCubit: limited-mode detection on init, sendMessage emits user bubble then answer, blank input is a no-op, repository errors surface as a retryable error bubble, clearChat resets state.
- ChatScreen widget tests: suggested chips + intro disclaimer banner visible on first open, limited-mode pill shown with no key configured, sending a message renders a user bubble and the assistant reply, a facility card renders with its disclaimer footer visible.
- Repository tests extended: FacilityRepository.getFacilitiesByStatus, TournamentRepository.getTournamentsInWindow/getParticipantCount, RatingRepository.getUserRank, QueueRepository.getQueueLength/getUserQueueEntry (all against fake_cloud_firestore, asserting the bounded limit/count() behavior).

* fix(assistant): verification fixes - analyzer errors, Italian intent matching, queue lookup, DI wiring

* arena assistant

* merge develop, fix map navigation

* fixed the format error

* fix(assistant): localize fallback answers and dynamic card labels

Rule-based/limited-mode answers were hardcoded English, violating the
i18n rule and task_board C2 ("templated ARB answer"). Move them all to
ARB (EN+IT) and pass AppLocalizations into ChatbotRepository.answer();
the English toolContext stays as LLM grounding only.

Also localize two user-facing data tokens the cards printed raw:
- TournamentCard now uses the shared TournamentStatusPill instead of
  Text(tournament.status), which rendered "in_progress" untranslated.
- FacilityStatusCard shows a people icon + count instead of "x2".

Map LlmClient TimeoutException to a distinct 'timeout' reason with its
own localized Test-Connection message (chatbotErrorTimeout).

Drop the duplicate short-disclaimer footer from FacilityStatusCard: the
repository already appends it unconditionally to the message text above
the card, so it was rendering twice in one bubble. Tests updated.

* fix(assistant): DI wiring, drop redundant index, parallelize tool reads

- main.dart: ChatbotRepository now reads the root-provided
  ChatbotToolsService and AssistantSettingsRepository instead of
  constructing its own, so the registered providers are actually used.
- firestore.indexes.json: remove the (ownerType, sportId, elo ASC)
  ratings index. RatingRepository.getUserRank (no degreeProgram) is
  already served by the existing (sportId, ownerType, elo) index, which
  the dashboard uses in production — the new one was a duplicate.
- ChatbotToolsService: fan out the per-row queue-length and
  participant-count reads with Future.wait (still bounded <= maxResults)
  instead of one sequential round-trip each.

* docs(task-board): check off Arena Assistant tasks (C1–C4)

Core (C1/C2) and UI (C3/C4) are delivered by the assistant feature and
now meet their specs (C2's rule-based path produces localized ARB
answers). Marked done per the PR review.

* fix(assistant): include leaderboard rank in limited-mode ELO answer

The rule-based ELO line printed elo + match count but dropped the rank,
while the LLM toolContext included it — a content asymmetry between the
grounded and limited-mode paths. Add a chatbotAnswerYourEloRanked ARB
variant (EN+IT) and branch on rank nullability so limited mode reaches
parity. Tests cover both the ranked and unranked branches.

---------

Co-authored-by: Abdullah Saeed <abdullahsaeed.engineer@gmail.com>
Co-authored-by: Mirko Pica <mirko.pica@mail.polimi.it>
Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* fix: reliable 1v1/2v2 match join

Second-player join was routing through a stale/cached match probe and
getting rejected or misreported as a network error; resolve it via the
facility's own currentMatchId instead. Also stop seeding phantom live
matches so every facility starts free.

* fix: register & profile provisioning flow

Removes the profile-setup/preset-avatar screen in favor of provisioning
the profile doc directly on register or on recovery (profile-missing
session), and keeps the register button's spinner state correctly gated
so recovery mode never flashes during a normal signup.

* fix: allow the confirm transaction to write both players' badges

* docs: general clean up

* fix: registration, opponent's badged, ongoing-tournament

* fix(auth): stop empty RegisterScreen flashing during registration

Landing/login/register navigated between auth screens with push /
pushReplacement, creating an imperative /register match. While the router
redirect holds on /register during `registrationInProgress`, go_router stacks
a fresh, empty declarative RegisterScreen over the filled pushed one — the
"middle" register page (empty fields, still-spinning button) seen between
submitting and the tutorial.

Switch all auth-screen transitions to `context.go` so the redirect is the sole
navigation authority and only one /register is ever mounted. Adds a regression
test that reproduces the duplicate (asserts a single RegisterScreen throughout
the register -> onboarding flow).

* fix(match): resolve mobile_scanner "already started" on the QR screen

QrScanView created its own MobileScannerController and passed it to
MobileScanner. Per the package contract, supplying a controller makes the
widget skip its WidgetsBindingObserver, so the app must drive the camera
lifecycle itself. It didn't — on background/resume or hot-restart the native
session desynced from the Dart state and start() threw
MOBILE_SCANNER_ALREADY_STARTED_ERROR, surfaced as the intermittent
"already started" error over the viewfinder.

The controller was never used (no torch/lens control), so let MobileScanner own
it: the widget then registers its lifecycle observer and handles start/stop on
resume/inactive plus the internal hot-restart mitigation.

* fix(challenges): clear an accepted challenge from the outbox once played

An accepted outgoing challenge lingered on the dashboard forever: nothing ever
marked it done. Complete it lazily, client-side (no write back to the challenge
doc): ChallengeCubit now also watches the signed-in user's own recent matches
and hides an accepted challenge once a confirmed match with the same opponent
and sport, played after the challenge was accepted, appears.

The match query reuses the dashboard's exact `watchUserMatches(uid, limit: 20)`
so the Firestore SDK shares one server-side listener rather than paying for a
second. Pending / declined / expired rows are untouched. Send-only cubits
(friends list) never open the stream.

Wires matchRepository through HomeScreen into the ChallengeCubit; adds cubit
coverage for the played / not-played / order-independent / pre-accept-match /
unconfirmed cases.

* fix(map): debounce tile loads to stop the freeze on fast zoom-out

A fast pinch zoom-out crosses several zoom levels in a few hundred ms. With
flutter_map's default tile-update transformer, every intermediate camera frame
kicks off a full tile reload, flooding the cached provider's dio + sqlite
pipeline and stalling the UI on a physical device.

Wire TileUpdateTransformers.debounce(100ms) on the TileLayer so only the resting
zoom fetches tiles — the flutter_map-recommended pairing with a cancellable
provider (CachedTileProvider is one), which also cancels the superseded
intermediate requests. Adds a regression test asserting a non-default
transformer is wired.

* fix(challenges): open the scan-to-play screen when a challenge is accepted

Accepting an incoming challenge from the dashboard only flipped its status; the
pending card then left the inbox and the recipient was dropped back onto an
empty dashboard with no way to reach the accepted challenge's "scan to play"
screen. accept() now reports whether the write committed, and a successful
accept routes the recipient to /challenge/:id — the accepted-state screen with
the QR check-in CTA the design intends.

Splits the section's accept/decline widget test to assert the navigation on
accept and its absence on decline.

* fix(challenges): reject a table whose sport differs from the challenge

An accepted challenge's scan-to-play routed to the blind scanner with no
knowledge of the challenge's sport, so a ping-pong challenge could be played
on a foosball table. Thread the challenge's sportId through /play/scan?sport=
into MatchCubit, which now rejects a facility of a different sport with a new
sportMismatch error instead of starting the match.

* test: cross-vertical integration pass for the 3 shared contracts

Add app/test/integration/ exercising each shared contract's real writer
transaction and real reader over one shared FakeFirebaseFirestore:
- match confirm -> profile history (singles uid-keyed, doubles pairId-keyed)
- facility status flip -> map FacilityCubit listener
- queue join transaction -> match check-in derived standing

Fix QueueEntryModel.fromMap coercing position via 'as int' (a Firestore
double would throw and drop the ticket); use 'as num' like the sibling
facility numeric fields, with a regression assertion.

* docs: correct ASSISTANT_API_KEY env var and tick Phase 7 integration tasks

- Rename the stale CHATBOT_API_KEY references to ASSISTANT_API_KEY (the
  actual --dart-define the assistant reads) in README + brain docs.
- Mark the Phase 7 cross-vertical integration spine items done.

* i18n: de-literalize language switcher, add locale render guard

Move the EN/IT segmented-control labels out of settings_screen into
languageEnglishShort/languageItalianShort ARB keys (values stay EN/IT
in both locales) so no user-facing string literals remain in lib/.

Add test/l10n/locale_render_test.dart: boots the app shell in each
supported locale and asserts no RenderFlex overflow / FlutterError is
captured -- guards against longer Italian copy overflowing tight rows,
which ARB-parity checks cannot catch.

* test(integration): assert the queue vertical's facility-status writes reach the map listener; tick localization spine

Contract #2 (facility status flip -> map listens) previously only drove the
match vertical as the status writer, and the test comment wrongly called it the
'canonical' writer. joinQueue/leaveQueue are a second writer of
facilities/{id}.status. Add a test that occupies a table via a real match, then
drives the real joinQueue (occupied -> queued) and leaveQueue (queued ->
occupied) transactions, asserting the real FacilityCubit stream observes both.

Tick the Phase-7 Localization Pass spine items: the audit found no hardcoded
user-facing strings, app_en/app_it are at full 433-key parity, and
locale_render_test boots both locales without overflow.

* test(match): add fake_cloud_firestore integration coverage for the full scan-to-ELO flow

Drives createMatch → joinMatch → submitResult → confirmResult as one
continuous sequence for singles and doubles, asserting the shared
contracts (match doc shape, facility status flip, queue position)
hold across the whole chain rather than at isolated states.

* docs(design): catalog screen mockups from the CampusArena design system

Adds §5.4 to the design document, referencing docs/design/CampusArena.dc.html
as the mockup source (no separate Figma file) and embedding the
docs/design/screenshots/ catalog grouped by the §5.2 screen inventory.

* ci(coverage): exclude generated l10n and firebase_options from the metric

The l10n string tables (app_localizations_en/it.dart) and firebase_options.dart
are machine-generated and contribute ~557 permanently-uncovered lines, dragging
the reported total well below the code that is actually tested. Strip them with
lcov --remove after the coverage run so the number reflects hand-written code.

* test(chatbot): cover llm_client, assistant-settings cubit, repo intents, tool_results

- llm_client_test: MockClient-backed coverage of every provider transport
  (OpenAI/OpenRouter/Regolo/Anthropic/Gemini), HTTP error-code mapping,
  timeout/socket/generic failure -> LlmException, and testConnection.
- assistant_settings_cubit_test: load, provider/model/key edits, testConnection
  success+failure, and save persistence.
- chatbot_repository_test: extend to the tournaments/queue/general intents,
  non-empty grounding, and per-user personalization context.
- tool_results_test: value-equality of the four typed tool results.

* test: foosball result-entry widget and forgot-password form

Closes the two spine-named gaps: the FoosballResultEntry widget (its strategy
logic was tested but the stepper UI was not) and the password-reset auth form
(validation, submit, success notice, and error path).

* test: widget coverage for team profile, assistant settings, and small widgets

- team_profile_screen: loading/error/ready states, two-member header, ELO chips
  (rated + placeholder), and empty vs populated match history.
- assistant_settings_screen: banner, provider-change reset prompt, key visibility
  toggle, save, and the missing-key Test Connection path (no network).
- confirm_dialog, result_message_view, typing_indicator, chatbot tournament_card:
  render + interaction tests.
- ci: lcov 2.5 needs --ignore-errors unused,empty for the filter step.

* test(match,tournaments): cover match-flow routing and organizer sheets

- match_flow_page: MatchFlowView builder across idle/active/readyToConfirm
  (deep-link + far-blind -> setup, nearby-blind -> auto check-in) and the
  error listener (snackbar + scanner re-arm). Was 0% covered.
- organizer_sheets: forfeit and advance bottom sheets, empty and populated
  paths through to slot/winner selection.

* test: cover doubles active-match, result-flow terminal phases, privacy

- active_match_screen: doubles roster (join slots + rostered tiles + waiting
  slots), resultSubmitted (submitter vs reviewer CTA), and disputed states.
- result_flow_page: ResultFlowView loading/closed(3 kinds)/error branches with
  routing + retry.
- privacy_screen: static-content render in both locales.

* test: cover result summary, QR manual entry, and tournament browse/detail

- result_summary_view: win/loss headers, animated ELO block, rank pill (singles
  only), achievement reveal, and Done -> home.
- qr_scan_screen: manual fallback-code entry (submit + empty-ignore) and the
  busy overlay.
- tournament_browser: empty state, ongoing section, and stream-error retry.
- tournament_detail: stream-error retry, register-bar registered/full labels,
  and the organizer cancel menu + confirm dismiss.

* test(result): cover the opponent-confirmation ReviewView

Both variants (submitter waiting footer vs opponent Confirm/Dispute bar), the
confirm action, the dispute confirmation dialog, the in-flight spinner, and the
doubles team-orientation of the score card.

* ci(coverage): surface an lcov report and a coverage summary

Adds an inline lcov report (read-lcov-report-action) and a totals block written
to the job's Summary tab, both reading the generated-file-filtered lcov.info.

* polish(ui): scan-lock haptic, queue transitions, status-card theme tokens

- QR capture fires HapticFeedback.selectionClick so a lock-on is felt
  while the phone is over the table, not just shown as a spinner.
- Queue section animates height (AnimatedSize) and cross-fades the
  position counter as the line advances, instead of snapping.
- FacilityStatusCard reuses the canonical facilityStatusColor helper
  (error/tertiary/primary roles) instead of raw Colors.green/red/amber.

* docs(task-board): mark Phase 7B testing + responsive polish done

* seed(firestore): fold real facilities into populate script with mixed statuses

Facility docs are now seeded from the hand-verified production data
(coordinates, QR codes, fallbackCodes, isOutdoor) instead of being treated
as read-only prerequisites. Statuses are mixed for the demo: fac_fb is
occupied by the active tournament's live semifinal, leo_gardella_pp_2 is
queued (walk-up occupant + two waiting players), every other table free so
the Assistant 'empty tables' query stays rich. queue subcollections cleared
on wipe; facility docs overwritten, never deleted, to keep FK IDs stable.

Marks the seeding-script and demo-dataset task-board items done.

* fix: CI pipeline

---------

Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* feat: design document structure

* feat: starting design document and images

* fix: merged other files into the design document

* docs: trim conclusions and constraints sections

- Drop the course-rules bullets from the constraints section (no
  social network / not a business app), keeping only genuine technical
  constraints; rename the section to Constraints.
- Remove the Course Requirement Compliance table from Conclusions.
- Remove the Effort Spent section and table.
- Strip development-timeline and verification-run dates so the document
  no longer states the build window.

* docs: swap screenshots for light/dark, en/it mockups across screen catalog

* docs: rewrite Chapter 1 (Introduction) tone and structure

Drop exam/course-compliance framing in favor of plain product
rationale, trim the Core/Sport-Specific split down to one sentence
(detail already lives in Ch.3), cut the placeholder hero figure and
the Reference Documents section (duplicates Ch.8 References), and
reflow the Scope feature list out of a single run-on sentence.

* docs: fix Chapter 2 accuracy and tone, wire in use-case diagram

Notification Dispatch previously claimed tournament bracket advancement
as a third client-local alert class; the app only fires an alert for
queue-front and Direct Challenge, bracket progress is passive live-listener
UI. Also dropped grader-facing phrasing from the Screen Inventory intro,
and swapped the use-case placeholder for the exported diagram.

* docs: fix Chapter 3 tone, drop unverifiable claim, wire in diagrams

Dropped grader-facing phrasing from the Trust Boundary section and an
unverifiable historical claim about queue ticket renumbering. Swapped
the architecture and ER placeholders for the exported diagrams.

* docs: fix Chapter 3 BracketEngine naming, wire in match-state diagram

BracketEngine was referenced as a class alongside real singleton classes
(EloCalculator, AchievementEvaluator), but bracket_engine.dart is a
top-level function/data-class module with no such class. Corrected the
two Ch.3 mentions and wired in the match-state-machine diagram, the
third figure in this chapter that was still a placeholder.

* docs: fix Chapter 2 screen count and BracketEngine naming

Same audit that flagged Ch.3's BracketEngine issue found it leaking into
Ch.2's traceability table, and found "Profile Setup" still counted in the
Screen Inventory — that screen was cut in Phase 2B (task_board.md) and
never existed as a route or file. Corrected the count to 29 and
renumbered the table.

* docs: adjust match-state diagram routing

* docs: rewrite Chapter 4 (Implementation Details) tone and wire in diagrams

Fixed a stale /onboarding/profile-setup route reference (now
/onboarding/tutorial), dropped two grader-facing tone violations, reworded
a TA-familiarity line and an overclaimed disclaimer-reuse sentence.
Authored confirm_txn and queue_sequence as hand-written .drawio sources
and wired both figures in.

* docs: rewrite Chapter 5 (UI/UX) screen catalog and wire in navigation diagram

Fix standings/settings mockup mix-up in the screen catalog (standings_en_dark
is actually Tournament Detail, settings_en_dark is actually Settings), drop
the Profile setup mockup for a screen that no longer exists in the app, and
author the navigation map as a drawio diagram in place of the placeholder.

* docs: fix Chapter 6 test counts, inventory table, and BracketEngine naming

* docs: fix Chapter 7 Future Work Basketball squad-size claim

* feat: added app icon

* docs: finalize design document, add user manual, presentation outline, and README deliverables

- Finalize main.tex: page-breakable tech-stack and traceability tables
  (xltabular), wrap long snake_case identifiers, correct title/release
  date, wire diagram figure paths, foosball indoor/outdoor parity.
- Add compiled design_document.pdf and exported diagram PNGs
  (architecture, ER, use-case, match-state, queue-sequence, confirm-txn,
  navigation).
- Add user_manual.md and presentation_outline.md.
- README: point /docs at the final deliverables (design_document.pdf,
  presentation.pdf, user_manual.md, design system).
- task_board: mark Spine design-doc/demo/README items complete.

* docs: fix user manual profile-setup screen and presentation stale test/screen counts

Address Christian's PR #32 review:
- user_manual §1.3: drop the non-existent Profile Setup screen (no avatar
  picker exists; permissions are requested at point-of-use), fold the tutorial
  into a single onboarding step
- presentation_outline: correct test-case count 739 -> 864 (slides 10, 11)
  and screen count 30 -> 29 (slide 11) to match the audited main.tex figures

---------

Co-authored-by: Christian Prendin <christianprendin2002@gmail.com>
* fix: changed app icon

* feat: added final presentation
@M1RK02
M1RK02 requested a review from ChristianPrendin July 13, 2026 13:41

@ChristianPrendin ChristianPrendin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did it lesssggoooo

@github-actions

Copy link
Copy Markdown

LCOV of commit 9730c55 during Flutter CI #114

Summary coverage rate:
  lines......: 90.4% (9427 of 10432 lines)
  functions..: no data found
  branches...: no data found

Files changed coverage rate: n/a

@M1RK02
M1RK02 merged commit 3590fc9 into main Jul 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants