Add generic deterministic CaseBuilder with versioned input contracts

This commit is contained in:
Nico committed 2026-10-13 16:14:40 +02:00
1 parent e985e1d5f0
commit 4543e7cd31
18 files changed
+2093 -3

No files matched your search

@@ -234,7 +234,8 @@ class CheckpointStore:
def advance(self, case_id: str, package_id: str, target: str, *, step_id: str,
reason: str | None = None, retry_not_before: str | None = None,
details: dict | None = None, additions: dict[str, bytes] | None = None,
artifacts: dict[str, str | None] | None = None) -> Path:
artifacts: dict[str, str | None] | None = None,
expected_generation: int | None = None) -> Path:
try:
if not isinstance(step_id, str) or not step_id.strip():
raise LifecycleError("INVALID_STEP", "A nonblank stable step_id is required")
@@ -242,6 +243,8 @@ class CheckpointStore:
raise LifecycleError("CHECKPOINT_INVALID", "Checkpoint details must be a JSON object")
if CHECKPOINT_POINTER in (artifacts or {}):
raise LifecycleError("RESERVED_ARTIFACT", "Lifecycle checkpoint pointer is service-managed")
if expected_generation is not None and (isinstance(expected_generation, bool) or not isinstance(expected_generation, int) or expected_generation < 0):
raise LifecycleError("INVALID_GENERATION", "expected_generation must be a nonnegative integer")
payload = {"target": target, "reason": reason, "retry_not_before": retry_not_before,
"details": details or {}, "artifacts": artifacts or {},
"additions": {name: hashlib.sha256(data).hexdigest() for name, data in (additions or {}).items()}}
@@ -260,6 +263,8 @@ class CheckpointStore:
self._index(latest, manifest)
atomic_json(self.workspace.runtime / "current_job.json", None)
return latest
if expected_generation is not None and manifest["package_generation"] != expected_generation:
raise LifecycleError("GENERATION_CHANGED", "Case changed while artifacts were being prepared")
resume = chain[0][1]["resume_state"] if manifest["state"] == State.BLOCKED else None
check_transition(manifest["state"], target, resume)
if manifest["state"] == State.BLOCKED and not self.retry_due(manifest):