SIGN IN SIGN UP

fix(watch): reconcile both directions on repair, narrow before promoting

Three defects introduced by the previous commit, from review:

- the repair path announced only a creation for the rescheduled root, but
  only the deletion contributes the stored descendants, and watch batches run
  with reconcile_stale=False. A file deleted while the watch was down kept its
  rows indefinitely, which is the stale-row divergence of #812/#817. Both
  events are dispatched now;

- the adopt-time budget check promoted the parent — usually the repository
  root — as soon as the deepest plan did not fit, handing every ignored tree
  back to the OS. That is the condition #811 is about, so a fix for it must
  not be able to re-create it. `_affordable_plan` now retries at shallower
  depths and then at a single recursive watch on the new directory, and only
  a repository with no free slot at all falls back to promotion;

- a failed reschedule inside the repair (ENOSPC from inotify, which is #811's
  own trigger) still marked the root repaired, leaving `_repaired_roots` with
  an entry that has no watch. The failure is now reported as a death, so a
  directory that ended up unwatched cannot pass quietly as repaired.

Each has a regression test that fails on the previous commit: "a file deleted
during the outage kept its rows", "the repository root was promoted", and "an
unwatched directory was reported as repaired".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012fHfGDiZedoxjpKzanHri3
T
Tirth Kanani committed
2c8961f40f01126f9a40b4aab58f181fb6a5a77e
Parent: c4c4d70