{
  "schemaVersion": 2,
  "date": "2026.07.26",
  "publishedAt": "2026-07-26T15:02:05-07:00",
  "timeZone": "America/Los_Angeles",
  "title": "Making Google authorization recoverable in Hiro",
  "publicationStatus": "Published",
  "executiveSummary": [
    "Hiro now recognizes rejected or malformed Google credentials as a recoverable authorization condition instead of exposing a generic Gmail RefreshError.",
    "When invalid credentials are encountered, Hiro automatically starts one interactive Google consent request, preserves the rejected token as a recoverable timestamped backup, and returns structured status to the task dashboard.",
    "The task dashboard now explains the authorization state, offers an explicit Reauthorize Google action, waits for consent to complete, and retries the Gmail import after authorization succeeds.",
    "The live recovery flow completed successfully, and a metadata-only Gmail API request confirmed that the replacement authorization works."
  ],
  "workstreams": [
    {
      "title": "Shared Google authorization service",
      "status": "Completed",
      "details": [
        "Added a shared credential loader for Gmail and Google Calendar so both integrations use the same refresh, recovery, and token-storage behavior.",
        "The loader first accepts a valid saved authorization, then attempts a normal refresh, and converts rejected or malformed credentials into an actionable reauthorization requirement.",
        "A single guarded background consent flow is reused while active, preventing repeated Gmail requests from opening multiple authorization windows.",
        "Successful consent replaces the token atomically. The rejected token is copied to a timestamped local backup before the consent request begins."
      ]
    },
    {
      "title": "API and dashboard recovery experience",
      "status": "Completed",
      "details": [
        "Added API endpoints for requesting Google reauthorization and reading its current state.",
        "Gmail import now returns a structured unauthorized response with a stable error code, human-readable explanation, and current authorization state when credentials cannot be refreshed.",
        "Added a Reauthorize Google action to the Gmail import dialog.",
        "The dashboard polls authorization state and automatically retries an interrupted import after fresh consent succeeds."
      ]
    },
    {
      "title": "Live authorization handoff",
      "status": "Completed",
      "details": [
        "Triggered the new reauthorization endpoint through the live task dashboard.",
        "Hiro reported that it is waiting for user authorization and created one recoverable backup of the rejected token.",
        "Google's interactive consent page was delegated to the computer's default browser and completed successfully.",
        "A read-only Gmail profile request verified the replacement credential without printing account information or reading messages."
      ]
    }
  ],
  "decisions": [
    "Keep the integration read-only by requesting only Gmail read-only and Calendar read-only scopes.",
    "Treat invalid_grant and malformed saved credentials as recoverable authorization conditions, not generic import failures.",
    "Start reauthorization automatically when invalid credentials are encountered, while retaining an explicit dashboard action for clarity and manual recovery.",
    "Run consent asynchronously so Hiro remains responsive and the dashboard can display progress.",
    "Preserve rejected authorization material locally for recovery, but never expose its contents through the API, dashboard, tests, logs, or public journal."
  ],
  "validation": [
    {
      "check": "Focused Google authorization and productivity tests",
      "status": "passed",
      "result": "Twelve tests passed, covering automatic recovery on rejected refresh, atomic token replacement, recoverable token backup, actionable API mapping, Gmail import behavior, and existing productivity routes."
    },
    {
      "check": "Live task dashboard",
      "status": "passed",
      "result": "The Gmail import dialog loaded with the new Reauthorize Google action and displayed the waiting-for-authorization message after the request."
    },
    {
      "check": "Live authorization state",
      "status": "passed",
      "result": "The status endpoint reported waiting_for_user, confirmed that a backup was created, and exposed no credential values."
    },
    {
      "check": "Fresh Gmail API authorization",
      "status": "passed",
      "result": "Hiro reported authorized and a metadata-only Gmail profile request completed successfully without exposing account information."
    },
    {
      "check": "Public journal validation",
      "status": "passed",
      "result": "The Hiro timestamped-entry unit tests passed, generated-journal validation passed for 44 entries, and the production site build completed successfully."
    }
  ],
  "currentState": [
    "Hiro is running with the new recovery code.",
    "Google authorization completed successfully and the replacement credential is active.",
    "Invalid saved credentials will now trigger the same single-window reauthorization path automatically.",
    "The previous rejected token has been preserved locally as a recoverable backup."
  ],
  "limitations": [
    "No labeled Gmail import was run during this work session, so live review-item ingestion remains to be checked separately.",
    "No Gmail messages were read or imported during this work session."
  ],
  "nextSteps": [
    "Retry the labeled Gmail import and confirm that review-first ingestion and duplicate prevention work with live data.",
    "Add recurring tasks and reminders in a later task-list iteration as previously planned."
  ],
  "disclosureNote": "This public entry omits credential paths, token values, OAuth client details, account information, email data, local network details, and backup contents."
}
