Skip to content

refactor(effort-graph): per-mutate snapshot + Decision lifecycle module#210

Merged
tonyketcham merged 2 commits into
mainfrom
stack/tonyketcham/feat/unified-watch-effort-graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2
Jul 18, 2026
Merged

refactor(effort-graph): per-mutate snapshot + Decision lifecycle module#210
tonyketcham merged 2 commits into
mainfrom
stack/tonyketcham/feat/unified-watch-effort-graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2

Conversation

@tonyketcham

Copy link
Copy Markdown
Collaborator

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]

Depends-On: #209

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
@tonyketcham

tonyketcham commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 docs(effort-graph): add Effort Graph glossary and ADRs 0001-0007 #205
2 feat(core,cli): unified watch with incremental reindex and live schema swap #206
3 feat(effort-graph): journaled semantic writer for the Effort Graph #207
4 refactor(config): loadConfig returns an initialized LoadedFlatbreadConfig #208
5 refactor(core): single Record-production module owns file→Record and path classification #209
6 refactor(effort-graph): per-mutate snapshot + Decision lifecycle module #210 👈
7 refactor(core): collection query executor owns filter/sort/paging with copy-on-output #211
8 refactor(core,cli): unified watch coordinator behind one seam, two adapters #212
9 refactor(core): per-build schema composer; delete the config-keyed schema cache #213
10 feat(effort-graph,flatbread): committed-generation bridge completes the ADR-0003/0004 contract #214
11 chore(core): drop unused graphql-compose-json and lru-cache dependencies #215
12 test: raise AVA concurrency now that the global-composer pin is obsolete #216
13 docs(agents): define orchestrator-executor operating model #217

@tonyketcham

Copy link
Copy Markdown
Collaborator Author

@Mergifyio queue

@mergify

mergify Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • Entered queue2026-07-18 08:36 UTC · Rule: owner-bypass · triggered by @tonyketcham with the @mergifyio queue command
  • Checks passed · in-place
  • Merged2026-07-18 09:00 UTC · at 526041e5d7c2d37fc27b60b42348216387a92fd2

This pull request spent 24 minutes 48 seconds in the queue, including 4 minutes 17 seconds running CI.

Required conditions to merge
  • author = tonyketcham
  • check-success = build (20.x, ubuntu-latest)
  • check-success = build (22.x, ubuntu-latest)
  • check-success = integration-nextjs (20.x, macos-latest)
  • check-success = integration-nextjs (20.x, ubuntu-latest)
  • check-success = integration-nextjs (20.x, windows-latest)
  • check-success = integration-nextjs (22.x, macos-latest)
  • check-success = integration-nextjs (22.x, ubuntu-latest)
  • check-success = integration-nextjs (22.x, windows-latest)
  • check-success = integration-sveltekit (20.x, macos-latest)
  • check-success = integration-sveltekit (20.x, ubuntu-latest)
  • check-success = integration-sveltekit (20.x, windows-latest)
  • check-success = integration-sveltekit (22.x, macos-latest)
  • check-success = integration-sveltekit (22.x, ubuntu-latest)
  • check-success = integration-sveltekit (22.x, windows-latest)
  • check-success = lint (20.x, ubuntu-latest)
  • check-success = lint (22.x, ubuntu-latest)
  • check-success = test (20.x, ubuntu-latest)
  • check-success = test (22.x, ubuntu-latest)
  • depends-on = FlatbreadLabs/flatbread#209 [⛓️ refactor(core): single Record-production module owns file→Record and path classification #209]

@mergify mergify Bot added the queued label Jul 18, 2026
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
tonyketcham merged commit baf75b5 into main Jul 18, 2026
19 checks passed
@tonyketcham
tonyketcham deleted the stack/tonyketcham/feat/unified-watch-effort-graph/per-mutate-snapshot-decision-lifecycle-module--1b760eb2 branch July 18, 2026 09:00
@mergify mergify Bot removed the queued label Jul 18, 2026
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