Skip to content

feat(workflow): admit step tasks idempotently - #581

Merged
gougoujiang merged 1 commit into
mainfrom
workflow-idempotent-task-admission
Sep 12, 2026
Merged

gougoujiang merged 1 commit into
mainfrom
workflow-idempotent-task-admission

Conversation

@gougoujiang

Copy link
Copy Markdown
Collaborator

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

  • Add Store.AdmitTask: task creation keyed by a stable admission key unique
    within 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 ErrTaskAdmissionConflict rather than adopting unrelated work.
  • The admitted payload is compared through a fingerprint (core/task) covering
    the caller's intent but excluding 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 (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)
  • Mutation check: removing the unique index makes the contention test fail with
    8 distinct tasks; reverted.
  • Full ./make test mysql and ./make test, architecture tests, markdownlint,
    git diff --check — all clean.

Docs: data-model.md (task admission columns + index), current-state.md.

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.
@gougoujiang
gougoujiang merged commit 2a8c451 into main Sep 12, 2026
6 checks passed
@gougoujiang
gougoujiang deleted the workflow-idempotent-task-admission branch September 12, 2026 21:13
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