Repository navigation
[obvious-fleet] test(backend): placement case gate rejects invalid manifests and forged input hashes - #271
Conversation
The placement stage has so far been judged against one reference scene, so its evidence cannot tell solver behavior apart from repeated reference output. Add an offline case suite: a manifest of independently authored scene pairs (synthetic base-scene/v0 sources beside root-wire scenes, hand-written on both sides), a runner that posts each wire scene to POST /v1/placements twice, and explicit expectations about the chosen spot, the per-check outcomes and unsure causes, the missing-evidence requests, the failing sweep runs and the chain ends. Two negative source-only cases stay labelled awaiting conversion for the future W7 bridge: a two-wall chain and a generated (non-evidence) hazard.
|
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 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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffd476ddb3
ℹ️ 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".
| case["input_sha256"] = stats.get("input_sha256") | ||
| case["input_sha_matches_file"] = case["input_sha256"] == _file_sha256(record.wire_path) |
There was a problem hiding this comment.
Fail the case when the echoed input hash is wrong
The runner records input_sha_matches_file, but never adds a mismatch or error when it is false, so a solver/API regression that returns an incorrect stats.input_sha256 still produces a matched case and a zero exit from the CLI. Treat this integrity check as a failure before calculating the case status.
Useful? React with 👍 / 👎.
[Low impact] The PR appears safe to merge; no outstanding issues were found.
|
… input hashes One evaluator contract change, from the P1 (Codex r4287473089) and P2 (Greptile r4287478936/r4287478951) findings on #271: - load_cases requires a nonempty cases list, unique nonempty ids, a recognized kind, the fixture records each kind needs (positive: source plus wire; negative: source), meaningful positive expectations (a pinned decision plus at least one geometric expectation of the right shape) and a negative expected_error with nonempty code and pointer. Malformed types are rejected with the manifest path and field instead of a silent default; fixture hash/path errors are still validated before any record is returned. - _positive_case now requires stats.input_sha256 to equal the sha256 of the exact posted wire bytes; a discrepancy becomes a named mismatch, so it fails the case status, report.ok and the CLI exit. load_cases and the existing four-case manifest stay compatible: no fixture repinning, no new geometry, no solver changes.
|
Independent review (Obvious, Autobuild + Max) Verdict: ready. Every reproducible claim in the body checks out exactly, and the 15 discriminating tests fail on the pre-fix source — the gate genuinely bites. Two low findings, both about claims rather than behavior; nothing blocking. Reviewed at head Findings (ranked)[low] The mypy claim in the body is wrong: the fix adds one new mypy error. Body says "4 before, 4 after, same messages." Measured with the locked toolchain ( [low] The [info] Body wording: "Nothing outside backend/server/placement/case_suite.py and backend/tests/test_placement_case_suite.py was changed" — the diff also adds the 8 fixture files and flips the [info] A manifest with malformed JSON (not merely malformed structure) dies with a bare [info] No automated tests ran on this PR. What I verified (commands and results)
Correctness reading of the fix diff: the forged-hash mismatch now lands in What I couldn't verify
Nothing was changed, pushed, merged, approved, or requested from this review; the only write is this comment. |
The replay CLI's default manifest is now the supported packet, so the two producer tests pinning the committed legacy refusal state pass that manifest explicitly; their intent (committed legacy fixtures stay bridge-incompatible, nonzero exit) is unchanged. Also reformat test_placement_case_suite.py, unformatted since the #271 integrity-fix import (bad040c), so the full lint battery is clean.
…t-runner [obvious-fleet] Merge origin/alpha into the capture-API placement-case replay runner (resolve stale early-#271 conflicts)
fleet-packet: burst8-bs-case-integrity
Completed fix SHA for the held W8 integration packet:
115b6ce313e51cede71496f267b65b8cd78aa118(head7b9bd3ffa7f90ca1fb7abb3e3726202d28619879, onobv/burst7-bs-placement-cases, stacked on the reviewedffd476ddb3a4c6f0b21683f8c353d4280f05dca5).What changed and why
The capability: the placement case suite (PR #271's offline evaluator) now rejects invalid evidence instead of passing it — an edited manifest can no longer silently disable the gate, and a response tied to different input bytes can no longer match.
Evidence chosen from the reviews on #271 (all three reproduced in
backend/server/placement/case_suite.pyat the reviewed commit):discussion_r4287473089:input_sha_matches_fileis recorded but never fails the case — a solver/API regression that echoes a wrongstats.input_sha256still producedmatchedand CLI exit 0.discussion_r4287478936:load_casesaccepts a manifest with no (or an empty)caseslist and reportsok: truewith zero cases run; a positive case can lose itsexpectationsentirely and still match.discussion_r4287478951: same hash hole seen from the standalone runner — only the pytest wrapper asserted the field.Fixed as one evaluator contract change:
load_casesnow requires: a nonemptycaseslist; unique nonemptyidstrings; a recognizedkind(positive/negative_source); the fixture records each kind needs (positive:sourceandwire; negative:source); meaningful positive expectations — a pinned nonemptydecisionplus at least one geometric expectation (spot,checks,missing_evidence,sweep_failing_runs,ends) with the right container shape and content; and a negativeexpected_errorobject with nonemptycodeandpointerstrings. Malformed types are rejected explicitly, naming the manifest path and the field (cases[N].kind,cases[0].expectations.checks must be an object, …) rather than falling back to a silent default. Fixture existence and pinned sha256s are still validated before any record is returned._positive_casenow requiresstats.input_sha256to equal the SHA256 of the exact posted wire bytes; any discrepancy becomes a named mismatch, which propagates into casestatus=mismatched,report.ok=falseand CLI exit 1.Alternatives considered
mismatchesand treat empty expectations as an errored case at run time. Rejected: the reviewer's point is that an edited manifest can silently weaken the gate; runtime-only checks still let a thinned manifest produce a plausible-looking report, and failures arrive late and unnamed. Duplicate IDs and unknown kinds would still load.placement-case-manifest/v0JSON Schema validated withjsonschema. Rejected: the loader is already the single validation point, plainCaseSuiteErrormessages name manifest paths and fields in words (a schema reports JSON-Schema paths), and no contracts export currently covers this manifest. Heavier artifact for the same guarantee.The chosen shape won on named, early, field-addressed failures and zero new artifacts.
How I verified it
Focused regression tests in
backend/tests/test_placement_case_suite.py(10 pre-existing + 16 new; 26 passed at head). The 15 discriminating tests fail on the pre-fix source and pass with the fix: missing/emptycases, duplicate IDs, malformed kinds ("positve","",3,None), absent/empty/decision-only expectations, wrong-typedspot/checks, absent or pointer-less negativeexpected_error, and forged echoed hashes through bothrun_suiteand the real CLI (mainexit 1, report written,status=mismatched+input_sha256named on stderr). One new test (test_missing_input_sha256_fails_the_report) is not discriminating:stats.input_sha256is already required byserver/schemas/result.schema.json, so a missing hash failed pre-fix too — but generically asresult_schema_invalid; with the fix it fails as a namedinput_sha256mismatch (and still would if the schema guard were ever relaxed).uv run --frozen pytest tests/test_placement_case_suite.py -q→ 26 passed (pre-fix: 15 failed / 11 passed).uv run --frozen --extra api pytest -q→ 5 failed / 1260 passed / 17 skipped; the five failures are the pre-existing baseline (1 SSR shingle-overlap hygiene test + 4 expired eval-gate tests inserver/eval/tests/test_gates.py), identical names to the base commit and to prior fleet runs (SD-96 expiry; reported separately, untouched).cd server && uv run pytest -q→ 312 passed.make lint(ruff check, ruff format --check, lint-imports 5 kept / 0 broken),make schemas— clean.python -m server.placement.case_suite --output=/tmp/burst8-cli-demo/report.json→cases=4 matched=2 awaiting_conversion=2 mismatched=0 errored=0, exit 0,ok=true— the valid existing wire suite still passes, with the two awaiting-conversion negatives clearly separated (multiwall-source-unsupportedat/walls,generated-hazard-unsupportedat/objects/1/evidence).make lint, and this PR does not invent one.What is not verified
POST /v1/placementsis untouched.backend/server/placement/case_suite.pyandbackend/tests/test_placement_case_suite.pywas changed; no Swift,server/,.github/, deployment, secrets,.env, lockfile or policy-threshold files were touched.Assumptions
/home/user/work/burst8-bs-case-integrity/repoon the repo sandbox) — per the delivery contract's "nobody will accept the card".decisionplus at least one geometric expectation key with content and the right shape. A decision-only positive case is rejected (it pins one bit and no geometry).wirefixture (ignored by the suite, as before) — kept permissive for forward compatibility; only the declaredexpected_erroris mandatory.ffd476dd, and no other open PR owned these findings ([obvious-fleet] Merge origin/alpha into the capture-API placement-case replay runner (resolve stale early-#271 conflicts) #274 is a separate writer on its own branch and own runner module).