Flows YAML — expression language (parser, evaluator, builtins) #1062

Closed
opened 2026-05-10 15:38:48 +00:00 by claude-desktop · 0 comments
Collaborator

User story

As a flow author, I want a single expression language usable in flow-level if:, per-step if:, and ${{ }} interpolations, so that I can compose conditions like has_label('area:agents') && !author_is('dev') without learning multiple sub-syntaxes.

Acceptance criteria

Parser

  • Hand-rolled recursive-descent parser in apps/server/src/domain/flows-yaml/expr/parser.ts.
  • Operators: &&, ||, !, ==, !=, in, matches, <, <=, >, >=.
  • Literals: string ('…'), number, bool, null.
  • Member access: event.pr.labels, steps.id.outputs.key.
  • Function call: has_label('x').
  • Returns typed AST; parse errors include line + column.

Evaluator

  • evaluate(ast, ctx) returns boolean | string | number | null | array.
  • Type errors at runtime raise ExpressionError with span.
  • Short-circuit && / ||.

Built-in functions

  • has_label(name), has_any_label(...), has_all_labels(...) — read from current event PR/issue labels.
  • author_is(...).
  • repo_matches(glob) — port glob impl from flow-dispatch.ts.
  • comment_matches(regex).
  • event_action().

Built-in variables

  • event.kind, event.action, event.repo, event.pr.*, event.issue.*, event.comment.*.
  • steps.<id>.outputs.<key>.
  • env.<KEY> from flow env: block.

Interpolation

  • ${{ expr }} inside with: arg values is resolved before op runs.
  • Whole-string interpolation returns native type; embedded interpolation stringifies.

Tests

  • Parser round-trip on each operator / literal / call.
  • Evaluator: short-circuit, type errors, missing variable.
  • Each builtin exercised against a synthetic event.

Out of scope

  • CEL adoption (Q1 from spec — hand-rolled chosen for v1).
  • User-defined functions.
  • Lambdas / closures.

References

  • Spec: docs/specs/flows-yaml.md §7.
  • Replaces: matchesFilters() at apps/server/src/domain/flows/flow-dispatch.ts:356–407.
## User story As a flow author, I want a single expression language usable in flow-level `if:`, per-step `if:`, and `${{ }}` interpolations, so that I can compose conditions like `has_label('area:agents') && !author_is('dev')` without learning multiple sub-syntaxes. ## Acceptance criteria ### Parser - [ ] Hand-rolled recursive-descent parser in `apps/server/src/domain/flows-yaml/expr/parser.ts`. - [ ] Operators: `&&`, `||`, `!`, `==`, `!=`, `in`, `matches`, `<`, `<=`, `>`, `>=`. - [ ] Literals: string (`'…'`), number, bool, null. - [ ] Member access: `event.pr.labels`, `steps.id.outputs.key`. - [ ] Function call: `has_label('x')`. - [ ] Returns typed AST; parse errors include line + column. ### Evaluator - [ ] `evaluate(ast, ctx)` returns `boolean | string | number | null | array`. - [ ] Type errors at runtime raise `ExpressionError` with span. - [ ] Short-circuit `&&` / `||`. ### Built-in functions - [ ] `has_label(name)`, `has_any_label(...)`, `has_all_labels(...)` — read from current event PR/issue labels. - [ ] `author_is(...)`. - [ ] `repo_matches(glob)` — port glob impl from `flow-dispatch.ts`. - [ ] `comment_matches(regex)`. - [ ] `event_action()`. ### Built-in variables - [ ] `event.kind`, `event.action`, `event.repo`, `event.pr.*`, `event.issue.*`, `event.comment.*`. - [ ] `steps.<id>.outputs.<key>`. - [ ] `env.<KEY>` from flow `env:` block. ### Interpolation - [ ] `${{ expr }}` inside `with:` arg values is resolved before op runs. - [ ] Whole-string interpolation returns native type; embedded interpolation stringifies. ### Tests - [ ] Parser round-trip on each operator / literal / call. - [ ] Evaluator: short-circuit, type errors, missing variable. - [ ] Each builtin exercised against a synthetic event. ## Out of scope - CEL adoption (Q1 from spec — hand-rolled chosen for v1). - User-defined functions. - Lambdas / closures. ## References - Spec: `docs/specs/flows-yaml.md` §7. - Replaces: `matchesFilters()` at `apps/server/src/domain/flows/flow-dispatch.ts:356–407`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#1062
No description provided.