式は、flow の配線レイヤーです。YAMLフィールドがデータを受け取る場所、つまり手順の with バインディング、when ガード、foreach のソース、flow の output では、${{ … }} の中に式を書きます。同じ配線は SDK でも、コンテキストオブジェクト c 上の型付きバインディングクロージャ((c) => …)として存在します。どちらのサーフェスも、FlowSpec 内の同一の式テキストへ変換されます。 これは意図的に、汎用プログラミング言語ではありません(信条: 「言語ではない」)。固定された文法を持つ、小さく純粋で副作用のないサブ言語です。文法は、閉じた参照、演算子、組み込み関数の集合で構成されます。この小ささこそが要点です。静的チェッカーが、式の中に時計の読み取り、乱数の取得、ネットワーク呼び出しが隠れていないことを証明できるのは、そのためです。

式が現れる場所

このサイト全体で使う実行例は、daily-price-watch flow です。ブラウザーTool search-price@1.2.0 が価格を読み取り、純粋な transform がそれを整形し、価格が下がったときに条件付きの http.post がシークレットの webhook に通知します。以下の配線はすべて、その flow から来ています。
steps:
  - id: lookup
    use: browser:search-price@1.2.0
    with: { query: ${{ input.query }} }

  - id: shape
    use: transform
    with:
      expr: { price: ${{ steps.lookup.output }}, at: ${{ now }} }

  - id: notify
    use: http.post
    when: ${{ steps.shape.output.price < input.threshold }}
    with:
      url:  ${{ secret.webhook }}
      json: ${{ steps.shape.output }}

output: { price: ${{ steps.shape.output.price }} }
flow("daily-price-watch")
  .step("lookup", browser.tool("search-price@1.2.0").with((c) => ({ query: c.input.query })))
  .step("shape", transform((c) => ({ price: c.steps.lookup.output, at: c.now })))
  .branch("dropped?", (c) => c.steps.shape.output.price < c.input.threshold, {
    yes: (f) => f.step("notify", http.post((c) => c.secret.webhook, (c) => c.steps.shape.output)),
    no:  (f) => f.noop("unchanged"),
  })
  .output((c) => ({ price: c.steps.shape.output.price }));
SDKでは、参照は注入されたコンテキスト c のプロパティ(c.input.queryc.steps.lookup.outputc.now)です。YAMLでは ${{ … }} の中の裸のパスです。これらは1つの真実、つまり同じ FlowSpec の2つの表現です。

参照

参照は、ランタイムがすでに保持している値を読み取ります。参照の集合は閉じています。式が名前にできるルートはこれらだけです。それ以外へのパスはコンパイル時に拒否されます。
参照解決先補足
input.<name>検証済みの flow 入力入力で宣言されたスキーマによって型付けされます。
steps.<id>.output完了した手順の出力そのToolの ToolContract 出力スキーマから型付けされます。
steps.<id>.output.<path>手順出力内のフィールド例: steps.shape.output.price
env.<KEY>許可リストに含まれる環境値env capability が必要です。文字列です。
secret.<KEY>シークレットの参照(値ではない)不透明です。taint rule を参照してください。
now注入された時計認められている唯一の時刻ソースです。リプレイ用に記録されます。
run.id現在の Run idこの FlowSpec を実行している Run の id です。
shape 手順の中で steps.lookup.output を参照することは、コンパイラーが依存エッジ lookup → shape を推論する方法でもあります。needs を手で書くことはほとんどありません。参照は、コンパイラーがグラフを構築するために読むものです。参照がどのようにエッジになるかは FlowSpec を参照してください。
now は関数呼び出しではなく、参照です。now()today() もありません。時刻は、この注入され記録された値を通じてのみ flow に入るため、リプレイ時にも決定論的です。

演算子

演算子の集合は固定されています。演算子は参照、リテラル、関数結果を組み合わせますが、式の外側には決して到達しません。
カテゴリー演算子意味
算術+ - * / %数値演算です。+ は文字列の連結も行います。
比較== != < <= > >=2つの値を比較し、boolean を返します。
論理&& || !boolean の論理積、論理和、否定です。
三項a ? b : ca が truthy なら b、そうでなければ c を選びます。
Nullish??左オペランドが null/undefined でなければ左、そうでなければ右です。
メンバーアクセス. []名前(.price)またはキー/インデックス(["price"])でフィールドを読み取ります。
notify 手順の when ガードは、比較とメンバーアクセスを組み合わせて使います。
${{ steps.shape.output.price < input.threshold }}
欠けている可能性のある値にフォールバックを与えるには ?? を使い、小さなインラインの選択には三項演算子を使います。
${{ steps.lookup.output.currency ?? "USD" }}
${{ steps.shape.output.price < input.threshold ? "drop" : "steady" }}

組み込み関数

組み込み関数には、固定され監査済みの集合が1つだけあります。合計で16個です。それ以外は存在せず、自分で定義することもできません。すべての組み込み関数は純粋です。時計を読まず、乱数を取得せず、I/Oを実行しません。純粋性があるからこそ、静的チェッカーは式が非決定性を隠していないことを証明できます。 実行例にはいくつか登場します。len は結果配列の長さを測り、default は欠けている currency にフォールバックし、map/filter はそれを再整形し、contains はタイトルを確認します。
${{ len(steps.lookup.output.results) }}
${{ default(steps.lookup.output.currency, "USD") }}
${{ map(steps.lookup.output.results, item.price) }}
${{ contains(lower(steps.lookup.output.title), "sale") }}
完全な集合、つまり文字列ヘルパー(loweruppertrimsplitjoinreplaceto_number)、リストヘルパー(slicenthunique)、その他は、各関数のシグネチャ、引数、結果とともに専用のリファレンスページで説明されています。

