refactor(supervise): drop obsolete queue helpers (archive_proposal, wait_for_response, audit_dir/path)
tracker-policy-pr / check-pr (pull_request) Successful in 10s
test / integration-docker (pull_request) Successful in 17s
lint / lint (push) Failing after 59s
test / unit (pull_request) Successful in 2m3s
test / integration-firecracker (pull_request) Successful in 3m28s
test / coverage (pull_request) Successful in 16s
test / publish-infra (pull_request) Has been skipped

Remove the supervise queue helpers that no live path uses anymore — they're
mechanisms the PRD-0070 / idempotent-poll migration superseded:

  - archive_proposal (+ QueueStore.archive_proposal): the poll stopped archiving
    on read (#469 review), so single-proposal archiving has no caller; decided
    proposals drop off the pending list via their response row and are reaped in
    bulk by archive_all_proposals on teardown/reconcile.
  - wait_for_response: the data plane polls the queue over the control-plane RPC
    now, so nothing block-waits on it.
  - audit_dir / audit_log_path: file-based audit paths, replaced by the SQLite
    AuditStore.

Also drops the tests that covered those obsolete helpers. Kept the prod-unused
read accessors list_pending_proposals and read_audit_entries: those aren't dead
mechanisms — they're the read side of live write paths that many tests use to
verify real queue/audit behavior.

Full unit suite green (2243).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 14:36:32 -04:00
parent 3e2cbcab88
commit 27a122e24b
5 changed files with 1 additions and 157 deletions
@@ -167,25 +167,6 @@ class QueueStore(DbStore):
raise FileNotFoundError(proposal_id) raise FileNotFoundError(proposal_id)
return self._row_to_response(row) return self._row_to_response(row)
def archive_proposal(self, proposal_id: str) -> None:
if not self.db_path.is_file():
return
with self._connection() as conn:
conn.execute(
"""
UPDATE supervise_proposals SET archived = 1
WHERE queue_key = ? AND id = ?
""",
(self.queue_key, proposal_id),
)
conn.execute(
"""
UPDATE supervise_responses SET archived = 1
WHERE queue_key = ? AND proposal_id = ?
""",
(self.queue_key, proposal_id),
)
def archive_all(self) -> None: def archive_all(self) -> None:
"""Archive every proposal + response for this queue key. Used to reap a """Archive every proposal + response for this queue key. Used to reap a
bottle's supervise records when it is torn down or reconciled away — bottle's supervise records when it is torn down or reconciled away —
@@ -20,9 +20,6 @@ from ...supervisor.plan import SupervisePlan
from ..store.store_manager import StoreManager from ..store.store_manager import StoreManager
from .queue import ( from .queue import (
archive_all_proposals, archive_all_proposals,
archive_proposal,
audit_dir,
audit_log_path,
list_all_pending_proposals, list_all_pending_proposals,
list_pending_proposals, list_pending_proposals,
read_audit_entries, read_audit_entries,
@@ -30,7 +27,6 @@ from .queue import (
read_response, read_response,
render_diff, render_diff,
sha256_hex, sha256_hex,
wait_for_response,
write_audit_entry, write_audit_entry,
write_proposal, write_proposal,
write_response, write_response,
+1 -49
View File
@@ -11,31 +11,13 @@ from __future__ import annotations
import difflib import difflib
import hashlib import hashlib
import time
from pathlib import Path from pathlib import Path
from ...supervisor.types import ( from ...supervisor.types import AuditEntry, Proposal, Response
AuditEntry,
DEFAULT_POLL_INTERVAL_SEC,
Proposal,
Response,
)
from ...paths import bot_bottle_root
from ..store.queue_store import QueueStore from ..store.queue_store import QueueStore
from ...store.audit_store import AuditStore from ...store.audit_store import AuditStore
# --- Paths -----------------------------------------------------------------
def audit_dir() -> Path:
return bot_bottle_root() / "audit"
def audit_log_path(component: str, slug: str) -> Path:
return audit_dir() / f"{component}-{slug}.log"
# --- Queue I/O ------------------------------------------------------------- # --- Queue I/O -------------------------------------------------------------
@@ -69,36 +51,6 @@ def read_response(bottle_slug: str, proposal_id: str) -> Response:
return QueueStore(bottle_slug).read_response(proposal_id) return QueueStore(bottle_slug).read_response(proposal_id)
def wait_for_response(
bottle_slug: str,
proposal_id: str,
*,
poll_interval: float = DEFAULT_POLL_INTERVAL_SEC,
deadline: float | None = None,
) -> Response:
"""Block until a response file appears for `proposal_id`, then return it.
`deadline` is an absolute time.monotonic() value after which the wait raises
TimeoutError. None waits forever — the natural shape, since the operator's
response time is unbounded.
Polls SQLite so the implementation stays portable and stdlib-only."""
store = QueueStore(bottle_slug)
while True:
try:
return store.read_response(proposal_id)
except FileNotFoundError:
pass
if deadline is not None and time.monotonic() >= deadline:
raise TimeoutError(f"no response for proposal {proposal_id!r}")
time.sleep(poll_interval)
def archive_proposal(bottle_slug: str, proposal_id: str) -> None:
"""Mark both proposal and response rows processed.
Idempotent — missing rows are silently skipped."""
QueueStore(bottle_slug).archive_proposal(proposal_id)
def archive_all_proposals(bottle_slug: str) -> None: def archive_all_proposals(bottle_slug: str) -> None:
"""Archive every proposal + response for `bottle_slug` (bottle teardown / """Archive every proposal + response for `bottle_slug` (bottle teardown /
reconcile cleanup). Idempotent.""" reconcile cleanup). Idempotent."""
-46
View File
@@ -1,8 +1,6 @@
"""Unit: supervise queue + audit log + diff helpers (PRD 0013).""" """Unit: supervise queue + audit log + diff helpers (PRD 0013)."""
import tempfile import tempfile
import threading
import time
import unittest import unittest
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@@ -21,14 +19,12 @@ from bot_bottle.orchestrator.supervisor import (
STATUS_REJECTED, STATUS_REJECTED,
TOOL_EGRESS_ALLOW, TOOL_EGRESS_ALLOW,
TOOL_GITLEAKS_ALLOW, TOOL_GITLEAKS_ALLOW,
archive_proposal,
list_pending_proposals, list_pending_proposals,
read_audit_entries, read_audit_entries,
read_proposal, read_proposal,
read_response, read_response,
render_diff, render_diff,
sha256_hex, sha256_hex,
wait_for_response,
write_audit_entry, write_audit_entry,
write_proposal, write_proposal,
write_response, write_response,
@@ -173,48 +169,6 @@ class TestQueueIO(unittest.TestCase):
write_response(self.slug, r) write_response(self.slug, r)
self.assertEqual(r, read_response(self.slug, "xyz")) self.assertEqual(r, read_response(self.slug, "xyz"))
def test_wait_for_response_returns_when_file_appears(self):
p = _proposal()
write_proposal(p)
def write_after_delay():
time.sleep(0.05)
write_response(self.slug, Response(
proposal_id=p.id, status=STATUS_APPROVED, notes="ok",
))
t = threading.Thread(target=write_after_delay)
t.start()
try:
r = wait_for_response(self.slug, p.id, poll_interval=0.01)
finally:
t.join()
self.assertEqual(STATUS_APPROVED, r.status)
self.assertEqual("ok", r.notes)
def test_wait_for_response_times_out(self):
deadline = time.monotonic() + 0.05
with self.assertRaises(TimeoutError):
wait_for_response(
self.slug, "never",
poll_interval=0.01, deadline=deadline,
)
def test_archive_proposal_hides_rows(self):
p = _proposal()
write_proposal(p)
write_response(self.slug, Response(
proposal_id=p.id, status=STATUS_APPROVED, notes="",
))
archive_proposal(self.slug, p.id)
self.assertEqual([], list_pending_proposals(self.slug))
with self.assertRaises(FileNotFoundError):
read_response(self.slug, p.id)
def test_archive_is_idempotent_on_missing_files(self):
# Should not raise.
archive_proposal(self.slug, "nope")
class TestAuditLog(unittest.TestCase): class TestAuditLog(unittest.TestCase):
def setUp(self): def setUp(self):
-39
View File
@@ -5,7 +5,6 @@ fallback paths."""
from __future__ import annotations from __future__ import annotations
import tempfile import tempfile
import time
import unittest import unittest
from pathlib import Path from pathlib import Path
from unittest.mock import patch from unittest.mock import patch
@@ -23,7 +22,6 @@ from bot_bottle.orchestrator.supervisor import (
read_audit_entries, read_audit_entries,
read_proposal, read_proposal,
read_response, read_response,
wait_for_response,
write_audit_entry, write_audit_entry,
) )
@@ -81,22 +79,6 @@ class TestReadMalformed(unittest.TestCase):
self.assertEqual([], list_pending_proposals("slug")) self.assertEqual([], list_pending_proposals("slug"))
class TestWaitForResponse(unittest.TestCase):
def test_missing_response_times_out(self) -> None:
with tempfile.TemporaryDirectory() as d:
with patch.dict("os.environ", {"HOME": d}):
QueueStore("slug").migrate()
with self.assertRaises(TimeoutError):
wait_for_response("slug", "p", deadline=time.monotonic())
def test_empty_db_response_does_not_count(self) -> None:
with tempfile.TemporaryDirectory() as d:
with patch.dict("os.environ", {"HOME": d}):
QueueStore("slug").migrate()
with self.assertRaises(TimeoutError):
wait_for_response("slug", "p", deadline=time.monotonic())
class TestReadAuditEntries(unittest.TestCase): class TestReadAuditEntries(unittest.TestCase):
def test_missing_log_returns_empty(self) -> None: def test_missing_log_returns_empty(self) -> None:
with tempfile.TemporaryDirectory() as home, \ with tempfile.TemporaryDirectory() as home, \
@@ -129,19 +111,6 @@ class TestReadAuditEntries(unittest.TestCase):
self.assertEqual(1, len(entries)) self.assertEqual(1, len(entries))
self.assertEqual("approve", entries[0].operator_action) self.assertEqual("approve", entries[0].operator_action)
def test_legacy_audit_log_file_does_not_count(self) -> None:
with tempfile.TemporaryDirectory() as home, \
patch.dict("os.environ", {"HOME": home}):
path = supervise.audit_log_path("egress", "slug")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
'{"timestamp": "t", "bottle_slug": "slug", "component": "egress",'
' "operator_action": "approve", "operator_notes": "",'
' "justification": "", "diff": ""}\n'
)
entries = read_audit_entries("egress", "slug")
self.assertEqual([], entries)
class TestStoreGuardBranches(unittest.TestCase): class TestStoreGuardBranches(unittest.TestCase):
"""Direct QueueStore / AuditStore construction and early-return guard branches.""" """Direct QueueStore / AuditStore construction and early-return guard branches."""
@@ -170,14 +139,6 @@ class TestStoreGuardBranches(unittest.TestCase):
db.unlink() db.unlink()
self.assertEqual([], store.list_all_pending_proposals()) self.assertEqual([], store.list_all_pending_proposals())
def test_queue_store_missing_db_archive_is_noop(self):
with tempfile.TemporaryDirectory() as d:
db = Path(d) / "q.db"
store = QueueStore("key", db_path=db)
store.migrate()
db.unlink()
store.archive_proposal("anything") # must not raise
def test_queue_store_chmod_oserror_is_swallowed(self): def test_queue_store_chmod_oserror_is_swallowed(self):
with tempfile.TemporaryDirectory() as d: with tempfile.TemporaryDirectory() as d:
db = Path(d) / "q.db" db = Path(d) / "q.db"