{"record":{"id":"4b3aa5a44a7cdab2","repo":"ruvnet/ruflo","slug":"cannot-enact-amendment-with-status-amendment-st","errorCode":null,"errorMessage":"Cannot enact amendment with status: ${amendment.status}","messagePattern":"Cannot enact amendment with status: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/guidance/src/meta-governance.ts","lineNumber":435,"sourceCode":"      amendment.status = 'approved';\n    } else {\n      amendment.status = 'rejected';\n    }\n\n    return amendment;\n  }\n\n  /**\n   * Enact an approved amendment\n   * Returns true if enacted successfully\n   */\n  enactAmendment(amendmentId: string): boolean {\n    const amendment = this.amendments.get(amendmentId);\n    if (!amendment) {\n      throw new Error(`Amendment not found: ${amendmentId}`);\n    }\n    if (amendment.status !== 'approved') {\n      throw new Error(`Cannot enact amendment with status: ${amendment.status}`);\n    }\n\n    // Check if any changes would violate immutable invariants\n    for (const change of amendment.changes) {\n      if (change.type === 'remove-rule' || change.type === 'modify-rule') {\n        const invariant = this.invariants.get(change.target);\n        if (invariant?.immutable) {\n          throw new Error(`Cannot modify immutable invariant: ${change.target}`);\n        }\n      }\n    }\n\n    amendment.status = 'enacted';\n    this.amendmentHistory.push(amendment);\n    this.amendments.delete(amendmentId);\n\n    return true;\n  }","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/guidance/src/meta-governance.ts#L417-L453","documentation":"enactAmendment() requires status 'approved' — the state set by resolveAmendment() when the supermajority threshold and requiredApprovals are met. Calling enact on an amendment that is still 'proposed' (you forgot resolveAmendment) or that was 'rejected' throws this error with the actual status. The status remains unchanged after the throw, so the amendment stays in the map and the mistake is recoverable.","triggerScenarios":"Calling enactAmendment() directly after proposeAmendment() without vote + resolveAmendment(); enacting an amendment that resolveAmendment() marked 'rejected' because approvals fell short of the 0.75 supermajority default or requiredApprovals.","commonSituations":"Scripts that skip the voting phase in single-admin setups; automation assuming approval succeeded without checking the resolved status; rejected amendments left in the map and later swept up by an enacter loop.","solutions":["Always run voteOnAmendment() for enough voters, then resolveAmendment(), and check the returned status === 'approved' before enacting","If status is 'proposed', resolve first; if 'rejected', do not enact — re-propose with broader support instead","Because approved-but-unenacted amendments are not exposed by getPendingAmendments(), wrap enact in try-catch and branch on the status in the message","Clear out rejected amendments with vetoAmendment() so enacter loops cannot trip on them"],"exampleFix":"// before\ngovernor.enactAmendment(id); // still 'proposed' -> throws\n// after\nfor (const v of voters) governor.voteOnAmendment(id, v, true);\nconst verdict = governor.resolveAmendment(id);\nif (verdict.status !== 'approved') {\n  throw new Error(`Amendment ${id} resolved as ${verdict.status}; refusing to enact`);\n}\ngovernor.enactAmendment(id);","handlingStrategy":"try-catch","validationCode":"// Only 'proposed' amendments are visible via getPendingAmendments();\n// confirm the resolved verdict before enacting:\nif (governor.getPendingAmendments().some(a => a.id === amendmentId)) {\n  throw new Error(`Amendment ${amendmentId} not resolved yet; call resolveAmendment() first`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  governor.enactAmendment(amendmentId);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Cannot enact amendment with status')) {\n    // amendment stays in the map unchanged; if 'proposed', resolve first; if 'rejected', abandon or veto\n    const status = err.message.split(': ')[1];\n    if (status === 'proposed') {\n      const verdict = governor.resolveAmendment(amendmentId);\n      if (verdict.status === 'approved') governor.enactAmendment(amendmentId);\n    } else {\n      governor.vetoAmendment(amendmentId, 'enact-blocked');\n    }\n  } else {\n    throw err;\n  }\n}","preventionTips":["Always resolveAmendment() and assert returned status === 'approved' before enacting","Clear rejected amendments via vetoAmendment() so enacter loops cannot trip on them","Ensure enough voters and requiredApprovals are met before expecting 'approved'"],"tags":["guidance","meta-governance","amendment","enactment","state-machine","invalid-state-transition"],"backgroundTag":"invalid-state-transition","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}