Skip to content

fix(cloud): Unwrap {"manifest": ...} envelope in builder draft_manifest - #1381

Merged
Aaron ("AJ") Steers (aaronsteers) merged 1 commit into
release-candidate/v1from
devin/1792014276-builder-draft-manifest-shape
Oct 16, 2026
Merged

Aaron ("AJ") Steers (aaronsteers) merged 1 commit into
release-candidate/v1from
devin/1792014276-builder-draft-manifest-shape

Conversation

@aaronsteers

Copy link
Copy Markdown
Member

Overview

👉 TL;DR: The draft manifest of a connector builder project could come back wrapped in an extra layer, while the published manifest came back bare. Both now have the same shape.

Specifically, a new _unwrap_builder_manifest() is used by CustomCloudSourceDefinition.draft_manifest and by rename().

Changes

  • Root cause: get_with_manifest returns the stored manifestDraft JSON unchanged in declarativeManifest.manifest (airbyte-platform-internal ConnectorBuilderProjectsHandler.kt:318). Drafts saved as a DeclarativeSourceManifest envelope ({"manifest": {...}, "spec": ..., ...}) came out wrapped.
  • The envelope is unwrapped only when manifest is a dict and none of the top-level keys of a bare manifest (streams, definitions, check) are present. Bare manifests and None pass through unchanged.

Test plan

  • New unit tests cover a wrapped draft, a bare draft, and no draft. Lint and the touched tests pass locally.

Link to Devin session: https://app.devin.ai/sessions/a51d808f26c9402fafc93e7640648187
Open in Devin Desktop: https://app.devin.ai/desktop/session/a51d808f26c9402fafc93e7640648187?variant=devin
Requested by: Aaron ("AJ") Steers (@aaronsteers)

get_with_manifest returns the stored draft under
declarativeManifest.manifest; drafts persisted as a
DeclarativeSourceManifest envelope surface as {"manifest": {...}, ...}
while the published manifest is bare. draft_manifest now unwraps that
envelope so both shapes match.

Co-Authored-By: AJ Steers <[email protected]>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

Copy link
Copy Markdown

👋 Greetings, Airbyte Team Member!

Here are some helpful tips and reminders for your convenience.

💡 Show Tips and Tricks

Testing This PyAirbyte Version

You can test this version of PyAirbyte using the following:

# Run PyAirbyte CLI from this branch:
uvx --from 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792014276-builder-draft-manifest-shape' pyairbyte --help

# Install PyAirbyte from this branch for development:
pip install 'git+https://github.com/airbytehq/PyAirbyte.git@devin/1792014276-builder-draft-manifest-shape'

PR Slash Commands

Airbyte Maintainers can execute the following slash commands on your PR:

  • /fix-pr - Fixes most formatting and linting issues
  • /uv-lock - Updates uv.lock file
  • /test-pr - Runs tests with the updated PyAirbyte
  • /prerelease - Builds and publishes a prerelease version to PyPI
📚 Show Repo Guidance

Helpful Resources

Community Support

Questions? Join the #pyairbyte channel in our Slack workspace.

📝 Edit this welcome message.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The narrowly scoped normalization matches the intended behavior, with no unresolved findings.

0 open findings

What changed in this PR

Normalizes connector builder draft manifests so wrapped drafts have the same shape as published manifests.

Changes:

  • Adds a shared unwrapping helper for draft reads and renames.
  • Preserves bare manifests and missing-draft handling.
  • Adds regression coverage for wrapped and bare drafts.
File Description
tests/​unit_tests/​test_cloud_credentials.py Tests wrapped and bare draft manifest reads.
airbyte/​cloud/​connectors.py Normalizes builder manifests in draft_manifest and rename().

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-code-quality

github-code-quality Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Python

Python / code-coverage/pytest-no-creds

release-candidate/v1 d9f652f devin/1792014276-bui... f79d85c +/-
65% 79% +14%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792014276-bui... f79d85c +/-
airbyte/mcp/cloud.py 52% 83% +31%
airbyte/_util/api_util.py 36% 68% +32%
airbyte/cloud/connectors.py 34% 77% +43%
airbyte/mcp/_datadog.py 0% 50% +50%
airbyte/mcp/_user_identity.py 0% 93% +93%
airbyte/_direct...ctors/models.py 0% 94% +94%
airbyte/mcp/_telemetry.py 0% 94% +94%
airbyte/mcp/_sso_auth.py 0% 96% +96%
airbyte/cloud/models.py 0% 97% +97%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%

