Skip to content

fix(watch): preserve watches when promotion fails - #957

Closed
yiheng-kkk wants to merge 1 commit into
tirth8205:mainfrom
yiheng-kkk:fix/927-watch-promotion-failure
Closed

yiheng-kkk wants to merge 1 commit into
tirth8205:mainfrom
yiheng-kkk:fix/927-watch-promotion-failure

Conversation

@yiheng-kkk

Copy link
Copy Markdown

Linked issue

Closes #927

What & why

A failed recursive promotion currently releases the parent and descendant watches before the replacement is registered. Register the replacement first, restore the prior supervisor state when scheduling fails, and report the attempted directory as unadopted so a later tick can retry.

How it was tested

uv run pytest tests/test_watch_robustness.py -q
uv run --python 3.10 --isolated --extra dev pytest tests/test_watch_robustness.py -q
uv run ruff check code_review_graph/ tests/test_watch_robustness.py
uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optional
uvx --from bandit[toml] bandit -r code_review_graph/ -c pyproject.toml -q

The watcher suite passed 57 tests on Python 3.13 and 3.10. Ruff, mypy, and Bandit passed.

The full suite completed with 2,985 passed, 9 skipped, 2 xpassed, and one unrelated token-budget failure in get_affected_flows_tool; the same failure reproduces on the unmodified base commit.

Checklist

  • Tests added for new functionality
  • All tests pass: one unrelated baseline failure is described above
  • Linting passes
  • Type checking passes
  • Lines are at most 100 characters
  • No documentation change is needed for this internal recovery fix

@tirth8205

Copy link
Copy Markdown
Owner

Thank you for this PR. I tested it on its own base and merged with staging.

On its own base, your fix works. The bug in #927 was real.

But staging already fixed #927 in #988. That fix keeps the old watches until the new one works. It also waits 30 seconds before it tries again. It marks the watcher as degraded while it waits.

Your change conflicts with #988 in code_review_graph/incremental.py. Your test also expects the opposite of #988. It wants degraded to be False and a retry on the next tick.

So I will not merge this one.

Thank you again for your help.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues block the watch-promotion recovery change.

No open issues 🎉

What changed in this PR

This PR addresses #927 by preserving existing filesystem watches when recursive promotion fails, allowing the watch supervisor to retry on a later tick.

Changes:

  • Register the recursive replacement before releasing existing watches, restoring supervisor state if registration fails.
  • Return promotion success to the adoption caller and add regression coverage for failure preservation and successful retry.
File Description
tests/​test_watch_robustness.py Tests that failed promotion preserves watches and a later retry succeeds.
code_review_graph/​incremental.py Makes watch promotion recoverable and propagates its success status.

Review effort: Balanced

@tirth8205

Copy link
Copy Markdown
Owner

Thank you for this. Commits e68e353 and d9c9d39 (#988) keep the old watches until the new one is live, then report degraded and retry after 30 seconds, and #927 is closed. That deliberately differs from the retry-next-tick policy here, so I'm closing this one.

@tirth8205 tirth8205 closed this Nov 9, 2026
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.

watch: a failed promotion releases every watch first, so the supervisor can end up watching nothing with no retry

3 participants