How it is supposed to work
The calendar standard has a property for exactly this: EXDATE. It lists dates that the rule would have produced but which are excluded. The rule is untouched; the series continues; one date is simply not in it.
Moving one occurrence uses a different mechanism — a separate entry with a RECURRENCE-ID pointing back at the original date, saying “the occurrence that was going to be on the 22nd is now on the 23rd at 8pm”. Again, the rule is untouched.
Both are in the expander above. Put a date in the cancelled box and the rest of the series is unaffected.
The counting subtlety
Here is a detail that catches implementations out, and it is genuinely counter-intuitive. If your rule says “every Tuesday, ten times”, and you cancel one of them, you get nine dates — not ten with an extra week added at the end.
The reason is that COUNT limits what the rule generates, and the exclusion is applied afterwards. A plugin that counts after excluding gives you an extra week that nobody asked for, and it will disagree with Google about the same series. The expander here follows the standard: set a count, exclude a date, and watch the total go down by one.
Where it goes wrong in practice
Cancelling the whole series. In plugins that store occurrences as rows, editing one is genuinely ambiguous — did you mean this one, or all of them? Many ask; some guess. Guessing wrong deletes a year of events.
The cancellation not surviving a sync. If your events come from Google or Outlook, cancelling on the website may be overwritten on the next fetch. Cancel it at the source instead — that is what having a single source means.
Feeds where EXDATE is read and ignored. This one is common and invisible: the importer parses the exclusion list, stores it, and never applies it when expanding. Your cancelled class stays on the website. The .ics reader on this site flags exclusions explicitly for exactly that reason.
What to check on your own site
Cancel one occurrence and then look at the permalinks of the ones on either side. They should be exactly as they were. If cancelling one date changes the URLs of the others, the plugin is rebuilding the series rather than recording an exception — and every link you have shared is now broken.