Files
skills/tests/test_hub_tree.py
T
Kevin Veen-BirkenbachandClaude Opus 5.5 a388c6ef77 fix(orchestrator): S1 fit, rename once, Deutschlandfunk e2e, squelch, tree cause
Requirement 001 phase 6, S1:

- Fit: Agents and Artifacts in a graph view fill the window from 861 px
  up (body.fit flex column); the e2e walk probes #agents/orbit at every
  width for a page no taller than the window.
- Rename once: a session seen under two names by one source (two claims)
  flipped its name on every state build, so the Stream logged a rename
  each poll. Names.observe keeps only the newest sighting per session
  and source, and the timeline emits each (session, before, after) pair
  once.
- Deutschlandfunk: root cause was the signed media-src, which never
  listed the redirect target f111.rndfnk.com that st01.sslstream.dlf.de
  answers with (fixed in ba97f7c). The e2e now replays that chain, a
  302 from one https origin to a second, in Chromium and Firefox, with
  self-signed throwaway certificates the drivers accept.
- Squelch: a settings slider 0..1 scales every tone; at 0 no beep plays,
  music on or off (Voice.beep skips a silent tone). Stored settings
  without the key load with 1.0.
- Tree: the missing tree file error names `pkgmgr branch tree` and the
  release and install step that makes the timer write it.

Evidence: make test 655 OK, ruff and markdownlint clean
(/tmp/s1-make-test-1.log); e2e 17 OK, none skipped (/tmp/s1-e2e-3.log);
MEDUSA 14 files, 0 findings.

🎯 Fix: 94 %
💥 Break: 4 %
💸 Cost: 100 %
⏱️ Time: –
🛡️ Security: 100 %
🏆 Score: 90
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 13:37:02 +00:00

78 lines
4.1 KiB
Python

from __future__ import annotations
import ast
import datetime
import json
import os
import unittest
from hub_case import HubCase
from hub_support import HUB, PAGE
GENERATED = datetime.datetime(2026, 11, 6, 12, tzinfo=datetime.UTC).timestamp()
def starts_git(path) -> list[int]:
lines = []
for node in ast.walk(ast.parse(path.read_text(encoding="utf-8"))):
if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value.split(" ")[0] in ("git", "/usr/bin/git"):
lines.append(node.lineno)
return lines
class TestHubTree(HubCase):
def test_tree_serves_the_pkgmgr_file_with_workers_and_reviews(self):
root = self.write_tree()
demo, feature = f"{root}/repos/github.com/me/demo", f"{root}/worktrees/github.com/me/demo/feature"
self.write_council({"rasci.json": {"work/feature": {"title": "F", "status": "running", "worker": "Skills session", "workdir": feature}}})
self.tool("review_request", {**self.arguments("feat: tree", "", self.page("tree.html", PAGE)), "worktree": feature, "branch": "feature"})
status, _, body = self.get("/api/tree")
self.assertEqual(status, 200, body)
data = json.loads(body)
repos = {entry["path"]: entry for entry in data["repos"]}
self.assertEqual((repos[demo]["name"], repos[demo]["known"], repos[f"{root}/outside/elsewhere"]["known"]), ("github.com/me/demo", True, False))
self.assertEqual((data["scanned"], data["delete_ready"], data["delete_reason"]), (GENERATED, False, "Secret Service locked"))
branches = {entry["name"]: entry for entry in repos[demo]["branches"]}
main = branches["main"]
self.assertEqual((main["upstream"], main["ahead"], main["behind"], main["gone"], main["state"]), ("origin/main", 1, 0, False, "protected"))
self.assertEqual((branches["feature"]["state"], branches["feature"]["reason"]), ("dirty", "dirty: 1 paths"))
trees = [(tree["path"], tree["dirty"], tree["untracked"], tree["workers"], tree["review"]) for tree in main["worktrees"] + branches["feature"]["worktrees"]]
self.assertEqual(trees, [(demo, False, 0, [], None), (feature, True, 1, ["Skills"], {"status": "pending", "gate": "pass", "title": "feat: tree"})])
self.assertEqual([(tree["path"], tree["workers"]) for tree in repos[demo]["detached"]], [(f"{root}/worktrees/github.com/me/demo/detached", [])])
def test_tree_reads_the_file_again_once_it_changes(self):
self.write_tree()
self.assertEqual(len(json.loads(self.get("/api/tree")[2])["repos"]), 2)
data = json.loads(self.tree_file.read_text(encoding="utf-8"))
data["repos"] = data["repos"][:1]
self.tree_file.write_text(json.dumps(data), encoding="utf-8")
self.assertEqual(len(json.loads(self.get("/api/tree")[2])["repos"]), 1)
def test_a_missing_or_broken_file_answers_with_the_reason(self):
status, _, body = self.get("/api/tree")
self.assertEqual(status, 200, body)
self.assertEqual((json.loads(body)["repos"], json.loads(body)["scanned"]), ([], None))
for words in ("no pkgmgr tree file", "`pkgmgr branch tree`", "release pkgmgr"):
with self.subTest(words=words):
self.assertIn(words, json.loads(body)["error"])
self.tree_file.write_text('{"repos": [{"path": "/x"}]}', encoding="utf-8")
self.assertIn("unreadable", json.loads(self.get("/api/tree")[2])["error"])
def test_the_state_poll_beats_the_heartbeat_the_tree_timer_waits_for(self):
heartbeat = self.state / "tree-heartbeat"
self.assertFalse(heartbeat.exists())
self.snapshot()
self.assertTrue(heartbeat.is_file())
os.utime(heartbeat, (0, 0))
self.snapshot()
self.assertGreater(heartbeat.stat().st_mtime, 0)
def test_no_module_of_the_orchestrator_starts_git(self):
sources = [HUB / "server.py", *sorted((HUB / "kvb_orchestrator").glob("*.py"))]
found = {path.name: starts_git(path) for path in sources}
self.assertEqual({name: lines for name, lines in found.items() if lines}, {})
if __name__ == "__main__":
unittest.main()