feat(workflows): compare workflow versions and preview fork sync changes - #8455
mzxchandra wants to merge 15 commits into
Conversation
…verlay The comparison engine now reports which loop and parallel fields changed and which blocks entered or left a container, and exposes the field lists and a shared hasChanges helper. A new overlay module merges two workflow states into one canvas: removed blocks, containers and edges ride along as ghosts at their old positions (nudged clear of live cards), deleted condition and router branches stay on the surviving card so their ghost edge keeps a handle, and every edge is classified by canonical port key. The renderer edge view gains a quiet ghost style for those removed connections.
The preview canvas accepts per-block and per-edge diff status: added and modified cards get a ring and a shared status label, removed cards and containers fade to a ghost, changed sub-block rows and sentence chips tint, and ghost edges sit under live ones so a rewired port shows the new line on top.
Side-by-side view of two workflow states: the overlaid canvas on the left and a change list on the right, sharing selection. Each touched block is a collapsible card with the app's block tile; modified blocks show field rows as folded line diffs with word marks, item-by-item list diffs for tools, conditions, routes and input fields, old to new pairs for scalars, and only the fact for secrets. Added and removed blocks show every field diffed against nothing, with long one-sided bodies capped behind one expander. Fork comparisons group credentials, picked resources and trigger paths into a muted environment bindings section.
The deploy modal offers "View changes" (live against the draft) when a redeploy is pending and a "Compare" action on every version row, opening a full-width comparison whose two sides are pickable from the header. The draft state hook is shared with change detection and only subscribes while something to compare against exists.
The synced deployed workflows list gets a "View changes" action per row. A new internal route and fork use case return the target as its editor holds it and the source deployment re-keyed into the target's block ids through the fork block map (or the derived id the sync would assign), with condition and route ids and variable ids aligned, so the two sides diff block for block like two versions of one workflow.
Comparison: keep basic/advanced mode changes visible, include a tool's permission, server and implementation fields in list diffs, include input field defaults, read checkbox records as records, show whitespace-only edits, cap word and line diffing so a pathological prompt cannot stall the pane, slot deleted branches back at their old position, never reuse a ghost edge id, and mask secret-looking keys at any depth (including key/value table rows) with a name-based fallback when a block definition is unknown. Change list: memoized cards that only re-render when their own selection changes, nested cards for blocks inside an added or removed container, a shared sign map, muted tokens that exist, hover and focus treatment, scroll edge fades, and a shared skeleton for both hosts. Fork preview: the before side is the target draft the sync overwrites, read in one snapshot and scoped to the target workspace; variables and their assignments are re-keyed by unique name; a create reports no target id; the change rows are a discriminated union; the query key sits under the fork diff keys so a sync invalidates it; a direction switch closes the preview.
…ew gaps - Fork "View changes" now loads only the previewed workflow: its deployed state, its identity mapping, its target row and its block pairs, instead of every deployed state in the source workspace plus the full promote plan. The plan item comes from the same buildForkPromotePlanItems decision the promote uses. The route gets a per-user rate limit. - Word marks give up past 64 edits per line pair, so many long rewritten lines cannot stall the tab. - Agent tool params that their block marks as password fields are masked whatever their name. - A list item whose label changed but whose body did not is no longer flagged as a masked value change. - Hoist double casts under their annotations, make sourceWorkflowId optional on the wire for rollout, lazy-load both diff modals, and re-record the settings module baseline: the block registry the diff canvas needs is reached only through the lazy chunk opened on click.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
All reported issues were addressed across 52 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
|
- Sync details load each replaced target's draft and diff it against the projected source, using the same projection and "has changes" rule as the per-workflow preview, so a row and its preview never disagree. Rows the sync would not change read "No changes" and offer no comparison. - Preview buttons wait until the list matches the selected direction. - Mask JSON-encoded secrets in text and scalar fields, and say "A masked value changed" when masking hides the only difference. - Show role changes on agent messages, highlight a router's changed Context on the canvas, and keep ghost edges under live ones into containers. - Added or removed loops and parallels list their iteration settings. - Size ghost containers the way the preview draws them. - "Order changed" only when items moved; CRLF and CR read as line breaks; collapsing unchanged lines closes every fold; selecting a nested block opens its container card; the version pickers have distinct names. - Drop redundant mock resets flagged by check:test-patterns.
|
@cubic review |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
|
@cubic-dev-ai review this PR |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 54 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
- The sync list's per-workflow change check measures the target drafts in one query first and skips itself past the fork state limit, then reads drafts a few at a time and drops each after comparing, instead of holding them all. - Mask keys with a secret word anywhere in them (secretAccessKey, aws_secret_access_key) while credential references stay readable. - Inline diffs show whitespace-only changes. - Opened folds reset when the compared bodies change. - Ghost collision boxes use the canvas's own block measurement.
|
@cubic-dev-ai review this PR |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 55 files
Tip: cubic used a learning from your PR history. Let your coding agent read cubic learnings directly with the cubic MCP.
Fix all with cubic | Re-trigger cubic
…ection per comparison - Agent message bodies go through the same masking as other text fields. - The sync change check's size guard counts block outputs and data, not only sub-blocks. - Picking another version clears the block selection.
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
…comes, not mock calls - Key the comparison view by the picked version pair instead of resetting on state identity, so live draft updates no longer clear the selection. - Agent message changes hidden by masking say so, like other fields. - Fork preview tests assert results, with mocks that answer only the right arguments, instead of asserting mock calls.
|
@cubic-dev-ai review this PR |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 55 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
…ists on both sides
|
@cubic-dev-ai review this PR |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 55 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
|
@cubic-dev-ai review this PR |
@mzxchandra I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 55 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
Summary
Adds a workflow version diff: a read-only canvas that paints added, modified and removed blocks and connections, next to a change list with field-level diffs.
It is used in two places.
How it works:
lib/workflows/comparisongains container (loop/parallel) config changes andbuildWorkflowDiffOverlay, which merges both states into one canvas with removed blocks and edges drawn as ghosts, nudged clear of live blocks.passwordsub-blocks, secret-looking keys at any depth (including JSON-encoded tool params and header tables), and block tools' own password params. A change hidden by masking still shows as "a masked value changed".buildForkPromotePlanItemsdecision the promote makes.Guards against slow input: line diffs cap at 2000 lines, word marks give up past 64 edits per line pair or 4000 characters, and both diff modals are lazy-loaded.
The settings route's module baseline is re-recorded (+355 modules). The growth is the block registry that the diff canvas needs, reached only through the lazy chunk opened on click. The tool registry stays out.
Follow-ups, not in this PR:
Type of Change
Testing
bun run check:api-validation:strict, the module-budget check, biome, and type-check forapps/simandpackages/workflow-rendererall pass.Reviewers: the overlay placement (
lib/workflows/comparison/overlay.ts) and the single-workflow plan resolution (resolveForkPlanItem) deserve the closest look.Checklist
Screenshots/Videos
Screenshots available on request; the deploy modal and fork sync page both open the same comparison view.
🤖 Generated with Claude Code