Skip to main content

ADR-019: Exact owned-calendar reschedule

Status: Accepted

Decision class: Target architecture

Tracked work: #922, under #899.

Acceptance provenance​

The lead accepted the narrow v2 design on 2026-10-04 after independent review. Design SHA256: b12c3f8f3315dccc11abdfb9de374c5dad63d83ec47f8a84150f31f91e35aed5. Correction review PASS SHA256: eb9f1b69d2d4629a47a46927dec069b5ca41bb10f1ea7680d89b79d3f7a30a4a. Lead acceptance SHA256: fa43436fab0cf55bac2165169c8bf2dc0cd41cbc2882e77f5e33d6c83a12d2da. The prior P1 scope finding was accepted and fixed. Acceptance defines the target; it does not establish implementation, live Google operations or Shipped truth.

Context​

Existing Calendar preparation is readonly. One reviewed reschedule must not overwrite a concurrent edit, duplicate an ambiguous mutation, confuse calendar manager access with data ownership, or renew historical authority during recovery. Reuse Calendar connections/bindings, canonical native jobs/approvals/artifacts and the narrow original-Root observation pattern; Gmail semantics do not become Calendar authority.

Decision​

One literal, model-free reschedule of one selected existing owned nonrecurring timed default event. No attendees, conference, attachments, special/unknown event fields, labels, all-day events, create, batch, invitation, automatic undo or learning. calendar.event.reschedule.v1 and a separate readonly calendar.event.observe-reschedule.v1 use the existing native job repository. The selected calendar/event IDs, original event ETag/full protected state, account/connection/consent/task/input/Goal revisions and original authenticated Root are immutable approved bindings. Existing readonly Calendar paths remain.

Exact identity and scopes​

Two new profiles require exact actual scope evidence on every refresh:

  • read: https://www.googleapis.com/auth/calendar.events.owned.readonly, https://www.googleapis.com/auth/calendar.calendarlist.readonly, openid, email;
  • write: https://www.googleapis.com/auth/calendar.events.owned, https://www.googleapis.com/auth/calendar.calendarlist.readonly, openid, email.

The documented email permission URL may represent email once; duplicate aliases, missing/malformed/extra scopes block before UserInfo/Calendar. No broad calendar.readonly, shared-event or metadata fallback. Same-token pinned UserInfo requires stable case-sensitive sub, verified email and known issuer provenance; pair credentials by subject/issuer, with exact email as additional drift check. No cached scope/identity substitutes for current token evidence.

Selected calendarList.get supplies actual ID, current accessRole, primary, secondary dataOwner and IANA timezone; no calendars.get. Require owner role AND primary ID equal verified email or secondary dataOwner equal verified email. Owner role alone is manager access. No email alias normalization. Event creator self/current verified email and organizer self/selected calendar ID must agree. Missing/ambiguous facts block. Disclose calendar-list metadata visibility separately from owned-event access; runtime contacts remain selected-ID-only.

Exact patch and readback​

Preview old/new RFC3339 seconds/numeric offsets, explicit IANA zones and UTC instants. Reject gaps, offset/zone mismatches, ambiguity without an explicit offset, invalid/end-before-start/no-op times. Duration1 minute–24 hours, future start within365 days; last fresh owner/event observations at most 10 seconds at claim and contact. Frozen full accepted resource rejects unknown fields and preserves protected title/description/location/reminders/identity/property/exclusion state.

One private correlation key seraphReschedule_ plus24 hex characters (41 ASCII characters) and random256-bit value is generated once and approved. Existing private/shared properties stay protected; source collision/over-bound maps block. PATCH adds only that property and exact start/end, using original ETag in If-Match, sendUpdates=none, conferenceDataVersion=0, supportsAttachments=false. No deprecated notification flag. UI qualifies provider reminders/notification behavior; no universal no-email guarantee. Marker is correlation, not provider idempotency or cryptographic authorship.

One PATCH, no automatic retry/rebase on412 or response loss. Definite412 is Conflict with proof of refusal; ambiguous contact is Unknown. An independent readonly exact-ID GET must verify different ETag, exact new instants/timezones, marker and unchanged full protected projection. PATCH response alone is not success. Server updated/sequence/ETag/display URL may change; no silent content normalization. Input/source/event JSON are bounded and duplicate/nonfinite JSON keys/values are rejected; source is never truncated for approval.

Native authority and finite work​

