Skip to content

feat: add idempotent transcript window backfill - #599

Open
EtanHey wants to merge 21 commits into
mainfrom
feat/indexing-window-backfill
Open

feat: add idempotent transcript window backfill#599
EtanHey wants to merge 21 commits into
mainfrom
feat/indexing-window-backfill

Conversation

@EtanHey

@EtanHey EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a bounded half-open UTC window to watch-backfill
  • persist offsets in a stable window-specific registry for idempotent reruns
  • support a legacy-excluded-only mode that replays only roots blocked by the old ingest policy
  • expose processed/scanned/matched/queued counters and validate complete windows

Stack

Stacked on #596 because the production replay depends on its corrected reverse-ingest denylist. Review and merge #596 first; do not merge this PR independently.

Verification

  • changed-only pre-push gate: 169 focused pytest + 3 MCP registration + 40 eval/routing + 1 Bun + regression shell, all passing
  • focused backfill/CLI regression: 111 passed; validation-focused run: 9 passed
  • production pass 1 for 2026-07-10T00:00:00Z through 2026-07-16T00:00:00Z: processed 441,514, scanned 441,514, matched 19,297, queued 13,877, 212 cycles
  • identical pass 2: processed 3, scanned 3, matched 0, queued 0, 7 cycles, proving idempotence
  • full-suite baseline on origin/main and this stack has the same 3 date/production-DB failures; no branch regression

Operations

Production receipt and before/after chunk counts are tracked in the lane collaboration document. The raw transcript JSONLs are retained.

No merge requested; ready for owner review.


Note

Medium Risk
Touches ingest paths, offset persistence, and denylist semantics for production backfills; behavior is heavily tested but mis-filtered windows or watermark logic could skip or duplicate data.

Overview
Adds bounded, idempotent transcript replay to watch-backfill via --since / --until (half-open UTC), optional --legacy-excluded-only for roots formerly blocked by blanket ingest policy, and window-scoped offset registries plus a registry flock lock (concurrent runs exit 2).

New backfill.py supplies WindowedFlush (time-filter queued writes while still advancing offsets for scanned-but-skipped lines), parse_backfill_window, and legacy path detection. ingest_denylist gains is_legacy_backfill_denylisted so legacy replays drop retired blanket patterns but keep workflow/brain-worker safety rules.

JSONLWatcher / JSONLTailer pick up injectable denylist/discovery predicates, per-file read byte caps, watermark-only progress for bad lines, synthesized-timestamp marking in normalization, and has_pending_input so incomplete runs exit 1 with scanned/matched/queued stats. Pre-push changed-only mapping now runs all CLI tests when cli/__init__.py changes.

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

Note

Add idempotent time-windowed backfill to the watch-backfill CLI command

  • Adds --since, --until, and --legacy-excluded-only flags to watch-backfill; the window determines which transcript entries are flushed and scopes the registry file name via window_registry_suffix.
  • Introduces WindowedFlush in backfill.py to filter normalized watcher entries to the requested UTC half-open interval, advancing offsets for non-matching lines so re-runs skip already-processed content.
  • Adds backfill_run_lock (fcntl-based) to prevent concurrent backfill runs against the same registry; contention exits with code 2.
  • Adds is_legacy_backfill_denylisted in ingest_denylist.py to apply the current denylist minus retired blanket patterns, enabling backfill of previously blocked roots.
  • Extends JSONLTailer.read_new_lines with byte-capped reads and progress markers for blank/malformed lines, and adds has_pending_input to JSONLWatcher to drive the new incomplete-exit logic (exit code 1).

Macroscope summarized 90ac44b.

Summary by CodeRabbit

  • New Features

    • Added time-windowed watch-backfill replay (--since/--until) and legacy-only replay via --legacy-excluded-only.
    • --dry-run now reports more detailed, windowed/scoped candidate summaries.
    • Improved watcher tailing with max_read_bytes control and optional progress-marker watermark events.
  • Bug Fixes

    • Backfills stop when no forward progress occurs.
    • Timestamp parsing/normalization is more robust, and denylisting no longer disrupts confirmed registry offsets.
    • Added a registry lock to prevent concurrent backfill runs.
  • Tests

    • Expanded CLI, watcher, backfill, denylist, concurrency, and “changed-only” test-selection coverage.

