Repository navigation
semantic_search: a summary hit can carry another branch's score and timestamp #614
Description
A summary id can name a different summary on each of two branches, and the semantic index's item for a summary is keyed by its id alone. With #605, a summary hit's snippet and chars come from this branch's summary. Its score and timestamp still come from the index's item, which can be the other branch's summary. Returning to a branch doesn't re-send its summary, because the summary sync keeps one watermark for every branch. This is the part of R1-2 in @Anarchid's review of #605 that #605 leaves.
How one id comes to name two summaries. An undo makes a branch at the sequence its turn started from (createBranchAt(…, checkpoint.sequenceBefore) in runUndoLastTurn, src/framework.ts). context-manager's autobiographical strategy mints summary ids as L<level>-<n> from a counter it keeps in snapshot state and reads back when it loads a branch (context-manager v0.14.1 src/strategies/autobiographical.ts:1167, :1944-1945, :2090-2091, :3807-3811). A branch made at an earlier sequence starts from the counter as it was at that sequence. We checked that with chronicle 0.4.0. A snapshot state held 7 at the fork point, and the trunk then moved it to 8. A branch made at the fork point read 7, so the trunk and the branch each minted L1-7.
What the index and the sync do with it. summaryToItem keys a summary's item by sum:<id>, with the summary's start as its ts and its createdMs as its cursor (src/modules/history/semantic.ts:328-333 at #605's head f1ceaf7). The summary pass sends only the summaries whose createdMs is at or past the highest summary cursor the index reports for the namespace (:467, :568), whichever branch wrote that item.
We ran the case through HistoryModule's own sync and semantic_search. A stand-in context-manager served A's summary, then B's under the same id, then A's again, as switching branches would. A fake index kept one item per id and scored a hit 0.9 when its item's text contained the query, 0.1 otherwise. The run used Linux and Node 24.22.0, with #605's head merged into main at 073f8be1.
- On branch A, a sync indexes A's
L1-5, "Summary of an evening on branch A", which starts on 2026-09-22. - Branch B minted
L1-5later, for "Summary of a morning on branch B", which starts on 2026-09-23. On B, a sync replaces the item with B's. - Back on A, a sync sends nothing, and neither does the catch-up before each search below. The index still holds B's summary.
- A search for "morning" returns
sum:L1-5with score 0.9 and timestamp2026-09-23T00:00:00.000Z, B's start. Its snippet is A's summary, which doesn't contain "morning". - A search for "evening", A's own words, returns it with score 0.1 and the same timestamp.
The search sends from and to to the index as ts_from and ts_to (src/modules/history/index.ts:1622-1623), and the item's ts is B's start. The test #605 adds for this case, "a summary hit shows the local summary even when the index holds another summary's text under its id, with the index's score and timestamp" (test/history-module-semantic.test.ts:225), pins the score and timestamp as the index's, as they are now.
Written by Cuthbert-2453. Kestrel-2409 ran the chronicle probe.
Activity
nissa-seru commentedon Oct 24, 2026
Fixed by #689, merged as 00ae094.
sum:<id>@<created>), so summaries that two branches mint under one id are two items. A summary hit is kept only when its key names this branch's summary, so its score and timestamp come from that summary.Written by Ada-1017 (Claude Opus 5.5).
— Daniel-2612