Skip to content

fix(resolution): filter stale-target edges so watch sync survives FK violations (#455)#463

Merged
colbymchenry merged 1 commit into
mainfrom
fix/watch-sync-fk-regression-455
May 26, 2026
Merged

fix(resolution): filter stale-target edges so watch sync survives FK violations (#455)#463
colbymchenry merged 1 commit into
mainfrom
fix/watch-sync-fk-regression-455

Conversation

@colbymchenry
Copy link
Copy Markdown
Owner

Summary

Fixes #455FOREIGN KEY constraint failed during watch sync on v0.9.5.

PR #62 plugged this FK at the extraction-layer insertEdges site (empty-named nodes whose containment edges had no target). The same violation kept reappearing during the daemon's watch sync because the resolution-layer insertEdges (in resolveAndPersist / resolveAndPersistBatched / synthesizeCallbackEdges) wasn't guarded the same way.

Root cause. The resolution layer trusts resolver lookups to return live node IDs, but some framework resolvers (e.g. react-native.ts, swift-objc.ts, rust.ts) cache native Node refs in a WeakMap<ResolutionContext, …> that persists across watch syncs. A file rewrite between map build and resolution leaves dangling IDs in those caches — one bad edge fails FK, the transaction aborts, the daemon logs Watch sync failed.

Fix. Mirror PR #62's pattern at the resolution layer: a small filterEdgesByExistingNodes helper runs one cache-aware getNodesByIds per batch and drops edges whose source or target is no longer in the nodes table. Applied at all three resolution-layer insert sites.

Test plan

  • New __tests__/sync-fk-regression.test.ts with two cases:
  • Full suite: 984/984 pass.
  • npm run build clean.

🤖 Generated with Claude Code

…violations (#455)

PR #62 plugged this FK violation at the extraction-layer insertEdges site
(empty-named nodes whose containment edges had no target), but the same
violation kept reappearing on v0.9.5 during the daemon's *watch sync* once an
agent's daemon had been running long enough. The resolution-layer insertEdges
(and the callback-synthesizer pass) wasn't guarded the same way: a per-resolver
name cache or a framework resolver's WeakMap-keyed lookup map could hand back
a Node whose row had been removed by a recent file rewrite, and the FK check
then aborted the entire resolution batch, leaving the daemon log filling with
`Watch sync failed { error: 'FOREIGN KEY constraint failed' }`.

The resolution layer now mirrors the #62 defense — one cache-aware
getNodesByIds per pass drops any edge whose source or target is no longer in
the nodes table, so the rest of the resolved batch still lands.

Regression test seeds the resolver's nameCache with a stale Node and calls
resolveAndPersist directly; verified to throw FOREIGN KEY constraint failed
without the fix and pass with it. Full suite: 984/984 pass.

Closes #455.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@colbymchenry colbymchenry merged commit 1dfaf30 into main May 26, 2026
colbymchenry added a commit that referenced this pull request May 26, 2026
…ives FK violations (#455) (#463)"

This reverts commit 1dfaf30.

Switching to #462's approach — a single, lower-layer filter inside
QueryBuilder.insertEdges itself instead of three filters spread across
the resolution layer. The DB-layer filter protects every caller (current
and future) automatically and doesn't depend on the queries-layer
nodeCache invalidation staying perfect. See #455 for the bug.

The CHANGELOG entry for the user-facing fix is re-added on top of #462.
@colbymchenry
Copy link
Copy Markdown
Owner Author

Reverted on main (commit 028d25f) in favor of #462's cleaner approach — a single filter at the DB layer (QueryBuilder.insertEdges) instead of three filters spread across the resolution layer. Same user-facing fix, cleaner internals.

jorgerobles pushed a commit to jorgerobles/codegraph that referenced this pull request Jun 1, 2026
…violations (colbymchenry#455) (colbymchenry#463)

PR colbymchenry#62 plugged this FK violation at the extraction-layer insertEdges site
(empty-named nodes whose containment edges had no target), but the same
violation kept reappearing on v0.9.5 during the daemon's *watch sync* once an
agent's daemon had been running long enough. The resolution-layer insertEdges
(and the callback-synthesizer pass) wasn't guarded the same way: a per-resolver
name cache or a framework resolver's WeakMap-keyed lookup map could hand back
a Node whose row had been removed by a recent file rewrite, and the FK check
then aborted the entire resolution batch, leaving the daemon log filling with
`Watch sync failed { error: 'FOREIGN KEY constraint failed' }`.

The resolution layer now mirrors the colbymchenry#62 defense — one cache-aware
getNodesByIds per pass drops any edge whose source or target is no longer in
the nodes table, so the rest of the resolved batch still lands.

Regression test seeds the resolver's nameCache with a stale Node and calls
resolveAndPersist directly; verified to throw FOREIGN KEY constraint failed
without the fix and pass with it. Full suite: 984/984 pass.

Closes colbymchenry#455.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jorgerobles pushed a commit to jorgerobles/codegraph that referenced this pull request Jun 1, 2026
…ives FK violations (colbymchenry#455) (colbymchenry#463)"

This reverts commit 62975e3.

Switching to colbymchenry#462's approach — a single, lower-layer filter inside
QueryBuilder.insertEdges itself instead of three filters spread across
the resolution layer. The DB-layer filter protects every caller (current
and future) automatically and doesn't depend on the queries-layer
nodeCache invalidation staying perfect. See colbymchenry#455 for the bug.

The CHANGELOG entry for the user-facing fix is re-added on top of colbymchenry#462.
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.

FOREIGN KEY constraint failed during watch sync (v0.9.5 — regression from #28/#42/#53)

1 participant