"""P0.3 写路径:dispatch/flow 落 sqlite run+events,revise 带 diff,同输入可重跑 diff。""" from __future__ import annotations import json import pathlib import subprocess import sys import tempfile import unittest ROOT = next( parent for parent in (pathlib.Path(__file__).resolve().parent, *pathlib.Path(__file__).resolve().parents) if (parent / "AGENTS.md").is_file() and (parent / ".git").exists() ) if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) DISPATCH_SCRIPTS = ( ROOT / "muse" / "lifecycle" / "dispatch" / "skills" / "dispatch-agent-task" / "scripts" ) EVIDENCE_DIR = ROOT / "muse" / "authority" / "evidence" / "skills" / "record-run-evidence" / "scripts" DISPATCH_TESTS = ROOT / "tests" / "skills" / "dispatch-agent-task" for path in (DISPATCH_SCRIPTS, EVIDENCE_DIR, DISPATCH_TESTS): if str(path) not in sys.path: sys.path.insert(0, str(path)) from framework.adapters.pi.runner import ExecutionPolicy # noqa: E402 from muse.flow.dispatch import run_dispatch # noqa: E402 from muse.store import add_review, connect, get_run, list_events # noqa: E402 from test_dispatch_agent_task import ( # noqa: E402 RecordingConnect, fake_launcher, make_spec, pi_stream_lines, ) class SqliteWritePathTest(unittest.TestCase): def setUp(self) -> None: self.tmp = pathlib.Path(tempfile.mkdtemp()) self.db = self.tmp / "muse.db" self.spec = make_spec(self.tmp) def _dispatch(self, run_id: str, run_dir: pathlib.Path): return run_dispatch( self.spec, repo_root=ROOT, policy=ExecutionPolicy(provider="p", model="claude-opus-test"), run_id=run_id, run_dir=run_dir, connect_factory=RecordingConnect(), launcher=fake_launcher( pi_stream_lines('{"title":"重启","beats":["警报","分歧","决断"]}') ), trigger_source="diagnostic", sqlite_path=self.db, ) def test_write_path_run_events_revise_and_rerun_diff(self) -> None: before = subprocess.check_output(["git", "status", "--short"], cwd=ROOT, text=True) receipt, code = self._dispatch("p03-write-1", self.tmp / "run-1") self.assertEqual(code, 0, receipt) self.assertEqual(receipt["status"], "completed") row = get_run("p03-write-1", self.db) self.assertIsNotNone(row) self.assertEqual(row["kind"], "agent.planner") self.assertIn("重启", row["output_text"]) events = list_events("p03-write-1", self.db) kinds = [item["kind"] for item in events] self.assertIn("run.started", kinds) self.assertIn("run.completed", kinds) self.assertTrue(any(kind.startswith("model.") or kind == "agent.started" for kind in kinds)) with connect(self.db) as conn: mode = conn.execute("PRAGMA journal_mode").fetchone()[0] self.assertEqual(str(mode).lower(), "wal") add_review( run_id="p03-write-1", target="candidate", action="revise", reviewer="qingse", reason="钩子再紧一寸", before_text=row["output_text"], after_text='{"title":"重启","beats":["警报","摊牌","决断"]}', path=self.db, ) with connect(self.db) as conn: review = conn.execute( "SELECT * FROM reviews WHERE run_id=?", ("p03-write-1",) ).fetchone() revision = conn.execute( "SELECT * FROM revisions WHERE review_id=?", (review["id"],) ).fetchone() self.assertEqual(review["action"], "revise") self.assertNotEqual(revision["before_text"], revision["after_text"]) receipt2, code2 = self._dispatch("p03-write-2", self.tmp / "run-2") self.assertEqual(code2, 0, receipt2) replay = get_run("p03-write-2", self.db) first_input = json.loads(row["input_json"]) second_input = json.loads(replay["input_json"]) self.assertEqual(first_input["specSha256"], second_input["specSha256"]) self.assertEqual(row["output_text"], replay["output_text"]) after = subprocess.check_output(["git", "status", "--short"], cwd=ROOT, text=True) self.assertEqual(before, after) ignored = subprocess.run( ["git", "check-ignore", "-q", "data/muse.db"], cwd=ROOT, ) self.assertEqual(ignored.returncode, 0) def test_search_and_freeze_recall_share_sqlite_vectors(self) -> None: from muse.store import upsert_card freeze_scripts = ROOT / "muse" / "lifecycle" / "context" / "skills" / "freeze-context" / "scripts" search_scripts = ROOT / "muse" / "lifecycle" / "context" / "skills" / "search-knowledge" / "scripts" sys.path.insert(0, str(freeze_scripts)) sys.path.insert(0, str(search_scripts)) import recall from search import search_cards upsert_card( card_id="c1", kind="craft", title="递进揭示", payload={"型": "craft", "名称": "递进揭示", "一句话摘要": "逐层加压"}, embedding=[1.0, 0.0], path=self.db, ) cards = search_cards( "揭示", sqlite_path=self.db, embedder=lambda _intent: [1.0, 0.0], ) recalled = recall.recall_cards([1.0, 0.0], kind="craft", sqlite_path=self.db) self.assertEqual(cards[0]["cardId"], "c1") self.assertEqual(recalled[0]["cardId"], "c1") self.assertAlmostEqual(cards[0]["score"], 1.0) if __name__ == "__main__": unittest.main()