One task/input and canonical WorkflowRunState checkpoint own each original effect. Immutable versioned native intent includes original Root/Goal/attempt/ fence/approval/input/account/source/request/artifact digests and fixed effect. Stage Vault/private bytes/identity/HTTP/evidence outside every SQL writer. Current live Root, Goal/task/input/revisions, consent/connections, lease/fence, staged freshness and exact empty-attachment approval are checked in a short SQLite writer; approval consumption and intent install are atomic. A second dispatch/contact CAS permanently spends the sole PATCH slot and invalidates prior closure before contact. No filesystem/Vault/network/nested writer there.

Priority60, attempts1, outstanding1, original runtime at most 120 seconds, approval at most 5 minutes capped by all original finite authority. HTTPS at most 10 seconds, zero redirects/retries. Persistent contacts: verification6 (two refresh/UserInfo plus two selected lists), preview4, execution9, original combined13, recovery4. No pagination/search. Replay keeps original request/marker/deadline/budget. Cancel before dispatch needs actual owned transport closure and proof no PATCH may exist. Postdispatch cancel/timeout/crash stays Unknown with original effect and liability. Versioned closure binds actual adapters' zero active/unsettled operations and equal started/settled counts to intent, fence and full history; empty registry/expired lease/restart does not prove closure.

Readonly recovery and privacy​

Same still-live original Root/principal only, separate current active RecoveryGoal/revision and explicit new finite readonly grant/auxiliary job. Expired/replaced/security-revoked Root blocks all contacts; no new-login/email continuity inference. Original Goal may be changed/closed but must retain its original owner provenance. Original write deadline may be expired. Require original nonrunning/no lease and positively bound owned transport closure. Recovery refreshes only readonly credential and performs selected list/event GET. Missing marker/coincident times/deleted or later-edited event cannot prove absence or authorize replay. Current exact state can yield verified observation.

One specialized pure writer appends bounded observation history to the original effect and completes current-fenced auxiliary atomically, checking both revisions. Preserve original status, immutable checkpoint/dispatch, deadline, Goal, authority/approval, effect write status, lease/fence and liabilities exactly. History is observation, never dispatch authority or original success renewal. No automatic reverse patch; any compensation needs a new current preview/ETag/ approval. Private encrypted artifacts use held nofollow publication and0700/ 0600 modes; plaintext access and availability share current Root/task/Goal/ consent/connection checks. Revocation hides bytes and cached UI content, with metadata/recovery retained. No inference/canonical memory write; explicit no_learning.

Consequences​

Accepted finite write permission schema supplement​

Lead acceptance of the independently reviewed supplement r2 (826fb2d47acdd1f857984d671b864b450581b3762bed627d71b86e01839a0090) permits one focused CalendarRescheduleConsent table and its bounded indexes. It binds server-derived owner/original Root, exact Goal/revision, read/write profile revisions and scope/identity/Vault metadata digests, encrypted selected calendar and event binding revisions, finite capped expiry, request/digest and active/revoked revision. It stores permission only; the native job checkpoint continues to own one-use intent, dispatch and original effect liability. No legacy readonly consent is promoted or reinterpreted and there is no backfill.

One active grant per original owner/Root/event binding includes naturally expired active rows. Expiry blocks authority without changing or renewing the row. Explicit local revocation by the still-live original owner/Root releases the slot even when the old Goal/grant has closed or expired; it needs no provider contact or Vault secret read. Fresh regrant requires a new explicit UUID, current finite active Goal, verified exact pair and freshly confirmed acknowledgements. It cannot renew or adopt previous jobs or approvals.

CPU core and existing Calendar/Mail paths remain usable without these grants. Strict ownership/schema/scope/closure policy can block ambiguous accounts or old receipts. Current-state observation cannot prove historical authorship or absence of provider notifications; owner/ACL read/write races remain bounded and explicit. Live Google usefulness requires separately authorized proof.

Verification​

Actual auth/SQLite/Vault/native approval/jobs/artifacts/UI with stateful intercepted Google HTTP only. Prove exact one patch/full readback, concurrent412/no retry, scope/account/ownership/DST/unknown-field/protected-state negatives, concurrent atomic claim rollback, stale Root/Goal/consent/lease/fence/input bytes, duplicate/ restart/cancel/response loss and actual closure. Prove readonly auxiliary with expired original deadline/changed Goal preserves liabilities and both row CAS; wrong Root/provenance/closure yields zero contacts. Managed production-gated browser success/Conflict/Unknown/recovery/current-private-read states and fresh independent whole review are required before completion. No real invitations.

Current official sources checked2026-10-04: events.get, events.patch, calendarList.get, CalendarList, resource versions, extended properties, scope guide and OpenID identity.