"""One read-only lifecycle view for web, MCP and exported reports.""" import json from pathlib import Path import sqlite3 from artemis.config import DB_PATH from artemis.runtime.task_control import list_instructions, read_state from artemis.runtime.task_progress import progress_snapshot def _records(session_id: str, path: Path): if not path.is_file(): return None, None, [] with sqlite3.connect(path.resolve().as_uri() + "?mode=ro", uri=True) as conn: conn.row_factory = sqlite3.Row tables = { row[0] for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'") } session = ( conn.execute("SELECT * FROM sessions WHERE session_id=?", (session_id,)).fetchone() if "sessions" in tables else None ) record = ( conn.execute( "SELECT payload FROM automation_jobs WHERE session_id=?", (session_id,) ).fetchone() if "automation_jobs" in tables else None ) rows = ( conn.execute( "SELECT payload FROM traces WHERE session_id=? AND type='final_review_diagnostic'", (session_id,), ).fetchall() if "traces" in tables else [] ) return session, record, rows def task_lifecycle(session_id: str, *, db_path=None, now=None) -> dict: session, record, rows = _records(session_id, Path(db_path) if db_path else DB_PATH) try: job = json.loads(record[0]) if record else {} except (ValueError, TypeError): job = {} reviews = [] for row in rows: try: value = json.loads(row[0]) except (ValueError, TypeError): continue if isinstance(value, dict): reviews.append(value) session = dict(session) if session else {} control = read_state(session_id) if session.get("pid") and control.get("pid") != session["pid"]: control = {} value = progress_snapshot( job=job if isinstance(job, dict) else {}, session=session, control=control, reviews=reviews, now=now, ) instructions = list_instructions(session_id, task_status=value["status"]) value["guidance_counts"] = { status: sum(item["status"] == status for item in instructions) for status in ("pending", "reading", "consumed", "unapplied", "unconfirmed") } return value def guidance_details(session_id: str, *, db_path=None) -> dict: lifecycle = task_lifecycle(session_id, db_path=db_path) return { "session_id": session_id, "lifecycle": lifecycle, "instructions": list_instructions(session_id, task_status=lifecycle["status"]), }