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
|
HEARTBEAT_LIMIT = 3600
|
||||||
OFFICE = re.compile(r"^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$")
|
OFFICE = re.compile(r"^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$")
|
||||||
CLAIM = "claim.json"
|
CLAIM = "claim.json"
|
||||||
|
WORK_WIDTH = 40
|
||||||
RASCI = Path(__file__).resolve().parents[2] / "kvb-org-rasci" / "scripts" / "rasci.py"
|
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
|
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:
|
def cmd_list(args: argparse.Namespace) -> int:
|
||||||
live = live_sessions()
|
live = live_sessions()
|
||||||
sid = None
|
sid = None
|
||||||
@@ -208,7 +214,10 @@ def cmd_list(args: argparse.Namespace) -> int:
|
|||||||
continue
|
continue
|
||||||
state = "stale" if is_stale(claim, live) else "live"
|
state = "stale" if is_stale(claim, live) else "live"
|
||||||
holder = claim["name"] or claim["session"]
|
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
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -253,6 +262,7 @@ def main() -> int:
|
|||||||
beat.set_defaults(func=cmd_beat)
|
beat.set_defaults(func=cmd_beat)
|
||||||
listing = sub.add_parser("list")
|
listing = sub.add_parser("list")
|
||||||
listing.add_argument("--mine", action="store_true")
|
listing.add_argument("--mine", action="store_true")
|
||||||
|
listing.add_argument("--long", action="store_true")
|
||||||
listing.add_argument("--session")
|
listing.add_argument("--session")
|
||||||
listing.set_defaults(func=cmd_list)
|
listing.set_defaults(func=cmd_list)
|
||||||
sub.add_parser("gate").set_defaults(func=cmd_gate)
|
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._-]*)*$")
|
TASK = re.compile(r"^[a-z0-9][a-z0-9._-]*(/[a-z0-9][a-z0-9._-]*)*$")
|
||||||
LETTERS = re.compile(r"^(?!.*(.).*\1)[RASCI]+$")
|
LETTERS = re.compile(r"^(?!.*(.).*\1)[RASCI]+$")
|
||||||
ORDER = "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")
|
STATUSES = ("queued", "running", "blocked", "done", "stopped")
|
||||||
ENDED = ("done", "stopped")
|
ENDED = ("done", "stopped")
|
||||||
FIELDS = (
|
FIELDS = (
|
||||||
@@ -441,34 +443,43 @@ def cmd_rm(args: argparse.Namespace) -> int:
|
|||||||
return 0
|
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:
|
def cmd_show(args: argparse.Namespace) -> int:
|
||||||
rows = merged(args.all)
|
rows = merged(args.all)
|
||||||
if not rows:
|
if not rows:
|
||||||
print("no tasks")
|
print("no tasks")
|
||||||
return 0
|
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("| " + " | ".join(header) + " |")
|
||||||
print("|" + "---|" * len(header))
|
print("|" + "---|" * len(header))
|
||||||
problems = []
|
problems = []
|
||||||
for rank, task in enumerate(ranked(rows), 1):
|
for rank, task in enumerate(ranked(rows), 1):
|
||||||
row = rows[task]
|
row = rows[task]
|
||||||
title = f"{task}: {row['title']}" if row.get("title") else task
|
|
||||||
score = wsjf(row)
|
score = wsjf(row)
|
||||||
values = [
|
values = [rank, f"{score[2]:g}" if score else "-", *columns(task, row)]
|
||||||
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
|
|
||||||
]
|
|
||||||
print("| " + " | ".join(str(v) for v in values) + " |")
|
print("| " + " | ".join(str(v) for v in values) + " |")
|
||||||
problems += gaps(task, row)
|
problems += gaps(task, row)
|
||||||
if problems:
|
if problems:
|
||||||
@@ -515,6 +526,7 @@ def main() -> int:
|
|||||||
p.set_defaults(func=func)
|
p.set_defaults(func=func)
|
||||||
p = sub.add_parser("show")
|
p = sub.add_parser("show")
|
||||||
p.add_argument("--all", action="store_true")
|
p.add_argument("--all", action="store_true")
|
||||||
|
p.add_argument("--wide", action="store_true")
|
||||||
p.set_defaults(func=cmd_show)
|
p.set_defaults(func=cmd_show)
|
||||||
p = sub.add_parser("artifacts")
|
p = sub.add_parser("artifacts")
|
||||||
p.add_argument("--ended", action="store_true")
|
p.add_argument("--ended", action="store_true")
|
||||||
|
|||||||
+45
-1
@@ -1,4 +1,4 @@
|
|||||||
"""Exercise the kvb-org-organize council script: election, staleness, release, gate."""
|
"""Exercise the kvb-org-organize council script: election, staleness, release, list, gate."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -85,6 +85,50 @@ class TestCouncil(unittest.TestCase):
|
|||||||
self._run("s1", "claim", "work/x", "--work", "w")
|
self._run("s1", "claim", "work/x", "--work", "w")
|
||||||
self.assertEqual(self._run("s1", "gate", stdin=inside).returncode, 0)
|
self.assertEqual(self._run("s1", "gate", stdin=inside).returncode, 0)
|
||||||
|
|
||||||
|
def _two_claims(self) -> str:
|
||||||
|
work = "fix the failing deploy of web-app-x on branch fix/web-app-x/deploy"
|
||||||
|
self._run("s1", "claim", "work/x", "--work", work)
|
||||||
|
self._run("s2", "claim", "work/y", "--work", "short work")
|
||||||
|
(self.sessions / "s2.json").unlink()
|
||||||
|
return work
|
||||||
|
|
||||||
|
def _since(self, office: str) -> str:
|
||||||
|
return json.loads((self.root / office / "claim.json").read_text(encoding="utf-8"))["since"]
|
||||||
|
|
||||||
|
def test_list_prints_office_holder_state_and_a_short_work_cell(self):
|
||||||
|
self._two_claims()
|
||||||
|
result = self._run("s1", "list")
|
||||||
|
self.assertEqual(result.returncode, 0, result.stderr)
|
||||||
|
self.assertEqual(
|
||||||
|
result.stdout.splitlines(),
|
||||||
|
["work/x\tOne\tlive\tfix the failing deploy of web-app-x on …", "work/y\tTwo\tstale\tshort work"],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_list_collapses_whitespace_in_the_short_work_cell(self):
|
||||||
|
self._run("s1", "claim", "work/z", "--work", "a\tb\nc")
|
||||||
|
self.assertEqual(self._run("s1", "list").stdout.splitlines(), ["work/z\tOne\tlive\ta b c"])
|
||||||
|
|
||||||
|
def test_list_long_prints_since_and_the_whole_work_line(self):
|
||||||
|
work = self._two_claims()
|
||||||
|
self.assertEqual(
|
||||||
|
self._run("s1", "list", "--long").stdout.splitlines(),
|
||||||
|
[
|
||||||
|
f"work/x\tOne\t{self._since('work/x')}\tlive\t{work}",
|
||||||
|
f"work/y\tTwo\t{self._since('work/y')}\tstale\tshort work",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_list_mine_keeps_only_the_callers_claims_in_both_forms(self):
|
||||||
|
work = self._two_claims()
|
||||||
|
self.assertEqual(
|
||||||
|
self._run("s1", "list", "--mine").stdout.splitlines(),
|
||||||
|
["work/x\tOne\tlive\tfix the failing deploy of web-app-x on …"],
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self._run("s1", "list", "--mine", "--long").stdout.splitlines(),
|
||||||
|
[f"work/x\tOne\t{self._since('work/x')}\tlive\t{work}"],
|
||||||
|
)
|
||||||
|
|
||||||
def test_invalid_office_is_a_usage_error(self):
|
def test_invalid_office_is_a_usage_error(self):
|
||||||
self.assertEqual(self._run("s1", "claim", "../x", "--work", "w").returncode, 2)
|
self.assertEqual(self._run("s1", "claim", "../x", "--work", "w").returncode, 2)
|
||||||
|
|
||||||
|
|||||||
+52
-10
@@ -14,7 +14,8 @@ from pathlib import Path
|
|||||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
RASCI = REPO_ROOT / "skills" / "kvb-org-rasci" / "scripts" / "rasci.py"
|
RASCI = REPO_ROOT / "skills" / "kvb-org-rasci" / "scripts" / "rasci.py"
|
||||||
COUNCIL = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "council.py"
|
COUNCIL = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "council.py"
|
||||||
HEADER = "| Prio | WSJF | Task | OKR | Status | Worker | Artifacts | Start | End | R | A | S | C | I |"
|
WIDE_HEADER = "| Prio | WSJF | Task | OKR | Status | Worker | Artifacts | Start | End | R | A | S | C | I |"
|
||||||
|
COMPACT_HEADER = "| Prio | WSJF | Task | Status | Worker | A |"
|
||||||
|
|
||||||
|
|
||||||
def local(iso: str) -> str:
|
def local(iso: str) -> str:
|
||||||
@@ -69,7 +70,7 @@ class TestRasci(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
(path / "claim.json").write_text(json.dumps(claim), encoding="utf-8")
|
(path / "claim.json").write_text(json.dumps(claim), encoding="utf-8")
|
||||||
|
|
||||||
def test_matrix_orders_by_priority_and_lists_roles(self):
|
def _two_tasks(self) -> str:
|
||||||
start = "2026-11-01T10:00:00+00:00"
|
start = "2026-11-01T10:00:00+00:00"
|
||||||
self._run("set", "work/a", "--title", "Alpha", "--objective", "Ship A", "--prio", "2",
|
self._run("set", "work/a", "--title", "Alpha", "--objective", "Ship A", "--prio", "2",
|
||||||
"--status", "running", "--worker", "Skills", "--start", start)
|
"--status", "running", "--worker", "Skills", "--start", start)
|
||||||
@@ -78,8 +79,12 @@ class TestRasci(unittest.TestCase):
|
|||||||
self._run("role", "work/a", "Pkgmgr", "C")
|
self._run("role", "work/a", "Pkgmgr", "C")
|
||||||
self._run("set", "work/b", "--title", "Beta", "--prio", "1", "--worker", "subagent Explore (haiku)")
|
self._run("set", "work/b", "--title", "Beta", "--prio", "1", "--worker", "subagent Explore (haiku)")
|
||||||
self._run("role", "work/b", "Pkgmgr", "R")
|
self._run("role", "work/b", "Pkgmgr", "R")
|
||||||
lines = self._out("show")
|
return start
|
||||||
self.assertEqual(lines[0], HEADER)
|
|
||||||
|
def test_wide_matrix_orders_by_priority_and_lists_roles(self):
|
||||||
|
start = self._two_tasks()
|
||||||
|
lines = self._out("show", "--wide")
|
||||||
|
self.assertEqual(lines[0], WIDE_HEADER)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
lines[2], "| 1 | - | work/b: Beta | - | queued | subagent Explore (haiku) | - | - | - | Pkgmgr | | | | |"
|
lines[2], "| 1 | - | work/b: Beta | - | queued | subagent Explore (haiku) | - | - | - | Pkgmgr | | | | |"
|
||||||
)
|
)
|
||||||
@@ -92,15 +97,43 @@ class TestRasci(unittest.TestCase):
|
|||||||
lines[4], "gaps: work/b has 0 A; work/b has no key result; work/a has no key result"
|
lines[4], "gaps: work/b has 0 A; work/b has no key result; work/a has no key result"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_show_prints_the_compact_table_by_default(self):
|
||||||
|
self._two_tasks()
|
||||||
|
self.assertEqual(
|
||||||
|
self._out("show"),
|
||||||
|
[
|
||||||
|
COMPACT_HEADER,
|
||||||
|
"|---|---|---|---|---|---|",
|
||||||
|
"| 1 | - | work/b: Beta | queued | subagent Explore (haiku) | |",
|
||||||
|
"| 2 | - | work/a: Alpha | running | Skills | Skills |",
|
||||||
|
"gaps: work/b has 0 A; work/b has no key result; work/a has no key result",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_compact_cells_keep_the_slug_and_truncate_title_worker_and_a(self):
|
||||||
|
slug = "work/" + "s" * 45
|
||||||
|
self._run("set", slug, "--title", "Hidden", "--prio", "1")
|
||||||
|
self._run("set", "work/a", "--title", "A title far longer than the forty character cell",
|
||||||
|
"--prio", "2", "--worker", "subagent general-purpose (default)")
|
||||||
|
self._run("role", "work/a", "Accountable Party With A Long Name", "A")
|
||||||
|
lines = self._out("show")
|
||||||
|
self.assertEqual(lines[2], f"| 1 | - | {slug}… | queued | - | |")
|
||||||
|
self.assertEqual(
|
||||||
|
lines[3],
|
||||||
|
"| 2 | - | work/a: A title far longer than the for… | queued | subagent general-purpos… "
|
||||||
|
"| Accountable Party With … |",
|
||||||
|
)
|
||||||
|
|
||||||
def test_key_results_gate_done_and_fill_the_okr_column(self):
|
def test_key_results_gate_done_and_fill_the_okr_column(self):
|
||||||
self._run("set", "work/a", "--objective", "Ship A", "--status", "running")
|
self._run("set", "work/a", "--objective", "Ship A", "--status", "running")
|
||||||
self._run("role", "work/a", "Skills", "RA")
|
self._run("role", "work/a", "Skills", "RA")
|
||||||
self._run("kr", "work/a", "add", "tests green")
|
self._run("kr", "work/a", "add", "tests green")
|
||||||
self._run("kr", "work/a", "add", "released")
|
self._run("kr", "work/a", "add", "released")
|
||||||
self._run("kr", "work/a", "met", "1")
|
self._run("kr", "work/a", "met", "1")
|
||||||
lines = self._out("show")
|
lines = self._out("show", "--wide")
|
||||||
self.assertIn("| Ship A (1/2 KR) | running |", lines[2])
|
self.assertIn("| Ship A (1/2 KR) | running |", lines[2])
|
||||||
self.assertEqual(len(lines), 3)
|
self.assertEqual(len(lines), 3)
|
||||||
|
self.assertNotIn("Ship A", self._out("show")[2])
|
||||||
self.assertEqual(self._run("set", "work/a", "--status", "done").returncode, 2)
|
self.assertEqual(self._run("set", "work/a", "--status", "done").returncode, 2)
|
||||||
self.assertIn("key result 2 [ ]: released", self._out("get", "work/a"))
|
self.assertIn("key result 2 [ ]: released", self._out("get", "work/a"))
|
||||||
self._run("kr", "work/a", "met", "2")
|
self._run("kr", "work/a", "met", "2")
|
||||||
@@ -122,7 +155,11 @@ class TestRasci(unittest.TestCase):
|
|||||||
self._run("set", "work/a", "--status", "done", "--end", "2026-11-01T12:00:00+00:00")
|
self._run("set", "work/a", "--status", "done", "--end", "2026-11-01T12:00:00+00:00")
|
||||||
self._run("role", "work/a", "Skills", "RA")
|
self._run("role", "work/a", "Skills", "RA")
|
||||||
self.assertEqual(self._out("show"), ["no tasks"])
|
self.assertEqual(self._out("show"), ["no tasks"])
|
||||||
self.assertIn("| done | - |", self._out("show", "--all")[2])
|
self.assertEqual(self._out("show", "--wide"), ["no tasks"])
|
||||||
|
self.assertEqual(self._out("show", "--all")[2], "| 1 | - | work/a | done | - | Skills |")
|
||||||
|
self.assertIn(
|
||||||
|
f"| done | - | - | - | {local('2026-11-01T12:00:00+00:00')} |", self._out("show", "--all", "--wide")[2]
|
||||||
|
)
|
||||||
|
|
||||||
def test_artifacts_and_tools(self):
|
def test_artifacts_and_tools(self):
|
||||||
self._run("set", "work/a", "--workdir", "/w/a", "--branch", "feat/a", "--status", "done",
|
self._run("set", "work/a", "--workdir", "/w/a", "--branch", "feat/a", "--status", "done",
|
||||||
@@ -145,7 +182,7 @@ class TestRasci(unittest.TestCase):
|
|||||||
self.assertRegex(rows[2][4], r"^\d\d-\d\d \d\d:\d\d$")
|
self.assertRegex(rows[2][4], r"^\d\d-\d\d \d\d:\d\d$")
|
||||||
self.assertEqual([r.split("\t")[1] for r in self._out("artifacts", "--ended")], ["work/a"] * 3)
|
self.assertEqual([r.split("\t")[1] for r in self._out("artifacts", "--ended")], ["work/a"] * 3)
|
||||||
self.assertEqual(len(self._out("artifacts", "--task", "work/b")), 1)
|
self.assertEqual(len(self._out("artifacts", "--task", "work/b")), 1)
|
||||||
self.assertIn("| queued | Two | 1 |", self._out("show")[2])
|
self.assertIn("| queued | Two | 1 |", self._out("show", "--wide")[2])
|
||||||
self.assertTrue(any(line.startswith("artifact: image:core:dev (since ") for line in self._out("get", "work/a")))
|
self.assertTrue(any(line.startswith("artifact: image:core:dev (since ") for line in self._out("get", "work/a")))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self._out("tools"), ["2\tmake test\twork/a work/b", "1\tcouncil.py\twork/a"]
|
self._out("tools"), ["2\tmake test\twork/a work/b", "1\tcouncil.py\twork/a"]
|
||||||
@@ -158,11 +195,15 @@ class TestRasci(unittest.TestCase):
|
|||||||
self._claim("chair", "s1", "One", "allocator")
|
self._claim("chair", "s1", "One", "allocator")
|
||||||
self._run("set", "work/live", "--prio", "1", "--status", "running")
|
self._run("set", "work/live", "--prio", "1", "--status", "running")
|
||||||
self._run("role", "work/live", "Two", "S")
|
self._run("role", "work/live", "Two", "S")
|
||||||
lines = self._out("show")
|
lines = self._out("show", "--wide")
|
||||||
self.assertTrue(lines[2].startswith("| 1 | - | work/live | - | running | One |"))
|
self.assertTrue(lines[2].startswith("| 1 | - | work/live | - | running | One |"))
|
||||||
self.assertTrue(lines[2].endswith("| One | One | Two | | |"))
|
self.assertTrue(lines[2].endswith("| One | One | Two | | |"))
|
||||||
self.assertTrue(lines[3].startswith("| 2 | - | cluster/dead | - | stale | Gone |"))
|
self.assertTrue(lines[3].startswith("| 2 | - | cluster/dead | - | stale | Gone |"))
|
||||||
self.assertFalse(any("chair" in line for line in lines))
|
self.assertFalse(any("chair" in line for line in lines))
|
||||||
|
lines = self._out("show")
|
||||||
|
self.assertEqual(lines[2], "| 1 | - | work/live | running | One | One |")
|
||||||
|
self.assertEqual(lines[3], "| 2 | - | cluster/dead | stale | Gone | Gone |")
|
||||||
|
self.assertFalse(any("chair" in line for line in lines))
|
||||||
|
|
||||||
def test_council_claim_takeover_and_release_write_through(self):
|
def test_council_claim_takeover_and_release_write_through(self):
|
||||||
self.assertEqual(self._run("claim", "work/x", "--work", "Do X", script=COUNCIL).returncode, 0)
|
self.assertEqual(self._run("claim", "work/x", "--work", "Do X", script=COUNCIL).returncode, 0)
|
||||||
@@ -188,7 +229,7 @@ class TestRasci(unittest.TestCase):
|
|||||||
self._run("role", "work/a", "Skills", "RA")
|
self._run("role", "work/a", "Skills", "RA")
|
||||||
self._run("role", "work/a", "Pkgmgr", "C")
|
self._run("role", "work/a", "Pkgmgr", "C")
|
||||||
self._run("role", "work/a", "Pkgmgr", "-")
|
self._run("role", "work/a", "Pkgmgr", "-")
|
||||||
self.assertTrue(self._out("show")[2].endswith("| Skills | Skills | | | |"))
|
self.assertTrue(self._out("show", "--wide")[2].endswith("| Skills | Skills | | | |"))
|
||||||
self.assertEqual(self._out("rm", "work/a"), ["removed work/a"])
|
self.assertEqual(self._out("rm", "work/a"), ["removed work/a"])
|
||||||
self.assertEqual(self._out("show"), ["no tasks"])
|
self.assertEqual(self._out("show"), ["no tasks"])
|
||||||
|
|
||||||
@@ -213,7 +254,8 @@ class TestRasci(unittest.TestCase):
|
|||||||
def test_pipe_in_a_title_does_not_break_the_table(self):
|
def test_pipe_in_a_title_does_not_break_the_table(self):
|
||||||
self._run("set", "work/a", "--title", "a | b")
|
self._run("set", "work/a", "--title", "a | b")
|
||||||
self._run("role", "work/a", "Skills", "RA")
|
self._run("role", "work/a", "Skills", "RA")
|
||||||
self.assertTrue(self._out("show")[2].startswith("| 1 | - | work/a: a \\| b | - | queued |"))
|
self.assertTrue(self._out("show", "--wide")[2].startswith("| 1 | - | work/a: a \\| b | - | queued |"))
|
||||||
|
self.assertEqual(self._out("show")[2], "| 1 | - | work/a: a \\| b | queued | - | Skills |")
|
||||||
|
|
||||||
def test_wsjf_ranks_by_median_estimates(self):
|
def test_wsjf_ranks_by_median_estimates(self):
|
||||||
self._run("set", "work/a", "--objective", "Ship A")
|
self._run("set", "work/a", "--objective", "Ship A")
|
||||||
|
|||||||
Reference in new issue
Block a user