Files
notplanned/ventures/codex-goal/tests/test_codex_goal.py
T
claudeandClaude Sonnet 5 70b7a8bbcd Build codex-goal venture: persistent objectives for codex exec sessions
Inspired by openai/codex#26966 (closed not planned). codex_goal.py is a
standalone CLI that stores one versioned, project-keyed goal per repo
outside Codex's own sandbox, and injects it as plain user context on
every new or resumed `codex exec` run. Writes are atomic and locked,
runs never mutate the goal, Codex is spawned via argv (no shell), and
signals are forwarded for normal cancellation behavior. 16 unit tests
plus a recorded two-run + crash-recovery demo, verified on macOS and
in the project's Colima Linux container. Launch page at
ventures/codex-goal/site/index.html uses a self-contained
scoreboard/arcade identity (vendored OFL DSEG7 font, no external
requests) distinct from the other ventures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 13:37:00 -07:00

249 lines
9.4 KiB
Python

import contextlib
import io
import json
import os
import signal
import subprocess
import sys
import tempfile
import time
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, ROOT)
FAKE_CODEX = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fake_codex.py")
import codex_goal # noqa: E402
class TempHome(unittest.TestCase):
def setUp(self):
self._home_dir = tempfile.TemporaryDirectory()
self._project_dir = tempfile.TemporaryDirectory()
os.environ["CODEX_GOAL_HOME"] = self._home_dir.name
os.chmod(FAKE_CODEX, 0o755)
def tearDown(self):
os.environ.pop("CODEX_GOAL_HOME", None)
self._home_dir.cleanup()
self._project_dir.cleanup()
def run_cli(self, *argv):
return codex_goal.main(list(argv))
@property
def project(self):
return self._project_dir.name
class SetShowTests(TempHome):
def test_set_then_show_roundtrip(self):
rc = self.run_cli("set", "-C", self.project, "Ship", "the", "thing")
self.assertEqual(rc, 0)
key = codex_goal.project_key(self.project)
state = codex_goal.load_state(codex_goal.goal_path(codex_goal.state_home(), key))
self.assertEqual(state["current"]["text"], "Ship the thing")
self.assertEqual(state["history"], [])
def test_show_without_goal_fails_clearly(self):
rc = self.run_cli("show", "-C", self.project)
self.assertEqual(rc, 1)
def test_second_set_pushes_history(self):
self.run_cli("set", "-C", self.project, "first goal")
self.run_cli("set", "-C", self.project, "second goal")
key = codex_goal.project_key(self.project)
state = codex_goal.load_state(codex_goal.goal_path(codex_goal.state_home(), key))
self.assertEqual(state["current"]["text"], "second goal")
self.assertEqual(len(state["history"]), 1)
self.assertEqual(state["history"][0]["text"], "first goal")
def test_empty_goal_rejected(self):
rc = self.run_cli("set", "-C", self.project, " ")
self.assertEqual(rc, 2)
def test_two_projects_get_independent_state(self):
with tempfile.TemporaryDirectory() as other:
self.run_cli("set", "-C", self.project, "goal A")
self.run_cli("set", "-C", other, "goal B")
key_a = codex_goal.project_key(self.project)
key_b = codex_goal.project_key(other)
self.assertNotEqual(key_a, key_b)
state_a = codex_goal.load_state(codex_goal.goal_path(codex_goal.state_home(), key_a))
state_b = codex_goal.load_state(codex_goal.goal_path(codex_goal.state_home(), key_b))
self.assertEqual(state_a["current"]["text"], "goal A")
self.assertEqual(state_b["current"]["text"], "goal B")
def test_state_lives_outside_project_dir(self):
self.run_cli("set", "-C", self.project, "stay external")
key = codex_goal.project_key(self.project)
path = codex_goal.goal_path(codex_goal.state_home(), key)
self.assertFalse(path.startswith(os.path.realpath(self.project)))
self.assertTrue(path.startswith(self._home_dir.name))
class RunDryRunTests(TempHome):
def test_run_without_goal_errors_and_does_not_spawn(self):
rc = self.run_cli("run", "-C", self.project, "--dry-run")
self.assertEqual(rc, 2)
def test_dry_run_new_session_injects_goal(self):
self.run_cli("set", "-C", self.project, "win the game")
rc = self.run_cli("run", "-C", self.project, "--codex-bin", "codex", "--dry-run")
self.assertEqual(rc, 0)
def test_dry_run_resume_last_builds_resume_argv(self):
self.run_cli("set", "-C", self.project, "resume goal")
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
rc = self.run_cli("run", "-C", self.project, "--last", "--dry-run")
self.assertEqual(rc, 0)
payload = json.loads(buf.getvalue())
self.assertEqual(payload["mode"], "resume")
self.assertIn("resume", payload["argv"])
self.assertIn("--last", payload["argv"])
def test_shell_metacharacters_survive_as_one_argv_item(self):
goal = "ship it; rm -rf / #not a shell command"
self.run_cli("set", "-C", self.project, goal)
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
self.run_cli("run", "-C", self.project, "--dry-run")
payload = json.loads(buf.getvalue())
self.assertIn(goal, payload["argv"][-1])
# the dangerous text is one argv element, never concatenated into a command string
self.assertEqual(sum(goal in item for item in payload["argv"]), 1)
class RunSpawnTests(TempHome):
def setUp(self):
super().setUp()
self._record = tempfile.NamedTemporaryFile(delete=False, suffix=".json")
self._record.close()
os.environ["FAKE_CODEX_RECORD"] = self._record.name
def tearDown(self):
os.environ.pop("FAKE_CODEX_RECORD", None)
os.environ.pop("FAKE_CODEX_EXIT", None)
os.environ.pop("FAKE_CODEX_SLEEP", None)
with contextlib.suppress(OSError):
os.remove(self._record.name)
super().tearDown()
def test_run_spawns_fake_codex_injects_goal_and_logs(self):
self.run_cli("set", "-C", self.project, "beat the deadline")
os.environ["FAKE_CODEX_EXIT"] = "0"
rc = self.run_cli("run", "-C", self.project, "--codex-bin", FAKE_CODEX)
self.assertEqual(rc, 0)
with open(self._record.name) as fh:
recorded = json.load(fh)
self.assertEqual(recorded["argv"][0], "exec")
self.assertIn("beat the deadline", recorded["argv"][-1])
self.assertIn(codex_goal.INJECT_HEADER, recorded["argv"][-1])
key = codex_goal.project_key(self.project)
log_path = codex_goal.runlog_path(codex_goal.state_home(), key)
with open(log_path) as fh:
lines = [json.loads(l) for l in fh if l.strip()]
self.assertEqual(len(lines), 1)
self.assertEqual(lines[0]["mode"], "new")
self.assertEqual(lines[0]["exit_code"], 0)
def test_run_preserves_nonzero_exit_status(self):
self.run_cli("set", "-C", self.project, "goal")
os.environ["FAKE_CODEX_EXIT"] = "17"
rc = self.run_cli("run", "-C", self.project, "--codex-bin", FAKE_CODEX)
self.assertEqual(rc, 17)
def test_two_runs_both_see_the_same_persisted_goal(self):
self.run_cli("set", "-C", self.project, "persist across sessions")
os.environ["FAKE_CODEX_EXIT"] = "0"
self.run_cli("run", "-C", self.project, "--codex-bin", FAKE_CODEX)
with open(self._record.name) as fh:
first = json.load(fh)
self.run_cli("run", "-C", self.project, "--last", "--codex-bin", FAKE_CODEX)
with open(self._record.name) as fh:
second = json.load(fh)
self.assertIn("persist across sessions", first["argv"][-1])
self.assertIn("persist across sessions", second["argv"][-1])
self.assertIn("resume", second["argv"])
key = codex_goal.project_key(self.project)
log_path = codex_goal.runlog_path(codex_goal.state_home(), key)
with open(log_path) as fh:
lines = [json.loads(l) for l in fh if l.strip()]
self.assertEqual(len(lines), 2)
self.assertEqual(lines[0]["mode"], "new")
self.assertEqual(lines[1]["mode"], "resume")
def test_failed_run_does_not_touch_the_goal(self):
self.run_cli("set", "-C", self.project, "do not corrupt me")
os.environ["FAKE_CODEX_EXIT"] = "1"
self.run_cli("run", "-C", self.project, "--codex-bin", FAKE_CODEX)
key = codex_goal.project_key(self.project)
state = codex_goal.load_state(codex_goal.goal_path(codex_goal.state_home(), key))
self.assertEqual(state["current"]["text"], "do not corrupt me")
def test_sigint_is_forwarded_and_cancels_child(self):
self.run_cli("set", "-C", self.project, "long running")
os.environ["FAKE_CODEX_SLEEP"] = "5"
child_argv = [
sys.executable,
os.path.join(ROOT, "codex_goal.py"),
"run",
"-C",
self.project,
"--codex-bin",
FAKE_CODEX,
]
env = dict(os.environ)
proc = subprocess.Popen(child_argv, env=env)
time.sleep(0.5)
proc.send_signal(signal.SIGINT)
rc = proc.wait(timeout=5)
self.assertEqual(rc, 130)
class AtomicWriteTests(TempHome):
def test_failed_write_leaves_existing_state_intact(self):
self.run_cli("set", "-C", self.project, "original goal")
key = codex_goal.project_key(self.project)
path = codex_goal.goal_path(codex_goal.state_home(), key)
with open(path) as fh:
before = fh.read()
with self.assertRaises(RuntimeError):
with codex_goal.locked(codex_goal.state_home(), key):
state = codex_goal.load_state(path)
state["current"]["text"] = "corrupted"
real_dump = json.dump
def exploding_dump(*a, **k):
raise RuntimeError("simulated crash mid-write")
json.dump = exploding_dump
try:
codex_goal.atomic_write_json(path, state)
finally:
json.dump = real_dump
with open(path) as fh:
after = fh.read()
self.assertEqual(before, after)
self.assertNotIn("corrupted", after)
if __name__ == "__main__":
unittest.main()