Skip to content

feat(calendar): guests on new events, and notifications and colour in the editor → 3.156.0-alpha (C12d + C13 of #884) - #976

Open
BBYU26 wants to merge 7 commits into
mainfrom
feat/calendar-new-guests-reminders-colour
Open

BBYU26 wants to merge 7 commits into
mainfrom
feat/calendar-new-guests-reminders-colour

Conversation

@BBYU26

@BBYU26 BBYU26 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

PR A of the regrouped plan for #884 (docs/calendar-remaining-plan-2026-10-22.md): C12d, C13a and C13b, one commit
per layer, then one commit for the version bump and What's New → 3.156.0-alpha.

What users get

  • New events can have guests. In the full editor (+ New event, or c) you can add people by address or from
    people already in your calendars, mark them optional, and choose what they may do. Before the event is made, PM
    asks whether Google should send the invitations and says how many, with nothing picked for you. Rooms are never
    emailed. Quick create (Shift+C) still makes events without guests.
  • Notifications in both editors. Up to five, each a notification or an email; on all-day events, days or weeks
    before at a time of day. A timed event on its calendar's defaults shows them as rows, as Google does; change one
    and the list becomes the event's own. An all-day event names its calendar's defaults, with Change to set its own.
    "Use {calendar}'s defaults" puts them back. Google sends them, not PM.
  • Colour in both editors. Google's eleven colours or the calendar's, from a keyboard-friendly Colour list. An
    event with a Google Calendar label keeps the colour its label gives it, and PM says so.
  • Changing only notifications or colour never asks about emailing guests, except for "this and the following
    events", whose new part is a new event that invites the guests again.
  • If Google answers a save without making the change, PM says so instead of "Saved"; if Google keeps only part of
    it, PM says "Google didn't keep all of your changes, so check the event."
  • On a repeating event Google keeps locked, Busy/Free, notifications and colour can now be changed for "This event"
    or "All events". Before, every change to a locked repeating event was refused.

Labels were cut from C13 by the spec's Adopted list (and C14's spec agrees). PM doesn't read, paint or set labels;
the plan row's "labels in both editors, labels on the grid" is superseded (DECISIONS 22-10). The rest is the Backlog
card "Google Calendar event labels: show and change them", starting with probes L-C2 to L-C4.

Per layer

C12d: new events can have guests (15a47ad)

  • create_calendar_event takes notify and asked. It reuses C12a's GuestAnswer::for_write: guests and no
    answer → needs_notify; an answer for other numbers → needs_notify_again; nobody to invite → none. The
    question comes after every other refusal (bad time, unsynced calendar, create_block, the plan), and nothing is
    sent or held before an answer.
  • CREATABLE gains attendees and the three permission keys. A new pure attendees::new_list checks each address,
    refuses repeats in any case, trims and keeps the draft's order. Each permission is sent only when it differs from
    Google's default.
  • SeriesPlan::create(calendar, id, body, own, chosen): the step sends the user's answer when it invites someone,
    and none otherwise. A plan with guests and no answer won't run. A held create keeps its plan, so Check again
    re-sends the first answer and never asks.
  • The editor's create mode gets the guest box and the email question. The backend's re-ask is shown at most once per
    Save; a second one shows as a banner. No save, Ctrl+S, Enter or close works from behind the question, and text in
    the guest box that isn't an address blocks Save (closing asks first).
  • Copy fixed outside the spec's table: help.ts said new events go out "with nobody invited"; the README and
    create_block's doc said the same.

