Skip to content

feat: pipeline output schema declarations + step output field static analysis#375

Merged
intel352 merged 7 commits intomainfrom
copilot/add-runtime-enforcement-for-step-outputs
Mar 24, 2026
Merged

feat: pipeline output schema declarations + step output field static analysis#375
intel352 merged 7 commits intomainfrom
copilot/add-runtime-enforcement-for-step-outputs

Conversation

Copy link
Contributor

Copilot AI commented Mar 24, 2026

182 built-in steps declare output schemas that are never validated — not at config load, not at runtime, not by wfctl validate. Pipelines also have no mechanism to declare their output contract, making step.workflow_call callers and HTTP consumers blind to what they'll receive.

Changes

config/pipeline.go — Pipeline output declarations (opt-in)

  • Added PipelineOutputDef (type, description) and Outputs map[string]PipelineOutputDef to PipelineConfig
  • Fully backwards-compatible: pipelines without outputs: are unchanged
pipelines:
  get-item:
    outputs:
      id:    { type: string }
      found: { type: boolean }
    steps: [...]

cmd/wfctl/template_validate.go — Phase 1 static analysis

  • Added stepFieldDotRe / stepFuncFieldRe regexes to extract the field name after a step reference (.steps.NAME.FIELD and step "NAME" "FIELD")
  • validatePipelineTemplates now tracks step type+config metadata for each pipeline step
  • New validateStepOutputField uses schema.InferStepOutputs to warn when a referenced field isn't in the step type's declared output schema
  • Steps with dynamic/placeholder outputs (keys like (key), (claims)) and steps with no declared outputs are silently skipped
  • All violations are warnings (never errors) — zero false-positive risk on existing configs
WARNING: pipeline "get-form" step "respond": references query.rows
         but step "query" (step.db_query) declares outputs: row, found

cmd/wfctl/contract.go — Contract enrichment

  • Added ResponseSchema map[string]string (field → type) to EndpointContract
  • generateContract populates ResponseSchema from the pipeline's outputs: declaration when present
  • compareContracts now flags as breaking: response field removal, response field type change
  • printContract renders schema fields indented under each endpoint
Original prompt

This section details on the original issue you should resolve

<issue_title>Pipeline and step output type schemas are metadata-only — no runtime or static enforcement</issue_title>
<issue_description>## Summary

The engine has comprehensive type metadata for step outputs (182 built-in steps declare their output keys, types, and descriptions via schema.StepOutputDef), but this metadata is never enforced — not at config load time, not at runtime, and not by wfctl validate. The schemas serve documentation purposes only.

This is a separate concern from #367 (missingkey=zero for template resolution). Even if #367 is fixed, there is no mechanism to declare or validate the shape of data flowing between steps or returned from pipelines.

What exists today (metadata, not enforcement)

Every built-in step declares its outputs in schema/step_schema_builtins.go:

// step.db_query (mode: single)
Outputs: []StepOutputDef{
    {Key: "row",   Type: "map",     Description: "First result row..."},
    {Key: "found", Type: "boolean", Description: "Whether a row was found"},
}

// step.auth_validate
Outputs: []StepOutputDef{
    {Key: "(claims)",       Type: "any",    Description: "All claims..."},
    {Key: "(subject_field)", Type: "string", Description: "Value of sub claim..."},
}

These are exposed via wfctl get_step_schema and MCP tooling. But at runtime, StepResult.Output is always map[string]any — the engine never checks that the actual output matches the declared schema.

What's missing

1. Pipeline-level output contract

PipelineConfig has no outputs: field:

type PipelineConfig struct {
    Trigger      PipelineTriggerConfig
    Steps        []PipelineStepConfig
    OnError      string
    Timeout      string
    Compensation []PipelineStepConfig
    // ← No OutputSchema, no Outputs, no ResponseContract
}

A pipeline can return anything. Callers (HTTP triggers, step.workflow_call, eventbus consumers) have no declared contract for what they'll receive.