@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_a28ac738-168e-4a0a-b290-ad041428e4d4)

@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

Changes

Transcript backfill

Layer / File(s) Summary
Window parsing and filtered flushing
src/brainlayer/backfill.py, tests/test_backfill.py
Adds UTC half-open window validation, filesystem-safe registry suffixes, legacy-root matching, locking, and WindowedFlush watermark/counter behavior with unit tests.
Legacy denylist and watcher filtering
src/brainlayer/ingest_denylist.py, src/brainlayer/watcher.py, tests/test_ingest_denylist.py, tests/test_jsonl_watcher.py
Separates retired blanket roots from active denylist matching and adds timestamp normalization, progress markers, configurable filtering, bounded reads, and registry-preserving polling.
Windowed watch-backfill execution
src/brainlayer/cli/__init__.py, tests/test_watch_backfill_cli.py
Adds window and legacy-only CLI modes, scoped registries, candidate reporting, filtered flushing, progress-based termination, locking, and integration tests.
Changed-only CLI test mapping
scripts/run_tests.sh, tests/test_run_tests_script.py
Maps CLI entrypoint changes to CLI tests without triggering the full pytest fallback.

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

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant WatchBackfillCLI
  participant JSONLWatcher
  participant WindowedFlush
  participant IndexAndRegistry
  WatchBackfillCLI->>JSONLWatcher: discover and poll scoped transcript files
  JSONLWatcher->>WindowedFlush: submit normalized watcher entries
  WindowedFlush->>IndexAndRegistry: forward entries within [since, until)
  IndexAndRegistry-->>WindowedFlush: return inserted offsets or suppression
  WindowedFlush-->>WatchBackfillCLI: return flush watermarks and counters
Loading

Poem

A bunny bounds through timestamps bright,
Filtering logs from left to right.
Old denied roots now hop in line,
While scoped registries keep time.
Tests bloom softly, green and neat—
The watcher’s replay is complete!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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 summarizes the main change: adding idempotent transcript window backfill.
✨ 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 feat/indexing-window-backfill

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_c71d115c-d021-4699-bde6-b6c06d7b1285)

@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_846e1b63-5155-4034-8285-38a5768b95a2)

@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: 09eb2ea05e

ℹ️ 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/backfill.py Outdated
Comment thread src/brainlayer/cli/__init__.py
Comment thread src/brainlayer/backfill.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_12ccdf6d-1923-4a47-bda3-eddcb40ef9af)

@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/backfill.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: bdc15e8f07

ℹ️ 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/cli/__init__.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_3758f2b0-6d93-47eb-b4d5-8e64de124ca6)

@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_34266de2-f60b-40b9-9335-5a4b24093acf)

@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: bdc15e8f07

ℹ️ 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/cli/__init__.py Outdated
Comment thread src/brainlayer/backfill.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: f459924a2d

ℹ️ 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
@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_47847407-0ab6-43ad-a28f-83ba19fc87bd)

@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\n@cursor review\n@bugbot review\n\nExact systemic CI-isolation head: . Please review this exact head.

@EtanHey

EtanHey commented Jul 17, 2026

Copy link
Copy Markdown
Owner Author

@codex review\n@cursor review\n@bugbot review\n\nCorrection: exact systemic CI-isolation head SHA is 5fe9e99. Please review this exact head.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 5fe9e9996d

ℹ️ 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".

@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_5ac86da7-aa9b-48e5-923b-0f04eeca60a5)

@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\n@cursor review\n@bugbot review\n\nFinal exact head after required-check repair: 39abf1e. Please review this exact head.

@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: 39abf1e550

ℹ️ 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/cli/__init__.py Outdated
EtanHey and others added 2 commits July 18, 2026 01:36
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	scripts/run_tests.sh
#	tests/test_run_tests_script.py
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@EtanHey
EtanHey changed the base branch from fix/reverse-ingest-denylist to main July 17, 2026 23:19
@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_a045e7d7-da5a-46da-8c12-44cce276ce02)

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

@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

Fix round pushed at exact head e43e1fb21c9ed8c72cc95d4bef95f57bbd144812; base retargeted to main.

