Found by the dev implementing #7523 (PR #7562) and deliberately not fixed there: it is a different handler in a file three cards were editing concurrently, so it wants its own card. Filing unassigned; grading is triage's channel.
The finding
DELETE /api/v1/reports/schedules/:scheduleId discriminates on existence exactly the way DELETE /reports/:id did before #7523:
| Target |
Response |
| A schedule id that does not exist |
204 — unscheduleReport() returns early (if (!schedule) return; // idempotent) |
| Another owner's schedule id |
404 — throws REPORT_NOT_FOUND → handleValidation |
Same oracle, quieter costume. #7523's card warned about precisely this shape — "align the unknown-id arm to the same status so the two arms are byte-indistinguishable; otherwise 404-vs-204 remains the same oracle in a quieter costume" — and that warning turns out to describe this route as it stands today.
Why it was read as correct
This handler is the one the #7523 investigation cited as proof of the right shape, because it does route its catch through handleValidation — which is why the cross-owner arm answers 404 rather than 500. The QA run (#7515) exercised only that cross-owner arm and recorded it as clean. The unknown-id arm was never probed on this route, so the surviving half went unseen.
⚠️ A pin currently locks the leak in place: packages/rest/src/rest.test.ts:1651 asserts the unknown arm is 204. Any fix must supersede that pin explicitly — replacing it with the opposite assertion on the same input, not deleting it quietly.
Suggested shape, if promoted
The same one #7523 landed, which is now precedent in the same file: answer both deny arms from one response emitted before the unschedule fires, via a visibility read that is already blind to the difference, so status and body cannot drift apart. Assert the two arms' whole responses are equal rather than pinning each arm's status separately — #7523's mutation table shows a per-arm assertion cannot fail on the half-fix.
Worth deciding at the same time whether any other unschedule/idempotent-early-return handler on this surface has the same split; #7523's fix covered only deleteReport.
Provenance
Reported by the dev in PR #7562's body under "Sibling finding — not fixed here, filing to the PM". Related: #7523 (closed by #7562), #2980 (getReport() blind to the difference — the mechanism the fix leans on).
Found by the dev implementing #7523 (PR #7562) and deliberately not fixed there: it is a different handler in a file three cards were editing concurrently, so it wants its own card. Filing unassigned; grading is triage's channel.
The finding
DELETE /api/v1/reports/schedules/:scheduleIddiscriminates on existence exactly the wayDELETE /reports/:iddid before #7523:unscheduleReport()returns early (if (!schedule) return; // idempotent)REPORT_NOT_FOUND→handleValidationSame oracle, quieter costume. #7523's card warned about precisely this shape — "align the unknown-id arm to the same status so the two arms are byte-indistinguishable; otherwise 404-vs-204 remains the same oracle in a quieter costume" — and that warning turns out to describe this route as it stands today.
Why it was read as correct
This handler is the one the #7523 investigation cited as proof of the right shape, because it does route its catch through
handleValidation— which is why the cross-owner arm answers 404 rather than 500. The QA run (#7515) exercised only that cross-owner arm and recorded it as clean. The unknown-id arm was never probed on this route, so the surviving half went unseen.packages/rest/src/rest.test.ts:1651asserts the unknown arm is 204. Any fix must supersede that pin explicitly — replacing it with the opposite assertion on the same input, not deleting it quietly.Suggested shape, if promoted
The same one #7523 landed, which is now precedent in the same file: answer both deny arms from one response emitted before the unschedule fires, via a visibility read that is already blind to the difference, so status and body cannot drift apart. Assert the two arms' whole responses are equal rather than pinning each arm's status separately — #7523's mutation table shows a per-arm assertion cannot fail on the half-fix.
Worth deciding at the same time whether any other
unschedule/idempotent-early-return handler on this surface has the same split; #7523's fix covered onlydeleteReport.Provenance
Reported by the dev in PR #7562's body under "Sibling finding — not fixed here, filing to the PM". Related: #7523 (closed by #7562), #2980 (
getReport()blind to the difference — the mechanism the fix leans on).