Skip to content

fix(cloudflare/workflow): harden Workflow reconcile + add lifecycle convergence tests#234

Open
sam-goodwin wants to merge 1 commit intomainfrom
claude/harden-cf-workflow
Open

fix(cloudflare/workflow): harden Workflow reconcile + add lifecycle convergence tests#234
sam-goodwin wants to merge 1 commit intomainfrom
claude/harden-cf-workflow

Conversation

@sam-goodwin
Copy link
Copy Markdown
Contributor

Hardens the Cloudflare Workflow reconciler. Previously every reconcile did a blind PUT regardless of live state, so every redeploy re-PUT and there was no signal when desired state already matched.

Reconciler changes

+ const observed = yield* getWorkflow({
+   accountId: acct,
+   workflowName: news.workflowName,
+ }).pipe(
+   Effect.map((r) => r as workflows.GetWorkflowResponse | undefined),
+   Effect.catchTag("WorkflowNotFound", () => Effect.succeed(undefined)),
+ );
+
+ if (
+   observed !== undefined &&
+   observed.className === news.className &&
+   observed.scriptName === news.scriptName
+ ) {
+   return {
+     workflowId: observed.id,
+     workflowName: observed.name,
+     className: observed.className,
+     scriptName: observed.scriptName,
+     accountId: acct,
+   };
+ }

  const result = yield* putWorkflow({ ... });

Observe live state first via getWorkflow. Only WorkflowNotFound is swallowed — auth, throttling, and 5xx errors must surface so the engine can retry or report cleanly. Skip the PUT when observed state already matches desired (no API churn on no-op redeploys).

New lifecycle tests

Each test runs destroy → deploy → … → destroy against a real Cloudflare account.

  • redeploy with same props is a no-op; workflow id is stable
  • reconcile resets scriptName re-targeted out-of-band via raw putWorkflow
  • reconcile re-creates a workflow deleted out-of-band (rides WorkflowNotFound in observe)
  • destroying an already-deleted workflow is a no-op (deleteWorkflow catches WorkflowNotFound)

Workflow has no read lifecycle so adopt(true) and "owned without --adopt" cases don't apply here. Physical-name (workflowName) replace is also outside scope: workflowName derives from the user-facing class name in Cloudflare.Workflow("X", ...) and the engine's Resource diff handles class-rename as a logical-id change.

The pre-existing output?.stableId TS error from #167/#179 in test.resources.ts is fixed the same way the prior hardening PRs did.

Distilled patch

No patch needed. Cloudflare's client/api.ts already maps HTTP status to categorized errors (TooManyRequests → throttling+retryable, 5xx → server+retryable), and WorkflowNotFound covers the only NotFound-like code the reconciler routes on.

…onvergence tests

Cloudflare Workflow's reconcile previously did a blind PUT-as-upsert on every deploy and trusted that the API would converge. That's correct for the happy path but skips observation entirely — which means an idempotent redeploy still re-PUTs, and there's no signal when desired state already matches live state.

- Observe live state via getWorkflow first; swallow only WorkflowNotFound (auth/throttling/5xx surface for engine retry, not as "no workflow live")
- Skip the put when observed className+scriptName already match desired — no API churn on no-op redeploys
- Adoption (output defined, olds undefined) now converges through the same observe path
- Add lifecycle convergence tests: idempotent redeploy preserves workflow id, scriptName drift via raw putWorkflow is overwritten, out-of-band deleteWorkflow triggers re-create, double-destroy is a no-op
- Pre-existing output?.stableId TS error from #167/#179 in test.resources.ts is fixed the same way the prior hardening PRs did

No distilled patch needed — Cloudflare's client/api.ts already maps HTTP status to categorized errors (TooManyRequests → throttling+retryable, 5xx → server+retryable), and WorkflowNotFound covers the only NotFound-like code the reconciler routes on.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@alchemy-version-bot
Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

alchemy

bun add alchemy@https://pkg.ing/alchemy/06f9d84

@alchemy.run/better-auth

bun add @alchemy.run/better-auth@https://pkg.ing/@alchemy.run/better-auth/06f9d84

@alchemy.run/pr-package

bun add @alchemy.run/pr-package@https://pkg.ing/@alchemy.run/pr-package/06f9d84

@alchemy-version-bot
Copy link
Copy Markdown
Contributor

alchemy-version-bot Bot commented May 5, 2026

Website Preview Deployed

URL: https://alchemyeffectwebsite-worker-pr-234-eua2x4z3w3sbk3v2.testing-2b2.workers.dev

Built from commit 06f9d84.


This comment updates automatically with each push.

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