2. Step output validation

When a step completes, the engine merges its output into PipelineContext.Current without checking that the output matches the step type's declared schema:

// interfaces/pipeline.go:87-98
func (pc *PipelineContext) MergeStepOutput(stepName string, output map[string]any) {
    // No validation that output matches StepSchema.Outputs
    pc.StepOutputs[stepName] = stepOut
    maps.Copy(pc.Current, output)
}

3. Cross-step type compatibility in wfctl

The validator checks that step names exist but never checks that referenced fields exist in the step type's declared output schema. The schema metadata is already there — it just isn't used.

Example: {{.steps.query.row.slug}} — the validator could check:

  1. query step exists ✅ (already done)
  2. query is step.db_query → declared outputs include row (type: map) — could do, not done
  3. row contains slug — can't know (depends on SQL), but could warn it's unverifiable

Even checking (2) would catch bugs like referencing steps.query.rows when the step uses mode: single (which only outputs row + found, not rows + count).

4. step.workflow_call output is (dynamic): any

Callers get no type contract from the called pipeline. If the called pipeline declared its output schema, step.workflow_call could validate that output_mapping references valid fields.

5. wfctl contract test has no response schemas

EndpointContract captures method, path, pipeline name — but no request/response schema. Output field removals or type changes are invisible to contract diffing.

Suggested implementation (backwards-compatible)

All proposals are opt-in — existing configs continue to work unchanged.

Phase 1: Static analysis with existing metadata (no config changes)

Use the existing StepSchema.Outputs in wfctl template validate to warn when a template references a field not in the step type's declared output. This requires zero config changes — the metadata already exists for all 182 built-in steps.

$ wfctl template validate --config pipeline.yaml
WARNING: pipeline "get-form" step "respond": references steps.query.rows
         but step "query" (step.db_query, mode: single) outputs: row, found
         (did you mean "row"?)

Phase 2: Optional pipeline output declarations

Add an optional outputs: block to pipeline config. When absent, behavior is unchanged (fully backwards-compatible). When present, enables downstream validation.

pipelines:
  get-form:
    outputs:
      id:
        type: string
      slug:
        type: string
      found:
        type: boolean
    steps: [...]

Benefits:

  • step.workflow_call callers can validate output_mapping against the called pipeline's declared outputs
  • wfctl contract test can include response schemas in endpoint contracts
  • Serves as inline documentatio...

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

…nalysis

- Add PipelineOutputDef and Outputs field to PipelineConfig (Phase 2)
- Add static output field validation to wfctl template validate (Phase 1)
- Add ResponseSchema to EndpointContract with breaking change detection (Phase 4)
- Tests for all new functionality

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoCodeAlone/workflow/sessions/ef399305-04d6-4981-ba4c-4fd338147b57
Copilot AI changed the title [WIP] Add runtime enforcement for step output type schemas feat: pipeline output schema declarations + step output field static analysis Mar 24, 2026
Copilot AI requested a review from intel352 March 24, 2026 18:24
@intel352 intel352 marked this pull request as ready for review March 24, 2026 21:24
Copilot AI review requested due to automatic review settings March 24, 2026 21:24
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 opt-in pipeline output schema declarations and extends wfctl tooling to (a) statically warn when templates reference undeclared step output fields and (b) enrich/compare HTTP endpoint contracts with declared response schemas.

Changes:

  • Add outputs: declarations to PipelineConfig via PipelineOutputDef for pipeline response contracts.
  • Enhance wfctl template validate with step-output-field static analysis using schema.StepSchemaRegistry.InferStepOutputs.
  • Extend wfctl contract to record responseSchema and treat response field removals/type changes as breaking.

Reviewed changes

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

