Repository navigation
[obvious-fleet] make local-api serves the capture API on 127.0.0.1 and stays local - #281
obvious-autobuild[bot] wants to merge 8 commits into
Conversation
…api) scripts/local_capture_api.py starts the real app under uvicorn, loopback only, fresh tokens + HMAC secret per run, memory state and volume storage, ingest hooks auto so finalize drives the orchestrator inline; it writes runs/local-api/session.json (0600) with the base URL, tokens, pid, sha. make local-api-smoke drives the mini-arkit packet over real TCP via httpx against the script as a subprocess — no TestClient, no drain_finalize. Co-authored-by: Obvious <agents@obvious.ai>
Fixes from the first runs: create pins Idempotency-Key to the packet id so each journey retags its packet copy; /review gates the reviewer before the not-ready check, so 404 result_not_ready needs the reviewer bearer while an app bearer gets 403; the event log carries stage events for the run, and a wrong bearer is unauthorized. Smoke suite passes twice in a row; ruff clean. Co-authored-by: Obvious <agents@obvious.ai>
…pages Captured on a fresh alpha clone: make local-api recording (banner, clean Ctrl-C), make local-api-smoke recording (6 passed over real TCP), Playwright review/result pages, sanitized journey receipt reaching terminal manual_review with the placement stage real and compose stubbed. Tokens redacted (scan-verified). Co-authored-by: Obvious <agents@obvious.ai>
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d0774fdbc1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…usal Codex P1 on #281: bare `make local-api` hands the launcher a bare --port. Pin the documented default (PORT=8000), the PORT=8123 override, the exported-but-empty PORT guard, and an actionable launcher refusal for `--port=`. Fails at the parent head by design.
…mpty --port loudly Codex P1 on #281: bare `make local-api` expanded $(PORT) to nothing, so the launcher got a bare --port and argparse exited instead of serving. - backend/Makefile: PORT ?= 8000 with an empty-value guard, so unset and exported-but-empty PORT both serve the documented default; PORT=8123 make local-api still overrides. - scripts/local_capture_api.py: --port parses through _port(), which refuses an empty value with an actionable message (exit 2) instead of argparse's bare "invalid int value: ''". Target name and CLI flags unchanged. Tests in the parent commit fail at that head and pass here; smoke module 10 passed; make lint clean.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c92df18d0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Three regressions for the open review findings on this PR and #293: a stray file named local-api must not make the target 'up to date', an inherited HSS_ORCH_MODAL_REMOTE=1 must not reach the child env, and a launcher started from a poisoned shell must still answer healthz on memory state and volume storage. Co-authored-by: obvious-autobuild[bot] <obvious-autobuild[bot]@users.noreply.github.com>
… targets - backend/Makefile: local-api and local-api-smoke join .PHONY, so a stray file named local-api can no longer make the target report 'up to date' and silently never start the server (open Codex P2 on #281). - scripts/local_capture_api.py: force HSS_ORCH_MODAL_REMOTE=0 (Greptile P1 raised on #293 — an inherited =1 dispatched a 'local' run to deployed Modal functions that cannot see its memory state or volume, and flips state to Modal Dict and storage to GCS via shared_remote_backends), and refuse eleven further parent-shell keys that would move work or bytes off this machine: Modal credentials and app/environment targeting, MODAL_TASK_ID, STATE_DICT_NAME, HSS_PACKET_CACHE, and the HSS_ORCH_TEST_* fault-injection hooks. Refusals print one stderr line naming the keys, never their values. - docs/local-capture-api.md: the forced-env table gains the HSS_ORCH_MODAL_REMOTE row and a refusal table with per-key reasons, plus what was reviewed and left alone. Target name, CLI flags and the session.json shape are unchanged. Co-authored-by: obvious-autobuild[bot] <obvious-autobuild[bot]@users.noreply.github.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@greptileai review this at plus |
[Medium impact] Adds a local development server for the capture API. Fix the session overwrite, proxy-sensitive readiness check, and destructive smoke test before merging.
|
| "stageStubs": True, | ||
| "startedAt": datetime.now(UTC).isoformat(), | ||
| } | ||
| _write_session(session_path, session) |
There was a problem hiding this comment.
Second launch loses working credentials
If make local-api is already running, a second launch replaces session.json before trying to bind the same port. The second process then fails, but the saved tokens now belong to it rather than the running server. Clients reading the file get 401 responses, and the working credentials are lost.
Preserve any live session and publish new credentials only after the new server owns its listener.
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/scripts/local_capture_api.py
Line: 265
Comment:
**Second launch loses working credentials**
If `make local-api` is already running, a second launch replaces `session.json` before trying to bind the same port. The second process then fails, but the saved tokens now belong to it rather than the running server. Clients reading the file get 401 responses, and the working credentials are lost.
Preserve any live session and publish new credentials only after the new server owns its listener.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| deadline = time.monotonic() + timeout_s | ||
| while time.monotonic() < deadline: | ||
| try: | ||
| with urllib.request.urlopen(f"{base}/v1/healthz", timeout=2) as response: |
There was a problem hiding this comment.
Proxy settings stop local startup
If the shell sets http_proxy without a loopback exception in no_proxy, the default urlopen sends this readiness request through the proxy instead of directly to the local API. If the proxy cannot reach that address, the launcher kills an otherwise healthy server after 60 seconds.
Use a proxy-free opener for this loopback-only check.
| with urllib.request.urlopen(f"{base}/v1/healthz", timeout=2) as response: | |
| with urllib.request.build_opener(urllib.request.ProxyHandler({})).open( | |
| f"{base}/v1/healthz", timeout=2 | |
| ) as response: |
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/scripts/local_capture_api.py
Line: 130
Comment:
**Proxy settings stop local startup**
If the shell sets `http_proxy` without a loopback exception in `no_proxy`, the default `urlopen` sends this readiness request through the proxy instead of directly to the local API. If the proxy cannot reach that address, the launcher kills an otherwise healthy server after 60 seconds.
Use a proxy-free opener for this loopback-only check.
```suggestion
with urllib.request.build_opener(urllib.request.ProxyHandler({})).open(
f"{base}/v1/healthz", timeout=2
) as response:
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| strays = [ROOT / "local-api", ROOT / "local-api-smoke"] | ||
| try: | ||
| for stray in strays: | ||
| stray.write_text("") |
There was a problem hiding this comment.
If backend/local-api or backend/local-api-smoke already exists, this test truncates it and then unconditionally deletes it in the finally block. Running the smoke tests can therefore destroy a developer’s existing files.
Copy the Makefile into tmp_path, run Make there, and create the stray files only in that temporary directory.
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_local_capture_api.py
Line: 439-442
Comment:
**Smoke test erases local files**
If `backend/local-api` or `backend/local-api-smoke` already exists, this test truncates it and then unconditionally deletes it in the `finally` block. Running the smoke tests can therefore destroy a developer’s existing files.
Copy the Makefile into `tmp_path`, run Make there, and create the stray files only in that temporary directory.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| def test_make_local_api_without_port_passes_the_documented_default() -> None: | ||
| # The documented flow (docs/local-capture-api.md) is bare `make local-api` | ||
| # on port 8000; the recipe must not hand the launcher a bare --port. | ||
| assert "--port 8000" in _make_n_local_api() |
There was a problem hiding this comment.
Port override fails smoke tests
If the shell exports PORT=8123, _make_n_local_api() inherits it and Make correctly emits --port 8123. This test still requires --port 8000, so make local-api-smoke reports a false failure for a supported setting.
Remove PORT from the test environment before checking the unset default.
| def test_make_local_api_without_port_passes_the_documented_default() -> None: | |
| # The documented flow (docs/local-capture-api.md) is bare `make local-api` | |
| # on port 8000; the recipe must not hand the launcher a bare --port. | |
| assert "--port 8000" in _make_n_local_api() | |
| def test_make_local_api_without_port_passes_the_documented_default(monkeypatch) -> None: | |
| # The documented flow (docs/local-capture-api.md) is bare `make local-api` | |
| # on port 8000; the recipe must not hand the launcher a bare --port. | |
| monkeypatch.delenv("PORT", raising=False) | |
| assert "--port 8000" in _make_n_local_api() |
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_local_capture_api.py
Line: 400-403
Comment:
**Port override fails smoke tests**
If the shell exports `PORT=8123`, `_make_n_local_api()` inherits it and Make correctly emits `--port 8123`. This test still requires `--port 8000`, so `make local-api-smoke` reports a false failure for a supported setting.
Remove `PORT` from the test environment before checking the unset default.
```suggestion
def test_make_local_api_without_port_passes_the_documented_default(monkeypatch) -> None:
# The documented flow (docs/local-capture-api.md) is bare `make local-api`
# on port 8000; the recipe must not hand the launcher a bare --port.
monkeypatch.delenv("PORT", raising=False)
assert "--port 8000" in _make_n_local_api()
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
fleet-packet: burst14-bs-fix-local-api-serve-281
Human author: Sam Gu (sgu07966@gmail.com). Continues the earlier fleet round on this branch (the PORT-default fix, commits
330164c3+8c92df18): this round clears the one open Codex P2 review thread on #281, and the Greptile P1 raised on #293 against this PR's launcher, so the iOS Debug path (#286) and the held app-states run (#293) can rely onmake local-api.What changed and why
Two findings, two commits (
0bdee232tests failing-first,dc7cdee1fix + doc), after mergingorigin/alphain (branch was 53 behind; merge commit2a39e938, no conflicts, no rebase):Codex P2 on [obvious-fleet] make local-api serves the capture API on 127.0.0.1 and stays local #281 (backend/Makefile:100) —
local-apiandlocal-api-smokewere missing from.PHONY, so a stray file namedlocal-apimademake local-apireport "up to date" and silently never start the server. Both targets joined the existing.PHONYline.Greptile P1 on Backend user run: drive the local API through every app state over a real socket #293 (backend/scripts/local_capture_api.py:197) — the launcher inherited
HSS_ORCH_MODAL_REMOTEfrom the developer's shell; with=1a "local" run dispatched to deployed Modal functions that cannot see its memory state or volume (andshared_remote_backendsflips state to Modal Dict and storage to GCS). The launcher now forcesHSS_ORCH_MODAL_REMOTE=0alongside the other forced local settings.The same audit applied to the rest of the environment — every
HSS_*/STATE_*/STORAGE_*/MODAL_*key the code or the Modal client reads that could change where work runs or where bytes go — found eleven more keys, now refused (dropped from the child env, one stderr line naming the keys, never values):MODAL_TOKEN_ID,MODAL_TOKEN_SECRET,MODAL_IDENTITY_TOKEN,MODAL_APP_NAME,MODAL_ENVIRONMENT,MODAL_TASK_ID,STATE_DICT_NAME,HSS_PACKET_CACHE, and theHSS_ORCH_TEST_FAIL_STAGE/HSS_ORCH_TEST_HANG_STAGE/HSS_ORCH_TEST_KILL_COORDINATOR_AFTERfault-injection hooks. The doc's new "What the launcher refuses from the shell" section lists each with its reason, plus what was reviewed and left alone (HSS_LOCAL_OUTCOME/HSS_LOCAL_STAGE_DELAY_Sstay deliberate pass-throughs; cache/model-path keys are inert because every stage that reads them runs as a committed stub locally;CAPTURE_API_URL/CAPTURE_API_TOKENare client-side;HSS_MODAL_SCHEDULEand Modal runtime keys are only read inside Modal images).Target name, CLI flags and the
session.jsonshape unchanged (pinned verbatim by the existing test). Files touched:backend/Makefile,backend/scripts/local_capture_api.py,backend/tests/test_local_capture_api.py,backend/docs/local-capture-api.md. Nothing underios/,.github/,backend/infra/, no lockfiles.How I verified it (commands and results)
Failing-first — the three new regressions ran at parent head
2a39e938before the fix:pytest tests/test_local_capture_api.py -k "stray_file or poisoned_shell" — pre-fix
After the fix:
Focused module, lint, and the full suite
The poisoned-shell regression starts the launcher with
HSS_ORCH_MODAL_REMOTE=1,STATE_BACKEND=modal_dict,STORAGE_BACKEND=gcs,MODAL_TOKEN_ID, andHSS_ORCH_TEST_FAIL_STAGEin the parent env, then asserts/v1/healthzanswers{"status":"ok","state":"memory","storage":"volume"}and the log names the refused keys — the exact failure the P1 describes, now pinned.What is not verified
make -nbehavior verified with GNU Make on Debian only.VISION_EXTERNAL_CALLS=off, loopback only.Assumptions
os.environ/getenvread underbackend/serverandbackend/scripts, plus the Modal client's own well-known keys. Keys read only inside Modal images (HSS_MODAL_SCHEDULE, Modal runtime fields) are documented rather than refused, since a loopback-only memory-state run never reaches them.HSS_ORCH_TEST_*) counts as in scope: they change how a local run behaves (injected failure/hang), a footgun the same shell-inheritance path delivers.server/orchestrator/api.py:170,server/api/finalize.py:522,server/orchestrator/stubs/validate.py:48) are left for the Backend user run: drive the local API through every app state over a real socket #293 round: they are outside this packet's owns-list, and this packet does not resolve their threads.🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact