Repository navigation
Exchange: ask before a save drops a series' changed occurrences, and delete its deleted ones again - #123
Open
Timtam wants to merge 5 commits into
Open
Exchange: ask before a save drops a series' changed occurrences, and delete its deleted ones again#123Timtam wants to merge 5 commits into
Timtam wants to merge 5 commits into
Conversation
…nd delete its deleted ones again
Decisions 243 and 246-253, the core and the Exchange adapter (PR A, part 1).
- cal-core: `Event::accepts_exception_loss`, the transient consent the
surfaces send after asking; `Event::deletions_not_restored`, the transient
result naming deleted occurrences that came back and could not be deleted
again; `SeriesRewrite` (slot, zone, pattern) with the refusal's detail
`{rewrite}:{lost}`.
- adapter-ews: the update builder plans what it rewrites (start and end, the
zone's clock, the pattern; a range change, a title or a rule removed rewrite
nothing) and where a deleted occurrence stands again: shifted with the
series (live round 6, R1-R3), at its instant under a new pattern, or not
placeable. The 245 refusal leaves the pure builder.
- The write path counts from the fresh copy what would be lost (changed
occurrences, and deleted ones it cannot place) and, without consent, refuses
`exceptions-would-be-lost: {rewrite}:{lost}`; nothing is sent. With consent,
or with only placeable deleted occurrences, it writes, then deletes each
deleted occurrence again where it now stands, confirmed by its start; any
that cannot be confirmed is named on the returned event. The consent never
rides back on it.
- A series head is never written blind (248): an unreadable copy refuses
`copy-unreadable`.
Tests: the plan for every kind of save; through the adapter the question, the
consent, the re-delete (and its failure), and the blind refusal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… phone
Decisions 243 and 246-253, the surfaces (PR A, part 2).
- The refusal's detail names both counts, `{rewrite}:{changed}:{deleted}`, so
the question says exactly what happens: a changed occurrence takes the
series' details again, a deleted one comes back (252).
- shared/exceptionsLoss.ts reads the refusal and builds the question both
editors ask, and the sentence naming deleted occurrences that came back.
- Desktop editor: the refusal opens a ConfirmDialog over the form, focus on
Cancel; "Trotzdem speichern" submits the same form again with
`accepts_exception_loss` on that one write. Deleted occurrences that could
not be deleted again are named in the focused notice before the editor goes
on (the split notice's pattern).
- Phone editor: the same through the shared scope dialog, and the same notice.
- Dragging a series and the carry dialog say the translated sentence instead
of the raw token (they ask in PR B, decisions 250, 251).
- Locales de/en; the fallback sentence for paths that cannot ask.
- Docs: ews.md (the question, the re-delete, no blind series), DESIGN
(243 status, 245 as a question, 246-253 with round 6), TODO, both
troubleshooting guides (the question explained), the tutorial's note on
moving a series with changed occurrences.
Tests: the shared helper (parse, both languages, the notice), the write-error
fallback, and the desktop editor (ask and resend with consent, cancel sends
nothing, the notice). Red proofs: three in the editor, eight in the adapter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat can be placed, name the right day - A re-delete after an update that told the attendees sends them the cancellation: the series they were sent has the occurrence again. - `Placement::Shifted` only where both clocks can give the wall time the restore finds an occurrence by; otherwise the deleted ones are counted in the question instead of coming back unasked. - The restore reads each probe as an occurrence, as absent (Exchange says deleted or past the end: nothing came back), or as unreadable (nothing proven: the occurrence is named). It reports where an occurrence now stands, as the read anchors an all-day day, also when the series cannot be read again (moved by the start the update wrote). - Only the series' own save asks. A split's cut refused the same way says the sentence: a yes there would create the new series a second time. - Drag (Day, Week, Month) and both carry dialogs name deleted occurrences that came back and could not be deleted again; the phone's write errors compose the loss sentence as the desktop's do. - Round 8 measured the range: an end date instead of a count, no end and an earlier end keep changed and deleted occurrences (U1-U3), as COUNT did (M5). Docs and comments cite it; DESIGN 245 and TODO no longer speak of a refusal; the guides say a split or delete from an occurrence on does not ask. Tests: a notified move cancels; a new pattern deletes again only what it still has (absent and unreadable probes); an unreadable stored zone cannot be placed; the split's cut says, not asks; the drag hears the saved series; the phone's text. Seven red proofs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t for one save only The restore after a rewrite decided "nothing came back" too readily and, in two places, the opposite: - Any probe that answered "deleted" or "past the end" silenced the deletion, though that answer speaks only for its own index. A neighbour the restore had just deleted again, or the index after a series' last occurrence, hid an occurrence that did come back at another time (a gap of the clock). Now nothing came back only where the series, read index by index, steps over the place (from an occurrence before it, or the series' start, to one after it, or its end), or where the rule's own index is a deletion Exchange kept; an index deleted again stands for the occurrence it held, and an occurrence within half a day of the place that is not the expected one is always named. - A new pattern without the start's own weekday named the deleted start as "came back"; the series' start now counts as a side, so a place before the first occurrence brought nothing back. - Where the series could not be read again, the day was anchored on the kind the series had before the update; now on the kind and the clock the update left it on (`UpdatePlan::written_all_day`, `written_clock`). The desktop editor withdraws the consent right after the resubmit it starts, so a resubmit the form's own validation stops leaves none for a later save; the phone hands the consent to that one save as an argument. Docs: the troubleshooting guides (de/en) no longer say every split or delete from an occurrence on is refused, say when count and end bring the zone question, and name the unreadable zone; "how often" is now the pattern's frequency and "how many times" the range (SeriesRewrite, pattern_of; bindings regenerated); the TODO and DESIGN summaries say the question names what would be lost; DESIGN 253 and ews.md describe the new restore rule; stale comments fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thout requestSubmit A whole-series day move from either editor writes the rule through begin_series_anew, which leaves out a week start that changes no day; the plan compared it with shift_series' rule, which keeps one, so every other Monday moved to Tuesday (or a weekly series with weeks from Sunday) was planned Unknown: its deleted occurrences were asked about and then left back, where a drag of the same series deleted them again. The plan now compares both patterns without a FirstDayOfWeek that changes no day (cal_core::series_shift::week_start_matters, now public); one that does change a day still tells two series apart. The desktop's "Save anyway" called the form's requestSubmit with the consent in a ref; on a web view without it (WebKit before Safari 16) the call threw, nothing was sent and the consent stayed for the next save. The yes now calls the submit directly with the consent as an argument, as the phone does; the form's own submit never carries it. Docs: the troubleshooting guides (de/en) no longer promise the question for a change or delete from a later appointment on, which is refused with the sentence; ews.md says the same, and it and DESIGN 253 name the week start the editor leaves out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
Decision 243, part A: before a save makes Exchange drop occurrences of a series the user changed or deleted on their own, Aperio asks. Deleted occurrences it deletes again afterwards, so only the changed ones are lost. Decisions 246-253 (Toni, 2026-10-11).
Why
Measured on Exchange 2019 (the zone-first live test and round 6):
DeleteItemonOccurrenceItemIdby index deletes an occurrence again at its moved slot (R1 +2 h, R2 +1 day with the weekday shifted, R3 a zone switch).Until now a move of such a series lost them silently; #122 only refused a zone-only rewrite (245).
What changes
Core (cal-core)
Event::accepts_exception_loss: the transient consent, sent only on the write repeated after the question.Event::deletions_not_restored: the transient result naming deleted occurrences that came back and could not be deleted again.SeriesRewrite(slot, zone, pattern) and the refusal detail{rewrite}:{changed}:{deleted}(exceptions-would-be-lost: slot:2:1).Exchange adapter
UpdatePlan::rewrite): Start and End, only the zone's clock, or the pattern (the rule's pattern element changes, the range does not count). None for a title, a range change, a rule removed, an exception, or a single made a series.Placement):Shiftedwhere the written rule is the server's moved with its first day ascal_core::shift_seriesmoves it (a move, a zone switch, the weekday shifted with a day move), compared without a week start that changes no day, which the editor leaves out where a drag moves it along;Sameunder a new pattern with the same slot;Unknownwhere pattern and slot change together, or where a clock the restore needs cannot be read.Forbidden). The 245 refusal is now this question (247).deletions_not_restored, where it now stands (an all-day day anchored as the read anchors it; where the series cannot be read again, moved as the update moved it, on the kind and clock the update left). The master's ChangeKey is read again afterwards. The consent never rides back on the returned event.copy-unreadable); singles keep the blind write.Surfaces (desktop + phone)
shared/exceptionsLoss.ts: reads the refusal, builds the question (what is rewritten, what is lost, with counts and plurals) and the notice.ConfirmDialogover the form, focus on Cancel. "Trotzdem speichern" calls the form's submit again directly, with the consent as an argument to that one call; the form's own submit never carries it. Deleted occurrences that came back are named in the focused notice before the editor goes on.Tests
each_save_says_what_it_rewrites_and_where_deleted_ones_stand): an hour later, a day later with Tuesdays, a day later still Mondays, longer, Mondays and Wednesdays, every other Monday, five times, until the 16th, renamed, no longer a series; an exception; a single made a series. A day later with a week start: every other Monday to every other Tuesday as the editor writes it and as a drag writes it, weeks from Sunday as the editor writes it (all placed), and every other Monday and Sunday without the week start it needs (not placed). The zone switch (New York, Tokyo, Paris; Vienna) and the end-zone shape plan a zone rewrite.zone:1:0, nothing sent) and goes out with consent, the consent not on the result; a move with only a deleted occurrence re-deletes the third at its moved time and reads the ChangeKey again, and with mismatching occurrences deletes nothing and names the slot; a series without a readable copy is refused.requestSubmit; the notice names the day and the editor goes on after it), a split's refused cut says and does not ask, and the drag hears the saved series.-D warnings,cargo test --workspace, both live-test generators,cargo xtask ts-types(binding copied), vitest (2390), lint,tsc, mobiletsc,check:bindings, docs build andcheck:links.Live test
Round 7, Aperio's own requests on Toni's Exchange 2019 at 9a1d34d, all as meant:
slot:1:0, nothing sent, both exceptions kept.pattern:1:0, nothing sent, both exceptions kept.item:Subjectsent, nothing deleted, both exceptions kept.Round 8, the range only, on the same series shape (Aperio plans no rewrite there and asked nothing): an end date instead of four times (U1), no end (U2), an earlier end that keeps the moved occurrence (U3) — Aperio sent the zone and the rule; both exceptions kept every time.
First check (fixed in a5e7b34)
Twenty-one findings were confirmed, nine distinct:
deletions_not_restored; the phone carry said the loss without counts. Both fixed.Second check (fixed in a5102bb)
Twelve findings were confirmed:
The phone fix has no test of its own (no phone editor tests exist); its types are checked.
Third check (fixed in f27e5ef)
Three findings were confirmed (five reports):
shift_serieskeeps it, so the patterns differed. Deleted occurrences were asked about and left back, where a drag deleted them again. Now compared without such a week start.requestSubmit, which WebKit before Safari 16 lacks: the yes did nothing and the consent stayed for the next save. Now the submit is called directly with the consent as an argument.Docs
The phone needs a fresh
.soand XCFramework.🤖 Generated with Claude Code