feat(workflows): compare deployment versions across UI and API
Compare deployment versions of the same workflow from a version row’s Compare action. The default is the previous saved version → selected version, or v1 → Draft when no predecessor exists. The modal supports saved versions and the current draft, with a read-only canvas and field changes beside it. Blocks match by stable IDs; this does not compare separate workflows or add a fork-sync preview.
The UI, redeployment checks, and authorized comparison operation share the canonical comparison engine. Action editor defaults remain distinct from unset values; only actual trigger deployment defaults are normalized. Structured values preserve array order, scalar types, explicit JSON nulls, and arbitrary own keys. Preview and API snapshots use the same runtime materialization while archive reads retain the original stored payload.
Canvas ports are resolved from each original snapshot. Removed connections keep their exact ports, including deleted branches, without changing live branch order or labels. Ghost layout uses the preview’s dimensions and each container’s coordinate space. Preview edges read only their own snapshot state.
A presentation registry covers every declared subblock type. It reuses column definitions, option labels, canonical value shaping, and branch-role helpers without mounting live editors. Actual tables render their declared columns; messages, tools, mappings, schemas, and files render labeled values. Array order and duplicate entries remain significant; object maps use stable key order. Masking happens after comparison so secret-only edits stay visible. Code and explicit JSON editor values keep text diffs.
The interface uses EMCN components, typography, and semantic colors. Comparison stays in the version action menu, and section titles have no counts. Connection changes include exact source/target identities and ports in the API, with branch labels in the UI.
The platform surface is GET /api/v2/workflows/{workflowId}/versions/compare?base=1&target=2, exposed through the generated CLI command sim workflows versions compare <workflowId> --base 1 --target 2, the MCP catalog, and Mothership’s shared CLI surface. The operation loads at most two snapshots from one workflow, checks a 16 MiB input budget before materialization, and caps the redacted result at 16 MiB. Credential-only changes remain detectable without disclosing their values. Native SDK convenience methods are not added.
Validation:
- 675 focused tests pass across comparison, canonicalization, overlays, field rendering, text diffs, and trigger resolution. Regression failures were demonstrated before their fixes.
- 10 integration checks pass against disposable PostgreSQL, including execution-default behavior through the real serializer/provider request, snapshot migration parity, arbitrary JSON keys, access boundaries, direction, exact ports, and input/result size admission.
- Real HTTP acceptance passes for the session UI contract, v2 endpoint, generated CLI, and generated MCP operation, including authentication/authorization/validation errors, secret redaction, and unchanged archived snapshots. This suite is wired into CI with a JSON failure report.
- Real-browser checks pass for both default selections, deleted condition/router rows, nested JSON changes, root/nested ghost collisions, and input/output/error/container port transitions. DOM measurements confirm no overlap, nested containment, and all expected edge paths. The structured presentation pass additionally verifies headers, duplicate keys, custom columns, secret-only edits, repeated messages/tools, input/response schemas, and same-name files. Reversing a comparison swaps added/removed rows correctly. Screenshots and reports were retained locally.
- Application, CLI, workflow-renderer, and workflow-types type checks pass; lint and all 54 repository audits pass.
Merged current staging, retained its shared CI report directory and readiness handling, and regenerated the conflicting registry-boundary baseline; the executable tool registry remains outside client graphs.