Repository navigation
Replay runner: enforce every reported acceptance check + hand-authored success-path tests - #275
Conversation
The runner previously trusted aggregate verdicts it could not have earned. It now binds each solver response's stats.input_sha256 to the exact bytes each path sent, compares the converted scene field-by-field against independently authored wire fixtures, checks a metre-to-feet sentinel, served-result HTTP/outcome/run identity, finalize-replay idempotence, and requires the positive cases to carry distinct normalized geometry (identity stamps are not evidence: one geometry under two case ids is a blocker). Positive refusals and negative solutions are blockers either way, and case selection cannot pass vacuously. The committed manifest's positive sources refuse conversion at the bridge (stand-off meter anchor), so the success path is exercised through a temporary manifest whose sources, wire scenes, and pinned expectations are authored by hand in the tests - the same geometry in metres and in feet, never produced by running the converter. Fault injections prove the checks are load-bearing: a corrupted converter, a forged input hash in both replies, a lying served result, a non-idempotent replay, a declared negative that solves, and a substituted boundary scene all fail the suite.
|
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 |
|
Full backend suite at 5f9e295: 1280 passed / 17 skipped / 5 failed (4:26). The 5 failures are the pre-existing baseline set, none on the PR's paths:
Focused suite (tests/test_capture_placement_cases.py): 17 passed. Placement contract suite (server/placement/tests): 14 passed. ruff check + ruff format --check clean on both changed files. |
|
Independent review (Obvious, Autobuild + Max) Verdict: Findings (ranked by severity)
What I verified (commands and results)Environment: detached worktree at the PR head 5f9e295,
What I couldn't verify
Nothing pushed, committed, merged, or approved — the only write is this comment. |
…into the pytest process Scoped port of the #274 review fix at c255a23 (the one piece the burst8 integrated copy lacked): _apply_dev_env returns a restore callable and run_cases restores the prior environment in a finally block, so in-process runs no longer leak STATE_BACKEND/STORAGE_BACKEND/VOLUME_ROOT/UPLOAD_URL_HMAC_SECRET into the rest of the pytest process; a test pins the full environ equality across a run. The rest of c255a23 (replay and cross-capture blockers, identity-normalized geometry hashing, the mutation tests) is already present in this branch's stronger #275-integrated form — distinctScenes AND distinctNormalizedGeometry AND distinct captureIds, applicable-aware replay blocking, full-run positive solve requirements — and is retained unchanged; the supported default manifest is not weakened. Evidence: the new test fails against the pre-port script (env leaked) and passes after; the placement-cases/pairs/suite suites pass (60 passed). Co-authored-by: obvious-autobuild[bot] <262744130+obvious-autobuild[bot]@users.noreply.github.com>
…-acceptance # Conflicts: # backend/scripts/capture_placement_cases.py # backend/tests/test_capture_placement_cases.py
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. |
|
Note Review skipped Codemetrics is installed on this repository, but it hasn't been added to your team yet. To enable AI code reviews for this repo, go to your dashboard and add BaseScanning to your team's repositories. |
[Medium impact] Updates test documentation and validation logic for placement case replay. The PR appears safe to merge, with a non-blocking gap around the unused same-id precedence check.
|
| def _assert_merge_precedence(merged: dict[str, Any], journey: dict[str, Any], mismatches: list[str]) -> None: | ||
| """Same-id precedence, asserted explicitly: every injected object appears in the | ||
| merged scene exactly once and the merged copy *is* the injected object.""" |
There was a problem hiding this comment.
The precedence check never runs
_assert_merge_precedence has no callers, although the updated documentation says same-id precedence is checked explicitly. The new success fixtures also inject no object that replaces an existing same-id object. This leaves unused code that looks like an active acceptance check.
Call it when collecting the merged scene and add a fixture with a same-id replacement, or remove the helper and narrow the documentation.
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/scripts/capture_placement_cases.py
Line: 524-526
Comment:
**The precedence check never runs**
`_assert_merge_precedence` has no callers, although the updated documentation says same-id precedence is checked explicitly. The new success fixtures also inject no object that replaces an existing same-id object. This leaves unused code that looks like an active acceptance check.
Call it when collecting the merged scene and add a fixture with a same-id replacement, or remove the helper and narrow the documentation.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Brings in #275 (replay acceptance) and #278 (runtime join) so #277 can land on alpha. backend/scripts/capture_placement_cases.py: the one conflict is the solver-response block; this branch's version is #275's plus solverDecision, so it is kept. Alpha's removal of _geometry_sha256 also merges in, so mergedGeometrySha256 has one meaning again: the canonical normalized geometry of a converted scene. backend/tests/test_capture_placement_cases.py: this branch's suite (bands on the supported sources, explicit --manifest for the legacy CLI runs, the malformed objects_3d test) plus: - the twin manifest pins _expected_a() for both twins (#275), and the twin test now asserts both twins solve cleanly so crossCapture is the only blocker (#277's intent that the refusal comes from geometry); - the legacy manifest's crossCapture is not_applicable again, because refused positives carry no converted geometry hash once _geometry_sha256 is gone; it now also asserts a reason and no crossCapture blocker; - test_a_failed_finalize_replay_fails_the_cli is restored from alpha; it checks the CLI exit code, which the run_cases variant does not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR: Make capture replay enforce every reported acceptance check
Repo: SamGu-NRX/BaseScanning
Branch:
obv/burst8-bs-replay-acceptance→ baseobv/burst7-bs-capture-scene-stage(stacked on PR #274's head,obv/burst7-bs-capture-placement-runner@ bdfbb3f)Commit: 5f9e295 — "Enforce every reported acceptance check in the placement replay runner"
Draft: yes — never merges, pushes to a default branch, or deploys.
Load-bearing assumption
The premise: a replay runner's worth comes from checks that would fail if the thing they describe broke — an acceptance suite whose success path is unreachable, or whose pass depends on the runner trusting the code it audits, reports trust that was never earned. Evidence that confirms this: any fault injection in the new tests breaking the suite. Evidence that would break it: the committed manifest's positive fixtures becoming convertible (the temporary-manifest fixtures in the tests would then be redundant but harmless).
Problem
The runner from burst 7 (
capture_placement_cases.py) walked the real capture front door but reported aggregate verdicts it could not have earned:/objects/0/geometry/standoff_m), sook: truewas unreachable; the suite reported that honestly as blockers, but nothing proved the runner could ever pass.stats.input_sha256to the exact bytes each path sent — two replies could agree with each other and both be wrong.--case-idsselections did not refuse to run.Solution
backend/scripts/capture_placement_cases.py, +~50 lines net):stats.input_sha256must equal the sha256 of the exact request bytes on both paths (staged capture and directPOST /v1/placements).manual_review, and the journey's own run id; finalize replay must stay idempotent or the suite blocks.backend/tests/test_capture_placement_cases.py, rewritten): the success path runs on a temporary manifest whose two positive sources, wire scenes, and pinned expectations are authored by hand in the test module — the same geometry in metres and in feet, never produced by running the converter (the committed manifest stays pinned to its refusal state, reported loudly). Fault-injection tests prove the checks are load-bearing: a corrupted converter, a forged input hash in both replies, a lying served result (kind or run id), a non-idempotent replay, a declared negative that solves, a substituted boundary scene, a removed stage, one geometry under two identities, and CLI exit codes through a real subprocess.Verification
tests/test_capture_placement_cases.py).server/placement/tests).ruff check+ruff format --check: clean on both files.Assumptions
spot.span_ft, checks, missing-evidence bands) are pinned from the solver's deterministic output on exactly the merged scenes the temp manifest produces; they are geometry facts, not policy.Merged alpha
Merged
origin/alpha(c2d4bb2) into this branch as merge commit 2a1dfd5 (a normal merge — no rebase, no force-push) so the PR can land on alpha now that #274 and the alpha-side fixes are in.Conflicts and resolutions
backend/scripts/capture_placement_cases.py— kept this branch's acceptance enforcement (independent merge oracle,stats.input_sha256bindings, field-by-field wire comparison, served-result HTTP-200/kind/run-id checks, applicable/not_applicablecrossCapture/finalizeReplayaggregates, full-run positive-solve requirement, empty-selection refusal) and adopted alpha's env-hygiene fix:_apply_dev_envnow returns a restore callable andrun_casesrestores the process environment in afinally. Alpha's merged-scene_geometry_sha256was superseded by this branch's canonicalized normalized-geometry hash (identity fully stripped, floats canonicalized) — both implement the same post-[obvious-fleet] Merge origin/alpha into the capture-API placement-case replay runner (resolve stale early-#271 conflicts) #274 review fix, and where they disagreed this feature branch's semantics stand.backend/tests/test_capture_placement_cases.py— took this branch's rewritten suite verbatim and kept two of alpha's three added tests verbatim (test_a_failed_finalize_replay_fails_the_cli— its faked replay dict is compatible with the branch's applicable-aware blocker, whose message contains "finalize replay"; andtest_run_cases_restores_the_process_environment). Alpha's twin-negative cross-capture test is superseded by this branch'stest_one_geometry_under_two_identities_fails_cross_capture, which pins the same intent — geometry compared, never identity stamps — through the solving path. One adaptation was required: the twin manifest's positive expectations changed from{"decision": "manual_review"}to_expected_a(), because alpha's hardenedcase_suitenow rejects positive cases without geometric expectations.Verification (in
backend/, at merge commit 2a1dfd5)uv run pytest tests/test_capture_placement_cases.py -q→ 19 passed (this branch's 17 + alpha's 2)uv run pytest server/placement/tests -q→ 14 passeduvx ruff check+uvx ruff format --checkon both files → cleanuv run lint-imports→ Contracts: 5 kept, 0 brokenok: false, 7 blockers, 4 cases — the pinned refusal state is still reported loudly, by designuv run pytest -q) → 4 failed, 1426 passed, 18 skipped, 14 errors in 594s — the 4 failures are the documented pre-existing eval-gate failures (server/eval/tests/test_gates.py, expired SD-96 pins) and the 14 errors are the pre-existingtests/test_capture_cold_restart.pycollection errors that pure alpha already ships (load_receiptimport); identical to the two parents' baselines, nothing new from this merge.