Mandatory fixes: all CLI tests now force BRAINLAYER_DB to a per-test sandbox; legacy-only replay filters during discovery before tailer reads and preserves nonlegacy shared-registry offsets; backfill reads are capped at 1 MiB and drain buffered records before further reads; hitting --max-cycles with pending input exits nonzero. Also added registry-scoped whole-run serialization, structural retired-root matching, malformed timestamp fallback, unspoofable progress markers, and the routing-test exclusion assertion.

Verification: official gate green with 3,597 passed / 9 skipped / 61 deselected / 1 expected xfail; MCP 3/3; isolated eval/hooks 40/40; Bun 1/1; FTS shell PASS; Ruff/format/diff clean. The local macOS default ulimit -n 256 reproduced the already-queued EMFILE-watermark baseline failure; authoritative gate used ulimit -n 4096 without changing the tree.

@codex review
@coderabbitai review
@cursor review
@BugBot review

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='e43e1fb21c9ed8c72cc95d4bef95f57bbd144812'
printf 'local_head='; git rev-parse HEAD
printf 'remote_pr_head='; gh pr view 599 --repo EtanHey/brainlayer --json headRefOid --jq .headRefOid
printf 'pr_base='; gh pr view 599 --repo EtanHey/brainlayer --json baseRefName --jq .baseRefName
printf '\nexpected_head=%s\n' "$expected"

printf '\n== changed files against main ==\n'
git diff --name-status main...HEAD

printf '\n== outlines ==\n'
for f in \
  src/brainlayer/cli/__init__.py \
  src/brainlayer/backfill.py \
  src/brainlayer/watcher.py \
  src/brainlayer/ingest_denylist.py \
  tests/test_watch_backfill_cli.py \
  tests/test_jsonl_watcher.py \
  tests/test_backfill.py \
  tests/test_run_tests_script.py \
  scripts/run_tests.sh
do
  if [ -f "$f" ]; then
    printf '\n-- %s --\n' "$f"
    case "$f" in
      *.py) ast-grep outline "$f" --items all --view compact || true ;;
      *) sed -n '1,240p' "$f" ;;
    esac
  fi
done

printf '\n== focused definitions and call sites ==\n'
rg -n -C 5 --glob '*.py' \
  'def watch_backfill\b|class JSONLTailer\b|def read_new_lines\b|class JSONLWatcher\b|def poll_once\b|def _discover_jsonl_files\b|class WindowedFlush\b|def is_legacy_backfill_denylisted\b|flock|lock|MAX.*READ|max_cycles|preserve_raw_progress|BRAINLAYER_DB' \
  src tests

Length of output: 183028


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '== commit identity ==\n'
git rev-parse HEAD
gh pr view 599 --repo EtanHey/brainlayer --json headRefOid,baseRefName --jq '"remote_head=\(.headRefOid)\nbase=\(.baseRefName)"'

printf '\n== files/stat ==\n'
git diff --stat main...HEAD
git diff --name-only main...HEAD

printf '\n== focused fix-round commits ==\n'
git log --oneline --decorate main..HEAD | head -40

printf '\n== current fix-round diff ==\n'
git diff --find-renames --unified=40 HEAD^..HEAD -- \
  src/brainlayer/backfill.py \
  src/brainlayer/watcher.py \
  src/brainlayer/cli/__init__.py \
  src/brainlayer/ingest_denylist.py \
  tests/test_backfill.py \
  tests/test_jsonl_watcher.py \
  tests/test_watch_backfill_cli.py \
  tests/test_cli_commands.py \
  tests/test_run_tests_script.py \
  scripts/run_tests.sh

printf '\n== source: backfill.py ==\n'
sed -n '1,260p' src/brainlayer/backfill.py
printf '\n== source: watcher.py tailer/watcher ==\n'
sed -n '1,420p' src/brainlayer/watcher.py
printf '\n== source: watch-backfill command ==\n'
sed -n '3470,3655p' src/brainlayer/cli/__init__.py
printf '\n== source: ingest denylist ==\n'
sed -n '1,260p' src/brainlayer/ingest_denylist.py

