FlowはCIジョブに自然に適しています。決定論的で、固定され、リプレイ可能だからです。コンパイル済みのFlowSpecはcontent-addressed digestを持ち、壁時計時刻に依存したRegistryの状態ではなく、コミット済みのbrowserflow.lockからToolを解決します。そのため、同じチェックは手元のラップトップでもCIランナーでも同じ答えを返します。このページでは3つのCIパターンを扱います。

パターン1 — コンパイルでゲート

純粋な型チェックで副作用ゼロ。すべてのpull requestの既定ゲートです。

パターン2 — ライブSmoke

CIでbrowserflow runを実行し、実ターゲットへ実際の副作用を発火します。

パターン3 — リプレイ検証

記録済みJournal fixtureに対するhermeticなリプレイ。ライブ依存はゼロです。

ここでCIが決定論的になる理由

browserflow FlowをCIで安全かつ安定して実行できるのは、次の2つの性質があるためです。
  • browserflow compile --checkは純粋です。 副作用なしでFlowを型チェックし、digestを出力します。また、固定済みToolの解決以外ではネットワークに触れません。コンパイラは(source, registry)の純粋関数なので、高速で、キャッシュ可能で、CIで安全に実行できます。
  • digestはランナー間で安定します。 browserflow.lockが存在すると、すべてのuse:範囲は、Registryにその時点である最新バージョンではなく、lockに記録された正確なバージョンとcontract digestへ解決されます。解決を決めるのはRegistryの最新状態ではなくlockです。だからこそ、browserflow compile --checkはローカルで見たものと同じdigestをCIで生成します。詳しくはFlowSpec IRを参照してください。
Flowと一緒にbrowserflow.lockをコミットしてください。lockは「解決」を壁時計時刻に依存した検索から再現可能な判断へ変えるものです。また、CIでのdigest比較を意味のあるものにします。
このページ全体では、daily-price-watch Flowを実行例として使います。ブラウザーToolのsearch-price@1.2.0が価格を読み取り、transform手順がそれを整形し、条件付きのhttp.postがシークレットのwebhookへ通知します。シークレットはartifact内には一切入りません。実行時にCI環境自身のSecrets providerから渡されます(Launchers)。

パターン1 — コンパイルでゲートする

最も低コストなCIゲートは、Flowを型チェックし、そのdigestを出力するものです。副作用を実行しないため、pull requestチェックやpre-commit hookの既定として適しています。
1

Flowがコンパイルできることを確認する

--checkはartifactを書き出さずに型チェックし、digestを出力します。型エラー、未解決のuse:、参照サイクル、ガードされていないskipは、実行時ではなくここでジョブを失敗させます。
browserflow compile flows/daily-price-watch.yaml --check
2

失敗時のdiagnosticsを読む

Compile diagnosticsは構造化されたsource map付きデータで、CLI、LSP、UIのどれでも同じ方法で表示されます。失敗時には、正確なYAML行、ルール名、修正案が示されます。
error[type_mismatch]: flows/daily-price-watch.yaml:9:18
  step "notify" input "json" expects { price: number }
  but steps.shape.output is typed { price: string, at: string }
  └ hint: wrap with `default(0, …)` or fix `shape`'s transform to emit a number
完全なカタログ(type_mismatchunresolved_versioncycleunguarded_skipなど)はDiagnosticsを参照してください。
3

digestをFlowのIDとして扱う

出力されるdigestは、Flowの構造固定されたrequiresを対象にします。それぞれのrequiresは、解決済みTool contract digestを持ちます。本文が変更されたToolが再公開されると、同じバージョン番号であってもcontract digestが変わり、その結果Flow digestも変わります。実行間でdigestを比較することは、「依存関係の閉包が変わったか?」を自己証明するチェックになります。
browserflow compile(および--check)は常に副作用がなく、プロンプトも表示しません。どのpipelineでも無人で安全に実行できます。

パターン2 — CIでFlowを実行する

CIで実際にFlowを実行するには、shell stepとしてbrowserflow runを使います。browserflowは標準的なNode/Bunバイナリです。CI環境にインストールし、そのまま呼び出してください。
name: smoke
on: [push, pull_request]

jobs:
  daily-price-watch:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: |
          browserflow run flows/daily-price-watch.yaml \
            --input query="wireless headphones" \
            --yes
        env:
          # シークレットは実行時にランナー環境から供給されます。
          # FlowSpecやエクスポートされたartifactには焼き込まれません。
          PRICE_WEBHOOK_URL: ${{ secrets.PRICE_WEBHOOK_URL }}
