Skip to content

[obvious-fleet] Backend: the first four real stages on CPU on a real app-format capture - #298

Draft
SamGu-NRX wants to merge 1 commit into
alphafrom
obv/burst14-bs-real-poses-scale
Draft

SamGu-NRX wants to merge 1 commit into
alphafrom
obv/burst14-bs-real-poses-scale

Conversation

@SamGu-NRX

Copy link
Copy Markdown
Owner

fleet-packet: burst14-bs-real-poses-scale

Human author: Sam Gu (sgu07966@gmail.com)

What changed

Adds backend/tests/test_real_cpu_upstream.py. It runs the first four processing stages for real, on CPU, against the committed app-format capture ios-kit/fixtures/mini-arkit-lidar, and pins the loud refusal a capture without depth gets instead of a scale claim. No production code changed.

The test builds the same StageContext objects the coordinator builds (ctx.gpu = None, external vision off), publishes each stage's artifacts with the runner's own _publish helper, and hydrates each next stage from graph.STAGE_INPUTS, so the chain is the orchestrator's chain, not a re-creation of it. Two assertions hold the whole thing to the same budget as the smallest CPU worker: every stage finishes under 600 seconds and the process peak RSS stays under 8 GiB, measured with the test pinned to two cores.

Per-stage results on mini-arkit-lidar, six 1920x1440 frames with ARKit poses and LiDAR depth:

Stage Result Wall Peak RSS
validate 6 keyframes accepted, warnings few_images, synthetic_source 0.9 s 250 MiB
poses fallback_arkit_fixed: all 6 frames registered over the ARKit priors, pose_source recorded per camera 9.7 s 1835 MiB
reads partial with no stills in the packet, cost $0 <0.1 s 1835 MiB
scale source arkit, rel_bound_95 0.035, status unverified <0.1 s 1835 MiB

The second chain runs the same four stages on data/fixtures/packets/mini-arkit (64x48 frames, no depth) and asserts the refusal shape: reads publishes status: disabled, and scale refuses with scale_unresolved / meter_model_unread and weak confidence instead of certifying a metric scale.

Why

backend/docs/STATUS.md records every real stage behind the journey stubs as unverified, and the dev journey answers every app question from stand-ins. Until now nothing proved that a real packet walks validate, poses, reads and scale on the CPU path. This test is that proof, kept executable: it fails if the guided-pose fallback stops registering frames, if scale starts claiming a source it cannot resolve, or if a stage blows the 2 CPU / 8 GB budget.

Two things I expected to need fixing turned out to work as designed, so I changed nothing: barcode.read degrades to an empty result under pytest when zxing-cpp is absent (the default make test environment), and scale records the LiDAR branch's own refusal (lidar_too_few_pairs, 0 pairs on this six-frame fixture) as an estimate while shipping the ARKit-only source and asking for a meter close-up.

Verification

Focused, in both environments:

taskset -c 0,1 env OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2 MKL_NUM_THREADS=2 HSS_POSES_THREADS=2 \
  uv run --frozen --extra api pytest tests/test_real_cpu_upstream.py
# 2 passed in ~12-15 s (also 2 passed with --extra reads, the real barcode decoder)

Full suite, once, on this head:

make test   # 5 failed, 1507 passed, 19 skipped, 14 errors in 908.60s (0:15:08)

Four of the failures are the pre-existing SD-96 gates on alpha (server/eval/tests/test_gates.py, same names as on the base), and the 14 collection errors are the pre-existing cold-restart fixtures, both unchanged by this PR. The fifth, server/orchestrator/tests/test_run.py::test_hard_stop_cancels_worker, is a timing-sensitive worker-cancellation test that failed only under the suite's sustained ~90% CPU contention: it passes in isolation (0.55 s) and passes immediately after the new tests (18 passed), so no test here leaks processes or threads into it. The new tests pass inside the suite.

Ruff check and format clean on the changed file.

Coverage limits, next to the claims they weaken: the fixtures are mini packets (six frames, one reference tap), so the LiDAR-vs-sparse alignment finds no usable pairs and scale ships ARKit-only, unverified. The VLM readers are off by design, so reads exercises the local barcode path only. No GPU, no model weights, no network.

Risk

The test adds about 12 seconds to make test and depends only on committed fixtures. Its budgets are wide enough for slower runners, but a much slower runner could trip the 600 s wall budget; that failure would be a signal, not a flake.

Checklist

  • The scope is focused; related changes belong together
  • I explained what changed and why
  • N/A before/after images: backend-only, nothing visible changes
  • N/A video: no interaction change