Show a summary per file
File Description
config/pipeline.go Adds opt-in pipeline Outputs contract metadata (PipelineOutputDef).
cmd/wfctl/template_validate.go Adds regex-based extraction of referenced step output fields and warns when not declared by the step schema.
cmd/wfctl/template_validate_test.go Adds tests for the new step output field warnings/no-warnings behavior.
cmd/wfctl/contract.go Adds ResponseSchema to endpoint contracts, populates from pipeline outputs, compares schema for breaking changes, prints schema.
cmd/wfctl/contract_test.go Adds tests for response schema extraction and breaking-change detection.
cmd/wfctl/test.go Formatting alignment only.
cmd/wfctl/plugin_install_new_test.go Formatting alignment only.
cmd/wfctl/main.go Formatting alignment only.
cmd/wfctl/infra_state.go Formatting alignment only.

Comment on lines +656 to +661
// Check for step output field references via dot-access (.steps.NAME.FIELD)
fieldDotMatches := stepFieldDotRe.FindAllStringSubmatch(actionContent, -1)
for _, m := range fieldDotMatches {
refStepName, refField := m[1], m[2]
validateStepOutputField(pipelineName, stepName, refStepName, refField, stepMeta, reg, result)
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

Field validation via stepFieldDotRe runs even when the template uses hyphenated dot-access (e.g., .steps.my-step.field), which is not valid Go-template field access and is already flagged by hyphenDotRe. This can lead to extra, misleading output-field warnings on top of the hyphenated dot-access warning. Consider skipping stepFieldDotRe validation when hyphenDotRe matches the same actionContent, or otherwise ensuring field validation only runs on syntactically valid access forms.

Copilot uses AI. Check for mistakes.
Comment on lines +535 to 543
// TestValidateStepOutputField_UndeclaredField_Warning checks that the validator
// warns when a template references a field that is not in the step type's
// declared output schema (Phase 1 static analysis).
func TestValidateStepOutputField_UndeclaredField_Warning(t *testing.T) {
// step.db_query with mode:single declares outputs: row, found
// Referencing .steps.query.rows (plural) should warn
cfg := &config.WorkflowConfig{
Pipelines: map[string]any{
"api": map[string]any{
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

This change replaces the previous hyphenated dot-access validation test with step-output-field tests, leaving hyphenated dot-access behavior untested. Since hyphenDotRe warnings are still part of validatePipelineTemplates, consider restoring a dedicated test that asserts the hyphenated dot-access warning to prevent regressions.

Copilot uses AI. Check for mistakes.
@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

- Tighten stepFuncFieldRe to require (^|pipe|paren) prefix, avoiding false matches
- Skip stepFieldDotRe field validation when hyphenDotRe fires (prevents double/misleading warnings on hyphenated dot-access)
- Update PipelineConfig.Outputs comment to accurately reflect current behavior
- Restore TestValidateConfigWithHyphenDotAccess test that was accidentally removed

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoCodeAlone/workflow/sessions/2e2b3ec2-d5d6-4637-a546-08f6a7dbd72e
@github-actions
Copy link

github-actions bot commented Mar 24, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:245: parsing iteration count: invalid syntax
baseline-bench.txt:307953: parsing iteration count: invalid syntax
baseline-bench.txt:608389: parsing iteration count: invalid syntax
baseline-bench.txt:920861: parsing iteration count: invalid syntax
baseline-bench.txt:1232235: parsing iteration count: invalid syntax
baseline-bench.txt:1546978: parsing iteration count: invalid syntax
benchmark-results.txt:245: parsing iteration count: invalid syntax
benchmark-results.txt:324906: parsing iteration count: invalid syntax
benchmark-results.txt:634345: parsing iteration count: invalid syntax
benchmark-results.txt:911852: parsing iteration count: invalid syntax
benchmark-results.txt:1200774: parsing iteration count: invalid syntax
benchmark-results.txt:1501045: 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              3.342m ± 190%   4.623m ± 121%       ~ (p=0.394 n=6)
ComponentLoad-4                    3.572m ±   1%   3.626m ±   1%  +1.50% (p=0.004 n=6)
ComponentExecute-4                 1.926µ ±   1%   1.954µ ±   3%  +1.45% (p=0.017 n=6)
PoolContention/workers-1-4         1.086µ ±   1%   1.095µ ±   1%  +0.83% (p=0.017 n=6)
PoolContention/workers-2-4         1.084µ ±   3%   1.082µ ±   4%       ~ (p=0.701 n=6)
PoolContention/workers-4-4         1.086µ ±   1%   1.082µ ±   1%       ~ (p=0.385 n=6)
PoolContention/workers-8-4         1.091µ ±   1%   1.092µ ±   1%       ~ (p=0.621 n=6)
PoolContention/workers-16-4        1.101µ ±   2%   1.100µ ±   1%       ~ (p=0.803 n=6)
ComponentLifecycle-4               3.662m ±   0%   3.617m ±   1%  -1.22% (p=0.002 n=6)
SourceValidation-4                 2.311µ ±   1%   2.236µ ±   2%  -3.27% (p=0.002 n=6)
RegistryConcurrent-4               836.2n ±   1%   781.7n ±   6%  -6.52% (p=0.009 n=6)
LoaderLoadFromString-4             3.688m ±   1%   3.612m ±   1%  -2.07% (p=0.002 n=6)
geomean                            17.66µ          17.99µ         +1.88%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.619 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.853 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.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.121 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.182Mi ± 0%   2.182Mi ± 0%       ~ (p=0.258 n=6)
geomean                             15.25Ki        15.25Ki       +0.00%
¹ all samples are equal

                            │ baseline-bench.txt │        benchmark-results.txt        │
                            │     allocs/op      │  allocs/op   vs base                │
InterpreterCreation-4                15.68k ± 0%   15.68k ± 0%       ~ (p=1.000 n=6)
ComponentLoad-4                      18.02k ± 0%   18.02k ± 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                 18.07k ± 0%   18.07k ± 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               18.06k ± 0%   18.06k ± 0%       ~ (p=1.000 n=6) ¹
geomean                               183.3         183.3       +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                  286.6n ± 5%   286.8n ± 2%       ~ (p=0.851 n=6)
CircuitBreakerExecution_Success-4          22.49n ± 0%   22.50n ± 0%  +0.07% (p=0.048 n=6)
CircuitBreakerExecution_Failure-4          64.37n ± 0%   64.49n ± 0%       ~ (p=0.177 n=6)
geomean                                    74.58n        74.66n       +0.11%

                                  │ 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                     854.7n ± 28%   840.2n ± 29%       ~ (p=0.240 n=6)
JQTransform_ObjectConstruction-4         1.396µ ±  1%   1.390µ ±  1%       ~ (p=0.121 n=6)
JQTransform_ArraySelect-4                3.237µ ±  0%   3.231µ ±  0%       ~ (p=0.554 n=6)
JQTransform_Complex-4                    37.55µ ±  1%   37.34µ ±  0%  -0.56% (p=0.009 n=6)
JQTransform_Throughput-4                 1.722µ ±  0%   1.718µ ±  0%  -0.26% (p=0.026 n=6)
SSEPublishDelivery-4                     69.82n ±  0%   69.77n ±  0%       ~ (p=0.506 n=6)
geomean                                  1.610µ         1.602µ        -0.53%

                                 │ 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.087µ ± 5%   1.095µ ± 10%       ~ (p=0.394 n=6)
SchemaValidation_AllFields-4                 1.669µ ± 8%   1.659µ ±  3%       ~ (p=0.310 n=6)
SchemaValidation_FormatValidation-4          1.573µ ± 1%   1.586µ ±  2%       ~ (p=0.195 n=6)
SchemaValidation_ManySchemas-4               1.826µ ± 5%   1.800µ ±  3%       ~ (p=0.394 n=6)
geomean                                      1.511µ        1.509µ        -0.11%

                                    │ 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.101µ ± 21%   1.245µ ± 29%        ~ (p=0.132 n=6)
EventStoreAppend_SQLite-4                  1.809m ±  6%   1.433m ±  4%  -20.77% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4           13.77µ ±  3%   13.74µ ±  3%        ~ (p=0.699 n=6)
GetTimeline_InMemory/events-50-4           76.25µ ± 19%   66.23µ ± 18%        ~ (p=0.589 n=6)
GetTimeline_InMemory/events-100-4          122.4µ ±  1%   123.8µ ±  2%   +1.17% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          628.5µ ±  6%   638.6µ ±  1%        ~ (p=0.065 n=6)
GetTimeline_InMemory/events-1000-4         1.283m ±  6%   1.305m ±  1%        ~ (p=0.065 n=6)
GetTimeline_SQLite/events-10-4             109.7µ ±  1%   108.5µ ±  2%   -1.07% (p=0.041 n=6)
GetTimeline_SQLite/events-50-4             251.2µ ±  0%   248.4µ ±  1%   -1.09% (p=0.004 n=6)
GetTimeline_SQLite/events-100-4            426.1µ ±  1%   419.2µ ±  2%   -1.61% (p=0.009 n=6)
GetTimeline_SQLite/events-500-4            1.799m ±  1%   1.792m ±  2%        ~ (p=0.065 n=6)
GetTimeline_SQLite/events-1000-4           3.503m ±  2%   3.488m ±  2%        ~ (p=0.093 n=6)
geomean                                    223.2µ         218.5µ         -2.10%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                  761.5 ± 8%     764.5 ± 11%       ~ (p=0.900 n=6)
EventStoreAppend_SQLite-4                  1.981Ki ± 3%   1.986Ki ±  2%       ~ (p=0.459 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%  +0.00% (p=0.015 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ±  0%       ~ (p=0.675 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=1.000 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ±  0%       ~ (p=0.602 n=6)
geomean                                    67.13Ki        67.17Ki        +0.05%
¹ 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.

Copy link
Contributor Author

Copilot AI commented Mar 24, 2026

@copilot apply changes based on the comments in this thread

All four review points addressed in adb2e27:

  1. stepFuncFieldRe too permissive — Added (?:^|\||\() prefix so it only matches actual step function calls, not substrings.
  2. Double warnings on hyphenated dot-accessstepFieldDotRe field validation is now skipped when hyphenDotRe matches the same action content.
  3. Inaccurate Outputs comment — Replaced the unimplemented step.workflow_call output_mapping reference with accurate wording about what is currently supported.
  4. Hyphenated dot-access test missingTestValidateConfigWithHyphenDotAccess restored.

@intel352
Copy link
Contributor

@copilot Address the failing test, linter, and build, regardless of pre-existing issues or not.

…lidatePipelineTemplates

The previous commit introduced a duplicate `reg := schema.NewStepSchemaRegistry()`
declaration causing a compile error ("no new variables on left side of :=").
Also fixes mixed spaces/tabs indentation in the same function block.

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Agent-Logs-Url: https://github.com/GoCodeAlone/workflow/sessions/d45a738a-055c-418e-825c-fce5c1de3dc5
Copy link
Contributor Author

Copilot AI commented Mar 24, 2026

@copilot Address the failing test, linter, and build, regardless of pre-existing issues or not.

Fixed in 6ed8e86. The previous commit accidentally introduced a duplicate reg := schema.NewStepSchemaRegistry() declaration (one at the top of the function and one after the loop), causing the no new variables on left side of := compile error. Also fixed the mixed spaces/tabs indentation in the same block.

@intel352 intel352 merged commit 0380fba into main Mar 24, 2026
18 checks passed
@intel352 intel352 deleted the copilot/add-runtime-enforcement-for-step-outputs branch March 24, 2026 22:52
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.

Pipeline and step output type schemas are metadata-only — no runtime or static enforcement

3 participants