Skip to content

feat(insights): the store indexes the words (M19c) - #36

Open
jgrodziski wants to merge 4 commits into
feat/m19c-words-in-explainfrom
feat/m19c-insight-word
Open

jgrodziski wants to merge 4 commits into
feat/m19c-words-in-explainfrom
feat/m19c-insight-word

Conversation

@jgrodziski

Copy link
Copy Markdown
Contributor

Second step of milestone M19c in PLAN.md §20 (word cloud / glossary, #6). Stacked on #35: review that first. This PR's diff is the four commits after #35.

The insights store moves to version 2. A new table, insight_word, holds one row per word a record defines, indexed by word and by record id, so the readers in PR 11 (insights --word, /glossary.json…) look a word up through an index instead of scanning every block.

The table (packages/insights)

  • One row per entry of a block's words, in block order. The row keeps the entry as JSON (entry); word, concern, sense and confidence are generated columns over it, as concept already is over insight.block. A column string holding U+0000 reads back cut short through node:sqlite, so a side-car that decodes would otherwise fail its round trip.
  • in stays inside entry: it is a reserved word in SQL, and nothing filters on it.
  • No meaning. It was a third of the table. A reader that shows it reads the record's block.
  • Kept in step with the records. A put deletes the record's rows, then writes them, in the same transaction as the record. --import rebuilds the table. Nothing is exported from it: the side-car round trip is unchanged.

The migration from version 1

INSIGHTS_DB_VERSION moves from 1 to 2. The migration creates the table and fills it from the words already stored. PLAN expected nothing to fill, but #35's build writes words into a store still at version 1. A migration that fails leaves the file at version 1, untouched.

The test is a real version-1 file: a sqlite3 .dump of a store #35's build wrote (test/fixtures/store-v1.sql), with that build's export and the script that wrote it. It is a text dump because .gitignore excludes *.db. Migrated, the store keeps its records, failures, ledger and export stamp, and exports the version-1 build's bytes.

Worth a look in review: readers still never migrate. PLAN's M16c rule is kept: codegraph insights and serve refuse a store that is not at this build's version. So after upgrading, insights refuses every existing store, and the serve panel shows no explanations, until any codegraph explain runs on it. The message says so, and that --dry-run, which calls no model, migrates it; a CLI test checks that it does. The alternative is to let a reader read a version-1 store, which would answer the same (no prompt-1 record has words), but that changes a documented rule.

Docs

  • docs/insights.md gains a "Version 2" paragraph, and its readers paragraph gives the remedy for an older store; docs/cli.md likewise.
  • The --insights help and the serve reference say an unmigrated store shows no explanations.
  • PLAN §20.3 gets an "as built" paragraph for the store; §20.4's store item is checked.

Verification

  • pnpm -r build and typecheck are green, eslint is clean on every changed file, pnpm run gen:schemas leaves schemas/ unchanged, and the Hugo site builds.
  • Tests:
    • insights 141 (5 new: a property that the table is every record's words in order, rebuilt by an import; a put replacing a record's words; the migration, the reader's refusal and a failed migration on the version-1 file);
    • cli 1,005 (2 new: insights refuses the version-1 file and leaves its bytes; explain --dry-run migrates it without a call);
    • vocabulary 332, core 544, viz 143, navigator-ui 105, city 94, extractor-typescript 66, scm 56, navigator 40, llm 34;
    • analyzer passes 613 of 614: the Node 26 node:sqlite warning test noted on feat(core): language lexicons on profiles (M19a) #26.
  • Deliberate mutations. I broke the store 8 times, one change at a time; the tests caught all 8 (words not deleted, not written, written in reverse, kept across an import, not filled by the migration, the hint dropped from the message, meaning kept by the migration, and by a put).
  • The single executable. ./build.sh --ts --sea passes its smoke checks.
  • Scale, on a synthetic side-car with Broadleaf's counts (28,206 records, 131,190 words): import 1.5 s; export byte-identical; insight_word 22.1 MB of a 90.9 MB store (indexes included); WHERE word = 'order' 0.5 ms; migrating a version-1 store that size 0.6 s.

Still owed for M19c:

  • PLAN's check on the real Broadleaf side-car (import, counts, identical export): there is none on this machine yet.
  • A live run on the Java fixture, and the per-word cost measured from it.
  • The readers (PR 11).

Part of #6.

🤖 Generated with Claude Code

Jérémie Grodziski and others added 4 commits October 27, 2026 18:15
Migration 1, the first rung of the ladder to run on a real file,
creates insight_word: one row per entry of a block's `words`, the
entry as JSON text less its `meaning`, with word, concern, sense and
confidence generated over it, indexed on word and id. A record's rows
are written with it in the unit's transaction (the replaced record's
deleted first), an import rebuilds the table, and nothing is exported
from it. The migration projects the words a version-1 block already
holds: a build asking prompt 2 on a version-1 store wrote some.

A reader still refuses an older store; its message now says any
`explain` on it migrates it, --dry-run included, which calls no model.

Tests: a property that the table is every record's words in block
order for any side-car, rebuilt by an import; a put replacing words,
and a refused unit writing none; and, from fixtures/store-v1.sql (the
.dump of a store the version-1 build wrote, with its export), the
migration keeping records, failures, ledger and stamp, exporting the
same bytes, the reader's refusal, and a failed migration left at 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`codegraph insights` on a store the version-1 build wrote exits 2,
names the migration that calls no model, and leaves the file's bytes
alone; `explain --dry-run` on it migrates it to version 2, with its
six records, asks for no client and writes no side-car.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/insights.md describes insight_word and the migration, and that
a reader refuses an older store with the way to migrate it; docs/
cli.md says so for `codegraph insights`. The `--insights` help (and
the website's serve page, which restates it) and the route's comment
say the panel shows nothing for a store no explain has migrated yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
§20.3 records the store as built: JSON entries with generated
columns, no `meaning`, the backfill §20.3 said was not needed, the
reader rule kept, the real-file fixture, and the scale measured on a
synthetic side-car at Broadleaf's counts. §20.4 checks the item, the
real Broadleaf side-car owed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant