Skip to content

feat: add skip_if and if fields to pipeline step execution#332

Merged
intel352 merged 16 commits intomainfrom
feat/step-skip-if
Mar 16, 2026
Merged

feat: add skip_if and if fields to pipeline step execution#332
intel352 merged 16 commits intomainfrom
feat/step-skip-if

Conversation

@intel352
Copy link
Contributor

Summary

  • Adds skip_if and if optional Go template fields to PipelineStepConfig (YAML: skip_if / if)
  • Implements SkippableStep wrapper in module/pipeline_step_skip.go — evaluated before Execute, zero overhead when neither field is set
  • skip_if: truthy result (non-empty, not "false", not "0") → step is skipped
  • if: inverse — falsy result → step is skipped; when both are set, skip_if takes precedence
  • Skipped steps output {"skipped": true, "reason": "..."} so downstream steps can inspect the result
  • Both buildPipelineSteps (named pipelines) and parseRoutePipelineSteps (inline route pipelines) apply the wrapper
  • 12 new unit tests covering all guard combinations, template resolution, step output access, and backward compatibility

Usage

steps:
  - name: steam_presence
    type: step.steam_presence_set
    skip_if: '{{ eq (config "STEAM_APP_ID") "" }}'
    config:
      status: online

  - name: conditional_step
    type: step.set
    if: '{{ .feature_enabled }}'
    config:
      values:
        done: "true"

Test plan

  • go test ./... -timeout 120s — all packages pass (no regressions)
  • TestStepSkipIf_SkipsWhenTrue — step not executed when skip_if is truthy
  • TestStepSkipIf_ExecutesWhenFalse — step executes when skip_if is falsy
  • TestStepSkipIf_OutputContainsSkippedFlag — skipped output includes skipped: true and reason
  • TestStepIf_ExecutesWhenTrue — step executes when if is truthy
  • TestStepIf_SkipsWhenFalse — step skipped when if is falsy
  • TestStepSkipIf_TemplateResolution — templates read from pipeline context (current, step outputs)
  • TestStepSkipIf_AbsentMeansExecute — backward compatible, always executes when neither field is set
  • TestStepSkipIf_FalseStringIsFalsy / TestStepSkipIf_ZeroStringIsFalsy — falsy value handling

🤖 Generated with Claude Code

intel352 and others added 15 commits March 15, 2026 01:55
Adds --url flag to wfctl plugin install that downloads a tar.gz archive
from a direct URL, extracts plugin.json to identify the plugin name,
installs to the plugin directory, and records the SHA-256 checksum in
the lockfile.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends wfctl plugin init to generate cmd/workflow-plugin-<name>/main.go,
internal/provider.go, internal/steps.go, go.mod, .goreleaser.yml, CI/release
GitHub Actions workflows, Makefile, and README.md. Adds --module flag for
custom Go module paths. Preserves existing plugin.json and .go skeleton for
backward compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add AutoFetchPlugin and AutoFetchDeclaredPlugins to plugin/autofetch.go,
which shell out to wfctl to download plugins not found locally. Extend
WorkflowConfig with a new PluginsConfig / ExternalPluginDecl type so
configs can declare plugins with autoFetch: true and an optional version
constraint. StdEngine gains SetExternalPluginDir and calls
AutoFetchDeclaredPlugins in BuildFromConfig before module loading.
The server's buildEngine registers the plugin dir so auto-fetch is active
at runtime. If wfctl is absent, a warning is logged and startup continues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Use struct conversion for staticIndexEntry → PluginSummary (staticcheck S1016)
- Remove unused updateLockfile and writePluginJSON functions
- Add nilerr annotations for intentional nil returns in integrity.go
- Add gosec annotation for exec.Command in autofetch.go
- Fix TestLoadRegistryConfigDefault to use DefaultRegistryConfig() directly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- integrity.go: fail closed when lockfile exists but is unreadable or
  unparseable, preventing integrity enforcement bypass
