Files
Open_Ultra_SAST/tests/test_plane_ax_live.py
T
Marius CiepluchandClaude Fable 5.1 4866665e5e feat(plane): ax-backed reconciler, status attribution and doctor (task 4)
Requirements 3.1, 3.2, 3.3, 3.4, 3.5, 2.2, 7.1, 7.3 of ai-service-plane.

- plane/reconciler.py (492 lines, test-counted): a Run's tasks submitted to ax
  in dependency order through the injectable `ax` CLI (apply / get task /
  delete task), serialize labels never overlapping, --workers, state.json with
  pending|running|suspended|done|failed|unfinished, a PID lock per run, done
  tasks skipped on rerun, unfinished tasks holding their dependants, a failed
  task halting the run. Budgets, inputs, output dir, artifact URL and the bound
  Model (annotation openultrasast.io/model; identity and priced parameters) go
  to the task as env in the rendered Task; consumed artifacts travel as files
  of a generated Workspace bound under /workspace. A threaded HTTP receiver
  takes each task's tar delivery, validating run/task and rejecting traversal.
  Outcome comes from the delivered summary.json (done only when every unit
  finished), never from ax's phase alone. status prints the token attribution
  table per task and Model from the summaries' usage fields (n/a, never an
  estimate) and writes attribution.json; doctor runs the four host checks.
- plane/workspaces.py: one Workspace manifest per case pin of a population.
- CLI: ousast plane run|status|doctor|workspaces.
- tests/test_plane_reconciler.py: a fake ax executable drives the DAG;
  tests/test_plane_ax_live.py (marker ax) applies ops/ax/smoke-task.yaml on a
  live cluster and skips unless doctor passes.
- Module audit rows and regenerated manifest.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
2026-09-29 18:10:09 +02:00

44 lines
1.4 KiB
Python

"""ai-service-plane Req 3.5/3.6: one trivial Task end to end on the real ax cluster of ``ops/ax/up.sh``.
Marker ``ax``; skipped unless every check of ``ousast plane doctor`` passes, so the host suite never depends on
the cluster. Run it on purpose: ``pytest -m ax tests/test_plane_ax_live.py``.
"""
from __future__ import annotations
import time
from pathlib import Path
import pytest
from openultrasast.plane.reconciler import Ax, doctor
pytestmark = pytest.mark.ax
SMOKE = Path("ops/ax/smoke-task.yaml")
@pytest.fixture(scope="module")
def live_ax() -> Ax:
checks = doctor()
failing = [f"{name}: {text}" for name, ok, text in checks if not ok]
if failing:
pytest.skip("no live ax cluster: " + "; ".join(failing))
if not SMOKE.is_file():
pytest.skip(f"{SMOKE} is not in this checkout")
return Ax()
def test_smoke_task_completes_on_the_cluster(live_ax: Ax) -> None:
live_ax.apply(SMOKE)
deadline = time.monotonic() + 600
phase = "Pending"
try:
while time.monotonic() < deadline:
phase = live_ax.phase("ousast-smoke")
if any(word in phase.lower() for word in ("completed", "succeeded", "failed", "error")):
break
time.sleep(5)
finally:
live_ax.delete("ousast-smoke")
assert phase.lower() in ("completed", "succeeded"), f"smoke task ended in phase {phase!r}"