Skip to content

fix(brain-repo): watcher must observe install_dir, not brain_repo_dir#81

Open
mt-alarcon wants to merge 1 commit into
evolution-foundation:mainfrom
mt-alarcon:fix/brain-repo-watcher-source-path
Open

fix(brain-repo): watcher must observe install_dir, not brain_repo_dir#81
mt-alarcon wants to merge 1 commit into
evolution-foundation:mainfrom
mt-alarcon:fix/brain-repo-watcher-source-path

Conversation

@mt-alarcon
Copy link
Copy Markdown

@mt-alarcon mt-alarcon commented May 14, 2026

Summary

  • BrainRepoWatcher.start() was scheduling its watchdog observers on brain_repo_dir/{memory,workspace,customizations,config-safe} (the sync target), not on install_dir/... (the source where users actually edit).
  • Result: auto-sync silently went dormant. The observer would only fire when job_runner.copytree modified the destination — i.e. when a sync was already in progress — so external edits to the working tree never enqueued a sync.
  • One-line semantic fix: swap self._brain_repo_dirself._install_dir on line 108 of dashboard/backend/brain_repo/watcher.py.

Symptoms before the fix

Reproduced on a production install (mt-alarcon, 2026-05-14):

  • Last auto-commit on the brain repo dated from the previous day even though hours of edits had accumulated in the working tree.
  • git status on dashboard/data/brain-repos/evo-nexus-brain always reported "clean" until sync_force ran manually.
  • No BrainRepoWatcher started log line was missing — the observer started normally, just on the wrong paths.
  • On dev reloads / repeated boots, watchdog raised RuntimeError: Cannot add watch ... already scheduled because the destination paths overlapped with previously-registered observers in the same process.

Why the original code looked correct

The constructor docs and field names already imply the right contract:

def __init__(
    self,
    install_dir: Path,         # workspace root, where the user edits
    brain_repo_dir: Path,      # mirror destination, where copytree writes
    ...
):

The _install_dir field is even passed to _ChangeHandler (line 100), but the loop a few lines below uses _brain_repo_dir. Likely an early copy-paste from the sync code path that mutates the destination — never caught because the observer does fire when the mirror writes, so a manual sync_force looks like it works.

The fix

         watched_any = False
         for rel_path in WATCH_PATHS:
-            watch_target = self._brain_repo_dir / rel_path
+            # Watch the SOURCE (install_dir / working tree) — that's where the user
+            # actually edits files. Pre-fix this pointed at brain_repo_dir, which is
+            # the SYNC TARGET and only changes when copytree runs (no useful signal),
+            # so auto-sync never fired for user edits.
+            watch_target = self._install_dir / rel_path
             if watch_target.exists():

No API change, no migration, no schema touch.

Test plan

  • Restart dashboard with the fix applied (launchctl kickstart -k gui/$(id -u)/com.mta.evonexus).
  • Create a sentinel file in the working tree: echo test > <install>/memory/.watcher_test.md.
  • Wait ~30 s (debounce) + commit/push window.
  • Confirm a new auto: file watcher sync commit landed in the brain repo within ~36 s and was pushed to origin.
  • Cleanup: git rm the sentinel from the brain repo (sync is one-way / copy-only, so deletions in the source don't propagate — separate issue, not addressed here).

Notes for reviewers

  • Sync is still one-way (copytree with dirs_exist_ok=True, no --delete). Removing files from the working tree does not propagate to the brain repo — that's a separate behavior worth a follow-up but unrelated to this fix.
  • The RuntimeError: already scheduled traceback was a symptom of the wrong path, not a separate bug. Once the observer points at the correct source paths, the conflict disappears (the destination paths are no longer registered).

Summary by Sourcery

Bug Fixes:

  • Ensure BrainRepoWatcher observes the install_dir source paths so changes in the working tree correctly trigger auto-sync instead of only reacting to mirror writes.

The Brain Repo file watcher was scheduling its filesystem observers on
paths under `brain_repo_dir/{memory,workspace,customizations,config-safe}` —
the SYNC TARGET. Those paths only change when `job_runner` runs `copytree`
to mirror the install, which the sync itself triggers. The user's actual
edits to `install_dir/{memory,workspace,...}` were never observed, so
`_sync_fn` never fired and auto-sync silently went dormant.

Symptoms in production (mt-alarcon install, 2026-05-14):
  - Last auto-commit on the brain repo was from the previous day even
    though hours of edits had accumulated in the working tree.
  - `git status` on the brain repo destination always reported "clean"
    until a manual sync_force ran.
  - No "BrainRepoWatcher started" log entry was missing — the observer
    started normally but on the wrong paths.
  - When the same Python module was loaded twice (e.g. dev reloaders),
    watchdog raised `RuntimeError: Cannot add watch ... already scheduled`
    because the destination paths overlapped with already-registered
    observers on the SAME process.

Fix: schedule the observer on `self._install_dir / rel_path` instead.
This matches the constructor's documented contract — `install_dir` is the
workspace root where the user edits, `brain_repo_dir` is the mirror.
One-line semantic fix; no API change, no migration needed.

Verified locally: created `memory/.watcher_test.md` in the working tree,
auto-sync committed to the brain repo in ~36s (30s debounce + ~6s
commit/push).
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 14, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

BrainRepoWatcher now correctly observes the install_dir (user-edited source workspace) instead of the brain_repo_dir (sync destination), restoring automatic sync triggering on user edits without changing any external APIs or schemas.

File-Level Changes

Change Details Files
Fix BrainRepoWatcher to watch the source working tree instead of the mirror destination so file-edit events correctly enqueue syncs.
  • In the BrainRepoWatcher.start() loop, changed the watch root from the brain repo mirror path to the install_dir working tree for all WATCH_PATHS.
  • Added an inline comment explaining the rationale: install_dir is the source where users edit, while brain_repo_dir is the sync target updated by copytree, which previously caused the watcher to only fire during sync operations.
dashboard/backend/brain_repo/watcher.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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