Overview & Architecture

Version: v1.7 | Last Updated: 2026-04-18


1. Scope

This wiki defines the complete Pickatale learning platform β€” every service, data model, API contract, compliance rule, and build gate. It is the single source of truth for Claude Code agents.

Rule: If it is not in this wiki, it does not exist. Do not infer, assume, or default.


2. Product Overview

Source: (D) Sig Dug | Status: Confirmed

Pickatale is a children's reading platform (ages 4–11) built on an invisible adaptive learning loop:

  1. Curriculum Mapper (CM) defines what a child should learn per territory/year
  2. Reader App delivers books the child wants to read
  3. Telemetry captures behavioral signals (page times, word taps, quiz scores)
  4. Learner Bot detects gaps, generates reports, recommends next content
  5. Adaptive Content Engine re-levels any story to a child's exact FK grade
  6. Teacher Portal shows curriculum progress, flags struggling readers
  7. Parent Portal delivers warm digests: "Sofia had a great reading day!"

The child just reads stories they love. The platform handles the rest invisibly.

Platform Pillars

Pillar Implementation
Invisible learning Reinforcement woven into stories the child already wants to read
Per-child AI agent Learner Bot with RAG memory per learner
Cross-nation analytics School β†’ District β†’ Nation β†’ Global roll-ups
All content is adaptive Static content is an intermediate state only
Modular APIs Every module can be sold/licensed independently

Key URLs

Service URL Port
Reader App app.readingtester.com 3125
Teacher Portal teacher.readingtester.com 3116
Parent Portal parents.readingtester.com 3118
Account Center account.readingtester.com 3126
Curriculum Mapper cm.readingtester.com 3117
Adaptive Engine adapt.readingtester.com 3119
Learner Bot (internal only) 3120
Telemetry (internal only) 3110
LRS (internal only) 3111
Content Service (internal only) 3112
Analytics (internal only) 3114
Wiki wiki.readingtester.com β€”

3. System Architecture Map

Source: Code audit 2026-04-18 | Status: Confirmed

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    CADDY (HTTPS)                    β”‚
β”‚           reverse proxy for all services            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚              β”‚                  β”‚
   [Reader App]  [Teacher Portal]  [Account Center]
   port 3125      port 3116           port 3126
   React+tRPC     Next.js+Express      Node+Express
        β”‚              β”‚                  β”‚
        β”‚         [Parent Portal]    [Subscriptions]
        β”‚          port 3118          Stripe Webhooks
        β”‚
   [Telemetry]──────→[Learner Bot]──────→[Teacher Portal]
   port 3110           port 3120           (enrichment)
        β”‚                  β”‚
        ↓                  ↓
      [LRS]        [Adaptive Engine]
   port 3111         port 3119
                    (FK leveling)
                         β”‚
                  [Content Service]
                   port 3112

All services share: MySQL 8 (shared Docker instance, internal access only)
All services read identity: Account Center session API
All child identity: UUID learner_id from Teacher Portal students table

Inter-Service Communication Rule (Non-Negotiable)


4. State Definitions

Source: Code audit | Status: Confirmed

User States (users.state)

State Meaning
pending_verification Registered, email not yet verified
active Normal operating state
suspended Admin-suspended, cannot login
archived Soft-deleted, all data retained

Student States (students.state)

State Meaning
created Account created, PIN assigned, not yet activated
pending_coppa (removed v1) Under-13 β€” awaiting parental consent
active Has logged in at least once
inactive No sessions in 30+ days
archived Soft-deleted or consent revoked

Subscription States

State Meaning
trialing 14-day free trial, full features
active Paid, full features
past_due Payment failed, grace period (7 days), full features
expired Grace period over, locked to free tier
cancelled Voluntarily cancelled, access until period end

Reading Session States

State Meaning
open Book opened, reading in progress
completed session_ended received with all pages
abandoned Closed without completing OR force-abandoned by cron after 24h

5. Build Order

Source: (D) Sig Dug | Status: Confirmed

Phase 1 β€” Identity & Auth (Account Center)

  1. users table + registration + email verification
  2. Login + session (uc_session cookie)
  3. Forgot password + reset
  4. School creation
  5. Membership (teacher β†’ school)

