fix(orchestrator): hub gate view reads recorded parents through gates.opened

W5 moved recorded parents into a journal that gates.opened() loads into a TEMP table; the W3 hub opened gates.db raw, so parents_of() raised and the gate view stayed empty. opened() and journal() take the kept directory explicitly (CLI: watch.KEPT, hub: its /kept mount), opened() closes the connection when setup raises, and the W3 tests use opened() plus a test for a parent from the kept journal.

🎯 Fix: 95 %
💥 Break: 3 %
💸 Cost: 100 %
⏱️ Time: 100 %
🛡️ Security: 100 %
🏆 Score: 92
🚦 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-12 18:07:17 +00:00
1 parent c4b67bb639
commit c8b2e586de
4 files changed
+51 -27

No files matched your search

+1 -1
View File
@@ -115,7 +115,7 @@ class TestWorkbenchArtifacts(HubCase):
next(item for item in data["repos"][0]["items"] if item["branch"] == "feature/kept")["skip"] = None
self.tree_file.write_text(json.dumps(data), encoding="utf-8")
self.repo = f"{root}/repos/github.com/me/demo"
db = gates.connect(self.kept / "gates.db")
db = gates.opened(self.kept / "gates.db", True, self.kept)
db.executemany("INSERT INTO rules VALUES (?, ?, ?, ?, ?)", [(self.repo, "main", "commit", "test", 0.0), (self.repo, "feature/done", "pr", "e2e", 0.0)])
db.executemany("INSERT INTO parents VALUES (?, ?, ?, ?)", [(self.repo, "feature", "main", "recorded"), (self.repo, "feature/done", "feature", "derived"), (self.repo, "feature/done", "feature/kept", "operator"), (self.repo, "feature/kept", "main", "derived")])
db.commit()
+14 -5
View File
@@ -3,7 +3,6 @@ from __future__ import annotations
import importlib.util
import json
import os
import sqlite3
import subprocess
import sys
import time
@@ -131,7 +130,7 @@ class TestRunnerGates(HubCase):
self.queue([rule("commit", "quality-high", branch="main", repo=repo), parent("main", branch="feature/x", repo=repo)])
self.run_gates_runner(["--gates", str(GATES)])
self.assertEqual(self.finished(), [("done", 0), ("done", 0)])
with closing(sqlite3.connect(self.db)) as db:
with closing(gates.opened(self.db, False, self.db.parent)) as db:
self.assertEqual(gates.resolve(db, repo, "feature/x", "commit"), ("quality-high", "main"))
self.assertEqual(gates.parents_of(db, repo, "feature/x"), [("main", "operator")])
self.assertEqual([call for call in self.scripts.calls if call[0].endswith("operator.py")], [])
@@ -163,7 +162,7 @@ class TestRunnerGates(HubCase):
class TestHubGateView(HubCase):
def write_db(self, repo: str) -> Path:
db_path = self.kept / "gates.db"
db = gates.connect(db_path)
db = gates.opened(db_path, True, self.kept)
db.executemany("INSERT INTO rules VALUES (?, ?, ?, ?, ?)", [(repo, "main", "commit", "test", 0.0), (repo, "main", "merge", "quality-high", 0.0), (repo, "feature", "pr", "e2e", 0.0), (repo, "feature/kept", "merge", "quick", 0.0)])
db.executemany("INSERT INTO parents VALUES (?, ?, ?, ?)", [(repo, "feature", "main", "recorded"), (repo, "feature/done", "feature", "derived"), (repo, "feature/done", "feature/kept", "operator"), (repo, "feature/kept", "main", "derived")])
db.commit()
@@ -182,7 +181,7 @@ class TestHubGateView(HubCase):
self.assertEqual(status, 200, body)
shown = json.loads(body)["gates"]
self.assertEqual(sorted(shown[repo]), ["feature", "feature/done", "feature/kept", "main"])
with closing(sqlite3.connect(f"file:{db_path}?mode=ro", uri=True)) as db, mock.patch.object(gates, "branches", return_value={name: "0" * 40 for name in shown[repo]}):
with closing(gates.opened(db_path, False, self.kept)) as db, mock.patch.object(gates, "branches", return_value={name: "0" * 40 for name in shown[repo]}):
self.assertEqual(shown[repo], gates.show(db, [repo])[repo])
done = shown[repo]["feature/done"]
self.assertEqual(done["parents"], [{"parent": "feature", "source": "derived"}, {"parent": "feature/kept", "source": "operator"}])
@@ -190,6 +189,16 @@ class TestHubGateView(HubCase):
self.assertEqual(db_path.read_bytes(), before)
self.assertEqual(sorted(path.name for path in self.kept.iterdir()), ["gates.db"])
def test_a_parent_in_the_kept_journal_of_the_mount_shows_as_recorded(self):
root = self.write_tree()
repo = f"{root}/repos/github.com/me/demo"
self.write_db(repo)
row = {"id": "1-1", "repo": repo, "branch": "main", "parent": "feature/kept"}
(self.kept / "gate-parents.jsonl").write_text(json.dumps(row) + "\n", encoding="utf-8")
status, _, body = self.get("/api/tree")
self.assertEqual(status, 200, body)
self.assertEqual(json.loads(body)["gates"][repo]["main"]["parents"], [{"parent": "feature/kept", "source": "recorded"}])
def test_without_a_database_or_with_a_broken_one_the_tree_carries_no_gates(self):
self.write_tree()
self.assertEqual(json.loads(self.get("/api/tree")[2])["gates"], {})
@@ -199,7 +208,7 @@ class TestHubGateView(HubCase):
def test_a_stored_level_outside_levels_or_an_older_gates_py_without_parents_of_keeps_the_tree_up_without_gates(self):
root = self.write_tree()
repo = f"{root}/repos/github.com/me/demo"
db = gates.connect(self.write_db(repo))
db = gates.opened(self.write_db(repo), True, self.kept)
db.execute("UPDATE rules SET level = 'bogus' WHERE branch = 'main' AND action = 'commit'")
db.commit()
status, _, body = self.get("/api/tree")