Validate training completion handoff
This commit is contained in:
@@ -33,5 +33,17 @@ def test_container_running_fails_closed_on_inspect_error(monkeypatch) -> None:
|
||||
def test_completion_command_requires_non_empty_string_list(tmp_path: Path) -> None:
|
||||
invalid = tmp_path / "command.json"
|
||||
invalid.write_text('{"shell": "unsafe"}', encoding="utf-8")
|
||||
value = MODULE.json.loads(invalid.read_text(encoding="utf-8"))
|
||||
assert not isinstance(value, list)
|
||||
try:
|
||||
MODULE.load_completion_command(invalid)
|
||||
except ValueError as exc:
|
||||
assert "non-empty JSON list" in str(exc)
|
||||
else:
|
||||
raise AssertionError("mapping-shaped completion command was accepted")
|
||||
|
||||
|
||||
def test_completion_command_preserves_exact_argv_without_shell_parsing(tmp_path: Path) -> None:
|
||||
command = tmp_path / "command.json"
|
||||
command.write_text('["docker", "exec", "-d", "geointel", "python", "loop.py"]', encoding="utf-8")
|
||||
assert MODULE.load_completion_command(command) == [
|
||||
"docker", "exec", "-d", "geointel", "python", "loop.py"
|
||||
]
|
||||
|
||||
@@ -11799,6 +11799,11 @@ Deployment evidence:
|
||||
`results.png` appears. The bound command permits up to 20 iterations, keeps
|
||||
protected test/background closed until calibration passes, and preserves
|
||||
the exact AdamW, 180-degree rotation, flip, scale and translation contract.
|
||||
- Extracted and tested strict completion-command validation. Only a non-empty
|
||||
JSON list of non-empty argv strings is accepted; mappings, empty arguments,
|
||||
malformed JSON and filesystem errors produce an explicit fail-closed
|
||||
supervisor state before any subprocess is started. The updated supervisor
|
||||
was activated live with one remaining v37 GPU process.
|
||||
- Added those aerial augmentation parameters to the orchestrator CLI and
|
||||
training command, preventing later failure-driven checkpoints from silently
|
||||
reverting to generic orientation assumptions.
|
||||
|
||||
@@ -37,6 +37,13 @@ def write_state(path: Path, payload: dict) -> None:
|
||||
temporary.replace(path)
|
||||
|
||||
|
||||
def load_completion_command(path: Path) -> list[str]:
|
||||
command = json.loads(path.read_text(encoding="utf-8"))
|
||||
if not isinstance(command, list) or not command or not all(isinstance(x, str) and x for x in command):
|
||||
raise ValueError("completion command must be a non-empty JSON list of non-empty strings")
|
||||
return command
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--container", required=True)
|
||||
@@ -64,9 +71,11 @@ def main() -> int:
|
||||
write_state(state_path, state)
|
||||
return 0
|
||||
if args.completion_command_json:
|
||||
command = json.loads(args.completion_command_json.read_text(encoding="utf-8"))
|
||||
if not isinstance(command, list) or not command or not all(isinstance(x, str) and x for x in command):
|
||||
try:
|
||||
command = load_completion_command(args.completion_command_json)
|
||||
except (OSError, json.JSONDecodeError, ValueError) as exc:
|
||||
state["status"] = "invalid_completion_command"
|
||||
state["completion_handoff_error"] = str(exc)
|
||||
write_state(state_path, state)
|
||||
return 4
|
||||
result = subprocess.run(command, check=False)
|
||||
|
||||
Reference in New Issue
Block a user