Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_ANON_KEY=your-anon-key-here
GEMINI_API_KEY=your-gemini-api-key-here
# GEMINI_API_KEY is intentionally NOT a client variable. It lives only in the
# `gemini-proxy` Supabase Edge Function — set it via
# `supabase secrets set GEMINI_API_KEY=...`. See
# supabase/functions/gemini-proxy/README.md.
HUGGINGFACE_TOKEN=your-huggingface-read-token-here
36 changes: 36 additions & 0 deletions docs/audit/2026-05-14/00_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Kudlit Full Audit — 2026-05-14 (Index)

Multi-agent orchestrated audit produced via `docs/kudlit_full_audit_prompt.md`. Seven specialist lanes ran in parallel; this index links to each lane's findings, the synthesis, and the executive summary.

## Start Here

- **[EXECUTIVE_SUMMARY.md](./EXECUTIVE_SUMMARY.md)** — one-page verdict, top strengths/risks, readiness call.
- **[99_top10_improvements.md](./99_top10_improvements.md)** — the prioritized ship list, severity-then-effort.

## Lane Reports

| # | Lane | File | Headline finding |
|---|---|---|---|
| 01 | UX/UI per-screen, mobile-first | [`01_ux_screens.md`](./01_ux_screens.md) | 29 screens audited (11 P0 / 34 P1 / 27 P2). Welcome card still says "Authentication is UI-only for now." despite working Supabase auth. |
| 02 | Multiplatform parity (Android / iOS / Web) | [`02_multiplatform.md`](./02_multiplatform.md) | Every `sqflite` datasource imports without a `kIsWeb` guard — web crashes on first cache hit. Live YOLO is mobile-only; web is single-frame capture. |
| 03 | Architecture & code quality | [`03_architecture.md`](./03_architecture.md) | 24 sites import `data/` from `presentation/` — clean-architecture inward-only rule systemically broken. Scanner domain has no `Either<Failure, T>` and no use cases. |
| 04 | Performance, offline-first, integrations | [`04_performance_offline.md`](./04_performance_offline.md) | `ScanTab` never disposes — PageView keeps all four tabs alive, so YOLO inference runs for the app's lifetime. The recent "pause on result" commit only gated the dispatch, not the native model. |
| 05 | Security & privacy | [`05_security_privacy.md`](./05_security_privacy.md) | `GEMINI_API_KEY` ships in the client bundle; phone OTP has no client-side rate limit; password recovery deep link has no `AuthChangeEvent.passwordRecovery` handler. |
| 06 | Accessibility | [`06_accessibility.md`](./06_accessibility.md) | 3 P0 WCAG AA contrast failures; primary profile/mic buttons have zero semantic labels; zero widgets honor `MediaQuery.disableAnimations`. |
| 07 | Navigation, IA & visual language | [`07_nav_ia_visual.md`](./07_nav_ia_visual.md) | Two 4-tab navs coexist — one live (`FloatingTabNav`), one orphaned (`AppBottomNav` + `HomeTab` + `ProfileTab`). `/admin/stroke-recorder` is reachable by any signed-in user. |

## How this audit was produced

The orchestrator ran in three phases per `docs/kudlit_full_audit_prompt.md`:

1. **Phase A (parallel)** — 3 Explore subagents mapped surface, data/integrations, and design system in one fan-out.
2. **Phase B (parallel)** — 7 lane auditors ran in a single fan-out, each writing its own markdown.
3. **Phase C (synthesis)** — a Plan subagent drafted the Top-10; the lead wrote this index and the executive summary.

Every finding cites `file_path:line`. No fabricated metrics. Prior audits in `docs/` were reconciled per each lane's Methods footer.

## What's NOT included

- No performance numbers (frame rate, bundle size, memory) — would require a profiling run.
- No browser-specific manual QA — findings come from code review and platform-branch tracing.
- No live Supabase RLS policy verification — code-side assumptions are flagged for backend review.
478 changes: 478 additions & 0 deletions docs/audit/2026-05-14/01_ux_screens.md

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions docs/audit/2026-05-14/02_multiplatform.md

Large diffs are not rendered by default.

155 changes: 155 additions & 0 deletions docs/audit/2026-05-14/03_architecture.md

Large diffs are not rendered by default.

204 changes: 204 additions & 0 deletions docs/audit/2026-05-14/04_performance_offline.md

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions docs/audit/2026-05-14/05_security_privacy.md

Large diffs are not rendered by default.

180 changes: 180 additions & 0 deletions docs/audit/2026-05-14/06_accessibility.md

Large diffs are not rendered by default.

166 changes: 166 additions & 0 deletions docs/audit/2026-05-14/07_nav_ia_visual.md

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions docs/audit/2026-05-14/99_top10_improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Top 10 Prioritized Improvements — Kudlit Audit 2026-05-14

**Synthesizer:** Plan agent · **Sources:** 7 lane reports in this directory

