Skip to content

fix(plugin/external): empty ConfigMessage for input-only STRICT_PROTO step contracts#644

Merged
intel352 merged 2 commits into
mainfrom
fix/empty-config-message-strict-proto
May 12, 2026
Merged

fix(plugin/external): empty ConfigMessage for input-only STRICT_PROTO step contracts#644
intel352 merged 2 commits into
mainfrom
fix/empty-config-message-strict-proto

Conversation

@intel352
Copy link
Copy Markdown
Contributor

Summary

Steps that declare STRICT_PROTO mode + InputMessage + OutputMessage but no ConfigMessage (input-only steps where data flows through the input proto, not a config schema) failed engine initialization with:

STRICT_PROTO contract for config message "" cannot use legacy Struct fallback: missing protobuf message name

Engine now treats empty ConfigMessage as "no typed config" → encodes cfg as legacy *structpb.Struct only, returns nil typed payload. Plugin's typed step factory reads from the InputMessage proto as designed.

Caught by

BMW PR #278 image-launch smoke against v0.51.3 + workflow-plugin-eventbus v0.3.0:

step.eventbus.ack: STRICT_PROTO contract for config message "" cannot use legacy Struct fallback: missing protobuf message name

Eventbus declares 3 input-only steps (ack/publish/consume); same shape applies to any plugin whose steps carry data via the input proto.

Diff

3 lines + 1 comment in createTypedConfigRequest + 1 unit test.

Test

TestCreateTypedConfigRequestEmptyConfigMessageStrictProto — asserts:

  • empty ConfigMessage + STRICT_PROTO + nil cfg → no error
  • typed payload returned is nil
  • legacy *structpb.Struct returned is empty (no fields)

Test plan

Release

Tag v0.51.4 post-merge.

🤖 Generated with Claude Code

…T_PROTO step contracts

Steps that declare STRICT_PROTO mode + InputMessage + OutputMessage but no
ConfigMessage (e.g., step.eventbus.ack, step.eventbus.publish) failed engine
initialization with:

  STRICT_PROTO contract for config message "" cannot use legacy Struct
  fallback: missing protobuf message name

The step has no per-instance config schema — data flows through the input
message. Engine now treats empty ConfigMessage as "no typed config", encodes
cfg as legacy *structpb.Struct, returns nil typed payload. Plugin's typed
factory reads from InputMessage as designed.

Caught by BMW PR #278 image-launch smoke against v0.51.3 + eventbus v0.3.0
(steps.eventbus.{ack,publish,consume} have empty ConfigMessage).

Test: TestCreateTypedConfigRequestEmptyConfigMessageStrictProto.
Copilot AI review requested due to automatic review settings May 12, 2026 15:07
Copy link
Copy Markdown
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

Fixes engine-side STRICT_PROTO config encoding for “input-only” step contracts that omit ConfigMessage, preventing initialization failures when a plugin declares typed input/output but no typed config schema.

Changes:

  • Treat empty ContractDescriptor.ConfigMessage as “no typed config”: encode only legacy Struct and return nil typed payload.
  • Add unit test covering STRICT_PROTO + empty ConfigMessage behavior.

Reviewed changes

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

File Description
plugin/external/adapter.go Adds an early return in createTypedConfigRequest for empty ConfigMessage to avoid typed-encode errors.
plugin/external/adapter_test.go Adds regression test for STRICT_PROTO step contracts with empty ConfigMessage.

