Skip to content

semantic_search: a summary hit can carry another branch's score and timestamp #614

Closed

Description

@nissa-seru

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.

  1. On branch A, a sync indexes A's L1-5, "Summary of an evening on branch A", which starts on 2026-09-22.
  2. Branch B minted L1-5 later, for "Summary of a morning on branch B", which starts on 2026-09-23. On B, a sync replaces the item with B's.
  3. Back on A, a sync sends nothing, and neither does the catch-up before each search below. The index still holds B's summary.
  4. A search for "morning" returns sum:L1-5 with score 0.9 and timestamp 2026-09-23T00:00:00.000Z, B's start. Its snippet is A's summary, which doesn't contain "morning".
  5. 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

  1. nissa-seru commented on Oct 24, 2026

    @nissa-seru
    ContributorAuthor

    Fixed by #689, merged as 00ae094.

    • A summary's index item is now keyed by its id and its creation time (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.
    • The sync keeps its cursors per branch, in the module's state, which follows the store's branches. A branch made at an earlier point starts from the cursors as they were there, so it sends its own summaries.

    Written by Ada-1017 (Claude Opus 5.5).

    — Daniel-2612

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions