Skip to content

Fix wfctl docs generate to support ApplicationConfig with embedded workflow files#326

Merged
intel352 merged 5 commits intomainfrom
copilot/compile-complex-yaml-test
Mar 14, 2026
Merged

Fix wfctl docs generate to support ApplicationConfig with embedded workflow files#326
intel352 merged 5 commits intomainfrom
copilot/compile-complex-yaml-test

Conversation

Copy link
Contributor

Copilot AI commented Mar 13, 2026

wfctl docs generate silently produced empty/useless documentation when given an ApplicationConfig (application.workflows[] format), because it used config.LoadFromFile which only handles plain WorkflowConfig — ignoring all referenced sub-files entirely.

Changes

cmd/wfctl/docs.go

  • Use config.NewFileSource(file).Load(ctx) — the same code path the core engine uses — to load and merge the configuration, ensuring docs always reflect exactly what the engine would see
  • For ApplicationConfig inputs, separately extract application.name and the workflow list for the README title and table
  • Use application.name as the doc title (falls back to filename-derived title; -title flag still overrides)
  • Add appInfo *config.ApplicationInfo to docsGenerator; render an Application Workflows table in README.md listing each embedded file and its name
  • Run schema.ValidateConfig (with lenient options: skip type checks, allow empty modules/no entry points) before generating docs so structural issues are surfaced early

config/config.go

  • Added mergeWorkflowSection helper used by MergeApplicationConfig to deep-merge list-bearing fields (routes, subscriptions, producers, definitions) when multiple embedded workflow files define the same top-level workflow key. Nil/non-slice destination values are treated as empty so later files' list items are never silently dropped. Scalar/map keys continue to use first-definition-wins semantics.
  • Fixed MergeApplicationConfig: when a workflow key already exists with a nil value (e.g. http: with no body in YAML), the incoming file's valid value is now used instead of being silently dropped by the unconditional continue

cmd/wfctl/docs_test.go

  • Extracted buildAppConfigFixture helper to eliminate repeated temp-dir setup across ApplicationConfig test cases
  • Fixed docsFullConfig and docsAPIWorkflowConfig fixtures: added secret: "$JWT_SECRET" to auth.jwt modules (required field now caught by the new pre-generation validation)
  • Added tests around a chimera-platform app with two embedded workflow files (api/api.yaml, jobs/application.yaml), verifying:
    • All expected .md files are generated
    • README contains app name as title and lists workflow sources with specific table-row format
    • modules.md includes modules from all sub-files
    • workflows.md covers HTTP routes, messaging topics, and state machine diagrams from all sub-files
    • pipelines.md includes pipelines (and compensation steps) from all sub-files
    • -title flag correctly overrides the application name
  • Added TestDocsApplicationConfigDuplicateWorkflowKey verifying that routes from two workflow files sharing the same workflows.http key are both present (deep merge)
  • Added TestDocsApplicationConfigDuplicateModuleName — duplicate module name across files → error (consistent with engine)
  • Added TestDocsApplicationConfigDuplicatePipelineName — duplicate pipeline name across files → error (consistent with engine)
  • Added TestDocsApplicationConfigNullWorkflowSection — null workflow section in first file → second file's content is used
  • Added TestDocsApplicationConfigThreeFiles — 3-file app (api + events + jobs) with full cross-file content assertions

config/merge_test.go

  • Added unit tests for mergeWorkflowSection edge cases: nil destination list, non-slice destination replacement, and normal append
  • Added TestMergeApplicationConfig_NilWorkflowKeyReplacedByLaterFile — null workflow key in first file is replaced by second file's value
  • Added TestMergeApplicationConfig_DuplicateModuleNameReturnsError and TestMergeApplicationConfig_DuplicatePipelineNameReturnsError — conflict detection unit tests

Example config that now works:

application:
  name: chimera-platform
  workflows:
    - file: ./api/api.yaml
      name: api
    - file: ./facade/facade.yaml
      name: facade
    - file: ./jobs/application.yaml
      name: jobs

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

… tests

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copilot AI changed the title [WIP] Add complex yaml test for documentation generation Fix wfctl docs generate to support ApplicationConfig with embedded workflow files Mar 13, 2026
Copilot AI requested a review from intel352 March 13, 2026 22:56
@intel352 intel352 marked this pull request as ready for review March 13, 2026 22:58
Copilot AI review requested due to automatic review settings March 13, 2026 22:58
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

This PR updates wfctl docs generate to correctly handle multi-workflow ApplicationConfig inputs (where an application.workflows[] list references multiple YAML files), ensuring documentation is generated from the fully loaded configuration rather than silently producing empty output.

