kin: auto-commit after pipeline

This commit is contained in:
Gros Frumos 2026-03-17 15:59:43 +02:00
parent 396f5193d3
commit 18160de45e
9 changed files with 449 additions and 0 deletions

View file

@ -1177,6 +1177,38 @@ def _execute_department_head_step(
}
# ---------------------------------------------------------------------------
# Watchdog helpers
# ---------------------------------------------------------------------------
import errno as _errno
def _check_parent_alive(
conn: sqlite3.Connection,
pipeline: dict,
task_id: str,
project_id: str,
) -> bool:
"""Check if parent process is alive. Returns True if pipeline should abort.
Only treats ESRCH (no such process) as dead parent.
PermissionError (pid 1 / init) and ValueError are ignored pipeline continues.
"""
ppid = os.getppid()
try:
os.kill(ppid, 0)
except OSError as exc:
if exc.errno == _errno.ESRCH:
reason = f"Parent process died unexpectedly (PID {ppid})"
_logger.warning("Pipeline %s: %s — aborting", pipeline["id"], reason)
models.update_pipeline(conn, pipeline["id"], status="failed")
models.update_task(conn, task_id, status="blocked", blocked_reason=reason)
return True
# PermissionError (EPERM) — process exists but we can't signal it: continue
return False
# ---------------------------------------------------------------------------
# Pipeline executor
# ---------------------------------------------------------------------------
@ -1234,6 +1266,9 @@ def run_pipeline(
pipeline = models.create_pipeline(
conn, task_id, project_id, route_type, steps,
)
# Save PID so watchdog can detect dead subprocesses (KIN-099)
models.update_pipeline(conn, pipeline["id"], pid=os.getpid())
pipeline["pid"] = os.getpid()
models.update_task(conn, task_id, status="in_progress")
results = []
@ -1248,6 +1283,19 @@ def run_pipeline(
model = step.get("model", "sonnet")
brief = step.get("brief")
# Check parent process is still alive (KIN-099 watchdog)
if not dry_run and pipeline:
if _check_parent_alive(conn, pipeline, task_id, project_id):
return {
"success": False,
"error": "parent_process_died",
"steps_completed": i,
"total_cost": total_cost,
"total_tokens": total_tokens,
"total_duration": total_duration,
"results": results,
}
# Worktree isolation: opt-in per project, for write-capable roles
_WORKTREE_ROLES = {"backend_dev", "frontend_dev", "debugger"}
worktree_path = None

View file

@ -332,3 +332,11 @@ routes:
dept_marketing:
steps: [marketing_head]
description: "Marketing task routed through department head"
dept_infra:
steps: [infra_head]
description: "Infrastructure task routed through department head"
dept_research:
steps: [research_head]
description: "Research task routed through department head"