- run: browserflow run flows/daily-price-watch.yaml --input query="wireless headphones" --yes
同じFlowSpecは、CLI、REST、MCP、cron、CI shell stepのどのLauncherでも変更なしに実行されます。ホスト間で変わるのはbuildRunDepsだけで、エンジンとFlowSpecは一定だからです。手元のラップトップで確認済みのFlowは、そのFlowSpecとPack digestが同一であるため、ランナー上でも再現されます。詳しくはLaunchersを参照してください。
browserflow runは、daily-price-watch内のwebhookへの条件付きhttp.postを含め、実際の副作用を実行します。それらの副作用を発火する意図がある場所(たとえばstaging webhookに対するsmoke test)でのみCI実行してください。ライブ依存なしで純粋に「何か変わったか?」をゲートしたい場合は、下のreplay --verifyを優先してください。

パターン3 — リプレイでToolアップグレードをゲートする

最も強力なCIゲートは、記録済みのJournalを移植可能で実行可能なfixtureとして使うものです。一度記録してfixtureをコミットし、CIで何も変わっていないことを検証します。ライブ依存はゼロです。
1

ローカルでJournal fixtureを記録する

Flowを一度実行し、Journalを取得します。ライブToolに触れるのはこの一度だけです。
browserflow run flows/daily-price-watch.yaml \
  --input query="wireless headphones" \
  --record-journal ./fix/daily-price-watch.journal.json
2

fixtureをコミットする

生成artifactの既定出力先はgitignoreされたout/ですが、fixtureとして固定したいJournalは意図的にコミットします。ここでは./fix/配下に置いています。Flowと一緒にGitへチェックインしてください。
3

すべての変更でCI検証する

browserflow replay --verifyは、保存されたJournalからFlowをhermeticかつオフラインで再実行し、すべての手順の出力が今も一致することを検証します。副作用はなく、プロンプトも表示しません。
browserflow replay ./fix/daily-price-watch.journal.json --verify
search-price@1.2.0search-price@1.3.0への更新(またはToolの再公開)によって挙動が変わると、リプレイは最初に分岐した手順で明確に失敗します。これにより、「アップグレードで何か変わったか?」を、ライブ依存なしで動く1コマンドの決定論的チェックにできます。
browserflow.lockに触れるpull requestでは、Actionのreplay --verify形式を必須チェックとして追加してください。クリーンなリプレイは、そのアップグレードがfixtureに対してbyte-for-byteで挙動を保持していることを意味します。失敗した場合は、どの手順が分岐したかが正確に示されます。

どのCIゲートを実行すべきですか?

ゲートコマンド副作用ネットワーク検出できるもの
Compile checkbrowserflow compile --checkなし固定済みToolの解決のみ型エラー、未解決バージョン、サイクル、ガードされていないskip、digest drift
Live smokebrowserflow run(Action経由)ありライブTool + 副作用実ターゲットに対するend-to-endの破損
Replay verifybrowserflow replay --verifyなしなし(hermetic)Toolアップグレードまたはリファクタ後の手順出力の変化
ありません。hermetic replayが既定であり、--verifyは常に副作用なしです。ライブ依存なしで、記録済みのJournalから再実行し、プロンプトも表示しません。ライブ版にあたるbrowserflow replay --rerunは副作用を再発火するため、browserflow runと同じeffect summary and confirmの経路に従います。実ターゲットへアクセスする意図がある場合にのみ使ってください。
browserflow.lockが存在すると解決が決定論的になるからです。範囲は、コンパイル時にRegistryがたまたま返すものではなく、lockに記録された正確なバージョンとcontract digestへ解決されます。その後、hash化の前にcanonicalizationがbyte単位で同一の形式(RFC 8785 JCS)を生成するため、同じsourceを同じlockに対してコンパイルする2つの環境は同じdigestを出力します。詳しくはFlowSpec IRを参照してください。
シークレットは実行時にランナー自身のSecrets providerから供給され、ログ、inspect、エラー、--dry plansを含むすべてのstreamでマスクされます。シークレットがFlowSpec、Pack export、Journalの中を移動することはありません。詳しくはSecretsを参照してください。

テストとリプレイ

Journalをfixtureとして記録し、それを中心に決定論的なテストスイートを構築します。

CLIリファレンス

compilerunreplayの完全なコマンドマップを、flagと例付きで示します。

FlowSpec IR

解決、canonicalization、digestがCIをどのように安定させるか。

Launcher

CI stepは数あるLauncherの1つです。1つのFlow、多数のfrontです。