"""Validate GAS orchestration recovery safeguards. Checks that the Apps Script sources contain the recovery fixes for: - stale run_all resume cleanup - fetch session label updates without budget reset - row-level core_satellite state saving """ from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] def _load(path: Path) -> str: return path.read_text(encoding="utf-8") def _must_contain(text: str, needle: str, label: str, errors: list[str]) -> None: if needle not in text: errors.append(label) def main() -> int: errors: list[str] = [] gas_lib = _load(ROOT / "gas_lib.gs") fetch = _load(ROOT / "src" / "gas_adapter_parts" / "gdc_01_fetch_fundamentals.gs") sat = _load(ROOT / "src" / "gas_adapter_parts" / "gdc_02_account_satellite.gs") _must_contain(gas_lib, "function clearRunAllState_()", "gas_lib:missing_clearRunAllState_", errors) _must_contain(gas_lib, "clearFetchCache();", "gas_lib:missing_clearFetchCache_call", errors) _must_contain(gas_lib, "invocation_mode=", "gas_lib:missing_invocation_mode", errors) _must_contain(fetch, "function setFetchSessionLabel_(", "fetch:missing_setFetchSessionLabel", errors) _must_contain(fetch, "fetch_session_updated_at", "fetch:missing_fetch_session_updated_at", errors) _must_contain(fetch, "isRunAllOrchestrated_()", "fetch:missing_orchestration_guard", errors) _must_contain(fetch, "beginFetchSession_(\"runDataFeed\")", "fetch:missing_runDataFeed_begin", errors) _must_contain(sat, "TIMEOUT_BUDGET_SEC", "sat:missing_timeout_budget", errors) _must_contain(sat, "cs_row_idx", "sat:missing_row_state_idx", errors) _must_contain(sat, "existingSheet.getDataRange().getValues()", "sat:missing_partial_row_restore", errors) _must_contain(sat, "setFetchSessionLabel_(\"runCoreSatelliteBatch\")", "sat:missing_session_label_update", errors) _must_contain(sat, "PARTIAL_SAVED", "sat:missing_partial_saved_status", errors) _must_contain(sat, "PARTIAL_SAVE_REQUESTED", "sat:missing_partial_save_signal", errors) _must_contain(sat, "return runCoreSatelliteBatch();", "sat:missing_resume_reentry", errors) if errors: print("GAS_ORCHESTRATION_RECOVERY_V1_FAIL") for item in errors: print(f" - {item}") return 1 print("GAS_ORCHESTRATION_RECOVERY_V1_OK") return 0 if __name__ == "__main__": raise SystemExit(main())