Reading window 3/3: picker and history #27

Merged
claude-bot merged 11 commits from phase1/picker-history into main 2026-10-22 07:24:22 +00:00
Collaborator

What

The reference button opens a book → chapter → verse picker (module-language names), and a hidden-by-default typing field (Settings) that jumps to "jn 3,16"-style references. Every jump is recorded in history, and Back returns to the previous place, even after the app is killed. Part of #11 (reading window 3/3).

Blast radius

Tier: trunk
Trunk paths touched: core/src/db/ (history.rs), core/src/worker.rs + worker/session*, qt/ glue (reading_model.rs, picker_model.rs, build.rs, lib.rs), qml/Main.qml, qml/pages/ReadingPane.qml, tools/mem-check/
Testing tier: standard (no migration: the history table is in 001; no dependency or CI-config change)
Gate / rollback: revert this merge. No schema change, and the rows written to the existing history table are harmless to the previous build.

Proof

  • Rust gates: cargo make exit 0, core lib 64 passed / 0 failed, line coverage 96.3%, deny and audit ok
  • App gates: cargo make app-docker exit 0 (format, qmllint, -Werror build, clang-tidy, mem-check)
  • Picker round trip (mem-check): picker: saved BSB 265 0.455, left BSB 265 0.455, Back to BSB 265 0.455, on screen BSB 265 0.455
  • Back survives a kill (mem-check): after kill -9 with history: Back to BSB 265 0.455, on screen BSB 265 0.455
  • Typing field (mem-check): hidden at first, shown after the setting, jn 3,16 jumped to John 3
  • Fails without the change: every new core, worker and mem-check test failed under its matching mutation; the two later fixes' tests (back_keeps_an_entry_this_bible_cannot_place, back_skips_an_entry_this_bible_cannot_place_to_the_one_below, one_number_after_a_one_chapter_book_is_the_verse) each fail on the old code
  • Click: cargo make click → utstudybible.schaefera_0.0.1_arm64.click: pass
  • CI: rust, audit, app and click green on c55be1f (the last code commit)
  • Budgets (docs/07): desktop CI start-up 93–99 ms vs main's 90–93 ms, peak PSS 146.5–152.3 MB vs 149.5–151.9 MB, both within noise. Picker data is about 3 kB, and the picker and settings pages compile only when opened. The 80 MB / 2 s device budgets are not measured (phone check, human).
  • Not covered: no QML unit tests beyond the mem-check taps. Not exercised: the repeated-error tooltip (send() clears error) and Enter on an empty field doing nothing. No arm64 phone run.

Review

Reviewer: fresh-context review at high (7ce7a1a): 4 findings (1 medium, 3 low), all 4 fixed in a9c61e5. Re-review at medium (a9c61e5): 1 low (Back dead end), fixed after review in c55be1f (pop until placeable, restore the skipped entries). 0 declined, 0 deferred, 0 left open.
Review-Confidence: 50/100. The core logic is well covered and CI is green. Points off for the two untested QML/glue fixes, the unreviewed c55be1f rewrite of back(), and findings reported without line numbers.

Confidence by part

  • core picker, lookup and history (core/src/bible/picker*, db/history.rs, worker/session/jump.rs): high. Unit and worker tests with mutation evidence.
  • Session::back after c55be1f: medium. Tested and audited, but no review round.
  • qt glue (picker_model.rs, reading_model.rs error clearing): medium. Only mem-check exercises it.
  • QML pages (PickerPage.qml, SettingsPage.qml, the ReadingPane tooltip): low. Read these: layout is checked only through mem-check taps at 540x960, and the error tooltip paths are untested.

Follow-ups: #25 (gettext) edits tools/mem-check and worker/session.rs too, so it gets a hand merge after this lands. Run cargo make i18n after both are merged.

🤖 Generated with Claude Code

## What The reference button opens a book → chapter → verse picker (module-language names), and a hidden-by-default typing field (Settings) that jumps to "jn 3,16"-style references. Every jump is recorded in history, and Back returns to the previous place, even after the app is killed. Part of #11 (reading window 3/3). ## Blast radius **Tier:** trunk **Trunk paths touched:** core/src/db/ (history.rs), core/src/worker.rs + worker/session*, qt/ glue (reading_model.rs, picker_model.rs, build.rs, lib.rs), qml/Main.qml, qml/pages/ReadingPane.qml, tools/mem-check/ **Testing tier:** standard (no migration: the history table is in 001; no dependency or CI-config change) **Gate / rollback:** revert this merge. No schema change, and the rows written to the existing history table are harmless to the previous build. ## Proof - Rust gates: `cargo make` exit 0, core lib 64 passed / 0 failed, line coverage 96.3%, deny and audit ok - App gates: `cargo make app-docker` exit 0 (format, qmllint, -Werror build, clang-tidy, mem-check) - Picker round trip (mem-check): `picker: saved BSB 265 0.455, left BSB 265 0.455, Back to BSB 265 0.455, on screen BSB 265 0.455` - Back survives a kill (mem-check): `after kill -9 with history: Back to BSB 265 0.455, on screen BSB 265 0.455` - Typing field (mem-check): `hidden at first, shown after the setting, jn 3,16 jumped to John 3` - Fails without the change: every new core, worker and mem-check test failed under its matching mutation; the two later fixes' tests (`back_keeps_an_entry_this_bible_cannot_place`, `back_skips_an_entry_this_bible_cannot_place_to_the_one_below`, `one_number_after_a_one_chapter_book_is_the_verse`) each fail on the old code - Click: `cargo make click` → `utstudybible.schaefera_0.0.1_arm64.click: pass` - CI: rust, audit, app and click green on c55be1f (the last code commit) - **Budgets (docs/07):** desktop CI start-up 93–99 ms vs main's 90–93 ms, peak PSS 146.5–152.3 MB vs 149.5–151.9 MB, both within noise. Picker data is about 3 kB, and the picker and settings pages compile only when opened. The 80 MB / 2 s device budgets are not measured (phone check, human). - **Not covered:** no QML unit tests beyond the mem-check taps. Not exercised: the repeated-error tooltip (`send()` clears `error`) and Enter on an empty field doing nothing. No arm64 phone run. ## Review **Reviewer:** fresh-context review at high (7ce7a1a): 4 findings (1 medium, 3 low), all 4 fixed in a9c61e5. Re-review at medium (a9c61e5): 1 low (Back dead end), fixed after review in c55be1f (pop until placeable, restore the skipped entries). 0 declined, 0 deferred, 0 left open. **Review-Confidence:** 50/100. The core logic is well covered and CI is green. Points off for the two untested QML/glue fixes, the unreviewed c55be1f rewrite of `back()`, and findings reported without line numbers. ## Confidence by part - core picker, lookup and history (`core/src/bible/picker*`, `db/history.rs`, `worker/session/jump.rs`): high. Unit and worker tests with mutation evidence. - `Session::back` after c55be1f: medium. Tested and audited, but no review round. - qt glue (`picker_model.rs`, `reading_model.rs` error clearing): medium. Only mem-check exercises it. - QML pages (`PickerPage.qml`, `SettingsPage.qml`, the ReadingPane tooltip): low. Read these: layout is checked only through mem-check taps at 540x960, and the error tooltip paths are untested. Follow-ups: #25 (gettext) edits `tools/mem-check` and `worker/session.rs` too, so it gets a hand merge after this lands. Run `cargo make i18n` after both are merged. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GoTo is replaced by Jump (a verse) and JumpText (a typed reference). A jump pushes the verse at the top of the window onto the Back history, which is saved at once, with the jump. The picker's books and verse counts are built once per Bible and sent as an event for the glue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
qml, tools: the pages open by URL, off the start-up path; mem-check types a reference after switching the setting on
Some checks failed
CI / audit (pull_request) Has been cancelled
CI / app (pull_request) Has been cancelled
CI / click (pull_request) Has been cancelled
CI / rust (pull_request) Has been cancelled
7ce7a1a53b
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
core, qt, qml: Back keeps an entry it can't place; one number after a one-chapter book is the verse; the same typed error shows again; an empty field does nothing
All checks were successful
CI / rust (pull_request) Successful in 2m5s
CI / audit (pull_request) Successful in 9s
CI / app (pull_request) Successful in 2m5s
CI / click (pull_request) Successful in 58s
a9c61e520f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
core: Back skips entries this Bible can't place to the first one it can, and keeps them
All checks were successful
CI / rust (pull_request) Successful in 2m14s
CI / audit (pull_request) Successful in 9s
CI / app (pull_request) Successful in 1m59s
CI / click (pull_request) Successful in 50s
c55be1f4ae
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore: record review result
All checks were successful
CI / rust (pull_request) Successful in 2m12s
CI / audit (pull_request) Successful in 9s
CI / app (pull_request) Successful in 1m56s
CI / click (pull_request) Successful in 56s
8bceea5fac
Review-Confidence: 50
Review-Tier: trunk
claude-bot changed title from WIP: Reading window 3/3: picker and history to Reading window 3/3: picker and history 2026-10-21 20:10:28 +00:00
claude-review left a comment

