Repository navigation
[obvious-fleet] Backend: served capture, events, and result bodies decode the way the Swift client decodes them - #285
Conversation
…p reads
A route miss, a method mismatch, and an unhandled stage crash answered with
plain-text or {"detail": ...} bodies, so the device's CaptureAPI.errorCodes
read no code from them and the app could only show a generic transport
failure. INTEGRATION.md promises "Errors are ApiError"; those three answers
now serve it too (404 not_found, 405 method_not_allowed, 500 internal),
while Starlette still logs the traceback.
test_capture_device_decoder_contract.py pins the whole served surface the
way Swift Codable decodes it: a strict mirror of the app's structs runs
every upload-walk, events (including an empty long-poll timeout), result,
review/preview-refusal, and error body through required-vs-optional keys,
Int-vs-Double strictness, unknown-key tolerance, and unknown-enum decoding.
The three error-shape tests failed on the pre-fix app; all 26 pass on this
one. Decisions and open contract questions are recorded in
backend/docs/hunter-contract-questions.md.
Co-Authored-By: Sam Gu <sgu07966@gmail.com>
|
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 |
|
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: 15f0bed975
ℹ️ 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".
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. |
[High impact] The PR appears safe to merge; the remaining issue is a non-blocking documentation correction.
|
…and composer-driven result cases
…ts integral numbers Starlette's exception headers (the 405 Allow list, WWW-Authenticate) now pass through the ApiError rewrite, and the Codable mirror's Int accepts exactly integral numbers (1.0, 1e0) the way JSONDecoder's NSNumber round-trip does while still rejecting 1.5, bools, strings, and out-of-Int64 values. The ready-result cases now serve the real composer CLI output for the committed synthetic fixtures, so producer drift fails the decoder check instead of escaping it.
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? 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". |
fleet-packet: burst11-bs-fix-contract-parity-285
What changed and why
The iOS client decodes this API with plain
Codable— all-or-nothing. One wrong value anywhere in a body makes the whole body unreadable, and the uploader ends the scananswerUnreadable. PR #257 pinned the upload-side key names; nothing pinned the result envelope, the eventdatafields, the terminal/outcome-less status sets, or the error shape the app'sCaptureAPI.errorCodesreads.This PR adds
backend/server/api/tests/test_capture_device_decoder_contract.py: a Python mirror of SwiftCodabledecoding for exactly the structs the app declares (CaptureAPI.swift,CaptureResult.swift), then runs every body the server actually serves through it — create, register, PUT, commit, finalize (both 202 shapes), events including an empty long-poll timeout, result for every reachable status, review/preview refusals, and every error body the walk produces.Result: the served shapes already match Swift. One real gap surfaced — three answers the route handlers never produce used bodies the app cannot decode.
server/api/app.pynow enforces INTEGRATION.md's "Errors are ApiError" for framework-level answers:{"detail": "Not Found"}not_foundmethod_not_allowedinternal(Starlette still logs the traceback)Review fixes (threads on head 15f0bed)
app.py:239).http_errorforwardsexc.headersinto theApiFailureresponse, so a 405 keeps Starlette's requiredAllowlist and any other framework headers (e.g.WWW-Authenticate). The 405 contract test now also assertsAllownames GET. Test (failing-first) in bc14fd2, fix in a7363eb.Intmirror accepts exactly integral numbers (Greptile P2,test_capture_device_decoder_contract.py:76). Swift'sJSONDecoderround-trips throughNSNumber:1.0and1e0decode intoInt;1.5, booleans, strings, and values outside Int64 still fail.as_intmirrors that, and the module docstring, mirror comment, and self-test now state the correct rule. Test (failing-first) in bc14fd2, fix in a7363eb.:843). Three cases run the composer's own CLI (python -m server.result.compose, themake result-goldeninvocation, in a subprocess because the import contract keeps api tests out of the result package) over the committed synthetic fixtures — synthetic → complete, photo → needs_views, reject → manual_review — and serve exactly theresult.jsonbytes the coordinator uploads asresult/result.json, through the route and the decoder. A hand-written_outcomecan no longer mask producer drift. Test in bc14fd2, hardened in a7363eb.How I verified it
Test logs (local, uv run --frozen)
server/api/tests— 208 passedmake lint:ruff check,ruff format --check,lint-imports— 5 kept / 0 brokenAllowassertion both failed on the pre-fix tree; the composer cases bite under a mutation and pass on real outputmake schemasno-drift check reported at 15f0bedWhat is not verified
CaptureAPI.swift/CaptureResult.swift, not compiled. An iOS build is what would fully confirm the mirror against the app.backend/**has no GitHub Actions workflow (pre-existing repo-wide gap), so the GitHub checks on this PR stay empty.server/eval/tests/test_gates.py, 14 errors intests/test_capture_cold_restart.py) were not touched and not rerun this turn.Assumptions
make result-goldenuses; done to keep the api → result import boundary the linter enforces.as_intfollows Swift Foundation'sNSNumberround-trip semantics: exactly integral numbers decode, fractional/bool/string/out-of-range fail.Human author: Sam Gu (sgu07966@gmail.com)