🔗 Obvious Project · 🧵 Obvious Thread · 🔀 Obvious PR Artifact


Opened by the maintainer from the Obvious thread's own description, so every review bot reviews it (CodeRabbit skips PRs authored by bots). The work and its commits are by Obvious.

Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>

…ges on CPU

The dev journey answers every app question from stand-ins and STATUS.md
records every real stage behind the stubs as unverified. Pin the first
four real stages to the committed mini-arkit-lidar packet: contexts built
the way the coordinator builds them, artifacts published and hydrated the
way runner._publish and graph.STAGE_INPUTS do it, stills promoted into a
local storage backend. Poses ships the ARKit-guided fixed-pose
triangulation (all six frames registered, pose_source recorded), scale
carries a source and rel_bound_95, both measured pinned to two cores
against the 2 CPU / 8 GB budget. The 64x48 mini-arkit packet runs the
same chain and pins its loud refusal: reads disabled, scale unresolved
(meter_model_unread).
@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Oct 16, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size:L 100-499 effective changed lines. label Oct 16, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-16T21:53:42.260993Z f905f1a Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f905f1a378

ℹ️ 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".

Comment on lines +37 to +40
os.environ.setdefault("OMP_NUM_THREADS", "2")
os.environ.setdefault("OPENBLAS_NUM_THREADS", "2")
os.environ.setdefault("MKL_NUM_THREADS", "2")
os.environ.setdefault("HSS_POSES_THREADS", "2")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate thread environment changes to this test

When pytest collects this module, these setdefault calls mutate the process-wide environment for every test that runs afterward. If the variables were initially unset, unrelated pose and numerical tests will now run with OMP_NUM_THREADS, BLAS threads, and HSS_POSES_THREADS forced to 2, changing their performance and potentially their behavior. Apply these limits only in a fixture that restores the environment, or run the real-CPU chain in a subprocess instead of changing globals at import time.

Useful? React with 👍 / 👎.

Comment on lines +18 to +26

Resource budget: every measurement is taken with the process pinned to two
cores (`taskset -c 0,1`) and `OMP_NUM_THREADS=2 OPENBLAS_NUM_THREADS=2
MKL_NUM_THREADS=2 HSS_POSES_THREADS=2`. A stage over 600 s or a process peak
RSS over 8 GiB fails the budget the target host (2 CPUs, 8 GB) imposes.

No weights, no network, no GPU: the VLM readers stay off
(`VISION_EXTERNAL_CALLS`), so `reads` runs only the local barcode decoder, and
`poses` triangulates over the ARKit priors with pycolmap on CPU.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce the two-core constraint inside the test

The test only limits library thread counts; it never pins the process to two CPUs. The documented taskset -c 0,1 constraint is supplied only by the focused shell command, while ordinary make test runs this test without it, so the wall-time and RSS assertions do not exercise the stated two-CPU worker budget and can pass under a different resource profile. Pin the process in the test or make the budget check conditional on an enforced affinity.

Useful? React with 👍 / 👎.

Comment on lines +228 to +231

# Scale carries a source and a 95% relative bound. The LiDAR depth and the
# meter tap both feed it; `source` names what shipped.
scale = json.loads((tmp_path / "scale" / "scale" / "scale.json").read_text())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Assert the expected scale source instead of merely checking presence

The test description and commit claim that this LiDAR fixture ships an ARKit-only scale, but this assertion accepts any non-none source. A regression that incorrectly certifies the result from LiDAR, a meter read, or another unsupported source would still pass, so the test does not pin the behavior it says to protect. Assert the expected source (and the corresponding refusal/source combination in the no-depth chain) explicitly.

Useful? React with 👍 / 👎.

@SamGu-NRX

Copy link
Copy Markdown
Owner Author

@greptileai review this at plus

@greptile-apps

greptile-apps Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 4/5 Tier: plus

[Low impact] Adds a test file for CPU pipeline stages.

The PR appears safe to merge, with non-blocking gaps in its resource measurements and LiDAR scale assertions.

Fix All in CodexFindings

  1. P2 CPU budget is not enforced ▶
  2. P2 Other tests inflate memory readings ▶
  3. P2 False scale claims pass ▶
Fix with agent prompt
### Issue 1
backend/tests/test_real_cpu_upstream.py:37-40
`make test` runs pytest without restricting it to two cores. These `setdefault` calls also preserve any higher thread counts already in the environment. A passing test therefore does not establish that the stages fit the two-core worker's time budget.