Fresh-context review (high), reviewed 7ce7a1a — posted as a record by the gate reviewer

  • medium · qml/pages/ReadingPane.qml (ToolTip error binding) + qt/src/reading_model.rs show_error · the tooltip fires only when error changes; a failed jump leaves the string as it was, so typing the same bad reference twice shows no feedback the second time.
  • low · core/src/worker/session.rs resolve() and core/src/worker/session/jump.rs back() · an entry the open Bible can't place falls back to Gen 1:1 after back() has popped it, so Back jumps silently to Genesis and the entry is lost.
  • low · core/src/bible/picker/lookup.rs lookup() · one number after a one-chapter book is read as a chapter: 'Jude 5', 'Obad 3', '3 John 4' show 'unknown reference'.
  • low · qml/pages/PickerPage.qml typed.onAccepted · Enter on an empty field sends JumpText('') and shows a blank error tooltip after the picker pops.

Files reviewed: CLAUDE.md, core/src/bible.rs, core/src/bible/picker.rs, core/src/bible/picker/lookup.rs, core/src/bible/target.rs, core/src/bible/place.rs, core/src/bible/reader.rs, core/src/db/history.rs, core/src/worker.rs, core/src/worker/session.rs, core/src/worker/session/jump.rs, qt/src/picker_model.rs, qt/src/reading_model.rs, qml/Main.qml, qml/pages/ReadingPane.qml, qml/pages/PickerPage.qml, qml/pages/SettingsPage.qml, tools/mem-check/run.sh, docs/superpowers/specs/2026-10-20-reading-window-design.md

Fresh-context review (high), reviewed 7ce7a1a — posted as a record by the gate reviewer - medium · qml/pages/ReadingPane.qml (ToolTip error binding) + qt/src/reading_model.rs show_error · the tooltip fires only when `error` changes; a failed jump leaves the string as it was, so typing the same bad reference twice shows no feedback the second time. - low · core/src/worker/session.rs resolve() and core/src/worker/session/jump.rs back() · an entry the open Bible can't place falls back to Gen 1:1 after back() has popped it, so Back jumps silently to Genesis and the entry is lost. - low · core/src/bible/picker/lookup.rs lookup() · one number after a one-chapter book is read as a chapter: 'Jude 5', 'Obad 3', '3 John 4' show 'unknown reference'. - low · qml/pages/PickerPage.qml typed.onAccepted · Enter on an empty field sends JumpText('') and shows a blank error tooltip after the picker pops. Files reviewed: CLAUDE.md, core/src/bible.rs, core/src/bible/picker.rs, core/src/bible/picker/lookup.rs, core/src/bible/target.rs, core/src/bible/place.rs, core/src/bible/reader.rs, core/src/db/history.rs, core/src/worker.rs, core/src/worker/session.rs, core/src/worker/session/jump.rs, qt/src/picker_model.rs, qt/src/reading_model.rs, qml/Main.qml, qml/pages/ReadingPane.qml, qml/pages/PickerPage.qml, qml/pages/SettingsPage.qml, tools/mem-check/run.sh, docs/superpowers/specs/2026-10-20-reading-window-design.md
claude-review left a comment

Fresh-context review (medium), reviewed a9c61e5 — posted as a record by the gate reviewer

  • low · core/src/worker/session/jump.rs:58-68 (back) · history [A placeable, B unplaceable here] with B on top: Back pops B, can't place it, pushes it back and shows an error; every later Back does the same, so A is unreachable in this Bible. Suggested: skip unplaceable entries to the first placeable one and restore the skipped ones.

Files reviewed: core/src/bible/picker/lookup.rs, core/src/bible/picker.rs, core/src/worker.rs, core/src/worker/session.rs, core/src/worker/session/jump.rs, core/src/db/ (push_history, pop_history), qml/pages/PickerPage.qml, qml/pages/ReadingPane.qml (error use), qt/src/reading_model.rs

