symfonic.services.effects.compensation¶
compensation ¶
Compensation and containment for already-dispatched irreversible effects.
Cancellation does not un-send a request. Once an irreversible effect has left the process, the honest sequence is: record the exposure, run the tested compensation procedure, rotate or revoke whatever credential the effect may have disclosed, and โ where no approved rule exists โ block cutover for that capability rather than describe the gap as handled.
That last clause is why registration is strict. A rule that was never tested is a plan, not a remedy, and a rule nobody named an approver for is not a decision anybody owns.
CompensationOutcome
dataclass
¶
CompensationOutcome(port_id: str, invocation_id: str, invoked: bool, credentials_rotated: bool, note: str, rule_id: str | None = None)
What containment actually did. invoked is never assumed true.
CompensationRegistry ¶
Which irreversible ports have an approved remedy, and which block cutover.
Source code in src/symfonic/services/effects/compensation.py
cutover_blocked_ports ¶
Irreversible ports among these that carry no approved rule.
Source code in src/symfonic/services/effects/compensation.py
CompensationRule
dataclass
¶
CompensationRule(rule_id: str, port_id: str, procedure: str, tested: bool, approved_by: str, rotates_credentials: bool = False)
One documented, tested procedure for undoing what can be undone.
ContainmentCoordinator ¶
ContainmentCoordinator(registry: CompensationRegistry, *, rotator: Callable[[str], None] | None = None)
Runs the approved remedy, or records precisely why it could not.