Phase 2 β€” Roster & Reading

  1. Class creation (Teacher Portal)
  2. Student add/bulk-import + PIN generation
  3. Child login (Reader App)
  4. Book open β†’ reading session β†’ telemetry
  5. Session end β†’ miles/tokens update

Phase 3 β€” Intelligence Loop

  1. Placement test
  2. Quiz β†’ Learner Bot feedback
  3. Telemetry β†’ Bot (vocab gaps + session summary)
  4. Nightly bot run β†’ teacher report
  5. Parent Portal digest

Phase 4 β€” Billing & Compliance

  1. Stripe integration + subscription lifecycle
  2. Entitlement enforcement across all services
  3. GDPR export + deletion
  4. Admin APIs + impersonation

6. Open Questions

# Question Owner Status
OQ-1 Azure Neural TTS β€” awaiting Sig portal.azure.com signup Sig Pending
OQ-2 SpeechAce phoneme API (fluency) β€” awaiting Borja Borja Pending
OQ-3 Staging environment β€” not yet built Jixian MUST BUILD
OQ-4 CI/CD pipeline β€” not yet built Jixian Phase 4
OQ-5 Night theme in Aa panel β€” keep or remove? Sig Pending decision
OQ-6 Class name display bug (Year3Blue β†’ "Year 3 Red") β€” root cause unknown Jixian Active

End-to-End System Flow (Authoritative)

This is the single canonical flow from school onboarding to reporting. Claude Code must be able to implement every step from this section alone. For detailed contracts, follow the page references in each step.

Phase 1: School Onboarding

  1. school_admin registers at teacher.readingtester.com β†’ POST /api/auth/register (Account Center) β†’ creates users row (role=school_admin, state=pending_verification) + schools row β†’ sends EMAIL_VERIFICATION email
  2. school_admin verifies email β†’ GET /api/auth/verify?token=... β†’ users.state=active β†’ DPA acceptance screen shown
  3. school_admin accepts DPA β†’ POST /api/schools/dpa β†’ INSERT school_dpa_agreements (school_id, user_id, accepted_at, ip_address) β†’ redirect to dashboard
  4. school_admin invites teacher β†’ POST /api/schools/invite (email, role=teacher) β†’ INSERT invites (token, email, school_id, role, expires_at=48h) β†’ sends TEACHER_INVITE email
  5. teacher accepts invite β†’ clicks link β†’ POST /api/auth/accept-invite (token, password) β†’ INSERT users (role=teacher, school_id) β†’ INSERT subscriptions (state=trialing, trial_ends_at=now()+14d) β†’ redirect to teacher portal

Authoritative detail: Page 11 (Sign-Up Flows), Page 03 (Identity)

Phase 2: Class & Student Setup

  1. teacher creates class β†’ POST /api/classes (name, year_level, school_id) β†’ INSERT classes (id, teacher_id, school_id, license_id from teacher_licenses) β†’ return class_id
  2. teacher imports students (CSV) β†’ POST /api/students/import (class_id, rows[]) β†’ for each row: INSERT students (username=auto-generated, pin=4-digit-random, school_id, teacher_id, class_id, learner_id=UUID) β†’ INSERT class_memberships (student_id, class_id) β†’ return login cards (username + PIN + QR)
  3. teacher prints login cards β†’ GET /api/classes/:id/login-cards β†’ renders card HTML with username, 4-digit PIN, QR code linking to app.readingtester.com?user=username. PIN shown once only β€” not retrievable after dismissal.

Authoritative detail: Page 05 (Roster), Page 11 (Sign-Up Flows Flow D)

Phase 3: Child First Login

  1. child enters username + PIN at app.readingtester.com β†’ POST /api/auth/child-login (username, pin) β†’ verify students table β†’ INSERT device_sessions (learner_id, device_id, policy=shared, created_at) β†’ return session JWT
  2. placement test shown β†’ child reads 3 calibration passages β†’ POST /api/placement (session_id, responses[]) β†’ calculate FK grade β†’ UPDATE students (placement_results JSON, reading_level DECIMAL) β†’ child lands on library
  3. library renders β†’ GET /api/recommendations?learner_id=... β†’ Learner Bot returns personalised book list β†’ fallback: Content Service catalog filtered by child's FK level

