NK-WP-0056-T12 done: pilot operations; synthetic rotation and revert pass
operations.py: OpenBao KV v2 CAS, LLDAP self-rotation (forward and revert), ExternalSecret force-sync and status, rollout restart (RolloutFailed is a definite failure), pilot checks. synthetic_pilot.py on the T08 stack plus OpenBao 2.5.4 with CCR-2026-0058's policy: rotation completed; a one-off wrong delivery reverted in declared order with the earlier value restored. Runner: revert steps run in declared order (BEA-10), definite failures revert, unknown outcomes stop, receipts carry error types. Found and corrected (in railiance-platform) CCR-2026-0058's required_parameters refusing reads. Packaging moved to new T13. 29 executor tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 994874@bnt-lap001 Assistant-Session: 6017c416-d619-47eb-bd1a-f670a02d7d87
This commit is contained in:
parent
9b7701a32a
commit
db56d8468e
11 changed files with 1088 additions and 33 deletions
|
|
@ -12,9 +12,10 @@ Runs one Bounded Execution Admission (canon/standards/bounded-execution-admissio
|
|||
started `execution.run.started` to the audit sink; no custody, no run
|
||||
steps in plan order, one consumer at a time; checks after each mutating step, after
|
||||
their settle time; the kill switch and the budget are checked between steps
|
||||
failure a failed check or an exhausted budget reverts the mutated steps in reverse
|
||||
order (or stops, as the admission says) and re-runs their checks; an error of
|
||||
unknown outcome stops without guessing; a failed revert stops and alerts
|
||||
failure a failed check, an exhausted budget or the kill switch runs the revert steps of
|
||||
the mutated steps in the admission's declared order (or stops, as it says), each
|
||||
proving the restored state with its own checks; an error of unknown outcome stops
|
||||
without guessing; a failed revert stops and alerts
|
||||
receipt one `execution.attribution.receipt` per run (PDP `not-evaluated` in the pilot)
|
||||
|
||||
Every effect goes through an injected interface: Carrier, operation and check callables, TrustOracle,
|
||||
|
|
@ -52,6 +53,12 @@ class Unknown(Exception):
|
|||
"""An operation's outcome is unknown: stop, do not guess, do not revert blindly."""
|
||||
|
||||
|
||||
class StepFailed(Exception):
|
||||
"""A definite, known-bad outcome (e.g. a rollout that never became ready): handled like a failed check.
|
||||
Operations outside this module may instead set `definite_failure = True` on their exception class."""
|
||||
definite_failure = True
|
||||
|
||||
|
||||
class Carrier(Protocol):
|
||||
def get(self, approval_id: str) -> dict: ...
|
||||
def consume(self, approval_id: str, request_digest: str, decision_id: str) -> dict: ...
|
||||
|
|
@ -203,9 +210,13 @@ class Run:
|
|||
def _budget_left(self) -> float:
|
||||
return self.spec["budget"]["wall_time_seconds"] - (self.d.monotonic() - self.start_mono)
|
||||
|
||||
def _record(self, step: dict, kind: str, outcome: str, checks: list[dict] | None = None) -> None:
|
||||
self.steps.append({"id": step["id"], "kind": kind, "operation": step["operation"], "target": step["target"],
|
||||
"consumer": step["consumer"], "outcome": outcome, "checks": checks or []})
|
||||
def _record(self, step: dict, kind: str, outcome: str, checks: list[dict] | None = None,
|
||||
error: BaseException | None = None) -> None:
|
||||
entry = {"id": step["id"], "kind": kind, "operation": step["operation"], "target": step["target"],
|
||||
"consumer": step["consumer"], "outcome": outcome, "checks": checks or []}
|
||||
if error is not None:
|
||||
entry["error_type"] = type(error).__name__ # the type only: messages can carry values
|
||||
self.steps.append(entry)
|
||||
|
||||
def _run_checks(self, ids: list[str], ctx: Context) -> tuple[bool, list[dict]]:
|
||||
results = []
|
||||
|
|
@ -230,18 +241,20 @@ class Run:
|
|||
return True, results
|
||||
|
||||
def _revert(self, done: list[dict], ctx: Context) -> bool:
|
||||
"""Undo the mutated steps in reverse order; each revert step must pass its own checks."""
|
||||
reverts = {s["id"]: s for s in self.spec["plan"].get("revert_steps", [])}
|
||||
for step in reversed(done):
|
||||
if not step["mutates"]:
|
||||
"""Undo the mutated steps. Revert steps run in the order the admission declares them (the author knows
|
||||
the recovery order: data before delivery before restart), limited to those whose forward step ran.
|
||||
Each must pass its own checks; an irreversible step that ran makes the revert fail."""
|
||||
mutated = [s for s in done if s["mutates"]]
|
||||
if any(s.get("irreversible") for s in mutated):
|
||||
return False
|
||||
wanted = {s["revert"] for s in mutated}
|
||||
for revert in self.spec["plan"].get("revert_steps", []):
|
||||
if revert["id"] not in wanted:
|
||||
continue
|
||||
if step.get("irreversible"):
|
||||
return False
|
||||
revert = reverts[step["revert"]]
|
||||
try:
|
||||
self.d.operations[revert["operation"]](revert, ctx)
|
||||
except Exception:
|
||||
self._record(revert, "revert", "error")
|
||||
except Exception as error:
|
||||
self._record(revert, "revert", "error", error=error)
|
||||
return False
|
||||
ok, results = self._run_checks(revert.get("checks_after", []), ctx)
|
||||
self._record(revert, "revert", "done" if ok else "check_failed", results)
|
||||
|
|
@ -262,11 +275,15 @@ class Run:
|
|||
return reason
|
||||
try:
|
||||
self.d.operations[step["operation"]](step, ctx)
|
||||
except Unknown:
|
||||
self._record(step, "step", "unknown")
|
||||
return "unknown"
|
||||
except Exception:
|
||||
self._record(step, "step", "unknown")
|
||||
except Exception as error:
|
||||
if getattr(error, "definite_failure", False) and step["mutates"]:
|
||||
# known-bad state: treat like a failed check, the step counts as done for the revert
|
||||
done.append(step)
|
||||
self._record(step, "step", "failed", error=error)
|
||||
if policy["on_check_failure"] == "revert":
|
||||
return "reverted" if self._revert(done, ctx) else "revert_failed"
|
||||
return "stopped"
|
||||
self._record(step, "step", "unknown", error=error) # outcome not established: stop
|
||||
return "unknown"
|
||||
done.append(step)
|
||||
ok, results = self._run_checks(step.get("checks_after", []), ctx) if step["mutates"] else (True, [])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue