feat(workflow): admit step tasks idempotently - #581
Merged
Merged
Conversation
A Workflow step dispatched its task with a plain create, and run creation, step creation, task admission, and step linkage were separate writes. A crash or a doubled dispatch between admitting the task and linking it onto the step run could create a second task, running the agent twice. Add AdmitTask: task creation keyed by a stable admission key unique within the space, arbitrated by a new unique index on (space_id, admission_key). The first insert wins; a replayed or concurrent admission that loses the race reads the winner back and returns it, and a different payload under the same key is refused with a conflict rather than adopting unrelated work. The admitted payload is compared through a fingerprint that covers the caller's intent but excludes the live-resolved agent revision, so recovery after an agent edit is not a false conflict. A NULL key never enters the index, so ordinary tasks are unchanged. The linear Workflow service dispatches each step through AdmitWorkflowTask with the key workflow/<workflow_run_id>/node/<step_id>. This closes the create-before-link duplication window; the reconciler that replays the key on recovery is a later slice. Also records the accepted Workflow runtime decision: the durable coordination substrate is form-agnostic and the reconcile decision is a pure function over persisted facts. Verified with focused task/workflow service tests, new real-MySQL admission tests (replay, payload conflict, space scope, and a contention winner whose mutation check fails without the unique index), the full MySQL scope, and the ordinary suite.
This was referenced Sep 12, 2026
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.
Closes backlog item
30-workflow-idempotent-task-admission.Problem
A Workflow step dispatched its task with a plain create, and run creation, step
creation, task admission, and step linkage were separate writes. A crash or a
doubled dispatch between admitting the task and linking it onto the step run
could create a second task, running the agent twice.
Change
Store.AdmitTask: task creation keyed by a stable admission key uniquewithin the space, arbitrated by a new unique index
(space_id, admission_key).The first insert wins; a replayed or concurrent admission that loses the race
reads the winner back and returns it; a different payload under the same key is
refused with
ErrTaskAdmissionConflictrather than adopting unrelated work.core/task) coveringthe caller's intent but excluding the live-resolved agent revision, so
recovery after an agent edit is not a false conflict. A
NULLkey never entersthe index, so ordinary tasks are unchanged.
AdmitWorkflowTaskwith the key
workflow/<workflow_run_id>/node/<step_id>.This closes the create-before-link duplication window. The reconciler that
replays the key on recovery is a later slice (items 40/50/60). The commit also
records the accepted Workflow-runtime decision (design §1.2 + new §10.1): the
durable coordination substrate is form-agnostic, and the reconcile decision is a
pure function over persisted facts.
Verification
./make test ./internal/service/task ./internal/service/workflow./make test mysql -run TestWorkflowTaskAdmission(replay, payload conflict,space scope, contention winner)
8 distinct tasks; reverted.
./make test mysqland./make test, architecture tests, markdownlint,git diff --check— all clean.Docs:
data-model.md(task admission columns + index),current-state.md.