C13a: saves can carry notifications and colour; a Google label keeps its colour (1d108c7)

  • RemindersDraft, ColourDraft = Calendar | Colour{id} (a "label" kind is refused at the boundary). Both drafts
    gain reminders and colour; FieldPermissions gains both; EventForEdit gains reminders, color_id,
    label_id. Two new reasons: label_read_only, reminders_unreadable.
  • PATCHABLE, REPEAT_PATCHABLE and CREATABLE gain reminders and colorId, never eventLabelId. A list goes
    whole and compares in any order; one Google holds is sent back as it is; new rows are checked (five at most, known
    method, 0 to four weeks, no repeats). A colour must be "1" to "11"; "Calendar colour" sends null. A colour save's
    conflict check also watches the label.
  • Gate: a lock leaves notifications and colour open; a label closes the colour; a list PM can't read closes the list.
  • C2-H1: a 200 reply whose copy holds every field sent as it was answers ignored_by_google, not "Saved"; one
    that holds part of the save is landed with the warning partly_applied (review round 1, below).
  • Repeating events: a series PATCH may carry both; "this and following" now carries notifications and colour onto the
    new part (before, they were silently dropped). C1-M1: a lock refuses only a delete or a change to title,
    description, place, time, repeat or guests. All and splits are checked against the series' own copy too.
  • Incidental fix: a refusal that said "reminders" now says "notifications".

C13b: notifications and colour in the editor (d10cbc9, TypeScript only)

  • New pure reminders.ts (words, units, all-day "N days before at HH:MM", rowProblem, sameReminders).
  • eventDraft.ts: the draft gains reminders and colour, seeded, compared, sent and rebased; a new event starts on
    the calendar's defaults and colour; remindersAfterKindSwitch(base, draft, editable, reset); guestNotice treats both
    as personal, like busy/free.
  • New ReminderList.tsx and EventColorPicker.tsx (a listbox in a popover, full keyboard model, focus back on the
    button, a ring in --ink plus ✓ on the chosen colour). Both editors draw the rows after Location, off by their
    permissions; EventEditor gains colorOf(calendarId). A row that can't be committed blocks every way to save.
  • Contrast test for the chosen ring (--ink on --panel, every accent, both contrast levels); PICKER_ORDER.

Version (37579fd)

3.156.0-alpha in the lockstep files and one What's New entry. Incidental fix in the same file: the 3.148.8 to
3.153.1 entries (dated 18 to 20 October) sat above 3.148.7 (21 October) because #968 merged before the stack; they
are now dated 21 October, so the list reads newest first. Nothing sorts or tests on these dates.

Deviations from Google (Google beside each)

PM Google
The invitations question is two radios with counts; Create stays off until one is picked Back / Don't send / Send buttons, no counts
A new event with guests gets no Meet link (Meet is C18) May add Meet, depending on the user's setting
Your own address on your own calendar is refused: "That's the organiser, who is on it already." Lists the organiser on the event already (inferred; probe G5 records it)
A labelled event's colour is read-only; PM sends you to Google Calendar Lets you change a labelled event's label or colour
All-day "Change" starts with one row, "1 day before at 09:00" Shows the all-day defaults as rows; the API doesn't give them to PM
Timed defaults keep a line naming them above the rows Shows the rows alone
"Use {calendar}'s defaults" button No such button
The same notification twice is refused: "Already on the list."; Add picks the next free row Add may put Google's usual row in twice (inferred)
A list PM can't read shows nothing as the event's, with a reason, and stays as it is No equivalent (Google reads its own lists)
Colour list: a 6-column grid with the focused colour's name under it; no ✓ on Calendar colour (its fill is a theme colour) A grid with names on hover
"This and following" on a locked repeating event stays refused, even for notifications or colour Google refuses the split too: it rewrites the repeat rule, which Google locks
Switching all-day/timed puts the event's own notifications back to the defaults and says so; switching back restores them, and says so when that replaces a list you made Resets them (inferred, probe NB8/E9f)
Picking the calendar whose address is on the list as a guest takes that guest off, and says so Lists the organiser itself (inferred)
Quick create has no guests, notifications or colour Neither has Google's (inferred)