printf '\n== specifically named regression tests ==\n'
rg -n -C 8 --glob 'test_*.py' \
  'max_cycles|1_048_576|max_read_bytes|backfill_run_lock|BackfillAlreadyRunning|legacy.*registry|discovery_predicate|progress.*marker|BRAINLAYER_DB|retired.*root|malformed.*timestamp' \
  tests/test_backfill.py tests/test_jsonl_watcher.py tests/test_watch_backfill_cli.py tests/test_cli_commands.py tests/test_ingest_denylist.py

Length of output: 124955


@EtanHey, I verified that both the local checkout and PR #599 remote head are e43e1fb21c9ed8c72cc95d4bef95f57bbd144812 (base: main).

Found one blocking regression in the bounded-read hardening:

  • src/brainlayer/watcher.pyJSONLTailer.read_new_lines: the 1 MiB limit is not retained when the file begins with a newline-free record larger than the cap. After the initial read fills self._buffer with 1 MiB, the next call computes remaining_budget <= 0 but calls f.read(read_budget) again, appending another full MiB. Repeated polls therefore grow the buffer without bound until a newline appears.
    The new test covers many complete short lines, but not an oversized unterminated line. Stop reading once the buffer reaches the cap (or explicitly define and enforce a separate maximum-record policy), and add a regression test for that case.
✅ 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/cli/__init__.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: e43e1fb21c

ℹ️ 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 on lines +189 to +190
if _is_claude_subagent(candidate):
return _claude_subagent_attribution(candidate) == "brain-worker"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Cap attribution reads during legacy backfill

For a legacy Claude subagent whose attributionAgent is absent or appears late in a large transcript, discovery invokes _claude_subagent_attribution, which iterates the entire file before JSONLTailer.read_new_lines(..., max_read_bytes=1_048_576) is ever reached. This bypasses the new 1 MiB backfill read cap (and can repeatedly require a multi-GB discovery read), allowing a single historical transcript to stall or exhaust the backfill's I/O/memory budget; make attribution detection bounded or defer it to the capped tailing path.

Useful? React with 👍 / 👎.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/brainlayer/cli/__init__.py (1)

3313-3337: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Retry the rewind-archive transaction on SQLite busy. A transient lock can leave this batch unarchived until a later flush; add the same bounded busy-retry/backoff pattern used elsewhere and keep the rollback/telemetry path intact.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/brainlayer/cli/__init__.py` around lines 3313 - 3337, Update the
rewind-archive transaction around cursor.execute("BEGIN IMMEDIATE") to use the
existing bounded SQLite-busy retry and backoff pattern from elsewhere in the
module. Retry transient busy/locked failures for the whole transaction,
preserving the affected count on success and keeping the existing rollback and
telemetry_span.finish error path for final failures.

Source: Coding guidelines

src/brainlayer/backfill.py (1)

5-68: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Canonicalize the registry before taking the backfill lock. src/brainlayer/backfill.py#L52-L68 uses the raw registry path to derive .backfill.lock, so symlinked or aliased paths can acquire separate locks and run the same backfill concurrently. Resolve the registry first, and reuse the existing cross-platform lock seam instead of importing fcntl unconditionally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/brainlayer/backfill.py` around lines 5 - 68, Update backfill_run_lock in
src/brainlayer/backfill.py to canonicalize the expanded registry path before
deriving the lock filename, and reuse the project’s existing cross-platform lock
abstraction instead of importing fcntl unconditionally. Update the related
expectations in tests/test_backfill.py lines 236-242 to reflect canonicalized
locking; no other sites require changes.

Source: Coding guidelines

tests/test_run_tests_script.py (1)

271-275: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that the unrelated test is excluded.

The test creates a placeholder for test_think_recall_integration.py but doesn't assert its exclusion. Add the same targeted_invocation check used in the CLI test above to ensure the unrelated file isn't inadvertently included in the test run.

💡 Proposed fix
     assert result.returncode == 0
     logged = pytest_log.read_text()
     assert str(test_root / "test_jsonl_watcher.py") in logged
