Files
state-hub/api/services/execution_queue.py
tegwick 0949d4c0d8 feat(classification-spine): implement STATE-WP-0065 repo-anchored model
Replace the ad-hoc coordination-domain spine with the Repo Classification
Standard: 14 market domains, classification columns on managed_repos, and
workplans anchored by repo_id (topic_id optional).

- Add Alembic migration d8e9f0a1b2c3 with data backfill and workstream→workplan rename
- Add api/classification.py validation and register-from-classification tooling
- Expose workplan-first REST/MCP surface with legacy workstream aliases
- Add C-24 consistency rule and legacy domain frontmatter mapping
- Update dashboard repos page with category/capability/stake filters
- Update orientation docs; mark STATE-WP-0065 finished
2026-06-22 13:52:13 +02:00

103 lines
3.6 KiB
Python

from __future__ import annotations
from typing import Any
from api.workplan_status import normalize_workplan_status
EXECUTION_STATES = {
"manual": "Not queued for autonomous pickup; humans or agents may still work manually.",
"queued": "Candidate for ordered pickup when dependencies and concurrency allow it.",
"scheduled": "Waiting for an external launch window; State Hub stores the requested time.",
"launching": "A launch request asks for immediate pickup or has been handed off.",
"paused": "Temporarily held outside the pickup stack.",
"completed": "Execution intent is closed; lifecycle status remains authoritative.",
"cancelled": "Execution intent was cancelled without changing lifecycle status.",
}
LAUNCH_MODES = {
"manual": "Do not request automation; keep intent visible only.",
"queued": "Place in the prioritized stack for later pickup.",
"scheduled": "Request pickup at or after a selected time.",
"immediate": "Request prompt activity-core or agent pickup.",
}
CONCURRENCY_MODES = {
"sequential": "Respect queue order and avoid parallel pickup for the same group.",
"parallel": "Eligible for concurrent pickup with other ready work.",
}
STATE_HUB_RESPONSIBILITIES = [
"store lifecycle status separately from execution intent",
"rank candidate workplans and expose dependency-aware eligibility",
"record launch requests and handoff metadata durably",
"surface manual, queued, scheduled, and immediate intent to operators",
]
ACTIVITY_CORE_RESPONSIBILITIES = [
"own schedules, wakeups, and recurring automation",
"dispatch coding agents and coordinate parallel execution",
"acknowledge, run, and complete launch requests when available",
]
EXECUTION_STATE_RANK = {
"launching": 0,
"queued": 1,
"scheduled": 2,
"manual": 3,
"paused": 4,
"completed": 5,
"cancelled": 6,
}
PRIORITY_RANK = {
"critical": 0,
"high": 1,
"medium": 2,
"low": 3,
}
CLOSED_WORKPLAN_STATUSES = {"finished", "archived"}
def execution_state_for_launch(launch_mode: str, immediate_pickup: bool = False) -> str:
mode = (launch_mode or "queued").strip().lower()
if immediate_pickup or mode == "immediate":
return "launching"
if mode == "scheduled":
return "scheduled"
if mode == "manual":
return "manual"
return "queued"
def workplan_blockers(
workplan_id: Any,
dependency_targets: dict[Any, list[Any]],
workplan_status: dict[Any, str],
workstream_id: Any = None,
) -> list[Any]:
scope_id = workplan_id if workplan_id is not None else workstream_id
blockers = []
for target_id in dependency_targets.get(scope_id, []):
target_status = normalize_workplan_status(workplan_status.get(target_id))
if target_status not in CLOSED_WORKPLAN_STATUSES:
blockers.append(target_id)
return blockers
workstream_blockers = workplan_blockers
def queue_sort_key(workstream: Any, *, eligible: bool) -> list[int | str]:
priority = str(getattr(workstream, "planning_priority", "") or "").strip().lower()
execution_state = str(getattr(workstream, "execution_state", "") or "manual").strip().lower()
return [
0 if eligible else 1,
EXECUTION_STATE_RANK.get(execution_state, 99),
PRIORITY_RANK.get(priority, 50),
getattr(workstream, "queue_rank", None) if getattr(workstream, "queue_rank", None) is not None else 999_999,
getattr(workstream, "planning_order", None) if getattr(workstream, "planning_order", None) is not None else 999_999,
str(getattr(workstream, "slug", "") or ""),
]