Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions scripts/harness_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,22 @@ def probe(harness, args):
os.killpg(proc.pid, signal.SIGKILL)
proc.wait(timeout=5)
output = stdout_path.read_text()
for line in output.splitlines():
try:
native_event = json.loads(line)
except ValueError:
continue
if not isinstance(native_event, dict):
continue
if harness == "codex-app" and native_event.get("type") == "harness.skip" and proc.returncode == 0:
row.update(
status="SKIP",
reason=native_event.get("reason", "Codex App probe precondition unavailable"),
exit_code=proc.returncode,
legs={"search": "SKIPPED", "store": "SKIPPED", "refound": "SKIPPED", "ingest": "SKIPPED"},
seconds=round(time.monotonic() - started, 2),
)
return row
if harness == "agy":
from harness_probe_native import agy_auth_flow

Expand Down
29 changes: 29 additions & 0 deletions scripts/harness_probe_native.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
from harness_probe import observe


def codex_app_running():
"""Check only the current user's process table; never start the app."""
try:
result = subprocess.run(
["pgrep", "-x", "-U", str(os.getuid()), "Codex"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
timeout=5,
)
except (OSError, subprocess.SubprocessError) as exc:
raise RuntimeError("Could not determine whether Codex App is running") from exc
if result.returncode == 0:
return True
if result.returncode == 1:
return False
raise RuntimeError("Could not determine whether Codex App is running")


def agy_auth_error(home):
"""macOS agy uses this OS-keyring item; inspect metadata only, never its value."""
try:
Expand Down Expand Up @@ -194,6 +212,17 @@ def app_probe(prompt, deadline):
home = Path(os.environ.get("CODEX_HOME", Path.home() / ".codex"))
sock = home / "app-server-control/app-server-control.sock"
if not sock.is_socket():
if not codex_app_running():
print(
json.dumps(
{
"type": "harness.skip",
"reason": "Codex App is not running; existing app context is unavailable",
}
),
flush=True,
)
return 0
raise RuntimeError("Existing app daemon control socket missing; no server started")
record = json.loads((home / "app-server-daemon/daemon.pid").read_text())
root = record["pid"]
Expand Down
20 changes: 20 additions & 0 deletions scripts/retirement_policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,16 @@
"target": "subprocess.check_output"
}
},
"6512dc7e2f2ea2c69d7ba067ca0db43314949c134d94416f8542da99e5f3d2b1": {
"non_cloud_purpose": "Local current-user process-table query for the Codex App (`pgrep -x -U <uid> Codex`) to distinguish an absent app from a running app with a missing control socket; it never launches Codex or contacts a network/model provider.",
"site": {
"call_sha256": "efbd127b18ab9e19fe8abc48b81a4b94df9e4b8923d96df8ee520e92cdd72eac",
"kind": "child_process",
"owner": "<module>.codex_app_running",
"path": "scripts/harness_probe_native.py",
"target": "subprocess.run"
}
},
"65b50f41df46124c85d1ba93a33e64e845927e9d62b571eaae05f5f8f937f2f4": {
"non_cloud_purpose": "Existing Google Drive resumable backup upload session or transport error metadata; not a model API. Site: src/brainlayer/backup_daily.py <module>.upload_file_to_drive_raw requests.Session.",
"site": {
Expand Down Expand Up @@ -4728,6 +4738,16 @@
"target": "import socket"
}
},
"d96c889adfe7312245aab3a1e9a2d5b4ada8a073764625f362237514ecdbea73": {
"non_cloud_purpose": "Local exception classification for the bounded Codex App process-presence query; subprocess errors become a harness diagnostic and do not trigger another process or network/model request.",
"site": {
"call_sha256": "cc683a58fabd9cf77fdd68bfe74e2a582706ccd4edb71e3d47fa4b6254b91911",
"kind": "callable_reference",
"owner": "<module>.codex_app_running",
"path": "scripts/harness_probe_native.py",
"target": "subprocess.SubprocessError"
}
},
"d9b55af618c33228420b271c253ad481da96c4baeaee04d48e5a56cce546bd4a": {
"non_cloud_purpose": "Existing local Python import/resource metadata or on-disk session-harvest application; no model SDK factory. Site: src/brainlayer/pipeline/style_embed.py <module> importlib.util.find_spec.",
"site": {
Expand Down
46 changes: 46 additions & 0 deletions tests/test_harness_probe_native.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,52 @@ def sendall(self, data):
assert bytes(c ^ mask[i % 4] for i, c in enumerate(pong[6:])) == b"ping"


def test_missing_app_socket_skips_only_when_codex_app_is_not_running(monkeypatch, tmp_path):
from io import StringIO

import pytest

module = load(monkeypatch)
monkeypatch.delenv("CODEX_HOME", raising=False)
monkeypatch.setattr(module.Path, "home", lambda: tmp_path)
monkeypatch.setattr(module, "codex_app_running", lambda: False)
output = StringIO()
monkeypatch.setattr(module.sys, "stdout", output)

assert module.app_probe("probe", module.time.monotonic() + 5) == 0
assert json.loads(output.getvalue()) == {
"type": "harness.skip",
"reason": "Codex App is not running; existing app context is unavailable",
}

monkeypatch.setattr(module, "codex_app_running", lambda: True)
with pytest.raises(RuntimeError, match="control socket missing"):
module.app_probe("probe", module.time.monotonic() + 5)


def test_codex_app_process_check_fails_closed(monkeypatch):
from types import SimpleNamespace

import pytest

module = load(monkeypatch)
outcomes = iter((0, 1, 2))
calls = []

def pgrep(argv, **kwargs):
calls.append((argv, kwargs))
return SimpleNamespace(returncode=next(outcomes))

monkeypatch.setattr(module.subprocess, "run", pgrep)
assert module.codex_app_running() is True
assert module.codex_app_running() is False
with pytest.raises(RuntimeError, match="Could not determine"):
module.codex_app_running()
assert all(argv == ["pgrep", "-x", "-U", str(module.os.getuid()), "Codex"] for argv, _ in calls)
assert all(kwargs["stdout"] == module.subprocess.DEVNULL for _, kwargs in calls)
assert all(kwargs["stderr"] == module.subprocess.DEVNULL for _, kwargs in calls)


def test_app_failure_interrupts_only_its_own_turn(monkeypatch, tmp_path):
import pytest

Expand Down
74 changes: 74 additions & 0 deletions tests/test_harness_probe_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,80 @@ def test_unavailable_app_surface_has_no_cli_substitution(monkeypatch, tmp_path):
assert "executable missing" in row["reason"]


def test_codex_app_skip_event_does_not_run_bridge_verification(monkeypatch, tmp_path):
import json
from types import SimpleNamespace

module = load(monkeypatch)
monkeypatch.setattr(module.shutil, "which", lambda _: "/python")

class Proc:
pid = 123
returncode = 0

def poll(self):
return 0

def spawn(*args, **kwargs):
kwargs["stdout"].write("[]\n")
kwargs["stdout"].write(
json.dumps(
{
"type": "harness.skip",
"reason": "Codex App is not running; existing app context is unavailable",
}
)
+ "\n"
)
return Proc()

monkeypatch.setattr(module.subprocess, "Popen", spawn)
monkeypatch.setattr(module, "Bridge", lambda: pytest.fail("skip must not verify a bridge"))
row = module.probe("codex-app", SimpleNamespace(output=tmp_path, timeout=5, ingest_timeout=0))
assert row["status"] == "SKIP"
assert row["reason"] == "Codex App is not running; existing app context is unavailable"
assert row["exit_code"] == 0
assert row["legs"] == {"search": "SKIPPED", "store": "SKIPPED", "refound": "SKIPPED", "ingest": "SKIPPED"}
assert module.exit_code([row]) == 1


def test_codex_app_running_without_socket_remains_failure(monkeypatch, tmp_path):
from types import SimpleNamespace

module = load(monkeypatch)
monkeypatch.setattr(module.shutil, "which", lambda _: "/python")

class Proc:
pid = 123
returncode = 1

def poll(self):
return 1

def spawn(*args, **kwargs):
kwargs["stdout"].write("")
kwargs["stderr"].write("Existing app daemon control socket missing; no server started\n")
return Proc()

monkeypatch.setattr(module.subprocess, "Popen", spawn)

class Bridge:
def __enter__(self):
return self

def __exit__(self, *_):
pass

def search(self, *_args, **_kwargs):
return {}

monkeypatch.setattr(module, "Bridge", Bridge)
row = module.probe("codex-app", SimpleNamespace(output=tmp_path, timeout=5, ingest_timeout=0))
assert row["status"] == "FAIL"
assert row["exit_code"] == 1
assert "exit 1" in row["reason"]


@pytest.mark.parametrize(("harness", "remove_override"), [("codex-cli", True), ("claude", False)])
def test_only_codex_cli_child_clears_t3_internal_originator_override(monkeypatch, tmp_path, harness, remove_override):
module = load(monkeypatch)
Expand Down
Loading