+    targeted_invocation = next(line for line in logged.splitlines() if str(test_root / "test_jsonl_watcher.py") in line)
+    assert str(test_root / "test_think_recall_integration.py") not in targeted_invocation
     assert "falling back to full pytest unit suite" not in result.stdout
     assert f"{test_root}/ -v" not in logged
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_run_tests_script.py` around lines 271 - 275, Extend the test
assertions around the logged targeted invocation to verify that
test_think_recall_integration.py is excluded, reusing the same
targeted_invocation check established by the CLI test above. Keep the existing
assertions for test_jsonl_watcher.py, fallback output, and full-suite invocation
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/brainlayer/backfill.py`:
- Around line 5-6: Update backfill_run_lock to resolve the registry path before
deriving its lock key, ensuring symlink and alias paths share one lock. Remove
direct fcntl usage and reuse the project’s shared cross-platform POSIX/Windows
run-lock helper instead.

In `@src/brainlayer/watcher.py`:
- Around line 656-666: Prevent the watcher’s read loop from reading again when
an unterminated buffer has exhausted its cap: update the read logic around the
buffer handling in src/brainlayer/watcher.py lines 656-666 to skip the file read
when remaining_budget is zero or negative, while preserving reads when budget
remains. In tests/test_jsonl_watcher.py lines 542-551, use a single newline-free
record larger than the cap and assert repeated polling does not increase the
buffer.

In `@tests/test_jsonl_watcher.py`:
- Around line 542-551: Update test_read_new_lines_bounds_offset_zero_read_bytes
to use an unterminated JSONL record larger than 256 bytes instead of repeated
newline-terminated records. Call JSONLTailer.read_new_lines repeatedly with
max_lines=1 and max_read_bytes=256, asserting after each call that
tailer._buffer never exceeds 256 bytes.

---

Outside diff comments:
In `@src/brainlayer/backfill.py`:
- Around line 5-68: Update backfill_run_lock in src/brainlayer/backfill.py to
canonicalize the expanded registry path before deriving the lock filename, and
reuse the project’s existing cross-platform lock abstraction instead of
importing fcntl unconditionally. Update the related expectations in
tests/test_backfill.py lines 236-242 to reflect canonicalized locking; no other
sites require changes.

In `@src/brainlayer/cli/__init__.py`:
- Around line 3313-3337: Update the rewind-archive transaction around
cursor.execute("BEGIN IMMEDIATE") to use the existing bounded SQLite-busy retry
and backoff pattern from elsewhere in the module. Retry transient busy/locked
failures for the whole transaction, preserving the affected count on success and
keeping the existing rollback and telemetry_span.finish error path for final
failures.

In `@tests/test_run_tests_script.py`:
- Around line 271-275: Extend the test assertions around the logged targeted
invocation to verify that test_think_recall_integration.py is excluded, reusing
the same targeted_invocation check established by the CLI test above. Keep the
existing assertions for test_jsonl_watcher.py, fallback output, and full-suite
invocation unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 84ea59b5-1353-4354-937c-9b50a0d7bb66

📥 Commits

Reviewing files that changed from the base of the PR and between fdc020c and e43e1fb.

📒 Files selected for processing (8)
  • scripts/run_tests.sh
  • src/brainlayer/backfill.py
  • src/brainlayer/cli/__init__.py
  • src/brainlayer/watcher.py
  • tests/test_backfill.py
  • tests/test_jsonl_watcher.py
  • tests/test_run_tests_script.py
  • tests/test_watch_backfill_cli.py
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Flag risky DB or concurrency changes explicitly and do not hand-wave lock behavior
Enforce one-write-at-a-time concurrency constraint; reads are safe but brain_digest is write-heavy and must not run in parallel with other MCP work
Run pytest before claiming behavior changed safely; current test suite has 929 tests

Files:

  • tests/test_run_tests_script.py
  • tests/test_jsonl_watcher.py
  • src/brainlayer/cli/__init__.py
  • src/brainlayer/backfill.py
  • tests/test_backfill.py
  • tests/test_watch_backfill_cli.py
  • src/brainlayer/watcher.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

Tests must not refresh the production backup heartbeat log; use BRAINLAYER_BACKUP_LOG_PATH and set provenance to pytest.

Files:

  • tests/test_run_tests_script.py
  • tests/test_jsonl_watcher.py
  • tests/test_backfill.py
  • tests/test_watch_backfill_cli.py
