You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fluent Mobile has no local pericope data today. Bible and Record tabs are verse-scoped (bible_texts, recordings keyed by bible_text_id). The verse/pericope toggle work (#407, #408, #409) needs offline-readable pericope groupings (verse ranges, titles, cross-chapter spans) so UI tickets can query SQLite instead of hardcoding stubs.
This ticket adds the data layer only: schema, repository writes, sync step, and read queries. Settings toggle UI (#407) and tab rendering (#408 / #409) are separate.
Engineering decision (Slack, 2026-08-28): pericope = new local table(s) + sync via normal post-login sync — not bundled inside #407/#408 auth/UI work.
Bandwidth decision (2026-09-03): Mobile targets low-connectivity regions. Pericope data on fluent-api is set-scoped (project.pericopeSetId), not assignment-scoped. Production sync MUST use a hybrid set-level path — not one HTTP call per assigned chapter.
projects.pericope_set_id
→ if set content already in SQLite at matching version → keep (0 bytes)
→ else if set is shipped in APK assets → seed SQLite (0 runtime bytes)
→ else GET /pericope-sets/{id} with If-None-Match / version → upsert (1 request per set)
→ queries join set pericopes to assigned chapters locally
Writes: replace/upsert entire set snapshot (from asset or API).
Reads:getChapterPericopes(projectId, bookId, chapterNumber) — resolve project → pericope_set_id, then filter set rows for that chapter (include cross-chapter groups that touch the chapter).
5. Sync orchestration
In sync.ts after projects (+ assignments as needed for UI, not for pericope HTTP):
Collect distinct non-null pericope_set_id from synced projects.
For each set: seed from bundle if applicable; else conditional set API fetch; else keep cache.
Never N chapter GETs.
6. Tests
Repository upsert/replace for a set; chapter query including cross-chapter verse lists.
Sync: skips null set; seeds from mock asset; calls set API once per distinct setId; does not call chapter route.
Conditional refresh / version match keeps cache without rewrite when possible.
Explicit non-goals
Chapter-wise sync loop for production mobile (forbidden — see bandwidth contract).
Bulk-by-assigned-chapters as the primary sync API (use set-level instead).
Summary
Fluent Mobile has no local pericope data today. Bible and Record tabs are verse-scoped (
bible_texts, recordings keyed bybible_text_id). The verse/pericope toggle work (#407, #408, #409) needs offline-readable pericope groupings (verse ranges, titles, cross-chapter spans) so UI tickets can query SQLite instead of hardcoding stubs.This ticket adds the data layer only: schema, repository writes, sync step, and read queries. Settings toggle UI (#407) and tab rendering (#408 / #409) are separate.
Engineering decision (Slack, 2026-08-28): pericope = new local table(s) + sync via normal post-login sync — not bundled inside #407/#408 auth/UI work.
Bandwidth decision (2026-09-03): Mobile targets low-connectivity regions. Pericope data on fluent-api is set-scoped (
project.pericopeSetId), not assignment-scoped. Production sync MUST use a hybrid set-level path — not one HTTP call per assigned chapter.Why this blocks downstream tickets
/self/settings(out of scope here).Without this ticket, implementers must stub pericope lists in UI hooks — exactly what we want to avoid.
Bandwidth / sync contract (authoritative)
pericope_set_id, not by(project, book, chapter)API round-tripspericope_set_idwhen missing or version mismatch; prefer 304 / ETag when unchanged (fluent-api#309)pericope_set_id IS NULL→ verse mode only; no errorGET /projects/{id}/pericopes/{bookCode}/{chapter}for every assigned chapterChapter-wise project route may remain for web on-demand use; mobile sync must not use it.
Bulk-by-assigned-chapters (mirror of
bulk-texts) is not the primary contract — set-level already covers shared sets with fewer bytes and fewer RTTs.Fluent API
Exists today
GET/pericope-setsGET/projects/{id}/pericopes/{bookCode}/{chapter}Projects expose
pericopeSetId. MobileApiProject/projectstable do not map this yet — include here.Required (sibling)
GET/pericope-sets/{id}(optional?bookCode=) + ETag / 304Until #309 lands, mobile can still ship schema + asset seed + queries; wire the network refresh when the set endpoint exists.
Response shape for groups (same as chapter route, aggregated by set):
versesmay span multiple chapters (required for Update Bible Tab for Verse/Pericope Toggle #408 Note 4).Current mobile state
src/db/schema.ts.FluentAPIpericope methods insrc/services/api.ts.syncAllData/sync.ts.books.code(USFM) is already synced.Proposed scope
1. Schema + migration
projects.pericope_set_id— nullable integer from project sync.pericope_set_id:version/etag/content_hash,source(bundle|api),updated_at.pericope_number,pericope_title, verse membership (prefer normalized child rows for Update Bible Tab for Verse/Pericope Toggle #408 partial-recording lookups).Follow adding a synced entity:
schema.ts+migrations.ts+src/types/db/types.ts.2. API client + types
PericopeGroup,PericopeVerseRef,PericopeSet).FluentAPI.getPericopeSet(id, opts?)— set-level download with conditional headers once fluent-api#309 exists.FluentAPI.getPericopeSets()for metadata.getChapterPericopesin a chapter loop.3. Asset seed (depends on #447)
pericope_set_id/ set name → bundled JSON.source = bundle.4. Repository + queries
getChapterPericopes(projectId, bookId, chapterNumber)— resolve project →pericope_set_id, then filter set rows for that chapter (include cross-chapter groups that touch the chapter).5. Sync orchestration
In
sync.tsafter projects (+ assignments as needed for UI, not for pericope HTTP):pericope_set_idfrom synced projects.6. Tests
Explicit non-goals
/self/settingsdisplayMode.bible_text_idunless a future API ticket says otherwise).Acceptance criteria
projects.pericope_set_id; fresh and upgraded DBs migrate cleanly.pericope_set_idvalues are hydrated at most once per sync (bundle and/or set API).queries.tsexposes chapter pericope groups for(projectId, bookId, chapter)offline.format:check,lint,architecture-guard,typecheck,npm test -- --ci).How to verify
pericope_set_id— airplane mode after install/seed path: chapter query still returns groups.pericope_set_id: sync succeeds; query returns empty list..../pericopes/{book}/{chapter}spam for N chapters.File pointers
src/db/schema.ts,src/db/migrations.tssrc/services/api.ts,src/types/api/src/services/sync.tssrc/db/repository.tssrc/db/queries.tssrc/services/mapApiProject.ts,src/types/api/responses.tsuseChapterPericopes.tsRelated