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
8 changes: 5 additions & 3 deletions codec_cookbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
HARD SAFETY CONTRACT (enforced in serve.py):
* Cookbook only ever stops a PM2 process it started, in the `cookbook-`
namespace, after explicit confirm=True.
* It never binds to or stops the protected ports (8083/8090/8094/9223/5678)
or any port currently bound by a non-cookbook process (live `pm2 jlist`
+ socket probe at call time).
* It never binds to or stops the protected ports (the live core stack —
8083 LLM+vision / 8084 STT / 8085 TTS / 8090 dashboard / 8094 pilot-runner
/ 9222+9223 CDP / 5678 n8n; see probe.PROTECTED_PORTS) or any port
currently bound by a non-cookbook process (live `pm2 jlist` + socket probe
at call time).
* Its own serve range is 8110-8119.
* It never issues docker stop/rm, never changes an existing service's port,
never restarts a running service.
Expand Down
15 changes: 14 additions & 1 deletion codec_cookbook/probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,20 @@
log = logging.getLogger("codec_cookbook.probe")

# Protected ports Cookbook must never bind to or stop. Mirrored by serve.py.
PROTECTED_PORTS = frozenset({8083, 8090, 8094, 9223, 5678})
# Verified by lsof against the live box (2026-06-01), not assumed:
# 8083 mlx_vlm.server — Qwen3.6 LLM + UI-TARS/VLM vision
# 8084 whisper_server — STT (Whisper)
# 8085 mlx_audio.server — TTS
# 8090 codec-dashboard
# 8094 pilot-runner
# 9222 Chrome DevTools CDP (routes/cdp.py + chrome skills; on-demand)
# 9223 pilot CDP (on-demand)
# 5678 n8n
# 8081/8082 probed FREE — Qwen+vision were consolidated onto 8083, so those
# slots are vacated; not protected (nothing to fat-finger there). This static
# denylist is belt-and-suspenders: allocate_port() also skips ANY live-bound
# port at call time, and stop() refuses anything outside the cookbook- namespace.
PROTECTED_PORTS = frozenset({8083, 8084, 8085, 8090, 8094, 9222, 9223, 5678})
# Cookbook's own serve range.
SERVE_RANGE = range(8110, 8120) # 8110-8119 inclusive
OS_RESERVE_GB = 24
Expand Down
3 changes: 2 additions & 1 deletion codec_cookbook/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Structural guarantees (the whole point of Cookbook):
* launch() only ever allocates a port in 8110-8119 that a live socket probe
+ pm2 jlist + our own served.json all agree is free. Protected ports
(8083/8090/8094/9223/5678) are never in range and are skipped anyway.
(8083/8084/8085/8090/8094/9222/9223/5678 — see probe.PROTECTED_PORTS) are
never in range and are skipped anyway.
* Every process we start is named `cookbook-<id>-<port>`.
* stop() will ONLY delete a process that (a) we recorded in served.json,
(b) is named `cookbook-…`, and (c) is NOT on a protected port — and only
Expand Down
57 changes: 55 additions & 2 deletions tests/test_cookbook.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
if str(_REPO) not in sys.path:
sys.path.insert(0, str(_REPO))

from codec_cookbook import args, catalog, fit, probe, serve # noqa: E402
from codec_cookbook import args, catalog, download, fit, probe, serve # noqa: E402

# A representative Qwen3-MoE-ish config for offline KV math.
_CFG = {"num_hidden_layers": 48, "num_attention_heads": 32,
Expand Down Expand Up @@ -174,11 +174,16 @@ def served(self, tmp_path, monkeypatch):
])
return serve

@pytest.mark.parametrize("port", [8083, 8090, 8094, 9223, 5678])
@pytest.mark.parametrize("port", [8083, 8084, 8085, 8090, 8094, 9222, 9223, 5678])
def test_refuses_protected_ports(self, served, port):
r = served.stop(port, confirm=True)
assert r["status"] == "refused"

def test_protected_set_covers_live_core_stack(self):
# the live core services verified by lsof on the box must all be protected
for port in (8083, 8084, 8085, 8090, 8094, 5678):
assert port in probe.PROTECTED_PORTS, f"{port} (live core service) not protected"

def test_refuses_protected_port_even_if_in_served(self, served):
# defense-in-depth: a (hypothetical) cookbook record on a protected port
served._save_served([{"id": "x", "port": 8090, "pm2_name": "cookbook-x-8090"}])
Expand Down Expand Up @@ -264,6 +269,54 @@ def test_serve_command_is_corrected_mlx_form(self, tmp_path, monkeypatch):
assert "--port" in argv and "8112" in argv


# ── downloads (HF spawn mocked — no network in CI) ──────────────────────────
class TestDownload:
def test_start_writes_status_and_does_not_hit_network(self, tmp_path, monkeypatch):
monkeypatch.setattr(download, "DL_DIR", str(tmp_path / "downloads"))
popened = {}

class FakeProc:
pid = 4242

def fake_popen(argv, **kw):
popened["argv"] = argv
popened["detached"] = kw.get("start_new_session")
return FakeProc()

monkeypatch.setattr(download.subprocess, "Popen", fake_popen)
res = download.start("mlx-community/Llama-3.2-3B-Instruct-4bit")
assert res["state"] == "starting" and res["pid"] == 4242
# spawned detached, and the child is python -c (the snapshot_download runner)
assert popened["detached"] is True
assert popened["argv"][1] == "-c" and "snapshot_download" in popened["argv"][2]

def test_status_reconciles_dead_pid_to_interrupted(self, tmp_path, monkeypatch):
monkeypatch.setattr(download, "DL_DIR", str(tmp_path / "downloads"))
monkeypatch.setattr(download.subprocess, "Popen",
lambda *a, **k: type("P", (), {"pid": 999999})())
monkeypatch.setattr(download, "_pid_alive", lambda pid: False)
download.start("r/x")
assert download.status("r/x")["state"] == "interrupted"

def test_status_not_started(self, tmp_path, monkeypatch):
monkeypatch.setattr(download, "DL_DIR", str(tmp_path / "downloads"))
assert download.status("never/seen")["state"] == "not_started"

def test_idempotent_start_when_running(self, tmp_path, monkeypatch):
monkeypatch.setattr(download, "DL_DIR", str(tmp_path / "downloads"))
calls = {"n": 0}

def fake_popen(*a, **k):
calls["n"] += 1
return type("P", (), {"pid": 4242})()

monkeypatch.setattr(download.subprocess, "Popen", fake_popen)
monkeypatch.setattr(download, "_pid_alive", lambda pid: True)
download.start("r/x")
download.start("r/x") # already running → must NOT spawn again
assert calls["n"] == 1


# ── skills smoke ─────────────────────────────────────────────────────────────
class TestSkills:
def test_all_six_discovered_with_expected_exposure(self):
Expand Down