Acceptance Criteria

Part of: Pickatale Master Build Wiki | Version: v1.7 | Last Updated: 2026-04-18


37. Acceptance Criteria

Source: (D) | Status: Confirmed as build gates. Each flow MUST pass all criteria before marked complete.**

Format: GIVEN [state] WHEN [action] THEN [result]


AC-01: Teacher Signup

GIVEN a new user visits account.readingtester.com/register with role=teacher
WHEN they submit a valid name, email, password
THEN:

GIVEN the teacher clicks the verification link
WHEN token is valid and not expired
THEN:

FAILURE: Duplicate email β†’ HTTP 409. Token expired β†’ HTTP 410 + resend button shown.


AC-02: Class Creation

GIVEN a teacher is authenticated (uc_session role=teacher)
WHEN they POST /api/v1/classes with class_name and year_level
THEN:

FAILURE: Missing class_name β†’ HTTP 422. Not authenticated β†’ HTTP 401.


AC-03: Child Login (Username + PIN)

GIVEN a child account exists (students.state != archived, locked=false)
WHEN child submits correct username + PIN at app.readingtester.com
THEN:

FAILURE: Wrong PIN 5Γ— β†’ students.locked=true, teacher_notifications INSERT, HTTP 423. Account archived β†’ HTTP 403.


AC-04: Reading Session (Book Open β†’ Session End)

GIVEN a child is authenticated and selects a book
WHEN the book is opened
THEN:

WHEN the child reads and turns pages
THEN:

WHEN child taps a word
THEN:

WHEN last page reached and session_ended fires
THEN:

FAILURE (offline): Events queued in IndexedDB via Service Worker. Auto-replay on reconnect (72h window). Session remains open until session_ended received or 24h cron force-abandons it.


AC-05: Telemetry Capture

GIVEN a reading session is active
WHEN POST /api/v1/events is called with event_type, session_id, learner_id, payload
THEN:

GIVEN the session ends
WHEN POST /api/v1/telemetry/session-summary is called
THEN:

FAILURE: If Learner Bot unreachable β†’ log error, continue (fire-and-forget). No retry.


AC-06: Nightly Report Generation

GIVEN a learner has active subscription (checkEntitlement β†’ full)
WHEN Learner Bot nightly cron fires at 04:00 UTC
THEN:

GIVEN teacher opens Teacher Portal learner view
WHEN GET /api/v1/bot/:learner_id/status is called
THEN:

FAILURE (learner on free tier): Bot skips. No report generated. Teacher Portal shows "Upgrade to view reports".


AC-07: Entitlement Enforcement

GIVEN a child's school subscription has expired
WHEN child opens a book (GET /api/books/:id/pages)
THEN:

GIVEN a child is on free tier
WHEN they attempt to start a comprehension quiz
THEN:

GIVEN a teacher's subscription transitions to past_due (payment failed)
WHEN Stripe fires invoice.payment_failed webhook
THEN:

GIVEN grace period expires (grace_ends_at < now())
WHEN expiry cron runs (every hour)
THEN:



47. Acceptance Criteria β€” Extended

Source: (D) | Status: Confirmed as build gates. Extends Section 37.**

AC-08: Billing Failure Flow

GIVEN a teacher has an active subscription (state=active)
WHEN Stripe fires invoice.payment_failed
THEN:

GIVEN grace period has expired (grace_ends_at < now(), detected by hourly cron)
WHEN expiry cron runs
THEN:

FAILURE: If Stripe webhook signature invalid β†’ 400, INSERT audit_log action='stripe_webhook_invalid'. No state change.


AC-09: Admin β€” Impersonate User

GIVEN a platform_admin is authenticated
WHEN POST /api/admin/users/:id/impersonate with reason="investigating missing report"
THEN:

GIVEN admin clicks "End Impersonation"
WHEN DELETE /api/admin/impersonate
THEN:

FAILURE: Admin attempts to impersonate another platform_admin β†’ 403. Token expires without end call β†’ session auto-invalidated at 1h by expiry cron.


AC-10: Admin β€” Grant Entitlement

GIVEN a platform_admin grants full entitlement to a school
WHEN POST /api/admin/entitlement/grant {school_id, tier:'full', expires_at: null}
THEN:

GIVEN admin revokes the grant
WHEN DELETE /api/admin/entitlement/grant/:id
THEN:


AC-11: GDPR Deletion β€” Child Account

GIVEN a parent or school_admin submits a GDPR deletion request
WHEN POST /api/gdpr/delete {learner_id: "uuid"}
THEN:

FAILURE: If any service deletion fails β†’ log to gdpr_requests.error_log. Manual resolution required. Do NOT mark complete until all services confirm.


AC-12: Offline Reading Session Replay

GIVEN a child is reading offline (no network)
WHEN they turn pages and tap words
THEN:

WHEN network restores
THEN:

FAILURE: If events not replayed within 72h (workbox limit): events lost. reading_session remains 'open'. Session-abandon cron marks it 'abandoned' after 24h last_event_at. No data lost from other sessions.


AC-13: First Child Login (Placement Test β†’ Library)

GIVEN a child account has state='created' and placement_test_completed=false
WHEN child logs in with correct username + PIN
THEN:

GIVEN child has placement_test_completed=true
WHEN child logs in
THEN: Direct redirect to /library. Placement test NOT shown again.

FAILURE: Placement test API fails mid-way β†’ allow retry from question 1. Do not partial-save. fk_level only written on full completion.


End of document. Version v1.7 β€” 2026-04-18 19:15 UTC