Lekture · internal · 17 September 2026
What we shipped vs the handover plan
Two open PRs close the QA blockers we found on device. Five of our fixes sit outside the handover matrix. The real gaps are the missing AI proxy (API #5), Android push config, iOS QA, and ops/legal — not admin auth or Apple ASN, which are already deployed.
17 September 2026
Sources: PR diffs, upstream/v4 and upstream/v3 source inspection, device QA (Android, prod + local backend). Statuses below are code-verified; device acceptance is noted separately.
Verified in source
Handover items already landed on v4/v3
These came in with the earlier merges (mobile #3/#4, API #3). They are not in the two open PRs; verified by source inspection just now.
| Item | Plan ID | Evidence |
|---|---|---|
| Unified VAD, level-scaled silence + mic sensitivity | 4.1 · 4.2 | getTurnDetection()/applyTurnDetection() present in WebRTCService.js on v4 |
| Pause → Prompt → Praise ladder | 4.3 | Prompt ladder merged (digital-hardening) |
| Never read ahead | 4.4 | WebRTCService.js:332 “NEVER READ AHEAD” rule |
| Beginner leniency 70–80% | 6.4a | Level-driven scoring in textUtils.js |
| Age & grade passed to tutor | 6.4b | profileId sent on session create (ReadingPage:271, RealTimeMode:2047) |
| Backend read-ahead leak | 6.4c | openai.service.ts no longer embeds passage; waits for app text |
TypeORM synchronize off in prod | 6.2a | NODE_ENV !== 'production' guard present on v3 |
| PII out of API logs (COPPA) | 6.2b | Auth/mail/report/purchase/session logs scrubbed |
| Trial 7 days / 150 pages | 6.3c | Signup provisioning + mobile reads trialDaysRemaining/isTrial |
| Realtime voice retention off | 6.5a · 7.4 | tracing: null in openai.service.ts |
| Push registration stub (dummy token) removed | — | API rejects dummy-token-* |
| Remote push pipeline (FCM + APNs, config-gated) | 6.3a partial | Pipeline + /api/devices/* deployed; Android google-services.json still missing |
| Per-user admin auth: login + TOTP MFA, roles, invites, audit log | 6.2c done | v3 admin module; deployed — /api/admin/auth/login, /setup-mfa, /verify-login, /accept-invite in the live prod Swagger |
| Apple App Store Server Notifications | 6.3b done | apple-asn.service.ts on v3; deployed — /api/subscriptions/apple/webhook live |
| Google Play RTDN webhook | IAP done | /api/subscriptions/google/webhook live |
Open PRs
What the two open PRs change
Mobile #5 — feat/qa-hardening → v4
- Mic permission requested before billing —
ReadingPage+RealTimeModenow ask forRECORD_AUDIO(cross-platform via vision-camera) before the charge-bearing call. A denied mic no longer costs a page. found in QA outside plan - Backend-authoritative page billing — no local decrement before the session succeeds; balance re-synced from the API.
- ReadingPage session lifecycle — single auto-start per mount, explicit Retry surface, whole-session metrics (
readingSessionMetrics.js), illustration/credits UI. Supports the Digital Reader MVP. - IAP restore + Pricing rework — restore flow and plan screen fixes.
- WebRTC metrics + log scrub — real AI-assist count, committed-chunk spoken-word totals (no phantom words), child-speech arrays kept out of logs (COPPA twin of 6.2b).
- Push hardening — re-register device on persisted sessions, token-refresh identity guard, listener teardown (6.3a partial).
apiHandler40s timeout — AbortController so a hung provider call can’t wedge the UI. outside plan- Android config — apply google-services plugin only when the JSON exists; placeholder release-keystore creds removed.
- OCR unavailable handling — 404/503 now surfaces “scanning temporarily unavailable” instead of blaming the image. outside plan
- EAS build pipeline —
eas.json, project id, signed preview APK. outside plan
API #5 — feat/ai-ocr-proxy → v3
- New server-side AI proxy —
POST /ai/ocr(multipart image →{text, words}) andPOST /ai/feedback. JWT-guarded, 30 req/min/user, JPEG/PNG + 4MB validation, prompts treat image text and assessment JSON as data (never instructions),store:false. Keeps the OpenAI key off the device. not a numbered matrix item - Additive module + 2 lines in
app.module.ts; no migration.
State of play: built ≠ merged ≠ deployed
| Code | Built (PR) | Merged | Deployed |
|---|---|---|---|
| Mobile QA hardening + mic fix (mobile #5) | yes | no | no |
AI proxy /ai/ocr + /ai/feedback (API #5) | yes | no | no |
| Admin auth · Apple ASN · Google RTDN · device register | yes | yes | yes |
Scope check
Fixes outside the handover plan
These were not in the August work package. They are real defects or tooling we added while getting a shippable build.
| Fix | Where | Why it mattered |
|---|---|---|
| Mic permission not requested before billing | Mobile #5 | Denied mic charged a page for a session that could never start |
| UI stuck after profile → PIN when a stray BACK fired | None (not a bug) | Investigated and disproved; no change shipped |
| OCR/feedback proxy missing on prod | API #5 | Scan & Read returned “No Text Detected” on a 404; now works once deployed |
apiHandler had no timeout | Mobile #5 | Hung provider calls locked the UI indefinitely |
| EAS/APK build pipeline | Mobile #5 | Needed a shareable signed APK; repo had no eas.json |
| Android google-services conditional + keystore creds removed | Mobile #5 | Build without Firebase config; no placeholder signing secrets in repo |
Not done
Still open from the plan
The published handover page is stale on two of these: per-user admin auth (6.2c) and Apple ASN (6.3b) are implemented on v3 and deployed — verified against the live prod Swagger. What remains is ops/QA/legal, plus the AI proxy in API #5: built and tested locally, but unmerged and undeployed (prod still 404s /ai/ocr).
| Item | Plan ID | State |
|---|---|---|
| Real remote push delivery (Android native config) | 6.3a | Blocked needs com.lekture google-services.json + APNs key; pipeline and /api/devices/* are live |
| Apple sign-in / Hide My Email + TestFlight child QA | 2.4 · 4.5 | Needs iOS QA code present on v4 |
| Secret rotation / SSH lockout | 6.1 | Ops |
| Parental consent verification / legal review | 6.5b · 6.5c | Legal |
| Google Play declarations | 6.5d | Ops |
| Scaling · model evaluation · owner/legal 7.x | 6.6 · 6.7 · 7.x | Future |
Takeaway
Merge is safe; deploy is the real gate
Both PRs are clean and mergeable (mobile #5 → v4, API #5 → v3). The API change is additive; the mobile change is additive on top of the current v4 head.
Merging is not shipping. OCR/feedback only works after the API is deployed with OPEN_AI_KEY set on the droplet — prod still 404s /ai/ocr today. The app needs a rebuild/release from the merged branch; push, iOS/TestFlight and real store billing stay open.
Test suites added during hardening were removed from both PRs (17 Sep) — they are not part of the handover build. The app bundle is unaffected, so the existing signed preview APK still matches this code except for the dropped tests.
Method: PR file lists from GitHub, targeted git grep against upstream/v4 and upstream/v3, and on-device Android QA against prod and a local API. No iOS validation performed.