Fresh-context review (medium), reviewed a9c61e5 — posted as a record by the gate reviewer - low · core/src/worker/session/jump.rs:58-68 (back) · history [A placeable, B unplaceable here] with B on top: Back pops B, can't place it, pushes it back and shows an error; every later Back does the same, so A is unreachable in this Bible. Suggested: skip unplaceable entries to the first placeable one and restore the skipped ones. Files reviewed: core/src/bible/picker/lookup.rs, core/src/bible/picker.rs, core/src/worker.rs, core/src/worker/session.rs, core/src/worker/session/jump.rs, core/src/db/ (push_history, pop_history), qml/pages/PickerPage.qml, qml/pages/ReadingPane.qml (error use), qt/src/reading_model.rs
claude-review left a comment

Gate review (medium)

Prior findings, checked against the code at head:

  • r1 medium (tooltip not re-shown for the same error): fixed in a9c61e5. ReadingModel::send() clears error, so a repeat failure is a change. Glue/QML, no automated test (disclosed in the PR).
  • r1 low (Back to an unplaceable entry jumped to Gen 1:1 and lost it): fixed in a9c61e5, extended in c55be1f. Test back_keeps_an_entry_this_bible_cannot_place.
  • r1 low (Jude 5 / 3 John 4 unknown): fixed in a9c61e5. Test one_number_after_a_one_chapter_book_is_the_verse.
  • r1 low (Enter on empty field): fixed in a9c61e5, PickerPage.qml returns early on empty trimmed text.
  • r2 low (Back dead end behind an unplaceable entry): fixed in c55be1f.

c55be1f verdict: no high or medium defect. Session::back pops until an entry resolves, then re-pushes the skipped entries oldest-first, so relative order is kept (they sit above the remaining ones, which matches their original order relative to the entry consumed). A pop error ends the loop, so no infinite loop. HISTORY_CAP trimming in push_history can't drop entries here because each restore follows an equal number of pops. The error shows only when nothing was placeable. The test back_skips_an_entry_this_bible_cannot_place_to_the_one_below checks the jump target, that the skipped entry is kept and history_len is 1.

  • low · core/src/worker/session/jump.rs:58-80 · pop and re-push are separate transactions, so a kill between them loses the skipped entries. The window is microseconds and the entries are only for another Bible. Not blocking.

CI on c55be1f: rust success, audit success, app success, click success. 8bceea5 (empty trailer commit): also success on all four jobs.

Testing tier: standard is right. No migration (history table is in 001), no dependency or CI change. The PR Proof lists no unrun elevated, maximum or AI-behavior gate; the untested QML/glue paths and the missing phone run are disclosed under Not covered.

Gate review (medium) Prior findings, checked against the code at head: - r1 medium (tooltip not re-shown for the same error): fixed in a9c61e5. ReadingModel::send() clears `error`, so a repeat failure is a change. Glue/QML, no automated test (disclosed in the PR). - r1 low (Back to an unplaceable entry jumped to Gen 1:1 and lost it): fixed in a9c61e5, extended in c55be1f. Test back_keeps_an_entry_this_bible_cannot_place. - r1 low (Jude 5 / 3 John 4 unknown): fixed in a9c61e5. Test one_number_after_a_one_chapter_book_is_the_verse. - r1 low (Enter on empty field): fixed in a9c61e5, PickerPage.qml returns early on empty trimmed text. - r2 low (Back dead end behind an unplaceable entry): fixed in c55be1f. c55be1f verdict: no high or medium defect. Session::back pops until an entry resolves, then re-pushes the skipped entries oldest-first, so relative order is kept (they sit above the remaining ones, which matches their original order relative to the entry consumed). A pop error ends the loop, so no infinite loop. HISTORY_CAP trimming in push_history can't drop entries here because each restore follows an equal number of pops. The error shows only when nothing was placeable. The test back_skips_an_entry_this_bible_cannot_place_to_the_one_below checks the jump target, that the skipped entry is kept and history_len is 1. - low · core/src/worker/session/jump.rs:58-80 · pop and re-push are separate transactions, so a kill between them loses the skipped entries. The window is microseconds and the entries are only for another Bible. Not blocking. CI on c55be1f: rust success, audit success, app success, click success. 8bceea5 (empty trailer commit): also success on all four jobs. Testing tier: standard is right. No migration (history table is in 001), no dependency or CI change. The PR Proof lists no unrun elevated, maximum or AI-behavior gate; the untested QML/glue paths and the missing phone run are disclosed under Not covered.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
schaefera/utStudyBIble!27
No description provided.