Authoritative detail: Page 11 (Sign-Up Flows Flow E), Page 06 (Reading & Telemetry Section 7.1)

Phase 4: Reading Session

  1. child opens book β†’ Reader App β†’ POST /api/telemetry/events (event_type=book_opened, learner_id, session_id=new UUID, book_id, client_ts) β†’ Telemetry stores in events table (deduplicated by event_id)
  2. child reads pages β†’ each page turn: POST /api/telemetry/events (event_type=page_turned, page_number, time_on_page_ms) β†’ stored offline if no connection, replayed via Service Worker on reconnect
  3. child taps word β†’ POST /api/telemetry/events (event_type=word_tapped, word) β†’ definition popup shown from Content Service
  4. child completes book β†’ POST /api/sessions/end (session_id, client_ts) β†’ Reader App calculates miles (words_read / 100) β†’ PATCH /api/progress/:learner_id (books_read+1, total_miles+N) β†’ Telemetry fires two parallel calls (5s timeout each): POST /learner-bot/api/v1/telemetry/vocab-taps AND POST /learner-bot/api/v1/telemetry/session-summary
  5. Service Worker guarantees delivery: events POST offline β†’ IndexedDB queue β†’ auto-replay on reconnect via BackgroundSync. Server deduplicates on event_id (UUID, 7-day retention).

Authoritative detail: Page 06 (Reading & Telemetry), Section 6 (Events)

Phase 5: Learning Loop (Nightly)

  1. 04:00 UTC β€” Learner Bot cron fires β†’ for each active learner: fetch telemetry (90-day window), load RAG memories (vocab_gaps, reading_patterns, curriculum_state), run gap analysis
  2. bot generates teacher report β†’ POST /api/v1/bot/:learner_id/report β†’ stores in bot_reports β†’ Teacher Portal dashboard updated
  3. bot detects struggling reader β†’ avg quiz score < 65% on 2+ quizzes β†’ INSERT intervention_queue (learner_id, reason, teacher_id) β†’ Teacher Portal attention list updated
  4. bot recommends next book β†’ UPDATE bot_memories (type=recommendation, content_id, reason) β†’ Reader App next-session library shows "Picked for You" card
  5. on failure: INSERT failed_runs (learner_id, error, attempted_at) β†’ 05:00 UTC re-run cron retries failed learners. 3 retries with exponential backoff (1s, 4s, 16s). After 3 failures β†’ alert_log.

Authoritative detail: Page 06 Section 7 (Telemetry β†’ Bot), Learner Bot service (port 3120)

Phase 6: Entitlement Enforcement

  1. every feature-gated API call β†’ service calls checkEntitlement({user_id, school_id, student_id}) β†’ checks in-process cache (60s TTL) β†’ if miss: GET /api/billing/entitlement (Account Center, X-Internal-Key, 2s timeout)
  2. entitlement resolution order: (1) entitlement_grants table (gifted/admin grants) β†’ if found, tier=full. (2) subscriptions table: trialing+valid β†’ full; active+valid β†’ full; past_due+grace β†’ full; else β†’ free. (3) default β†’ free.
  3. timeout fallback: if Account Center >2s β†’ tier=free (fail-safe). INSERT audit_log (action=entitlement_check_failed). Child can still read 50 free books. Learner Bot paused. Reports not generated.
  4. subscription expires: hourly cron β†’ subscriptions WHERE state=past_due AND grace_ends_at < now() β†’ UPDATE state=expired β†’ UPDATE students.entitlement_tier=free β†’ INSERT audit_log β†’ send SCHOOL_SUBSCRIPTION_EXPIRED email
  5. parent linked (optional): teacher invites parent β†’ parent accepts β†’ INSERT parent_links (parent_id, student_id, school_id, teacher_id, approved_at) β†’ parent can view progress at parents.readingtester.com (read-only). Parent NEVER creates a new child record.

Authoritative detail: Page 04 Section 29.3–29.7 (Billing)

End of authoritative E2E flow. Every step above maps to a specific page and section. If a build question arises that is not answered by following these steps to their referenced sections, it is a spec gap β€” do not invent behavior.