Commit 0069c97
committed
feat: node-level text/summary versioning with deferred regeneration
Decouples "this node changed" from "this node's summary is current", so a
small edit no longer prices a summary regeneration immediately.
Each MD node carries two counters:
text_version bumps on every detected content change (cheap, sha256)
summary_version the text_version the stored summary was generated from
A node is stale iff the two differ.
update() now does NO LLM work at all. It diffs section hashes, bumps
text_version on changed/added nodes, and leaves summary_version behind.
Regeneration is deferred to the next read (get_document_structure), where
every stale node is regenerated in one batch and summary_version catches
up. N edits between two reads therefore cost one regeneration, not N.
This also removes the previous ancestor-expansion pass, which was a no-op:
a parent's `text` excludes its children, so re-summarizing an ancestor fed
the model byte-identical input and produced the same summary at full cost.
Parent summaries are still generated from each node's own text -- there is
no child-to-parent roll-up, by design.
Versions are monotonic across re-index. index() reuses the doc_id for a
known path, so versions are carried forward and only bumped where the
section hash actually moved; a reader holding version N never sees it drop.
Also fixes a bug this exposed: _reconcile_summaries called _save_doc, which
evicts `structure` from memory for lazy reload, so get_document_structure
then served an empty tree. A retrieval against a doc with any stale node
got `[]` and answered from hallucinated line numbers instead of erroring.
Reconcile now reloads after saving.
Scope: MD only. update() already rejects PDFs, and PDF nodes carry no
section hashes, so version fields are simply absent there and the staleness
check tolerates that.
Deliberately not included: a semantic-change gate (embedding or LLM) to
suppress regeneration for immaterial edits. Dropping propagation and
deferring to read already removed both cost drivers, so the remaining
saving is marginal and a predicate that can under-fire on a negation or a
changed number risks the silent staleness this design exists to prevent.
Tests: 8 new cases, summarizer stubbed so they run offline with no API key.
15 passing.1 parent d9a954b commit 0069c97
4 files changed
Lines changed: 331 additions & 43 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
| 135 | + | |
135 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
136 | 159 | | |
137 | 160 | | |
138 | 161 | | |
| |||
142 | 165 | | |
143 | 166 | | |
144 | 167 | | |
145 | | - | |
| 168 | + | |
146 | 169 | | |
147 | 170 | | |
148 | 171 | | |
| |||
286 | 309 | | |
287 | 310 | | |
288 | 311 | | |
289 | | - | |
290 | 312 | | |
291 | | - | |
292 | | - | |
293 | | - | |
294 | | - | |
295 | | - | |
296 | | - | |
297 | | - | |
298 | | - | |
299 | | - | |
300 | | - | |
301 | | - | |
302 | | - | |
303 | | - | |
304 | | - | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | | - | |
309 | | - | |
310 | | - | |
311 | | - | |
312 | | - | |
313 | | - | |
314 | | - | |
315 | | - | |
316 | | - | |
317 | | - | |
318 | 313 | | |
319 | | - | |
320 | | - | |
321 | | - | |
322 | | - | |
323 | | - | |
324 | | - | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
325 | 320 | | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
326 | 325 | | |
327 | | - | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
328 | 337 | | |
329 | 338 | | |
330 | 339 | | |
331 | 340 | | |
332 | 341 | | |
333 | 342 | | |
334 | 343 | | |
335 | | - | |
| 344 | + | |
| 345 | + | |
336 | 346 | | |
337 | 347 | | |
338 | 348 | | |
| |||
360 | 370 | | |
361 | 371 | | |
362 | 372 | | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
363 | 419 | | |
364 | | - | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
365 | 425 | | |
366 | 426 | | |
| 427 | + | |
367 | 428 | | |
368 | 429 | | |
369 | 430 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
230 | 230 | | |
231 | 231 | | |
232 | 232 | | |
233 | | - | |
234 | | - | |
235 | | - | |
236 | | - | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
237 | 238 | | |
238 | 239 | | |
239 | 240 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
723 | 723 | | |
724 | 724 | | |
725 | 725 | | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
| 733 | + | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
726 | 739 | | |
727 | 740 | | |
728 | 741 | | |
| |||
0 commit comments