Skip to content

fix(deploy): pass persona source.kind into connect-session + status poll#133

Merged
khaliqgant merged 1 commit into
mainfrom
fix/deploy-connect-respects-persona-source-scope
May 23, 2026
Merged

fix(deploy): pass persona source.kind into connect-session + status poll#133
khaliqgant merged 1 commit into
mainfrom
fix/deploy-connect-respects-persona-source-scope

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

DO NOT MERGE until AgentWorkforce/cloud#1001 ships. Until it does, sending the new fields is a no-op on the cloud side (today's behavior preserved), so this PR is safe to land in either order — but the user-visible bug only resolves once both halves are live.

Background

Personas declare each integration with a `source` discriminator (`deployer_user` / `workspace` / `workspace_service_account`). Three components are supposed to agree on what that means:

Component Honors source.kind?
persona-kit (declaration + default)
cloud runtime dispatcher (`persona-integration-resolver.ts:261`)
cloud connect-session endpoint ❌ (always writes `workspace_integrations`)
workforce deploy CLI (preflight, post #131) ✅ reads from the right table
workforce deploy CLI (connect-side) ❌ never sent the scope

Result: every persona defaulting to `deployer_user` (the persona-kit default) runs OAuth visibly, the cloud reports success, the row lands in the wrong table, `/me/integrations` stays empty, the preflight re-prompts on the next deploy, and the runtime dispatcher silently has nothing to resolve at tick time. `workspace_service_account` has the same problem.

What this PR does (workforce half)

  • `IntegrationConnectResolver.connect(...)` now takes `source?: IntegrationSource` (mirrors what `isConnected` already received in fix(deploy): scope-aware + configKey-aware integration preflight #131).
  • `relayfileIntegrationResolver.connect(...)` POSTs `scope: { kind, name? }` in the connect-session body and appends `scope=...` (+ `serviceAccountName=...` when applicable) to the status-poll URL.
  • The walker in `connectIntegrations` threads each integration's declared `source` through to the resolver — same field already used by the preflight, so the read and write scopes are guaranteed to match by construction.
  • Defaults to `{ kind: 'deployer_user' }` when `source` is omitted (matches the persona-kit default).

Cloud half (cloud#1001)

The cloud connect-session endpoint must:

  • Accept `scope: { kind: 'deployer_user' | 'workspace' | 'workspace_service_account'; name?: string }` in the request body.
  • Insert the new row into `user_integrations` (kind=deployer_user) or `workspace_integrations` (kind=workspace; or kind=workspace_service_account with `service_account_name`).
  • Default to `workspace` when scope is omitted (today's behavior — no breakage for callers that haven't been updated).
  • Status poll endpoint accepts the same `scope` query param.

Test plan

  • `pnpm --filter @agentworkforce/deploy test` — 121/121 pass
  • 3 new tests covering each `source.kind` variant going out on the wire (deployer_user default, workspace explicit, workspace_service_account with name attribution).
  • Existing connect / status-poll / auth-recovery tests still green.
  • After cloud#1001 ships: end-to-end against cloud. Verify a fresh `deployer_user` deploy creates a row visible via `GET /me/integrations`, the preflight skips OAuth on the next deploy, and the runtime dispatcher resolves `ctx.github` at tick time.

Dependencies

Repo Issue Must ship before merging
AgentWorkforce/cloud #1001

🤖 Generated with Claude Code

Closes the third leg of the integration-scope contract. The persona
declares source (deployer_user / workspace / workspace_service_account)
and persona-kit + the cloud's runtime dispatcher both honor it — but
the cloud's connect-session endpoint ignores it and always writes to
workspace_integrations. So a persona declaring (or defaulting to)
deployer_user runs OAuth, the row lands in workspace_integrations,
/me/integrations stays empty, the preflight re-prompts on the next
deploy, and the runtime dispatcher silently has nothing to resolve at
tick time.

This change threads the persona's declared `source` from each
integration declaration through to `relayfileIntegrationResolver.connect`,
which now posts `scope: { kind, name? }` in the connect-session body and
appends `scope=...` (+ optional `serviceAccountName`) to the status
poll URL. The cloud half — accepting and honoring those fields — is
tracked separately as AgentWorkforce/cloud#1001 and must merge first.

DO NOT MERGE until cloud#1001 ships. Until it does, sending the new
fields is a no-op on the server side (today's behavior preserved), so
this PR is safe to land in either order, but the user-visible bug
only resolves once both halves are live.

Tests: 121/121 pass, 3 new covering each scope.kind variant going out
on the wire (deployer_user default, workspace explicit, and
workspace_service_account with name attribution).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: efc48d76-65fa-4078-bf98-8dc6d0e80ea7

📥 Commits

Reviewing files that changed from the base of the PR and between 07cf3b2 and f25853f.

📒 Files selected for processing (2)
  • packages/deploy/src/connect.test.ts
  • packages/deploy/src/connect.ts

📝 Walkthrough

Walkthrough

This PR extends the integration connection flow to scope creation and status polling by integration source (deployer_user, workspace, or workspace_service_account). The connect method signature now accepts an optional source parameter, which is propagated through the Relayfile resolver to the cloud connect-session endpoint and status polling, with a helper translating source into the expected scope JSON format.

Changes

Integration Source Scoping

Layer / File(s) Summary
Connect interface signature update
packages/deploy/src/connect.ts
IntegrationConnectResolver.connect signature extends to include an optional source?: IntegrationSource parameter for scoping connection creation.
Relayfile resolver and scope helper
packages/deploy/src/connect.ts
Relayfile resolver's connect method computes an effective source, builds session body with scope, and sends it to the connect-session endpoint. New scopeRequest helper maps IntegrationSource to cloud scope JSON shape, including name propagation for service accounts. Status polling now includes scope and serviceAccountName parameters.
Orchestration integration source threading
packages/deploy/src/connect.ts
connectIntegrations loop now passes persona-derived source through to integrations.connect call instead of calling without scope information.
Test coverage for source-scoped connections
packages/deploy/src/connect.test.ts
Updated default connect test to expect scope in session body. Added three new tests covering workspace source, workspace_service_account source with name propagation, and default deployer_user when source is omitted.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant relayfileResolver
  participant connectSessionAPI as /connect-session
  participant statusAPI as /status polling
  Caller->>relayfileResolver: connect(provider, source)
  relayfileResolver->>relayfileResolver: derive effectiveSource
  relayfileResolver->>relayfileResolver: build sessionBody with scope
  relayfileResolver->>connectSessionAPI: POST (allowedIntegrations, scope)
  connectSessionAPI-->>relayfileResolver: sessionId
  loop poll until connected
    relayfileResolver->>statusAPI: GET (scope, serviceAccountName)
    statusAPI-->>relayfileResolver: status
  end
  relayfileResolver-->>Caller: connectionId
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • AgentWorkforce/workforce#97: Introduced the new IntegrationSource discriminator that this PR now uses to scope connect-session requests and default to deployer_user.
  • AgentWorkforce/workforce#131: Both PRs make integration operations persona-source aware by adding and using source/defaulting to deployer_user and scoping requests accordingly in connect.ts.
  • AgentWorkforce/workforce#116: Both PRs modify the connectIntegrations orchestration in connect.ts to extend integration connection setup (main PR adds scope/source propagation, retrieved PR adds authRecovery/token-refresh behavior on 401).

Poem

🐰 Scopes bloom where sources flow,
Deployer, workspace, service accounts show,
Each connection knows its rightful place,
Tests affirm the proper grace!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: passing persona source.kind into connect-session and status polling, which directly aligns with the primary objective of fixing the integration source scoping issue.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the background, implementation details, test coverage, and dependencies required for the fix.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deploy-connect-respects-persona-source-scope

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@khaliqgant khaliqgant merged commit 99195fc into main May 23, 2026
3 checks passed
@khaliqgant khaliqgant deleted the fix/deploy-connect-respects-persona-source-scope branch May 23, 2026 09:34
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