Files
skills/tests/test_hub_ancestry.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 f7a59d43a5 feat(orchestrator): W3 artifacts tree, ancestry, gates and token-free cleanup
Artifacts show the branch tree with ancestry and gate marks. The cleanup runner calls pkgmgr branch cleanup with its real argv and parses its repos[*].items output, refuses any plan with a remote-branch action, marks an item done only when pkgmgr reports it done, and offers no delete for a branch a worktree holds. Every text states what the pkgmgr command does to volumes. Committed below the bar on the operator's word 2026-11-12 (no further per-bundle tests, one gate after the merge): the W3 e2e never ran; the client switches to --local once pkgmgr ships it.

🎯 Fix: 90 %
💥 Break: 6 %
💸 Cost: 30 %
⏱️ Time: 40 %
🛡️ Security: 100 %
🏆 Score: 100
🚦 Gate: ⛔ break 6 % > 5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 16:15:14 +00:00

145 lines
8.2 KiB
Python

from __future__ import annotations
import json
import re
import subprocess
import unittest
from hub_case import HubCase
from hub_support import HUB, signing
ANCESTRY = HUB / "templates" / "ancestry.js"
PAGE = HUB / "templates" / "page-ancestry.js"
STYLE = HUB / "templates" / "style-ancestry.css"
REPO = "/home/op/Repositories/demo"
# Param: argv[1] is ancestry.js; prints the layouts, gate marks and argv the page builds.
PROBE = """
const A = require(process.argv[1]);
const node = (id) => ({id, label: id});
const shape = (plan) => ({
rows: plan.nodes.map((entry) => [entry.id, entry.generation]),
ys: Object.fromEntries(plan.nodes.map((entry) => [entry.id, entry.y])),
xs: Object.fromEntries(plan.nodes.map((entry) => [entry.id, entry.x])),
links: plan.links.map((link) => [link.parent, link.child, link.derived, link.y1 < link.y2]),
size: [plan.width, plan.height],
});
const family = shape(A.layout(["main", "feature/a", "feature/b", "feature/ab", "lonely"].map(node), [
{child: "feature/a", parent: "main", source: "recorded"},
{child: "feature/b", parent: "main", source: "derived"},
{child: "feature/ab", parent: "feature/a", source: "operator"},
{child: "feature/ab", parent: "feature/b", source: "derived"},
{child: "ghost", parent: "main", source: "recorded"},
]));
const looped = shape(A.layout(["x", "y", "z"].map(node), [{child: "x", parent: "y"}, {child: "y", parent: "x"}, {child: "z", parent: "z"}]));
const chain = shape(A.layout(["main", "mid", "leaf"].map(node), [{child: "leaf", parent: "main"}, {child: "leaf", parent: "mid"}, {child: "mid", parent: "main"}]));
const roots = Array.from({length: 20}, (_, index) => `b${String(index).padStart(2, "0")}`);
const wide = shape(A.layout([...roots, "child"].map(node), [{child: "child", parent: "b00"}]));
const shown = {resolved: {commit: {level: "test", source: "feature/a"}, merge: {level: "quality-high", source: "main"}, push: {level: "none", source: null}, pr: {level: "e2e", source: "feature/a"}}, rules: {commit: "test", pr: "e2e"}};
console.log(JSON.stringify({
family, looped, chain, wide, spacing: A.SPACING,
marks: A.gateMarks("feature/a", shown),
bare: A.gateMarks("lonely", null),
cycle: [null, "none", "quick", "test", "quality-high", "e2e"].map(A.nextRule),
argv: [A.ruleArgv("/r", "b", "push", "test"), A.ruleArgv("/r", "b", "push", null), A.parentArgv("/r", "b", "p", false), A.parentArgv("/r", "b", "p", true)],
icons: A.LEVEL_ICONS,
}));
"""
class TestAncestryLayout(unittest.TestCase):
@classmethod
def setUpClass(cls):
done = subprocess.run(["node", "-e", PROBE, str(ANCESTRY)], capture_output=True, text=True, timeout=60, check=True)
cls.data = json.loads(done.stdout)
def test_generations_run_top_down_and_a_node_with_several_parents_sits_below_the_deepest(self):
family = self.data["family"]
self.assertEqual(family["rows"], [["main", 0], ["feature/a", 1], ["feature/b", 1], ["feature/ab", 2], ["lonely", 0]])
self.assertLess(family["ys"]["main"], family["ys"]["feature/a"])
self.assertLess(family["ys"]["feature/a"], family["ys"]["feature/ab"])
self.assertEqual(family["ys"]["feature/a"], family["ys"]["feature/b"])
self.assertEqual(self.data["chain"]["rows"], [["main", 0], ["mid", 1], ["leaf", 2]])
def test_every_parent_edge_is_drawn_downwards_and_only_derived_ones_are_marked_dashed(self):
self.assertEqual(self.data["family"]["links"], [
["main", "feature/a", False, True],
["main", "feature/b", True, True],
["feature/a", "feature/ab", False, True],
["feature/b", "feature/ab", True, True],
])
def test_a_child_sits_between_its_parents_and_unknown_or_looping_edges_do_not_break_the_layout(self):
xs = self.data["family"]["xs"]
self.assertTrue(min(xs["feature/a"], xs["feature/b"]) <= xs["feature/ab"] <= max(xs["feature/a"], xs["feature/b"]))
looped = self.data["looped"]
self.assertEqual(sorted(generation for _, generation in looped["rows"]), [0, 0, 1])
self.assertEqual([link[:2] for link in looped["links"] if link[0] == link[1]], [])
def test_a_wide_generation_wraps_into_bands_and_the_next_generation_starts_below_them(self):
wide, spacing = self.data["wide"], self.data["spacing"]
self.assertEqual(wide["size"], [spacing["wrap"] * spacing["column"], 4 * spacing["row"]])
roots = [name for name, generation in wide["rows"] if generation == 0]
self.assertEqual(len(roots), 20)
self.assertEqual(len({(wide["xs"][name], wide["ys"][name]) for name in roots}), 20)
self.assertEqual(sorted({wide["ys"][name] for name in roots}), [spacing["row"] * band + spacing["row"] / 2 for band in range(3)])
self.assertEqual(wide["ys"]["child"], 3.5 * spacing["row"])
self.assertTrue(all(0 < x < wide["size"][0] for x in wide["xs"].values()))
class TestGateMarks(unittest.TestCase):
@classmethod
def setUpClass(cls):
done = subprocess.run(["node", "-e", PROBE, str(ANCESTRY)], capture_output=True, text=True, timeout=60, check=True)
cls.data = json.loads(done.stdout)
def test_each_action_shows_its_resolved_level_with_own_rules_solid_and_inherited_faint_with_the_source(self):
marks = {mark["action"]: mark for mark in self.data["marks"]}
self.assertEqual(list(marks), ["commit", "merge", "push", "pr"])
self.assertEqual([(marks[a]["level"], marks[a]["own"], marks[a]["inherited"]) for a in marks], [("test", True, False), ("quality-high", False, True), ("none", False, True), ("e2e", True, False)])
self.assertEqual([marks[a]["title"] for a in marks], ["commit: test · own rule", "merge: quality-high · from main", "push: none · no rule", "pr: e2e · own rule"])
self.assertEqual([marks[a]["icon"] for a in marks], ["bi-check2-circle", "bi-shield-check", "bi-circle", "bi-browser-chrome"])
def test_a_branch_gates_py_does_not_know_shows_none_inherited_for_every_action(self):
self.assertEqual([(mark["level"], mark["inherited"], mark["source"]) for mark in self.data["bare"]], [("none", True, "")] * 4)
def test_a_click_cycles_the_own_rule_through_every_level_and_back_to_inherited(self):
self.assertEqual(self.data["cycle"], ["none", "quick", "test", "quality-high", "e2e", None])
def test_the_staged_argv_is_exactly_what_signing_gate_argv_accepts(self):
for argv in self.data["argv"]:
with self.subTest(argv=argv):
self.assertEqual(signing.gate_argv(argv), argv)
self.assertEqual(self.data["argv"][1], ["rule", "drop", "--repo", "/r", "--branch", "b", "--action", "push"])
def test_the_level_icons_are_the_page_levels_of_gates_py(self):
self.assertEqual(list(self.data["icons"]), list(signing.GATE_LEVELS))
class TestAncestryAssets(unittest.TestCase):
def test_the_layout_module_parses_on_its_own_and_derived_edges_are_dashed(self):
done = subprocess.run(["node", "--check", str(ANCESTRY)], capture_output=True, text=True, timeout=60, check=False)
self.assertEqual(done.returncode, 0, done.stderr)
self.assertIn("svg .ancestry-edge.derived { stroke-dasharray: 5 5; }", STYLE.read_text(encoding="utf-8"))
self.assertIn(".gate-mark.inherited { opacity: .4; }", STYLE.read_text(encoding="utf-8"))
def test_every_new_control_is_an_icon_with_title_and_aria_label(self):
page = PAGE.read_text(encoding="utf-8")
for control in re.findall(r'iconBtn\("(bi-[a-z0-9-]+)", "([^"]+)"', page):
with self.subTest(control=control):
self.assertTrue(control[1])
self.assertIn('...named(`${mark.action} gate`, title)', page)
self.assertIn('role: "button", "aria-label": node.label', page)
class TestHubAncestry(HubCase):
def test_the_page_ships_the_layout_module_and_the_gate_level_icons(self):
html = self.index()
self.assertIn(ANCESTRY.read_text(encoding="utf-8").strip(), html)
for name in ("bi-circle", "bi-lightning-charge", "bi-check2-circle", "bi-shield-check", "bi-browser-chrome", "bi-key", "bi-key-fill", "bi-check2-all"):
with self.subTest(name=name):
self.assertIn(f'<symbol id="{name}"', html)
if __name__ == "__main__":
unittest.main()