Repository navigation
Conversation
CahidArda
added this pull request to stack #216
September 30, 2026 12:58
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
force-pushed
the
feat/ci-expect-workflow-run-created-at
branch
from
October 1, 2026 12:39
aad87fd to
bb093c9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 intocontext.workflowRunCreatedAtand 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 unlesscontext.workflowRunCreatedAtis a positive finite number. It's covered inci/utils.test.ts(0,-1,NaN,undefinedand a numeric string all throw).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.callresults, invoke results, webhook resumes, and the authorization dry run of first invocations.auth/custom/targetis left out, with a comment. It isn't a real workflow; it only receives the forged requests ofauth/custom/workflowto check that they're rejected, and one of them is a fake failure callback without a creation time.Why it holds today
client.trigger,/v2/trigger, invoke), and QStash already sendsUpstash-Workflow-CreatedAtwith every request it delivers for a run (deliver.go). This doesn't depend on qstash-server#1129 being deployed.wait-for-event/notifier-workflowis started throughcontext.call, which is a direct first invocation with no header. It passes because of theservechange 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
tsc -p examples/ciagainst a local build of feat: send Upstash-Workflow-CreatedAt so runs with the same id aren't deduplicated #214 passes.ci/utils.test.ts: 5 passed.🤖 Generated with Claude Code
https://claude.ai/code/session_017dm7F4hg2khgYDqBwP9nx5