- autofetch.go: extract stripVersionConstraint helper; detect compound
  version constraints and fall back to latest; check both pluginName and
  workflow-plugin-<name> alternate form for installed-check; log restart
  warning when new plugins are downloaded (they require a server restart)
- autofetch_test.go: test stripVersionConstraint directly instead of
  duplicating the logic inline; add compound-constraint cases
- engine.go: clarify comment that auto-fetched plugins need a restart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- generator.go: use plugin/external/sdk imports and types (PluginProvider,
  StepInstance, StepResult, StepTypes/CreateStep) instead of plugin/sdk
- PLUGIN_AUTHORING.md: update examples to match external SDK interfaces
- plugin_install.go: hash installed binary (not archive) for lockfile,
  add hashFileSHA256 helper, add install mode mutual exclusivity check,
  update installFromLocal to write lockfile, normalize plugin names
- plugin_lockfile.go: add registry param to updateLockfileWithChecksum,
  pass version/registry in installFromLockfile, remove dir on mismatch
- registry_source.go: validate URL in NewStaticRegistrySource
- config.go: clarify Version field forwarding semantics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- registry_source.go: use explicit field assignment for PluginSummary
  instead of struct type conversion (clearer, avoids tag confusion)
- plugin_lockfile.go: don't pass @Version in installFromLockfile to
  prevent lockfile overwrite before checksum verification
- plugin_install.go: add verifyInstalledPlugin() call in installFromURL
  for parity with registry installs
- engine.go: add TODO to move auto-fetch before plugin discovery so
  newly fetched plugins are available without restart
- integrity_test.go: add tests for unreadable and malformed lockfile
  to verify fail-closed behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- registry_source.go: add nolint:gosimple for S1016 — explicit field
  assignment preferred for clarity across different struct tags
- generator_test.go: add TestGenerateProjectStructure verifying all
  generated files exist and use correct external SDK imports/types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…immediately

Auto-fetch was running inside BuildFromConfig, which executes after
external plugins are already discovered and loaded. Plugins downloaded
by auto-fetch required a server restart to take effect.

Move auto-fetch to buildEngine in cmd/server/main.go, before
DiscoverPlugins/LoadPlugin. Remove the now-unused externalPluginDir
field and SetExternalPluginDir from the engine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enables pipelines to call other pipelines by name with full context
forwarding. Supports template-resolved workflow names and stop_pipeline
option. Required for WebSocket message routing to game-specific pipelines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- registry_source.go: switch GitHubRegistrySource to use registryHTTPClient
  (timeout-configured) for both ListPlugins and FetchManifest; update comment
- autofetch.go: scan for AutoFetch=true entries before exec.LookPath to avoid
  misleading startup warning when no plugins need auto-fetch
- autofetch.go: add internal autoFetchPlugin helper that accepts *slog.Logger
  and emits structured log entries when a logger is available; public
  AutoFetchPlugin delegates to it; AutoFetchDeclaredPlugins passes its logger
- autofetch_test.go: rename TestAutoFetchPlugin_CorrectArgs to
  TestAutoFetchPlugin_SkipsWhenExists to match what the test actually asserts
- integrity.go: replace os.ReadFile + sha256.Sum256 with streaming
  os.Open + io.Copy into sha256.New() to keep memory bounded for large binaries
- integrity_test.go: add t.Skip guard in UnreadableLockfile test when the file
  is actually readable (Windows / root environments)
- pipeline_step_workflow_call.go: use resolved workflowName (not s.workflow)
  in async return payload and sync error message for consistency
- docs/PLUGIN_AUTHORING.md: clarify the distinction between plugin.json name
  (short, used by engine) and the registry/provider manifest name
  (workflow-plugin- prefixed), and which is used for dependency resolution
- config/config.go: MergeApplicationConfig now merges Plugins.External from
  each referenced workflow file into the combined config, deduplicated by name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- docs/PLUGIN_AUTHORING.md: close unclosed code fence after StepProvider example
