feat(library): acquire exact chapters on demand - #172
Conversation
Context: Manifest identities were honest but missing chapters still required full-session replay and stayed disabled. Changes: Verify one immutable artifact in-browser, import/hydrate it in exact scope, route internal navigation through targeted acquisition, and expose remote availability in the dropdown. Impact: Artifact-backed chapters download on selection; missing references remain disabled; integrity failures never fall through to scraping. Tests: 6 focused files, 70 tests; full suite 301 files and 9,412 passed; TypeScript; lint; production build; diff check. Docs: CORE-015 targeted-acquisition amendment, reader guide, WORKLOG. ADR: CORE-015 Phase C acquisition slice.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5c2c753e0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const acquired = await acquirePublishedChapter({ | ||
| novelId: activeNovelId, | ||
| versionId: lookupVersionId, | ||
| chapterNumber: internalTarget.chapterNumber, | ||
| loadChapterFromIDB: loadChapterFromIDBCallback, |
There was a problem hiding this comment.
Discard acquisitions completed after the active scope changes
When a user selects a remote chapter and switches novels or versions before the registry/artifact requests finish, this awaited operation still returns the old scoped chapter. chaptersSlice.handleNavigate then unconditionally merges that result, sets it as currentChapterId, and reopens the reader, while this method also persists the stale chapter as last active. Revalidate the current novel/version after acquisition or make the operation cancellable so an obsolete request cannot navigate across scopes.
Useful? React with 👍 / 👎.
| availability: isVirtualStableId(summary.stableId) | ||
| ? (summary.publicationArtifact ? 'remote' : 'not-cached') | ||
| : 'ready', |
There was a problem hiding this comment.
Deduplicate remote placeholders against legacy summaries
When a cached legacy summary lacks chapterNumber but exposes it through a title such as Chapter 2, the initial number-based dedupe cannot match it. The later display-number fallback removes only not-cached placeholders, so classifying an artifact-backed placeholder as remote leaves both the ready chapter and a second “download on select” row for the same chapter; selecting the latter also performs an unnecessary re-import. Treat remote virtual rows as placeholders in the display-number dedupe as well.
Useful? React with 👍 / 👎.
| let bytes: ArrayBuffer; | ||
| try { | ||
| bytes = await response.arrayBuffer(); |
There was a problem hiding this comment.
Enforce the artifact size limit while streaming the response
When an artifact endpoint is stale, compromised, or misconfigured and returns a body larger than the manifest's accepted declaration, arrayBuffer() buffers the entire response before the byte-length check runs. Consequently the new 64 MiB mobile-memory guard does not bound actual download allocation, and a very large response can exhaust or freeze the browser before producing the intended integrity error. Reject an oversized Content-Length when available and otherwise read the stream with a hard byte cap.
Useful? React with 👍 / 👎.
…rgeted-acquisition # Conflicts: # docs/WORKLOG.md
|
Independent source review completed for this stack, including this PR's source at Final verdict: APPROVE for #169/#170/#171/#172, with no remaining LFS-contract blockers. Review Node 24.19 focused tests, types, production build and client/integrity checks passed on the integrated source. Production Chromium verifies the three late-navigation scope cases and the frozen-graph offline round trip. Real chapter-2 acquisition fetched one 5.34 MB published artifact in 2.61 seconds with zero full-session requests; cached offline navigation took 97 ms (single measurements, not semantic scan latency). Remaining acceptance limits: actual package main publication, complete novel/index, real semantic scan and physical mobile/Safari. Ordinary backup reopen without a graph loses active selection (CONS-07); readable chapters remain and its assertion is still failing. No full-feature acceptance is claimed. |
Summary
Selecting an uncached published chapter downloads and verifies that chapter alone, then imports it through the existing scoped storage path. Integrity failures remain visible and never fall through to scraping. Late downloads cannot navigate a different book or translation.
Changes
Validation
137 focused tests in 15 files pass on Node 24.19.0, plus types, production build and client security. Four production Chromium cases pass: three late-navigation scope changes and frozen-graph export/offline reopen/invalidation. Main-targeted CI will refresh after parent merge.
A real published Dungeon Defense chapter required one 5,344,477-byte download and zero full-session requests: 2.61 seconds cold navigation, 97 ms cached offline navigation (single observations, not semantic scan latency). This probe pinned package URLs to reviewed candidate bytes before main publication.
Independent Grok 4.6 source review approved the stack and publisher follow-up (
gen-1788706940-9sQzYYjBNNTha8hDheXr). All supplied file bytes were matched to anonymous public GitHub downloads. Codex review was requested but returned no verdict.Remaining acceptance
Merge after #171 and publish companion data separately. Ordinary two-chapter backup reimport retains readable chapters but loses active book/version selection (CONS-07); its assertion remains failing and is tracked for a focused follow-up. Frozen-graph selection is tested separately. Complete novel/index, real semantic scan and physical Safari/mobile checks remain open.