## Method
- Pulled the P0 and highest-impact P1 items from each lane.
- Sorted by severity (P0→P1→P2) then effort (S→M→L).
- Every row has Evidence (`file_path:line`) drawn from the source lane file.

## Top 10

| # | Lane | Area | Severity | Effort | Recommendation | Evidence |
|---|---|---|---|---|---|---|
| 1 | Security | OTP brute-force defense | P0 | S | Implement client-side OTP resend cooldown (30–60s) and a 5-attempt verify lockout with backoff; remove the dead `_resendCooldown=0` field and dead cooldown branch. | `lib/features/auth/presentation/screens/phone_otp_screen.dart:46,95,134-162` (from `05_security_privacy.md`) |
| 2 | Security | Password recovery deep link | P0 | S | Wire `AuthChangeEvent.passwordRecovery` to a dedicated reset screen that forces `updateUser(password:)`; stop reusing `/auth/reset` for both OAuth callback and recovery. | `lib/app/router/app_router.dart:138-142`; `lib/features/auth/data/datasources/supabase_auth_datasource.dart:179` (from `05_security_privacy.md`) |
| 3 | Nav/IA | Admin route exposure | P0 | S | Gate `/admin/stroke-recorder` at the router with a role check (`is_admin` from `profile_summary`), not just at the Settings tile. | `lib/app/router/app_router.dart:163-167`; `lib/features/home/presentation/widgets/settings/admin_section.dart:63` (from `07_nav_ia_visual.md`) |
| 4 | Accessibility | WCAG AA contrast — TextField hints | P0 | S | Raise TextField hint alpha from `withAlpha(110)` to `withAlpha(160)` (≈4.5:1 on light surface); darken `subtleForeground` token so 12pt `bodySmall` clears AA. | `lib/features/home/presentation/widgets/butty_chat/chat_input_bar.dart:47-50`; `lib/features/home/presentation/widgets/translate/text_input_box.dart:45-48`; `lib/core/design_system/kudlit_colors.dart:24` (from `06_accessibility.md`) |
| 5 | UX | Auth welcome credibility | P0 | S | Delete the "Authentication is UI-only for now." caption from the welcome card — it contradicts the working Supabase backend at the most fragile point in the funnel. | `lib/features/auth/presentation/screens/auth_welcome_screen.dart:62` (from `01_ux_screens.md`) |
| 6 | Multiplatform | Web password reset redirect | P0 | S | Provide explicit `redirectTo: '${Uri.base.origin}/auth/reset'` for password reset on web (currently `null`) and register an in-app `/auth/reset` handler. | `lib/features/auth/data/datasources/supabase_auth_datasource.dart:179`,`:85-87` (from `02_multiplatform.md`) |
| 7 | Security | Bundled cloud API key | P0 | M | Move Gemini API calls behind a server proxy (Supabase Edge Function); remove `GEMINI_API_KEY` from the client bundle so it cannot be extracted from the APK/web bundle. | `lib/features/translator/presentation/providers/translator_providers.dart:54-55`; `lib/features/translator/data/datasources/cloud_gemma_datasource.dart:47` (from `05_security_privacy.md`) |
| 8 | Performance | YOLO inference leak | P0 | M | Stop YOLO inference when ScanTab is off-screen — PageView keeps it alive. Either gate `ScannerCamera` mount on `_activeTab == AppTab.scan`, or call `detector.pauseInference()` on tab change. | `lib/features/auth/presentation/screens/home_screen.dart:122-145`; `lib/features/scanner/data/datasources/yolo_baybayin_detector.dart:155-159` (from `04_performance_offline.md`) |
| 9 | Multiplatform | Web data layer crash | P0 | M | Add web-stub or `sqflite_common_ffi_web` adapters for every `sqflite` datasource so chat history, scan history, lesson progress and profile cache do not crash on web; currently no `kIsWeb` guard around construction. | `lib/features/translator/data/datasources/sqlite_*.dart`; `lib/features/scanner/data/datasources/sqlite_scan_history_datasource.dart`; `lib/features/learning/data/datasources/sqlite_lesson_progress_datasource.dart`; `lib/features/home/data/datasources/local_profile_management_datasource.dart` (from `02_multiplatform.md`) |
| 10 | Architecture | Scanner failure surface | P0 | L | Add `Either<Failure, T>` to the scanner domain — wrap detect/capture/torch/switch/pause/resume in typed failures and add a `scanner/domain/usecases/` directory; today every error in the highest-failure-rate feature bubbles as a raw exception. | `lib/features/scanner/domain/repositories/baybayin_detector.dart:16-32` (from `03_architecture.md`) |

