Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

104 lines
3.4 KiB
Python

"""File-write conflict detection for ``/fleet conflicts``.
Pure detection logic over write events. Presentation lives in
``interactive_shell.command_registry.agents.conflicts_view`` so this
module stays independent of the interactive shell.
"""
from __future__ import annotations
from collections import defaultdict
from collections.abc import Sequence
from dataclasses import dataclass
#: Default conflict window. Two distinct agents writing the same file within
#: this many seconds is treated as a conflict (per AniketXD on Discord).
DEFAULT_WINDOW_SECONDS: float = 10.0
@dataclass(frozen=True)
class WriteEvent:
"""A single observed write to a path by an agent process.
``agent`` is a display name plus pid (e.g. ``"claude-code:8421"``).
``timestamp`` is unix seconds, when the write was observed.
"""
agent: str
path: str
timestamp: float
@dataclass(frozen=True)
class FileWriteConflict:
"""Two or more distinct agents wrote to the same path within the window.
``agents`` is sorted alphabetically for stable output. ``first_seen`` and
``last_seen`` are the earliest and latest observed write timestamps in the
colliding cluster.
"""
path: str
agents: tuple[str, ...]
first_seen: float
last_seen: float
def detect_conflicts(
events: Sequence[WriteEvent],
window_seconds: float,
opensre_agent_id: str,
) -> list[FileWriteConflict]:
"""Return file-write conflicts within ``window_seconds`` of the most recent event.
A conflict is a path written by two or more distinct agents whose write
events all fall within ``window_seconds`` of the most recent non-OpenSRE
event. Repeated writes by the same agent collapse to a single entry in
``agents``. Events whose ``agent`` matches ``opensre_agent_id`` are removed
before window selection so OpenSRE never reports itself as a colliding
agent and never anchors the window with its own activity.
The window is anchored on the most recent event timestamp rather than
wall-clock ``now`` so the function stays pure and testable. The boundary is
inclusive: an event exactly ``window_seconds`` older than the anchor is kept.
Results are sorted by ``last_seen`` descending (freshest collisions first),
with ``path`` ascending as a stable tiebreaker.
"""
relevant = [e for e in events if e.agent != opensre_agent_id]
if not relevant:
return []
anchor = max(e.timestamp for e in relevant)
in_window = [e for e in relevant if anchor - e.timestamp <= window_seconds]
by_path: dict[str, list[WriteEvent]] = defaultdict(list)
for event in in_window:
by_path[event.path].append(event)
conflicts: list[FileWriteConflict] = []
for path, group in by_path.items():
distinct_agents = {e.agent for e in group}
if len(distinct_agents) < 2:
continue
timestamps = [e.timestamp for e in group]
conflicts.append(
FileWriteConflict(
path=path,
agents=tuple(sorted(distinct_agents)),
first_seen=min(timestamps),
last_seen=max(timestamps),
)
)
conflicts.sort(key=lambda c: c.path)
conflicts.sort(key=lambda c: c.last_seen, reverse=True)
return conflicts
__all__ = [
"DEFAULT_WINDOW_SECONDS",
"FileWriteConflict",
"WriteEvent",
"detect_conflicts",
]