# AGS Vocab

An adaptive vocabulary assessment in the All Gifted family. Measures the words a learner knows on the **Vocabile scale** (0–1500, mapped to grade bands K → Beyond G12) using **Item Response Theory** (3PL) and **Computerized Adaptive Testing**.

- **Backend:** Laravel 11 (PHP 8.2) + MySQL + Filament v5 admin
- **Mobile + web:** Flutter 3.38 (Web compiled today; Android / iOS / Desktop ready)
- **Auth:** Sanctum bearer tokens with **OTP-only sign-in** (mirrors AGS Math)
- **Mail:** Namecheap Private Email (SMTP) — configurable from the admin

---

## Where things are documented

| Topic | File |
|---|---|
| Overview, quick-start, what's in the box | **this README** |
| **Full technical spec** — every module, method, table, formula | [docs/technical-spec.md](docs/technical-spec.md) |
| **Multi-tenancy architecture** — SaaS design, scoping, tenant resolution, AGS attribution | [docs/multi-tenancy.md](docs/multi-tenancy.md) |
| **Classrooms, enrolment, roles** — QR-driven join flow, per-classroom roles, permission matrix | [docs/classes-and-enrolment.md](docs/classes-and-enrolment.md) |
| **School admin user manual** — task-oriented, non-technical | [docs/admin-manual.md](docs/admin-manual.md) |
| **Student user manual** — learner-friendly walkthrough | [docs/student-manual.md](docs/student-manual.md) |
| System architecture + ER diagram | [docs/architecture.md](docs/architecture.md) |
| Admin operations (developer-flavoured quick reference) | [docs/admin-guide.md](docs/admin-guide.md) |
| Adding features (seeders, migrations, services, screens…) | [docs/developer-guide.md](docs/developer-guide.md) |
| Every API endpoint with request/response shapes | [docs/api-reference.md](docs/api-reference.md) |
| IRT engine, scoring, 3 test types, cooldowns | [docs/irt-and-test-types.md](docs/irt-and-test-types.md) |
| Read-aloud voices, accents, persona rotation, TTS bridge | [docs/voices-and-tts.md](docs/voices-and-tts.md) |
| Word bank seed pipeline + path to 5000 | [docs/word-bank.md](docs/word-bank.md) |
| **Word source rationale** — which lists we pull from + why (academic defence) | [docs/word-sources.md](docs/word-sources.md) |

Every config and table column also has a `description` shown in the Filament admin form — `/admin/configs` is the most up-to-date "what does this knob do" reference.

---

## What's in the box (current state)

**Content**
- **1458 words** across grade bands K (Kindy) through BEYOND G12, plus 18 Singlish entries
- **20 genres** classifying every word by usage domain (Everyday, Scientific, Medical, Singapore/Singlish, …)
- Per-word: POS, grade level(s), intrinsic difficulty (Common → Rare), brief definition, IPA + respelling for some, status (Public/Draft/…)
- ~1700 calibrated test questions, each tagged with **Bloom level** (1 Remember → 6 Create) and **skill** (Recognition / Recall / Production / Pronunciation)

**Test engine**
- 3 test types mirroring AGS Math:
  - **Vocab Diagnostic** — full adaptive CAT (15–40 items, stop at SE ≤ 0.30). 30-day cooldown for free users. Awards 0 kudos so the IRT walk stays unbiased.
  - **Skill Practice** — scope-limited 10-item drill. Pick by Skill / Part of Speech / Grade Level / **Genre**. Does NOT touch your canonical Vocabile score.
  - **Vocab Path** — open-ended adaptive practice with freshness penalty. **Premium only**.
- IRT model: 3PL with EAP ability estimator (61-point Gauss quadrature), Maximum Fisher Information item selector
- Scoring: `Vocabile = 650 + 200·θ`, clamped to [0, 1500]. Level lookup is DB-driven via `vocabile_levels`.

**Gamification**
- **Lives** (5/5 by default, regen 1/20min, premium = unlimited). Lost on wrong answer **except** in Diagnostic.
- **Kudos** awarded per correct answer: `(word.difficulty.rank + 1)` (1–7 kudos). Aligns with AGS Math's formula. Diagnostic awards 0.
- All gamification events written to immutable ledger tables (`kudo_events`, `life_events`) ready for cross-product sync.

**Admin (Filament v5 at `/admin`)**
- Crimson gradient sidebar + AGS logo, AGS Math visual style
- OTP-only sign-in (mirrors learner app)
- 14 resources: Words, Questions, Parts of Speech, Grade Levels, Bloom's Taxonomy, Skills, Word Difficulty, **Genres**, **Reader Voices**, Test Sessions, Kudos Events, Lives Events, Users, Publishing Statuses, **Site Configuration**
- Dashboard with stat widgets + recent sessions table
- Every config is admin-editable (branding colours/fonts/logo, mail SMTP, feature flags, school accent, …)

**Read-aloud (TTS)**
- 5 personas per accent, **14 personas across 6 accents** (US / UK / AU / SG / IN)
- Admin picks the school's accent via `/admin/configs → default_accent_code` — Flutter rotates through personas in that accent
- Each persona has a unique stickman appearance (letter, color, mood) + voice profile (pitch, rate, voice-name hints)
- Karaoke-style highlight on whichever line is being spoken
- Falls back gracefully when fancy voices aren't installed

