feat(kvb-org-rasci, kvb-org-organize): print compact register and council tables
Agents read these tables on every status, organize and matrix step. rasci.py show now prints Prio, WSJF, Task, Status, Worker and A (task slug never cut, title cell 40 characters, worker and A 24), down from 1817 to 1003 words on the live register; show --wide prints the old 14-column table byte for byte. council.py list prints office, holder, state and a 40-character work cell with whitespace collapsed, 1236 to 276 words; list --long prints the old form. The old assertions now run against --wide and --long; new tests pin the compact defaults, cutting, --all, --mine and pipes in titles, and fail against the previous scripts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
48ae64c932
commit
031fd3deb4
4 files changed
+137
-29
No files matched your search
@@ -31,6 +31,7 @@ ROOT = Path(os.environ.get("AGENTS_COUNCIL_ROOT", "/tmp/agents-council"))
|
||||
HEARTBEAT_LIMIT = 3600
|
||||
OFFICE = re.compile(r"^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$")
|
||||
CLAIM = "claim.json"
|
||||
WORK_WIDTH = 40
|
||||
RASCI = Path(__file__).resolve().parents[2] / "kvb-org-rasci" / "scripts" / "rasci.py"
|
||||
|
||||
|
||||
@@ -198,6 +199,11 @@ def cmd_beat(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def short(text: str, width: int) -> str:
|
||||
text = " ".join(text.split())
|
||||
return text if len(text) <= width else text[: width - 1] + "…"
|
||||
|
||||
|
||||
def cmd_list(args: argparse.Namespace) -> int:
|
||||
live = live_sessions()
|
||||
sid = None
|
||||
@@ -208,7 +214,10 @@ def cmd_list(args: argparse.Namespace) -> int:
|
||||
continue
|
||||
state = "stale" if is_stale(claim, live) else "live"
|
||||
holder = claim["name"] or claim["session"]
|
||||
print(f"{office}\t{holder}\t{claim['since']}\t{state}\t{claim['work']}")
|
||||
if args.long:
|
||||
print(f"{office}\t{holder}\t{claim['since']}\t{state}\t{claim['work']}")
|
||||
else:
|
||||
print(f"{office}\t{holder}\t{state}\t{short(claim['work'], WORK_WIDTH)}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -253,6 +262,7 @@ def main() -> int:
|
||||
beat.set_defaults(func=cmd_beat)
|
||||
listing = sub.add_parser("list")
|
||||
listing.add_argument("--mine", action="store_true")
|
||||
listing.add_argument("--long", action="store_true")
|
||||
listing.add_argument("--session")
|
||||
listing.set_defaults(func=cmd_list)
|
||||
sub.add_parser("gate").set_defaults(func=cmd_gate)
|
||||
|
||||
@@ -33,6 +33,8 @@ COUNCIL = Path(__file__).resolve().parents[2] / "kvb-org-organize" / "scripts" /
|
||||
TASK = re.compile(r"^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$")
|
||||
LETTERS = re.compile(r"^(?!.*(.).*\1)[RASCI]+$")
|
||||
ORDER = "RASCI"
|
||||
WIDE = ("Prio", "WSJF", "Task", "OKR", "Status", "Worker", "Artifacts", "Start", "End", *ORDER)
|
||||
COMPACT = ("Prio", "WSJF", "Task", "Status", "Worker", "A")
|
||||
STATUSES = ("queued", "running", "blocked", "done", "stopped")
|
||||
ENDED = ("done", "stopped")
|
||||
FIELDS = (
|
||||
@@ -441,34 +443,43 @@ def cmd_rm(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def holders(row: dict, letter: str, width: int) -> str:
|
||||
parties = sorted(row["roles"], key=lambda p: (p != OPERATOR, p.lower()))
|
||||
return cell(", ".join(p for p in parties if letter in row["roles"][p]), width)
|
||||
|
||||
|
||||
def wide(task: str, row: dict) -> list[object]:
|
||||
title = f"{task}: {row['title']}" if row.get("title") else task
|
||||
return [
|
||||
cell(title, 50),
|
||||
okr(row),
|
||||
row["status"],
|
||||
cell(row.get("worker") or "-", 30),
|
||||
len(row.get("artifacts", [])) or "-",
|
||||
when(row.get("start")),
|
||||
when(row.get("end")),
|
||||
*(holders(row, letter, 30) for letter in ORDER),
|
||||
]
|
||||
|
||||
|
||||
def compact(task: str, row: dict) -> list[object]:
|
||||
title = cell(f"{task}: {row['title']}", max(40, len(task) + 1)) if row.get("title") else task
|
||||
return [title, row["status"], cell(row.get("worker") or "-", 24), holders(row, "A", 24)]
|
||||
|
||||
|
||||
def cmd_show(args: argparse.Namespace) -> int:
|
||||
rows = merged(args.all)
|
||||
if not rows:
|
||||
print("no tasks")
|
||||
return 0
|
||||
header = ["Prio", "WSJF", "Task", "OKR", "Status", "Worker", "Artifacts", "Start", "End", *ORDER]
|
||||
header, columns = (WIDE, wide) if args.wide else (COMPACT, compact)
|
||||
print("| " + " | ".join(header) + " |")
|
||||
print("|" + "---|" * len(header))
|
||||
problems = []
|
||||
for rank, task in enumerate(ranked(rows), 1):
|
||||
row = rows[task]
|
||||
title = f"{task}: {row['title']}" if row.get("title") else task
|
||||
score = wsjf(row)
|
||||
values = [
|
||||
rank,
|
||||
f"{score[2]:g}" if score else "-",
|
||||
cell(title, 50),
|
||||
okr(row),
|
||||
row["status"],
|
||||
cell(row.get("worker") or "-", 30),
|
||||
len(row.get("artifacts", [])) or "-",
|
||||
when(row.get("start")),
|
||||
when(row.get("end")),
|
||||
]
|
||||
parties = sorted(row["roles"], key=lambda p: (p != OPERATOR, p.lower()))
|
||||
values += [
|
||||
cell(", ".join(p for p in parties if letter in row["roles"][p]), 30) for letter in ORDER
|
||||
]
|
||||
values = [rank, f"{score[2]:g}" if score else "-", *columns(task, row)]
|
||||
print("| " + " | ".join(str(v) for v in values) + " |")
|
||||
problems += gaps(task, row)
|
||||
if problems:
|
||||
@@ -515,6 +526,7 @@ def main() -> int:
|
||||
p.set_defaults(func=func)
|
||||
p = sub.add_parser("show")
|
||||
p.add_argument("--all", action="store_true")
|
||||
p.add_argument("--wide", action="store_true")
|
||||
p.set_defaults(func=cmd_show)
|
||||
p = sub.add_parser("artifacts")
|
||||
p.add_argument("--ended", action="store_true")
|
||||
|
||||
Reference in new issue
Block a user