Lumenor: A Socratic AI Tutor, and a Rejection From Apple
How this was put together: reconstructed from session transcripts and file timestamps in the project's repo, not a diary kept at the time. Dates are inferred from slice docs and script timestamps, which lined up closely with what the sessions described. One unrelated session about a completely different industrial-IoT project was excluded entirely, as was a rejection-fix effort still in progress at research time.
Key milestones
- Jul 5Repo scaffolded; the compiler shipped and went live the same day; domain purchased that evening
- Jul 6Six more slices built in one day — tutor core, mastery engine, voice, uploads, insights, native iOS shell
- Jul 6 eve – Jul 7 amNative Sign in with Apple/Google debugged across 3 TestFlight builds — root cause was two layers deep
- Jul 7Marathon day: 6 more slices plus a full hardening pass — 61-scenario eval suite passes at 96.7%
- Jul 8Age-gate, App Review readiness doc, store screenshots — pre-submission checklist closed out
- Jul 9–13Six builds stuck un-selectable in App Store Connect — fixed via a sibling app's own institutional memory
- Jul 13–14Video-avatar tutor (two personas, tuned voice detection) built and shipped
- Jul 15Build 30 rejected on three grounds; same-day layout fix shipped; resubmission fix underway
The tool stack, end to end
What it is: a Socratic AI tutor for iOS and web — "the tutor that knows you." It compiles a personal curriculum for any goal you type, teaches by questioning rather than answering, tracks mastery on a visual skill map called a "Constellation," and offers voice and video-avatar tutoring alongside text.
Origin: before the repo existed
The project's own idea/ folder holds the paper trail: a PRD and market-analysis pass done under two different working names — "Lumi" and "Lumina" — plus a standalone research report ranking self-directed learning interests. Somewhere between that research and the first commit, the product was renamed Lumenor (a trademark sweep confirmed no conflicting education marks under that name). The "teaches by questioning, not answering" thesis and the constellation metaphor both survived from the original research into the shipped product essentially intact.
Day 1: the compiler, and a very rough infrastructure day
Goal: stand up the repo and prove the core loop — type any learning goal, get back a personalized skill map in under a minute.
What actually happened was mostly a fight with deployment infrastructure, not the product logic, in order: a branch-name mismatch between the local repo and the host; a database URL still holding literal placeholder text; auth keys never added to the deploy target; a missing lockfile that made the installer guess an ancient package-manager version; a strict environment-variable mode that silently stripped every variable not explicitly declared; a project misconfigured as a generic type instead of the actual framework; production auth keys that are domain-locked and silently refuse to initialize on a preview domain; background jobs that never registered with the deployed app at all; and a compile-race condition that let the same task run itself multiple times over, producing a visibly duplicated mess on the very first real test.
Every one of these was diagnosed and fixed the same day, each time from a pasted build log. By evening, five wildly different test goals — LLM internals, Dostoevsky, GCSE trigonometry, options trading, spoken Afrikaans — each compiled into a distinct, personalized skill graph in 19–25 seconds. The domain was purchased that same evening once the live demo worked. This was clearly the single hardest day of the project — not because the product logic was hard, but because nearly every piece of hosting/auth/build infrastructure had its own gotcha, each only discoverable by shipping and reading the real error.
Day 2: a full app skeleton, then a hard fight with native sign-in
In a single day, Lumenor went from a bare compiler to a mostly-complete app skeleton: the Socratic tutor core, a mastery/spaced-repetition engine, a voice scaffold, file uploads with retrieval, weekly insights, a paywall scaffold, and a native iOS shell.
Getting Sign in with Apple and Google working in that native shell turned into a genuinely difficult, multi-hour arc, because the root cause was two layers deep. The first hypothesis — a missing native-app registration in the auth dashboard — turned out not to be it; fixing it produced identical errors. The real cause, found only by reading the auth provider's own SDK source: the web SDK running inside the native shell has no native OAuth-token strategy at all — those only exist in dedicated native mobile SDKs. No dashboard setting could ever have fixed it; the fix had to be code. Even after that, Apple sign-in kept failing because its entitlement had been enabled mid-session, after the current test build had already been signed — requiring two more builds before it actually worked. Google worked immediately since it was pure web code needing no rebuild.
The marathon day: explanation lenses, and the eval harness doing its job
This was, by file timestamps, the single most productive day of the build — ten-plus feature slices and a full security pass in one sitting. The best "honest" story of the whole project is the explanation-lens feature, which lets a learner shift explanation register by tapping a control or just saying "explain it like I'm five."
| Eval run | Result | What it caught |
|---|---|---|
| First run | 4 of 8 scenarios (50%) | Asking for a simpler explanation caused the tutor to quietly hand over the answer disguised as an analogy |
| After prompt tightening | Same failure recurred a third time | Prose alone wasn't reliably beating the model's "just be helpful" reflex |
| After a hard-coded example pair, run twice | 8 of 8 (100%) | Only trusted once it passed twice in a row, to rule out judge-scoring noise |
Notably, all three built-in jailbreak and answer-extraction probes passed on the very first run with zero leaks — the failures that took three rounds to fix were teaching-quality bugs, not safety bugs. The same day's 12-scenario red-team pack caught a real prompt-injection hole (uploaded document text flowing straight into the tutor's prompt, completely unframed) and surfaced that in-app account deletion — an outright App Store requirement — simply didn't exist anywhere yet. The full 61-scenario suite passed at 96.7% with zero answer leaks.
The paywall decision made this same day is worth calling out: faced with wiring a subscription paywall before submitting, or shipping free during the beta, the call was to ship free — specifically because a sibling app's only-ever App Store rejection had been a subscription-disclosure guideline. The reasoning: concentrate first-submission review risk on the surface that had just gotten real hardening, not on the least-tested feature. That decision turned out not to be quite risk-free either, as the next section shows.
A submission that wouldn't attach, solved by a sibling project's own history
Six consecutive build uploads showed up in the App Store's build-selection screen with their radio buttons permanently greyed out — no combination of clicking would let any of them attach to the version. The fix came from PJ pasting in troubleshooting notes from his other app, which had hit the exact same symptom before: builds produced with the CI workflow's distribution setting on "Internal Testing Only" are permanently ineligible for App Store submission, no matter what — that's the platform's design, not a bug. Changing one dropdown and cutting a fresh build fixed it. A good example of institutional memory paying off across two different apps in the same portfolio.
The rejection
Build 30 was submitted, and the review came back with three findings:
- The app icon read as a placeholder — it looked like an unfinished design-tool export.
- The app "appears to be a pre-release, test, or trial version." The trigger was the premium page's own "Beta" / "everything unlocked while in beta" copy — the very language chosen a week earlier specifically to reduce a different kind of rejection risk. It traded one risk for another.
- Sharing user data with third-party AI services needs an explicit in-app consent step naming exactly what's sent and to whom, before any of it happens — a privacy-policy page alone wasn't sufficient under this reading of the guideline.
A fully specific fix plan was written for all three: strip "beta" language everywhere including store metadata; add a one-time AI-data consent screen mirroring the existing age-gate pattern; replace the icon with a finalized brand mark. That work was still in progress at the time of this writeup.
Meanwhile: the video-avatar tutor
While the build sat in review, a video-avatar tutor feature shipped — two personas chosen on data rather than taste (matched lighting and expression so they'd read as a designed pair, voices picked using a numeric voice-trait API rather than by ear). Real dogfooding surfaced a hard cutoff after 3–4 minutes and a clunky voice mode. The cutoff's root cause was found by reading the provider's own session-analytics dashboard: the actual session had run for exactly the code's own default length cap, because an intended override wasn't reaching the deployed build. Fixed two ways — raised the default, and made the avatar transparently reconnect on any cap or network drop rather than ending the conversation at all.
A separate bug — overlapping text on both the avatar and voice screens, reported with six screenshots — turned out to be both canvases rendering the conversation twice: a floating decorative text line, the real transcript, and the controls all stacked on the same pixels. Fixed with a shared text-rendering component and both canvases rebuilt into one clean vertical stack, shipped the same session it was diagnosed in.
Open issues
- App icon read as an unfinished placeholder to Apple's reviewer — needs the finalized brand mark shipped
- "Beta" language across the app and store metadata read as an unfinished product — needs stripping everywhere
- No in-app consent step yet for sharing data with third-party AI providers (tutoring, speech, voice)
- Turn-pipeline latency measured at 3.28s p50 time-to-first-token — the real gap versus feeling "ChatGPT-smooth"
Upgrade opportunities
- Ship the three-part rejection fix and resubmit for App Store review
- Cut voice/avatar latency as Slice 19's main focus
- Work through the five-slice V2 roadmap: Nightly Observation, Share-your-sky, certificates, camera-to-constellation, cross-track misconception memory