Skip to content

fix: roll back proposals when embedding generation fails - #141

Open
GautamSharma99 wants to merge 1 commit into
Autoloops:mainfrom
GautamSharma99:fix/proposal-apply-atomicity
Open

fix: roll back proposals when embedding generation fails#141
GautamSharma99 wants to merge 1 commit into
Autoloops:mainfrom
GautamSharma99:fix/proposal-apply-atomicity

Conversation

@GautamSharma99

Copy link
Copy Markdown
Contributor

Summary

This PR fixes proposal-apply atomicity when graph-object embedding generation fails.

Previously, greplica proposal apply persisted the memory commit and proposal records before generating embeddings. If embedding
generation then failed—for example because the local model could not load or an external embedding request failed—the command
reported an error even though the proposal had already been written to SQLite.

The failed proposal could not be safely retried because its graph-object IDs already existed.

Problem

The proposal-apply path performed these operations in order:

  1. Validate and normalize the proposal.
  2. Create the memory commit.
  3. Persist components, flows, claims, sources, edges, and memberships.
  4. Generate and store graph-object embeddings.
  5. Return a successful result.

Step 4 can fail independently of SQLite persistence. Because steps 2 and 3 had already committed, an embedding failure left partial
proposal state behind while reporting the overall operation as failed.

This could leave:

  • A memory commit from an operation reported as unsuccessful.
  • Components, flows, claims, sources, and edges from the failed apply.
  • Graph memberships associated with the failed commit.
  • Partially generated graph-object embeddings.
  • A proposal that could not be retried because validation detected duplicate IDs.

Root cause

KnowledgeGraphService.applyProposal() called createMemoryCommit() and createProposalRecords() separately before awaiting
GraphContextBuilder.ensureForGraph().

There was no rollback path for proposal data if embedding generation rejected.

Additionally, memory-commit creation and proposal-record creation were separate persistence operations, so a record-write failure
could leave an orphan memory commit.

Changes

Make commit and proposal-record creation atomic

Added SqliteRepository.createMemoryCommitWithProposal().

This method creates the memory commit and its proposal records within one SQLite transaction. If writing any component, flow, claim,
source, edge, or membership fails, the entire transaction is rolled back, including the memory commit.

Roll back proposal data after embedding failures

Added SqliteRepository.rollbackProposalRecords().

If embedding generation fails after the proposal has been persisted, the service now removes the data created by that apply attempt
in a compensating SQLite transaction:

  • Graph-object embeddings for the proposal’s components, flows, and claims.
  • Edges.
  • Components.
  • Flows.
  • Claims.
  • Sources.
  • The memory commit and its cascading graph memberships.

The original embedding error is then rethrown so callers still receive the actual failure.

Avoid orphan commits during anchor fingerprinting

Code-anchor fingerprints are now computed before creating the memory commit. A fingerprinting failure therefore cannot leave an
empty or orphaned commit behind.

Add deterministic regression coverage

Added scripts/check-proposal-apply-atomicity.js and included it in the default npm test chain.

The regression test injects a context builder that:

  1. Stores an embedding for a newly created graph object.
  2. Throws a synthetic embedding failure.

It then verifies that:

  • applyProposal() rejects with the embedding error.
  • The visible graph remains empty.
  • No components, flows, claims, sources, or edges remain.
  • No graph memberships or memory commits remain.
  • No graph-object embeddings remain.
  • The exact same proposal can subsequently be applied successfully.

Behavior before this PR

proposal records persisted

embedding generation fails

command reports failure

proposal data remains in SQLite

retry fails because IDs already exist

Behavior after this PR

proposal records persisted atomically

embedding generation fails

proposal data and generated embeddings are rolled back

original embedding error is reported

same proposal can be retried safely

Testing

The following checks pass:

npm test
npm run typecheck

The full test suite includes:

  • Transcript bundle checks.
  • Repository-context checks.
  • Installation-option checks.
  • Graph-view checks.
  • Offline browser graph-view rendering.
  • Proposal validation.
  • Proposal-apply atomicity and retry behavior.
  • BM25 tokenization.
  • Code-anchor drift detection.

Scope

This PR is limited to proposal-apply failure handling and its regression coverage.

It does not change:

  • Proposal normalization or validation rules.
  • Successful proposal-apply output.
  • Embedding provider configuration.
  • Retrieval or ranking behavior.
  • The graph schema.
  • Existing proposal formats.

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