Skip to content

feat(workflow): data-reference chips (upstream-scoped, byte-safe)#2604

Open
simlarsen wants to merge 1 commit into
feat/workflow-guided-canvasfrom
feat/workflow-data-chips
Open

feat(workflow): data-reference chips (upstream-scoped, byte-safe)#2604
simlarsen wants to merge 1 commit into
feat/workflow-guided-canvasfrom
feat/workflow-data-chips

Conversation

@simlarsen

Copy link
Copy Markdown
Contributor

The last and highest-risk piece of the workflow-builder simplification: turning the raw {{…}} data references into chips. Stacked on #2602 (GitHub retargets to master once the stack merges).

The problem

Referencing another step's output meant opening a full-screen modal that string-appended a raw token like {{local.components.slack-1.returnValues.error}} into the field — which you then had to read and manage by hand. No readability, no removal, no validation that the referenced step actually runs before this one.

What changed

For text arguments, the footer is now an inline data-reference helper:

  • Existing tokens render as friendly, removable chipsError · from Send to Slack — that turn red when the referenced step or output no longer exists.
  • Insert from an inline menu scoped to upstream steps (with a "Show all steps" escape), so you can't reference data that won't exist at runtime.
  • Variables still use the existing VariableModal.

Why this design (and not contentEditable)

The stored value stays a plain string — tokens are appended / index-spliced — so a workflow round-trips byte-for-byte and the executor is untouched. A rich contentEditable chip editor was deliberately avoided: it risks corrupting saved workflows and, critically, jsdom can't test it, so it couldn't be verified with the component-testing approach used here. Keeping the string as the source of truth makes all the logic pure and fully testable.

  • Only text-like fields get the chip footer; JSON editors keep the existing modal picker.
  • The set of fields that show a data-reference footer is unchanged — only the footer's UI changed.
  • buildComponentToken / variable tokens are byte-identical to what ComponentValuePickerModal / VariableModal already produce.

Verification

  • TokenUtils.test.ts (14): parse/classify/describe/append + byte-exact removeTokenOccurrence (correct occurrence among adjacent duplicates, surrounding text preserved, out-of-range no-op).
  • DataReferenceInput.test.tsx (4, RTL/jsdom): chips render, broken-reference flagged, precise removal, upstream-scoped insert + show-all.
  • 48 workflow tests pass; tsc ✓, eslint ✓.
  • Adversarial review pass (dedicated agent) traced the full value-sync chain (setFieldValue → onChange → setComponent → props.fields → useAsyncEffect), confirmed the token formats byte-for-byte against the pickers and executor, and found no real bug or regression — only two theoretical nits that match pre-existing behavior.

🤖 Generated with Claude Code

…upstream-scoped insert

Referencing another step's output used to mean picking from a full-screen
modal that string-appended a raw {{local.components.…}} token you then had to
read and manage by hand. This adds an inline chip experience for text
arguments:

- Existing tokens in the value render as friendly, removable chips
  ("Error · from Send to Slack"), turning red when the referenced step or
  output no longer exists.
- A new reference is inserted from an inline menu scoped to UPSTREAM steps
  (with a "Show all steps" escape), so you can't reference data that won't
  exist at runtime.
- Variables still use the existing VariableModal.

Deliberately NOT a contentEditable editor: the stored value stays a plain
string (tokens are appended / index-spliced), so a workflow round-trips
byte-for-byte and the executor is untouched. This also makes it fully
unit-testable — the parse/describe/append/remove logic is pure (TokenUtils),
and the token format is byte-identical to what ComponentValuePickerModal /
VariableModal already produce.

Only text-like fields get the chip footer; JSON editors keep the existing
modal-based picker. The set of fields that show a data-reference footer is
unchanged from before — only the footer's UI changed.

Tests: TokenUtils (14 pure-logic cases incl. byte-exact occurrence removal and
adjacent-duplicate disambiguation) + DataReferenceInput (4 RTL cases: chip
render, broken-reference flag, precise removal, upstream-scoped insert +
show-all). 48 workflow tests pass; tsc + eslint clean. Verified byte-safety
and the value-sync chain via an adversarial review pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@simlarsen

Copy link
Copy Markdown
Contributor Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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.

2 participants