"""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