Changes:

  • Detect ApplicationConfig by peeking raw YAML bytes and, when present, load + merge referenced workflow files into a single WorkflowConfig.
  • Use application.name as the default docs title (unless -title is provided).
  • Render an “Application Workflows” table in README.md listing each referenced workflow file, and add a suite of tests covering generation and content.

Reviewed changes

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

File Description
cmd/wfctl/docs.go Adds ApplicationConfig detection/merge path, uses application name for title, and emits an Application Workflows table in README.
cmd/wfctl/docs_test.go Adds tests validating docs generation and content for an app config referencing multiple workflow files, including title override behavior.

Comment on lines +80 to +93
var cfg *config.WorkflowConfig
var appInfo *config.ApplicationInfo
if config.IsApplicationConfig(rawData) {
appCfg, appErr := config.LoadApplicationConfig(configFile)
if appErr != nil {
return fmt.Errorf("failed to load application config: %w", appErr)
}
merged, mergeErr := config.MergeApplicationConfig(appCfg)
if mergeErr != nil {
return fmt.Errorf("failed to merge application config: %w", mergeErr)
}
cfg = merged
appInfo = &appCfg.Application
} else {
@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

@github-actions
Copy link

github-actions bot commented Mar 13, 2026

⏱ Benchmark Results

No significant performance regressions detected.

benchstat comparison (baseline → PR)
## benchstat: baseline → PR
baseline-bench.txt:244: parsing iteration count: invalid syntax
baseline-bench.txt:313820: parsing iteration count: invalid syntax
baseline-bench.txt:623853: parsing iteration count: invalid syntax
baseline-bench.txt:909421: parsing iteration count: invalid syntax
baseline-bench.txt:1191908: parsing iteration count: invalid syntax
baseline-bench.txt:1501872: parsing iteration count: invalid syntax
benchmark-results.txt:244: parsing iteration count: invalid syntax
benchmark-results.txt:311474: parsing iteration count: invalid syntax
benchmark-results.txt:621327: parsing iteration count: invalid syntax
benchmark-results.txt:920827: parsing iteration count: invalid syntax
benchmark-results.txt:1223261: parsing iteration count: invalid syntax
benchmark-results.txt:1540059: 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               8.685m ± 65%   8.932m ± 66%       ~ (p=0.937 n=6)
ComponentLoad-4                     3.500m ±  1%   3.472m ±  0%  -0.80% (p=0.009 n=6)
ComponentExecute-4                  1.999µ ±  0%   2.007µ ±  0%       ~ (p=0.061 n=6)
PoolContention/workers-1-4          1.089µ ±  1%   1.095µ ±  1%       ~ (p=0.143 n=6)
PoolContention/workers-2-4          1.088µ ±  2%   1.090µ ±  4%       ~ (p=0.855 n=6)
PoolContention/workers-4-4          1.082µ ±  1%   1.091µ ±  1%  +0.79% (p=0.039 n=6)
PoolContention/workers-8-4          1.092µ ±  1%   1.100µ ±  1%       ~ (p=0.093 n=6)
PoolContention/workers-16-4         1.094µ ±  1%   1.098µ ±  2%       ~ (p=0.197 n=6)
ComponentLifecycle-4                3.525m ±  1%   3.505m ±  1%       ~ (p=0.394 n=6)
SourceValidation-4                  2.301µ ±  1%   2.307µ ±  1%       ~ (p=0.589 n=6)
RegistryConcurrent-4                769.2n ±  4%   797.5n ±  6%       ~ (p=0.180 n=6)
LoaderLoadFromString-4              3.548m ±  1%   3.529m ±  1%       ~ (p=0.310 n=6)
geomean                             18.88µ         19.01µ        +0.65%

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

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

pkg: github.com/GoCodeAlone/workflow/middleware
                                  │ baseline-bench.txt │       benchmark-results.txt       │
                                  │       sec/op       │   sec/op     vs base              │
CircuitBreakerDetection-4                  289.7n ± 8%   290.8n ± 5%       ~ (p=0.667 n=6)
CircuitBreakerExecution_Success-4          22.51n ± 1%   22.61n ± 2%       ~ (p=0.258 n=6)
CircuitBreakerExecution_Failure-4          64.52n ± 0%   65.46n ± 1%  +1.46% (p=0.002 n=6)
geomean                                    74.93n        75.51n       +0.77%

                                  │ 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                     900.4n ± 27%   855.8n ± 28%  -4.95% (p=0.026 n=6)
JQTransform_ObjectConstruction-4         1.449µ ±  1%   1.434µ ±  1%  -1.04% (p=0.009 n=6)
JQTransform_ArraySelect-4                3.376µ ±  1%   3.292µ ±  1%  -2.50% (p=0.002 n=6)
JQTransform_Complex-4                    37.95µ ±  1%   37.81µ ±  0%  -0.37% (p=0.026 n=6)
JQTransform_Throughput-4                 1.783µ ±  1%   1.754µ ±  1%  -1.63% (p=0.002 n=6)
SSEPublishDelivery-4                     65.70n ±  1%   66.07n ±  1%       ~ (p=0.145 n=6)
geomean                                  1.642µ         1.614µ        -1.67%

                                 │ 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.120µ ±  3%   1.165µ ±  2%   +4.06% (p=0.009 n=6)
SchemaValidation_AllFields-4                1.694µ ± 11%   1.710µ ±  7%        ~ (p=0.485 n=6)
SchemaValidation_FormatValidation-4         1.597µ ±  2%   1.634µ ±  1%   +2.25% (p=0.011 n=6)
SchemaValidation_ManySchemas-4              1.826µ ±  2%   2.431µ ± 23%  +33.14% (p=0.002 n=6)
geomean                                     1.534µ         1.677µ         +9.35%

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

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

pkg: github.com/GoCodeAlone/workflow/store
                                   │ baseline-bench.txt │       benchmark-results.txt        │
                                   │       sec/op       │    sec/op     vs base              │
EventStoreAppend_InMemory-4                1.215µ ± 29%   1.189µ ± 15%       ~ (p=0.732 n=6)
EventStoreAppend_SQLite-4                  1.363m ±  5%   1.332m ±  5%       ~ (p=0.093 n=6)
GetTimeline_InMemory/events-10-4           14.15µ ±  5%   14.03µ ±  1%       ~ (p=0.394 n=6)
GetTimeline_InMemory/events-50-4           79.25µ ±  3%   78.00µ ±  2%  -1.58% (p=0.041 n=6)
GetTimeline_InMemory/events-100-4          124.1µ ±  1%   133.1µ ± 22%  +7.20% (p=0.009 n=6)
GetTimeline_InMemory/events-500-4          639.0µ ±  0%   643.2µ ±  0%  +0.66% (p=0.002 n=6)
GetTimeline_InMemory/events-1000-4         1.308m ±  2%   1.313m ±  1%       ~ (p=0.394 n=6)
GetTimeline_SQLite/events-10-4             109.0µ ±  1%   110.3µ ±  0%  +1.17% (p=0.002 n=6)
GetTimeline_SQLite/events-50-4             250.8µ ±  1%   252.2µ ±  0%  +0.54% (p=0.009 n=6)
GetTimeline_SQLite/events-100-4            427.1µ ±  1%   424.6µ ±  0%       ~ (p=0.132 n=6)
GetTimeline_SQLite/events-500-4            1.808m ±  0%   1.796m ±  1%  -0.65% (p=0.004 n=6)
GetTimeline_SQLite/events-1000-4           3.511m ±  1%   3.494m ±  0%  -0.49% (p=0.026 n=6)
geomean                                    221.9µ         222.1µ        +0.09%

                                   │ baseline-bench.txt │         benchmark-results.txt         │
                                   │        B/op        │     B/op       vs base                │
EventStoreAppend_InMemory-4                  798.5 ± 7%     747.0 ± 13%       ~ (p=0.407 n=6)
EventStoreAppend_SQLite-4                  1.986Ki ± 2%   1.984Ki ±  2%       ~ (p=0.855 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=1.000 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=1.000 n=6)
geomean                                    67.41Ki        67.03Ki        -0.56%
¹ 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.

…extract test helper

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Done in commit 46b4deb. Both review points addressed:

  1. Deep merge for duplicate workflow keys — added mergeWorkflowSection in config/config.go that appends list-bearing fields (routes, subscriptions, producers, definitions) when multiple embedded files define the same top-level workflow key (e.g. two files both have workflows.http). Scalar keys still use first-definition-wins. Covered by the new TestDocsApplicationConfigDuplicateWorkflowKey test.

  2. Test fixture deduplication — extracted buildAppConfigFixture helper that sets up the chimera-platform temp dir tree and returns appCfgPath/outDir, eliminating the repeated 15-line setup block across the five main ApplicationConfig test functions.

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

This PR fixes wfctl docs generate so it can correctly generate documentation from an ApplicationConfig (with application.workflows[]), by loading and merging all referenced workflow files into a single WorkflowConfig.

Changes:

  • Detect ApplicationConfig inputs and merge embedded workflow configs before generating docs.
  • Enhance README generation to use application.name as the default title and list embedded workflow sources.
  • Add deep-merge behavior for specific list-bearing workflow sections when multiple workflow files contribute to the same workflow key, plus new tests covering these scenarios.

Reviewed changes

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

File Description
cmd/wfctl/docs.go Detects ApplicationConfig, merges referenced workflow files, uses app name as default title, and renders an “Application Workflows” table in README.
config/config.go Adds mergeWorkflowSection and uses it during MergeApplicationConfig to append specific list fields when workflow keys collide.
cmd/wfctl/docs_test.go Adds ApplicationConfig fixtures/tests validating merged output and duplicate-workflow-key deep-merge behavior.

@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

… test assertions

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Done in commit 8532ce3. Both issues addressed:

  1. mergeWorkflowSection nil/non-slice fix — replaced the simple type-assertion with a switch on dst[k]: []any appends as before; nil and any non-slice value both take the src list (so an explicit routes: null in an earlier file no longer blocks later files' routes). Covered by three new tests: TestMergeWorkflowSection_NilDestinationList, TestMergeWorkflowSection_NonSliceDestinationReplaced, TestMergeWorkflowSection_AppendsToExistingList.

  2. README assertions tightened — replaced strings.Contains(content, "api") / "jobs" with checks for the exact table row format | \api` |and| `./api/api.yaml` |`, verifying both the name column and the file column render correctly.

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

Updates wfctl docs generate to properly handle ApplicationConfig (multi-file) inputs by loading and merging all referenced workflow files, and extends generated README content to show embedded workflow sources.

Changes:

  • Detect ApplicationConfig inputs and generate docs from a merged WorkflowConfig spanning all referenced files.
  • Deep-merge specific list-bearing workflow subsections (routes, subscriptions, producers, definitions) when multiple embedded workflow files share the same top-level workflow key.
  • Add/expand test coverage for ApplicationConfig doc generation and duplicate workflow-key merging behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
cmd/wfctl/docs.go Detect ApplicationConfig, merge embedded workflow files, use application.name as title fallback, and render an “Application Workflows” table in README.
config/config.go Add mergeWorkflowSection and use it to append list-bearing fields when merging duplicate workflow keys across files.
cmd/wfctl/docs_test.go Add ApplicationConfig fixtures and tests validating merged documentation output and title override behavior.
config/merge_test.go Add focused unit tests for mergeWorkflowSection list-merge edge cases (nil/non-slice dst, append).

Comment on lines +382 to +390
if existing, exists := combined.Workflows[k]; exists {
dstMap, dstOk := existing.(map[string]any)
srcMap, srcOk := v.(map[string]any)
if dstOk && srcOk {
mergeWorkflowSection(dstMap, srcMap)
continue
}
// Cannot deep-merge (not both maps) — first definition wins.
continue
@intel352
Copy link
Contributor

@copilot apply changes based on the comments in this thread

When generating documentation, the docs should reflect how the engine sees/interprets the yaml. So if the engine doesn't merge pipelines that are named the same, neither should the docs. We should ensure some consistency.
Make sure we're not overriding how the engine loads/manages multi-file configs, we should be following the same logic, ideally using the same code path for interpreting configs as the core engine already does.

Add more tests with more complex scenarios and validate the outcomes are as expected. Maybe as part of docs generation, start with config validation before generating docs so if there's any unsupported scenario, we capture it early.

…in docs; add validation + complex tests

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 14, 2026

@copilot apply changes based on the comments in this thread

When generating documentation, the doc...

Done in commit 86efa1a. Here's a summary of what changed:

  1. config/config.go — nil existing workflow key: When combined.Workflows[k] is present but nil (e.g. http: with no body in YAML), the type assertion was failing and the unconditional continue was silently dropping the later file's value. Added an explicit if existing == nil guard so the incoming value is used. Unit tests added in config/merge_test.go.

  2. cmd/wfctl/docs.go — same code path as engine: Config loading now uses config.NewFileSource(configFile).Load(ctx) — the identical code path the engine uses. Application metadata (name, workflow list) is extracted separately only when needed for the README table. Docs generation now mirrors exactly what the engine would see.

  3. Schema validation before docs generation: Added schema.ValidateConfig call (with lenient options: skip type checks, allow no entry points, allow empty modules) between loading and generating. This catches structural issues like missing required config fields early. As a result, the auth.jwt module fixtures had secret: "$JWT_SECRET" added since that field is required.

  4. More complex tests:

    • Duplicate module name → error (consistent with engine)
    • Duplicate pipeline name → error (consistent with engine)
    • Null workflow section in first file → second file's content used
    • 3-file app (api + events + jobs) with full cross-file content assertions

@intel352 intel352 merged commit 7a65020 into main Mar 14, 2026
15 checks passed
@intel352 intel352 deleted the copilot/compile-complex-yaml-test branch March 14, 2026 00:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants