fix: marking a cancellation Done applies the outcome it asked for (#96) - #101
Merged
Merged
Conversation
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>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
pataniaeli
deleted the
fix/issue-96-cancellation-done-applies-status
branch
September 10, 2026 00:27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_idnames 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 that20260829001000already had to design around forevent_tracking.Every
cancellation_requestsrow in production is in that state — all four, including the one still Pending. Verified read-only:(booking_id, occurrence_date)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_typeand 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
SkippedReservationnow 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,tscandeslintclean. 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