## Why these 10
Every item is a P0 from its source lane and together they span all seven lanes — Security (2), Multiplatform (2), plus one each from UX, Nav/IA, Accessibility, Performance, and Architecture. The ordering is severity-then-effort: six S-effort wins precede three M-effort fixes, and the single L-effort refactor closes the list. Deferred-but-tempting P0s — consolidating Login/Welcome (UX, M), failed-write reaper (Perf, M), deleting orphan nav widgets (Nav/IA, S), reduced-motion helper (A11y, M), guarding `initializeFlutterGemma` on web (Multiplatform, M), and the presentation→data import refactor (Architecture, L) — were held back because they either duplicate the lane coverage already chosen here or depend on one of the Top 10 landing first.

## Companion Sections
- Item 1 → `05_security_privacy.md` § "Phone OTP rate limit / lockout"
- Item 2 → `05_security_privacy.md` § "Password reset deep-link safety"
- Item 3 → `07_nav_ia_visual.md` § "Routing red flags"
- Item 4 → `06_accessibility.md` § "Contrast (WCAG AA)"
- Item 5 → `01_ux_screens.md` § `auth_welcome_screen.dart`
- Item 6 → `02_multiplatform.md` § "Auth deep links"
- Item 7 → `05_security_privacy.md` § "Secrets / env handling"
- Item 8 → `04_performance_offline.md` § "Inference cadence & camera lifecycle"
- Item 9 → `02_multiplatform.md` § "Platform idiom mismatches"
- Item 10 → `03_architecture.md` § "Error handling (Either<Failure, T>) coverage"
23 changes: 23 additions & 0 deletions docs/audit/2026-05-14/EXECUTIVE_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Kudlit — Executive Summary (Audit 2026-05-14)

## Overall readiness

**`needs-polish`** — Kudlit's product surface and offline architecture are genuinely strong, but a small number of P0 issues block a confident public release. None are intractable; nine of the Top 10 are S/M-effort and can be cleared in a focused sprint. The single L-effort item (scanner domain `Either<Failure, T>` refactor) is structural and can be sequenced after the rest.

## Top 3 strengths

1. **Butty chat memory architecture is sound and verified.** Two-layer split (episodic chat history + semantic memory facts with a `normalized` UNIQUE index), 20-turn sliding window, and "Start fresh" that preserves memory all behave as designed. See `04_performance_offline.md` § Butty chat memory.
2. **Design tokens are real, the theme is wired through `MaterialApp.router`, and shared shells (`KudlitAuthShell`, `HomeTopbar`, `FloatingTabNav`) are reused consistently across auth and home.** Light/dark variants are both present. Many of the visual fragmentation issues called out by Lane 7 are inline-duplication of *existing* tokens, not missing tokens — i.e., the fix is mechanical.
3. **Clean Architecture's domain boundary is held cleanly.** Lane 3 found zero `package:flutter` imports inside `domain/`, single-quote and trailing-comma compliance is solid, and there are no `_buildXxxWidget()` private UI helpers — the codebase already enforces the harder rules in `CLAUDE.md`.

## Top 5 risks

1. **`GEMINI_API_KEY` is shipped in the client bundle** (`translator_providers.dart:54-55`). Anyone can extract it from the APK or web bundle. Must move behind a server proxy before any public release.
2. **YOLO inference runs forever.** `HomeScreen` mounts all four tabs in a PageView (`home_screen.dart:122-145`); the native YOLO model keeps detecting even when the user is in Translate/Learn/Butty. Battery, heat, and a recent "pause on result" commit (7f28abc) only papered over the dispatch — not the model.
3. **Web is silently broken in the data layer.** Every `sqflite` datasource is constructed without a `kIsWeb` guard; the first cache read on web will throw `MissingPluginException`. Compounding this, password reset on web passes `redirectTo: null`, so flow correctness depends on Supabase Site URL configuration that is invisible to the code.
4. **Auth is one mis-step from a take-over.** Password recovery deep link establishes a Supabase session but has no `AuthChangeEvent.passwordRecovery` handler and no forced-password-update screen — if a recovery email leaks, the link is effectively a "log in as me." Combined with no client-side OTP cooldown (`phone_otp_screen.dart:46` has a dead `_resendCooldown = 0`), the auth surface is the weakest part of the app.
5. **Admin route is reachable by any signed-in user.** `/admin/stroke-recorder` is only hidden in Settings; the router has no role guard (`app_router.dart:163-167`). A typed URL bypasses the entire affordance.

## One-paragraph verdict

Kudlit is a well-shaped, opinionated app that has clearly been built with care — the offline-first memory architecture, the unified design system, and the held-firm Clean Architecture boundary on `domain/` are above-average for a Flutter project of this size. The risks blocking ship cluster narrowly in three areas: an exposed cloud key, a leaking inference loop, and a soft auth surface (password reset handler, OTP throttling, admin route guard). Fix the Top 10 in `99_top10_improvements.md` — six are ≤1-day changes, three are 1–3 days, one is the larger scanner-domain refactor — and Kudlit moves from "needs-polish" to "ship-ready" inside a single focused sprint. Defer the broader presentation→data refactor (24 sites) to a follow-up release; it is real debt but not blocking.
Loading
Loading