mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-07-23 16:49:46 -07:00
update
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,366 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SCRIPT = Path(__file__).parents[1] / "scripts" / "peer_consult.py"
|
||||
|
||||
|
||||
def load_broker():
|
||||
if not SCRIPT.exists():
|
||||
raise AssertionError("peer_consult.py is missing")
|
||||
spec = importlib.util.spec_from_file_location("peer_consult", SCRIPT)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class RoutingTests(unittest.TestCase):
|
||||
def test_routes_approved_models_and_effort(self):
|
||||
broker = load_broker()
|
||||
|
||||
cases = {
|
||||
("claude", "quick", False): ("codex", "gpt-5.6-terra", "medium"),
|
||||
("claude", "quick", True): ("codex", "gpt-5.6-terra", "high"),
|
||||
("claude", "standard", False): ("codex", "gpt-5.6-sol", "medium"),
|
||||
("claude", "deep", False): ("codex", "gpt-5.6-sol", "high"),
|
||||
("codex", "quick", False): ("claude", "claude-sonnet-4-6", "medium"),
|
||||
("codex", "quick", True): ("claude", "claude-sonnet-5", "high"),
|
||||
("codex", "standard", False): ("claude", "claude-opus-4-8", "medium"),
|
||||
("codex", "deep", False): ("claude", "claude-opus-4-8", "high"),
|
||||
}
|
||||
|
||||
for inputs, expected in cases.items():
|
||||
with self.subTest(inputs=inputs):
|
||||
route = broker.select_route(*inputs)
|
||||
self.assertEqual(
|
||||
(route.provider, route.model, route.effort),
|
||||
expected,
|
||||
)
|
||||
|
||||
def test_rejects_unknown_complexity(self):
|
||||
broker = load_broker()
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Unknown complexity"):
|
||||
broker.select_route("codex", "extreme")
|
||||
|
||||
def test_accepts_only_target_provider_model_overrides(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(
|
||||
hasattr(broker, "apply_model_override"),
|
||||
"model override validation is missing",
|
||||
)
|
||||
codex_route = broker.apply_model_override(
|
||||
broker.select_route("claude", "quick"),
|
||||
"gpt-5.6-sol",
|
||||
)
|
||||
claude_route = broker.apply_model_override(
|
||||
broker.select_route("codex", "quick"),
|
||||
"claude-opus-4-8",
|
||||
)
|
||||
|
||||
self.assertEqual(codex_route.model, "gpt-5.6-sol")
|
||||
self.assertEqual(claude_route.model, "claude-opus-4-8")
|
||||
|
||||
def test_rejects_disallowed_or_wrong_provider_model_overrides(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(
|
||||
hasattr(broker, "apply_model_override"),
|
||||
"model override validation is missing",
|
||||
)
|
||||
route = broker.select_route("claude", "quick")
|
||||
|
||||
for model in (
|
||||
"gpt-5.6-luna",
|
||||
"gpt-5.5",
|
||||
"claude-sonnet-5",
|
||||
"claude-haiku-4-5",
|
||||
):
|
||||
with self.subTest(model=model):
|
||||
with self.assertRaisesRegex(ValueError, "not allowed"):
|
||||
broker.apply_model_override(route, model)
|
||||
|
||||
|
||||
class CallerDetectionTests(unittest.TestCase):
|
||||
def test_detects_codex_and_claude_environments(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(hasattr(broker, "detect_caller"), "caller detection is missing")
|
||||
|
||||
self.assertEqual(broker.detect_caller({"CODEX_THREAD_ID": "abc"}), "codex")
|
||||
self.assertEqual(broker.detect_caller({"CLAUDECODE": "1"}), "claude")
|
||||
self.assertEqual(
|
||||
broker.detect_caller({"CLAUDE_CODE_ENTRYPOINT": "cli"}),
|
||||
"claude",
|
||||
)
|
||||
|
||||
def test_rejects_missing_or_ambiguous_caller(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(hasattr(broker, "detect_caller"), "caller detection is missing")
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "Cannot detect"):
|
||||
broker.detect_caller({})
|
||||
with self.assertRaisesRegex(ValueError, "Ambiguous"):
|
||||
broker.detect_caller({"CODEX_THREAD_ID": "abc", "CLAUDECODE": "1"})
|
||||
|
||||
|
||||
class CommandTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.broker = load_broker()
|
||||
self.repo = Path("/tmp/example-repo")
|
||||
self.schema = Path(__file__).parents[1] / "scripts" / "memo.schema.json"
|
||||
|
||||
def test_builds_read_only_ephemeral_codex_command(self):
|
||||
self.assertTrue(hasattr(self.broker, "build_command"), "command builder is missing")
|
||||
route = self.broker.select_route("claude", "standard")
|
||||
command = self.broker.build_command(
|
||||
route,
|
||||
self.repo,
|
||||
self.schema,
|
||||
Path("/tmp/peer-output.json"),
|
||||
)
|
||||
|
||||
self.assertEqual(command[:2], ["codex", "exec"])
|
||||
self.assertIn("gpt-5.6-sol", command)
|
||||
self.assertIn('model_reasoning_effort="medium"', command)
|
||||
self.assertIn('approval_policy="never"', command)
|
||||
self.assertIn("read-only", command)
|
||||
self.assertIn("--ephemeral", command)
|
||||
self.assertIn("--ignore-user-config", command)
|
||||
self.assertIn("--json", command)
|
||||
self.assertIn(str(self.schema), command)
|
||||
self.assertIn("/tmp/peer-output.json", command)
|
||||
self.assertEqual(command[-1], "-")
|
||||
self.assertFalse(any("dangerously" in arg for arg in command))
|
||||
|
||||
def test_builds_safe_nonpersistent_claude_command(self):
|
||||
self.assertTrue(hasattr(self.broker, "build_command"), "command builder is missing")
|
||||
route = self.broker.select_route("codex", "deep")
|
||||
command = self.broker.build_command(route, self.repo, self.schema)
|
||||
|
||||
self.assertEqual(command[0], "claude")
|
||||
self.assertIn("--print", command)
|
||||
self.assertIn("claude-opus-4-8", command)
|
||||
self.assertIn("high", command)
|
||||
self.assertIn("--safe-mode", command)
|
||||
self.assertIn("--permission-mode", command)
|
||||
self.assertIn("plan", command)
|
||||
self.assertIn("--no-session-persistence", command)
|
||||
self.assertIn("Read,Glob,Grep", command)
|
||||
self.assertNotIn("Bash", command)
|
||||
schema_arg = command[command.index("--json-schema") + 1]
|
||||
self.assertEqual(json.loads(schema_arg)["additionalProperties"], False)
|
||||
|
||||
def test_peer_prompt_forbids_changes_and_requires_evidence(self):
|
||||
self.assertTrue(hasattr(self.broker, "build_peer_prompt"), "peer prompt builder is missing")
|
||||
route = self.broker.select_route("codex", "standard")
|
||||
prompt = self.broker.build_peer_prompt("Should we split this service?", route)
|
||||
|
||||
for phrase in (
|
||||
"read-only",
|
||||
"Do not modify",
|
||||
"Do not implement",
|
||||
"Do not delegate",
|
||||
"repository evidence",
|
||||
"Should we split this service?",
|
||||
):
|
||||
with self.subTest(phrase=phrase):
|
||||
self.assertIn(phrase, prompt)
|
||||
|
||||
|
||||
class MemoTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.broker = load_broker()
|
||||
self.memo = {
|
||||
"verdict": "Split only after measuring coupling.",
|
||||
"evidence": ["src/service.py:42 owns both workflows."],
|
||||
"risks": ["A premature split adds coordination overhead."],
|
||||
"alternatives": ["Extract an internal module first."],
|
||||
"recommendation": "Instrument boundaries, then reassess.",
|
||||
"confidence": "medium",
|
||||
}
|
||||
|
||||
def test_validates_and_renders_standardized_memo(self):
|
||||
self.assertTrue(hasattr(self.broker, "validate_memo"), "memo validation is missing")
|
||||
self.assertTrue(hasattr(self.broker, "render_memo"), "memo rendering is missing")
|
||||
route = self.broker.select_route("codex", "standard")
|
||||
|
||||
validated = self.broker.validate_memo(self.memo)
|
||||
rendered = self.broker.render_memo(validated, route)
|
||||
|
||||
for heading in (
|
||||
"## Peer consultation",
|
||||
"### Verdict",
|
||||
"### Evidence",
|
||||
"### Risks",
|
||||
"### Alternatives",
|
||||
"### Recommendation",
|
||||
"### Confidence",
|
||||
):
|
||||
with self.subTest(heading=heading):
|
||||
self.assertIn(heading, rendered)
|
||||
self.assertIn("Claude · claude-opus-4-8 · medium", rendered)
|
||||
|
||||
def test_rejects_malformed_memo(self):
|
||||
self.assertTrue(hasattr(self.broker, "validate_memo"), "memo validation is missing")
|
||||
|
||||
malformed = dict(self.memo, confidence="certain", surprise="extra")
|
||||
with self.assertRaisesRegex(ValueError, "memo"):
|
||||
self.broker.validate_memo(malformed)
|
||||
|
||||
def test_extracts_claude_structured_output(self):
|
||||
self.assertTrue(
|
||||
hasattr(self.broker, "parse_claude_output"),
|
||||
"Claude output parser is missing",
|
||||
)
|
||||
envelope = json.dumps({"type": "result", "structured_output": self.memo})
|
||||
|
||||
self.assertEqual(self.broker.parse_claude_output(envelope), self.memo)
|
||||
|
||||
|
||||
class CliTests(unittest.TestCase):
|
||||
def test_dry_run_prints_route_command_and_prompt_without_provider_call(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(hasattr(broker, "main"), "CLI entrypoint is missing")
|
||||
with tempfile.TemporaryDirectory() as repo:
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SCRIPT),
|
||||
"--caller",
|
||||
"codex",
|
||||
"--complexity",
|
||||
"standard",
|
||||
"--repo",
|
||||
repo,
|
||||
"--dry-run",
|
||||
],
|
||||
input="Should we split this service?",
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
payload = json.loads(result.stdout)
|
||||
self.assertEqual(payload["provider"], "claude")
|
||||
self.assertEqual(payload["model"], "claude-opus-4-8")
|
||||
self.assertEqual(payload["effort"], "medium")
|
||||
self.assertIn("--safe-mode", payload["command"])
|
||||
self.assertIn("Should we split this service?", payload["prompt"])
|
||||
|
||||
def test_cli_rejects_luna_override_before_execution(self):
|
||||
broker = load_broker()
|
||||
self.assertTrue(hasattr(broker, "main"), "CLI entrypoint is missing")
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(SCRIPT),
|
||||
"--caller",
|
||||
"claude",
|
||||
"--complexity",
|
||||
"quick",
|
||||
"--model",
|
||||
"gpt-5.6-luna",
|
||||
"--dry-run",
|
||||
],
|
||||
input="Review this choice.",
|
||||
text=True,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("not allowed", result.stderr)
|
||||
|
||||
|
||||
class ExecutionTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.broker = load_broker()
|
||||
self.memo = {
|
||||
"verdict": "Keep the boundary for now.",
|
||||
"evidence": ["src/core.py:10 has one caller."],
|
||||
"risks": ["The caller may grow."],
|
||||
"alternatives": ["Revisit after instrumentation."],
|
||||
"recommendation": "Measure first.",
|
||||
"confidence": "high",
|
||||
}
|
||||
|
||||
def _write_executable(self, path, source):
|
||||
path.write_text(source)
|
||||
path.chmod(0o755)
|
||||
|
||||
def test_runs_claude_and_extracts_structured_memo(self):
|
||||
self.assertTrue(
|
||||
hasattr(self.broker, "run_consultation"),
|
||||
"consultation runner is missing",
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
root_path = Path(root)
|
||||
bin_dir = root_path / "bin"
|
||||
repo = root_path / "repo"
|
||||
bin_dir.mkdir()
|
||||
repo.mkdir()
|
||||
envelope = json.dumps({"structured_output": self.memo})
|
||||
self._write_executable(
|
||||
bin_dir / "claude",
|
||||
"#!/bin/sh\nread request\nprintf '%s' '" + envelope + "'\n",
|
||||
)
|
||||
env = dict(os.environ, PATH=f"{bin_dir}:{os.environ['PATH']}")
|
||||
route = self.broker.select_route("codex", "standard")
|
||||
|
||||
memo = self.broker.run_consultation(
|
||||
route,
|
||||
repo,
|
||||
"Review this design.",
|
||||
SCRIPT.with_name("memo.schema.json"),
|
||||
env=env,
|
||||
)
|
||||
|
||||
self.assertEqual(memo, self.memo)
|
||||
|
||||
def test_runs_codex_and_reads_temporary_structured_memo(self):
|
||||
self.assertTrue(
|
||||
hasattr(self.broker, "run_consultation"),
|
||||
"consultation runner is missing",
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
root_path = Path(root)
|
||||
bin_dir = root_path / "bin"
|
||||
repo = root_path / "repo"
|
||||
bin_dir.mkdir()
|
||||
repo.mkdir()
|
||||
memo_json = json.dumps(self.memo)
|
||||
self._write_executable(
|
||||
bin_dir / "codex",
|
||||
"#!/bin/sh\n"
|
||||
"while [ \"$#\" -gt 0 ]; do\n"
|
||||
" if [ \"$1\" = \"--output-last-message\" ]; then\n"
|
||||
" shift\n"
|
||||
f" printf '%s' '{memo_json}' > \"$1\"\n"
|
||||
" exit 0\n"
|
||||
" fi\n"
|
||||
" shift\n"
|
||||
"done\n"
|
||||
"exit 9\n",
|
||||
)
|
||||
env = dict(os.environ, PATH=f"{bin_dir}:{os.environ['PATH']}")
|
||||
route = self.broker.select_route("claude", "standard")
|
||||
|
||||
memo = self.broker.run_consultation(
|
||||
route,
|
||||
repo,
|
||||
"Review this design.",
|
||||
SCRIPT.with_name("memo.schema.json"),
|
||||
env=env,
|
||||
)
|
||||
|
||||
self.assertEqual(memo, self.memo)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user