Money job isolation criterion conflicts with shared Admin job controls (#1140) #1183

Open
opened 2026-10-06 05:08:56 +00:00 by kayg · 4 comments
Owner

Independent #1140 review finding: the prompt requires a User's import job to be invisible to and not resumable by another User, without a Role exception.

Money progress, results, receipts and confirmation have owner checks and pass the new ordinary cross-User tests. Shared Admin job routes have a broader policy. admin_jobs_view calls JobQueue::list_admin_jobs without a User filter. get_admin_job reads a job summary by ID without an owner check. Shared Admin retry and resume controls are per kind, including money.import. An Admin or owner Role with admin scope can therefore see another User's job ID, state and counts, and control its queue. These summary routes do not return export bytes, preview data or Undo receipts.

Relevant code is crates/calternal-server/src/wire.rs: admin_jobs_view, get_admin_job, retry_admin_failed_jobs, resume_admin_job_kind. Money now registers on that shared queue.

This conflicts with the unqualified isolation criterion in the review prompt. Resolve it by restricting private Money job metadata and controls to the job owner, or document an explicit reviewed exception for content-free Admin operations. Test an Admin who is a different User, not only two data-scope Users.

The review does not change the shared Admin job design: that would alter another crate's behavior outside its owned Money fix. This remains a policy gap for #1140; no budget payload leak is claimed.

Independent #1140 review finding: the prompt requires a User's import job to be invisible to and not resumable by another User, without a Role exception. Money progress, results, receipts and confirmation have owner checks and pass the new ordinary cross-User tests. Shared Admin job routes have a broader policy. `admin_jobs_view` calls `JobQueue::list_admin_jobs` without a User filter. `get_admin_job` reads a job summary by ID without an owner check. Shared Admin retry and resume controls are per kind, including `money.import`. An Admin or owner Role with admin scope can therefore see another User's job ID, state and counts, and control its queue. These summary routes do not return export bytes, preview data or Undo receipts. Relevant code is `crates/calternal-server/src/wire.rs`: `admin_jobs_view`, `get_admin_job`, `retry_admin_failed_jobs`, `resume_admin_job_kind`. Money now registers on that shared queue. This conflicts with the unqualified isolation criterion in the review prompt. Resolve it by restricting private Money job metadata and controls to the job owner, or document an explicit reviewed exception for content-free Admin operations. Test an Admin who is a different User, not only two data-scope Users. The review does not change the shared Admin job design: that would alter another crate's behavior outside its owned Money fix. This remains a policy gap for #1140; no budget payload leak is claimed.
Author
Owner

Implemented the owner decision in DESIGN §48: Admins can retry/cancel another User's Money jobs through content-free operational views. Both list and detail clear progress text and item counts, replace raw error text with a fixed error class, and expose only a scalar input byte count. Results and Undo receipts keep their data-owner checks.

The live Admin + second User fixture checks private budget/file/result/receipt markers, progress counts, result/receipt isolation and retry/cancel. Its first run found a fixture race: Money kind registration happens after the background API idle window. The fixture now registers and pauses the operational kind explicitly before inserting its synthetic private job, so no Worker can consume the fixture while controls are checked. Final crate gates are in progress on branch job/wizard-1140, current committed head b825bae75.

Implemented the owner decision in DESIGN §48: Admins can retry/cancel another User's Money jobs through content-free operational views. Both list and detail clear progress text and item counts, replace raw error text with a fixed error class, and expose only a scalar input byte count. Results and Undo receipts keep their data-owner checks. The live Admin + second User fixture checks private budget/file/result/receipt markers, progress counts, result/receipt isolation and retry/cancel. Its first run found a fixture race: Money kind registration happens after the background API idle window. The fixture now registers and pauses the operational kind explicitly before inserting its synthetic private job, so no Worker can consume the fixture while controls are checked. Final crate gates are in progress on branch job/wizard-1140, current committed head b825bae75.
Author
Owner

#1183 committed in 7cb7c2c4805c779baed1692f32b2fa4490507bce. DESIGN §48 records the owner-approved exception: an Admin can retry/cancel another User's Money work, with content-free operational views only. List and detail clear progress text/counts and replace raw errors with a fixed class. One bounded scalar query supplies source bytes; neither view deserializes a Money result or receipt.

The live-app fixture uses an actual Admin and a second User. It verifies private Budget/file/result/receipt markers are absent, counts are null, results are 404, receipts are empty, and retry/cancel work without changing private payload fields.

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 266 filtered out; finished in 11.13s
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 17m 57s

The ignored fixture is also invoked by the enabled live-app process wrapper in the full crate gate. Full server tests and production screenshot evidence remain in progress.

#1183 committed in `7cb7c2c4805c779baed1692f32b2fa4490507bce`. DESIGN §48 records the owner-approved exception: an Admin can retry/cancel another User's Money work, with content-free operational views only. List and detail clear progress text/counts and replace raw errors with a fixed class. One bounded scalar query supplies source bytes; neither view deserializes a Money result or receipt. The live-app fixture uses an actual Admin and a second User. It verifies private Budget/file/result/receipt markers are absent, counts are null, results are 404, receipts are empty, and retry/cancel work without changing private payload fields. ``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 266 filtered out; finished in 11.13s Finished `dev` profile [unoptimized + debuginfo] target(s) in 17m 57s ``` The ignored fixture is also invoked by the enabled live-app process wrapper in the full crate gate. Full server tests and production screenshot evidence remain in progress.
Author
Owner

Finding and fix at head 48b973914: Admin cancellation is a queue operation. Pending cancellation changes state to cancelled; leased cancellation sets cancellation_requested. Neither sets the live browser import control. Publication now reads both owner-bound forms at its final checkpoint, before the source moves. A real-route regression interrupts at intent and Trash, cancels through JobQueue without a RAM signal, resumes the same live operation, and compares the restored original file names and hashes.

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 120 filtered out; finished in 5.08s

Money Clippy and complete tests pass:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.95s
test result: ok. 119 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 128.57s
test result: ok. 27 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 12.66s

All six production macOS-emulated screenshot cases pass at 390, 820 and 1440 px in light and dark (36 captures). Final web and server gates are in progress.

Finding and fix at head 48b973914: Admin cancellation is a queue operation. Pending cancellation changes state to cancelled; leased cancellation sets cancellation_requested. Neither sets the live browser import control. Publication now reads both owner-bound forms at its final checkpoint, before the source moves. A real-route regression interrupts at intent and Trash, cancels through JobQueue without a RAM signal, resumes the same live operation, and compares the restored original file names and hashes. ``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 120 filtered out; finished in 5.08s ``` Money Clippy and complete tests pass: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 11.95s test result: ok. 119 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 128.57s test result: ok. 27 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 12.66s ``` All six production macOS-emulated screenshot cases pass at 390, 820 and 1440 px in light and dark (36 captures). Final web and server gates are in progress.
Author
Owner

All four review findings are implemented in ten atomic commits. The branch is not fully green: the server suite has an unresolved startup deadline failure, tracked in #1192. Do not mark this report READY FOR MERGE.

Branch: job/wizard-1140. Head: abd4812a0a6a59a48e68f1aabeae2aafed31cbe0. The one origin/dev merge check returned Already up to date. No push, deploy, promotion or merge into dev was performed.

Built

  • #1177: an owner-bound server Undo endpoint compares exact published names and BLAKE3 hashes under the shared writer lock. Any edit or rename refuses with the owner's exact plain-text copy. Refusal changes no file or receipt. A matching import goes to Trash and its predecessor is restored in one journal. The receipt is single-use, including restart, replay and late parser completion.
  • #1178: the server owns replacement staging, durable intent, old-budget Trash, publication and finalization. FULL commits precede source mutations. Restart and real process-exit tests cover all four publication phases and both inverse phases. Cancellation restores the original exact bytes before publication. Admin queue cancellation checks both pending cancellation state and leased cancellation flags; abandonment cannot produce a false creation receipt.
  • #1183: Admin Money job lists/details expose content-free operational fields. They hide names, amounts, item counts, raw errors, results and receipts. Retry/cancel remains available. The production route test uses an Admin and a second User. DESIGN §48 records the isolation exception.
  • #1184: both production upgrade fixtures now include Notifications 0007. Original receipt timestamps/checksums, retained data and repeat-upgrade assertions remain.
  • Contracts, generated API types, classifications and exact performance proofs are refreshed. No guard was weakened; scoped exceptions fell from 22177 to 22149. The existing benchmark now covers publication, replacement and both inverses.

UX gaps closed

Edited imports retain their Budget and Undo receipt after refusal. Unchanged Undo works with touch and keyboard. Open Budget navigates. Replacement uses the server operation. Session changes fence writes. Admin views carry no Budget content. Six macOS-emulated production cases cover 390, 820 and 1440 px, light and dark; 36 screenshots are attached. Action alignment crops were checked. Visual quality review remains with the orchestrator.

UX gaps left / known gaps

Legacy receipts without trusted published hashes refuse Undo. There is no separate one-click redo toast after Undo; Files never restores the replacement from the browser. The full server gate remains failed at the unchanged 15-second startup deadline. An isolated fresh-process retry also failed; a load-only cause is not established. #1192 has both outputs. The focused Admin regression, all five upgrade tests and the remaining server integration checks pass.

Decisions

Use the existing FULL authority pool for the small, non-rebuildable import intents and inverse receipts; parsing/progress stays NORMAL. DESIGN §2 records this narrow exception. Legacy receipts fail closed; do not manufacture trusted hashes from possibly edited files. Reuse the existing queue and filesystem journal with deterministic private Trash names, including rename-once replay when restoration reuses the source path.

Gate output (verbatim excerpts)

cargo fmt --check passed with exit 0 and no output. git diff --check passed.

Per-crate Clippy (--all-targets -- -D warnings), in order: calternal-fs, calternal-plugin-money, calternal-server:

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 3m 31s
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 39.36s
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 41.59s

cargo test -p calternal-fs:

test result: ok. 96 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 19.51s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.55s
test result: ok. 48 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.29s
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

cargo test -p calternal-plugin-money -- --test-threads=4:

test result: ok. 119 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 156.47s
test result: ok. 27 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 8.90s

cd apps/web && bun run check; bun run test --maxWorkers=2:

perf-lint: PASS; 0 violations; 22149 scoped exceptions
svelte-check found 0 errors and 4 warnings in 3 files
 Test Files  271 passed (271)
      Tests  1883 passed (1883)

cargo test -p calternal-server -- --test-threads=4 — failed:

HTTP startup waited for an upgrade backfill: Elapsed(())
test result: FAILED. 255 passed; 1 failed; 11 ignored; 0 measured; 0 filtered out; finished in 102.92s

Unchanged isolated startup retry — failed:

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 266 filtered out; finished in 97.22s

Focused wire::tests::admin_money_jobs_are_content_free -- --ignored --exact --test-threads=1:

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 266 filtered out; finished in 20.85s

cargo test -p calternal-server --test perf_guards --test private_index_permissions -- --test-threads=4:

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 45.79s
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.85s

python3 -m unittest discover -s tests/adversarial -p test_xuser_classification.py:

Ran 16 tests in 0.421s

OK

For the merge round

  • Resolve #1192 and run cargo test -p calternal-server -- --test-threads=4; prove HTTP startup does not wait for a locked upgrade backfill. Keep the current expectation.
  • Run tests/adversarial/run.sh; prove the combined authorization, cross-User and hostile-input matrices. This job ran focused real-route regressions and offline classifications, not the full matrices.
  • After the combined production build, run CALTERNAL_E2E_ASSET_OVERRIDE=1 bun apps/web/e2e/money-wizard-1140.mjs --acceptance with CALTERNAL_SERVER_BIN set to that build; prove the full format/restart/replacement acceptance round.
  • Staging/o2 and real macOS interoperability remain merge-round work under the verification policy. The requested second independent numbers review remains outstanding.
  • This is a correctness job. No performance run was made under the current policy. When measured on the perf VM, use flock /root/perf.lock bash -c 'uptime; bun bench/money-wizard-1140.mjs' with the combined shared release binary in CALTERNAL_SERVER_BIN; record p50/p95, CPU/RSS and the burst beside docs/perf/baseline.json.

Screenshot attachments

Files

  • apps/web/e2e/money-wizard-1140.mjs
  • apps/web/src/lib/money/api.test.ts
  • apps/web/src/lib/money/api.ts
  • apps/web/src/lib/money/import-session.svelte.test.ts
  • apps/web/src/lib/money/import-session.svelte.ts
  • bench/money-wizard-1140.mjs
  • contracts/action-policy.json
  • contracts/actions.json
  • contracts/openapi.json
  • contracts/perf/adoption-1058.json
  • contracts/perf/exceptions.json
  • contracts/perf/ratchet.json
  • contracts/perf/registry.json
  • crates/calternal-fs/src/journal.rs
  • crates/calternal-fs/src/trash.rs
  • crates/calternal-server/src/upgrade_tests.rs
  • crates/calternal-server/src/wire.rs
  • crates/plugins/money/src/durable_import.rs
  • crates/plugins/money/src/durable_import_tests.rs
  • crates/plugins/money/src/routes.rs
  • crates/plugins/money/src/tests.rs
  • docs/DESIGN.md
  • packages/api-client/src/generated.ts
  • tests/adversarial/test_xuser_classification.py
  • tests/adversarial/xuser_matrix.py

Cleanup

Cargo clean completed:

Removed 18614 files, 13.6GiB total

Web build output, .svelte-kit and the job's generated test temporary directory were removed. Review artifacts remain ignored and attached to #1140. The worktree is clean.

All four review findings are implemented in ten atomic commits. The branch is **not fully green**: the server suite has an unresolved startup deadline failure, tracked in [#1192](https://git.kayg.org/kayg/calternal/issues/1192). Do not mark this report READY FOR MERGE. Branch: `job/wizard-1140`. Head: `abd4812a0a6a59a48e68f1aabeae2aafed31cbe0`. The one origin/dev merge check returned `Already up to date.` No push, deploy, promotion or merge into dev was performed. **Built** - #1177: an owner-bound server Undo endpoint compares exact published names and BLAKE3 hashes under the shared writer lock. Any edit or rename refuses with the owner's exact plain-text copy. Refusal changes no file or receipt. A matching import goes to Trash and its predecessor is restored in one journal. The receipt is single-use, including restart, replay and late parser completion. - #1178: the server owns replacement staging, durable intent, old-budget Trash, publication and finalization. FULL commits precede source mutations. Restart and real process-exit tests cover all four publication phases and both inverse phases. Cancellation restores the original exact bytes before publication. Admin queue cancellation checks both pending cancellation state and leased cancellation flags; abandonment cannot produce a false creation receipt. - #1183: Admin Money job lists/details expose content-free operational fields. They hide names, amounts, item counts, raw errors, results and receipts. Retry/cancel remains available. The production route test uses an Admin and a second User. DESIGN §48 records the isolation exception. - #1184: both production upgrade fixtures now include Notifications 0007. Original receipt timestamps/checksums, retained data and repeat-upgrade assertions remain. - Contracts, generated API types, classifications and exact performance proofs are refreshed. No guard was weakened; scoped exceptions fell from 22177 to 22149. The existing benchmark now covers publication, replacement and both inverses. **UX gaps closed** Edited imports retain their Budget and Undo receipt after refusal. Unchanged Undo works with touch and keyboard. Open Budget navigates. Replacement uses the server operation. Session changes fence writes. Admin views carry no Budget content. Six macOS-emulated production cases cover 390, 820 and 1440 px, light and dark; 36 screenshots are attached. Action alignment crops were checked. Visual quality review remains with the orchestrator. **UX gaps left / known gaps** Legacy receipts without trusted published hashes refuse Undo. There is no separate one-click redo toast after Undo; Files never restores the replacement from the browser. The full server gate remains failed at the unchanged 15-second startup deadline. An isolated fresh-process retry also failed; a load-only cause is not established. #1192 has both outputs. The focused Admin regression, all five upgrade tests and the remaining server integration checks pass. **Decisions** Use the existing FULL authority pool for the small, non-rebuildable import intents and inverse receipts; parsing/progress stays NORMAL. DESIGN §2 records this narrow exception. Legacy receipts fail closed; do not manufacture trusted hashes from possibly edited files. Reuse the existing queue and filesystem journal with deterministic private Trash names, including rename-once replay when restoration reuses the source path. **Gate output (verbatim excerpts)** `cargo fmt --check` passed with exit 0 and no output. `git diff --check` passed. Per-crate Clippy (`--all-targets -- -D warnings`), in order: calternal-fs, calternal-plugin-money, calternal-server: ``` Finished `dev` profile [unoptimized + debuginfo] target(s) in 3m 31s Finished `dev` profile [unoptimized + debuginfo] target(s) in 39.36s Finished `dev` profile [unoptimized + debuginfo] target(s) in 41.59s ``` `cargo test -p calternal-fs`: ``` test result: ok. 96 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 19.51s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.55s test result: ok. 48 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 9.29s test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` `cargo test -p calternal-plugin-money -- --test-threads=4`: ``` test result: ok. 119 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out; finished in 156.47s test result: ok. 27 passed; 0 failed; 3 ignored; 0 measured; 0 filtered out; finished in 8.90s ``` `cd apps/web && bun run check`; `bun run test --maxWorkers=2`: ``` perf-lint: PASS; 0 violations; 22149 scoped exceptions svelte-check found 0 errors and 4 warnings in 3 files Test Files 271 passed (271) Tests 1883 passed (1883) ``` `cargo test -p calternal-server -- --test-threads=4` — failed: ``` HTTP startup waited for an upgrade backfill: Elapsed(()) test result: FAILED. 255 passed; 1 failed; 11 ignored; 0 measured; 0 filtered out; finished in 102.92s ``` Unchanged isolated startup retry — failed: ``` test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 266 filtered out; finished in 97.22s ``` Focused `wire::tests::admin_money_jobs_are_content_free -- --ignored --exact --test-threads=1`: ``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 266 filtered out; finished in 20.85s ``` `cargo test -p calternal-server --test perf_guards --test private_index_permissions -- --test-threads=4`: ``` test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 45.79s test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.85s ``` `python3 -m unittest discover -s tests/adversarial -p test_xuser_classification.py`: ``` Ran 16 tests in 0.421s OK ``` **For the merge round** - Resolve #1192 and run `cargo test -p calternal-server -- --test-threads=4`; prove HTTP startup does not wait for a locked upgrade backfill. Keep the current expectation. - Run `tests/adversarial/run.sh`; prove the combined authorization, cross-User and hostile-input matrices. This job ran focused real-route regressions and offline classifications, not the full matrices. - After the combined production build, run `CALTERNAL_E2E_ASSET_OVERRIDE=1 bun apps/web/e2e/money-wizard-1140.mjs --acceptance` with `CALTERNAL_SERVER_BIN` set to that build; prove the full format/restart/replacement acceptance round. - Staging/o2 and real macOS interoperability remain merge-round work under the verification policy. The requested second independent numbers review remains outstanding. - This is a correctness job. No performance run was made under the current policy. When measured on the perf VM, use `flock /root/perf.lock bash -c 'uptime; bun bench/money-wizard-1140.mjs'` with the combined shared release binary in `CALTERNAL_SERVER_BIN`; record p50/p95, CPU/RSS and the burst beside `docs/perf/baseline.json`. **Screenshot attachments** - [inverse-light-390.zip](https://git.kayg.org/attachments/a4c8756e-074e-43c6-9acc-c73895de3c11) - [inverse-light-820.zip](https://git.kayg.org/attachments/ff6328bc-300f-4a11-83e5-0cf1202dba20) - [inverse-light-1440.zip](https://git.kayg.org/attachments/101b5cff-9b7f-4413-bb82-b8307a8015b8) - [inverse-dark-390.zip](https://git.kayg.org/attachments/656922c3-178e-483a-9b03-480519eae66e) - [inverse-dark-820.zip](https://git.kayg.org/attachments/df3ca33d-8c5e-466c-995c-993fa7279985) - [inverse-dark-1440.zip](https://git.kayg.org/attachments/d921a64b-afe4-4a8f-b3c4-336de453dd13) **Files** - `apps/web/e2e/money-wizard-1140.mjs` - `apps/web/src/lib/money/api.test.ts` - `apps/web/src/lib/money/api.ts` - `apps/web/src/lib/money/import-session.svelte.test.ts` - `apps/web/src/lib/money/import-session.svelte.ts` - `bench/money-wizard-1140.mjs` - `contracts/action-policy.json` - `contracts/actions.json` - `contracts/openapi.json` - `contracts/perf/adoption-1058.json` - `contracts/perf/exceptions.json` - `contracts/perf/ratchet.json` - `contracts/perf/registry.json` - `crates/calternal-fs/src/journal.rs` - `crates/calternal-fs/src/trash.rs` - `crates/calternal-server/src/upgrade_tests.rs` - `crates/calternal-server/src/wire.rs` - `crates/plugins/money/src/durable_import.rs` - `crates/plugins/money/src/durable_import_tests.rs` - `crates/plugins/money/src/routes.rs` - `crates/plugins/money/src/tests.rs` - `docs/DESIGN.md` - `packages/api-client/src/generated.ts` - `tests/adversarial/test_xuser_classification.py` - `tests/adversarial/xuser_matrix.py` **Cleanup** Cargo clean completed: ``` Removed 18614 files, 13.6GiB total ``` Web build output, .svelte-kit and the job's generated test temporary directory were removed. Review artifacts remain ignored and attached to #1140. The worktree is clean.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
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#1183
No description provided.