Skip to content

test: check context.workflowRunCreatedAt in every ci route function - #215

Open
CahidArda wants to merge 1 commit into
mainfrom
feat/ci-expect-workflow-run-created-at
Open

CahidArda wants to merge 1 commit into
mainfrom
feat/ci-expect-workflow-run-created-at

Conversation

@CahidArda

Copy link
Copy Markdown
Collaborator

Stacked on #214. The base is feat/workflow-created-at-dedup; once #214 is merged, retarget this to main. The diff here is only the CI check.

Why

#214 makes every request of a workflow run carry Upstash-Workflow-CreatedAt. QStash uses it to tell apart runs started with the same workflow run id and the same data. The SDK reads it from the incoming request into context.workflowRunCreatedAt and sends it with everything the run publishes. So if a request ever reaches a route function without it, that run is back to being deduplicated against earlier runs with the same id. This PR makes the CI app fail when that happens.

What changed

  • expectWorkflowRunCreatedAt(context) (examples/ci/app/ci/utils.ts) throws unless context.workflowRunCreatedAt is a positive finite number. It's covered in ci/utils.test.ts (0, -1, NaN, undefined and a numeric string all throw).
  • It's called as the first line of every route function in the CI app: 47 of them, across serve, serveMany/createWorkflow, invoked and invoker workflows, and the same-run-id target and child from feat: send Upstash-Workflow-CreatedAt so runs with the same id aren't deduplicated #214. Route functions run from the top on every request, so this runs before the first step, in every invocation: steps, waits and their timeouts, context.call results, invoke results, webhook resumes, and the authorization dry run of first invocations.
  • auth/custom/target is left out, with a comment. It isn't a real workflow; it only receives the forged requests of auth/custom/workflow to check that they're rejected, and one of them is a fake failure callback without a creation time.

Why it holds today

  • Every CI run is created by QStash (client.trigger, /v2/trigger, invoke), and QStash already sends Upstash-Workflow-CreatedAt with every request it delivers for a run (deliver.go). This doesn't depend on qstash-server#1129 being deployed.
  • wait-for-event/notifier-workflow is started through context.call, which is a direct first invocation with no header. It passes because of the serve change in feat: send Upstash-Workflow-CreatedAt so runs with the same id aren't deduplicated #214: a first invocation that doesn't come from QStash gets the trigger time as its creation time, and the trigger sends that same value.

Checks

🤖 Generated with Claude Code

https://claude.ai/code/session_017dm7F4hg2khgYDqBwP9nx5

@CahidArda
CahidArda added this pull request to stack #216 September 30, 2026 12:58
Base automatically changed from feat/workflow-created-at-dedup to main October 1, 2026 12:37
expectWorkflowRunCreatedAt(context) throws unless the context carries a
positive creation time of the run. It is called at the top of every route
function in the ci app, before the first step, so it is checked in every
invocation of a workflow endpoint.

auth/custom/target is left out: it only receives the forged requests of
auth/custom/workflow, one of them a fake failure callback without a
creation time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017dm7F4hg2khgYDqBwP9nx5
@CahidArda
CahidArda force-pushed the feat/ci-expect-workflow-run-created-at branch from aad87fd to bb093c9 Compare October 1, 2026 12:39
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