Skip to content

workflow: a Condition link with a blank or malformed output name saves as an unconditional Link #2155

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 51b59b987fa04ba2826cdffe436e8997
Branch: dev/3-402-condition-when
Base: c4dc6e4
At: 2026-11-02T11:48:45Z

A Condition link that cannot be honoured is saved as if it were a plain Link, silently. In Settings → Workflows, choose Condition and leave the output name blank, or type one the graph will not accept (result_ok, ready.now, with space): Add link succeeds, no error appears, and what lands on disk is an unconditional edge. At run time that branch is then taken whether or not the condition holds — the opposite of what the user asked for.

The canvas gives no second chance to notice: only loop edges are drawn dashed (plugins/workflow/client.js:1294); a Condition link is drawn exactly like a plain one, and the Settings canvas never shows the kind.

Reproduction (live main c4dc6e490c621d2d3681da4d808689b171c37efa)

Deterministic, through the real normalizeGraph, on the base commit (node /home/xuke/.dsh/bot/workspace/tmp/probe2152/condition.mjs):

good condition : [{"from":"a","to":"b","when":{"output":"ready","eq":"yes"}}]
blank output   : [{"from":"a","to":"b"}]
underscored    : [{"from":"b","to":"c"}]

So {when: {output: ''}} and {when: {output: 'result_ok'}} both come back as a Link with no when at all, and the save reports success.

Cause

  • plugins/workflow/src/graph.js:106-114 — cleanWhen returns null when the value is not an object, when output is empty, or when it does not match PORT_RE (:51 /^[a-z][a-z0-9-]{0,40}$/).
  • :141-143 — cleanEdge reads const when = cleanWhen(edge.when, true) and then spreads ...(when ? { when } : {}): a rejected when is indistinguishable from an absent one.
  • :562-564 — the run loop does if (!edge.when) take = true, so the downgraded edge fires unconditionally.
  • plugins/workflow/client.js:1602 — the form sends edge.when = edgeForm.until verbatim, including {output: '', eq: ''}; :1761 disables Add link only when from/to are empty.

Expected

A when that is present but unusable is a refusal, not a downgrade: the save fails and says why (e.g. A condition link needs an output name like "ready"), through the existing save() → wf-err channel. The same rule applies to a loop's until: absent is fine (no early stop), but a partially filled one is refused rather than dropped. Legal values are preserved exactly as they are today.

Scope

  • plugins/workflow/src/graph.js — cleanEdge (and the caller that reports the error) rejects an edge whose when/until is present but invalid instead of dropping it; no change to what a valid when/until means, no change to ranking or the run loop.
  • plugins/workflow/client.js — Condition requires a non-empty output name before Add link is enabled, and a host refusal surfaces in wf-err; read-only canvas keeps committing nothing.
  • plugins/workflow/test/*.test.js — the cases below.

plugins/** is not in patches/ds-bot/files/ and not in any patches/ds-bot/000*.patch (checked against the base tree: the patch scheme mirrors core/ and .github/scripts/), so no generate and no bundle rebuild is involved.

Acceptance (red first, on the base commit)

  1. {when: {output: ''}} → a save error, and nothing is written (today: ok, edge without when).
  2. {when: {output: 'result_ok'}} → a save error (same today-gap).
  3. {when: {output: 'ready', eq: 'yes'}} → kept exactly.
  4. A loop sending {until: {output: ''}} → a save error; a loop with no until still saves.
  5. Existing graphs read back unchanged: templates, deliveryWorkflow, weeklyWorkflow and their condition edges stay green.
  6. Read-only canvas: a click still commits nothing.

Dedupe

Searched live main and the issue log before opening: condition link (25 hits), edge condition workflow (15), unconditional edge (6), titles unconditional / edge condition / when output — nothing covers a malformed when being dropped silently. Backlog P-79 is "an onFail edge cannot be added", P-32 is "the inspector cannot change outputs"; both are different surfaces. The suggestion to turn the free-text output name into a dropdown of the source node's outputs belongs to this same form, so it is not filed separately.

Activity

  1. HugoXK commented on Nov 2, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 51b59b987fa04ba2826cdffe436e8997
    Reason: merged as 41ff819 at 2026-11-02T12:57:33Z
    At: 2026-11-02T12:58:59Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions