Skip to content

fix(source-granola): stop dropping notes on incremental window boundaries - #84279

Draft
devin-ai-integration[bot] wants to merge 3 commits into
masterfrom
devin/1786498422-source-granola-boundary-gap
Draft

fix(source-granola): stop dropping notes on incremental window boundaries#84279
devin-ai-integration[bot] wants to merge 3 commits into
masterfrom
devin/1786498422-source-granola-boundary-gap

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

What

source-granola's notes stream silently drops records that were created on an incremental slice-boundary date.

The notes DatetimeBasedCursor sliced a 30-day window (step: P30D) with bare-date bounds (datetime_format: "%Y-%m-%d") and cursor_granularity: P1D. Granola interprets a bare date as midnight, so created_before=D excludes everything that happened during day D, while P1D granularity starts the next slice at D+1. Every note created on a boundary date falls into that gap, and the sync still reports success.

The reporter measured 9 of 142 notes lost with start_date=2025-10-12, all landing exactly on the four 30-day boundaries. Reproduced directly against the API (from the issue):

GET /v1/notes?created_after=2025-11-10&created_before=2025-11-10  ->  0 records

while two notes exist on that date.

Resolves https://github.com/airbytehq/airbyte-internal-issues/issues/16931:

This is the non-breaking half of that issue (I-2). The cursor switch from created_at to updated_at (I-1) is a state-format change and ships separately as a breaking release, so this fix can merge on its own.

How

The windowing is removed rather than made finer-grained, and slice bounds are formatted as full timestamps:

   cursor_datetime_formats:
     - "%Y-%m-%dT%H:%M:%SZ"
     - "%Y-%m-%dT%H:%M:%S.%fZ"
-  datetime_format: "%Y-%m-%d"
+    - "%Y-%m-%d"
+  datetime_format: "%Y-%m-%dT%H:%M:%SZ"
-  end_time_option: {field_name: created_before, ...}
-  end_datetime: {...}
-  step: P30D
-  cursor_granularity: P1D

Two points worth a reviewer's attention:

  • Why unbounded instead of cursor_granularity: PT1S (the fix sketched in the issue): a windowed variant still leaves a gap of up to one granularity unit between created_before=T and created_after=T+1s, and Granola timestamps carry milliseconds, so a note created inside that second would still be dropped. With no end_time_option/step there is no boundary to fall through. The API has no updated_before parameter either, so the follow-up updated_at cursor has to be unbounded regardless — this lands the connector on that shape now.
  • "%Y-%m-%d" is required in cursor_datetime_formats. Existing connections have persisted bare-date state ({"created_at": "2026-06-01"}). CustomFormatConcurrentStreamStateConverter.parse_timestamp raises ValueError for any state value that matches none of cursor_datetime_formats + [datetime_format], so without that entry every existing sync would fail on its first read. Keeping it makes the state-format change forward-and-backward compatible, i.e. non-breaking and no reset required.

The user-facing start_date spec field is untouched and still takes YYYY-MM-DD; only the cursor's internal slice/state formatting changed.

Trade-off: with a single slice, notes now checkpoints once at the end of the stream rather than every 30 days, so an interrupted backfill resumes from the previous state instead of mid-range. Given notes volume and that correctness was the failing property here, that is the intended exchange.

Declarative-First Evaluation

Entirely declarative — a DatetimeBasedCursor configuration change. No components.py, no custom Python component.

Review guide

  1. airbyte-integrations/connectors/source-granola/manifest.yaml — the notes incremental_sync block
  2. airbyte-integrations/connectors/source-granola/unit_tests/test_notes_incremental_sync.py — new tests
  3. metadata.yaml / docs/integrations/sources/granola.md — 0.2.12 bump and changelog

Test Coverage

New connector-local unit_tests/ project (following the source-gong manifest-only layout). /v1/notes is mocked with a callback that emulates the API's created_after / created_before semantics as measured in the issue, and the declarative source is read end to end:

  • test_boundary_date_note_is_not_dropped — a note whose created_at lands exactly on a former 30-day boundary. Verified failing against the pre-fix manifest for the right reason (['before-boundary', 'after-boundary'] != ['before-boundary', 'on-boundary', 'after-boundary']) and passing with the fix.
  • test_notes_request_is_unbounded — asserts the exact outgoing query (created_after=2026-01-01T00:00:00Z, no created_before, single request).
  • test_legacy_date_state_is_accepted_and_emits_iso_state — pre-fix bare-date state syncs successfully and re-emits state in the new format.

poe test-unit-tests: 3 passed. poe test-integration-tests: 2 passed, 6 skipped (no acceptance-test scenarios configured for this connector — unchanged by this PR).

No live Granola credentials were available in this environment (SECRET_SOURCE-GRANOLA__CREDS was not retrievable), so the API behavior encoded in the mock comes from the reporter's curl output and the vendor OpenAPI spec rather than from a sync I ran myself.

