feat(orchestrator): W2 Kanban board with swimlanes and forecast

Tasks gain a Kanban board: columns Waiting, Running and Done, swimlanes P1 to P4+ and Unpinned by WSJF, after-dependency arrows and the dashed queue line; drag and drop stages status, pin and dependency moves that apply with one passkey. The burn chart gets the trend and a forecast. Committed below the bar on the operator's word 2026-11-12 (no further per-bundle tests, one gate after the merge): the W2 e2e never ran.

🎯 Fix: 80 %
💥 Break: 10 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 100 %
🏆 Score: 72
🚦 Gate: ⛔ fix 80 % < 90, break 10 % > 5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-12 16:14:36 +00:00
1 parent fd8fc08603
commit 8e63b21e21
18 files changed
+1375 -76

No files matched your search

+5 -2
View File
@@ -16,9 +16,12 @@ Returns:
Runs as the operator, outside any sandbox, every operator command a
pinned passkey signed in the Agent Orchestrator: exactly the signed text, with bash
in the signed directory, each signature once and before its expiry.
Runs every signed task change (priority pin or WSJF estimate) as
Runs every signed task change (priority pin, status, dependency or WSJF
estimate; a board batch is one job per change) as
`python3 -I -B <rasci> <argv>` from / without a shell, after checking the
argv shape itself, and kills its process group after 30 s. Refuses task
argv shape itself, and kills its process group after 30 s; rasci.py
refuses a cycle or a done with an open key result, and its reason is the
job's output. Refuses task
changes without --rasci and --council or into a council that is a
symlink or another uid's, and every other job. Posts the last 200 output lines
back while it runs and the exit code at its end. The exit code is 0.
+24 -7
View File
@@ -27,6 +27,7 @@ PRIO = re.compile(r"[1-9][0-9]{0,2}")
FIBONACCI = ("1", "2", "3", "5", "8", "13", "21", "34", "55", "89")
ESTIMATOR = "Operator"
COMMANDS = "operator/"
STATUSES = ("queued", "running", "blocked", "done", "stopped")
APPROVAL_KEYS = {"v", "kind", "items", "nonce", "expires"}
RULE_KEYS = {"v", "kind", "id", "kinds", "tool", "pattern", "scope", "expires"}
WARNINGS = (
@@ -156,14 +157,31 @@ def task_item(task: str, argv: list[str]) -> dict:
return {"kind": "task", "id": task, "argv": argv}
def board_flags(task: str, flags: list[str]) -> bool:
if flags == ["--unpin"]:
return True
if len(flags) != 2:
return False
flag, value = flags
if flag == "--prio":
return bool(PRIO.fullmatch(value))
if flag == "--status":
return value in STATUSES
linked = flag in ("--after", "--drop-after")
return linked and value != task and bool(TASK.fullmatch(value)) and not value.startswith(COMMANDS)
def task_argv(task: object, argv: object) -> list[str]:
"""Args:
task: the register task the change is for, a rasci.py slug outside
operator/.
argv: the rasci.py arguments, one of `set <task> --prio <1-999>`,
`estimate <task> Operator --value <fib> --complexity <fib>`,
`estimate <task> Operator --value <fib>` (the operator's business
value alone) or `estimate <task> Operator --drop`.
argv: the rasci.py arguments, one of `set <task>` with exactly one
of `--prio <1-999>`, `--unpin`, `--status <status>`,
`--after <other>` or `--drop-after <other>` (other a register
slug outside operator/), `estimate <task> Operator --value <fib>
--complexity <fib>`, `estimate <task> Operator --value <fib>`
(the operator's business value alone) or
`estimate <task> Operator --drop`.
Returns:
argv, the hub and the runner check it the same way.
@@ -176,12 +194,11 @@ def task_argv(task: object, argv: object) -> list[str]:
if not isinstance(argv, list) or not all(isinstance(part, str) for part in argv):
raise Refused("the argv is no list of texts")
rest = argv[3:]
pinned = argv[:3] == ["set", task, "--prio"] and len(argv) == 4 and bool(PRIO.fullmatch(argv[3]))
valued = len(rest) in (2, 4) and rest[0] == "--value" and rest[1] in FIBONACCI
estimated = valued and (len(rest) == 2 or (rest[2] == "--complexity" and rest[3] in FIBONACCI))
if pinned or (argv[:3] == ["estimate", task, ESTIMATOR] and (estimated or rest == ["--drop"])):
if (argv[:2] == ["set", task] and board_flags(task, argv[2:])) or (argv[:3] == ["estimate", task, ESTIMATOR] and (estimated or rest == ["--drop"])):
return argv
raise Refused("the argv is no task priority or WSJF estimate change of this task")
raise Refused("the argv is no task priority, status, dependency or WSJF estimate change of this task")
def approval_payload(hashes: list[str], nonce: str, expires: int) -> dict: