feat: add flow.Mutator API; deprecate BuildStep#76
Merged
Conversation
- ErrWorkflow.Unwrap(): skip nil errors (fixes errors.Is/As correctness) - State.SetStepResult(): atomic write for Status+Err+FinishedAt under one lock - reset(): clear full StepResult (not just Status) to avoid stale FinishedAt - tests: guard strings.Index assertions with require >= 0
…efer SetStepResult
…Error deprecated-only
…s from SetStepResult
Adds the design-phase artifacts for the upcoming flow.Mutator feature: - openspec/changes/2026-05-06-step-mutator/: proposal, design, tasks, and capability deltas (mutators / composite-steps / step-configuration) - docs/superpowers/specs/2026-05-06-step-mutator-design.md: brainstorming notes - docs/superpowers/plans/2026-05-11-step-mutator.md: TDD implementation plan - example/15_mutator_test.go: reference example file (build-tagged out until the implementation lands) No production code changes in this commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…covery Add three tests: - TestMutator_receivesWorkflowCtx: verifies the workflow ctx passed to Do is forwarded into each mutator function. - TestMutator_unrelatedBuilderEntryIgnored: verifies that a Builder keyed on a step not in the workflow is silently dropped (scope isolation). - TestMutator_panicCaughtWhenDontPanic: verifies panics in mutators are caught when DontPanic=true and surface as the step's terminal error. Production change: wrap each mutator call in catchPanicAsError when DontPanic is set (mirrors the makeDoForStep `do` idiom). On panic, SetError on the state and short-circuit in tick — marking the step Failed immediately so the goroutine never overwrites the captured error.
…ld-step # Conflicts: # build_step.go # error.go # example/13_composite_step_test.go # state.go # workflow.go
…test.go
- Rename 15_mutator_test.go → 13_mutators_test.go to fit the
sequential learning path (after 12_testing_workflows).
- Adopt the same header/structure as 04_callbacks, 08_workflow_in_workflow,
10_observability: "What you'll learn" → "Where they fit" → "When to
reach for which mechanism" → progressive examples.
- Use a single coherent use case (release-pipeline *Notify steps with a
cross-cutting Mutator) instead of disjoint Greet/Farewell fixtures.
- Five examples, each building on the previous:
1. _fieldDefaults — pure field mutation (return nil Builder)
2. _contributeConfig — Builder with BeforeStep
3. _ctxValue — Mutator receives the workflow ctx
4. _subWorkflow — parent Mutator reaches inner Steps via
MutatorReceiver
5. _multipleInOrder — plan-first, then Mutators in slice order
- Drop the old _retryOverride / _nilBuilder / _unwrap variants — they
belonged to the design-doc stage; production users care about defaults,
callbacks, ctx, propagation, and ordering.
- Update example/README.md to list 13_mutators in the "Operate, debug,
test" section.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply MODIFIED and ADDED requirements from the 2026-05-06-step-mutator
change to the main capability specs:
composite-steps: BuildStep marked deprecated, added
"Sub-workflow construction inside Do" and
"MutatorReceiver propagation" requirements.
step-configuration: added "Mutators field on Workflow" requirement.
mutators: new capability spec covering the Mutator interface,
first-schedule merge semantics, once-per-step
enforcement, dispatch via Unwrap, scope rules, and
MutatorReceiver propagation across workflow
boundaries.
The change directory moves to openspec/changes/archive/ dated 2026-05-12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
XiangyuKuangMSFT
approved these changes
May 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.
Summary
flow.Mutator— a type-dispatched, once-per-step configuration contributor — produced only via the generic constructorflow.Mutate[T Steper](fn).Workflow.Mutators []Mutator; apply once per step at first-schedule (after upstream check, beforestate.Option()) insideWorkflow.tick.MutatorReceiverinterface so parent workflows propagate Mutators into nested*Workflow/SubWorkflow-embedded composite steps.StepBuilder.BuildStepandSubWorkflow.Reset; replace the BuildStep pattern with Do()-time sub-workflow construction (ExampleMutate_subWorkflow).openspec/changes/2026-05-06-step-mutator/(proposal, design, three spec deltas, tasks).Design highlights
Mutate[T]walks the step'sUnwrap()chain; first matching layer wins. Walk stops at workflow boundaries (interface{ StateOf(Steper) *State }) — inner-workflow steps are reached viaPrependMutators, never by descent.StepConfigis merged into the wrapper key in the owning workflow's state map. Builder entries keyed on any other step are silently dropped.state.MutatorsAppliedflag flips before invoking user fn (panic-safe underDontPanic).Input/Before/Afterrun before Mutator-contributed ones; multiple Mutators contribute inWorkflow.Mutatorsslice order.testify/assert; examples use stdlibExample*+// Output:.Files
mutator.go/mutator_test.go— interface, constructor, dispatchstate.go—mutatorsApplied bool+ accessorsworkflow.go—Mutatorsfield,applyMutatorshelper, tick splice,PrependMutatorson*Workflowand*SubWorkflowworkflow_mutator_test.go— black-box integration suitebuild_step.go,workflow.go(SubWorkflow.Reset) — deprecation godocexample/15_mutator_test.go— 7 runnable examplesexample/13_composite_step_test.go— deprecation note on legacy composite exampleTest plan
go build ./...go vet ./...go test ./... -count=1(workflow + example packages, ~4.6s)openspec validate 2026-05-06-step-mutator --strictticksplice point and ordering before mergeOut of scope (deferred)
BuildStep/SubWorkflow.Reset— next major versionBuildSteptoMutateopenspec archive(post-merge)🤖 Generated with Claude Code