feat(orchestrator): read the pkgmgr branch tree file instead of running git

Phase 3 of requirement 001. The orchestrator no longer starts git: orchestrator/kvb_orchestrator/tree.py maps the document that pkgmgr branch tree writes (repos, branches, worktrees, detached heads, delete_ready) onto the Tree view, re-reads it when mtime or size change, and reports a missing or broken file as an empty tree with the error. compose.yml mounts the tree directory read-only (a file mount would keep the replaced inode). /api/state touches state/tree-heartbeat; kvb-orchestrator-tree.timer runs every minute and its oneshot service runs only while that heartbeat is younger than 5 minutes, with PATH=/usr/bin:/bin and the pkgmgr venv that the install builds from the newest v* tag a fully trusted GPG key signed, cloned fresh from PKGMGR_REPO because the local checkout's config is sandbox-writable.

Dialectic verdict 2026-11-06 (2 rounds, 5 skeptics, ~85 %): hardened git runner in pkgmgr, gc classification shared with the tree scan, forge facts only from an unlocked Secret Service token with a 600 s cache, atomic tree output refreshed by open/close/drop/gc, host units outside sandbox-writable paths, orchestrator reads the file only. Losing objections: extending the host runner (it would run bash -c on signed text), a new payload kind, copying the pkgmgr checkout, a tree limited to .worktrees. Operator answers: signed-tag venv, UI delete keeps volumes and is local only, ignored files beyond known caches block a delete, scan only while the page polls. Delete jobs, gc_item signing and the UI delete path follow in phase 4.

MEDUSA acceptances (kvb-think-criticize 2026-11-07, 1 skeptic): MEDUSA-MM-SCAN-058 orchestrator/server.py:64 and :80 false positives (case-insensitive substring pla in --templates and Places; --tree-file is a read-only JSON file, no model loading).

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

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-07 06:51:49 +00:00
1 parent 9ef2fb6327
commit 912a3dc07f
17 files changed
+453 -251

No files matched your search

+9 -4
View File
@@ -40,12 +40,11 @@ class HubCase(unittest.TestCase):
self.state = base / "state"
self.outside = base / "outside"
self.projects = base / "projects"
self.repos = base / "repos"
self.worktrees = base / "worktrees"
self.tree_file = base / "tree" / "branch-tree.json"
self.timers = base / "timers"
self.approvers = base / "config" / "approvers.json"
self.replay = base / "replay"
for path in (self.pages, self.state, self.outside, self.projects, self.repos, self.worktrees, self.timers):
for path in (self.pages, self.state, self.outside, self.projects, self.tree_file.parent, self.timers):
path.mkdir()
silence = mock.patch.object(server.Handler, "log_message")
silence.start()
@@ -64,7 +63,7 @@ class HubCase(unittest.TestCase):
transcripts=self.projects,
scripts=self.scripts,
templates=HUB / "templates",
places=server.Places(repos=self.repos, worktrees=self.worktrees, timers=self.timers),
places=server.Places(tree_file=self.tree_file, timers=self.timers),
model=server.Model(url="", model="", key=""),
engines=self.engines,
approvers=self.approvers,
@@ -190,6 +189,12 @@ class HubCase(unittest.TestCase):
text = content if isinstance(content, str) else json.dumps(content)
path.write_text(text, encoding="utf-8")
def write_tree(self) -> Path:
root = self.outside / "scan"
sample = (Path(__file__).parent / "branch-tree.json").read_text(encoding="utf-8")
self.tree_file.write_text(sample.replace("@ROOT@", str(root)), encoding="utf-8")
return root
def jsonl(self, rows: list[dict]) -> str:
return "".join(json.dumps(row) + "\n" for row in rows)