Skip to content

fix(cloud): reject malformed executionOrder instead of silently runni… - #117

Merged
finalerock44 merged 1 commit into
devfrom
fix/110-workspace-config-validation
Aug 13, 2026
Merged

fix(cloud): reject malformed executionOrder instead of silently runni…#117
finalerock44 merged 1 commit into
devfrom
fix/110-workspace-config-validation

Conversation

@finalerock44

@finalerock44 finalerock44 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

…ng in parallel

A workspace config.yaml was yaml.load'ed and straight-cast to IWorkspaceConfig, so a wrong-shaped executionOrder was never checked. The intuitive bare-list form made executionOrder an Array, .flowsOrder came back undefined, resolveSequentialFlows returned [], and every flow ran in parallel - same cost, wrong semantics, green run. The only symptom was depends_on being null on every result row.

Add a zod schema as the single source of truth for the config shape (src/services/workspace-config.schema.ts) and route all three former cast sites through one validated loader, loadWorkspaceConfig:

  • A malformed executionOrder is now fatal (exit 1), with a message showing what was found next to the expected shape. A bare list is not valid Maestro either, so there is nothing to accept - and a warning in CI logs is exactly what got missed.
  • Unrecognised top-level keys warn (and are preserved, since the config is forwarded to the API as fields.workspaceConfig), catching flowOrder, a top-level continueOnFailure, tags in place of includeTags, and flowTimeout.
  • executionOrder on a single-file input warns instead of being dropped: planSingleFile never sequences, so it was silently ignored even when well-formed.
  • continueOnFailure's real default (true) now lives in the schema instead of being re-specified at three read sites.
  • WORKSPACE_CONFIG_KEYS is derived from the schema so isWorkspaceConfigFile's detection set can no longer drift from it.
  • includeTags/excludeTags scalar coercion moves from readYamlFileAsJson into the schema, so the loader is a plain YAML read and the validator is pure.

Warnings go through an injected callback: cloud.ts passes logger.warn (stderr, so it survives --json), the MCP tool passes logStderr since its stdout is the JSON-RPC channel.

Also fixes two test fixtures that used a tags: key the CLI never read.

Verified on dev: the bare-list form now exits 1 before anything is submitted, and a well-formed executionOrder chains depends_on null -> 36962 -> 36963 across results 36962-36964.

Fixes #110

What & why

Type of change

  • fix — bug fix
  • feat — new feature
  • perf — performance improvement
  • refactor — code change that's neither a fix nor a feature
  • docs — documentation only
  • chore / ci / build / test — tooling, no user-facing change
  • Breaking change (title has ! or PR notes a BREAKING CHANGE:)

Checklist

  • PR title follows the Conventional Commits format (see comment above)
  • pnpm lint passes
  • pnpm typecheck passes
  • pnpm build passes
  • I have not bumped the version or edited CHANGELOG.md (release-please handles this)
  • I have signed the CLA (the bot will prompt on first contribution)
  • Docs / README.md / STYLE_GUIDE.md updated if behaviour or output changed

How to test


View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

…ng in parallel

A workspace config.yaml was yaml.load'ed and straight-cast to
IWorkspaceConfig, so a wrong-shaped executionOrder was never checked. The
intuitive bare-list form made executionOrder an Array, .flowsOrder came back
undefined, resolveSequentialFlows returned [], and every flow ran in
parallel - same cost, wrong semantics, green run. The only symptom was
depends_on being null on every result row.

Add a zod schema as the single source of truth for the config shape
(src/services/workspace-config.schema.ts) and route all three former cast
sites through one validated loader, loadWorkspaceConfig:

- A malformed executionOrder is now fatal (exit 1), with a message showing
  what was found next to the expected shape. A bare list is not valid
  Maestro either, so there is nothing to accept - and a warning in CI logs
  is exactly what got missed.
- Unrecognised top-level keys warn (and are preserved, since the config is
  forwarded to the API as fields.workspaceConfig), catching flowOrder, a
  top-level continueOnFailure, tags in place of includeTags, and flowTimeout.
- executionOrder on a single-file input warns instead of being dropped:
  planSingleFile never sequences, so it was silently ignored even when
  well-formed.
- continueOnFailure's real default (true) now lives in the schema instead of
  being re-specified at three read sites.
- WORKSPACE_CONFIG_KEYS is derived from the schema so isWorkspaceConfigFile's
  detection set can no longer drift from it.
- includeTags/excludeTags scalar coercion moves from readYamlFileAsJson into
  the schema, so the loader is a plain YAML read and the validator is pure.

Warnings go through an injected callback: cloud.ts passes logger.warn (stderr,
so it survives --json), the MCP tool passes logStderr since its stdout is the
JSON-RPC channel.

Also fixes two test fixtures that used a tags: key the CLI never read.

Verified on dev: the bare-list form now exits 1 before anything is submitted,
and a well-formed executionOrder chains depends_on null -> 36962 -> 36963
across results 36962-36964.

Fixes #110
@finalerock44 finalerock44 self-assigned this Aug 13, 2026
@finalerock44
finalerock44 merged commit 1973a42 into dev Aug 13, 2026
11 checks passed
@finalerock44
finalerock44 deleted the fix/110-workspace-config-validation branch August 13, 2026 09:51
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.

Malformed executionOrder is silently ignored — sequential flows run in parallel with no warning

1 participant