src/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/**/*.py: Use paths.py:get_db_path() for all database path resolution; do not hard-code the BrainLayer database path.
Each worker must use its own database connection and retry on SQLITE_BUSY.
For bulk database operations, stop enrichment workers, checkpoint WAL before and after, drop and recreate FTS triggers around large deletes, batch deletes in 5–10K chunks, and checkpoint every three batches.
Use the canonical BrainLayer naming: BrainLayer (זיכרון) means “memory.”

Files:

  • src/brainlayer/cli/__init__.py
  • src/brainlayer/backfill.py
  • src/brainlayer/watcher.py
src/brainlayer/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/brainlayer/**/*.py: Preserve ai_code, stack_trace, and user_message verbatim; skip noise, summarize build logs, and retain only structure for directory listings.
Use AST-aware tree-sitter chunking, never split stack traces, and mask large tool output.
Keep Groq as the primary enrichment backend, Gemini as fallback, and Ollama as the offline last resort; honor BRAINLAYER_ENRICH_BACKEND and BRAINLAYER_ENRICH_RATE.
Default searches must exclude lifecycle-managed chunks; include_archived=True may expose history. brain_supersede must apply its personal-data safety gate, and brain_archive must soft-delete with a timestamp.

Files:

  • src/brainlayer/cli/__init__.py
  • src/brainlayer/backfill.py
  • src/brainlayer/watcher.py
src/brainlayer/watcher*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/brainlayer/watcher*.py: The JSONL watcher must apply filters in order: entry-type whitelist, classification, minimum chunk length, and system-reminder stripping.
Persist watcher offsets, detect file rewinds, and soft-archive chunks reverted by checkpoint restoration.

Files:

  • src/brainlayer/watcher.py
🪛 ast-grep (0.44.1)
tests/test_run_tests_script.py

[error] 268-268: Command coming from incoming request
Context: subprocess.run(["bash", str(SCRIPT_PATH)], capture_output=True, text=True, env=env)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

tests/test_jsonl_watcher.py

[info] 495-504: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
watcher_module._OFFSET_TOMBSTONES_KEY: {
"/valid.jsonl": 123.0,
"/string.jsonl": "invalid",
"/bool.jsonl": True,
"/nan.jsonl": float("nan"),
}
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 776-781: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
str(existing): {"offset": existing.stat().st_size, "inode": existing.stat().st_ino},
str(deleted): {"offset": 100, "inode": 999},
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 805-809: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
str(deleted): {"offset": 100, "inode": 999},
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

tests/test_watch_backfill_cli.py

[info] 418-426: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
str(direct): {
"offset": 1,
"inode": direct.stat().st_ino,
"mtime": direct.stat().st_mtime,
}
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

src/brainlayer/watcher.py

[warning] 322-322: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(self.path)
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)


[warning] 658-658: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(self.filepath, "rb")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🔇 Additional comments (7)
scripts/run_tests.sh (1)

153-159: LGTM!

src/brainlayer/backfill.py (1)

71-88: LGTM!

Also applies to: 91-158

tests/test_backfill.py (1)

3-5: LGTM!

Also applies to: 20-216, 219-234

src/brainlayer/watcher.py (1)

17-41: LGTM!

Also applies to: 99-600, 645-655, 667-714, 873-1071, 1227-1313

tests/test_jsonl_watcher.py (1)

13-518: LGTM!

Also applies to: 730-1891

src/brainlayer/cli/__init__.py (1)

3505-3624: LGTM!

tests/test_watch_backfill_cli.py (1)

3-16: LGTM!

Also applies to: 119-140, 409-547

Comment on lines +5 to +6
import errno
import fcntl

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -i 'pyproject.toml|README.md|watcher.py|backfill.py' --exec \
  rg -n -C3 'Windows|fcntl|msvcrt|backfill_run_lock|_lock_offset_registry_file' {}

Repository: EtanHey/brainlayer

Length of output: 4310


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the backfill lock implementation and nearby helpers.
sed -n '1,120p' src/brainlayer/backfill.py
printf '\n----\n'
sed -n '220,420p' src/brainlayer/watcher.py
printf '\n----\n'
sed -n '1,140p' src/brainlayer/tests/test_backfill.py

Repository: EtanHey/brainlayer

Length of output: 13527