User Impact

Notes created on an incremental window boundary date are no longer dropped. Existing connections keep syncing with their current state — no reset, no schema change, no config change.

Can this PR be safely reverted and rolled back?

  • YES 💚
  • NO ❌

Reverting restores the previous behavior (including the record loss). State written by 0.2.12 is a full timestamp; the pre-0.2.12 cursor accepts that format too, so a rollback does not break state parsing.

Link to Devin session: https://app.devin.ai/sessions/ce7406d5bb334fc5a23acbbc19c0db2d

Co-Authored-By: bot_apk <apk@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

Copy link
Copy Markdown
Contributor

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

PR Slash Commands

Airbyte Maintainers (that's you!) can execute the following slash commands on your PR:

  • 🛠️ Quick Fixes
    • /format-fix - Fixes most formatting issues.
    • /bump-version - Bumps connector versions, scraping changelog description from the PR title.
      • Bump types: patch (default), minor, major, major_rc, rc, promote.
      • The rc type is a smart default: applies minor_rc if stable, or bumps the RC number if already RC.
      • The promote type strips the RC suffix to finalize a release.
      • Example: /bump-version type=rc or /bump-version type=minor
    • /bump-progressive-rollout-version - Alias for /bump-version type=rc. Bumps with an RC suffix and enables progressive rollout.
  • ❇️ AI Testing and Review (internal link: AI-SDLC Docs):
    • /ai-prove-fix - Runs prerelease readiness checks, including testing against customer connections.
    • /ai-canary-prerelease - Rolls out prerelease to 5-10 connections for canary testing.
    • /ai-review - AI-powered PR review for connector safety and quality gates.
  • 📝 AI Documentation:
    • /ai-docs-review - AI-powered documentation review for PRs with connector changes.
    • /ai-create-docs-pr - Creates a documentation PR for connector changes, stacked on the current PR.
  • 🚀 Connector Releases:
    • /publish-connectors-prerelease - Publishes pre-release connector builds (tagged as {version}-preview.{git-sha}) for all modified connectors in the PR.
    • /enable-autopilot-rollouts - Enables autopilot progressive rollouts for the modified connector(s) in the PR, remediating "autopilot rollouts not enabled for {connector-name}" auto-merge blockers. Sets defaultRolloutMode: autopilot and enableProgressiveRollout: true, preserving any existing autopilotConfig.
      • Optional args: connector=<CONNECTOR_NAME> (defaults to the modified connectors in the PR), strategy=fast|slow|default (defaults to fast).
      • Example: /enable-autopilot-rollouts or /enable-autopilot-rollouts connector=source-faker strategy=slow
  • ☕️ JVM connectors:
    • /update-connector-cdk-version connector=<CONNECTOR_NAME> - Updates the specified connector to the latest CDK version.
      Example: /update-connector-cdk-version connector=destination-bigquery
  • 🐍 Python connectors:
    • /poe connector source-example lock - Run the Poe lock task on the source-example connector, committing the results back to the branch.
    • /poe source example lock - Alias for /poe connector source-example lock.
    • /poe source example use-cdk-branch my/branch - Pin the source-example CDK reference to the branch name specified.
    • /poe source example use-cdk-latest - Update the source-example CDK dependency to the latest available version.
  • ⚙️ Admin commands:
    • /force-merge reason="<REASON>" - Force merges the PR using admin privileges, bypassing CI checks. Requires a reason.
      Example: /force-merge reason="CI is flaky, tests pass locally"
📚 Show Repo Guidance

Helpful Resources

📝 Edit this welcome message.

@github-actions

Copy link
Copy Markdown
Contributor

Note

Autopilot progressive rollouts are not enabled for the following modified connector(s):

  • source-granola

This is a courtesy heads-up only — it does not block merge or fail any check.
To enable automatic progressive rollouts for the connector(s) above, comment
/enable-autopilot-rollouts on this PR. This sets defaultRolloutMode: autopilot
and enableProgressiveRollout: true in each connector's metadata.yaml,
preserving any existing autopilotConfig.

Co-Authored-By: bot_apk <apk@cognition.ai>
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Deploy preview for airbyte-docs ready!

Project:airbyte-docs
Status: ✅  Deploy successful!
Preview URL:https://airbyte-docs-24omwsu78-airbyte-growth.vercel.app
Latest Commit:123c671

Deployed with vercel-action

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

source-granola Connector Test Results

6 tests   4 ✅  5s ⏱️
2 suites  2 💤
2 files    0 ❌

Results for commit 123c671.

♻️ This comment has been updated with latest results.

Co-Authored-By: bot_apk <apk@cognition.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

connectors/source/granola hyd-fix Hydra: ai-fix stage has run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants