Skip to content

Fix document-level graph semantics, watcher debounce reliability, and test isolation - #15

Merged
drewburchfield merged 2 commits into
masterfrom
fix/graph-quality-backlog
Jul 13, 2026
Merged

Fix document-level graph semantics, watcher debounce reliability, and test isolation#15
drewburchfield merged 2 commits into
masterfrom
fix/graph-quality-backlog

Conversation

@drewburchfield

@drewburchfield drewburchfield commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-on to #14: fixes the backlog surfaced by the v1.1.0 quality gate, clawpatch, and live dogfooding against a real vault.

Fixes

Chunked notes distorted every graph tool (live-reproduced: an 87-chunk transcript occupied all 5 hub slots with identical inflated counts)

  • search_notes / get_similar_notes: one result per note (best-matching chunk via DISTINCT ON), deterministic ordering
  • get_hub_notes / get_orphaned_notes: one row per document
  • Connection-count refresh: counts DISTINCT connected documents, paginates over documents, and no longer overwrites last_indexed_at (which could mask stale files from the startup scan)
  • Algorithm version persisted in index_metadata → upgraded deployments auto-refresh their materialized counts once

get_similar_notes rewritten as a single SQL query: any-chunk-to-any-chunk semantics for multi-chunk sources, self-exclusion in SQL, no nested pool acquisition (previously deadlocked with max_connections=1)

File watcher silently dropped change events (live-reproduced: a created note stayed unindexed until its next edit)

  • Debounce compared wall-clock timestamps against a monotonic sleep; sub-ms clock jitter hit the "newer change pending" branch with nothing rescheduled
  • Now: monotonic timestamps, early wakes re-sleep the remainder, and the per-file lock is held across the reindex (no concurrent re-index of the same file)

Robustness / correctness

  • initialize() closes the pool on post-creation failure instead of leaking it
  • NaN thresholds rejected by validation (previously reached the database)

Test isolation

  • Integration fixtures delete their rows in teardown (previously permanently wrote ml/machine-learning.md etc. into whatever DB was configured)
  • test_e2e_docker.py requires explicit RUN_E2E_TESTS=1 instead of running whenever credentials exist

Testing

  • 142 unit tests pass (new: algo-version migration test); ruff, black, mypy clean
  • Live verification on a real 234-document vault: standalone E2E suite extended with uniqueness regression checks, 39/39 pass; hub list now shows 5 distinct documents topped by the vault's MOC navigation notes (semantically correct hubs)

Open in Devin Review

…tion

Backlog fixes from the v1.1.0 quality gate and live dogfooding:

- Collapse chunk rows to one result per note in search_notes and
  get_similar_notes (best-matching chunk wins); dedupe hub/orphan
  results by document; count DISTINCT connected documents in the
  connection-count refresh. A 87-chunk transcript previously filled the
  entire hub list with chunk-inflated counts (live-reproduced).
  Algorithm version is persisted in index_metadata so upgraded
  deployments refresh their materialized counts once automatically.
- Rewrite get_similar_notes as a single any-chunk-to-any-chunk SQL
  query: deterministic for multi-chunk sources, self excluded in SQL,
  and no nested pool acquisition (deadlocked with max_connections=1).
- Fix silent debounce drops in the file watcher: wall-clock timestamps
  compared against a monotonic sleep could abandon a pending change on
  sub-millisecond clock jitter (live-reproduced: a created note stayed
  unindexed until its next edit). Use monotonic time and re-sleep the
  remainder on early wakes; hold the per-file lock across the reindex.
- Stop the connection-count refresh from overwriting last_indexed_at,
  which could mask genuinely stale files from the startup scan.
- Close the pool when initialize() fails after pool creation.
- Reject NaN thresholds in validate_float_range (NaN compares False
  against both bounds and reached the database).
- Test isolation: integration fixtures delete their rows in teardown,
  and test_e2e_docker.py requires an explicit RUN_E2E_TESTS opt-in
  instead of running whenever credentials exist.
- run_e2e_tests.py: add uniqueness regression checks for search,
  similar, hub, and orphan results (39 checks, all passing live).

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

- Failed connection-count refresh is no longer recorded as a completed
  algo migration: _do_refresh re-raises, set_metadata only runs after
  success, and the freshness check logs an actionable error naming the
  consequence (Critical/High from review)
- Delete stale chunk rows when a note shrinks on re-index (watcher,
  indexer); batch upserts update total_chunks on conflict
- get_similar_notes requires a non-NULL source embedding so a
  failed-embedding note errors instead of returning empty success
- _cleanup_lock no longer pops a per-file lock that is held or has
  waiters, closing the residual concurrent-reindex window
- Tests: NaN/inf validation, pool cleanup on failed initialize,
  debounce re-sleep regression (fails on the old early-return code),
  failed-migration honesty, staleness test exercises its named branch
- test_tools fixture closes the pool even if cleanup delete fails
@drewburchfield

Copy link
Copy Markdown
Owner Author

Quality gate review (T3, 5 lenses) + live dogfooding

Findings remediated in 3ec0467:

  • Critical (silent-failures): a failed connection-count refresh was permanently stamped as a completed algorithm migration (_do_refresh swallowed exceptions; set_metadata ran unconditionally), locking in wrong counts forever. Fixed: refresh re-raises, version recorded only on success, freshness-check failures log an actionable error and retry on the next call. Regression test added.
  • High: blanket exception downgrade in _ensure_fresh_counts escalated from warning to error with stated consequence; NaN validation now unit-tested (nan/inf/string forms).
  • Mediums: stale chunk rows on note shrink are now deleted on re-index (watcher + indexer; batch upsert updates total_chunks on conflict) — this closes the pre-existing data bug two reviewers flagged as interacting with the new DISTINCT ON queries; get_similar_notes errors on a never-embedded source instead of returning empty success; _cleanup_lock no longer pops a held/waited per-file lock (residual concurrent-reindex window); pool-cleanup and debounce re-sleep paths got direct regression tests (the debounce test fails on the pre-fix code).

Live verification on a real 234-document vault (rebuilt container):

  • E2E suite 39/39, uniqueness checks green
  • Full MCP battery over stdio: all 5 tools, zero duplicate results; the 87-chunk transcript appears exactly once in search; hubs are 5 distinct documents topped by MOC navigation notes
  • Indexer exits 1 with an honest error on the one unreadable (iCloud-stuck) file and self-heals when it syncs

150 unit tests pass; lint, mypy, black clean; CI green.

@drewburchfield
drewburchfield merged commit 3e2f1ee into master Jul 13, 2026
4 checks passed
@drewburchfield
drewburchfield deleted the fix/graph-quality-backlog branch July 13, 2026 13:51
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