Edit one day of a repeating event with --occurrence and --apply-to - #435
Open
robzolkos wants to merge 4 commits into
Open
Edit one day of a repeating event with --occurrence and --apply-to#435robzolkos wants to merge 4 commits into
robzolkos wants to merge 4 commits into
Conversation
hey event edit <id> changed a whole series and nothing else; the day view served an occurrence_id nothing on the CLI could take. HEY's own form puts a choice to anyone editing one day of a series — update just this event, or this and all future events — and the SDK already spoke both through UpdateOccurrence. The Omarchy calendar plugin wants that choice in a machine-safe shape. --occurrence takes the occurrence_id exactly as day and week serve it, naming the series the positional id names; --apply-to is required with it and takes current or future, which are apply_to_future=0 and =1 on the wire. The day is read on its own date, as the window HEY answers with every series still recurring through it and with the day itself where an earlier edit already wrote it out, so no wider scan is needed and a day HEY has made its own keeps its own title, times and countdown. A schedule change cannot apply to one day, so current refuses the repeat flags. An occurrence edit keeps what a whole-event edit loses, or refuses. The countdown is a recording of its own under the event, so it is read back — from the day, or from the series' first day in one more bounded read — and sent again; only --countdown 0 removes it, and a label that cannot be read back stops the edit. Notes are served only as plain text and nothing can tell formatted ones from plain, so an edit that would send notes back as text refuses unless --allow-plain-notes accepts the loss or --notes replaces them. The circle is sent back too, because a future edit records a new series that HEY circles only when told. A whole-event edit is unchanged on the wire. HEY's 404 on the occurrence route arrives from the SDK as a bare form error, so the edit names it as the not-found it is, with both things it can mean.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Validate countdown labels and their 1–30 range before constructing update parameters.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds CLI support for editing individual occurrences of repeating events with current/future scope and field preservation.
Changes:
- Adds occurrence editing flags, validation, and routing.
- Preserves event metadata, countdowns, and notes safely.
- Updates tests, documentation, skills, API coverage, and CLI surface.
- Requires countdown label range validation before approval.
File summaries
| File | Reviewed changes |
|---|---|
skills/hey/SKILL.md |
Updated agent guidance. |
internal/cmd/recording_filter.go |
Supports multi-type recording reads. |
internal/cmd/events.go |
Adds flags, help text, and routing. |
internal/cmd/events_occurrence.go |
Implements occurrence editing and preservation. |
internal/cmd/events_occurrence_test.go |
Adds occurrence edit coverage. |
docs/cli.md |
Documents occurrence editing. |
API-COVERAGE.md |
Records endpoint coverage. |
.surface |
Updates the CLI surface snapshot. |
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 1
- Review effort level: Lite (auto)
Note
Copilot is running an experiment and ran this review at Lite.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+389
to
+395
| value, _ := strconv.Atoi(match[1]) | ||
| units := map[string]hey.CountdownUnit{ | ||
| "day": hey.CountdownUnitDays, | ||
| "week": hey.CountdownUnitWeeks, | ||
| "month": hey.CountdownUnitMonths, | ||
| } | ||
| return hey.CountdownParams{Value: value, Unit: units[match[2]]}, nil |
Review of the occurrence edit found six ways a day could be written with something other than what it held, and one it cannot keep at all. A countdown exactly one day long is labelled "0 months before": HEY tries months before weeks before days and takes a remainder of up to a day as a match, which is every all-day event's one-day countdown. The parser took the zero and the SDK then sent nothing, which HEY reads as removal. The recording's own span says what a zero is, and any other zero stops the edit. A day that was moved is still listed under the date it stands for, but its own countdown ends where it now starts, so the identifier day never held it and the series' countdown was sent in its place. The moved-to day is read for it first. A clock time the zone springs over — 02:30 in New York on the second Sunday of March — is moved an hour on by ActiveSupport and an hour back by Go's time.Date, so a title-only edit would have moved the day. wallClockOn resolves it as HEY does. --calendar was both where the series was looked for and where the day was moved to, so a move to another calendar could never find its series. The day is read over every calendar now, and with --occurrence the flag is only the destination. A day HEY has written out carried its own id in `day` and `week` while its occurrence_id named the series, so the pair as served could not feed the edit. The row publishes the series id, as the docs already promised. --occurrence given empty, a script's unset variable, fell through to editing the whole series. It is a usage error now. An attached email the editor cannot read is left out of what HEY serves, and HEY clears the attachment whether the write sends an empty entry id or no key at all, so nothing client-side can keep it. The help and the docs say so instead.
…ld undo A second review of the occurrence edit found four more ways a write could reach something other than what was asked. A day HEY has written out has an id of its own, and HEY's event routes act on that id for that day alone. The last commit rewrote it to the series id in `day` and `week`, so the same `edit <id>` or `delete <id>` that had changed one day would have reached the whole series. It keeps its id again; parent_id names the series, and the docs say that is what --occurrence takes beside the occurrence_id. A future edit records the new series on the series' calendar unless told otherwise, so a day that had been moved to another calendar would have moved back with everything after it. The write names the day's calendar when --calendar does not. A future edit records the new series from the series' guest list and invites it, so a day that had come to have guests of its own would have lost them, and the series' guests would have been invited to it. That is refused until --invite says whose list the new series gets; the day alone keeps its own list as before. HEY shows one day of a series the series' countdown whenever the day has none of its own, and a write that names no countdown removes only the day's own, so --countdown 0 on one day of a series with a countdown was reported done and changed nothing. It is refused with where the countdown can come off: a future edit, or the series itself.
…nothing HEY labels a countdown by trying months, then weeks, then days, and taking a remainder of up to a whole day as a match, so eight days at midnight is "1 weeks before" and 29 days is "4 weeks before" — and sending the label back shortened every one of them by a day. The recording's span is the countdown plus however far into its day the event starts, never a day more, so the length is read off the span now; the label settles the one thing the span cannot, days against HEY's month, which is not a whole number of them. The one-day case that used to be special is just a span of a day. --repeat-times 0 and a negative count meant "forever", since the parser only knew a positive count from the zero value. It knows the flag was given now, and refuses a count under one — on a create and a whole-event edit as well as on the future edit that would have split the series on it. The day and the week help said an occurrence's id is its series, which is true of a day HEY draws from the series and not of one it has written out, whose id is its own and whose series is parent_id. Both say so.
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.
Summary
hey event edit <id>changed a whole series and nothing else, whilehey event dayandweekserved anoccurrence_idnothing on the CLI could take. HEY's own form asks anyone editing one day of a series whether to update just this event or this and all future events, and the SDK already speaks both throughUpdateOccurrence. The Omarchy HEY Calendar plugin wants that choice in a machine-safe shape.What changes
--occurrencetakes theoccurrence_idbyte for byte asday/weekserve it, and its series must be the positional id.--apply-tois required with it and takes onlycurrent(apply_to_future=0,OccurrenceScopeThisEvent) orfuture(apply_to_future=1,OccurrenceScopeThisAndFollowing); it is a usage error without--occurrence.currentrefuses--repeat,--repeat-untiland--repeat-times, matching HEY's rule that a schedule change cannot apply to one occurrence.[date, date+1), over--calendaror every calendar. HEY answers that window with every series still recurring through it and with the day itself where an earlier edit already wrote it out, so no wider scan is needed, and a day HEY has made its own keeps its own title, times, notes, reminders and countdown rather than the series'. An id that names one day of another series, or an event that does not repeat, is refused with the reason before anything is written.--inviteis given, exactly as before.--countdown 0is the explicit removal and an unreadable label stops the edit. Notes are served only as plain text and nothing can tell formatted from plain, so an occurrence edit that would send notes back as text refuses unless--allow-plain-notesaccepts the loss or--notesreplaces them.Output
Same envelope as every mutation:
summaryisOccurrence updatedorOccurrence and the following updated,datais the recording HEY answered. AfterfutureHEY splits the series and the answer is still the edited day, so callers should readday/weekagain for the new series id.Also
Help text,
docs/cli.md, the skill,API-COVERAGE.mdand the.surfacesnapshot are updated. MCP tool metadata is derived from the SDK model and needs no change.Summary by cubic
Adds editing one day of a repeating event with
hey event edit <series-id> --occurrence <occurrence_id> --apply-to current|future, bringing HEY's own form choice to the CLI; an id alone still edits the whole series unchanged.New Features
--occurrencetakes theoccurrence_idfromhey event day/weekexactly as served, and the series it names must match the positional id; an empty value is a usage error.--apply-tois required with--occurrenceand accepts onlycurrent(that day alone) orfuture(that day and all following);currentrefuses--repeat,--repeat-until, and--repeat-times.parent_idnames the series--occurrencetakes.futureedit makes HEY split the series, so callers should re-readday/weekfor the new series id before editing it further; it keeps a moved day on its current calendar and refuses a day whose guest list differs from the series' until--invitenames the new list.--occurrence,--calendaronly moves the day.Safeguards
--countdown 0removes it, which is refused for one day of a series that has one.--allow-plain-notesis accepted or--notesreplaces them.--repeat-timesunder one — which used to mean forever — is now refused there, on a create, and on afutureoccurrence edit.Written for commit b2c8504. Summary will update on new commits.