Replace the legacy CCD workspace with acquisition, direct-dq, original and lagged CCD contracts so the DRL-versus-constant-mean mechanism is reproducible and fail-closed. Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
4.9 KiB
Python
100 lines
4.9 KiB
Python
"""Immutable, fsync-backed acquisition artifact transactions."""
|
|
from __future__ import annotations
|
|
import ctypes
|
|
import errno
|
|
from hashlib import sha256
|
|
import json, os, shutil, uuid
|
|
from pathlib import Path
|
|
from typing import Mapping, Any
|
|
import numpy as np
|
|
from .contracts import ARTIFACT_SCHEMA_ID, canonical_json
|
|
from .validation import array_sha256, validate_acquisition_semantics
|
|
|
|
|
|
def file_sha256(path: Path) -> str:
|
|
digest = sha256()
|
|
with path.open("rb") as stream:
|
|
for block in iter(lambda: stream.read(8 * 1024 * 1024), b""):
|
|
digest.update(block)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def rename_noreplace(source: Path, destination: Path) -> None:
|
|
"""Atomically publish a directory without replacing any existing inode."""
|
|
libc = ctypes.CDLL(None, use_errno=True)
|
|
renameat2 = getattr(libc, "renameat2", None)
|
|
if renameat2 is None:
|
|
raise RuntimeError("atomic no-replace publication unavailable: libc renameat2 is missing")
|
|
renameat2.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint]
|
|
renameat2.restype = ctypes.c_int
|
|
result = renameat2(-100, os.fsencode(source), -100, os.fsencode(destination), 1)
|
|
if result == 0:
|
|
return
|
|
code = ctypes.get_errno()
|
|
if code in (errno.EEXIST, errno.ENOTEMPTY):
|
|
raise FileExistsError(destination)
|
|
if code in (errno.ENOSYS, errno.EINVAL, errno.ENOTSUP):
|
|
raise RuntimeError("atomic no-replace publication unavailable; refusing unsafe fallback") from OSError(code, os.strerror(code))
|
|
raise OSError(code, os.strerror(code), destination)
|
|
|
|
|
|
def _hash_array(value: np.ndarray) -> str:
|
|
return sha256(np.ascontiguousarray(value).tobytes()).hexdigest()
|
|
|
|
|
|
def _fsync_file(path: Path) -> None:
|
|
with path.open("rb") as stream:
|
|
os.fsync(stream.fileno())
|
|
|
|
|
|
def _validate_sha(value: np.ndarray, key: str) -> None:
|
|
if value.ndim != 0 or value.dtype.kind not in "SU": raise ValueError(f"{key} must be a scalar string")
|
|
text = str(value.item())
|
|
if len(text) != 64: raise ValueError(f"{key} must be SHA256")
|
|
int(text, 16)
|
|
|
|
|
|
class ArtifactTransaction:
|
|
def __init__(self, destination: str | Path):
|
|
self.destination = Path(destination)
|
|
self.partial = self.destination.with_name(f".{self.destination.name}.partial.{os.getpid()}.{uuid.uuid4().hex}")
|
|
self.active = False
|
|
|
|
def __enter__(self):
|
|
if self.destination.exists(): raise FileExistsError(self.destination)
|
|
self.destination.parent.mkdir(parents=True, exist_ok=True)
|
|
self.partial.mkdir(); self.active = True
|
|
return self
|
|
|
|
def write(self, *, arrays: Mapping[str, Any], config: dict, state: Mapping[str, Any]):
|
|
if not self.active: raise RuntimeError("transaction inactive")
|
|
data, state_arrays = validate_acquisition_semantics(arrays=arrays, config=config, state=state)
|
|
np.savez_compressed(self.partial/"fields.npz", **data)
|
|
(self.partial/"config.json").write_bytes(canonical_json(config))
|
|
np.savez_compressed(self.partial/"controller_state.npz", **state_arrays)
|
|
for path in self.partial.iterdir():
|
|
if path.is_file(): _fsync_file(path)
|
|
files = {path.name:file_sha256(path) for path in sorted(self.partial.iterdir()) if path.is_file()}
|
|
manifest = {"schema_id":ARTIFACT_SCHEMA_ID,"complete":True,"files":files,"state_array_sha256":{key:array_sha256(value) for key,value in state_arrays.items()},"config_sha256":sha256(canonical_json(config)).hexdigest(),"field_count":data["ux"].shape[0]}
|
|
validate_acquisition_semantics(arrays=data, config=config, state=state_arrays, manifest=manifest)
|
|
(self.partial/"manifest.json").write_bytes(canonical_json(manifest)); _fsync_file(self.partial/"manifest.json")
|
|
return manifest
|
|
|
|
def publish(self):
|
|
manifest = json.loads((self.partial/"manifest.json").read_text())
|
|
if not manifest.get("complete") or manifest.get("schema_id") != ARTIFACT_SCHEMA_ID: raise RuntimeError("partial artifact is not complete")
|
|
for name, digest in manifest["files"].items():
|
|
if file_sha256(self.partial/name) != digest: raise RuntimeError("artifact hash validation failed")
|
|
with np.load(self.partial/"fields.npz", allow_pickle=False) as fields, np.load(self.partial/"controller_state.npz", allow_pickle=False) as state:
|
|
config=json.loads((self.partial/"config.json").read_text())
|
|
validate_acquisition_semantics(arrays={key:fields[key] for key in fields.files},config=config,state={key:state[key] for key in state.files},manifest=manifest)
|
|
rename_noreplace(self.partial, self.destination)
|
|
directory_fd = os.open(self.destination.parent, os.O_RDONLY)
|
|
try: os.fsync(directory_fd)
|
|
finally: os.close(directory_fd)
|
|
self.active = False
|
|
return self.destination
|
|
|
|
def __exit__(self, typ, value, tb):
|
|
if self.active: shutil.rmtree(self.partial, ignore_errors=True); self.active=False
|