Step: filters can now return true or false which can be directly wired as a signal.
- positional circuit interface even in Pipeline.
- :return_args is now :return_ctx, since flow_options are passed through the pipeline anyway, thanks to the consistency introduced in this version.
===> :application_ctx :application_circuit_options
- pipes can now, theoretically, be traced, too.
- simpler! all executables have the same signature.
- return ctx, flow_options, signal
Pipeline.call now behaves like just another circuit. pipeline tasks: circuit interface, returned signal is ignored.
-
Remove (depracate)
Pipeline::Rowas a row is always an [ID, Object] tuple. -
It's now
Activity::Pipelineas this concept is used for many other cases. -
The
Activity.Pipeline()method is the only way to create a Pipeline instance. -
Move
Activity::TaskWrap::PipelinetoActivity::Pipeline -
Deprecate
Pipeline.new -
Remove {:row} option from ADDS as we're not wrapping anything anymore.
-
Remove
TaskWrap::Extension::WrapStatic. We only useTaskWrap::Extensionnow. Note that presently, you cannot add options to theconfigfield anymore via an extension. -
Remove
Trailblazer::Activity::TaskBuilder.Binary()deprecation. This results inTrailblazer::Activity::TaskBuildernow beingTrailblazer::Activity::Circuit::TaskAdapter. -
Introduce
Activity::Adds.callas the canonical entry point for altering pipelines. -
Allow
:replacein ADDS friendly interface. -
Adds.callaccepts a:rowoption. -
Rename
:end_eventsto:terminiinCircuit#to_h. -
Remove
Activity.callfor the sake of the new canonical invoke. UseRailway.__(activity, ctx)instead.
TaskWrap::INITIAL_wRAP_STATICis now::INITIAL_TASK_WRAP.- Remove
#def_stepsand#assert_invokeand move it to totrailblazer-core-utils. - We don't use
Compilerin tests anymore as this is a concept moved totrailblazer-workflow.
- In
Extension#call, remove the dependency toImplementation::Task. The logic only receives the actual circuit task. - Remove deprecated
Extension()interface. - Remove
Trailblazer::Activity::Introspect::Graph.
- Revert the static "fix" from 0.16.3 and make the compaction fix optional, as most Ruby versions are solving this problem natively. See here for details: https://dev.to/trailblazer/gc-compaction-behold-your-hash-keys-2ii1#quick-fix
- Fix a bug in Ruby 3.2:
NoMethodError: undefined method[]' for nil`. Thanks @tiagotex for finding the problem and solution. This bug only occurs when compacting the memory using GC, which is only introduced with Ruby 3.2.
- Allow passing custom
:flow_optionstoTesting#assert_invoke.
- Allow overriding
Activity.call.
- Remove
Activity#[]. Please useactivity.to_h[:config]. - Change
Activity#to_h[:nodes]. This is now aSchema::Nodes"hash" that is keyed by task that points toNodes::Attributesdata structures (a replacement forActivity::NodeAttributes). This decision reduces logic and improves performance: it turned out that most of the time an introspect lookup queries for a task, not ID. - Introduce
Activity::Introspect.Nodes()as a consistent and fast interface for introspection and removeActivity::Introspect::TaskMap. - Remove
Activity::NodeAttributes. - Move
Introspect::Graphtotrailblazer-developer. It's a data structure very specific to rendering, which is not a part of pure runtime behavior.Activity::Introspect.Graph()is now deprecated. TaskWrap.container_activity_fornow accepts:idfor setting an ID for the containered activity to anything other thannil.- Re-add
:nodesto the container activity hash as this provides a consistent way for treating allActivitys. - Remove
Activity::Config. This immutable hash interface was used in one place, only, and can easily be replaced withconfig.merge(). - Add
Introspect::Render. Please consider this private.
-
Remove
Intermediate.call, this is now done throughIntermediate::Compiler. -
Introduce
Intermediate::Compilerwhich is simplified and is 10% faster. -
A terminus ("end event") in
Schema::Intermediateno longer has outputs but an empty array. Thestop_event: trueoption is still required to mark theTaskRefas a terminus. -
Schema::Intermediatenow keeps a map{<terminus ID> => :semantic}instead of the flat termini ID list and one default start event instead of an array. This looks as follows.Schema::Intermediate.new( { # ... Intermediate::TaskRef("End.success", stop_event: true) => [Inter::Out(:success, nil)] }, ["End.success"], [:a] # start
Now becomes
Schema::Intermediate.new( { # ... Intermediate::TaskRef("End.success", stop_event: true) => [] }, {"End.success" => :success}, :a # start
-
In line with the change in
Intermediate, theImplementionterminiTasks now don't have outputs anymore.implementation = { # ... "End.success" => Schema::Implementation::Task(Activity::End.new(semantic: :success), [], []) # No need for outputs here. }
- Introduce
Extension.WrapStatic()as a consistent interface for creating wrap_static extensions exposing the friendly interface. - Deprecate
Extension(merge: ...)since we haveExtension.WrapStaticnow. - Better deprecation warnings for extensions using
Insertand not the friendly interface.
- Rename
Circuit::RuntoCircuit::Runnerfor consistency withTaskWrap::Runner. - Add
:flat_activitykeyword argument toTesting.nested_activity, so you can inject any activity for:D. Also, allow to change:D's ID with the:d_idoption. - Introduce
Deprecate.warnto have consistent deprecation warnings across all gems. - Introduce
Activity.callas a top-level entry point and abstraction forTaskWrap.invoke.
-
Remove the
:wrap_statickeyword argument forTaskWrap.invokeand replace it with:container_activity. -
Make
TaskWrap.initial_wrap_staticreturnINITIAL_TASK_WRAPinstead of recompiling it for every#invoke. -
Introduce
TaskWrap.container_activity_forto build "host activities" that are used to provide a wrap_static to the actually run activity. This is also used in theEach()macro and other places. -
Allow
append: nilfor friendly interface.TaskWrap.Extension([method(:add_1), id: "user.add_1", append: nil])`.
This appends the step to the end of the pipeline.
- Add
Introspect.find_pathto retrieve aGraph::Nodeand its hosting activity from a deeply nested graph. Note that this method is still considered private. - Add
Introspect::TaskMapas a slim interface for introspectingActivityinstances. Note thatGraphmight get moved todeveloperas it is very specific to rendering circuits.
- Rename
Activity::TaskBuilder.Binary()toActivity::Circuit::TaskAdapter.for_step(). It returns aTaskAdapterinstance, which is a bit more descriptive than aTaskinstance. - Add
Circuit.Step(callable_with_step_interface)which accepts a step-interface callable and, when called, returns its result and thectx. This is great for deciders in macros where you don't want the step's result on thectx. - Add
TaskWrap::Pipeline::TaskBuilder.
- Remove
Pipeline.insert_beforeand friends. Pipeline is now altered using ADDS mechanics, just as we do it with theSequencein thetrailblazer-activity-dsl-lineargem. Pipeline::Mergeis nowTaskWrap::Extension. The "pre-friendly interface" you used to leverage for creating taskWrap (tw) extensions is now deprecated and you will see warnings. See https://trailblazer.to/2.1/docs/activity.html#activity-taskwrap-extension- Replace
TaskWrap::Extension()withTaskWrap::Extension.WrapStatic()as a consistent interface for creating tW extensions at compile-time. - Remove
Insert.find. - Rename
Activity::State::ConfigtoActivity::Config. - Move
VariableMappingto thetrailblazer-activity-dsl-lineargem. - Move
Pipeline.prependto thetrailblazer-activity-linear-dslgem. - Add
Testing#assert_callas a consistent test implementation. (0.14.0.beta2)
- Removed
TaskWrap::Inject::Defaults. This is now implemented throughdsl's:injectoption. - Removed
TaskWrap::VariableMapping.Extension. - Renamed private
TaskWrap::VariableMapping.merge_forto.merge_instructions_foras there's no {Merge} instance, yet. - Extract invocation logic in
TaskBuilder::TaskintoTask#call_option. - Add
TaskWrap::Pipeline::prepend.
- Use extracted
trailblazer-option.
- Allow injecting
:wrap_staticintoTaskWrap.invoke.
- Support for Ruby 3.0.
- Bug fix:
:outputfilter fromTaskWrap::VariableMappingwasn't returning the correctflow_options. If the wrapped task changed itsflow_options, the original one was still returned from the taskWrap run, not the updated one.
- Introduce the
config_wrap:option inIntermediate.call(intermediate, implementation, config_merge: {})to allow injecting data into the activity's:configfield.
- Allow
Testing.def_task&Testing.def_tasksto return custom signals
- Upgrading
trailblazer-contextversion 🥁
- Internal warning fixes.
- Support for Ruby 2.7. Most warnings are gone.
- Update IllegalSignalError exception for more clarity
- Require
developer>= 0.0.7. - Move
Activity::Introspectback to this very gem. - This is hopefully the last release before 2.1.0.

- Move some test helpers to
Activity::Testingto export them to other gems - Remove introspection modules, it'll also be part of the
Devtools now. - Remove tracing modules, it'll be part of the
Devtools now.
Unreleased.
Unreleased.
- Use
context-0.9.1.
- Change API of
Input/Outputfilters. Instead of calling them withoriginal_ctx, circuit_optionsthey're now called with the complete (original!) circuit interface. This simplifies calling and provides all circuit arguments to the filter which can then filter-out what is not needed.:inputis now called with((original_ctx, flow_options), circuit_options):outputis now called with(new_ctx, (original_ctx, flow_options), circuit_options)
- Update
Presentto renderDeveloper.wtf?for given activity fearlessly
- Use
Context.forto create contexts.
- Fix
Presentso it works with Ruby <= 2.3.
- Remove
hirbgem dependency.
- Separate the DSL from the runtime code. The latter sits in this gem.
- Separate the runtime {Activity} from its compilation, which happens through {Intermediate} (the structure) and {Implementation} (the runtime implementation) now.
- Introduce {Pipeline} which is a simpler and much fast type of activity, mostly for the taskWrap.
- When recording DSL calls, use the
object_idas key, so cloned methods are considered as different recordings.
- Alias
Trace.calltoTrace.invokefor consistency. - Allow injecting your own stack into
Trace.invoke. This enables us to provide tracing even when there's an exception (due to, well, mutability). - Minor changes in
Trace::Presentso that "unfinished" stacks can also be rendered. Trace::Present.treeis now private and superseded byPresent.call.
- Remove
DSL::Helper, "helper" methods now sit directly in theDSLnamespace.
- Allow all
Optiontypes for input/output.
- Make
:inputand:outputstandard options of the DSL to create variable mappings.
- The
:taskoption inCircuit::callis now named:start_taskfor consistency. - Removed the
:argumenteroption forActivity::call. Instead, anActivitypasses itself via the:activityoption. - Removed the
:extensionoption. Instead, any option from the DSL thatis_a?(DSL::Extension)will be processed inadd_task!. - Replace argumenters with
TaskWrap::invoke. This simplifies the wholecallprocess, and moves all initialization of args to the top. - Added
Introspect::Graph::find. - Removed
Introspect::Enumeratorin favor of theGraphAPI.
- Introducing
Introspect::Enumeratorand removingIntrospect.find.EnumeratorcontainsEnumerableand exposes all necessary utility methods.
-
In Path(), allow referencing an existing task, instead of creating an end event. This avoids having to use two
Output() => ..and is much cleaner.Path( end_id: :find_model) do .. end
- In
Path(), we removed the#pathmethod in favor of a cleanertaskDSL method. We now use the default plus_polessuccessandfailureeverywhere for consistency. This means that ataskhas two outputs, and if you referencedOutput(:success), that would be only one of them. We're planning to havepassback which has onesuccessplus_pole, only. This change makes the DSL wiring behavior much more consistent. - Changed
TaskBuilder::Builder.()to a functionTaskBuilder::Builder().
- Include all end events without outgoing connections into
Activity.outputs. In earlier versions, we were filtering out end events without incoming connections, which reduces the number of outputs, but might not represent the desired interface of an activity. - Add
_endtoRailwayandFastTrack. - Move
Builder::FastTrack::PassFastand:::FailFasttoActivity::FastTracksince those are signals and unrelated to builders.
- Rename
Nested()toSubprocessand move the original one to theoperationgem. - Add merging:
Activity.merge!now allows to compose an activity by merging another. - Enforce using
Output(..) => Track(:success)instead of just the track color:success. This allow having IDs both symbols and strings.
- Make
:outputsthe canonical way to define outputs, and not:plus_poles. The latter is computed by the DSL if not passed. - Allow injecting
inspectimplementations intoIntrospectmethods. - Add
Nested. - Add
TaskBuilder::Task#to_s.
Endis not aStructso we can maintain more state, and are immutable.
- Remove
decomposeand replace it with a betterto_h. Enddoesn't have a redundant@nameanymore but only a semantic.
- We now use the "Module Subclass" pattern, and activities aren't classes anymore but modules.
- In the
TaskWrap, rename:result_directionto:return_signaland:result_argsto:return_args,
- Allow passing a
:normalizerto the DSL. - Builders don't have to provide
keywordsas we can filter them automatically.
- Remove
Activity#end_events.
- Restructure all
Wrap-specific tasks. - Remove
Hash::Immutable, we will use thehamstergem instead.
-
The
Activity#callAPI is nowsignal, options, _ignored_circuit_options = Activity.( options, **circuit_options )
The third return value, which is typically the
circuit_options, is ignored and for all task calls in thisActivity, an identical, unchangeable set ofcircuit_optionsis passed to. This dramatically reduces unintended behavior with the task_wrap, tracing, etc. and usually simplifies tasks.The new API allows using bare
Activityinstances as tasks without any clumsy nesting work, making nesting very simple.A typical task will look as follows.
->( (options, flow_options), **circuit_args ) do [ signal, [options, flow_options], *this_will_be_ignored ] end
A task can only emit a signal and "options" (whatever data structure that may be), and can not change the
circuit_optionswhich usually contain activity-wide "global" configuration.
Nestednow isSubprocessbecause it literally does nothing else but calling a process (or activity).
Nestednow uses kw args forstart_atand the newcalloption. The latter allows to set the called method on the nested activity, e.g.__call.
- Introduce
Activity#outputsand ditch#end_events.
- Consistent return values for all graph operations:
node, edge. Edgenow always gets an id.#connect_for!always throws away the old edge, fixing a bug where graph and circuit would look different.- Internal simplifications for
Graphalteration methods.
- Fix loading order.
- In
Activity::Before, allow specifying what predecessing tasks to connect to the new_task via the:predecessorsoption, and without knowing the direction. This will be the new preferred style inTrailblazer:::Sequencewhere we can always assume directions are limited toRightandLeft(e.g., with nested activities, this changes to a colorful selection of directions).
- Temporarily allow injecting a
to_hashtransformer into aContainerChain. This allows to ignore certain container types such asDry::Containerin the KW transformation. Note that this is a temp fix and will be replaced with proper pattern matching.
- Introduce
Context::ContainerChainto eventually replace the heavy-weightSkillobject. - Fix a bug in
Optionwhere wrong args were passed when used withoutflow_options.
- Fix
Context#[], it returnednilwhen it should returnfalse.
- Make
Trailblazer::OptionandTrailblazer::Option::KWa mix of lambda and object so it's easily extendable.
- It is now
Trailblazer::Args.
Wrappedis nowWrap. Also, a consistentAlterationsinterface allows tweaking here.
- The
Wrapped::Runnernow appliesAlterationsto each task'sCircuit. This means you can inject:task_alterationsintoCircuit#call, which will then be merged into the task's original circuit, and then run. While this might sound like crazy talk, this allows any kind of external injection (tracing, input/output contracts, step dependency injections, ...) for specific or all tasks of any circuit.
- Simpler tracing with
Stack. - Added
Context. - Simplified
Circuit#call.
- Make the first argument to
#Activity(@name) always a Hash where:idis a reserved key for the name of the circuit.
- Make
flow_optionsan immutable data structure just asoptions. It now needs to be returned from a#call.
- First release into an unsuspecting world. 🚀