Comment thread plugin/external/adapter.go Outdated
Comment thread plugin/external/adapter_test.go Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
plugin/external/adapter.go 60.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 12, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:262: parsing iteration count: invalid syntax
baseline-bench.txt:317561: parsing iteration count: invalid syntax
baseline-bench.txt:623700: parsing iteration count: invalid syntax
baseline-bench.txt:940768: parsing iteration count: invalid syntax
baseline-bench.txt:1287893: parsing iteration count: invalid syntax
baseline-bench.txt:1548096: parsing iteration count: invalid syntax
benchmark-results.txt:262: parsing iteration count: invalid syntax
benchmark-results.txt:309881: parsing iteration count: invalid syntax
benchmark-results.txt:620466: parsing iteration count: invalid syntax
benchmark-results.txt:937675: parsing iteration count: invalid syntax
benchmark-results.txt:1238270: parsing iteration count: invalid syntax
benchmark-results.txt:1579389: 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               5.803m ± 70%   3.806m ± 160%       ~ (p=0.699 n=6)
ComponentLoad-4                     3.542m ±  7%   3.608m ±   2%       ~ (p=0.065 n=6)
ComponentExecute-4                  1.940µ ±  1%   1.987µ ±   1%  +2.42% (p=0.002 n=6)
PoolContention/workers-1-4          1.086µ ±  1%   1.099µ ±   1%  +1.20% (p=0.006 n=6)
PoolContention/workers-2-4          1.081µ ±  3%   1.108µ ±   4%  +2.50% (p=0.026 n=6)
PoolContention/workers-4-4          1.088µ ±  2%   1.115µ ±   1%  +2.48% (p=0.002 n=6)
PoolContention/workers-8-4          1.087µ ±  1%   1.113µ ±   1%  +2.35% (p=0.002 n=6)
PoolContention/workers-16-4         1.127µ ±  3%   1.107µ ±   3%       ~ (p=0.784 n=6)
ComponentLifecycle-4                3.654m ±  7%   3.638m ±   1%       ~ (p=0.485 n=6)
SourceValidation-4                  2.349µ ±  0%   2.352µ ±   1%       ~ (p=0.848 n=6)
RegistryConcurrent-4                821.3n ±  3%   828.8n ±   4%       ~ (p=0.615 n=6)
LoaderLoadFromString-4              3.744m ±  2%   3.694m ±   1%       ~ (p=0.240 n=6)
geomean                             18.54µ         18.05µ         -2.63%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.855 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.288 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.563 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.835 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                  282.7n ± 8%   287.2n ± 6%       ~ (p=0.058 n=6)
CircuitBreakerExecution_Success-4          21.54n ± 0%   21.54n ± 2%       ~ (p=0.708 n=6)
CircuitBreakerExecution_Failure-4          66.25n ± 0%   66.31n ± 0%       ~ (p=0.102 n=6)
geomean                                    73.88n        74.30n       +0.57%

                                  │ 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                     878.1n ± 27%   899.7n ± 36%       ~ (p=0.132 n=6)
JQTransform_ObjectConstruction-4         1.471µ ±  0%   1.498µ ±  1%  +1.80% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.309µ ±  1%   3.434µ ±  2%  +3.79% (p=0.002 n=6)
JQTransform_Complex-4                    38.15µ ±  2%   39.14µ ±  1%  +2.59% (p=0.002 n=6)
JQTransform_Throughput-4                 1.782µ ±  0%   1.847µ ±  2%  +3.62% (p=0.002 n=6)
SSEPublishDelivery-4                     67.75n ±  1%   67.76n ±  1%       ~ (p=0.818 n=6)
geomean                                  1.643µ         1.682µ        +2.37%

                                 │ 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.119µ ± 4%   1.117µ ± 16%       ~ (p=0.818 n=6)
SchemaValidation_AllFields-4                 1.668µ ± 1%   1.652µ ±  8%       ~ (p=0.394 n=6)
SchemaValidation_FormatValidation-4          1.581µ ± 1%   1.590µ ±  3%       ~ (p=0.063 n=6)
SchemaValidation_ManySchemas-4               1.828µ ± 3%   1.820µ ±  4%       ~ (p=0.937 n=6)
geomean                                      1.524µ        1.520µ        -0.23%

                                    │ 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.183µ ± 10%   1.145µ ± 12%       ~ (p=0.394 n=6)
EventStoreAppend_SQLite-4                  1.663m ± 12%   1.498m ± 10%  -9.92% (p=0.041 n=6)
GetTimeline_InMemory/events-10-4           13.84µ ±  2%   13.83µ ±  2%       ~ (p=0.699 n=6)
GetTimeline_InMemory/events-50-4           64.34µ ± 20%   77.28µ ± 18%       ~ (p=0.093 n=6)
GetTimeline_InMemory/events-100-4          122.6µ ±  2%   128.5µ ±  1%  +4.82% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          633.5µ ±  2%   665.9µ ±  1%  +5.12% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.287m ±  0%   1.361m ±  2%  +5.77% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             107.5µ ±  1%   109.9µ ±  1%  +2.26% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             248.5µ ±  1%   258.2µ ±  3%  +3.91% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            419.9µ ±  0%   438.9µ ±  1%  +4.52% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.793m ±  1%   1.876m ±  1%  +4.61% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.516m ±  1%   3.624m ±  1%  +3.06% (p=0.002 n=6)
geomean                                    219.3µ         226.3µ        +3.21%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                  779.0 ± 6%     758.5 ± 12%       ~ (p=0.818 n=6)
EventStoreAppend_SQLite-4                  1.989Ki ± 1%   1.983Ki ±  1%       ~ (p=0.294 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.455 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ±  0%       ~ (p=0.448 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%  +0.00% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ±  0%       ~ (p=0.177 n=6)
geomean                                    67.28Ki        67.11Ki        -0.24%
¹ 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.

Copilot AI review requested due to automatic review settings May 12, 2026 15:30
Copy link
Copy Markdown
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

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@intel352 intel352 merged commit 8ac9bf5 into main May 12, 2026
26 of 27 checks passed
@intel352 intel352 deleted the fix/empty-config-message-strict-proto branch May 12, 2026 16:04
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