**Sounds**
- Correct / wrong / tada (celebrate) — ported from AGS Math
- Mute toggle in app bar (persisted)

**Mail**
- Wired to Namecheap Private Email (`mail.privateemail.com:465` SSL) using `pam@allgifted.com`
- Password encrypted at rest via Laravel `Crypt`
- Each AGS deployment can re-point to its own SMTP relay by editing the config rows — no code change, no redeploy

---

## Quick start

### Prerequisites
- PHP 8.2+ with `pdo_mysql` and `intl` extensions
- Composer 2.x
- MySQL 8 (Laragon ships it)
- Flutter 3.38+ with Dart 3.10+
- Node 22+ (only if you later add Filament asset compilation; `php artisan serve` doesn't need it)

### Backend
```pwsh
# 1. Create the database
mysql -u root -p -e "CREATE DATABASE vocabile CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"

# 2. Configure .env  — DB_DATABASE=vocabile, DB_USERNAME=root, DB_PASSWORD=...

# 3. Migrate + seed (idempotent)
php artisan migrate:fresh --seed

# 4. Serve
php artisan serve --host=0.0.0.0 --port=8000
```

Admin lives at `http://127.0.0.1:8000/admin`. Sign in with `pamelaliusm@gmail.com` — the OTP arrives via the configured SMTP, or appears in `storage/logs/laravel.log` in dev mode (`local.INFO: [OTP] Code for …`).

### Flutter (web — easiest path)
```pwsh
cd mobile
flutter pub get
flutter build web --release --dart-define=VOCABILE_API_URL=http://127.0.0.1:8000/api

# Serve the built bundle (any static host works):
php -S 0.0.0.0:8001 -t build/web
```

Then open `http://127.0.0.1:8001` in an Incognito Chrome window. The platform-aware default URL falls back to `http://127.0.0.1:8000/api` on web and `http://10.0.2.2:8000/api` on Android emulator.

### Common dev gotchas
- **Old Flutter web service worker** caches the bundle aggressively. The build step deletes `flutter_service_worker.js` to prevent this; if you ever see "stale build", open Chrome DevTools → Application → Service Workers → Unregister, then hard-refresh.
- **PowerShell strips `^`** from composer constraints. Use single quotes around the version: `composer require 'vendor/pkg:^3.0'`.
- **Server bound to 127.0.0.1 only?** Use `--host=0.0.0.0` so phones/emulators on your Wi-Fi (`192.168.x.y:8000`) can reach it.

---

## Project layout

```
c:/projects/vocabile/
├── app/
│   ├── Filament/Admin/Resources/   # 15 Filament resources (Words, Questions, Genres, Voices, …)
│   ├── Http/Controllers/Api/       # AuthController, TestController, VoicesController, …
│   ├── Models/                     # 20-ish Eloquent models
│   ├── Providers/
│   │   ├── AppServiceProvider.php  # binds SiteConfig, overrides mail config at boot
│   │   └── Filament/AdminPanelProvider.php  # branding, navigation groups, render hooks
│   └── Services/
│       ├── Auth/OtpService.php         # OTP generation + delivery + verification
│       ├── Config/SiteConfig.php       # cached reader for admin configs
│       ├── Gamification/               # KudosService, LivesService, OutboundKudosSync
│       └── Irt/                        # ItemSelector, AbilityEstimator, VocabileScore, Strategies/
├── database/
│   ├── migrations/                 # ~35 migrations, one per table or schema change
│   └── seeders/
│       ├── ConfigSeeder.php
│       ├── TaxonomySeeder.php      # POS / levels / Bloom / difficulty / skills
│       ├── GenresSeeder.php
│       ├── VoicesSeeder.php        # 14 reader voices across 6 accents
│       ├── VocabileSeeder.php      # ~18 hand-crafted words with all 3 skills
│       ├── SinglishSeeder.php      # 18 Singlish entries
│       ├── BulkWordsSeeder.php     # ~1400 curated words; loads bulk_words*.php
│       ├── GenreAssignSeeder.php   # back-fills genres onto all words
│       └── data/
│           └── bulk_words*.php     # the word source files (parts 1–4 so far)
├── resources/views/filament/
│   └── admin-theme.blade.php       # injected CSS that gives Filament the AGS look
├── routes/api.php
├── mobile/                         # Flutter app
│   ├── lib/
│   │   ├── api_client.dart
│   │   ├── main.dart
│   │   ├── models/
│   │   ├── screens/                # login, home, test, results
│   │   ├── services/               # sound, read-aloud, JS-interop TTS bridge
│   │   └── widgets/                # stickman_mascot, kudos_chip, lives_header, …
│   └── web/
│       └── index.html              # contains the inline TTS bridge (window.agsTts*)
└── docs/                           # subsystem deep-dives — see top of this file
```

---

## License

Proprietary — All Gifted.

---

Last updated: this build of the project (1458 words, 14 voices, 20 genres, OTP login, Namecheap mail, AGS-themed Filament admin).
