Files
DynamisLab/src/CCD_analysis/karman_dynamic/runtime.py
T
Frank14fandCursor b144d62920 feat(ccd): freeze dynamic-increment analysis pipeline
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>
2026-08-07 16:41:00 +08:00

47 lines
4.0 KiB
Python

"""Campaign role adapter; solver/PPO imports remain lazy until guarded execution."""
from pathlib import Path
import tempfile,shutil
import numpy as np
from CCD_analysis.acquisition.artifacts import file_sha256
from CCD_analysis.acquisition.runtime import build_role_runtime,initialize_role,run_role_acquisition
from .artifacts import load_role_artifact,publish_wrapper
from .phase import load_phase_compact
from .contracts import CASE_ID,LEGACY_ROLE,constant_mean_provenance,DEFAULT_REPO_MAPPING,DEFAULT_OPTANE_ROOT,EXPECTED_OPTANE_MOUNT,LEASE_PATH,COOLDOWN_PATH
from .safety import require_execution_environment,validate_optane_storage,exclusive_lease,enforce_cooldown
class FixedPolicy:
def __init__(self,normalized): self.normalized=np.asarray(normalized,np.float32); self.device="cpu"
def predict(self,obs,deterministic=True): return self.normalized.copy(),None
def execute_role(*,role,output,campaign_id,warmup_intervals,collect_boundaries,drl_artifact=None,phase_artifact=None,launch_delay_seconds=120,smoke=False):
require_execution_environment(); output=Path(output)
backing=validate_optane_storage(repo_mapping=DEFAULT_REPO_MAPPING,optane_root=DEFAULT_OPTANE_ROOT,mount=EXPECTED_OPTANE_MOUNT)
if backing!=output.parent.resolve(strict=False) and backing not in output.parent.resolve(strict=False).parents: raise ValueError("role output must be below validated Optane campaign root")
if output.exists(): raise FileExistsError(output)
if smoke and (warmup_intervals,collect_boundaries)!=(1,2): raise ValueError("smoke requires exactly --warmup-intervals 1 --collect-boundaries 2")
relative_parent=output.parent.resolve(strict=False).relative_to(backing)
current=backing
for component in relative_parent.parts:
current=current/component
if current.is_symlink(): raise ValueError("campaign output parents must not be symlinks")
current.mkdir(exist_ok=True)
if output.parent.resolve(strict=True).relative_to(backing)!=relative_parent: raise ValueError("campaign output parent escaped validated Optane root")
if output.exists(): raise FileExistsError(output)
total=warmup_intervals+collect_boundaries; staging=Path(tempfile.mkdtemp(prefix=f".{role}.campaign-",dir=output.parent)); payload=staging/"payload"; provenance=None
try:
with exclusive_lease(LEASE_PATH,campaign_id=campaign_id,role=role):
enforce_cooldown(COOLDOWN_PATH,delay_seconds=launch_delay_seconds)
legacy=LEGACY_ROLE[role]; runtime=None; initializer=initialize_role
if role=="constant_mean":
if drl_artifact is None or phase_artifact is None: raise ValueError("constant_mean requires --drl-artifact and --phase-artifact")
gate=load_phase_compact(phase_artifact)
if not gate["summary"]["gate_passed"]: raise ValueError("constant_mean blocked by failed phase/stationarity gate")
drl=load_role_artifact(drl_artifact,expected_role="drl"); start,_=drl["metadata"]["retained_slice"]; provenance=constant_mean_provenance(drl_manifest_sha256=file_sha256(Path(drl_artifact)/"campaign_manifest.json"),effective=drl["telemetry"]["effective_applied_action"],retained_start=start)
if provenance != gate["summary"]["constant_mean_provenance"]: raise ValueError("phase gate constant_mean provenance mismatch")
mean=np.asarray(provenance["constant_mean_physical_action"],np.float32); normalized=((mean/.01)-np.asarray([0.,-4.,4.],np.float32))/8
if np.any(normalized < -1) or np.any(normalized > 1): raise ValueError("fresh DRL mean cannot be represented by frozen policy action bounds")
runtime=build_role_runtime(case=CASE_ID,role="q_ctl")
def initializer(current,**kwargs): initialize_role(current,**kwargs); current.policy=FixedPolicy(normalized)
run_role_acquisition(case=CASE_ID,role=legacy,output=payload,control_count=total,field_interval=800,runtime=runtime,initializer=initializer)
return publish_wrapper(staging,output,role=role,campaign_id=campaign_id,warmup_intervals=warmup_intervals,collect_boundaries=collect_boundaries,constant_mean_provenance=provenance,smoke=smoke)
except Exception:
shutil.rmtree(staging,ignore_errors=True); raise