Feature: ResourceFileWatcher coverage boost
  Additional scenarios that exercise previously uncovered error-handling
  and fallback code paths in resource_file_watcher.py.

  Background:
    Given the resource_file_watcher module is imported

  # ---- start() OSError fallback to PollingObserver (lines 255-264) ----

  Scenario: start falls back to PollingObserver when inotify raises OSError
    Given a ResourceFileWatcher with default auto_reindex enabled
    And the native Observer start method is patched to raise OSError
    When the watcher start method is called
    Then the watcher should be running
    And the watcher should be using polling observer

  # ---- watch() schedule OSError fallback when not using polling (lines 168-180, 295-304) ----

  Scenario: watch falls back to polling when schedule raises OSError on non-polling observer
    Given a ResourceFileWatcher that is running with a mock native observer
    And a temporary file exists for watching
    And the mock observer schedule method raises OSError
    When I watch the temporary file with resource "res-fallback" project "proj-fallback"
    Then the watcher should have switched to polling observer
    And the temporary file should be in the watched paths

  # ---- watch() schedule OSError re-raise when already polling (line 183) ----

  Scenario: watch re-raises OSError when schedule fails and already using polling
    Given a ResourceFileWatcher that is running with a mock polling observer
    And a temporary file exists for watching
    And the mock observer schedule method raises OSError
    When I watch the temporary file expecting OSError with resource "res-reraise" project "proj-reraise"
    Then the watch call should have raised OSError

  # ---- stop() with observer that stays alive after join (lines 325-329) ----

  Scenario: stop logs warning when observer thread does not terminate in time
    Given a ResourceFileWatcher with a mock observer that stays alive after join
    When the watcher is stopped
    Then the watcher should not be running after stop
    And the observer join timeout warning path should have been exercised

  # ---- _switch_to_polling_observer_locked with existing observer (lines 297-304) ----

  Scenario: switch to polling observer stops old observer and creates new one
    Given a ResourceFileWatcher with a mock native observer and watched paths
    And a temporary file exists for watching
    When _switch_to_polling_observer_locked is called directly
    Then the old observer should have been stopped
    And the watcher should now have a polling observer

  # ---- start() with pre-registered paths rebuilds watches with polling fallback ----

  Scenario: start with pre-registered paths falls back to polling on OSError
    Given a ResourceFileWatcher with a pre-registered watched path
    And the native Observer start method is patched to raise OSError
    When the watcher start method is called
    Then the watcher should be running
    And the watcher should be using polling observer
    And the directory watches should include the pre-registered path parent

  # ---- _fire_change with dest_path populates event details ----

  Scenario: _fire_change includes dest_path in EventBus event details
    Given a ResourceFileWatcher with a tracking EventBus and debounce 0
    And the watcher is marked as running
    When _fire_change is invoked with a dest_path argument
    Then the emitted event details should contain the dest_path

  # ---- _fire_change when watcher not running exits early (line 459) ----

  Scenario: _fire_change exits early when watcher is not running
    Given a ResourceFileWatcher with a tracking callback
    And the watcher is not running
    When _fire_change is invoked directly
    Then the callback should not have been called