組み込み関数リファレンス

16個すべての組み込み関数(lenloweruppertrimjsondefaultmapfiltercontainssplitjoinreplaceslicenthto_numberunique)を、完全なシグネチャと例付きで説明します。
設計上、uuid()now()random()fetch()、その他の非純粋な組み込み関数はありません。非決定性は式の中からは決して発生しません。非決定性は記録されたソースから来ます。時刻なら now 参照、新しい識別子なら std/controluuid のようなToolです(seed された random capability から取得し、その出力を Journal に記録します)。記録された値がリプレイされるため、結果は安定します。だからこそ式レイヤーは純粋なままであり、uuid() 組み込み関数は存在しません。Tools と Packs を参照してください。

リテラル

式にはリテラル値を直接含めることができます。リテラルの集合は JSON に対応しています。
種類
文字列"USD", "drop"
数値42, 3.14, -1
Booleantrue, false
Nullnull
配列[1, 2, 3]
オブジェクト{ price: 10, currency: "USD" }
output 投影と shape 手順はどちらも、フィールド自体が式であるオブジェクトリテラルを構築します。オブジェクトリテラルは、単一の正規化された式として FlowSpec に保持されます。

ユーザー定義関数なし、ループなし

文法は、汎用言語が始まる手前で止まります。
  • ユーザー定義関数はありません。 上記の組み込み集合が語彙のすべてです。新しい callable を宣言する方法はありません。
  • ループはありません。 反復は手順レベルの構造であり、式の構造ではありません。配列を処理するには、手順で foreach を使います。これは各要素に対して手順を fan out し、出力を集めます。foreach 手順の内部では、現在の要素とそのインデックスを参照として使えます(SDKでは itemindex)。制御フロー を参照してください。
mapfilter 組み込み関数は、小さく純粋な、式内での値の再整形のために存在します。foreach の代わりではありません。各要素がそれぞれ独自の手順(効果を持つTool呼び出し、リトライポリシー、タイムアウト)を必要とする場合、適切な手段は foreach です。

式のチェック方法

すべての式はコンパイル時に処理され、実行時にその場で解釈されることはありません。
1

ASTへパースする

${{ … }} の中のテキストは抽象構文木へパースされます。スキーマ内で Expr は brand されているため、コンパイラーは生の文字列と、パース済みで検証済みの式を区別できます。
2

参照先スキーマに対して型チェックする

各参照は、それが指すスキーマ、つまり入力の宣言型や手順の ToolContract 出力スキーマに対して解決されます。そして、演算子と関数が型互換性についてチェックされます。形状の不一致や入力ミスは、ランタイムの意外な挙動ではなくコンパイルエラーです。
3

未宣言の参照と集合外の関数を拒否する

式が宣言されていない参照(未知の入力、未知の手順、env capability のない env キー)に触れたり、固定された組み込み集合の外にある関数を呼び出したりした場合、その式は拒否されます。式の中に時計の読み取りやネットワーク呼び出しを隠す場所はありません。
この静的な契約が、再現性を誠実に保ちます。コンパイルできる flow とは、すべての配線が型付けされ、境界付けられ、純粋であることを証明できる flow です。完全な検証パイプラインと診断については、FlowSpec を参照してください。

式内のシークレット

secret.<KEY> 参照は不透明です。これはシークレットへの参照を運ぶだけで、値は運びません。シークレットの taint rule は、まさにこの式レイヤーで静的に強制されます。 secret. 参照は、全体が変換されないまま、Tool入力の引数としてのみバインドできます。そして、そのToolが secrets capability を宣言している場合にのみ許可されます。演算子、メンバーアクセス、組み込み関数を通過することはできません。仕様どおり、以下はそれぞれ secret_leak 診断で拒否されます。
${{ secret.webhook + "/notify" }}    # secret に対する演算子
${{ json(secret.webhook) }}          # secret に対する組み込み関数
${{ default(secret.webhook, "x") }}  # secret に対する組み込み関数
${{ secret.webhook.path }}           # secret に対するメンバーアクセス
daily-price-watch では、${{ secret.webhook }} は全体のまま http.post にバインドされます。http.post はシークレットを含む入力が供給されたときにだけ secrets を宣言します。そのため境界は保たれ、URL は解決するToolの内部でのみ合成されます。そのレイヤーは、値が Journal に到達する前にマスクできます。シークレットは式レイヤーを流れないため、式レイヤーを通じて漏えいすることはありません。
secret. 参照は flow の output に決して現れてはならず、secrets を宣言していないToolにバインドされてもいけません。この境界の完全な扱いは シークレット にあります。

関連

組み込み関数リファレンス

16個の関数すべてを扱うリファレンスです。シグネチャ、引数、例を示します。

制御フロー

whenbranchforeach、並列性など、式が供給先となる手順レベルの構造です。

シークレット

secret. 参照の背後にある境界と、静的に強制される taint rule です。

FlowSpec & IR

式がどのようにパースされ、型チェックされ、canonical で content-addressed な IR へ変換されるかを説明します。

Tools と Packs

記録された非決定性が存在する場所、つまり std/controluuid と標準 Pack です。