Skip to content

fix(wfctl): R-A4 align rule consults top-level secrets.generate + entries (workflow#541)#550

Merged
intel352 merged 4 commits into
mainfrom
feat/r-a4-toplevel-secrets
May 5, 2026
Merged

fix(wfctl): R-A4 align rule consults top-level secrets.generate + entries (workflow#541)#550
intel352 merged 4 commits into
mainfrom
feat/r-a4-toplevel-secrets

Conversation

@intel352
Copy link
Copy Markdown
Contributor

@intel352 intel352 commented May 5, 2026

Summary

wfctl infra align --strict previously fired a false-positive R-A4 FAIL for env-var tokens that resolve via the top-level secrets.generate / secrets.entries blocks. buildAlignContext only populated ctx.secretKeys from the module-form (secrets.generate / secrets.requires modules), so any config using the canonical top-level secrets: block was unfairly flagged.

This PR fixes the original W-541 align bug. During Copilot review, three additional incomplete-merge surfaces in config.processImports were surfaced and addressed in follow-on commits to keep the import-merge logic internally consistent. Two further direct-parsing-caller paths (parseSecretsConfig, wfctl secrets) were scope-cut to follow-up issues for separate PRs.

Closes workflow#541. Unblocks core-dump PR #190 (which uses inline top-level secrets.generate to declare STAGING_PG_PASSWORD; verified against the actual config — no imports: involved).

Scope (delta from original spec)

The original plan limited this PR to buildAlignContext + 2 align tests. Copilot review surfaced that config.processImports was missing several merge surfaces that the new align behavior would expose. Rather than ship a half-fix, the import-merge work was completed in the same PR. Honest scope:

1. Original W-541 fix — R-A4 align rule (commit abc2db8c)

  • cmd/wfctl/infra_align_rules.go: extend buildAlignContext's top-level-secrets branch to also seed ctx.secretKeys from cfg.Secrets.Generate[].Key and cfg.Secrets.Entries[].Name.
  • Tests: TestInfraAlign_RA4_TopLevelSecretsGenerate_DoesNotFire, TestInfraAlign_RA4_TopLevelSecretsEntries_DoesNotFire.

2. processImports — Secrets merge (commit 8632612f)

  • config/config.go: processImports now merges WorkflowConfig.Secrets. Generate (dedupe by Key), Entries (dedupe by Name), scalars parent-wins.
  • Tests: TestLoadFromFile_ImportSecretsMerge, TestLoadFromFile_ImportSecretsOnlyInImport, TestInfraAlign_RA4_TopLevelSecrets_FromImport_DoesNotFire.

3. processImports — SecretStores + Secrets.Config per-key merge (commit dd81421d)

  • config/config.go: merge WorkflowConfig.SecretStores (per-store dedupe by name); change Secrets.Config from all-or-nothing to per-key merge to support shared-defaults + local-override pattern.
  • Tests: TestProcessImports_MergesSecretStoresFromImport, TestProcessImports_SecretsConfigMergesPerKey_LocalOverride.

4. processImports — Environments merge + canonical provider IDs (commit 30bc8bc8)

  • config/config.go: merge WorkflowConfig.Environments per-env (parent wins). ResolveSecretStore consults Environments[env].SecretsStoreOverride; without merge, imported overrides drop and secrets route to wrong backend.
  • Test fixtures: use canonical aws-secrets-manager and gcp-secret-manager (per docs/dsl-reference.md and cmd/wfctl/wizard.go).
  • Tests: TestProcessImports_MergesEnvironmentsFromImport.

A complete WorkflowConfig field audit is documented in commit 30bc8bc8's body. Remaining unmerged top-level fields (Requires, Infrastructure, Engine, CI, Infra, Services, Mesh, Networking, Security) are intentionally out-of-scope follow-ups — none are referenced by the alignment-rule code path that motivated this PR.

Follow-up issues

The merge work in this PR only benefits callers that go through config.LoadFromFile. Two direct-parsing-caller paths still bypass processImports and were scope-cut to keep this PR focused:

  • workflow#551 — parseSecretsConfig (cmd/wfctl/infra_bootstrap.go, cmd/wfctl/infra.go) raw-unmarshals only the current file. After this PR, wfctl infra align correctly resolves imported secrets.generate keys, but wfctl infra bootstrap will silently skip generation of those secrets.
  • workflow#552 — wfctl secrets family (loadSecretsConfig, runSecretsSetup) parses YAML directly. Configs that move secrets.entries / secretStores / per-env overrides into shared imports will look empty or route to the wrong store in secrets get / validate / export / setup.

Both are mechanical refactors (call config.LoadFromFile instead of yaml.Unmarshal) but have a wide enough blast radius to deserve their own PR + regression test coverage.

Test plan

  • GOWORK=off go test -race -count=1 ./config/ ./cmd/wfctl/ — green
  • Each new test verified to FAIL without its corresponding fix (proves bug + locks behavior)
  • Existing R-A4 module-form test (TestInfraAlign_RA4_SecretsGenerate_DoesNotFire) and all import precedence tests still pass
  • No go.mod / go.sum changes (the Go Mod Tidy failure on this PR is pre-existing AWS SDK drift on main, unrelated to this change)

Design reference

docs/plans/2026-05-05-iac-deferred-cleanup-design.md (PR 1, W-541). v0.21.1 will be cut after this merges.

Rollback

Revert the squash commit on main. Stopgap fallback is the env-var export pattern in core-dump's deploy.yml align step (STAGING_PG_PASSWORD, STAGING_VPC_UUID).

…ries (workflow#541)

buildAlignContext now populates ctx.secretKeys from
cfg.Secrets.Generate[i].Key and cfg.Secrets.Entries[i].Name in addition
to the existing module-form (secrets.generate / secrets.requires) path.
This unblocks core-dump PR #190 which uses top-level secrets.generate to
declare STAGING_PG_PASSWORD; before this fix wfctl infra align --strict
fired a false-positive R-A4 FAIL because the unresolved env var
${STAGING_PG_PASSWORD} couldn't be matched against a known secret key.

Adds two pinning tests:
- TestInfraAlign_RA4_TopLevelSecretsGenerate_DoesNotFire
- TestInfraAlign_RA4_TopLevelSecretsEntries_DoesNotFire

Existing module-form R-A4 tests remain green (no regression).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 5, 2026 08:43
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

This PR updates wfctl infra align so R-A4 treats secrets declared in the canonical top-level secrets: block as valid env-var resolution sources, reducing false positives in infrastructure config validation.

Changes:

  • Extend buildAlignContext to seed secret keys from top-level secrets.generate[].key and secrets.entries[].name.
  • Add regression tests covering top-level secrets.generate and secrets.entries for R-A4.
  • Keep existing module-form secrets.generate handling unchanged while broadening top-level config support.

Reviewed changes

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

File Description
cmd/wfctl/infra_align_rules.go Adds top-level secret key collection so R-A4 can recognize canonical secrets: declarations.
cmd/wfctl/infra_align_test.go Adds R-A4 regression tests for top-level secrets.generate and secrets.entries.

Comment thread cmd/wfctl/infra_align_rules.go
Comment thread cmd/wfctl/infra_align_test.go
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:260: parsing iteration count: invalid syntax
baseline-bench.txt:297945: parsing iteration count: invalid syntax
baseline-bench.txt:602536: parsing iteration count: invalid syntax
baseline-bench.txt:899675: parsing iteration count: invalid syntax
baseline-bench.txt:1189389: parsing iteration count: invalid syntax
baseline-bench.txt:1517254: parsing iteration count: invalid syntax
benchmark-results.txt:260: parsing iteration count: invalid syntax
benchmark-results.txt:405717: parsing iteration count: invalid syntax
benchmark-results.txt:782641: parsing iteration count: invalid syntax
benchmark-results.txt:1199897: parsing iteration count: invalid syntax
benchmark-results.txt:1598001: parsing iteration count: invalid syntax
benchmark-results.txt:2017819: parsing iteration count: invalid syntax
goos: linux
goarch: amd64
pkg: github.com/GoCodeAlone/workflow/dynamic
cpu: AMD EPYC 9V74 80-Core Processor                
                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │       sec/op       │    sec/op      vs base               │
InterpreterCreation-4              3.117m ± 254%   2.345m ± 245%        ~ (p=0.132 n=6)
ComponentLoad-4                    3.471m ±  12%   2.697m ±  11%  -22.30% (p=0.002 n=6)
ComponentExecute-4                 1.803µ ±   0%   1.397µ ±   2%  -22.52% (p=0.002 n=6)
PoolContention/workers-1-4        1011.5n ±   0%   786.0n ±   1%  -22.30% (p=0.002 n=6)
PoolContention/workers-2-4        1016.0n ±   1%   785.3n ±   3%  -22.71% (p=0.002 n=6)
PoolContention/workers-4-4        1018.0n ±   1%   782.3n ±   2%  -23.15% (p=0.002 n=6)
PoolContention/workers-8-4        1018.0n ±   1%   784.7n ±   1%  -22.92% (p=0.002 n=6)
PoolContention/workers-16-4       1021.0n ±   1%   784.7n ±   3%  -23.14% (p=0.002 n=6)
ComponentLifecycle-4               3.517m ±   0%   2.752m ±   1%  -21.76% (p=0.002 n=6)
SourceValidation-4                 2.076µ ±   2%   1.607µ ±   1%  -22.57% (p=0.002 n=6)
RegistryConcurrent-4               748.2n ±   2%   581.7n ±   3%  -22.25% (p=0.002 n=6)
LoaderLoadFromString-4             3.568m ±   0%   2.744m ±   1%  -23.10% (p=0.002 n=6)
geomean                            16.52µ          12.75µ         -22.79%

                            │ baseline-bench.txt │        benchmark-results.txt         │
                            │        B/op        │     B/op      vs base                │
InterpreterCreation-4               2.027Mi ± 0%   2.027Mi ± 0%       ~ (p=0.732 n=6)
ComponentLoad-4                     2.180Mi ± 0%   2.180Mi ± 0%       ~ (p=0.974 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.457 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%  +0.00% (p=0.037 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                  295.9n ± 7%   233.1n ± 4%  -21.22% (p=0.002 n=6)
CircuitBreakerExecution_Success-4          22.65n ± 0%   17.59n ± 0%  -22.38% (p=0.002 n=6)
CircuitBreakerExecution_Failure-4          70.98n ± 1%   55.98n ± 0%  -21.13% (p=0.002 n=6)
geomean                                    78.07n        61.22n       -21.58%

                                  │ 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                     831.4n ± 32%   643.3n ± 35%  -22.62% (p=0.026 n=6)
JQTransform_ObjectConstruction-4         1.445µ ±  1%   1.104µ ±  1%  -23.61% (p=0.002 n=6)
JQTransform_ArraySelect-4                3.403µ ±  2%   2.728µ ±  4%  -19.84% (p=0.002 n=6)
JQTransform_Complex-4                    41.23µ ±  2%   32.92µ ±  1%  -20.14% (p=0.002 n=6)
JQTransform_Throughput-4                 1.758µ ±  1%   1.351µ ±  3%  -23.13% (p=0.002 n=6)
SSEPublishDelivery-4                     64.33n ±  1%   50.03n ±  1%  -22.24% (p=0.002 n=6)
geomean                                  1.634µ         1.276µ        -21.94%

                                 │ 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                  1108.5n ± 14%   888.5n ± 11%  -19.85% (p=0.002 n=6)
SchemaValidation_AllFields-4                1.660µ ±  1%   1.288µ ±  3%  -22.39% (p=0.002 n=6)
SchemaValidation_FormatValidation-4         1.593µ ±  1%   1.245µ ±  4%  -21.85% (p=0.002 n=6)
SchemaValidation_ManySchemas-4              1.623µ ±  1%   1.252µ ±  2%  -22.89% (p=0.002 n=6)
geomean                                     1.477µ         1.156µ        -21.75%

                                    │ 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               1129.0n ± 13%   765.9n ±   6%   -32.17% (p=0.002 n=6)
EventStoreAppend_SQLite-4                  1.028m ±  4%   5.061m ± 168%  +392.38% (p=0.002 n=6)
GetTimeline_InMemory/events-10-4          12.312µ ±  3%   8.635µ ±  19%   -29.87% (p=0.002 n=6)
GetTimeline_InMemory/events-50-4           69.65µ ± 24%   42.19µ ±   1%   -39.42% (p=0.002 n=6)
GetTimeline_InMemory/events-100-4         106.61µ ±  0%   84.01µ ±   1%   -21.20% (p=0.002 n=6)
GetTimeline_InMemory/events-500-4          546.5µ ±  1%   432.3µ ±   1%   -20.90% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4        1108.8µ ±  2%   878.3µ ±   1%   -20.79% (p=0.002 n=6)
GetTimeline_SQLite/events-10-4             85.81µ ±  1%   66.97µ ±   2%   -21.96% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             221.2µ ±  2%   177.0µ ±   1%   -20.00% (p=0.002 n=6)
GetTimeline_SQLite/events-100-4            383.8µ ±  0%   308.0µ ±   0%   -19.75% (p=0.002 n=6)
GetTimeline_SQLite/events-500-4            1.676m ±  0%   1.353m ±   1%   -19.24% (p=0.002 n=6)
GetTimeline_SQLite/events-1000-4           3.259m ±  0%   2.650m ±   1%   -18.69% (p=0.002 n=6)
geomean                                    192.3µ         170.2µ          -11.51%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                  775.0 ± 4%     849.0 ± 12%       ~ (p=0.126 n=6)
EventStoreAppend_SQLite-4                  1.983Ki ± 2%   1.979Ki ±  4%       ~ (p=0.669 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=1.000 n=6)
GetTimeline_InMemory/events-1000-4         944.3Ki ± 0%   944.3Ki ±  0%       ~ (p=0.123 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%  -0.00% (p=0.022 n=6)
geomean                                    67.23Ki        67.74Ki        +0.75%
¹ 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%    52.00 ± 2%       ~ (p=0.061 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.160k       -0.16%
¹ all samples are equal

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

processImports previously dropped imported WorkflowConfig.Secrets blocks,
so a `secrets:` declaration in shared.yaml never reached the loaded
parent config. R-A4 (and R-A9) consult cfg.Secrets via buildAlignContext;
without the merge, an env var resolved by an imported secrets.generate /
secrets.entries entry continued to false-positive as unresolved.

Merge semantics match the rest of processImports:
- DefaultStore / Provider / Config / Rotation: parent wins; imported
  value adopted only when parent's field is unset.
- Generate slice: append imported entries; dedupe by Key (parent first).
- Entries slice: append imported entries; dedupe by Name (parent first).

Tests:
- config: TestLoadFromFile_ImportSecretsMerge (precedence + dedupe);
          TestLoadFromFile_ImportSecretsOnlyInImport (cfg.Secrets nil
          before fix, populated after).
- wfctl: TestInfraAlign_RA4_TopLevelSecrets_FromImport_DoesNotFire
          (verified to FAIL without the merge — proves the bug + fix).

Addresses Copilot inline review on PR #550.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread config/config.go Outdated
Comment thread config/config.go Outdated
Comment thread config/config_import_test.go Outdated
…fig (workflow#541)

Two follow-on fixes from Copilot round-2 review on PR #550:

1. SecretStores merge: ResolveSecretStore / getProviderForStore look up
   store names against WorkflowConfig.SecretStores. processImports
   previously dropped that map entirely; an imported `defaultStore` or
   `entries[*].store` reference would later be treated as a raw provider
   name and fail with unknown-provider. Now merged with parent-wins
   per-key dedupe (matching Workflows / Triggers / Pipelines / Platform
   / Plugins.External semantics).

2. Secrets.Config per-key merge: previously the entire imported map was
   discarded as soon as the main file defined any key, breaking the
   shared-defaults + local-override pattern (e.g. import provides
   {repo, token_env, api_url}; main only overrides {token_env}). Now
   per-key merge: imported keys absent from main survive, main wins
   on key conflicts.

3. Comment-typo fix in config_import_test.go (missing space).

Tests:
- TestProcessImports_MergesSecretStoresFromImport
- TestProcessImports_SecretsConfigMergesPerKey_LocalOverride
Both verified to FAIL without their respective fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread config/config.go
Comment thread config/config_import_test.go Outdated
Comment thread config/config_import_test.go Outdated
…IDs (workflow#541)

Round-3 review surfaced a third missing merge surface in processImports.
This commit closes it and audits the remaining WorkflowConfig fields so
the cascade ends here.

Field audit of WorkflowConfig vs processImports merge coverage:

  Already merged BEFORE this PR:
    Modules, Workflows, Triggers, Pipelines, Platform, Plugins.External,
    Sidecars

  Merged in earlier commits of this PR (#550):
    Secrets (Generate dedupe-by-Key, Entries dedupe-by-Name, Config
    per-key, scalars parent-wins), SecretStores (per-store dedupe-by-name)

  Merged in THIS commit:
    Environments (per-env dedupe-by-name, parent-wins) — closes the
    finding that ResolveSecretStore consults
    Environments[env].SecretsStoreOverride and dropping it routes
    secrets to the wrong backend.

  Intentionally NOT merged (out-of-scope follow-ups for #541):
    Requires, Infrastructure, Engine, CI, Infra, Services, Mesh,
    Networking, Security — these are all single-document config blocks
    where merge semantics need separate design + dedicated regression
    tests; will file follow-up issues.

Test fixture cleanup (Copilot round-3 nits): use canonical provider IDs
`aws-secrets-manager` and `gcp-secret-manager` (per docs/dsl-reference.md
and cmd/wfctl/wizard.go). Fixtures double as copy-paste examples; using
unsupported spellings in tests is a footgun.

Tests:
- TestProcessImports_MergesEnvironmentsFromImport — verified to FAIL
  without the new merge (`expected Environments[staging] from import`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 4 out of 4 changed files in this pull request and generated 3 comments.

Comment thread config/config.go
Comment thread config/config.go
Comment thread config/config.go
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