Run the measured chain in a fresh process with explicit thread settings and two-core affinity, or check those conditions before accepting the measurements.

### Issue 2
backend/tests/test_real_cpu_upstream.py:170
`VmHWM` includes the whole pytest process, including tests that ran before this chain. If an earlier test reaches 8 GiB, these budget checks fail even when the new stages stay below that limit. That makes failures depend on unrelated tests and points developers at the wrong work.

Measure each chain in a fresh child process so its memory peak belongs to the work being tested.

### Issue 3
backend/tests/test_real_cpu_upstream.py:232-234
These assertions accept any nonempty `source`, a `status` of `verified`, and a zero `rel_bound_95`. They also pass if the LiDAR attempt disappears entirely. This fixture is expected to produce ARKit-only, unverified scale, so the test would miss a false scale claim.

Assert the expected `source` and `status`, plus the unused LiDAR estimate with `reason` equal to `lidar_too_few_pairs`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds two real CPU chains through validate, poses, reads, and scale, using committed packets and the runner's _publish helper. No production code changes.

  • A CPU test runs the first four real stages on two packets.

Acknowledged by SamGu-NRX: the tiny fixtures do not establish usable LiDAR alignment, external readers and GPU work are intentionally excluded, and the missing barcode package is allowed to degrade under pytest. SamGu-NRX also identifies the reported full-suite failures as pre-existing or contention-only; none was established as caused by this change.

Reviews (1) · Last reviewed commit: "test(orchestrator): run the real validat..." · Reviewed by Greptile

Comment on lines +37 to +40
os.environ.setdefault("OMP_NUM_THREADS", "2")
os.environ.setdefault("OPENBLAS_NUM_THREADS", "2")
os.environ.setdefault("MKL_NUM_THREADS", "2")
os.environ.setdefault("HSS_POSES_THREADS", "2")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 CPU budget is not enforced

make test runs pytest without restricting it to two cores. These setdefault calls also preserve any higher thread counts already in the environment. A passing test therefore does not establish that the stages fit the two-core worker's time budget.

Run the measured chain in a fresh process with explicit thread settings and two-core affinity, or check those conditions before accepting the measurements.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_real_cpu_upstream.py
Line: 37-40

Comment:
**CPU budget is not enforced**

`make test` runs pytest without restricting it to two cores. These `setdefault` calls also preserve any higher thread counts already in the environment. A passing test therefore does not establish that the stages fit the two-core worker's time budget.

Run the measured chain in a fresh process with explicit thread settings and two-core affinity, or check those conditions before accepting the measurements.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

stored = _publish(_ctx(stage, work_dir, packet_dir, storage, capture_id, run_id, branch, tier), result)
report[stage] = {
"wall_s": round(wall_s, 2),
"vm_hwm_mb": round(_vm_hwm_mb(), 1),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Other tests inflate memory readings

VmHWM includes the whole pytest process, including tests that ran before this chain. If an earlier test reaches 8 GiB, these budget checks fail even when the new stages stay below that limit. That makes failures depend on unrelated tests and points developers at the wrong work.

Measure each chain in a fresh child process so its memory peak belongs to the work being tested.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_real_cpu_upstream.py
Line: 170

Comment:
**Other tests inflate memory readings**

`VmHWM` includes the whole pytest process, including tests that ran before this chain. If an earlier test reaches 8 GiB, these budget checks fail even when the new stages stay below that limit. That makes failures depend on unrelated tests and points developers at the wrong work.

Measure each chain in a fresh child process so its memory peak belongs to the work being tested.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

Comment on lines +232 to +234
assert scale["source"]
assert scale["source"] != "none"
assert isinstance(scale["rel_bound_95"], float) and scale["rel_bound_95"] >= 0.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 False scale claims pass

These assertions accept any nonempty source, a status of verified, and a zero rel_bound_95. They also pass if the LiDAR attempt disappears entirely. This fixture is expected to produce ARKit-only, unverified scale, so the test would miss a false scale claim.

Assert the expected source and status, plus the unused LiDAR estimate with reason equal to lidar_too_few_pairs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/tests/test_real_cpu_upstream.py
Line: 232-234

Comment:
**False scale claims pass**

These assertions accept any nonempty `source`, a `status` of `verified`, and a zero `rel_bound_95`. They also pass if the LiDAR attempt disappears entirely. This fixture is expected to produce ARKit-only, unverified scale, so the test would miss a false scale claim.

Assert the expected `source` and `status`, plus the unused LiDAR estimate with `reason` equal to `lidar_too_few_pairs`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Codex Fix in Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 effective changed lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant