mirror of
https://github.com/calesthio/OpenMontage.git
synced 2026-08-05 15:20:40 +08:00
backlot phase 0 review fixes: fail-closed gates, atomic checkpoint writes, event attribution hardening
- gate = manifest OR caller (stricter wins); unknown pipeline_type now raises instead of silently disabling enforcement; corrupt manifest logs and falls back; misleading diagnostic fixed - write_checkpoint backfills pipeline_type from project.json marker so omitting the kwarg can't bypass gates - checkpoint writes are atomic (temp + os.replace); history archiving is copy-based and best-effort (Windows open-file safe) - manifest loads cached (load_pipeline_readonly); stage gate lookup moved to pipeline_loader.get_stage_human_approval_default; PROJECTS_DIR unified in lib/paths.py - events: containment + root-normalization for explicit project dirs, no ghost-project mkdir, 0.0 cost preserved, nested-call depth tag, wrapper simplified - documentary-montage edit-director gate footer (was missed); AGENT_GUIDE no longer claims edit/compose always auto-proceed
This commit is contained in:
@@ -21,11 +21,31 @@ SCHEMA_PATH = (
|
||||
)
|
||||
|
||||
|
||||
from functools import lru_cache
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _load_manifest_schema() -> dict:
|
||||
with open(SCHEMA_PATH) as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def _load_pipeline_cached(name: str, defs_dir_key: str) -> dict[str, Any]:
|
||||
"""Cached manifest load. Treat the returned dict as READ-ONLY."""
|
||||
return load_pipeline(name, Path(defs_dir_key) if defs_dir_key else None)
|
||||
|
||||
|
||||
def load_pipeline_readonly(name: str, defs_dir: Optional[Path] = None) -> dict[str, Any]:
|
||||
"""Load a manifest through a cache. The result MUST NOT be mutated.
|
||||
|
||||
Manifests are immutable within a run; hot paths (gate checks on every
|
||||
checkpoint write, board state derivation) should use this instead of
|
||||
re-parsing YAML + re-validating the schema each call.
|
||||
"""
|
||||
return _load_pipeline_cached(name, str(defs_dir) if defs_dir else "")
|
||||
|
||||
|
||||
def load_pipeline(name: str, defs_dir: Optional[Path] = None) -> dict[str, Any]:
|
||||
"""Load and validate a pipeline manifest by name.
|
||||
|
||||
@@ -150,6 +170,18 @@ def get_stage_skill(manifest: dict, stage_name: str) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def get_stage_human_approval_default(manifest: dict, stage_name: str) -> Optional[bool]:
|
||||
"""Whether a stage gates on human approval. None if the stage isn't declared.
|
||||
|
||||
This is the single lookup used by gate enforcement (lib/checkpoint.py)
|
||||
and the Backlot board — keep them reading the same field the same way.
|
||||
"""
|
||||
for stage in manifest["stages"]:
|
||||
if stage["name"] == stage_name:
|
||||
return bool(stage.get("human_approval_default", False))
|
||||
return None
|
||||
|
||||
|
||||
def get_stage_review_focus(manifest: dict, stage_name: str) -> list[str]:
|
||||
"""Get the review focus items for a stage."""
|
||||
for stage in manifest["stages"]:
|
||||
|
||||
Reference in New Issue
Block a user