Flipped tests (on purpose)

  • C12d (C10's create tripwires, lifted for guests): patch.rs creatable_is_the_c11_list_with_no_guests →
    creatable_is_the_c11_list_and_the_guests, insert_body_sends_only_what_a_create_may; plan.rs
    a_create_plan_refuses_a_key_a_create_may_not_send; calendar_edit.rs
    a_create_sends_no_guests_reminders_conference_or_notes → a_create_sends_no_reminders_conference_or_notes;
    dto.rs new_event_draft_reads_what_the_editor_sends. TS: eventDraft (two, guests: []), useEventWrites
    (createEvent's arguments, Check again's call), NewEventEditor/QuickCreatePopover (sentFor and sent keys
    gain guests), one WriteNotices fixture.
  • C13a: nothing_but_the_patchable_fields_is_ever_sent (13 keys; eventLabelId still never sent);
    creatable_is_the_c11_list_and_the_guests → creatable_is_the_c11_list_guests_notifications_and_colour;
    a_create_plan_refuses_a_key_a_create_may_not_send (eventLabelId still refused);
    a_create_sends_no_reminders_conference_or_notes → a_create_sends_no_conference_or_notes_and_no_defaults;
    a_master_pm_cant_read_or_change_says_why (a locked series' read_only() is None);
    a_locked_copy_keeps_only_what_google_lets_it_change (notifications and colour open).
  • C13b: editReasons (C13a's hidden-reason test removed with shownReasons; colorId now names the colour);
    EventEditor "no row yet" → "a labelled event's colour and unreadable notifications stay as they are, saying
    why"; NewEventEditor "Save creates…" and QuickCreatePopover "Enter in the title creates…" (sent keys gain
    colour and reminders, both null); eventDraft "toNewEvent sends every field".

Every new guard was mutation-checked by its builder (broken, test seen failing, restored); six that first survived
had their tests strengthened.

Review round 1

  • A save Google kept part of (a new title beside a colour it wouldn't clear) said "kept the event as it was.
    Nothing changed." and skipped the mirror, so the next title edit hit a conflict over PM's own change. Now the
    reply is read field by field (patch::any_applied, patch::none_applied, Shown::Partly): ignored only when
    every field sent is as it was; part of it lands Google's copy in the mirror and the session with the new warning
    partly_applied. The same reading runs after a lost answer and after a server error that may have applied, where
    part of the save used to read "Nothing was saved". A one-off made to repeat is read on a 200 too.
  • New events: picking the calendar whose address is on the list as a guest takes that guest off and says so (it
    was a stuck "Organiser" row, sent, and counted differently from the backend); the account-as-me count now has a
    test.
  • Notifications: a switch back that replaces a list you made says so; a conflict that puts Google's time back over a
    switch puts that switch's notifications back too (a save would otherwise send a reset whose reason was gone); the
    new event editor's line names the calendar picked; an all-day row of 0 days says "Pick at least 1 day before." at
    any time of day.
  • Copy: help, What's New and DECISIONS qualify "never asks" for "this and following", and What's New says only timed
    events show their defaults as rows.
  • Test flipped on purpose: rowProblem(-540, true) now expects "Pick at least 1 day before."; the EventEditor kind
    switch test expects the new line when switching back over a list made; one C11 fixture
    (a_one_off_with_no_zone_anywhere_refuses_a_repeat) now answers with the New York nodes it was sent, since a
    reply in London's would now read as a save Google kept otherwise.

Review round 2

  • Notifications: "Use {calendar}'s defaults" pressed before a switch between all-day and timed (or again after
    one) was silently replaced by the event's own list on switching back, and by a conflict that put Google's time
    back over the switch; the save then sent no notifications. remindersAfterKindSwitch now takes and returns
    whether the draft's defaults are a switch's own, untouched since (a ref in the event editor, cleared by any change
    to the list); only those go back to the event's list. Defaults the user picked stay and are saved, as Google web
    would leave them.
  • Tests: eventDraft "a switch there and back keeps the defaults I picked"; EventEditor "switching to all day and
    back keeps the defaults I picked", "defaults picked again after the switch stay when switching back" and "a
    conflict that undoes the switch to all day keeps the defaults I picked". The existing kind-switch calls pass the
    new argument; nothing else flipped.

Live tests for Bobby

All in docs/calendar-live-tests-2026-10-10.md, dev build, PM write test:

  1. "New events with guests (C12d, PR A: 3.156.0-alpha)": G4-G6 from devtools, then CG1-CG9 in the app, with
    your second Google account as the guest (CG10 from review round 1). Note: whether Google lists you (the organiser) among the guests; that no
    Meet link appears; whether Don't send still puts the event on the guest's calendar; how many invitations arrive
    after Check again's re-send (CG9); Google web's own question words.
  2. "Notifications and colour: C13a probes (devtools, PR A: 3.156.0-alpha)": N1-N14. The ones that decide code:
    N2 (Google takes the empty list beside "use the defaults"), N3 (900 minutes reads "1 day before at 09:00"),
    N6 (null clears a colour, or PM must send ""), N8 (reads carry the label without asking), N5 (Google's colour
    names and order; NB10 again through the editor).
  3. "Notifications and colour in the editors (C13b, PR A: 3.156.0-alpha)": NB1-NB19 (NB17-NB18 from review
    round 1, NB19 from round 2). Set PM write test's own
    default notifications in Google web first, and put them back afterwards.

Delete every probe event in Google web afterwards.

Gate

Full just check on the branch tip, in a capped scope (22-10-2026): passed, exit 0, first run, nothing to fix.

Recipe Result
prettier All matched files use Prettier code style
eslint 0 errors; 26 warnings, all react-refresh in files this PR doesn't touch
tsc, cargo-fmt, ruff, ruff-fmt clean
version lockstep: all 7 files agree on 3.156.0-alpha
files, headers, license-subset, ci-membership, sync-set ✓ (803 files placed; 726 SPDX headers; 34 tables)
ipc-commands ✓ 310 names in ipc.ts match the 313 registered
calendar-write-fence ✓ 152 Rust and 515 TypeScript files; no model-path module reaches a calendar write
script-deps, action-pins, requirements-lock, node-version ✓
npm-licenses, sidecar-licences, model-licences ✓
frontend-test (vitest) 181 files, 2,663 tests passed
build-frontend built
clippy (-D warnings), cargo-check clean
rust-test 2,483 passed, 0 failed, 2 ignored
sidecar-test 111 tests OK (8 skipped)
deny advisories ok, bans ok, licenses ok, sources ok
pip-audit, npm-audit no known vulnerabilities
gitleaks, gitleaks-history no leaks found
zizmor no findings

Review

Reviewed by four areas (guests on new events, the backend save rules, the editors, tests and copy), each finding checked by a skeptic, then fixed and re-reviewed until a round came back dry (3 rounds):

  • Round 1 (11 kept, 2 refuted; 23653c8): a save Google keeps only part of now lands as Saved with "Google didn't keep all of your changes, so check the event." instead of "Nothing changed" (and the mirror and the session are updated); a one-off made to repeat gets the same check; picking the calendar a typed guest names takes that guest off, so the counts match; the account-as-me count is now tested on a secondary calendar; all-day "0 days" says what to pick; switching kind back no longer replaces a list you made, and a conflict that undoes a switch drops its note; help and What's New say a "this and the following events" change does ask.
  • Round 2 (2 kept; 7d711c0): switching kind there and back, or a conflict that undoes it, keeps a "Use the calendar's defaults" you pressed.
  • Round 3: dry. Full gate passed after the fixes.
  • Also: 3.155.0's What's New (unreleased) no longer says a Busy or Free change never asks.

For Bobby: live tests CG10, NB17 (only if probe N6 shows Google ignores a null colour) and NB18 were added to the live-test doc. A test-only issue: EventEditor.test.tsx's afterEach uses vi.clearAllMocks(), which leaves unused mockResolvedValueOnce answers queued, so one failing conflict test can make the next fail too; changing it touches how 96 tests reset, so it's left for its own change.

Part of #884

🤖 Generated with Claude Code

BBYU26 and others added 4 commits October 22, 2026 17:34
The full editor's create mode gets the guest box (suggestions, Optional,
the three guest permissions) and asks Send or Don't send before a new
event with guests goes to Google, with nothing picked. The create command
takes the answer and the numbers it was given for; a new event with guests
and no answer, or an answer for other numbers, is asked about and nothing
is sent. Guests go in the insert body as new entries, permissions only
where they differ from Google's. A held create keeps its answer, so Check
again sends what was first sent. Quick create stays without guests.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bel keeps its colour (C13a of #884)

The save core for an event's notifications and colour, with no editor
control sending them yet (C13b adds those). Notifications go whole and
compare in any order; a colour goes as one of Google's eleven, or null
for the calendar's own. Labels were cut from C13 by its critiques: PM
never writes one, and an event with a label keeps its colour read-only.
A locked repeating event now saves its notifications and colour, and a
single save Google answers without the change says it was ignored.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#884)

Both editors, for an existing event and a new one, gain Notifications and
Colour under the location, as Google Calendar's event page has them. A
timed event shows its calendar's defaults as rows, and the first change
makes the list its own; an all-day event names its defaults with Change.
Up to five, each a notification or an email; a row that can't be saved
says why and holds the save. The colour is the calendar's or one of
Google's eleven, picked from a list. Neither ever asks about emailing
guests, a labelled event keeps its colour, and switching between timed
and all-day puts the event's own notifications back to the defaults.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ew events, notifications and colour (#884)

Bumps to 3.156.0-alpha for PR A (C12d, C13a, C13b) and adds one What's New
entry covering guests on new events, notifications and colour in both
editors, and a Google label keeping its colour. Labels themselves were cut
from C13, so the entry promises none.

Also re-dates the 3.148.8 to 3.153.1 entries to 2026-10-21: they sat above
3.148.7 (21 October) because #968 merged before the stack, so the list now
reads newest first. Nothing sorts or tests on these dates.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@BBYU26 BBYU26 added enhancement New feature or request rust Pull requests that update rust code javascript Pull requests that update javascript code labels Oct 22, 2026
Copilot AI balanced review requested due to automatic review settings October 22, 2026 18:09
@BBYU26 BBYU26 self-assigned this Oct 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

BBYU26 and others added 3 commits October 22, 2026 20:04
- A one-event save Google kept part of (a new title beside a colour it
  wouldn't clear) said "kept the event as it was. Nothing changed." and
  skipped the mirror and the session. The reply is now read field by
  field (patch::any_applied, patch::none_applied, Shown::Partly): ignored
  only when every field sent is as it was; part of it lands Google's copy
  and says "Google didn't keep all of your changes, so check the event."
  (new warning partly_applied). The same reading runs after a lost answer
  and after a server error that may have applied, which said "Nothing was
  saved".
- A one-off made to repeat is read on a 200 too: a rule landed beside a
  colour Google kept is saved with the same warning, never a plain Saved.
- New events: picking the calendar whose address is on the list as a
  guest takes that guest off and says so, instead of a stuck "Organiser"
  row that was sent and counted unlike the backend. The account-as-me
  count now has a test.
- Notifications: a switch back to the event's kind that replaces a list
  the user made says so; a conflict that puts Google's time back over a
  switch puts that switch's notifications back and drops its line; the
  new event editor's line names the calendar picked; an all-day row of 0
  days says "Pick at least 1 day before." at any time of day.
- Copy: help, What's New qualify "never asks" for this and following
  events, and What's New says only timed events show defaults as rows.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Notifications: "Use {calendar}'s defaults" pressed before a switch
  between all-day and timed, or again after one, was silently replaced
  by the event's own list on switching back, and by a conflict that put
  Google's time back over the switch; the save then sent no
  notifications. remindersAfterKindSwitch now takes and returns whether
  the draft's defaults are the ones a switch put there, untouched since
  (a ref in the event editor, set from each switch, cleared by any change
  to the list, kept in step by the conflict rebase). Only those go back
  to the event's list on the way back (C2-L5); defaults the user picked
  stay and are saved, as Google web would leave them. The new event
  editor passes false: its seed is the calendar's defaults, so the
  result is the same either way.
- Tests: eventDraft "a switch there and back keeps the defaults I
  picked"; EventEditor "switching to all day and back keeps the defaults
  I picked", "defaults picked again after the switch stay when switching
  back" and "a conflict that undoes the switch to all day keeps the
  defaults I picked".

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nge never asks (#884)

The new part of a "this and the following events" change invites the guests
again, so it does ask. 3.155.0 is unreleased, so its entry is corrected.

Part of #884

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request javascript Pull requests that update javascript code rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants