Review cross-User Money preview cancellation response profile #1444

Open
opened 2026-10-08 04:55:26 +00:00 by kayg · 3 comments
Owner

Observed during Forgejo #1402's XUser matrix against the shared release server at d395b6e25.

Route: DELETE /api/v1/money/imports/{import_id} (money_import_cancel). A real A-owned staged Import preview was present. As User B, canceling A's preview returned HTTP 404; canceling a same-shape absent UUID also returned 404, but the response profiles differed: (404, 69, '69', 'application/json') versus (404, 67, '67', 'application/json').

The OpenAPI contract says missing, expired, and foreign previews return 404. The body-size difference means the foreign and absent branches do not have the same response profile. No preview was canceled across Users. Please review and make the denial indistinguishable if the distinction is not intentional. No product code was changed.

Observed during Forgejo #1402's XUser matrix against the shared release server at `d395b6e25`. Route: `DELETE /api/v1/money/imports/{import_id}` (`money_import_cancel`). A real A-owned staged Import preview was present. As User B, canceling A's preview returned HTTP 404; canceling a same-shape absent UUID also returned 404, but the response profiles differed: `(404, 69, '69', 'application/json')` versus `(404, 67, '67', 'application/json')`. The OpenAPI contract says missing, expired, and foreign previews return 404. The body-size difference means the foreign and absent branches do not have the same response profile. No preview was canceled across Users. Please review and make the denial indistinguishable if the distinction is not intentional. No product code was changed.
Author
Owner

Starting investigation on branch job/xcancel-1443, based on d395b6e25d. I have read issues #1443 and #1444 and will compare foreign and missing responses across the Money routes before changing code.

Starting investigation on branch job/xcancel-1443, based on d395b6e25da20d8c67b8b9fc1a593b813939c8a7. I have read issues #1443 and #1444 and will compare foreign and missing responses across the Money routes before changing code.
Author
Owner

Finding: cancel_import routes an absent owner-visible preview through cancel_import_job(import_id). When a foreign detached preview uses that same progress UUID, the job route returns a 404 ErrorEnvelope; for a missing UUID it returns 204 and the preview route then returns its import preview not found ErrorEnvelope. This explains the 69-byte versus 67-byte bodies. The durable cancellation update is owner-scoped; I will keep the foreign preview unchanged and make both preview responses identical.

Finding: `cancel_import` routes an absent owner-visible preview through `cancel_import_job(import_id)`. When a foreign detached preview uses that same progress UUID, the job route returns a 404 ErrorEnvelope; for a missing UUID it returns 204 and the preview route then returns its `import preview not found` ErrorEnvelope. This explains the 69-byte versus 67-byte bodies. The durable cancellation update is owner-scoped; I will keep the foreign preview unchanged and make both preview responses identical.
Author
Owner

Completed: #1443 and #1444

Foreign Money import Job DELETE requests now return the same idempotent 204 response as missing or stopped jobs, and they do not cancel or otherwise change the foreign Job. Foreign and missing preview DELETE requests return byte-identical 404 responses. The same fixed 404 behavior is documented for preview confirmation. The Money route audit found no other foreign-versus-missing response-body differences.

Rust route regression coverage compares status, headers, and raw body bytes for foreign and missing progress, result, Job cancellation, preview confirmation, and preview cancellation. It also verifies that the owner's durable result remains unchanged and publishable. The XUser matrix now creates a real import fixture and checks those identities against a live API.

Files: crates/plugins/money/src/routes.rs, crates/plugins/money/src/tests.rs, crates/plugins/money/src/durable_import_tests.rs, crates/plugins/money/src/review_1140.rs, tests/adversarial/xuser_matrix.py, tests/adversarial/reliability_1202.mjs, contracts/openapi.json, packages/api-client/src/generated.ts, and docs/DESIGN.md.

Decisions: Followed the supplied cross-User rule: foreign Job DELETE is indistinguishable from missing/stopped (204); foreign preview DELETE/confirm is indistinguishable from missing/expired (404). No other Money route needed a response change. The cancellation preflight adds no I/O: it uses the keyed Job lookup and the pending-preview map, which is admission-limited to one pending preview.

