Adapt the coordination graph to flavor and indexed depends_on.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Omit flavor=residual by default, draw hub depends_on first, and keep
citation waits_on as a tagged fallback. Explorer checkbox and CLI
--include-residuals match State Hub include_residuals.

Assistant: grok
Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
codex 2026-09-14 16:20:24 +02:00
parent 0b0abedbe8
commit 725e440b1e
8 changed files with 410 additions and 39 deletions

View file

@ -15,22 +15,76 @@ from urllib.parse import urlencode
OPEN_WORKPLAN = frozenset({"proposed", "ready", "active", "blocked", "backlog"})
OPEN_TASK = frozenset({"todo", "progress", "wait"})
RESIDUAL_FLAVOR = "residual"
WP_ID_RE = re.compile(r"\b([A-Z]{2,12}(?:-[A-Z]+)?-WP-[A-Z0-9-]+)\b")
FLAVOR_COLORS = {
"planning": "#6366f1",
"implementation": "#1d4ed8",
"refactoring": "#b45309",
"extension": "#0f766e",
"residual": "#64748b",
}
def is_residual_flavor(value: Any) -> bool:
"""True only for the closed flavor token. Titles are not consulted."""
return str(value or "").strip().lower() == RESIDUAL_FLAVOR
def _flavor(record: dict[str, Any]) -> str | None:
text = str(record.get("flavor") or "").strip().lower()
return text or None
def _indexed_depends_targets(workplan: dict[str, Any]) -> list[str]:
"""Hub-indexed workplan ids from depends_on stubs or id lists."""
raw = workplan.get("depends_on")
if raw is None:
raw = workplan.get("depends_on_workplans")
if raw is None:
return []
if not isinstance(raw, list):
raw = [raw]
targets: list[str] = []
seen: set[str] = set()
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 not target:
continue
key = str(target)
if key in seen:
continue
seen.add(key)
targets.append(key)
return targets
def coordination_graph_payload(
workplans: list[dict[str, Any]],
tasks: list[dict[str, Any]],
*,
include_residuals: bool = False,
) -> dict[str, Any]:
"""Build a GraphExplorerPayload of open workplans and their open tasks."""
open_wps = [wp for wp in workplans if str(wp.get("status") or "") in OPEN_WORKPLAN]
open_all = [wp for wp in workplans if str(wp.get("status") or "") in OPEN_WORKPLAN]
residual_open = sum(1 for wp in open_all if is_residual_flavor(_flavor(wp)))
if include_residuals:
open_wps = open_all
else:
open_wps = [wp for wp in open_all if not is_residual_flavor(_flavor(wp))]
wp_by_id = {str(wp.get("id")): wp for wp in open_wps if wp.get("id")}
slug_to_id = {
str(wp.get("slug") or "").lower(): str(wp["id"])
for wp in open_wps
if wp.get("slug") and wp.get("id")
}
# also index record ids like CUST-WP-0071 from title/slug
record_to_id: dict[str, str] = {}
for wp in open_wps:
slug = str(wp.get("slug") or "")
@ -45,12 +99,14 @@ def coordination_graph_payload(
for task in tasks
if str(task.get("status") or "") in OPEN_TASK
and str(task.get("workplan_id") or task.get("workstream_id") or "") in wp_by_id
and (include_residuals or not is_residual_flavor(_flavor(task)))
]
elements: list[dict[str, Any]] = []
for wp in open_wps:
wp_id = str(wp["id"])
status = str(wp.get("status") or "unknown")
flavor = _flavor(wp)
elements.append(
{
"data": {
@ -62,10 +118,13 @@ def coordination_graph_payload(
"name": wp.get("title"),
"lifecycle": status,
"status": status,
"flavor": flavor,
"nodeClass": flavor or "unspecified",
"repo": wp.get("repo") or wp.get("slug"),
"displayState": "show" if status != "blocked" else "highlight",
"unresolved": status in {"blocked", "proposed"},
"chokepoint": 0,
"color": FLAVOR_COLORS.get(flavor or "", "#1d4ed8"),
}
}
)
@ -77,6 +136,7 @@ def coordination_graph_payload(
wp_id = str(task.get("workplan_id") or task.get("workstream_id"))
status = str(task.get("status") or "todo")
human = bool(task.get("needs_human"))
flavor = _flavor(task) or _flavor(wp_by_id.get(wp_id) or {})
elements.append(
{
"data": {
@ -88,6 +148,8 @@ def coordination_graph_payload(
"name": task.get("title"),
"lifecycle": status,
"status": status,
"flavor": flavor,
"nodeClass": flavor or "unspecified",
"needsHuman": human,
"displayState": "highlight" if status == "wait" or human else "show",
"unresolved": status == "wait" or human,
@ -105,6 +167,7 @@ def coordination_graph_payload(
"source": f"task:{task_id}",
"target": f"workplan:{wp_id}",
"edgeType": "belongs_to",
"edgeSource": "membership",
"strength": "weak",
"sourceLayer": "task",
"targetLayer": "workplan",
@ -115,7 +178,46 @@ def coordination_graph_payload(
if status == "wait" or human:
wait_edges += 1
# Cross-workplan citations in wait/blocking text.
indexed_pairs: set[tuple[str, str]] = set()
depends_count = 0
chokepoint: dict[str, int] = {str(wp["id"]): 0 for wp in open_wps}
for wp in open_wps:
src = str(wp["id"])
for dst_raw in _indexed_depends_targets(wp):
dst = (
dst_raw
if dst_raw in wp_by_id
else record_to_id.get(dst_raw.upper())
or slug_to_id.get(dst_raw.lower())
)
if not dst or dst == src or dst not in wp_by_id:
continue
pair = (src, dst)
if pair in indexed_pairs:
continue
indexed_pairs.add(pair)
elements.append(
{
"data": {
"id": f"edge:depends:{src}:{dst}",
"stableKey": f"edge:depends:{src}:{dst}",
"kind": "Edge",
"layer": "dependency",
"displayState": "show",
"source": f"workplan:{src}",
"target": f"workplan:{dst}",
"edgeType": "depends_on",
"edgeSource": "indexed",
"strength": "strong",
"sourceLayer": "workplan",
"targetLayer": "workplan",
}
}
)
depends_count += 1
chokepoint[dst] = chokepoint.get(dst, 0) + 1
cited = 0
for task in open_tasks:
blob = " ".join(
@ -125,28 +227,38 @@ def coordination_graph_payload(
src_wp = str(task.get("workplan_id") or task.get("workstream_id"))
for token in set(WP_ID_RE.findall(blob.upper())):
dst = record_to_id.get(token) or slug_to_id.get(token.lower())
if dst and dst != src_wp and dst in wp_by_id:
elements.append(
{
"data": {
"id": f"edge:cites:{task['id']}:{dst}",
"stableKey": f"edge:cites:{task['id']}:{dst}",
"kind": "Edge",
"layer": "dependency",
"displayState": "show",
"source": f"workplan:{src_wp}",
"target": f"workplan:{dst}",
"edgeType": "waits_on",
"strength": "strong",
"sourceLayer": "workplan",
"targetLayer": "workplan",
}
if not dst or dst == src_wp or dst not in wp_by_id:
continue
if (src_wp, dst) in indexed_pairs:
continue
elements.append(
{
"data": {
"id": f"edge:cites:{task['id']}:{dst}",
"stableKey": f"edge:cites:{task['id']}:{dst}",
"kind": "Edge",
"layer": "dependency",
"displayState": "show",
"source": f"workplan:{src_wp}",
"target": f"workplan:{dst}",
"edgeType": "waits_on",
"edgeSource": "citation",
"strength": "strong",
"sourceLayer": "workplan",
"targetLayer": "workplan",
}
)
cited += 1
for el in elements:
if el["data"]["id"] == f"workplan:{dst}":
el["data"]["chokepoint"] = int(el["data"].get("chokepoint") or 0) + 1
}
)
cited += 1
chokepoint[dst] = chokepoint.get(dst, 0) + 1
indexed_pairs.add((src_wp, dst))
for el in elements:
node_id = el["data"]["id"]
if not node_id.startswith("workplan:"):
continue
wp_id = node_id.split(":", 1)[1]
el["data"]["chokepoint"] = int(chokepoint.get(wp_id) or 0)
generated = datetime.now(timezone.utc).isoformat()
return {
@ -159,8 +271,11 @@ def coordination_graph_payload(
"open_workplans": len(open_wps),
"open_tasks": len(open_tasks),
"belongs_to_edges": belongs,
"depends_on_edges": depends_count,
"citation_edges": cited,
"wait_or_human_tasks": wait_edges,
"residual_open_workplans": residual_open,
"include_residuals": include_residuals,
},
"elements": elements,
"hidden_elements": [],
@ -173,6 +288,20 @@ def fetch_hub_lists(api_base: str) -> tuple[list[dict[str, Any]], list[dict[str,
for status in sorted(OPEN_WORKPLAN):
workplans.extend(_get_json(f"{base}/workplans/?{urlencode({'status': status})}"))
tasks = _get_json(f"{base}/tasks/")
dep_rows = _get_json_optional(f"{base}/state/deps?include_residuals=true")
by_id = {
str(row.get("id")): row
for row in dep_rows
if isinstance(row, dict) and row.get("id")
}
for wp in workplans:
extra = by_id.get(str(wp.get("id")))
if not extra:
continue
if extra.get("depends_on") and not wp.get("depends_on"):
wp["depends_on"] = extra["depends_on"]
if extra.get("flavor") and not wp.get("flavor"):
wp["flavor"] = extra["flavor"]
return workplans, tasks
@ -184,5 +313,12 @@ def _get_json(url: str) -> list[dict[str, Any]]:
raise RuntimeError(f"unexpected JSON from {url}")
def _get_json_optional(url: str) -> list[dict[str, Any]]:
try:
return _get_json(url)
except Exception:
return []
def default_hub() -> str:
return os.environ.get("STATE_HUB_URL") or os.environ.get("API_BASE") or "http://127.0.0.1:8000"