Skip to content

fix(calendar): make OAuth failures loud and diagnosable; document permanent fix - #130

Merged
nfeuer merged 2 commits into
mainfrom
fix/calendar-oauth-permanence
Jul 10, 2026
Merged

fix(calendar): make OAuth failures loud and diagnosable; document permanent fix#130
nfeuer merged 2 commits into
mainfrom
fix/calendar-oauth-permanence

Conversation

@nfeuer

@nfeuer nfeuer commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Problem

Google Calendar has died recurrently with invalid_grant (currently dead since 2026-06-24; token last refreshed 2026-06-07). Root cause: the OAuth consent screen is in Testing publishing status, so Google expires refresh tokens 7 days after issuance. The code made it worse by degrading silently — logger.warning only, no alert, no health probe (violates the no-silent-fallbacks convention).

Changes (spec_v3.md §3.2.2, §12.1)

  • GoogleCalendarClient.authenticate() raises typed CalendarAuthError (reason=refresh_rejected|token_missing_headless) with remediation in the message.
  • _try_build_calendar_client logs event_type="fallback_activated" on every degraded path and takes an on_unavailable callback; cli.py wires it to dispatch_fallback_alert → dead calendar now posts to Discord.
  • SelfDiagnostic gains a calendar-token live probe (boot + donna health).
  • New runbook docs/operations/calendar-oauth.md: root cause, one-time Console fix (publish app to Production), relink steps.
  • Followups logged: Gmail alert/probe parity; fold the failure contract into §3.2.2.

Operator action required (the actual permanent fix)

Publish the OAuth app to Production in Google Cloud Console, then relink once — see the runbook. Until then the token dies weekly.

Testing

  • 2,246 unit tests pass; ruff clean.
  • New: tests/unit/test_self_diagnostic_calendar.py (4 tests), TestAuthenticateRefreshRejected in test_calendar_client.py.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty

nfeuer and others added 2 commits July 10, 2026 02:04
…manent fix

The calendar integration has died recurrently with invalid_grant because the
OAuth consent screen is in Testing status (Google expires refresh tokens after
7 days). The code compounded this by degrading silently: a bare
logger.warning, no user-visible alert, no health probe, and a bare
RuntimeError from google-auth.

Per spec_v3.md §3.2.2 (calendar direct API) and the no-silent-fallbacks
convention:

- GoogleCalendarClient.authenticate() now raises a typed CalendarAuthError
  (reason=refresh_rejected | token_missing_headless) with remediation steps
  in the message, instead of leaking google.auth RefreshError.
- _try_build_calendar_client logs event_type="fallback_activated" on every
  degraded path and accepts an on_unavailable callback; cli.py wires it to
  NotificationService.dispatch_fallback_alert so a dead calendar posts to
  Discord instead of only Loki.
- SelfDiagnostic gains a calendar-token check that live-probes the refresh
  token at boot and via `donna health`, reporting invalid_grant with the fix.
- New runbook docs/operations/calendar-oauth.md: root cause (Testing-status
  7-day expiry), one-time Console fix (publish to Production), relink steps.
- Followups: Gmail needs the same alert/probe parity; spec §3.2.2 should
  absorb the failure/alerting contract.

The permanent fix requires one operator action documented in the runbook:
publish the OAuth app to Production, then relink once.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty
- conftest calendar-client stub accepts the new on_unavailable kwarg.
- cli.py guards the Optional notification_service in the unavailable
  callback (factory already logged fallback_activated).
- Drop an unused type-ignore in the health-check token probe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K7Ppai9Xbhv5iEuGBwi3ty
@nfeuer
nfeuer merged commit 3a7b6d0 into main Jul 10, 2026
12 checks passed
@nfeuer
nfeuer deleted the fix/calendar-oauth-permanence branch July 10, 2026 16:19
nfeuer pushed a commit that referenced this pull request Jul 10, 2026
Resolve conflict in docs/superpowers/specs/followups.md by keeping both
the calendar-OAuth (PR #130, now on main) and output-standard follow-up
entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0163xNY4AKcd3pHF8CYVGPH5
nfeuer pushed a commit that referenced this pull request Jul 10, 2026
Resolve conflict in docs/superpowers/specs/followups.md by keeping both
the calendar-OAuth (PR #130, now on main) and local-LLM-reliability
follow-up entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0163xNY4AKcd3pHF8CYVGPH5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant