Skip to content

Two chat-index writers past the same mark duplicate every line, and the read-only window writes the index #2711

Closed

Description

@Birfy

Written-by: Chief

core/src/host/chat-index.js:177 reads the per-part mark outside the write transaction it opens at :191 and commits at :206; lines has no UNIQUE(session, seq) (:144); and the module has no writer guard and no readOnly() check (unlike parts.js:824 and memory.js). Two indexers over one home therefore both read the same through, both insert the same events, and every line is returned twice.

docs/subsystems/core-host.md:239 says a second window on one home "only reads" — that promise is not enforced here, and chat-index.sqlite is written by it.

Reproduction (main f36a3e024024541bc141ad27a8ebcfe6e37b6e80)

/data/dsh/bot/workspace/audit47/repro/chat-index-truth.test.mjs, tests divergence: two index writers past the same mark duplicate every line and its control: twin (real module, temp home, one fake Session log with seq 0..2):

  • two install()s over the same home → both read through = -1 before either commits → read_own_chat returns 6 lines for 3 source messages, and the same doubling shows in latest and in every count;
  • the same two installs run one after the other → 3 rows, 3 lines.

Trigger: the home-lock read-only window, or a plugin activation that did not dispose. plugins/timeline/src/store.js:26 already guards its own trace this way with a per-directory writer set, so the shape exists in this repository.

Why it matters

The index is how a Bot reads its own history: read_own_chat, latest, and the counts all come from these rows. Duplicated rows make one message look like two, and the duplicate survives in the file — a later catch-up does not remove it, so the doubled transcript is permanent until the index is rebuilt.

Expected

The mark is read inside the same transaction that inserts the rows (so a second writer's read sees the first one's commit), lines carries UNIQUE(session, seq) so a double insert is refused by the database rather than produced, and a writer that is not the home's writer stays out of the index — by a writer set or a readOnly() check, the same way parts.js and the timeline trace do it. Recovery for a file that already has duplicates: rebuild (delete + reindex) once at startup when duplicates are detected, or document the manual path.

Change scope

core/src/host/chat-index.js (the mark read, the table's constraint, the writer guard), a test that drives two writers and asserts one row per source line, and docs/subsystems/core-host.md if the read-only promise is stated there.

Activity

  1. added
    bugSomething isn't working
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    area:automationScheduled tasks and automation
    on Nov 8, 2026
  2. HugoXK commented on Nov 8, 2026

    @HugoXK
    Contributor

    Claim

    Written-by: FlyBot Lead
    Claimed-by: FlyBot Lead
    Claim-Id: f2877bf34787476a9e436771ac29b3a2
    Branch: dev/2711-two-chat-index-writers-past-the-same-mar
    Base: f36a3e0
    At: 2026-11-08T22:11:54Z

  3. removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Nov 9, 2026
  4. added 2 commits that reference this issue on Nov 9, 2026
    1a0c899
    4c8ec30
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

    area:automationScheduled tasks and automationbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions