refactor(effort-graph): per-mutate snapshot + Decision lifecycle module#210
Merged
Conversation
Every EffortGraphIndex method re-scanned and re-parsed every .md file under all six collection dirs, so planning an AcceptDecision cost ≥2 full scans and a Write* with N edges cost 1 + N — with two scans mid-plan able to observe different disk states (consistency window). Decision lifecycle rules were inlined in planMutation's 290-line switch and testable only through the full writer+journal path. - New snapshot.ts: buildEffortGraphSnapshot scans once into frozen records with byId/byEffort/byKind lookups; getRawBytes supplies journal before-images; createEffortGraphSnapshot is an in-memory adapter so planner/lifecycle tests need no filesystem. EffortGraphWriterOptions.index is retyped to the injectable EffortGraphSnapshotSource. - writer.ts builds the snapshot exactly once per mutate — under the lock, after journal recovery, before planMutation — so a plan sees one consistent disk state; planMutation is now synchronous over the snapshot and never touches disk. - New decision-lifecycle.ts owns the semantic rules: accept validates proposed state, rejects proposed same-Effort siblings with rejected_by; supersede flips a Decision target to state: superseded. Planner keeps generic expansion to PlannedWrites. - index-store.ts keeps EffortGraphIndex for external readers, reimplemented over one snapshot build per lookup. Journal protocol, lock, and frontmatter byte-handling are untouched — journal.ts, lock.ts, frontmatter.ts and their crash-safety suites have zero diff. Test plan: new suites for snapshot (immutability, one-scan consistency), planner-against-snapshot (planned writes incl. beforeBytes asserted against snapshot raw bytes), and direct decision-lifecycle units (sibling rejection, supersede, no-siblings / already-rejected / non-proposed edges); writer.test.ts extended append-only with one-buildSnapshot-per-mutate proof. Full suite green: pnpm verify (243 AVA incl. all 61 effort-graph tests, 52 vitest). Co-authored-by: Cursor <[email protected]> Change-Id: I1b760eb2c8bd404c59b087c28973a2f95decd9db
Collaborator
Author
|
This pull request is part of a Mergify stack:
|
This was referenced Jul 17, 2026
Merged
Merged
Collaborator
Author
|
@Mergifyio queue |
Contributor
Merge Queue Status
This pull request spent 24 minutes 48 seconds in the queue, including 4 minutes 17 seconds running CI. Required conditions to merge
|
Base automatically changed from
stack/tonyketcham/feat/unified-watch-effort-graph/single-record-prod-module-owns-file-record-path--f11d80de
to
main
July 18, 2026 08:55
…graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2
tonyketcham
deleted the
stack/tonyketcham/feat/unified-watch-effort-graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2
branch
July 18, 2026 09:00
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every EffortGraphIndex method re-scanned and re-parsed every .md file
under all six collection dirs, so planning an AcceptDecision cost ≥2
full scans and a Write* with N edges cost 1 + N — with two scans
mid-plan able to observe different disk states (consistency window).
Decision lifecycle rules were inlined in planMutation's 290-line switch
and testable only through the full writer+journal path.
records with byId/byEffort/byKind lookups; getRawBytes supplies
journal before-images; createEffortGraphSnapshot is an in-memory
adapter so planner/lifecycle tests need no filesystem.
EffortGraphWriterOptions.index is retyped to the injectable
EffortGraphSnapshotSource.
lock, after journal recovery, before planMutation — so a plan sees
one consistent disk state; planMutation is now synchronous over the
snapshot and never touches disk.
proposed state, rejects proposed same-Effort siblings with
rejected_by; supersede flips a Decision target to state: superseded.
Planner keeps generic expansion to PlannedWrites.
reimplemented over one snapshot build per lookup.
Journal protocol, lock, and frontmatter byte-handling are untouched —
journal.ts, lock.ts, frontmatter.ts and their crash-safety suites have
zero diff.
Test plan: new suites for snapshot (immutability, one-scan
consistency), planner-against-snapshot (planned writes incl.
beforeBytes asserted against snapshot raw bytes), and direct
decision-lifecycle units (sibling rejection, supersede, no-siblings /
already-rejected / non-proposed edges); writer.test.ts extended
append-only with one-buildSnapshot-per-mutate proof. Full suite green:
pnpm verify (243 AVA incl. all 61 effort-graph tests, 52 vitest).
Co-authored-by: Cursor [email protected]
Depends-On: #209