fix(workflow, custom): billing attribution passthrough#5657
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview
For custom blocks, the child runs as the source owner in the source workspace; the handler calls A unit test asserts the parent snapshot is passed via Reviewed by Cursor Bugbot for commit f5e44bd. Configure here. |
Greptile SummaryThis PR passes billing attribution into nested workflow execution contexts. The main changes are:
Confidence Score: 4/5The custom-block billing path needs a fix before merging.
apps/sim/executor/handlers/workflow/workflow-handler.ts
|
| Filename | Overview |
|---|---|
| apps/sim/executor/execution/executor.ts | Copies context extension billing attribution into execution metadata so nested tool calls can read it. |
| apps/sim/executor/execution/types.ts | Adds the typed billing attribution extension and documents its precedence over metadata. |
| apps/sim/executor/handlers/workflow/workflow-handler.ts | Forwards attribution into child executors and resolves custom-block attribution against the source workflow context. |
| apps/sim/executor/handlers/workflow/workflow-handler.test.ts | Adds mocks and coverage for non-custom child workflow attribution passthrough. |
Reviews (1): Last reviewed commit: "fix(workflow, custom): billing attributi..." | Re-trigger Greptile
| childBillingAttribution = await resolveBillingAttribution({ | ||
| actorUserId: loadUserId, | ||
| workspaceId: childWorkflow.workspaceId, | ||
| }) |
There was a problem hiding this comment.
Custom Blocks Charge Source Workspace
When a cross-workspace custom block runs, this branch resolves the billing snapshot for loadUserId and childWorkflow.workspaceId instead of the consuming execution. A consumer with valid billing can now fail because the source workspace has no payer, or usage can be attributed to the source owner even though this custom-block path is supposed to bill the consumer workflow.
There was a problem hiding this comment.
There is no cross-customer charge. Custom blocks are org-scoped: getCustomBlockAuthority only resolves blocks where customBlock.organizationId matches the consumer workspace's organization, and returns null for non-org workspaces. Source and consumer workspaces always bill the same org account. The difference is bookkeeping only (usage-log actor, member cap), identical to pre-enforcement.
The consumer's snapshot cannot be used here. The internal routes reject any header whose actorUserId/workspaceId don't match the authenticated request scope — and the request scope for a custom-block child is the source owner in the source workspace by design (same identity as a deployed API/schedule run of the source workflow). Attaching the consumer's snapshot would fail that check on every call.
There was a problem hiding this comment.
You're right on both counts. The getCustomBlockAuthority code confirms it:
const consumerWs = await getWorkspaceWithOwner(consumerWorkspaceId, { includeArchived: true })
if (!consumerWs?.organizationId) return null
// ...
.where(and(eq(customBlock.type, type), eq(customBlock.organizationId, consumerWs.organizationId)))The org-id equality enforcer means source and consumer are always in the same org (and personal workspaces can't use custom blocks at all). The "cross-workspace" concern I raised collapses to intra-org bookkeeping, and since the internal routes authenticate requests as the source owner in the source workspace, attaching the consumer's snapshot would fail that scope check on every call. The implementation is correct. Withdrawing the concern.
Summary
Billing attribution passthrough for custom blocks, workflow blocks to allow blocks within that need attribution to work.
Type of Change
Testing
Tested manually
Checklist