Skip to content

refactor: replace optional task interfaces with TaskInfo, drop task wrappers - #2

Merged
RangelReale merged 2 commits into
masterfrom
ccr-d9abf2a7-vkby81
Oct 1, 2026
Merged

RangelReale merged 2 commits into
masterfrom
ccr-d9abf2a7-vkby81

Conversation

@RangelReale

Copy link
Copy Markdown
Contributor

Summary

Optional task behavior was spread over five interfaces (TaskName, TaskSteps, TaskWithOptions, TaskWithInitError, TaskWithNotRun) discovered by type assertion. Every wrapper had to forward each one by hand, which led to several layers of wrapping (taskFuture → BaseOverloadedTask → instancetask.taskBuild → svcinit.taskBuild → closures), and some layers missed one.

This replaces them with a single extension point and removes the generic wrappers.

Changes

  • TaskInfo: a struct (Name, Steps, Options, InitError, NotRun) returned by one method, TaskWithInfo.TaskInfo(). The manager reads everything through GetTaskInfo.

  • Removed wrap.go: WrapTask, UnwrapTask, TaskWithWrapped, BaseOverloadedTask and BaseWrappedTask are gone.

    • To decorate a task, use BuildTask(WithParent(t), WithName(...)).
    • To customize how steps are called, use the WithHandler option on AddTask.
    • Callbacks now receive the task passed to AddTask, so there is nothing to unwrap.
  • BuildTask is the only decorator: it merges its parent's TaskInfo:

    • the parent's name is used when none is set
    • the parent's options are applied first
    • the parent's init error is included
    • the parent's NotRun is called

    It also gains a WithNotRun option. Its computed state is stored atomically, because SetParent runs during the setup step.

  • instancetask.Build returns *instancetask.Task[T], which embeds the built task instead of forwarding its methods. Adds instancetask.WithNotRun.

  • futuretask.New returns *futuretask.Task[T]. It is now instancetask.Build plus a WithNotRun callback that resolves the future; the hand-written forwarding wrapper is gone.

  • ServiceName is removed: a Service can implement TaskWithInfo directly.

  • Task options are computed when they are used, from the task's current TaskInfo, instead of once in AddTask.

  • README: new section documenting TaskInfo.

Bugs fixed

  • Decorating a future task with BuildTask(WithParent(...)) or instancetask.WithParent dropped its "not run" notification. If the task's stage never ran, the future was never resolved and anything waiting on its Value() blocked forever.
  • BuildTask(WithParent(x)) dropped x's options. For example, wrapping SignalTask or TimeoutTask lost their WithCancelContext(true).
  • BuildTask ignored its parent's init error. As a result, a task returned from an instancetask.Provider callback with an init error was accepted silently.
  • Options of the task returned by an instancetask.Provider callback (WithCancelContext, WithStartStepManager) were ignored, because options were applied before the setup step created that task.

Breaking changes

This is a breaking API change: the five optional interfaces, ServiceName and the wrap.go API are removed, and the return types of instancetask.Build, instancetask.Provider and futuretask.New change.

Test plan

  • go vet ./...
  • go test -race -count=2 ./...
  • TestNotRunDecorated (futuretask): a future decorated with one and two levels of BuildTask(WithParent(...)) resolves with ErrTaskNotRun when an earlier stage's setup fails. Confirmed failing when the parent NotRun forwarding is removed.
  • TestProviderTaskOptions (instancetask): WithCancelContext and WithStartStepManager from a provided task take effect. Confirmed failing on the previous commit.
  • New BuildTask decorator tests cover info merging, parent options taking effect, WithHandler, callbacks receiving the decorated task, parent init errors, and NotRun ordering.
  • TestProviderInitErrorFromSetup: a provided task with an init error fails the setup step.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CLyyJx24FhyAuthMeMuKPx


Generated by Claude Code

claude added 2 commits October 1, 2026 11:20
…rappers

Optional task behavior was spread over five interfaces (TaskName, TaskSteps,
TaskWithOptions, TaskWithInitError, TaskWithNotRun) discovered by type
assertion, so every wrapper had to forward each of them by hand. Some layers
missed one: decorating a futuretask with BuildTask(WithParent(...)) or
instancetask.WithParent dropped TaskNotRun, leaving the future unresolved and
its waiters blocked forever when the task's stage never ran.

- Add TaskInfo (Name, Steps, Options, InitError, NotRun) returned by a single
  TaskWithInfo.TaskInfo() method, and GetTaskInfo.
- Remove wrap.go (WrapTask, UnwrapTask, TaskWithWrapped, BaseOverloadedTask,
  BaseWrappedTask): use BuildTask(WithParent, WithName) to decorate and the
  WithHandler task option to customize step calls.
- BuildTask merges its parent's TaskInfo (name fallback, options, init error,
  NotRun) and gains a WithNotRun option. Its computed state is stored
  atomically, as SetParent runs during the setup step.
- instancetask.Build returns *instancetask.Task[T], which embeds the built
  task instead of forwarding its methods; adds instancetask.WithNotRun.
- futuretask.New returns *futuretask.Task[T], built on instancetask with a
  WithNotRun callback instead of a hand-written forwarding wrapper.
- ServiceName is removed: a Service may implement TaskWithInfo directly.
- Regression test for the decorated futuretask never resolving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CLyyJx24FhyAuthMeMuKPx
Task instance options were applied once, when the task was added, so options
of a task only known after the "setup" step were ignored, like the ones from
the task returned by an instancetask.Provider callback (WithCancelContext,
WithStartStepManager).

The task wrapper now keeps only the Manager.AddTask options, and computes the
effective options from the task's current TaskInfo when they are used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CLyyJx24FhyAuthMeMuKPx
@RangelReale
RangelReale merged commit 96ad674 into master Oct 1, 2026
1 check passed
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.

2 participants