Skip to content

Re-anchor every linked book in the host - #507

Draft
imnasnainaec wants to merge 10 commits into
mainfrom
449-host-reanchor
Draft

imnasnainaec wants to merge 10 commits into
mainfrom
449-host-reanchor

Conversation

@imnasnainaec

@imnasnainaec imnasnainaec commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Part of #426; closes #449.

Summary

The host re-anchors every book the session's draft links into, in the session's turn, as the whole-text read delivers each book and again when a book's text changes.

openSession
  startTextRead                       # a fresh read; closing the session ends it
BookService
  onBookRead(book)         -> session turn: re-anchor the book, record the pass
  onTextRead(textBookIds)  -> session turn: stale the links into books the text lacks
  onBookTextChanged        -> after the session's turn: emit onAnalysisChanged
undo / redo
  re-anchor what the step restores to each book passed over since, in memory
  apply the step and its re-anchor as one
  • The pass reads one book through Add derived indexes with a rebuild-and-diff command #487's lookups (getScope, findLinksInBooks, getSegmentAnalyses), runs the WebView's own reanchorDraftToBook over it, and applies the links it rewrote, with the scope's record when it moved a split.
  • Undo. A pass is no undo step. EditHistory records each pass while a step predates it, and an undo or redo re-anchors what it restores to the latest text of each book passed over since, as recordBookPass does on main.
  • Open book. An edit to the open book that the read has yet to reach waits for the book's new text, hands it to onBookRead, then is reported, so its pass runs before the refetch.
  • Read-only sessions aren't re-anchored, as main leaves the import view alone.

Evidence

  • Parity with the WebView passes. Every case of reanchor-analysis.test.ts (88) and reanchor-draft.test.ts (7) now also checks that the host's change set brings the draft to the WebView pass's result, and that the pass over the book's links alone, as the host reads them, makes the same changes. Five fixtures that gave two links one id now give each its own, as the host's records need.

  • The Data layer: capture a performance baseline before the database migration #407 tiers. perf:bench re-anchors every book of each tier both ways, ignoring the time each pass stamps:

    tier books changed by WebView by host disagreeing host, all books
    small 82 1 1 0 0.8 s
    medium 82 1 1 0 1.8 s
    large-loadable 82 67 67 0 3.4 s
    large 82 72 72 0 4.5 s

    One run each, on a development machine. The changed books hold the tiers' cross-verse phrases, which the pass stales on both paths.

  • Undo and redo. An edit, then a Scripture edit and its pass, then an undo restores the earlier link on the word's new ref; a redo restores the later one there too.

  • A mid-session Scripture edit. BookService hands the edited book to onBookRead before reporting the edit, the pass runs in the session's turn, and onAnalysisChanged waits for that turn.

  • npm run test:coverage passes at 100%; npm run lint and tsc are clean.

Decisions with a strong alternative

  1. Every book is read at session open. The 2e checkpoint measured about 1.4 s and +38 MB of host heap. Alternative: read on demand, as main does.
  2. Session open starts the read and close ends it, here rather than in 4a. Open doesn't wait for the book list; a failure to list it shows up where getTextReadStatus reports it. Each session gets a fresh read, so one opened after an earlier read still hears of every book.
  3. No onAnalysisChanged after a pass on a book's first arrival. Until 3k the event only names a Scripture edit; 3k's touched ranges can cover passes.
  4. An undo or redo and its re-anchor are one apply, so a failure applies nothing and the step stays. Alternative: apply the step, then re-anchor, and leave stale anchors when the second fails.
  5. The open book's edit waits for its new text before it's reported. The view's refetch already waited for that tokenizing, so no view waits longer.
  6. getScope is copied from [3d] Add token commands with parity tests #443 rather than stacking on it. Whichever lands second takes the other's version.
  7. Books the text lacks are looked for among the canon's book codes, as presentBookIds lists books present.

Follow-up

Merge Danger

Door: two-way

No released build opens a session, so no pass runs. Released builds see one change: since they never start the whole-text read, each edit to the open book is reported once the book's new text is tokenized instead of at once.

Blast Radius: BookService's edit reporting

🤖 Generated with Claude Code


This change is Reviewable

imnasnainaec and others added 10 commits October 23, 2026 17:28
3j re-anchors each linked book as the read delivers it, so the read now
hands its tokenized text to onBookRead listeners, ahead of the edit
report for a reread. onTextRead and getTextBookIds name the books
holding text once every book has read, for staling links into the rest.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host's pass runs the WebView's own pass over a draft and puts each
link it rewrote, with the draft's record when it moved a split. Parity
tests check the result against the WebView pass on its drift and
missing-book cases, from the whole draft and from one book's analysis.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EditHistory records each pass a session runs while a step predates it,
and after an undo or redo re-anchors what it restored to the latest text
of each book passed over since that step was captured, as the WebView's
history does. The undo or redo returns its changes merged with the
re-anchor's. A pass is no step, and a pass no held step predates is
forgotten.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each case of reanchor-analysis.test.ts and reanchor-draft.test.ts now
also checks that the host's change set brings the draft to what the
WebView's pass gave, and that the pass over the book's analysis alone
makes the same changes. Five fixtures that gave two links one id now
give each its own, as the host's records need.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
perf:bench now re-anchors every book of each tier both ways, the host's
from the book's analysis alone, and reports books changed by each and
books where they disagree, ignoring the time each pass stamps. On every
tier they agree. The tiers' cross-verse phrases stale on first arrival:
1 book changes on small and medium, 67 on large-loadable, 72 on large.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The history now asks for the re-anchor of what a step would restore
before applying anything, and applies the step merged with it in one
apply, so a failure applies nothing and leaves the step where it was,
and the inverse a redo replays covers the re-anchor's changes too.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While the whole-text read has yet to reach the open book, its edit was
reported at once, so a pass could not run before the view's refetch.
The edit now waits for the book's new text, hands it to onBookRead's
listeners, then is reported; the view's refetch waited on that
tokenizing already.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A re-anchor pass needs the draft's splits and its record, so the
repository gains getScope, answered from the kept draft. It matches the
getScope 3d (#443) adds, so whichever lands second can take the other's.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
startTextRead reads the text afresh, ending any read under way, so a
session opened after an earlier read still hears of every book; it
doesn't wait on the book list, leaving a failure for the status to
report. stopTextRead ends the read and leaves the view's books kept.
getTextBookIds goes, since a fresh read reports to onTextRead.

Part of #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a session starts its whole-text read and closing it ends it.
Each book the read delivers, and each edited book, re-anchors in the
session's turn through the repository's lookups, as no undo step but
recorded for undo and redo; books the text lacks stale once the read is
done. onAnalysisChanged for an edit waits for the session's turn, so a
refetch sees re-anchored links. A read-only session isn't re-anchored.

Closes #449.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[3j] Re-anchor every linked book in the host

1 participant