Source inspection: 2026-09-13. This is a navigation guide to the implementation, not a claim that deployed services or every runtime behavior have been verified. Refresh the relevant sections when the code changes.
One Concept teaches one short technical concept per day, with topic follows, learned history, streaks, likes, saved concepts, and push reminders.
| Area | Entry points and purpose |
|---|---|
| Mobile | mobile/README.md is the local developer on-ramp; mobile/index.ts registers mobile/App.tsx; Expo SDK 57, React Native 0.86, React 19, TypeScript. mobile/src/screens/AboutScreen.tsx links learners to the public review explanation. |
| Review website | admin/README.md, admin/src/main.tsx, App.tsx; independent React/TypeScript/Vite static editorial workspace with Supabase Auth and private FastAPI calls. admin/public/about.html and privacy.html are public OAuth information pages. admin/src/hosting.ts generates exact-origin static response headers during build; admin/public/_redirects handles Netlify SPA deep links. Direct deployment steps are in docs/EDITORIAL_ROLLOUT.md. |
| Backend | backend/app/main.py; FastAPI, async SQLAlchemy/asyncpg, Pydantic settings, ES256 JWT verification. Docker uses Python 3.12. |
| Database | backend/migrations/; Supabase PostgreSQL schema, RLS, seeds, and incremental migrations. |
| Content lifecycle | docs/CONTENT_ARCHITECTURE.md, docs/CONTENT_OPERATIONS.md; portable subject/curriculum imports, durable refill, reviewed publication, daily review, protected health report. |
| Content engine | backend/app/services/generation.py, pool.py, prefetch.py; Gemini lessons from a curated backlog. |
| Operations | .github/workflows/, backend/railway.json, backend/Dockerfile, mobile/eas.json, mobile/app.config.js. |
| Hosting migration | docs/RAILWAY_MIGRATION.md; source/destination evidence, worker handover, EAS endpoint publication, recovery redirects and retirement. New Railway services use dashboard settings because legacy Config as Code is deprecated. |
| Documentation | Root README.md, RELEASING.md, CONTRIBUTING.md, docs/ARCHITECTURE.md, docs/ROADMAP.md, and the backend/mobile guides. |
| Agent guidance | Root AGENTS.md; mobile/AGENTS.md adds Expo documentation requirements and mobile/CLAUDE.md references it. |
| Authentication email | backend/email-templates/ contains branded signup, recovery, and password-changed HTML; docs/EMAIL_TEMPLATES.md covers manual Supabase installation and activation checks. Templates use the configured sender and are not installed by app deployment. |
| Engineering handbook | docs/handbook/ONE_CONCEPT_HANDBOOK.md explains the full stack and learning lifecycle; docs/handbook/build_pdf.py renders the printable guide with vector diagrams. Build and verification instructions are in docs/handbook/README.md. |
api/v1/editorial.py exposes private account/onboarding and owner-management APIs.
services/editorial_accounts.py verifies current Supabase sessions, confirmed
email, membership, capabilities and MFA before account-lock acquisition and
again afterward, using current-query time for session deadlines;
editorial_management.py serializes
versioned account mutations and records their audit events. editorial_invites.py
is the server-only Auth invitation adapter. Migration 0032_editorial_accounts.sql
keeps memberships and account events inaccessible to browser roles. The one-time
owner CLI is python -m app.workers.editorial_accounts; see
editorial-accounts.md for activation and frontend handoff.
services/editorial_revisions.py adds authenticated, immutable exact-revision
review decisions and legacy attestation (#275); migration 0033 stores the one-time
published inventory, review events and exact-version provenance. publication.py
requires that approval through the authenticated service; the old free-text CLI
publish/reject commands fail closed. See editorial-provenance.md.
api/v1/editorial_content.py exposes #276 queues, details, history/timeline and
versioned actions. editorial_queries.py builds private read models;
editorial_workflow.py owns authenticated commands, request receipts and workflow
audit. Migration 0034 adds private workflow evidence/assignment metadata. The
operator client python -m app.workers.editorial_review uses those same HTTP
gates. See editorial-api.md for permissions, retry semantics
and rollout. The admin/ website (#278) consumes these APIs; mobile attribution (#280) projects exact-version evidence through learner reads;
owner reporting (#297) remains a separate child of #263.
services/editorial_notifications.py drains the private #279 outbox through the
existing reminders worker; editorial_mail.py uses Gmail HTTPS/OAuth (no Railway
SMTP upgrade). editorial_email_template.py renders the escaped HTML template
in app/templates/editorial_review.html alongside a plain-text fallback.
api/v1/editorial_notifications.py exposes owner delivery/policy
controls and the reviewer timezone. admin/src/Notifications.tsx shows overdue
work and safe retries. See editorial-notifications.md
for migration 0038, free sender setup and the direct production email rollout.
editorial_generation.py owns #277 authenticated durable revision requests,
claim fencing, provider orchestration and private draft completion. Migration
0035 stores immutable source/feedback/result links and backlog claim tokens.
editorial_generation_status.py exposes bounded private supply/planning status;
review_capacity.py shares human-work limits across pool/prefetch/rewrite paths.
The existing workers/pool_topup.py runs a bounded revision batch before refill;
content_health.py adds aggregate job conditions. See
editorial-generation.md for API and staging contracts.
services/review_attribution.py selects public name/date/version evidence in the
same SQL statement as a published lesson. schemas/daily.py adds nullable
ConceptOut.review; detail, daily selection and folded state/review paths use it.
mobile/src/services/conceptMapping.ts shares version validation across detail,
daily and cached reads. components/ReviewAttribution.tsx renders borderless credit only in
ConceptDetailScreen, below the card opened from History/Saved. Today/review
cards and legacy/mismatched metadata have no label.
See editorial-provenance.md
for compatibility, historical offline semantics and rollout.
admin/src/App.tsx owns session fencing, capability gates and navigation.
Auth.tsx handles invitation/recovery, password and MFA; Settings.tsx handles
registered identity. Queue.tsx supplies topic/status/deadline filtering and
shared approved/published views. Review.tsx and LessonView.tsx render complete
packages, diffs, history, comments, checklist decisions and safe corrections.
Team.tsx manages owner-only membership; Generation.tsx requests bounded work.
api.ts and useCommand.ts preserve exact operation retries and stale-token
failures. Migration 0037 adds audited review deadlines. Queue totals are computed
with page results in one statement; published rows require matching exact-version
provenance. Build/browser CI uses public fixture values only. See
the review website guide for staging and hosting boundaries.
admin/src/OwnerDashboard.tsx contains read-only activity, reviewer, operations
and event views. OwnerDemo.tsx / ownerDemo.ts provide a deterministic adapter
selected before Auth/API initialization. api/v1/owner.py enforces the existing
manage_reviewers administrator capability; services/owner_reporting.py
aggregates canonical completions and immutable editorial evidence.
services/owner_telemetry.py records opt-in bounded observations from the API and
existing workers. Migration 0039 adds the private telemetry table and report
indexes; the schema contract includes them. See owner-dashboard.md
for metrics, privacy, retention and activation deferred to #281.
services/achievements.py awards permanent, data-driven milestones under the
existing profile lock and caller transaction. Migration 0016_achievements.sql
introduces streak awards; 0027_expanded_achievements.sql adds categorized
concept, review, weekly-quiz, perfect-score and distinct learning-path
achievements with a historical backfill. The evaluator reads only accepted
server records, with the composite award key preserving a first-earned date on
retries or concurrent devices. Weekly quiz submission takes the profile lock
before scoring and evaluating awards. api/v1/achievements.py serves the
collection, category/requirement/progress metadata and seen APIs.
On mobile, AchievementsContext owns one keyed account instance,
achievementStore fences async results, and achievementCache joins account
cleanup. Profile opens AchievementsScreen; shared badge/detail/celebration
components render every category and a server-confirmed nearest milestone.
See ACHIEVEMENTS.md for rollout and extension rules.
docs/multilingual-content/README.md is the approved future-work design for
localized daily lessons, flashcards and quizzes. Canonical concept identities
remain the source of progress, history and analytics; future reviewed locale
variants supply complete learner-facing payloads with server-selected fallback.
Language-learning study cards and games are a separate future model, not copies
of technical concepts or translations. This section documents no runtime
implementation.
services/analytics.py provides the single bounded, account-scoped read model
behind GET /v1/me/analytics. It counts accepted concept completions, completed
reviews and immutable weekly quiz attempts; derives streaks with the existing
service; groups the fixed 28-day activity window in the profile IANA timezone;
and reuses the existing server evaluators for topic, subtopic and achievement
progress. It does not write client-derived counters or expose another account’s
data. schemas/analytics.py owns the response contract and
tests/test_analytics.py covers authentication and local-day aggregation.
services/analyticsApi.ts supplies an account-fenced typed client. Profile opens
AnalyticsScreen, which displays a quiet server-confirmed summary, seven-day
activity view, quiz performance, actual topic distribution, learning-path
progress, recent concepts and achievement totals. Empty/loading/unavailable
states are explicit; the screen fetches only for the signed-in account and can
be refreshed manually.
user_concept_completions is the authoritative record that a learner has
consumed a concept, distinct from the date a daily assignment counts for a
streak. Migration 0023_subtopic_completions.sql backfills it from completed
daily assignments; 0024_backfill_subtopic_completion_events.sql records
already-complete paths as seen historical events. user_subtopic_completions records an immutable event for
the exact sorted set of published concept IDs in an active subtopic. A new
published concept produces a different catalog signature and naturally returns
the path to active progress; an editorial revision does not.
services/subtopic_progress.py calculates boundaries server-side under the
same profile lock as daily completion. GET /v1/me/subtopics/progress provides
Profile’s learning-path summary; POST /v1/daily/complete returns a completion
event only when it was newly recorded. The Today card appears only for that
confirmed response, while the acknowledgement endpoint retains delivery state
for later achievement and challenge work.
subtopic_quizzes freezes up to seven reviewed MCQs against a learner-owned
user_subtopic_completions event. The snapshot stores every source concept
slug, content version, question, option and answer key. A new content catalog
therefore creates a distinct future completion event, while an already opened
quiz stays unchanged. subtopic_quiz_attempts is append-only: retries create
new answer/score rows and the history endpoint retains earlier results.
services/subtopic_quizzes.py chooses only reviewed MCQs from the completion
record’s exact concept IDs and never generates questions at request time.
api/v1/subtopic_quizzes.py keeps answer keys server-side until submission.
Composite ownership constraints keep quizzes and attempts bound to the same
account as their completion parent. Profile exposes completed paths through
SubtopicQuizzesScreen and SubtopicQuizScreen; the feature is optional and
never changes a daily lesson, streak or weekly quiz.
App.tsx composes SafeArea, Theme, Connectivity, Auth, and Progress providers.
The signed-in shell owns top/side safe-area padding outside scrolling screens;
its offline/sync banners and Achievements header avoid adding that inset twice.
Bottom tabs retain their own bottom inset. Native achievement modals still
handle their own insets.
It holds the native splash until fonts are ready (or fail), checks public API and
Supabase configuration before starting providers, and wraps the root in
AppRecoveryBoundary. ConfigurationState, UnavailableState, and
api/errorRecovery.ts provide safe learner-facing recovery copy; raw API error
payloads are not rendered. components/support.ts opens the support email.
The root then lays out the signed-in flow.
The visible signed-out flow is AuthScreen; authenticated users get bottom tabs
inside a root stack, with a concept-detail modal above them.
| Screen | Responsibility |
|---|---|
TodayScreen.tsx |
Daily lesson, learned action, streak, loading/exhausted/offline states, and a server-confirmed topic-selection prompt when no topics are followed. |
HistoryScreen.tsx |
Paginated learning history, search within loaded records, offline pages and navigation to concept details. |
StatsScreen.tsx |
Learned-only overall/topic counts and separate compact review activity; no catalog denominators or completion bars. |
AnalyticsScreen.tsx |
Profile-linked, server-confirmed concepts, reviews, activity, quizzes, topics, learning paths and achievements with empty/recovery states. |
WeeklyQuizScreen.tsx |
Optional server-backed weekly quiz: eligibility progress, seven reviewed questions, icon-marked accessible answer selection, result feedback and reattempts. |
SubtopicQuizzesScreen.tsx / SubtopicQuizScreen.tsx |
Profile-linked optional quizzes for completed subtopics, frozen reviewed questions, icon-marked accessible answer selection, retries, and prior-score history. |
ProfileScreen.tsx |
Account, learning-path progress, clear reminder prerequisites, immediate per-control queued reminder writes, theme, sign-out, and links to profile subpages. |
EditProfileScreen.tsx |
Preferred-name editing; confirmed, account-fenced Progress state update. |
ProfileSharingScreen.tsx / PublicProfileLink.tsx |
Opt-in field choices using shared icon-led controls, native share/local QR and uncached incoming public-profile view. |
PersonalizationScreen.tsx |
Server topic catalog and follow controls through useTopics. |
SavedScreen.tsx |
Recent/cached saved concepts, older metadata pagination, search/category filters, and detail navigation. |
ConceptDetailScreen.tsx |
Cached full lesson first, then online refresh by slug; bundled catalog fallback. |
AuthScreen.tsx |
Sign-in, sign-up, password recovery, and accessible show/hide password controls that reset on mode changes or submission. |
AboutScreen.tsx |
Branding and app information. |
All screens live in mobile/src/screens/. Reusable presentation in
mobile/src/components/ covers lesson cards/actions, category/follow controls,
like counts, streak/flame visuals, buttons, skeletons, the offline banner,
SearchField and CollectionConceptRow for compact accessible Saved/History collections,
UnavailableState (animated offline/retry UI), and the What's New card.
ScreenHeader supplies the common eyebrow/title/supporting-copy hierarchy and
Surface supplies the borderless elevated containers. useReducedMotion gates
tap feedback, while UnavailableState uses a single short entrance motion and
SyncStatusBanner gives paused offline writes a clear retry action.
Saved uses a non-shrinking horizontal ScrollView for its short filter rail, with
content-driven chip height and separate virtualized lesson rows.
src/theme/index.ts defines readable text/control, success, danger and subtle
surface colours plus touch-target and motion constants.
Only Today’s green completion pill has a half-point UI outline; other containers
use borderless surfaces and spacing. The theme also defines spacing, radii,
typography, shadows, and scaling; ThemeContext persists light/dark preference.
src/navigation.ts types the root stack.
History uses hooks/useHistory.ts and services/historyApi.ts to load one
50-item page per request from the existing history endpoint. Page caches join
account cleanup; the startup progress aggregate remains compact. Data screens
share hooks/useRefreshControl.tsx for native pull gestures and refresh buttons.
src/lib/supabase.tscreates the Auth client and remains import-safe when public configuration is absent so the configuration recovery UI can render.secureStorage.tschunks native session storage through Expo SecureStore, with AsyncStorage on web.AuthContext.tsxowns session startup, sign-in/up/recovery/sign-out, supplies the API token provider, and triggers push registration and timezone sync.services/authSession.tsrestores cached identity while refresh is pending; confirmed sign-out still clears account caches.authErrors.tskeeps raw transport diagnostics out of authentication forms.src/api/client.tsmakes authenticated JSON requests, exposesApiError, and infers connectivity from request results. Its account epoch rejects a request that crosses sign-out/sign-in, and it honors a serverRetry-Afterhold.ConnectivityContextdrives the global banner; there is no native connectivity listener.api/fetchWithTimeout.tsbounds API and auth fetches to 15 seconds.ProgressContext.tsxis the shared UI state owner. It loads cached state before revalidation, applies optimistic actions, serializes mutation requests, and flushes queued work on the same mutation chain.services/syncLoop.tsretries while offline or actions remain, using 5–30 second backoff, including when only some requests succeed. Daily refreshes preserve pending actions; account/source changes invalidate them and clear the displayed state. Foreground and browser reconnect events wake an idle loop immediately; backgrounding pauses timers. This remains compatible with the current APK and has no closed-app worker. Screen retries use its serializedrefresh; topic and detail screens have their own retry paths. Failed loads do not substitute demo lessons or totals for an authenticated account.services/progressRepository.tsdefines the persistence interface.remoteProgressRepository.tsimplements API state, account caching, optimistic offline fallbacks, and replay. It opts into compact startup metadata; Stats uses full server totals plus older-topic counts throughprogressTotals.ts.pendingProgress.tsretains unacknowledged likes, saves, and same-day completions during server reconciliation.localProgressRepository.tsandstorage.tsretain local/demo support; this is not a separate visible guest navigation flow.mutationQueue.tswires AsyncStorage tomutationOutbox.ts, which serializes disk writes and stores the latest intent per like/save/topic/completion key. Replay discards stale-day completions, retains retryable failures with a persisted 5-second-to-5-minute backoff, and pauses after eight attempts until the learner explicitly retries. It does not backdate server completion.accountCaches.tscentralizes account cache cleanup. The remote repository's epoch guards reject late mutation callbacks after a wipe; the API invalidates requests still waiting for an old account's token during cleanup. Device theme/demo state is separate from account data.dailyApi.tsmaps server concepts to UI types and clears an old daily cache; current daily data arrives in/v1/me/state.conceptApi.tspersists full lessons by slug, including each cached daily lesson and missing saved lessons downloaded with three workers. Offline reading requires a completed download.offlineCache.tsprovides per-entry storage and fences late writes on sign-out.mobile/tests/uses Node's built-in runner for pure service, storage, account-boundary and sync-loop regressions; browser scripts exercise exported-app flows without live credentials. The helper resolver lets Node load Metro-style extensionless source imports without adding a second test framework. UI concept IDs are slugs, while the database also has UUIDs.hooks/useSavedConcepts.tsloads older Saved metadata in 50-record pages on that screen, retaining full search/filter access.services/savedApi.tsowns its account-keyed disk cache;accountCaches.tsclears it and invalidates late writes. Missing offline metadata can be recovered from downloaded lesson bodies.hooks/useTopics.ts,services/topicsApi.ts, andtopicStore.tsshare the cached dynamic topic catalog. Follow changes enter the same durable outbox as other actions; queued choices override stale server responses until replay. Both the catalog and full-concept cache participate in account cleanup.services/notifications.tshandles permissions, Android channel setup, Expo tokens, timezone sync, preference caching, and deregistration before sign-out.data/concepts.ts,services/dailyConcept.ts,dates.ts,streak.ts, andtopics.tssupport the bundled catalog, local selection, dates, and mappings.data/whatsNew.ts,hooks/useWhatsNew.ts, andservices/whatsNewStore.tscontrol version announcements. Every release includes a matching one-time card focused on new features and user-visible improvements, perRELEASING.md.components/WhatsNewCard.tsxscrolls long highlight lists independently of the heading/dismissal controls so small screens can reach every item.src/types/index.tsdefines shared concept, progress, daily, history, and streak types. API payloads also have types near their service consumers.
INCIDENT_RESPONSE.md defines the production triage and communication sequence for Railway API/workers, Supabase, GitHub release gates, and mobile delivery. It deliberately separates learner-safe UI recovery from private diagnostics and restore procedures.
main.py configures CORS, routes, production documentation visibility, safe SQLAlchemy and unexpected-exception responses, and a shared
JWKS cache, and engine cleanup. Its lifespan owns db/keepalive.py's configurable
database probes; checkout/query and connection return are bounded, failures retry,
and cancellation awaits cleanup before engine disposal. config.py loads settings
and normalizes pooler URLs; db/session.py creates the async engine/session
dependency and reuses the most recently returned connection to keep a hot slot.
The existing pre-ping, transaction pooler mode, and pool limits remain in place.
Authenticated routes pass through core/rate_limit.py after JWT verification. It uses bounded in-process per-account read/write token buckets, returning 429 with Retry-After; a multi-replica deployment must replace it with shared state.
deps.py obtains identity from bearer tokens verified by core/security.py
(ES256, issuer, audience, expiry, and subject). core/errors.py formats auth errors.
Routes (backend/app/api/v1/) |
Implementation |
|---|---|
health.py: GET /health |
Liveness plus a database query. |
topics.py: GET /v1/topics |
Active topics, published counts, follow state. |
daily.py: GET /v1/daily, POST /v1/daily/complete |
Selection, completion, server-derived date and streaks. Typed 409 reasons distinguish personalization_required (no active follows) from catalog_exhausted. |
me.py: GET /v1/me/state, /stats |
Optional compact state, exact totals, today's lesson. |
me.py: GET /v1/me/history, /saved |
Cursor pages through services/collections.py; default 50, maximum 100 items. |
analytics.py: GET /v1/me/analytics |
Bounded account-owned learning totals, timezone-grouped activity, quizzes, topic/subtopic progress and achievement collection. |
me.py: PUT /v1/me/topics, PATCH /v1/me |
Whole-set follows, profile name, PostgreSQL-validated timezone. |
me.py: GET/PUT /v1/me/notifications, POST/DELETE /v1/me/push-token |
Reminder preferences and scoped device registration/removal. |
concepts.py: GET /v1/concepts/{slug}, PUT/DELETE .../like, .../save |
Published lesson detail and independent interaction writes. |
pages.py: GET /privacy, /confirmed, /reset-password |
Public privacy and branded Auth landing pages; email redirects use the mobile API origin and reset uses Supabase Auth in the browser. See auth redirects. |
router.py mounts authenticated feature routers under /v1. Response/input
models live in schemas/daily.py, me.py, notifications.py, and topics.py.
services/selection.pyreturns an existing assignment first. With no active followed topic it returns the stablepersonalization_requiredstate without creating an assignment or signaling generation. Otherwise it excludes every previously assigned concept, prefers the least recently seen followed topic, and widens to the global published catalog only when that non-empty followed pool is dry. It handles concurrent inserts and schedules background prefetch, never waits on Gemini.GET /me/stateprojects the same condition throughdaily_availability; old clients can safely ignore that additive field.services/state.pyaggregates profile, follows, learned/saved metadata, likes, assignment slug, and derived streaks in one SQL statement. The/me/statehandler then calls selection separately to adddaily; one HTTP request does not mean one database statement for the entire endpoint. Compact clients get at most 50 enriched learned/saved rows, older-topic counts and continuation cursors; bare membership and aggregate streak/totals remain complete. Legacy clients keep the full detail lists until upgraded.services/interactions.pyimplements likes/saves, full-set follows, and idempotent completion of the most recent assignment from today or yesterday. Yesterday's grace applies when there is no newer assignment; older days cannot be completed.streaks.pyderives consecutive runs from assigned completion days.services/users.pyprovides bootstrap fallback for the new-user DB trigger.services/concepts.pyloads published details. Public like counts exclude the viewer's own like; the mobile UI adds that one locally.services/generation.pybuilds versioned prompts, calls Gemini through httpx, validates output, and exposes rate-limit errors.pool.pyclaims backlog work withFOR UPDATE SKIP LOCKED, stages generated concepts and editorial revisions, refunds throttled attempts and reclaims stale work. Claims and daily call reservations commit together before contacting the provider; quota denial rolls back the claim.services/generation_budget.pyatomically reserves from the sharedgeneration_daily_usageledger using PostgreSQL's Pacific calendar day. All API prefetch, scheduled refill, manual rewrite and editorial revision jobs share this budget and concurrency limit. Failed/uncertain calls retain their reservation; restarts do not reset it.services/prefetch.pyschedules bounded background top-ups, with a low unread watermark, durable per-topic targets, and per-process in-flight topic tracking. Exhausted shared budget is a normal stop condition.services/reminders.pyclaims due user/day/time slots before sending Expo push batches, handles timezone and midnight windows, suppresses completed days, and drops unregistered device tokens. A claimed but failed send can miss a reminder.workers/pool_topup.pyandworkers/reminders.pyare cron entry points.workers/rewrite_catalog.pyis a maintenance command that rewrites existing lessons through Gemini with the shared budget and generation kill switch; do not run it merely to inspect the project.
services/curriculum.pyvalidates subject/subtopic/plan imports, duplicate candidates and prerequisite graphs.backend/content/subjects.jsonandbackend/content/subtopics.jsonretain the five-topic taxonomy;curriculum.example.jsonshows future data-only expansion.services/supply.pypersists assigned-count-plus-reserve demand and plans for active readers.pool.pycounts drafts/in-flight claims in capacity and calls the shared quota/concurrency checks before committing any provider request.prefetch.pystarts only after the demand transaction commits.services/publication.pystages corrections, validates explicit approval and increments versions while preserving identities and prior bodies.workers/rewrite_catalog.pynow drafts revisions under durable claims.services/reviews.pyandapi/v1/reviews.pyselect/complete separate review records.selection.pylocks profiles across daily choice;streaks.py,state.pyand reminders count completed review days without increasing unique learned totals.me/state?reviews=trueopts into a separate review payload.services/weekly_quiz_notifications.pyqueues opted-in unfinished quizzes at local 09:00, claims per-device deliveries before HTTP, and tracks Expo tickets/receipts with bounded safe retries. Migration0031_weekly_quiz_notifications.sqlstores preferences and the private outbox.workers/reminders.pyruns it after daily reminders only when its rollout flag is enabled. MobileWeeklyQuizNotificationNavigator.tsxconsumes cold/warm taps inside the authenticated navigation tree;weeklyQuizNotificationIntent.tsvalidates and deduplicates payloads. See weekly-quiz-notifications.md for activation and delivery limits.services/weekly_quizzes.pyfreezes one reviewed MCQ from each of seven completed concepts into a per-user weekly snapshot.api/v1/quizzes.pyscores submissions server-side and appends immutable attempts; answer keys are only returned after submission.services/subtopic_quizzes.pyindependently freezes reviewed questions from one completed subtopic’s exact catalog.api/v1/subtopic_quizzes.pypermits repeat attempts and returns account-scoped attempt history; it does not couple the subtopic flow to the weekly quiz.workers/content.pyexposes maintainer-only imports, revision inspection, approval, failed-plan correction/retry and health reports.content_health.pycomputes supply/queue/quota/worker conditions and deduplicates transitions. This uses backend credentials, with no public administration API.- Mobile review payloads and versioned concept bodies use the existing caches;
review IDs key durable outbox intents.
pendingProgress.tsmerges pending completion without new learned rows. Today labels review/exploration and Stats separates review totals. Dynamic subject labels use existing generic UI. - Added regressions cover imports, publication races, two-device review, grace, refill/call bounds, a 365-day three-reader simulation, protected health changes, backup restoration, and browser offline/reconnect in both themes.
db/schema.py compares actual catalog metadata against backend/schema/contract.json.
workers/schema_check.py performs the bounded read-only target check using
DIRECT_URL; docs/SCHEMA_VERIFICATION.md covers contract maintenance and
Railway/GitHub setup. No SQL is applied by verification.
db/models.py mirrors the SQL schema; migrations are the schema authority.
The seventeen tables cover profiles, topics, concepts, user topics, daily assignments,
concept interactions, notification preferences, device tokens, the concept
backlog, reminder logs, shared daily generation usage, supply targets, editorial
revisions/retry logs, daily reviews, worker runs and health conditions. Unique constraints
enforce one daily assignment and no concept repeats per user. RLS adds isolation behind backend identity checks.
| Migration | Purpose |
|---|---|
0001_schema.sql |
Core tables, indexes, timestamps, new-user bootstrap trigger. |
0002_rls.sql |
Read/write ownership policies. |
0003_seed_topics.sql, 0004_seed_concepts.sql |
Five topics and twenty initial lessons. |
0005_concept_backlog.sql, 0006_seed_backlog.sql |
Curated generation queue and seed subjects. |
0007_reminder_log.sql |
Unique reminder claims. |
0008_backlog_claimed_at.sql |
Timestamp for reclaiming abandoned generation. |
0009_like_count_index.sql |
Index for public like counts. |
0010_generation_daily_usage.sql |
Backend-only daily Gemini call reservations shared by all generation paths. |
0011_content_supply.sql |
Durable, coalesced demand beyond bootstrap inventory. |
0012_curriculum_publication.sql |
Structured curriculum, content versions, review drafts and audited retry grants. |
0013_daily_reviews.sql |
Separate review activities; preserves new-assignment uniqueness. |
0014_content_operations.sql |
Worker heartbeat and deduplicated condition state. |
0015_revision_generation_claims.sql |
Durable claims for correction drafting. |
0017_topic_subtopics.sql–0019_subtopic_topic_cleanup.sql |
Parent-scoped subtopics, explicit classification of published/planned content, category integrity, and safe empty-topic cleanup. |
Migrations 0001–0015 are recorded in migrations/applied.txt. The owner applied
0011–0015 during 1.9.0 release preparation, and a separate read-only production
connection verified their tables, RLS, columns, indexes, constraints and backfill.
The backend/worker rollout remains pending; the ledger does not prove deployment.
Application connections use the transaction pooler; migration DDL uses DIRECT_URL
and the session pooler. Applied migrations must not be rewritten.
- Mobile dependencies/scripts are in
mobile/package.jsonandpackage-lock.json; use npm.npm run typecheckrunstsc --noEmit;npm testuses Node 24's built-in runner for session recovery, auth messages, request timeouts, offline cache cleanup, outbox ordering, topic persistence, and sync scheduling. Expo provides Android/iOS/web development commands. Native project folders are not tracked. EAS profiles separate development, preview, production, and production APKs. - Backend dependencies are pinned in
requirements.txt/requirements-dev.txt. Frombackend/, run.venv/bin/python -m uvicorn app.main:app --reload --port 8000for development and.venv/bin/python -m pytestfor tests after configuration. - Nine test modules cover HTTP contracts, token validation, daily selection,
writes/streaks, generation, reminders, notification preferences, and connection
warm-up/cleanup. The pool integration checks compare real PostgreSQL idle expiry
with warming disabled/enabled and print timing plus physical-connection counts.
tests/conftest.pysupplies a disposable PostgreSQL 16 database through Podman on port 55433, applies every migration, and disables live generation. HTTP calls to Gemini/Expo are mocked. Database-dependent tests skip if Podman cannot start. .github/workflows/eas-update.ymlpublishes preview OTA on qualifying mobile pushes todevelop; manual dispatch also publishes preview only.eas-build.ymlis a manual Android build workflow.release.ymlis manually dispatched onmainafter operator confirmation of the deployed backend/worker SHA; its guard rejects missing/mismatched revisions and non-main dispatches. It publishes production then preview OTA, creates a version tag/GitHub release, and dispatchesrelease-apk.yml. APK publication is gated on nativeruntimeVersionchanges. Railway deploys the backend independently; followRELEASING.mdfor migration and release ordering.pr-quality.ymlis the general pull-request gate fordevelopandmain. It runs mobile Node 24 typechecking/tests and backend Ruff F/E9 plus pytest. The backend job installs Podman and fails if its disposable PostgreSQL 16 fixture skips, so a green backend result includes database coverage.migrations.ymlseparately checks the applied ledger onmainand PRs intomain; trusted main runs use a directproduction-schemaenvironment job.release.ymluses its own direct protected actual-schema job before OTA;production-schema.ymlremains independently dispatchable.audit.ymlruns dependency audits, Ruff, and TypeScript checks and files findings as issues.cleanup.ymlmanages stale issues; Dependabot schedules dependency updates with Expo-managed version restrictions.mobile/app.config.jscurrently has app version1.10.4and native runtime1.10.1;package.json's1.0.0is not the release-version authority.
These observations are recorded for future assigned work; setup does not change the implementation or older documentation:
- Backend/architecture prose still describes synchronous on-demand generation; selection now schedules background prefetch and widens the stored catalog.
/me/statedocumentation says one query; its aggregate is one query, followed by selection queries for the folded daily lesson.- Some comments describe the HTTP client as unwired or the repository as local only. Both are used by the authenticated application today.
mobile/DEPLOYMENT.mddescribes an older OTA trigger and fewer workflows. Use current workflow YAML plusRELEASING.mdto trace release behavior.- The roadmap's older offline milestones predate the current full-lesson cache, cached personalization, and foreground queue synchronization. Closed-app OS background scheduling remains outside the current APK's capabilities.
- The backend README's test-count/phase notes are historical. See WORK_LOG.md for the actual local validation baseline.
api/v1/profile_sharing.py exposes caller-owned settings and separately filtered
public JSON/browser reads. services/profile_sharing.py owns row locking, version
checks, earned-achievement filtering and revocable random tokens. Migration 0028
adds the backend-only sharing table; 0029 preserves stored timezones during phone
initialization. mobile/src/services/profileSharing.ts owns link parsing, local QR
and anonymous visitor requests. See profile-sharing.md for
privacy guarantees, migration order and phone acceptance checks.
api/v1/connections.py, schemas/connections.py and services/connections.py
provide the private consent lifecycle, request preferences, blocks and database
rate/cooldown limits. Migration 0030 adds the RLS-protected tables and pair/index
constraints. ConnectionsScreen.tsx owns private list/settings states;
ConnectionControls.tsx adds explicit actions to a shared-profile view. The typed
services/connections.ts client and transient publicProfileNavigation.ts keep
navigation/account boundaries separate from anonymous profile data. See
connections.md for API, privacy, deployment and acceptance steps.