Use a canonical, cross-platform run lock in src/brainlayer/backfill.py:53-68. backfill_run_lock still imports fcntl directly, so this path is broken on Windows. It also keys the lock off the raw registry path, so symlink/alias paths can take separate locks and run concurrently. Resolve the registry path before deriving the lock and reuse the shared POSIX/Windows lock helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/brainlayer/backfill.py` around lines 5 - 6, Update backfill_run_lock to
resolve the registry path before deriving its lock key, ensuring symlink and
alias paths share one lock. Remove direct fcntl usage and reuse the project’s
shared cross-platform POSIX/Windows run-lock helper instead.

Source: Coding guidelines

Comment thread src/brainlayer/watcher.py
Comment on lines +656 to +666
new_data = b""
if b"\n" not in self._buffer:
try:
with open(self.filepath, "rb") as f:
f.seek(self.offset + len(self._buffer))
if max_read_bytes is None:
new_data = f.read()
else:
read_budget = max(1, max_read_bytes)
remaining_budget = read_budget - len(self._buffer)
new_data = f.read(read_budget if remaining_budget <= 0 else remaining_budget)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Enforce the read cap for oversized unterminated records. A full buffer currently triggers another full-budget read on each poll.

  • src/brainlayer/watcher.py#L656-L666: do not read when the remaining budget is zero or negative.
  • tests/test_jsonl_watcher.py#L542-L551: use one record larger than the cap without a newline and assert repeated calls do not grow the buffer.
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 658-658: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(self.filepath, "rb")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

📍 Affects 2 files
  • src/brainlayer/watcher.py#L656-L666 (this comment)
  • tests/test_jsonl_watcher.py#L542-L551
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/brainlayer/watcher.py` around lines 656 - 666, Prevent the watcher’s read
loop from reading again when an unterminated buffer has exhausted its cap:
update the read logic around the buffer handling in src/brainlayer/watcher.py
lines 656-666 to skip the file read when remaining_budget is zero or negative,
while preserving reads when budget remains. In tests/test_jsonl_watcher.py lines
542-551, use a single newline-free record larger than the cap and assert
repeated polling does not increase the buffer.

Comment on lines +542 to +551
def test_read_new_lines_bounds_offset_zero_read_bytes(self, tmp_path):
transcript = tmp_path / "large.jsonl"
transcript.write_bytes(b'{"type":"msg","payload":"xxxxxxxxxxxxxxxx"}\n' * 1_000)
tailer = JSONLTailer(str(transcript))

for _ in range(10):
lines = tailer.read_new_lines(max_lines=1, max_read_bytes=256)

assert len(lines) == 1
assert len(tailer._buffer) <= 256

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Exercise an oversized unterminated record.

Short complete lines drain the buffer and cannot catch the cap regression. Write more than 256 bytes without a newline, call read_new_lines repeatedly, and assert the buffer never exceeds 256 bytes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_jsonl_watcher.py` around lines 542 - 551, Update
test_read_new_lines_bounds_offset_zero_read_bytes to use an unterminated JSONL
record larger than 256 bytes instead of repeated newline-terminated records.
Call JSONLTailer.read_new_lines repeatedly with max_lines=1 and
max_read_bytes=256, asserting after each call that tailer._buffer never exceeds
256 bytes.

@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_edd7dce2-752b-4274-ba5c-f8aab43b1fbd)

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

@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: 90ac44bea8

ℹ️ 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 on lines +3530 to +3534
if registry:
registry_path = registry.expanduser()
elif window:
scope_suffix = "-legacy-excluded-only" if legacy_excluded_only else ""
registry_path = db_path.parent / f"backfill-offsets-{window_registry_suffix(*window)}{scope_suffix}.json"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Isolate explicit window registries from the live watcher

For a time-windowed run, accepting an explicit --registry lets callers select the live offsets.json (the option help still says that is its default). WindowedFlush confirms offsets for every scanned out-of-window record, so if a live watcher has not yet read a file, its next tailer/restart begins at the backfill's EOF watermark and permanently skips those records without indexing them. This remains possible on the non-legacy window path despite the legacy discovery fix; reject the live registry for windowed backfills or namespace the supplied path before constructing the watcher.

Useful? React with 👍 / 👎.

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