- cmd/wfctl/plugin_install.go: streaming hashFileSHA256 via io.Copy + sha256.New()
- cmd/wfctl/plugin_install.go: warn on hash failure instead of silent empty checksum
- config/merge_test.go: add TestMergeApplicationConfig_PluginDedup covering first-definition-wins dedup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- pipeline_step_workflow_call.go: propagate stop_pipeline in async mode
- integrity.go: "open plugin binary" instead of "read" for os.Open error
- plugin_install.go: lowercase "warning:" for consistency
- merge_test.go: use filepath.Join and check writeFileContent errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Steps now support optional `skip_if` and `if` Go template guards.
When `skip_if` evaluates to a truthy value the step is skipped and
the pipeline continues; `if` is the logical inverse. Skipped steps
produce `{"skipped": true, "reason": "..."}` output so downstream
steps can inspect the result.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 15, 2026 23:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds conditional execution controls for pipeline steps via skip_if / if guard templates, and wires them into both named pipelines and inline route pipelines. The PR also includes several plugin/registry-related robustness improvements (integrity hashing, auto-fetch logging/behavior, lockfile handling, and registry HTTP timeouts) plus plugin declaration deduplication during application config merge.

Changes:

  • Add skip_if / if fields to PipelineStepConfig and wrap steps with a new SkippableStep guard.
  • Extend workflow_call to support templated workflow names and an optional stop_pipeline behavior.
  • Improve plugin tooling reliability (streaming SHA-256 hashing, registry HTTP client timeouts, auto-fetch behavior) and deduplicate merged external plugin declarations.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
engine.go Parses skip_if/if on inline route steps and wraps built steps with SkippableStep.
config/pipeline.go Adds SkipIf/If fields to pipeline step config schema.
module/pipeline_step_skip.go Introduces SkippableStep wrapper implementing guard evaluation and skip output.
module/pipeline_step_skip_test.go Adds unit tests covering guard combinations and template access.
module/pipeline_step_workflow_call.go Adds stop_pipeline, supports templated workflow lookup, and returns Stop in results.
config/config.go Merges/deduplicates external plugin declarations when merging application workflow files.
config/merge_test.go Tests plugin deduplication behavior during application config merge.
plugin/autofetch.go Refactors auto-fetch to support optional slog logging and avoids wfctl warnings when unused.
plugin/autofetch_test.go Renames/retargets test to validate skip-when-installed behavior.
plugin/integrity.go Switches integrity hashing to streaming I/O for plugin binaries.
plugin/integrity_test.go Makes unreadable-lockfile test resilient across platforms/root execution.
cmd/wfctl/registry_source.go Routes registry HTTP through a shared client with a timeout; minor struct init tweak.
cmd/wfctl/plugin_install.go Uses streaming SHA-256 hashing; tweaks lockfile update behavior/messages.
cmd/wfctl/plugin_lockfile.go Improves checksum-mismatch handling by warning on removal failures.
cmd/wfctl/multi_registry.go Small variable rename/cleanup in static registry construction.
docs/PLUGIN_AUTHORING.md Clarifies plugin naming expectations and short vs canonical naming.

Comment on lines +48 to +66
val, err := s.tmpl.Resolve(s.skipIf, pc)
if err != nil {
// Template resolution errors are non-fatal: treat as falsy (execute).
val = ""
}
if isTruthy(val) {
return skippedResult("skip_if evaluated to true"), nil
}
}