Verification after merging origin/dev:

  • cargo fmt --check — passed, no output.
  • cargo clippy -p calternal-plugin-money --all-targets -- -D warnings — passed:
    Finished \dev` profile [unoptimized + debuginfo] target(s) in 1.13s`
  • cargo test -p calternal-plugin-money — passed:
    test result: ok. 125 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 121.18s
    test result: ok. 29 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 23.70s
    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
  • The system python3 -m pytest tests/adversarial/test_xuser_classification.py could not start because pytest is not installed in that interpreter (/home/kayg/.nix-profile/bin/python3: No module named pytest). The same gate passed in an ephemeral uv environment with uv run --with pytest python -m pytest tests/adversarial/test_xuser_classification.py: 20 passed in 1.29s.
  • cargo clean — Removed 12444 files, 8.7GiB total.

Known gap / merge round: The full live XUser matrix was not run in this focused job. Run ADVERSARIAL_MATRIX_DIRECT=1 python3 xuser_matrix.py from tests/adversarial/ against the disposable live server; it must prove the new foreign-versus-missing comparisons and owner-state preservation.

Commits: 7f0e43df320916e63fc30e781bdaa6691407d133 (fix), d77accdf04d1e1225d32f69cfefcff143d660ec7 (contract/docs). Final head: d45ceeda6eac95d2944ce907c3b378dce942341e. Worktree is clean.

## Completed: #1443 and #1444 Foreign Money import Job DELETE requests now return the same idempotent 204 response as missing or stopped jobs, and they do not cancel or otherwise change the foreign Job. Foreign and missing preview DELETE requests return byte-identical 404 responses. The same fixed 404 behavior is documented for preview confirmation. The Money route audit found no other foreign-versus-missing response-body differences. Rust route regression coverage compares status, headers, and raw body bytes for foreign and missing progress, result, Job cancellation, preview confirmation, and preview cancellation. It also verifies that the owner's durable result remains unchanged and publishable. The XUser matrix now creates a real import fixture and checks those identities against a live API. Files: `crates/plugins/money/src/routes.rs`, `crates/plugins/money/src/tests.rs`, `crates/plugins/money/src/durable_import_tests.rs`, `crates/plugins/money/src/review_1140.rs`, `tests/adversarial/xuser_matrix.py`, `tests/adversarial/reliability_1202.mjs`, `contracts/openapi.json`, `packages/api-client/src/generated.ts`, and `docs/DESIGN.md`. Decisions: Followed the supplied cross-User rule: foreign Job DELETE is indistinguishable from missing/stopped (204); foreign preview DELETE/confirm is indistinguishable from missing/expired (404). No other Money route needed a response change. The cancellation preflight adds no I/O: it uses the keyed Job lookup and the pending-preview map, which is admission-limited to one pending preview. Verification after merging `origin/dev`: - `cargo fmt --check` — passed, no output. - `cargo clippy -p calternal-plugin-money --all-targets -- -D warnings` — passed: ` Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 1.13s` - `cargo test -p calternal-plugin-money` — passed: `test result: ok. 125 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 121.18s` `test result: ok. 29 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 23.70s` `test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s` - The system `python3 -m pytest tests/adversarial/test_xuser_classification.py` could not start because pytest is not installed in that interpreter (`/home/kayg/.nix-profile/bin/python3: No module named pytest`). The same gate passed in an ephemeral uv environment with `uv run --with pytest python -m pytest tests/adversarial/test_xuser_classification.py`: `20 passed in 1.29s`. - `cargo clean` — `Removed 12444 files, 8.7GiB total`. Known gap / merge round: The full live XUser matrix was not run in this focused job. Run `ADVERSARIAL_MATRIX_DIRECT=1 python3 xuser_matrix.py` from `tests/adversarial/` against the disposable live server; it must prove the new foreign-versus-missing comparisons and owner-state preservation. Commits: `7f0e43df320916e63fc30e781bdaa6691407d133` (fix), `d77accdf04d1e1225d32f69cfefcff143d660ec7` (contract/docs). Final head: `d45ceeda6eac95d2944ce907c3b378dce942341e`. Worktree is clean.
Commenting is not possible because the repository is archived.
No labels
No milestone
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-v0#1444
No description provided.