Skip to content

fix: prune stale watcher offset entries - #597

Merged
EtanHey merged 21 commits into
mainfrom
fix/prune-stale-offsets
Jul 17, 2026
Merged

fix: prune stale watcher offset entries#597
EtanHey merged 21 commits into
mainfrom
fix/prune-stale-offsets

Conversation

@EtanHey

@EtanHey EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • prune offset-registry entries whose transcript files no longer exist
  • persist the pruned registry on the watcher first poll
  • retry pruning persistence after a failed flush

Verification

  • changed-only pre-push gate: 72 focused pytest + 3 MCP registration + 40 eval/routing + 1 Bun + regression shell, all passing
  • focused watcher/run-test suite: 72 passed
  • live read-only receipt before implementation: 13,684 offset entries, 7,618 missing files, 6,066 existing files
  • full-suite baseline on origin/main and this branch has the same 3 date/production-DB failures; no branch regression

Risk

This changes registry maintenance only. It never deletes transcript JSONLs or indexed chunks.

No merge requested; ready for owner review.


Note

Medium Risk
Changes durable offset persistence, concurrent merge rules, and fail-closed prune logic; bugs could retain stale entries or incorrectly drop offsets and restart tailing from byte zero, but scope stays limited to the offset registry.

Overview
Hardens #597 offset pruning so stale transcript offsets are dropped only when the watcher has positive evidence the containing subtree is available—not merely because a sibling file exists elsewhere in the watch root.

Prune safety: prune_missing_files now requires the most-specific watch root to look mounted, no broken/unavailable symlinks on the path (including when the tracked path is a symlink), a live JSONL in the candidate’s parent directory from the current poll, and a final missing-file check without transient OSError. Uncertain cases keep the offset and set last_prune_complete false so JSONLWatcher retries pruning across polls.

Registry integrity: Deletes record generation-aware tombstones so stale in-memory writers cannot resurrect pruned paths; tombstones are sanitized on load/flush and compacted after retention. flush() merges only dirty paths under an exclusive file lock, refuses to overwrite a corrupt on-disk registry, and persists via temp file + fsync (file and parent dir) + os.replace. mark_rewind bumps generation so confirmed checkpoint rewinds can persist lower offsets without losing to stale flushes.

Tests & CI: Large expansion of test_jsonl_watcher.py (mount/symlink/stat-error cases, concurrency, tombstones, fsync, first-poll prune). run_tests.sh maps watcher.py to targeted watcher tests in changed-only pre-push. Design/plan docs added under docs/plans/.

Reviewed by Cursor Bugbot for commit a8d2bcd. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Prune stale offset entries from OffsetRegistry with fail-closed safety checks

  • OffsetRegistry gains crash-durable writes (fsync + atomic os.replace), cross-platform file locking, and tombstone tracking to prevent stale writers from resurrecting deleted paths.
  • prune_missing_files removes only entries confirmed missing via live-evidence checks on watch roots and parent directories; sets last_prune_complete=False when evidence is insufficient or errors occur.
  • JSONLWatcher.poll_once runs pruning on first poll, retries until prune_missing_files completes safely, and calls registry.mark_rewind on checkpoint restores to allow lower confirmed offsets.
  • Concurrent registry merges use a generation-aware policy to block stale updates and prevent offset regression for the same inode.
  • Risk: flush now acquires an exclusive advisory lock on a .lock file on every write; on Windows this uses a retry loop via msvcrt.locking.

Macroscope summarized a8d2bcd.

Summary by CodeRabbit

  • Bug Fixes

    • Improved watcher reliability when files are deleted, recreated, moved, or temporarily inaccessible.
    • Prevented stale file offsets from reappearing or overwriting newer progress.
    • Added safer handling for unavailable roots, mounted paths, broken links, and permission errors.
    • Pruned obsolete offset entries during polling, with retries when startup paths are unavailable.
  • Tests

    • Expanded coverage for offset cleanup, persistence, concurrency, and startup pruning.
    • Updated changed-file test selection to run the relevant watcher tests automatically.

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_53328ed5-7161-4288-ae24-6b57a35729a5)

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The offset registry now supports tombstones, locked merge-based persistence, atomic writes, and safe pruning of missing files. JSONLWatcher performs pruning during polling, and changed-only test selection maps watcher changes to the watcher test module.

Changes

Offset Registry and Watcher Pruning

Layer / File(s) Summary
Registry durability and tombstones
src/brainlayer/watcher.py
OffsetRegistry tracks dirty and removed paths, persists tombstones, merges concurrent updates under an inter-process lock, prunes expired tombstones, and atomically writes registry state.
Safe watcher pruning
src/brainlayer/watcher.py, tests/test_jsonl_watcher.py
Missing-file pruning checks root and symlink availability, retries incomplete startup pruning, and tests persistence, concurrency, tombstones, filesystem edge cases, and polling integration.
Watcher test selection mapping
scripts/run_tests.sh, tests/test_run_tests_script.py
Changed-only execution maps watcher.py changes to test_jsonl_watcher.py without invoking the full suite.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant JSONLWatcher
  participant OffsetRegistry
  participant Filesystem
  JSONLWatcher->>OffsetRegistry: prune_missing_files(active_roots, active_files)
  OffsetRegistry->>Filesystem: inspect tracked paths and root targets
  Filesystem-->>OffsetRegistry: availability and stat results
  OffsetRegistry-->>JSONLWatcher: prune count and completion status
  JSONLWatcher->>OffsetRegistry: flush registry changes
