Skip to content

feat(wfctl): validate iacProvider.computePlanVersion in plugin manifest (#693)#696

Merged
intel352 merged 1 commit into
mainfrom
feat/693-manifest-validation
May 17, 2026
Merged

feat(wfctl): validate iacProvider.computePlanVersion in plugin manifest (#693)#696
intel352 merged 1 commit into
mainfrom
feat/693-manifest-validation

Conversation

@intel352
Copy link
Copy Markdown
Contributor

Summary

Phase 2.1 follow-up to workflow#640 Phase 2 cascade (PR #694, v0.54.0). Adds the manifest validation gate at cmd/wfctl/deploy_providers.go::findIaCPluginDir that was flagged as missing in the Phase 1 inventory's Assumption 8.

Without this gate, a typo in iacProvider.computePlanVersion (V2, v2.0, two, v3) silently routes through the v1 dispatch path via wfctlhelpers.DispatchVersionFor's empty/unknown default — breaking the Phase 2 hard-cutover contract per ADR 0024 + ADR 0040.

Design choice

Issue #693 listed 3 options:

  1. Full pluginmanifest package (schema-driven, generic) — ~200 LOC, overkill
  2. Reuse existing schema/ JSON Schema validator — ~50 LOC, more machinery than warranted
  3. Lightweight computePlanVersion ∈ {v1, v2} enum check — minimum viable, picked

Picked option 3 per YAGNI. The field's domain is a 3-element enum ("", "v1", "v2") closed by the gRPC spec; a switch statement is the right tool.

Behavior

  • Empty/missing field → permitted (defaults to v1 dispatch per DispatchVersionFor)
  • "v1" / "v2" → permitted
  • Anything else → hard-fail with plugin %q manifest has invalid iacProvider.computePlanVersion %q (must be "", "v1", or "v2")

Validation fires only on the matching plugin manifest (not on every plugin in the directory) — so unrelated misconfigured plugins don't block resolving the right one.

Test plan

  • 7 table-driven test cases in TestFindIaCPluginDir_ComputePlanVersionValidation: empty, v1, v2 pass; V2 (uppercase typo), v2.0 (decimal typo), two (word typo), v3 (future Phase 2.3 tag pre-introduction) all reject with the actionable error
  • All pass locally: GOWORK=off go test ./cmd/wfctl/ -run 'TestFindIaCPluginDir_ComputePlanVersionValidation' -v

Rollback

Revert this commit. Pre-existing v1 behavior restored (silent fallback on typo).

Closes #693.

…st (#693)

Phase 2.1 follow-up to workflow#640 Phase 2 cascade (PR #694, v0.54.0).
Per ADR 0024 + ADR 0040 hard-cutover discipline + Phase 1 Assumption 8:
findIaCPluginDir previously json.Unmarshal'd the matching plugin
manifest and returned the raw iacProvider.computePlanVersion string
without validation. A typo (V2, v2.0, two, v3) would silently route
through the v1 dispatch path via wfctlhelpers.DispatchVersionFor's
empty/unknown default — breaking the Phase 2 hard-cutover contract.

Validation gate now hard-fails on the matching plugin manifest when
computePlanVersion is not in {"", "v1", "v2"}. Operators see the
misconfiguration loudly with an actionable error instead of silent
fallback. Empty/missing field still permitted (defaults to v1 dispatch).

Per workflow#693 issue body's 3-option design space, picked option 3
(lightweight enum check, minimum viable) over option 1 (full
pluginmanifest package, ~200 LOC overkill) and option 2 (reuse
existing schema/ JSON Schema validator, ~50 LOC). YAGNI applies — the
field's domain is a 3-element enum closed by the proto spec.

7 test cases (table-driven): empty, v1, v2 pass; V2 (uppercase typo),
v2.0 (decimal typo), two (word typo), v3 (future Phase 2.3 tag
pre-introduction) all reject with the actionable error.

Closes #693.
Copilot AI review requested due to automatic review settings May 17, 2026 05:11
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

Adds a lightweight validation gate for IaC plugin manifest iacProvider.computePlanVersion so wfctl rejects invalid dispatch-version values instead of accepting silent v1 fallback behavior.

Changes:

  • Validates matching IaC plugin manifests against "", "v1", and "v2".
  • Adds table-driven tests for accepted and rejected manifest values.

Reviewed changes

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

File Description
cmd/wfctl/deploy_providers.go Adds compute plan version enum validation during IaC plugin discovery.
cmd/wfctl/deploy_providers_compute_plan_version_test.go Adds regression tests for valid and invalid manifest values.
Comments suppressed due to low confidence (1)

cmd/wfctl/deploy_providers.go:164

  • After adding this enum check, the function comment above still says the returned raw string is unconstrained and that this loader performs only minimal json.Unmarshal validation. That documentation is now stale and will mislead future callers about whether invalid values can be returned from this path.
		switch m.IaCProvider.ComputePlanVersion {
		case "", "v1", "v2":
			// valid

Comment on lines +162 to +166
switch m.IaCProvider.ComputePlanVersion {
case "", "v1", "v2":
// valid
default:
return "", "", false, fmt.Errorf(
t.Fatalf("write manifest: %v", writeErr)
}

name, gotVer, _, err := findIaCPluginDir(pluginDir, "test-provider")
@codecov
Copy link
Copy Markdown

codecov Bot commented May 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:276: parsing iteration count: invalid syntax
baseline-bench.txt:329524: parsing iteration count: invalid syntax
baseline-bench.txt:612302: parsing iteration count: invalid syntax
baseline-bench.txt:880884: parsing iteration count: invalid syntax
baseline-bench.txt:1159222: parsing iteration count: invalid syntax
baseline-bench.txt:1421663: parsing iteration count: invalid syntax
benchmark-results.txt:276: parsing iteration count: invalid syntax
benchmark-results.txt:313663: parsing iteration count: invalid syntax
benchmark-results.txt:837323: parsing iteration count: invalid syntax
benchmark-results.txt:1125846: parsing iteration count: invalid syntax
benchmark-results.txt:1442266: parsing iteration count: invalid syntax
benchmark-results.txt:1744724: 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               7.008m ± 55%   6.522m ± 54%       ~ (p=0.394 n=6)
ComponentLoad-4                     3.572m ±  1%   3.589m ± 10%       ~ (p=0.065 n=6)
ComponentExecute-4                  1.951µ ±  1%   1.927µ ±  1%  -1.23% (p=0.041 n=6)
PoolContention/workers-1-4          1.100µ ±  2%   1.095µ ±  3%       ~ (p=0.509 n=6)
PoolContention/workers-2-4          1.072µ ±  1%   1.073µ ±  2%       ~ (p=0.900 n=6)
PoolContention/workers-4-4          1.075µ ±  1%   1.069µ ±  2%       ~ (p=0.331 n=6)
PoolContention/workers-8-4          1.077µ ±  0%   1.073µ ±  1%  -0.42% (p=0.037 n=6)
PoolContention/workers-16-4         1.078µ ±  1%   1.076µ ±  2%       ~ (p=0.366 n=6)
ComponentLifecycle-4                3.599m ±  1%   3.586m ±  1%       ~ (p=0.065 n=6)
SourceValidation-4                  2.283µ ±  2%   2.303µ ±  0%       ~ (p=0.054 n=6)
RegistryConcurrent-4                778.1n ±  7%   785.1n ±  5%       ~ (p=0.240 n=6)
LoaderLoadFromString-4              3.620m ±  1%   3.597m ±  0%  -0.63% (p=0.002 n=6)
geomean                             18.55µ         18.42µ        -0.73%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.794 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 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.183Mi ± 0%   2.183Mi ± 0%       ~ (p=0.502 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.732 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                  284.7n ± 4%   286.4n ± 1%       ~ (p=0.056 n=6)
CircuitBreakerExecution_Success-4          21.54n ± 0%   21.53n ± 0%       ~ (p=0.584 n=6)
CircuitBreakerExecution_Failure-4          66.14n ± 0%   66.16n ± 0%       ~ (p=0.734 n=6)
geomean                                    74.02n        74.16n       +0.19%

                                  │ 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              │
IaCStateBackend_InProcess-4              310.0n ± 25%   307.6n ± 28%       ~ (p=0.621 n=6)
IaCStateBackend_GRPC-4                   9.388m ±  2%   9.369m ±  5%       ~ (p=1.000 n=6)
JQTransform_Simple-4                     666.5n ± 37%   658.4n ± 38%       ~ (p=0.589 n=6)
JQTransform_ObjectConstruction-4         1.507µ ±  1%   1.498µ ±  1%       ~ (p=0.071 n=6)
JQTransform_ArraySelect-4                3.435µ ±  1%   3.428µ ±  1%       ~ (p=0.310 n=6)
JQTransform_Complex-4                    38.81µ ±  1%   38.55µ ±  0%  -0.69% (p=0.002 n=6)
JQTransform_Throughput-4                 1.846µ ±  1%   1.839µ ±  0%       ~ (p=0.089 n=6)
SSEPublishDelivery-4                     63.78n ±  1%   63.68n ±  0%       ~ (p=0.223 n=6)
geomean                                  3.825µ         3.805µ        -0.53%

                                 │ baseline-bench.txt │         benchmark-results.txt         │
                                 │        B/op        │     B/op       vs base                │
IaCStateBackend_InProcess-4              416.0 ± 0%       416.0 ±  0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                 5.871Mi ± 9%     5.909Mi ± 12%       ~ (p=0.937 n=6)
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.08%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                 │ baseline-bench.txt │        benchmark-results.txt        │
                                 │     allocs/op      │  allocs/op   vs base                │
IaCStateBackend_InProcess-4              2.000 ± 0%      2.000 ± 0%       ~ (p=1.000 n=6) ¹
IaCStateBackend_GRPC-4                  6.834k ± 0%     6.833k ± 0%       ~ (p=0.548 n=6)
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µ ± 21%   1.118µ ± 6%       ~ (p=0.853 n=6)
SchemaValidation_AllFields-4                1.677µ ±  2%   1.644µ ± 2%       ~ (p=0.065 n=6)
SchemaValidation_FormatValidation-4         1.595µ ±  1%   1.570µ ± 1%  -1.57% (p=0.026 n=6)
SchemaValidation_ManySchemas-4              1.831µ ±  3%   1.784µ ± 4%       ~ (p=0.132 n=6)
geomean                                     1.530µ         1.506µ       -1.56%

                                    │ 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.233µ ± 18%   1.150µ ± 19%        ~ (p=0.485 n=6)
EventStoreAppend_SQLite-4                  1.386m ±  8%   1.367m ±  3%        ~ (p=0.589 n=6)
GetTimeline_InMemory/events-10-4           13.09µ ±  3%   13.31µ ±  2%        ~ (p=0.394 n=6)
GetTimeline_InMemory/events-50-4           60.23µ ± 25%   74.11µ ±  4%  +23.06% (p=0.026 n=6)
GetTimeline_InMemory/events-100-4          120.3µ ±  0%   147.5µ ±  1%  +22.59% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          619.8µ ±  0%   623.8µ ±  2%   +0.63% (p=0.009 n=6)
GetTimeline_InMemory/events-1000-4         1.270m ±  0%   1.274m ±  0%        ~ (p=0.180 n=6)
GetTimeline_SQLite/events-10-4             105.6µ ±  2%   104.7µ ±  1%   -0.85% (p=0.004 n=6)
GetTimeline_SQLite/events-50-4             244.0µ ±  1%   244.5µ ±  0%        ~ (p=0.310 n=6)
GetTimeline_SQLite/events-100-4            418.2µ ±  1%   414.5µ ±  1%   -0.87% (p=0.009 n=6)
GetTimeline_SQLite/events-500-4            1.785m ±  1%   1.773m ±  1%        ~ (p=0.132 n=6)
GetTimeline_SQLite/events-1000-4           3.514m ±  1%   3.465m ±  0%   -1.40% (p=0.002 n=6)
geomean                                    212.8µ         218.5µ         +2.68%

                                   │ baseline-bench.txt │        benchmark-results.txt         │
                                   │        B/op        │     B/op      vs base                │
EventStoreAppend_InMemory-4                  788.0 ± 8%     807.0 ± 8%       ~ (p=0.394 n=6)
EventStoreAppend_SQLite-4                  1.984Ki ± 2%   1.985Ki ± 2%       ~ (p=0.615 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.405 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.405 n=6)
GetTimeline_SQLite/events-1000-4           1.639Mi ± 0%   1.639Mi ± 0%       ~ (p=0.394 n=6)
geomean                                    67.33Ki        67.47Ki       +0.20%
¹ 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
Copy Markdown
Contributor Author

Copilot review acknowledgement

Copilot raised that the validated computePlanVersion returned by findIaCPluginDir is discarded by discoverAndLoadIaCProvider (pName, _, hasBinary, findErr := findIaCPluginDir(...)), and that the actual dispatch routing reads CapabilitiesResponse.ComputePlanVersion over gRPC via typedIaCAdapter.ComputePlanVersion(). Confirmed.

The PR body's framing slightly overstated the validation's role in dispatch — the gRPC capability is runtime-authoritative for dispatch routing. The plugin.json field is metadata for registry/install-time tooling.

The validation gate is still valuable as defense-in-depth:

  1. Catches typos at plugin-load time (findIaCPluginDir returning an error short-circuits discoverAndLoadIaCProvider before the gRPC handshake)
  2. Plugin metadata correctness for workflow-registry consumers + future tooling that reads plugin.json without a gRPC handshake
  3. Cheap (~15 LOC) defense matching ADR 0024 hard-cutover discipline at the manifest layer

Adding a 2nd test exercising end-to-end through discoverAndLoadIaCProvider would tighten the regression gate; tracking for a Phase 2.1.1 follow-up if the value warrants the test fixture cost (would require spinning up a fake plugin binary or mocking gRPC handshake).

Defensive gate stays; framing clarified above. CI re-run for the unrelated TestPluginConformanceArtifactNoGoModFailsCleanly flake in progress (test passes locally; flake not caused by this PR's changes).

@intel352 intel352 merged commit 5f59e77 into main May 17, 2026
36 of 37 checks passed
@intel352 intel352 deleted the feat/693-manifest-validation branch May 17, 2026 05:44
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.

Phase 2.1 follow-up to #640: manifest validation gate at findIaCPluginDir

2 participants