Dedupe same-fingerprint investigation tasks across federated peers#2818
Merged
Conversation
…er-merge boundary
…gress status, not lease liveness
…ged affectedTasks into survivor description
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two federated peers can each create an investigation task for the same
metadata.investigationFingerprintbefore the next sync sweep. The per-process fingerprint dedup added in #2615 is serialized on a module-level promise tail, so a single install never mints two open investigations for one failure cause — butcosTaskMerge.jsunions cross-peer records by id, so both peers' rows survive synchronization.This adds a deterministic, side-independent dedup pass at the peer-merge boundary in
server/services/cosTaskMerge.js(a new rule 4, run after the existing per-id union). For eachinvestigationFingerprintgroup with more than one OPEN copy it:in_progresscopy wins outright (an agent is running it — never orphan an in-flight investigation); otherwise the lower-id / older copy wins. When more than one copy isin_progress, the collapse is skipped for that sweep (both run to completion and the group self-heals as each turns terminal).metadata.affectedTasks(deduped, sorted) onto the survivor, and mirrors the peer-contributed ids into the survivor's description (idempotent, deterministic) so the investigation agent/user — and theisReapableInvestigationreaper — see every task blocked on the cause, matching the local same-fingerprint path inagentErrorAnalysis.completedwith asupersededBymarker rather than deleting them (LWW sync never propagates deletions; a terminal status converges via the existing rule-2 status rank). ThesupersededBymarker keeps the pass idempotent and lets a mid-propagation partial survivor re-fold the full union.The no-orphan guard keys on the
in_progressstatus (which replicates via rule-2 rank) rather than lease liveness, so a peer whose lease timestamp is stale still recognizes an actively-worked copy. "Open" matchesagentErrorAnalysis.OPEN_INVESTIGATION_STATUSES(pending/in_progress/challenged/blocked), so twoblockedsame-cause copies are collapsed too. The residual pre-claim-snapshot race across ≥3 peers is documented and accepted (investigation tasks are approval-gated diagnostics that execute nothing on their own; the storm is already bounded to one per peer per cause).Test plan
cd server && npm test -- cosTaskMerge— 43 tests pass, including new coverage proving two same-fingerprint tasks from two peers converge to one active row (both sweep directions), the in_progress-survivor no-orphan case (including a stale/expired-lease copy), both-in-progress skip,affectedTasksunion dedupe, blocked+blocked and blocked+pending collapse, description mirroring + idempotency, and re-fold after a partial per-id revert.cd server && npm test -- cosTask agentErrorAnalysis peerSync— 606 related federation/task tests pass (no regression).Reviewed locally with claude, ollama, and codex (series). claude flagged that keying the no-orphan guard on lease liveness was stale-prone across 3+ peers (fixed → status-based); codex flagged that
blockedinvestigations escaped the terminal filter and that merged affected tasks weren't surfaced in the survivor's description (both fixed).Closes #2628