Loading

Possibly related PRs

  • EtanHey/brainlayer#104: Introduced the watcher and OffsetRegistry implementation extended by these persistence and pruning changes.

Poem

I’m a bunny guarding offsets tight,
Tombstones keep stale paths out of sight.
Roots wake up, then pruning runs,
Locked writes hop safely, one by one.
Tests map the trail with carrots bright!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: pruning stale watcher offset entries.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/prune-stale-offsets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@BugBot review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_5d208530-2a15-477b-aa07-b37fae8b0cc8)

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_dd343b91-8061-427e-80f3-c7c02c2f905a)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment thread src/brainlayer/watcher.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c78dbd458

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py Outdated
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_f2a89304-ddf6-4942-8102-1e476e68525a)

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

Comment thread src/brainlayer/watcher.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a7c629dbe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py Outdated
@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@BugBot review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_bca2205d-4d2f-4326-a279-0b50f4dc4baf)

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_9a341851-d6ec-4f01-8887-caa12af905ce)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a7c629dbe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py Outdated
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_1a5fb71d-85bf-4c49-aa5b-8b2172a3545a)

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4e7a4d7e1c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py Outdated
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_59249ec5-2f74-4fc1-908a-3a69cf206edc)

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

Final identity hardening at exact head 931ea24: after a process loads a tombstone, set() now requires a positive live inode before advancing its generation or clearing the tombstone. This prevents a second callback with inode 0 from resurrecting an unavailable file while preserving the same-inode and new-inode recreation paths. Added RED-to-GREEN regression; focused watcher and bridge suite 119/119; a fresh full committed-tree gate is green.

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@BugBot review

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_413bc189-9520-4ed8-a430-deffb82df0cf)

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_2940cd73-c694-4ce5-abd3-b0c077055954)

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 931ea2488e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_594f95ff-4ad1-4600-bfa5-858d9d8475e9)

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

Exact head a8d2bcd closes the final two review findings: tombstones are generation-authoritative across inode history, and OffsetRegistry uses real POSIX or Windows advisory locking. Fresh committed-tree verification: 3,566 passed, 9 skipped, 61 deselected, 1 expected xfail; MCP 3/3; isolated eval and hooks 40/40; Bun 1/1; FTS shell pass; focused watcher and bridge 121/121; Ruff, format, and diff clean.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@cursor review

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@BugBot review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_edba4aa7-586a-4492-9f59-352d4b756d4d)

@cursor

cursor Bot commented Jul 17, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_87ea57b8-7922-481b-abe5-f46c2e6efa81)

Comment thread src/brainlayer/watcher.py

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8d2bcd381

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/brainlayer/watcher.py
@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

MERGE-READY on frozen exact head a8d2bcd3813bc25d672e6410297c0d4085e390f6. Remote CI run 29616956656 is green across Python 3.11/3.12/3.13, lint, changes, and Swift; CodeRabbit passed; exact-head Codex and Macroscope reviews completed; unresolved review threads: 0; worktree matches origin. Cursor Bugbot remains the disclosed neutral quota skip. Late non-data-destroying findings were dispositioned without repushing: Windows portability is NOT_APPLICABLE for the Mac+Linux fleet, and oversized tombstone sanitization is deferred to the #596/#598 follow-up. No merge or restart performed; @layerSpec retains the merge gate.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

layerSpec merge-gate v3: PASS — merging a8d2bcd. Verified independently (not relayed): all CI green (3.11/3.12/3.13 + lint/changes/swift + CodeRabbit PASS; Bugbot quota-disclosed; Macroscope neutral); 24 review threads, 0 unresolved (GraphQL-confirmed); every reviewer accounted for on this exact head; head frozen (my convergence guidance stopped the 6-round churn so CI could complete). The two late findings correctly dispositioned WITHOUT re-push: Windows dir-fsync portability = NOT_APPLICABLE (Mac+Linux fleet, no Windows target — verdict-class correct); oversized-tombstone-integer = deferred to the #596/#598 follow-up with malformed-entry sanitization. 6 adversarial rounds closed real data-safety holes on the prune path (unmount-prune of live sessions, tombstone-resurrection, inode-history, delayed-set, inode-0). BLOCKED = branch-protection-needs-authority-approval; merge authority is mine. Data-safe.

🤖 Generated with Claude Code

@EtanHey
EtanHey merged commit b2b7924 into main Jul 17, 2026
9 checks passed
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