{"record":{"id":"98517b6f9a2d458f","repo":"ruvnet/ruflo","slug":"proposal-proposalid-already-resolved","errorCode":null,"errorMessage":"Proposal ${proposalId} already resolved","messagePattern":"Proposal (.+?) already resolved","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"v3/@claude-flow/guidance/src/adversarial.ts","lineNumber":699,"sourceCode":"      }\n      if (oldestId) {\n        this.proposals.delete(oldestId);\n      }\n    }\n\n    return proposalId;\n  }\n\n  /**\n   * Vote on a proposal\n   */\n  vote(proposalId: string, voterId: string, approve: boolean): void {\n    const proposal = this.proposals.get(proposalId);\n    if (!proposal) {\n      throw new Error(`Proposal ${proposalId} not found`);\n    }\n    if (proposal.resolved) {\n      throw new Error(`Proposal ${proposalId} already resolved`);\n    }\n\n    proposal.votes.set(voterId, approve);\n  }\n\n  /**\n   * Resolve a proposal (check if quorum reached)\n   */\n  resolve(proposalId: string): QuorumResult {\n    const proposal = this.proposals.get(proposalId);\n    if (!proposal) {\n      throw new Error(`Proposal ${proposalId} not found`);\n    }\n\n    // Single pass over votes instead of two filter calls\n    let forCount = 0;\n    let againstCount = 0;\n    for (const v of proposal.votes.values()) {","sourceCodeStart":681,"sourceCodeEnd":717,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/guidance/src/adversarial.ts#L681-L717","documentation":"AdversarialCoordinator.vote() refuses votes on proposals already marked `resolved` (set by resolve()). This keeps the final quorum decision immutable after resolution. The check runs after the existence check, so seeing this error means the ID is valid but voting is closed.","triggerScenarios":"Calling vote() after resolve(proposalId) already ran; two voters racing where one voter triggers resolution and the second vote lands after; retry pipelines that replay all votes including ones already counted toward a resolved proposal.","commonSituations":"Fan-out voting where late voters straggle in after quorum was reached; event replay / at-least-once delivery re-delivering a vote; test suites that reuse a proposal across cases without recreating it.","solutions":["Check `coordinator.getProposal(id)?.resolved` before voting and skip closed proposals","Treat this error as a no-op signal in retry/replay logic (catch and drop the duplicate vote)","Propose a new proposal if a post-resolution vote genuinely must be recorded"],"exampleFix":"// before\ncoordinator.vote(proposalId, voterId, true); // may throw 'already resolved'\n\n// after\nconst p = coordinator.getProposal(proposalId);\nif (p && !p.resolved) {\n  coordinator.vote(proposalId, voterId, true);\n} else {\n  // voting closed; duplicate or late vote — drop it\n}","handlingStrategy":"validation","validationCode":"const p = coordinator.getProposal(proposalId);\nif (p && !p.resolved) {\n  coordinator.vote(proposalId, voterId, approve);\n}","typeGuard":null,"tryCatchPattern":"try {\n  coordinator.vote(proposalId, voterId, approve);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('already resolved')) {\n    // late/duplicate vote after quorum — safe to ignore\n  } else throw err;\n}","preventionTips":["Make voter pipelines idempotent: check `resolved` before voting","Drop replayed votes in at-least-once delivery systems instead of retrying them","Create a fresh proposal per test case"],"tags":["adversarial","proposal","guidance","lifecycle","double-submit"],"backgroundTag":"operation-on-resolved-entity","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}