atlas-iac/services/hermes/scripts/cli_lane_board.py

83 lines
3.0 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Bounded Kanban access and workspace resolution for Hermes CLI workers."""
from __future__ import annotations
import json
import sqlite3
import sys
import time
from pathlib import Path
from typing import Any, Callable
from cli_lane_config import (
BOARD_CORRUPTION_ERRORS,
EXTERNAL_PREFIX,
KANBAN_STORAGE_ATTEMPTS,
WORKTREE_LOCK,
)
def _task_value(task: Any, name: str, default: Any = None) -> Any:
return getattr(task, name, default)
def _task_context(kanban_db: Any, conn: Any, task_id: str) -> str:
value = kanban_db.build_worker_context(conn, task_id)
if isinstance(value, str):
return value
return json.dumps(value, indent=2, default=str)
def _resolve_workspace(kanban_db: Any, conn: Any, task: Any, board: str) -> Path:
# External coding lanes always receive their own linked worktree. A task
# that does not resolve to a Git repository is blocked rather than sharing
# a mutable checkout with another unattended worker.
# Git worktree creation updates common-repository metadata. Serialize that
# short materialization step while allowing the provider workers themselves
# to run concurrently in independent worktrees.
with WORKTREE_LOCK:
value, branch_name = kanban_db._resolve_worktree_workspace(task, board=board)
kanban_db.set_branch_name(conn, str(_task_value(task, "id")), branch_name)
return Path(value).resolve()
def _record_board_access_error(board: str, error: Exception) -> None:
"""Report one board access failure once while allowing other lanes to run."""
failure_kind = "storage" if isinstance(error, (OSError, sqlite3.Error)) else "access"
detail = f"{failure_kind} {type(error).__name__}: {error}"
if BOARD_CORRUPTION_ERRORS.get(board) == detail:
return
print(
f"temporarily skipping Kanban board {board!r}: {detail}",
file=sys.stderr,
flush=True,
)
BOARD_CORRUPTION_ERRORS[board] = detail
def _board_call(
kanban_db: Any,
board: str,
operation: Callable[[Any], Any],
) -> Any:
"""Run one bounded Kanban operation on a fresh, promptly closed connection."""
last_storage_error: Exception | None = None
for attempt in range(KANBAN_STORAGE_ATTEMPTS):
conn = None
try:
with kanban_db.scoped_current_board(board):
conn = kanban_db.connect(board=board)
result = operation(conn)
BOARD_CORRUPTION_ERRORS.pop(board, None)
return result
except (OSError, sqlite3.Error) as error:
last_storage_error = error
_record_board_access_error(board, error)
if attempt + 1 < KANBAN_STORAGE_ATTEMPTS:
time.sleep(0.2 * (attempt + 1))
finally:
if conn is not None:
conn.close()
assert last_storage_error is not None
raise last_storage_error
def _external(task: Any) -> bool:
return str(_task_value(task, "assignee", "") or "").startswith(EXTERNAL_PREFIX)