Skip to content

fix: marking a cancellation Done applies the outcome it asked for (#96) - #101

Merged
pataniaeli merged 1 commit into
devfrom
fix/issue-96-cancellation-done-applies-status
Sep 10, 2026
Merged

fix: marking a cancellation Done applies the outcome it asked for (#96)#101
pataniaeli merged 1 commit into
devfrom
fix/issue-96-cancellation-done-applies-status

Conversation

@pataniaeli

Copy link
Copy Markdown
Collaborator

Closes #96.

The reported bug

Marking a request Done set status='Done' on the request row and touched nothing else, so the booking stayed Pending Cancellation forever.

The reason it could not simply be fixed there

Applying the outcome is not enough on its own, because the request cannot find the reservation it is about.

cancellation_requests.occurrence_id names a dated row. For a weekly booking that row does not survive an edit — the PATCH handler deletes every occurrence and reinserts it on each save, so the ids change while the values are carried across on the date. That is the same id instability #69's investigation flagged and that 20260829001000 already had to design around for event_tracking.

Every cancellation_requests row in production is in that state — all four, including the one still Pending. Verified read-only:

rows matching the id the Pending request stored 0
rows matching (booking_id, occurrence_date) 1
status of that row Pending Cancellation

A second consequence nobody was looking for

Auto-Cancel finds those reservations anyway, because it scans by status rather than by request. But it could not attribute them back to a request, so it lost the cancellation_type and fell back to 'Cancelled'. A request that asked to go Virtual would have cancelled the meeting outright. That is fixed by the same change.

The fix

Requests record occurrence_date, resolved at creation while the id still names a live row, and are matched on (booking_id, occurrence_date) — with the id tried first for the cases where it still resolves. The date is the stable identifier the write model actually preserves.

Applying the statuses and building the audit rows are now shared with Auto-Cancel, which has to reach the same result: the same request resolved either way should leave the database in the same state.

Marking Done also acts on reservations with no reservation code, which Auto-Cancel deliberately excludes. A missing code means CSC cannot be asked; it says nothing about whether an administrator has dealt with it, and marking Done by hand is that administrator saying they have. That is why SkippedReservation now carries the identity to act on rather than only its four display fields.

What this does not recover

The backfill sets nothing in production. There is no record anywhere of which date those four requests named, so they are left NULL rather than guessed at.

The one still Pending needs your eye: its booking has two weeks marked Pending Cancellation (9/15 and 9/22) and only one request, so even by hand it is ambiguous which the request meant. Easiest resolved directly in the UI.

The backfill is written for environments where the rows are intact, and so applying this to a restored backup does the right thing.

Verification

npm run build, tsc and eslint clean. The migration was dry-run against production inside an aborting transaction — 4 requests, 0 backfilled, 4 still orphaned, exactly as its comment predicts — then rolled back, with no column persisting. The matching was proven with the read-only query above.

Not applied — the migration still needs running on deploy.

I could not click through the Cancellations tab: the dev server is behind a login wall.

🤖 Generated with Claude Code

Marking a request Done set status='Done' on the request row and touched
nothing else, so the booking stayed Pending Cancellation forever.

Applying the outcome is not enough on its own, because the request could
not find the reservation it was about. cancellation_requests.occurrence_id
names a dated row, and for a weekly booking that row does not survive an
edit: the PATCH handler deletes every occurrence and reinserts it on each
save, so the ids change while the values are carried across on the date.
Every cancellation_requests row in production is in that state -- all four,
including the one still Pending.

That had a second consequence nobody was looking for. Auto-Cancel finds
those reservations anyway, because it scans by status rather than by
request, but it could not attribute them back to a request -- so it lost
the cancellation_type and fell back to 'Cancelled'. A request that asked
to go Virtual would have cancelled the meeting outright.

So requests now record occurrence_date, resolved at creation while the id
still names a live row, and are matched on (booking_id, occurrence_date)
with the id tried first for the cases where it still resolves. The date is
the stable identifier the write model actually preserves, which is what
20260829001000 concluded for event_tracking and what issue #69 concluded
for calendar UIDs.

Verified against production, read-only: the id the one Pending request
stored resolves to zero rows, while (booking_id, date) resolves to exactly
one -- and it is the row marked Pending Cancellation.

The backfill recovers nothing in production, because there is no record
anywhere of which date those four requests named. They are left NULL
rather than guessed at; the one still Pending needs an administrator's eye,
since its booking has two weeks pending and only one request. The backfill
is written for environments where the rows are intact, and for a restore.

Marking Done includes reservations with no code on file, which Auto-Cancel
excludes. A missing code means CSC cannot be asked; it says nothing about
whether an administrator has dealt with it, and marking Done by hand is
that administrator saying they have. This is why SkippedReservation now
carries the identity to act on rather than only the four display fields.

Applying the statuses and building the audit rows are now shared with
Auto-Cancel, which has to reach the same result: the same request resolved
either way should leave the database in the same state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
chambers Ready Ready Preview Sep 10, 2026 12:03am UTC

@pataniaeli pataniaeli self-assigned this Sep 10, 2026
@pataniaeli pataniaeli added the bug Something isn't working label Sep 10, 2026
@pataniaeli pataniaeli linked an issue Sep 10, 2026 that may be closed by this pull request
@pataniaeli
pataniaeli merged commit 0add0eb into dev Sep 10, 2026
4 checks passed
@pataniaeli
pataniaeli deleted the fix/issue-96-cancellation-done-applies-status branch September 10, 2026 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Marking a cancellation as done doesn't do anything

1 participant