// Evaluate if (inverse logic: falsy → skip)
if s.ifExpr != "" {
val, err := s.tmpl.Resolve(s.ifExpr, pc)
if err != nil {
// Template resolution errors are non-fatal: treat as falsy (skip).
val = ""
}
if !isTruthy(val) {
return skippedResult("if evaluated to false"), nil
Comment on lines +91 to +92
"skipped": true,
"reason": reason,
Comment on lines +30 to +31
// template evaluates to truthy. Falsy or absent with no SkipIf → execute.
// When both SkipIf and If are set, SkipIf takes precedence.
@github-actions
Copy link

github-actions bot commented Mar 15, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:244: parsing iteration count: invalid syntax
baseline-bench.txt:308861: parsing iteration count: invalid syntax
baseline-bench.txt:604677: parsing iteration count: invalid syntax
baseline-bench.txt:906862: parsing iteration count: invalid syntax
baseline-bench.txt:1188248: parsing iteration count: invalid syntax
baseline-bench.txt:1476258: parsing iteration count: invalid syntax
benchmark-results.txt:244: parsing iteration count: invalid syntax
benchmark-results.txt:303365: parsing iteration count: invalid syntax
benchmark-results.txt:622023: parsing iteration count: invalid syntax
benchmark-results.txt:925627: parsing iteration count: invalid syntax
benchmark-results.txt:1227143: parsing iteration count: invalid syntax
benchmark-results.txt:1522446: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 7763 64-Core Processor                
                            │ baseline-bench.txt │       benchmark-results.txt        │
                            │       sec/op       │    sec/op     vs base              │
InterpreterCreation-4               9.657m ± 68%   9.515m ± 68%       ~ (p=0.937 n=6)
ComponentLoad-4                     3.695m ±  5%   3.462m ±  2%  -6.30% (p=0.004 n=6)
ComponentExecute-4                  2.003µ ±  1%   2.007µ ±  1%       ~ (p=0.788 n=6)
PoolContention/workers-1-4          1.106µ ±  1%   1.098µ ±  0%  -0.72% (p=0.011 n=6)
PoolContention/workers-2-4          1.106µ ±  4%   1.100µ ±  3%       ~ (p=0.292 n=6)
PoolContention/workers-4-4          1.105µ ±  0%   1.102µ ±  1%       ~ (p=0.236 n=6)
PoolContention/workers-8-4          1.116µ ±  1%   1.108µ ±  1%  -0.67% (p=0.015 n=6)
PoolContention/workers-16-4         1.113µ ±  2%   1.117µ ±  5%       ~ (p=0.310 n=6)
ComponentLifecycle-4                3.561m ±  4%   3.517m ±  3%       ~ (p=0.132 n=6)
SourceValidation-4                  2.363µ ±  2%   2.297µ ±  1%  -2.79% (p=0.002 n=6)
RegistryConcurrent-4                801.0n ±  4%   774.5n ±  5%       ~ (p=0.240 n=6)
LoaderLoadFromString-4              3.613m ±  1%   3.507m ±  1%  -2.93% (p=0.002 n=6)
geomean                             19.44µ         19.12µ        -1.66%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               1.944Mi ± 0%   1.944Mi ± 0%       ~ (p=0.913 n=6)
ComponentLoad-4                     2.097Mi ± 0%   2.097Mi ± 0%       ~ (p=0.937 n=6)
ComponentExecute-4                  1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4          1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4         1.203Ki ± 0%   1.203Ki ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                2.099Mi ± 0%   2.099Mi ± 0%       ~ (p=0.818 n=6)
SourceValidation-4                  1.984Ki ± 0%   1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                1.133Ki ± 0%   1.133Ki ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4              2.099Mi ± 0%   2.099Mi ± 0%       ~ (p=0.258 n=6)
geomean                             15.05Ki        15.05Ki       +0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.09k ± 0%   15.09k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      17.43k ± 0%   17.43k ± 0%       ~ (p=1.000 n=6)
ComponentExecute-4                    25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-1-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-2-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-4-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-8-4            25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
PoolContention/workers-16-4           25.00 ± 0%    25.00 ± 0%       ~ (p=1.000 n=6) ¹
ComponentLifecycle-4                 17.48k ± 0%   17.48k ± 0%       ~ (p=1.000 n=6) ¹
SourceValidation-4                    32.00 ± 0%    32.00 ± 0%       ~ (p=1.000 n=6) ¹
RegistryConcurrent-4                  2.000 ± 0%    2.000 ± 0%       ~ (p=1.000 n=6) ¹
LoaderLoadFromString-4               17.47k ± 0%   17.47k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               181.2         181.2       +0.00%
¹ all samples are equal

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                  291.4n ± 3%   286.3n ± 0%       ~ (p=0.065 n=6)
CircuitBreakerExecution_Success-4          22.51n ± 2%   22.50n ± 1%       ~ (p=0.991 n=6)
CircuitBreakerExecution_Failure-4          64.44n ± 0%   64.43n ± 1%       ~ (p=0.970 n=6)
geomean                                    75.05n        74.59n       -0.61%

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │        B/op        │    B/op     vs base                │
CircuitBreakerDetection-4                 144.0 ± 0%     144.0 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                  │ baseline-bench.txt │       benchmark-results.txt        │
                                  │     allocs/op      │ allocs/op   vs base                │
CircuitBreakerDetection-4                 1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Success-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
CircuitBreakerExecution_Failure-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                              ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/module
                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │       sec/op       │    sec/op     vs base              │
JQTransform_Simple-4                     966.2n ± 18%   858.7n ± 28%       ~ (p=0.132 n=6)
JQTransform_ObjectConstruction-4         1.478µ ±  1%   1.443µ ±  0%  -2.34% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.372µ ±  1%   3.300µ ±  1%  -2.11% (p=0.002 n=6)
JQTransform_Complex-4                    38.34µ ±  1%   38.29µ ±  1%       ~ (p=0.667 n=6)
JQTransform_Throughput-4                 1.789µ ±  2%   1.768µ ±  1%  -1.15% (p=0.022 n=6)
SSEPublishDelivery-4                     66.69n ±  2%   65.87n ±  0%  -1.23% (p=0.002 n=6)
geomean                                  1.674µ         1.622µ        -3.08%

                                 │ baseline-bench.txt │        benchmark-results.txt         │
                                 │        B/op        │     B/op      vs base                │
JQTransform_Simple-4                   1.273Ki ± 0%     1.273Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4       1.773Ki ± 0%     1.773Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4              2.625Ki ± 0%     2.625Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                  16.22Ki ± 0%     16.22Ki ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4               1.984Ki ± 0%     1.984Ki ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²                 +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │       benchmark-results.txt        │
                                 │     allocs/op      │ allocs/op   vs base                │
JQTransform_Simple-4                     10.00 ± 0%     10.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ObjectConstruction-4         15.00 ± 0%     15.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_ArraySelect-4                30.00 ± 0%     30.00 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Complex-4                    324.0 ± 0%     324.0 ± 0%       ~ (p=1.000 n=6) ¹
JQTransform_Throughput-4                 17.00 ± 0%     17.00 ± 0%       ~ (p=1.000 n=6) ¹
SSEPublishDelivery-4                     0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                             ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/schema
                                    │ baseline-bench.txt │       benchmark-results.txt       │
                                    │       sec/op       │   sec/op     vs base              │
SchemaValidation_Simple-4                    1.114µ ± 5%   1.135µ ± 3%       ~ (p=0.420 n=6)
SchemaValidation_AllFields-4                 1.682µ ± 9%   1.707µ ± 2%       ~ (p=0.589 n=6)
SchemaValidation_FormatValidation-4          1.603µ ± 3%   1.599µ ± 2%       ~ (p=0.848 n=6)
SchemaValidation_ManySchemas-4               1.844µ ± 3%   1.812µ ± 5%       ~ (p=0.310 n=6)
geomean                                      1.534µ        1.539µ       +0.35%

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │        B/op        │    B/op     vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                    │ baseline-bench.txt │       benchmark-results.txt        │
                                    │     allocs/op      │ allocs/op   vs base                │
SchemaValidation_Simple-4                   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_AllFields-4                0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_FormatValidation-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
SchemaValidation_ManySchemas-4              0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                                                ²               +0.00%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base               │
EventStoreAppend_InMemory-4                1.299µ ± 15%   1.253µ ± 13%        ~ (p=0.589 n=6)
EventStoreAppend_SQLite-4                  1.367m ±  5%   1.847m ± 16%  +35.19% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           14.51µ ±  2%   13.77µ ±  3%   -5.06% (p=0.002 n=6)
GetTimeline_InMemory/events-50-4           79.03µ ±  2%   77.05µ ± 14%   -2.51% (p=0.009 n=6)
GetTimeline_InMemory/events-100-4          123.4µ ±  1%   123.8µ ±  0%        ~ (p=0.240 n=6)
GetTimeline_InMemory/events-500-4          653.2µ ±  4%   637.2µ ±  1%        ~ (p=0.065 n=6)
GetTimeline_InMemory/events-1000-4         1.305m ±  1%   1.303m ±  0%        ~ (p=0.818 n=6)
GetTimeline_SQLite/events-10-4             111.7µ ±  1%   108.6µ ±  0%   -2.82% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             266.9µ ±  4%   249.3µ ±  0%   -6.58% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            438.8µ ±  4%   421.4µ ±  0%   -3.95% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.818m ±  3%   1.787m ±  2%   -1.70% (p=0.015 n=6)
GetTimeline_SQLite/events-1000-4           3.539m ±  4%   3.489m ±  0%   -1.40% (p=0.002 n=6)
geomean                                    226.3µ         226.2µ         -0.02%

                                   │ baseline-bench.txt │        benchmark-results.txt         │
                                   │        B/op        │     B/op      vs base                │
EventStoreAppend_InMemory-4                  821.5 ± 6%     770.5 ± 3%  -6.21% (p=0.017 n=6)
EventStoreAppend_SQLite-4                  1.983Ki ± 2%   1.983Ki ± 1%       ~ (p=0.946 n=6)
GetTimeline_InMemory/events-10-4           7.953Ki ± 0%   7.953Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4           46.62Ki ± 0%   46.62Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4          94.48Ki ± 0%   94.48Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4          472.8Ki ± 0%   472.8Ki ± 0%       ~ (p=0.545 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ± 0%       ~ (p=0.864 n=6)
GetTimeline_SQLite/events-10-4             16.74Ki ± 0%   16.74Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4             87.14Ki ± 0%   87.14Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4            175.4Ki ± 0%   175.4Ki ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4            846.1Ki ± 0%   846.1Ki ± 0%       ~ (p=0.247 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ± 0%       ~ (p=0.675 n=6)
geomean                                    67.56Ki        67.20Ki       -0.53%
¹ all samples are equal

                                   │ baseline-bench.txt │        benchmark-results.txt        │
                                   │     allocs/op      │  allocs/op   vs base                │
EventStoreAppend_InMemory-4                  7.000 ± 0%    7.000 ± 0%       ~ (p=1.000 n=6) ¹
EventStoreAppend_SQLite-4                    53.00 ± 0%    53.00 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-10-4             125.0 ± 0%    125.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-50-4             653.0 ± 0%    653.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-100-4           1.306k ± 0%   1.306k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-500-4           6.514k ± 0%   6.514k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_InMemory/events-1000-4          13.02k ± 0%   13.02k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-10-4               382.0 ± 0%    382.0 ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-50-4              1.852k ± 0%   1.852k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-100-4             3.681k ± 0%   3.681k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-500-4             18.54k ± 0%   18.54k ± 0%       ~ (p=1.000 n=6) ¹
GetTimeline_SQLite/events-1000-4            37.29k ± 0%   37.29k ± 0%       ~ (p=1.000 n=6) ¹
geomean                                     1.162k        1.162k       +0.00%
¹ all samples are equal

Benchmarks run with go test -bench=. -benchmem -count=6.
Regressions ≥ 20% are flagged. Results compared via benchstat.

@intel352
Copy link
Contributor Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Contributor

Copilot AI commented Mar 15, 2026

@intel352 I've opened a new pull request, #333, to work on those changes. Once the pull request is ready, I'll request review from you.

@intel352 intel352 merged commit 8eadf5e into main Mar 16, 2026
15 checks passed
@intel352 intel352 deleted the feat/step-skip-if branch March 16, 2026 01:01
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.

3 participants