Specify prj- drivers and cross-owner wait mode.
COORDINATION-WP-0005: map project-repository workplans onto CoordinationCase, refuse residual as a driver, and document the wait mode without enabling live worker injection. Assistant: grok Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
parent
b69839b5ef
commit
d2802fc55d
9 changed files with 582 additions and 6 deletions
205
src/coordination_engine/project_driver.py
Normal file
205
src/coordination_engine/project_driver.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
"""Derive a project-driver CoordinationCase from hub-shaped JSON.
|
||||
|
||||
Does not author workplans, wake workers, or treat residual flavor as demand.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
RESIDUAL = "residual"
|
||||
PROJECT_CATEGORIES = frozenset({"project"})
|
||||
OPEN_PLANS = frozenset({"proposed", "ready", "active", "blocked", "backlog"})
|
||||
SATISFIED = frozenset({"done", "finished", "archived", "cancel"})
|
||||
|
||||
|
||||
def repo_is_project(repo: dict[str, Any] | None) -> bool:
|
||||
if not repo:
|
||||
return False
|
||||
slug = str(repo.get("slug") or "")
|
||||
category = str(
|
||||
repo.get("category")
|
||||
or (repo.get("repo_classification") or {}).get("category")
|
||||
or ""
|
||||
)
|
||||
flavor = str(repo.get("repo_flavor") or "")
|
||||
return slug.startswith("prj-") or category in PROJECT_CATEGORIES or flavor == "project"
|
||||
|
||||
|
||||
def workplan_is_residual(plan: dict[str, Any] | None) -> bool:
|
||||
if not plan:
|
||||
return False
|
||||
return str(plan.get("flavor") or "").strip().lower() == RESIDUAL
|
||||
|
||||
|
||||
def _index_repos(snapshot: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
repos = snapshot.get("repos") or {}
|
||||
if isinstance(repos, dict):
|
||||
# Hub adapter snapshot uses {repo_id: slug}. Tests may pass full rows.
|
||||
out: dict[str, dict[str, Any]] = {}
|
||||
for key, value in repos.items():
|
||||
if isinstance(value, dict):
|
||||
out[str(value.get("id") or key)] = value
|
||||
else:
|
||||
out[str(key)] = {"id": key, "slug": value}
|
||||
return out
|
||||
return {str(row.get("id")): row for row in repos if isinstance(row, dict) and row.get("id")}
|
||||
|
||||
|
||||
def _plans(snapshot: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
rows = snapshot.get("plans") or snapshot.get("workplans") or []
|
||||
return {str(row["id"]): row for row in rows if isinstance(row, dict) and row.get("id")}
|
||||
|
||||
|
||||
def _depends_targets(plan: dict[str, Any]) -> list[str]:
|
||||
raw = plan.get("depends_on")
|
||||
if raw is None:
|
||||
raw = plan.get("depends_on_workplans")
|
||||
if raw is None:
|
||||
return []
|
||||
if not isinstance(raw, list):
|
||||
raw = [raw]
|
||||
out: list[str] = []
|
||||
for item in raw:
|
||||
if isinstance(item, dict):
|
||||
target = item.get("workplan_id") or item.get("workstream_id") or item.get("id")
|
||||
else:
|
||||
target = item
|
||||
if target:
|
||||
out.append(str(target))
|
||||
return out
|
||||
|
||||
|
||||
def _related_ids(plan: dict[str, Any]) -> list[str]:
|
||||
raw = plan.get("related") or plan.get("related_workplans") or []
|
||||
if isinstance(raw, str):
|
||||
return [raw]
|
||||
return [str(item) for item in raw]
|
||||
|
||||
|
||||
def _canonical(plan: dict[str, Any]) -> str:
|
||||
return str(plan.get("record_id") or plan.get("slug") or plan.get("id") or "")
|
||||
|
||||
|
||||
def _match_plan(plans: dict[str, dict[str, Any]], token: str) -> dict[str, Any] | None:
|
||||
if token in plans:
|
||||
return plans[token]
|
||||
needle = token.lower().replace("_", "-")
|
||||
for plan in plans.values():
|
||||
slug = str(plan.get("slug") or "").lower().replace("_", "-")
|
||||
record = str(plan.get("record_id") or "").lower()
|
||||
if slug == needle or record == needle or slug.upper() == token.upper():
|
||||
return plan
|
||||
if str(plan.get("id")) == token:
|
||||
return plan
|
||||
return None
|
||||
|
||||
|
||||
class ResidualDriverError(ValueError):
|
||||
"""Raised when a residual workplan is asked to drive a case."""
|
||||
|
||||
|
||||
class NotProjectDriverError(ValueError):
|
||||
"""Raised when the selected workplan is not in a project repository."""
|
||||
|
||||
|
||||
def build_driver_case(
|
||||
snapshot: dict[str, Any],
|
||||
workplan_id: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a derived CoordinationCase from hub JSON.
|
||||
|
||||
Residual flavor is refused as a driver. Non-project workplans are refused
|
||||
as drivers (use cross_owner_wait instead).
|
||||
"""
|
||||
repos = _index_repos(snapshot)
|
||||
plans = _plans(snapshot)
|
||||
if workplan_id:
|
||||
plan = _match_plan(plans, workplan_id)
|
||||
if plan is None:
|
||||
raise KeyError(f"workplan not in snapshot: {workplan_id}")
|
||||
else:
|
||||
plan = next(
|
||||
(
|
||||
row
|
||||
for row in plans.values()
|
||||
if str(row.get("status") or "") in OPEN_PLANS
|
||||
and repo_is_project(repos.get(str(row.get("repo_id"))))
|
||||
and not workplan_is_residual(row)
|
||||
),
|
||||
None,
|
||||
)
|
||||
if plan is None:
|
||||
raise NotProjectDriverError("no open project-repository workplan in snapshot")
|
||||
|
||||
if workplan_is_residual(plan):
|
||||
raise ResidualDriverError("residual flavor is not a coordination driver")
|
||||
|
||||
repo = repos.get(str(plan.get("repo_id")))
|
||||
if not repo_is_project(repo):
|
||||
raise NotProjectDriverError("workplan repo is not a project repository")
|
||||
|
||||
repo_slug = str((repo or {}).get("slug") or "")
|
||||
driver_id = str(plan["id"])
|
||||
implementers = []
|
||||
seen_repos: set[str] = set()
|
||||
commitments = []
|
||||
for token in _depends_targets(plan):
|
||||
target = _match_plan(plans, token)
|
||||
commitments.append(
|
||||
{
|
||||
"kind": "completion",
|
||||
"obligor_workplan_id": str(target["id"]) if target else token,
|
||||
"beneficiary_workplan_id": driver_id,
|
||||
"state": (
|
||||
"satisfied"
|
||||
if target and str(target.get("status") or "") in SATISFIED
|
||||
else "active"
|
||||
),
|
||||
}
|
||||
)
|
||||
if target:
|
||||
t_repo = repos.get(str(target.get("repo_id")))
|
||||
slug = str((t_repo or {}).get("slug") or "")
|
||||
if slug and slug not in seen_repos and slug != repo_slug:
|
||||
seen_repos.add(slug)
|
||||
implementers.append(
|
||||
{
|
||||
"repo": slug,
|
||||
"workplan_id": str(target["id"]),
|
||||
"role": "implementer",
|
||||
}
|
||||
)
|
||||
for token in _related_ids(plan):
|
||||
target = _match_plan(plans, token)
|
||||
if not target:
|
||||
continue
|
||||
t_repo = repos.get(str(target.get("repo_id")))
|
||||
slug = str((t_repo or {}).get("slug") or "")
|
||||
if slug and slug not in seen_repos and slug != repo_slug:
|
||||
seen_repos.add(slug)
|
||||
implementers.append(
|
||||
{
|
||||
"repo": slug,
|
||||
"workplan_id": str(target["id"]),
|
||||
"role": "implementer",
|
||||
}
|
||||
)
|
||||
|
||||
flavor = str(plan.get("flavor") or "").strip().lower() or None
|
||||
return {
|
||||
"id": f"case:prj:{repo_slug}:{driver_id}",
|
||||
"scenario_type": "project_driver",
|
||||
"driver_workplan_id": driver_id,
|
||||
"driver_repo": repo_slug,
|
||||
"flavor": flavor,
|
||||
"goal": plan.get("title"),
|
||||
"implementers": implementers,
|
||||
"commitments": commitments,
|
||||
"actionable": True,
|
||||
}
|
||||
|
||||
|
||||
def actionable_demand(plan: dict[str, Any]) -> bool:
|
||||
"""False for residual flavor. Unspecified flavor is relevant."""
|
||||
return not workplan_is_residual(plan)
|
||||
Loading…
Add table
Add a link
Reference in a new issue