Memoize the reload parse and short-circuit value-equal snapshots
The loader gains a ParseMemo — the previous walk's parsed documents keyed by root-relative path, trusted on the git-index heuristic (mtime + size, no hashing) and passed as an input so the loader stays stateless. A hit skips exactly one file read; schema, order, coercions, dedupe, and every directory listing run fresh, so memoized and cold walks are output- identical (golden-corpus equivalence suite). Entries record only past the schema gate, so a defect can never be answered from the memo. The store skips the snapshot assignment wholesale when the fresh model is value-equal — no @Observable churn, no render pass, no snapshotGeneration bump — and a new landedReloads counter carries walk-completion for the three consumers whose subject is the walk, not the snapshot: the card window's comment thread, the comment search index, and the auto-committer's covering gate (which now counts a completed walk as covering even when nothing changed). Warnings and defects move on their own equality; failed reloads bump neither counter. An injectable ParseCounter makes the single-file-echo claim a test. Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
@@ -484,13 +484,19 @@ struct CardWindowHost: View {
|
||||
attachments.isEditable = !locked
|
||||
session.comments.isEditable = !locked
|
||||
}
|
||||
// **The thread re-reads on every applied snapshot** (05 ▸ The comments column: "the pane
|
||||
// **The thread re-reads on every landed reload** (05 ▸ The comments column: "the pane
|
||||
// reloads its thread from the same FSEvents stream").
|
||||
//
|
||||
// *Any* reload, not a filtered one, and that is a deliberate choice worth stating: the
|
||||
// store's observable surface publishes `snapshotGeneration` and a `BoardModel` — it does
|
||||
// not vend the changed paths, and comments are outside the snapshot entirely
|
||||
// (01-storage-format.md § Enhanced schema), so there is nothing to filter *on* here.
|
||||
// store's observable surface publishes counters and a `BoardModel` — it does not vend the
|
||||
// changed paths, and comments are outside the snapshot entirely (01-storage-format.md
|
||||
// § Enhanced schema), so there is nothing to filter *on* here.
|
||||
//
|
||||
// **`landedReloads`, not `snapshotGeneration`**, and for that same sentence's reason: a
|
||||
// comment arriving changes the tree and leaves the model value-equal, and a value-equal
|
||||
// landing skips the snapshot assignment (blessed 2026-07-31). Watching the applied counter
|
||||
// would mean the one kind of change this pane exists to notice is the one kind it would
|
||||
// sleep through.
|
||||
// Re-reading one card's thread is a handful of small files and happens only while a card
|
||||
// window is open; filtering would mean either widening the store's surface to carry paths,
|
||||
// or the pane keeping its own watcher — a second stream over the same tree, which the
|
||||
@@ -499,7 +505,7 @@ struct CardWindowHost: View {
|
||||
// ledger's comment receipts through `CommentPath.classify` to tell a foreign arrival from
|
||||
// its own echo (`CardComments.reload`). `initial:` is deliberately absent: `start()`
|
||||
// already did the opening read, after the residue sweep that has to precede it.
|
||||
.onChange(of: store.snapshotGeneration) { _, _ in
|
||||
.onChange(of: store.landedReloads) { _, _ in
|
||||
session.comments.reload()
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user