feat(conflicts): add file overlap conflict detection (S-1 Phase 1)

Detects when 2+ agent sessions write the same file within a project.
New conflicts.svelte.ts store, shared tool-files.ts utility, dispatcher
integration, health attention scoring (SCORE_FILE_CONFLICT=70), and UI
indicators in ProjectHeader + StatusBar. 170/170 tests pass.
This commit is contained in:
Hibryda 2026-03-11 00:12:10 +01:00
parent 8e00e0ef8c
commit 82fb618c76
12 changed files with 483 additions and 37 deletions

View file

@ -25,6 +25,8 @@ import {
} from './adapters/groups-bridge';
import { tel } from './adapters/telemetry-bridge';
import { recordActivity, recordToolDone, recordTokenSnapshot } from './stores/health.svelte';
import { recordFileWrite, clearSessionWrites } from './stores/conflicts.svelte';
import { extractWritePaths } from './utils/tool-files';
let unlistenMsg: (() => void) | null = null;
let unlistenExit: (() => void) | null = null;
@ -180,7 +182,18 @@ function handleAgentEvent(sessionId: string, event: Record<string, unknown>): vo
}
// Health: record tool start
const projId = sessionProjectMap.get(sessionId);
if (projId) recordActivity(projId, tc.name);
if (projId) {
recordActivity(projId, tc.name);
// Conflict detection: track file writes
const writePaths = extractWritePaths(tc);
for (const filePath of writePaths) {
const isNewConflict = recordFileWrite(projId, sessionId, filePath);
if (isNewConflict) {
const shortName = filePath.split('/').pop() ?? filePath;
notify('warning', `File conflict: ${shortName} — multiple agents writing`);
}
}
}
break;
}
@ -214,6 +227,8 @@ function handleAgentEvent(sessionId: string, event: Record<string, unknown>): vo
if (costProjId) {
recordTokenSnapshot(costProjId, cost.inputTokens + cost.outputTokens, cost.totalCostUsd);
recordToolDone(costProjId);
// Conflict tracking: clear session writes on completion
clearSessionWrites(costProjId, sessionId);
}
// Persist session state for project-scoped sessions
persistSessionForProject(sessionId);