Skip to content

fix(run): bound catalog watches to declaration space - #316

Closed
schickling wants to merge 2 commits into
schickling/2026-08-22-agent-publish-source-modefrom
schickling/2026-08-23-catalog-watch-boundary
Closed

fix(run): bound catalog watches to declaration space#316
schickling wants to merge 2 commits into
schickling/2026-08-22-agent-publish-source-modefrom
schickling/2026-08-23-catalog-watch-boundary

Conversation

@schickling

@schickling schickling commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Problem

The supervisor registers one recursive filesystem watch over the whole catalog. notify eagerly traverses Resource worktrees before callback filtering, so large payloads can delay the first reconcile, allocate unbounded inotify watches, and silently fall back to timer-only polling if registration fails.

The fallback also has a cadence bug: when watcher installation fails, its sender is dropped and the receiver reports Disconnected. The old wait loop treated that as an immediate wake, so it could continuously reconcile instead of polling on the configured interval.

Goal

Bound watcher installation to the declaration namespace while retaining immediate declaration create/update/remove detection and a real timer fallback that cannot spin when watcher installation fails.

Decisions

  • Subscribe non-recursively to each discovery-visible declaration directory. The shared agent_spec::is_catalog_path semantics stop traversal before Resource, inbox, archive, status, PTY, and control trees.
  • Refresh the shallow subscription set after each reconcile. A watched ancestor observes new directory topology, reconciliation sees the new declaration, and refresh installs watches for later edits.
  • Preserve installation errors and report one clear timer-fallback diagnostic instead of returning an opaque None.
  • Centralize the wait boundary: ordinary events wake early, timeout reaches the exact deadline, stop remains responsive in bounded slices, and a disconnected watcher channel sleeps through the remaining interval.
  • Keep one reconciliation protocol and the existing timer; this PR adds no dependency or background thread.

Callback-only filtering was rejected because it runs after recursive registration has already traversed the payload. A single shallow root watch was rejected because later edits in newly nested declaration directories would not wake immediately. Restarting the reconciliation protocol on channel disconnect was rejected because the existing timer is already the intended fallback.

Verification

  • Public reproduction: cargo run --release -> recursive watches: 519, declaration-only shallow watches: 4.
  • Focused shallow-watcher controls: 6 passed, 0 failed.
  • Failure-capable cadence control: forced watcher-install failure waits for the timer, while an ordinary declaration mutation still wakes early; 1 passed, 0 failed.
  • cargo test --quiet --workspace --lib --bins -- --test-threads=1: 373 passed, 0 failed.
  • Rustfmt 1.95 --check and git diff --check: passed.
  • nix build .#default --no-link --max-jobs 1 --cores 2 --option min-free 50000000000: passed.

Complexity

One small watcher guard owns the desired shallow subscription set and refresh operation. One wait helper owns event/timer/stop semantics. Both reuse existing discovery and reconciliation machinery; there is no new dependency, background thread, or protocol.

Concerns

The declaration namespace itself remains recursively discoverable by design. An organizational directory named resources is not excluded unless it is actual Agent state, preserving current discovery semantics.

Friction & bottlenecks

  • Friction: callback filtering looked bounded but could not affect backend registration traversal.
  • Friction: a timer fallback existed structurally but channel disconnection bypassed its cadence.
  • Bottleneck: verification was serialized under the shared build-capacity fence.

Follow-ups

None.

References

@schickling schickling changed the title fix(run): bound catalog declaration watches fix(run): bound catalog watches to declaration space Aug 23, 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.

Recursive catalog watcher traverses Resource payloads before first reconcile

1 participant