Python / code-coverage/pytest

release-candidate/v1 d9f652f devin/1792014276-bui... f79d85c +/-
71% 81% +10%
View coverage of the 10 most impacted files.
File release-candidate/v1 d9f652f devin/1792014276-bui... f79d85c +/-
airbyte/mcp/cloud.py 52% 83% +31%
airbyte/cloud/connectors.py 51% 82% +31%
airbyte/mcp/_datadog.py 0% 50% +50%
airbyte/mcp/int..._registry_ui.py 0% 92% +92%
airbyte/mcp/_user_identity.py 0% 93% +93%
airbyte/_direct...ctors/models.py 0% 94% +94%
airbyte/mcp/_telemetry.py 0% 94% +94%
airbyte/mcp/_sso_auth.py 0% 96% +96%
airbyte/cloud/models.py 0% 97% +97%
airbyte/mcp/int...nc_status_ui.py 0% 97% +97%

Python / code-coverage/pytest-unit

release-candidate/v1 devin/1792014276-bui... f79d85c +/-
— 76% —

Line coverage data for the release-candidate/v1 branch is not yet available.

View coverage of the 10 most covered files.
File release-candidate/v1 devin/1792014276-bui... f79d85c +/-
airbyte/cloud/models.py — 97% —
airbyte/mcp/_sso_auth.py — 96% —
airbyte/_direct...ctors/models.py — 94% —
airbyte/cloud/client.py — 90% —
airbyte/exceptions.py — 89% —
airbyte/mcp/cloud.py — 83% —
airbyte/progress.py — 80% —
airbyte/cloud/connectors.py — 77% —
airbyte/_util/api_util.py — 68% —
airbyte/shared/...ql_processor.py — 68% —

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 14, 2026 22:06 UTC

@aaronsteers
Aaron ("AJ") Steers (aaronsteers) marked this pull request as ready for review October 16, 2026 00:07
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) merged commit cc28ed4 into release-candidate/v1 Oct 16, 2026
33 of 34 checks passed
@aaronsteers
Aaron ("AJ") Steers (aaronsteers) deleted the devin/1792014276-builder-draft-manifest-shape branch October 16, 2026 00:07

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Devin Review

builder_project = project["builderProject"]
draft_manifest = (
project["declarativeManifest"]["manifest"]
_unwrap_builder_manifest(project["declarativeManifest"]["manifest"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Rename drops saved draft metadata

When a draft uses an envelope, rename() sends only its nested manifest to update_connector_builder_project. The replacement draftManifest loses saved envelope fields such as spec and version.

Learn more

A builder draft can store a DeclarativeSourceManifest object with both a nested manifest and separate envelope fields. The rename operation reads that saved draft and sends draft_manifest to update_connector_builder_project, which writes it back as builderProject.draftManifest. Unwrapping the draft before this write drops the other envelope fields from storage. The read-only draft_manifest property can still return a bare manifest without changing the stored draft.

Example: A saved draft of {"manifest": {"type": "DeclarativeSource", "version": "0.1.0"}, "spec": {"connectionSpecification": {}}, "version": 1} becomes {"type": "DeclarativeSource", "version": "0.1.0"} after a rename; its separate spec is lost.

Recommended fix: Pass the original project["declarativeManifest"]["manifest"] into the update operation during rename. Unwrap only when returning draft_manifest to callers, and add a rename test that asserts the full envelope reaches the update request.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: this is real. rename() sends the unwrapped manifest back as draftManifest. For a draft stored in the {"manifest": ..., "spec": ..., "version": ...} envelope, the rename therefore drops the envelope fields, including any spec that exists only on the envelope.

Rename shouldn't rewrite the stored draft. The minimal fix is to send the raw project["declarativeManifest"]["manifest"] during rename and unwrap only in the read-only draft_manifest property. That needs a rename test asserting the full envelope reaches update_connector_builder_project. This PR is already merged, so I've asked AJ whether to do it as a follow-up PR.

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.

2 participants