Maestro task 0fe3f1f2-4932-4d10-81dd-74c4f630d215 — "Calendar list and selection" (Feature 16, task 2 of 5).
Done-condition
GET /api/v1/integrations/google/calendars uses the stored refresh token to call the Calendar API calendarList endpoint and returns [{id, summary, primary, accessRole}] filtered to calendars with writer or owner access; PUT /api/v1/integrations/google/calendar {calendar_id} validates the id against that list and stores calendar_id + calendar_name. Tests against a fake Calendar API cover listing, filtering out reader-only calendars, selecting a valid calendar, rejecting an unknown id, and the not-connected case returning a clear 409.
Scope
Listing and selection only. Push to Calendar (5885d2b1), token-failure bookkeeping (b8d8da5a), the settings screen (677261bc) and Feature 17's shopping-list trigger are untouched. The developer states in NOTES what it chose when a Calendar call here meets an expired or revoked grant, rather than half-building task b8d8da5a's disconnect semantics or hiding the failure.
Points for review
The listing returns exactly the named fields and nothing else from Google's response (the fake deliberately adds description, summaryOverride, colours and reminders) and filters to writer/owner (the fake includes reader and freeBusyReader).
Selection validates the client's id against the same filtered list rather than trusting it, stores id and name, and rejects an unknown id with a clear error (422 unknown_calendar, stored row unchanged).
The not-connected case answers 409 not_connected without calling Google.
internal/gcal/calendars.go is new and reuses the reviewed gcal.Service machinery, including the per-account mutex for anything reading or writing the credential, and the token-secrecy discipline (nothing in a response, header, log or error). It adds Config.CalendarAPI (a base URL the binary supplies, with a GoogleCalendarAPI constant) so the fake can be pointed at — not a new env var or boot requirement; please confirm that.
migrations/queries/settings.sql gained SetIntegrationsGoogleCalendar :execrows and sqlc was regenerated (committed, no drift); no new migration.
go test -race -count=1 ./... — 26 packages ok, 0 failures (server 129.2s)
CGO_ENABLED=0 go build ./... — ok
make sqlc-check SQLC=$(go env GOPATH)/bin/sqlc — no drift
cd web && npm run typecheck && npm test && npm run build — PASS, 45 files / 764 tests (no web change)
The developer also ran deliberate mutation checks (removing the writer/owner filter, trusting the client's id, dropping the lock in SelectCalendar, dropping the invalid_grant mapping, stopping paging, changing the summary, leaking Google's body or the refresh error into error text) and reports each made at least one new test fail.
## Task
Maestro task `0fe3f1f2-4932-4d10-81dd-74c4f630d215` — "Calendar list and selection" (Feature 16, task 2 of 5).
## Done-condition
`GET /api/v1/integrations/google/calendars` uses the stored refresh token to call the Calendar API calendarList endpoint and returns `[{id, summary, primary, accessRole}]` filtered to calendars with writer or owner access; `PUT /api/v1/integrations/google/calendar {calendar_id}` validates the id against that list and stores `calendar_id` + `calendar_name`. Tests against a fake Calendar API cover listing, filtering out reader-only calendars, selecting a valid calendar, rejecting an unknown id, and the not-connected case returning a clear 409.
## Scope
Listing and selection only. Push to Calendar (`5885d2b1`), token-failure bookkeeping (`b8d8da5a`), the settings screen (`677261bc`) and Feature 17's shopping-list trigger are untouched. The developer states in NOTES what it chose when a Calendar call here meets an expired or revoked grant, rather than half-building task `b8d8da5a`'s disconnect semantics or hiding the failure.
## Points for review
- The listing returns exactly the named fields and nothing else from Google's response (the fake deliberately adds `description`, `summaryOverride`, colours and reminders) and filters to writer/owner (the fake includes reader and freeBusyReader).
- Selection validates the client's id against the same filtered list rather than trusting it, stores id and name, and rejects an unknown id with a clear error (422 `unknown_calendar`, stored row unchanged).
- The not-connected case answers 409 `not_connected` without calling Google.
- `internal/gcal/calendars.go` is new and reuses the reviewed `gcal.Service` machinery, including the per-account mutex for anything reading or writing the credential, and the token-secrecy discipline (nothing in a response, header, log or error). It adds `Config.CalendarAPI` (a base URL the binary supplies, with a `GoogleCalendarAPI` constant) so the fake can be pointed at — not a new env var or boot requirement; please confirm that.
- `migrations/queries/settings.sql` gained `SetIntegrationsGoogleCalendar :execrows` and sqlc was regenerated (committed, no drift); no new migration.
## Evidence (orchestrator-run gate at 1e3e42e0b14d3519d22e939af0382a9ca65f93dc)
- `gofmt -l .` — empty; `go vet ./...` — clean
- `go test -race -count=1 ./...` — 26 packages ok, 0 failures (server 129.2s)
- `CGO_ENABLED=0 go build ./...` — ok
- `make sqlc-check SQLC=$(go env GOPATH)/bin/sqlc` — no drift
- `cd web && npm run typecheck && npm test && npm run build` — PASS, 45 files / 764 tests (no web change)
The developer also ran deliberate mutation checks (removing the writer/owner filter, trusting the client's id, dropping the lock in SelectCalendar, dropping the invalid_grant mapping, stopping paging, changing the summary, leaking Google's body or the refresh error into error text) and reports each made at least one new test fail.
Implementation commit: `1e3e42e0b14d3519d22e939af0382a9ca65f93dc`
GET /api/v1/integrations/google/calendars trades the stored refresh token
for an access token, reads every calendarList page and answers
[{id, summary, primary, accessRole}] for writer and owner calendars only.
PUT /api/v1/integrations/google/calendar {calendar_id} validates the id
against that same filtered listing and stores calendar_id and
calendar_name; any other id is 422 unknown_calendar.
Both hold the service's account lock, answer 409 not_connected with
nothing connected, and 502 reconnect_required when Google no longer
accepts the grant, leaving the connection as it is. The gcaltest fake
gains the refresh grant and a pausable calendarList endpoint.
Maestro task 0fe3f1f2-4932-4d10-81dd-74c4f630d215.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deduplicate by calendar id as the pages are collected, keeping the
first occurrence in Google's order (MQ-126-01), with a test serving
two overlapping pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Task
Maestro task
0fe3f1f2-4932-4d10-81dd-74c4f630d215— "Calendar list and selection" (Feature 16, task 2 of 5).Done-condition
GET /api/v1/integrations/google/calendarsuses the stored refresh token to call the Calendar API calendarList endpoint and returns[{id, summary, primary, accessRole}]filtered to calendars with writer or owner access;PUT /api/v1/integrations/google/calendar {calendar_id}validates the id against that list and storescalendar_id+calendar_name. Tests against a fake Calendar API cover listing, filtering out reader-only calendars, selecting a valid calendar, rejecting an unknown id, and the not-connected case returning a clear 409.Scope
Listing and selection only. Push to Calendar (
5885d2b1), token-failure bookkeeping (b8d8da5a), the settings screen (677261bc) and Feature 17's shopping-list trigger are untouched. The developer states in NOTES what it chose when a Calendar call here meets an expired or revoked grant, rather than half-building taskb8d8da5a's disconnect semantics or hiding the failure.Points for review
description,summaryOverride, colours and reminders) and filters to writer/owner (the fake includes reader and freeBusyReader).unknown_calendar, stored row unchanged).not_connectedwithout calling Google.internal/gcal/calendars.gois new and reuses the reviewedgcal.Servicemachinery, including the per-account mutex for anything reading or writing the credential, and the token-secrecy discipline (nothing in a response, header, log or error). It addsConfig.CalendarAPI(a base URL the binary supplies, with aGoogleCalendarAPIconstant) so the fake can be pointed at — not a new env var or boot requirement; please confirm that.migrations/queries/settings.sqlgainedSetIntegrationsGoogleCalendar :execrowsand sqlc was regenerated (committed, no drift); no new migration.Evidence (orchestrator-run gate at
1e3e42e0b1)gofmt -l .— empty;go vet ./...— cleango test -race -count=1 ./...— 26 packages ok, 0 failures (server 129.2s)CGO_ENABLED=0 go build ./...— okmake sqlc-check SQLC=$(go env GOPATH)/bin/sqlc— no driftcd web && npm run typecheck && npm test && npm run build— PASS, 45 files / 764 tests (no web change)The developer also ran deliberate mutation checks (removing the writer/owner filter, trusting the client's id, dropping the lock in SelectCalendar, dropping the invalid_grant mapping, stopping paging, changing the summary, leaking Google's body or the refresh error into error text) and reports each made at least one new test fail.
Implementation commit:
1e3e42e0b14d3519d22e939af0382a9ca65f93dcGET /api/v1/integrations/google/calendars trades the stored refresh token for an access token, reads every calendarList page and answers [{id, summary, primary, accessRole}] for writer and owner calendars only. PUT /api/v1/integrations/google/calendar {calendar_id} validates the id against that same filtered listing and stores calendar_id and calendar_name; any other id is 422 unknown_calendar. Both hold the service's account lock, answer 409 not_connected with nothing connected, and 502 reconnect_required when Google no longer accepts the grant, leaving the connection as it is. The gcaltest fake gains the refresh grant and a pausable calendarList endpoint. Maestro task 0fe3f1f2-4932-4d10-81dd-74c4f630d215. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Independent Codex gpt-6-sol review: APPROVED for this exact commit.
Independent Codex gpt-6-sol review: APPROVED for this exact commit.