fix: cross-project event flooding + pending accumulation + diff_review brain exclusion
Phase 1: Collector auto-cleanup of auto_resolved/expired pending files after Gateway forwarding Phase 2: Watcher project filter (only MY sessions emit events) + Collector event forward filter Phase 3: Extension diff_review excludes brain/ artifact files (task.md, implementation_plan.md)
This commit is contained in:
55
watcher.py
55
watcher.py
@@ -40,7 +40,11 @@ class BrainEvent:
|
||||
|
||||
|
||||
class BrainEventHandler(FileSystemEventHandler):
|
||||
"""Watchdog handler that filters, debounces, and deduplicates brain events."""
|
||||
"""Watchdog handler that filters, debounces, and deduplicates brain events.
|
||||
|
||||
Phase 2 FIX: Only emits events for sessions belonging to the current project
|
||||
(Config.PROJECT_NAME), using bridge/register/ files for session→project mapping.
|
||||
"""
|
||||
|
||||
def __init__(self, event_queue: asyncio.Queue, loop: asyncio.AbstractEventLoop):
|
||||
super().__init__()
|
||||
@@ -49,6 +53,10 @@ class BrainEventHandler(FileSystemEventHandler):
|
||||
self._last_events: dict[str, float] = {} # path -> timestamp (debounce)
|
||||
self._content_hashes: dict[str, str] = {} # path -> md5 hash (dedup)
|
||||
self._known_sessions: set[str] = set()
|
||||
# Phase 2: project filter
|
||||
self._session_project_map: dict[str, str] = {} # conv_id → project_name
|
||||
self._project_map_ts: float = 0 # last load timestamp
|
||||
self._PROJECT_MAP_TTL: float = 60.0 # reload every 60s
|
||||
self._initialize_known_sessions()
|
||||
|
||||
def _initialize_known_sessions(self):
|
||||
@@ -77,6 +85,47 @@ class BrainEventHandler(FileSystemEventHandler):
|
||||
f"pre-loaded {hash_count} content hashes"
|
||||
)
|
||||
|
||||
def _load_session_project_map(self) -> dict[str, str]:
|
||||
"""Load session→project mapping from bridge/register/ files (cached)."""
|
||||
now = time.time()
|
||||
if now - self._project_map_ts < self._PROJECT_MAP_TTL:
|
||||
return self._session_project_map
|
||||
|
||||
import json
|
||||
register_dir = Config.BRAIN_PATH.parent / "bridge" / "register"
|
||||
if not register_dir.exists():
|
||||
self._project_map_ts = now
|
||||
return self._session_project_map
|
||||
|
||||
new_map: dict[str, str] = {}
|
||||
for f in register_dir.glob("*.json"):
|
||||
try:
|
||||
data = json.loads(f.read_text(encoding="utf-8-sig"))
|
||||
conv_id = data.get("conversation_id", "")
|
||||
project = data.get("project_name", "")
|
||||
if conv_id and project:
|
||||
new_map[conv_id] = project
|
||||
except (json.JSONDecodeError, OSError):
|
||||
pass
|
||||
|
||||
self._session_project_map = new_map
|
||||
self._project_map_ts = now
|
||||
return self._session_project_map
|
||||
|
||||
def _is_my_session(self, conv_id: str) -> bool:
|
||||
"""Check if a session belongs to the current project.
|
||||
|
||||
Returns True for:
|
||||
- Sessions registered to Config.PROJECT_NAME
|
||||
- Unknown sessions (not in any register file — allow to avoid blocking)
|
||||
Returns False for sessions registered to OTHER projects.
|
||||
"""
|
||||
session_map = self._load_session_project_map()
|
||||
project = session_map.get(conv_id)
|
||||
if project is None:
|
||||
return True # Unknown → allow (newly started, not yet registered)
|
||||
return project == Config.PROJECT_NAME
|
||||
|
||||
def dispatch(self, event: FileSystemEvent):
|
||||
"""Early filter: skip events for files/dirs we don't care about.
|
||||
|
||||
@@ -169,6 +218,10 @@ class BrainEventHandler(FileSystemEventHandler):
|
||||
if not conv_id:
|
||||
return
|
||||
|
||||
# Phase 2 FIX: only emit events for MY project's sessions
|
||||
if not self._is_my_session(conv_id):
|
||||
return
|
||||
|
||||
# Exclude files in .system_generated subdirectory (AG internal logs)
|
||||
try:
|
||||
relative = path.relative_to(Config.BRAIN_PATH / conv_id)
|
||||
|
||||
Reference in New Issue
Block a user