コンテンツにスキップ

AI WF2Des — システムワークフロー

他のページは部品を仕様化しています — worker の generation/internal event path は 概要、 エンジンは 組み立てアーキテクチャ、フィールドレベルの契約は I/O 定義 と Internal API。 このページは部品が 動く 様子を示します: バックエンド、apps/wf2des ワーカー、wf2des-api、 Figma plugin にまたがる、エンドツーエンドのすべてのランタイムフローです。


全体像

flowchart LR
  subgraph SURFACES["Surfaces"]
    PL["Plugin (in Figma)"]
    AC["API callers"]
    MA["MCP agents"]
  end
  subgraph BE["Backend — product control plane"]
    API["wf2des API<br/>trigger · registration · status · list ·<br/>confirm · cancel ·<br/>placement / feedback flags"]
    PG[("PostgreSQL wf2des row<br/>status · phase · attempt ·<br/>open-generation partial unique")]
    DREG[("platform design table<br/>type=component — the SHARED registry")]
    SQSG[("generation SQS queue<br/>(backend-owned)")]
    WH["POST /v1/webhooks/ai-status<br/>(X-API-Key) + registry apply"]
  end
  subgraph AI["AI side — guinness-ai-v2"]
    WK["apps/wf2des worker<br/>generation + internal events · never writes PG"]
    SQSE[("wf2des-events intake queue<br/>(AI-owned; backend send-only)")]
    EB["EventBridge schedule<br/>(component resync)"]
    DAPI["wf2des-api<br/>(internal-api route group)"]
    LLM["LLM<br/>model + prompt version pinned"]
  end
  subgraph ST["Stores"]
    DD[("DocumentDB guinness_v2<br/>wireframe · design_rule · design_component ·<br/>project_figma_file · design_generation_result ·<br/>design_resolution")]
    S3[("S3 — snapshots · artifacts ·<br/>result manifests")]
  end
  FIG["Figma file"]

  PL -->|"trigger · registration · poll ·<br/>confirm · cancel · flags · discovery"| API
  AC --> API
  MA --> API
  API -->|"row FIRST — status '0', phase parse"| PG
  API --> SQSG
  API -->|"rule / plugin events"| SQSE
  SQSG --> WK
  SQSE --> WK
  EB --> WK
  WK <--> LLM
  WK --> DD
  WK --> S3
  WK -->|"completion webhook {job_id, attempt, nonce,<br/>status, error?, result_manifest_url?,<br/>manifest_schema_version}"| WH
  WK -->|"component_sweep manifest<br/>(discovered type=component)"| WH
  WH -->|"attempt-guarded flip: status/phase<br/>+ result refs + flag_count"| PG
  WH -->|"design type=component upserts"| DREG
  PL <-->|"data plane: file reg · preview ·<br/>spec · details"| DAPI
  MA -.->|"spec reads"| DAPI
  DAPI <--> DD
  DAPI <--> S3
  WK -.->|"REST read (PAT): component walk ·<br/>rule-board render · memo fallback"| FIG
  API -.->|"trigger-time snapshot (service-account PAT):<br/>frame + board section"| FIG
  PL ==>|"the ONLY writes into Figma"| FIG

このページのあらゆるフローを形づくる 3 つの法則があります。その 1 — Figma に書き込むのは Figma セッションのみ: Figma の REST サーフェスにはデザインを書き込むエンドポイントが存在しないため、 ファイルに現れるすべてのピクセルは、デザイナーのエディタセッション内で plugin のメインスレッドに よって作られます。その 2 — ワーカーは PostgreSQL に決して書き込まない: あらゆる PG 書き込みは バックエンド側です — トリガーおよびフレーム登録エンドポイント、confirm / cancel / placement / feedback エンドポイント、generation 完了時の ai-status Webhook ハンドラ、 加えてコンポーネントレジストリ upsert — バックエンドが component_sweep のマニフェストからプラットフォーム design テーブル (type=component)を書き込む箇所です。その 3 — 登録 → 処理 → コミット: バックエンドが実行を 登録し(wf2des 行 + イミュータブルな S3 入力)、ワーカーがそれを処理し(DocumentDB コンテキスト + S3 アーティファクトとマニフェスト)、コミットはバックエンド側です(generation Webhook が行を反転し、 バックエンドが component_sweep のマニフェストをレジストリに適用します)。

本ページ全体で使うステータス語彙: status は '0' processing · '1' completed · '2' failed · '3' rejected · '4' cancelled。phase は status '0' の内側に存在し — '0' parse · '1' awaiting_confirm · '2' assemble — 行がターミナルステータスに達するとクリアされます。


プロジェクトオンボーディング — プロジェクトごとに一度

オンボーディングはレイテンシではなくスループットの仕事です: プロジェクトごとに一度だけ行われ、 generation が後で pin するすべてのもの — ファイル、コンポーネント、ルール、 ワイヤーフレーム — はここから入ります。

flowchart TD
  A1["1 · Project exists on the platform;<br/>the service account has view access"]
  A2["2 · Register the Figma files —<br/>POST /internal/projects/{project_id}/figma-files via wf2des-api<br/>(role: working | library) → project_figma_file docs;<br/>also the plugin's org/project + file resolution"]
  A3["3 · COMPONENTS — component_sweep populates the SHARED registry:<br/>EventBridge schedule / plugin resync event →<br/>Figma REST full-file walk (/components lists published only) →<br/>design_component context docs (variants · text slots · size)<br/>+ the sweep manifest → backend UPSERTS platform design rows type=component"]
  A4["4 · RULES — designer selects guideline boards in the plugin →<br/>rule event on wf2des-events → rule_process renders each board (PAT)<br/>+ vision-extracts per board + deterministic merge →<br/>versioned IMMUTABLE design_rule doc, worker-written directly<br/>(webhook-free; keyed (design_rule_id, content_hash of the merged RuleSet))"]
  A5["5 · WF REGISTRATION — designers register selected frames<br/>in-Figma via the plugin → backend snapshot +<br/>registration events on wf2des-events → wf_parse →<br/>wireframe cache docs"]
  A6["6 · INGEST ISSUES LOG (staging/issues.json) —<br/>memo-marker unknowns · screen-ID grammar violations ·<br/>non-auto-layout roots — reviewed BEFORE generation<br/>runs against the file"]
  A1 --> A2 --> A3 --> A4 --> A5 --> A6

このフローには、下流で重要になる 2 つの性質があります。第一に、3 つの取り込み実行はすべて 冪等 です: 各出力ドキュメントがそれ自身の記録であるため、リプレイされたイベントは重複せず 収束します。第二に、登録時にキャプチャされた WF snapshot はキャッシュであってコミットメントでは ありません — generation はトリガー時にフレームを再 snapshot するため、古い登録が実行に投入される ことは決してありません。


Plugin 経由の Generation — 主要フロー

インタラクティブフロー: デザイナーがワイヤーフレームフレームを選択し、バックエンドがそれを登録して snapshot し、ワーカーが parse し、デザイナーが parse を confirm し、ワーカーが assemble し、 plugin が結果をネイティブ Figma としてマテリアライズします。

sequenceDiagram
  actor D as Designer
  participant P as Plugin
  participant A as Backend (API + PG)
  participant Q as Generation SQS
  participant W as wf2des worker
  participant X as wf2des-api

  D->>P: select WF frame + screen ID (prefilled)
  P->>A: POST trigger (figma_file_key, wf_node_id, screen_id, auto_confirm, prompt?, placement_target?)
  A->>A: INSERT wf2des row FIRST — status '0', phase parse<br/>(partial-unique dedupe hit → 409 + the existing wf2des_id)
  A-->>P: 201 + wf2des_id (immediate)
  Note over A: SNAPSHOT AT TRIGGER — between row creation and enqueue,<br/>Figma REST (the wf2design service-account PAT) captures the frame<br/>+ its enclosing board section → S3 + wf_content_hash;<br/>unregistered frames auto-register
  A->>Q: parse message {wf2des_id, attempt, nonce, snapshot scope + hash, input URLs}
  Q->>W: PARSE (one-shot)
  Note over W: deterministic WFNode extraction + LLM roles/intent →<br/>result-doc parse block + immutable parse.json<br/>(DocDB commits CAS-fenced on (wf2des_id, attempt))
  W->>A: webhook parse_done {job_id, attempt, nonce}
  A->>A: wf2des row → phase awaiting_confirm
  P->>A: poll GET (backoff) — status '0', phase awaiting_confirm
  P->>X: GET /internal/wf2des/{wf2des_id}/parse
  X-->>P: parse block + presigned parse.json
  P-->>D: PARSE PREVIEW — structure + memo influences (resolved memos excluded) + snapshot age/hash
  D->>P: confirm or reject
  alt reject
    P->>X: POST /internal/wf2des/{wf2des_id}/reject {reason_code, note} → parse.rejected (detail FIRST)
    P->>A: POST confirm {attempt, reject: true} → status '3' rejected (flag LAST)
  else confirm
    P->>A: POST confirm {attempt, parse_artifact_hash}
    A->>A: CAS on phase awaiting_confirm + attempt → phase assemble
    A->>Q: assemble message {wf2des_id, attempt, fresh nonce, confirm decision}
    Q->>W: ASSEMBLE (one-shot — pins rehydrated from the result doc)
    Note over W: registry hygiene + deterministic role/kind gate →<br/>design_resolution ledger consult → section-parallel K-sample<br/>self-consistency voting selection → ledger write-back → deterministic stitch →<br/>rules validator → computed confidence
    W->>W: S3 result artifact + result manifest
    W->>A: webhook succeeded {job_id, attempt, nonce, result_manifest_url}
    A->>A: handler — deduped on (job_id, attempt, nonce), ONE PG txn:<br/>status '1' + result refs + flag_count, phase cleared
    P->>A: poll GET → completed (+ top-level flag_count)
    P->>X: GET /internal/wf2des/{wf2des_id}/result
    X-->>P: self-contained spec (no follow-up lookups)
    Note over P: MATERIALIZE — chunked build ·<br/>pluginData {jobId, specVersion} stamped
    P-->>D: new tagged frame, flags visualized
    P->>X: POST /internal/wf2des/{wf2des_id}/placement (detail FIRST)
    P->>A: POST placement → materialized_at (flag LAST)
    D->>P: adopt / fix
    P->>X: POST /internal/wf2des/{wf2des_id}/feedback (detail FIRST)
    P->>A: POST feedback → feedback_status (flag LAST)
  end

load-bearing な詳細:

  • 行が先、エンキューは後。 wf2des 行はジョブより先に存在するため、ワーカー内での行の 読み込みミスは実際のエラーであり、結果整合性ウィンドウでは決してありません。オープン generation の 部分ユニーク(partial unique) — (figma_file_key, wf_node_id) WHERE status='0' — が 並行トリガーを重複排除します: 2 番目の呼び出し元は 409 + 既存の wf2des_id を受け取り、 実行中の generation にアタッチします。
  • トリガーが送るのは識別子であってツリーではない。 ワイヤーフレームのコンテンツが plugin から 移動することは決してありません。バックエンドがトリガー時にサーバー側で snapshot をキャプチャし (スコープはフレーム + それを包含する board section)、SQS メッセージがそのスコープ + wf_content_hash を運びます。
  • confirm チェックポイントは CAS。 Confirm は {attempt, parse_artifact_hash} を運び、行の phase awaiting_confirm + attempt に対して compare-and-swap されます — 古い confirm は 409 を、 再送は 200 エコーを受け取ります。
  • 詳細を先に・フラグを後に(detail first, flag last) — plugin のあらゆる書き込みペアは、 バックエンド行の対応するフラグを反転する前に、コンテンツ詳細を wf2des-api(DocumentDB)に 着地させます: status '3' の前に parse.rejected、materialized_at の前に placement 詳細、 feedback_status の前に feedback 詳細。
  • 完了時の書き込み者は 1 つ。 ai-status ハンドラが完了時の唯一の PG 書き込み者です: (job_id, attempt, nonce) で重複排除され — job_id は wf2des 行 id — attempt ガード付き、 1 つの PostgreSQL トランザクションで、result_manifest_url が参照するマニフェストを適用します。

レイテンシソフトターゲット(p50、計測値 — ゲートではありません): trigger→parse プレビュー < 60s · confirm→spec < 2min · spec→materialized < 30s。すべてのフェーズは結果 ドキュメントの timings ブロックにタイムスタンプされます。


API / MCP 経由の Generation — 差分のみ

API 呼び出し元や MCP エージェントは plugin と同じワイヤーを走りますが、3 つの違いがあります:

  1. auto_confirm = 1 回の呼び出し。 同じバックエンド POST を auto_confirm=true で呼ぶと、 parse + assemble が単一のワーカー呼び出し内で連続実行されます — awaiting_confirm での待機なし、 confirm ラウンドトリップなし、発火するのはターミナル Webhook のみです。parse 記録 (memo influence、role)は、マテリアライズ時のレビューのために引き続き書き込まれます。
  2. ステータスはバックエンドから、コンテンツは wf2des-api から。 呼び出し元は完了まで バックエンドの GET をポーリングし(+ トップレベルの flag_count)、spec は GET /internal/wf2des/{wf2des_id}/result から読み込みます — Figma フレームからでは決して ありません。フレームはまだ存在しないからです(書き込み境界)。
  3. MCP エージェントも同じ 2 つのプレーンに乗る。 バックエンド MCP サーバーが trigger/status ツールをフロントします — プラットフォームの trigger-* / get-* ツール命名に従い、最終的な ツール名は MCP ラッパーとともに確定します — auto_confirm は 強制 です: parse チェック ポイントに座る人間がいないためです。コンテンツ読み込みは read スコープのサービストークンで internal-api を通ります。MCP エージェントはトリガーと読み込みを行い、Figma に書き込むことは 決してありません(後日のプロトタイピング専用経路は、開いている Figma デスクトップ セッションを駆動できます — これは「Figma の内側」であり、壁のチャネル 4 側です)。

生成されたデザインは後から、遅延マテリアライゼーションを通じてネイティブ Figma になります — 次のセクションです。


遅延マテリアライゼーション

native frame build 後、plugin は actual PNG、node/bounds manifest、materializer report を capture し、hash-fenced render_review event を送ります。pass は元 frame を保持します。最初の needs_changes のみ、元を保持したまま隣に candidate を 1 つ build でき、2 回目の render で verify します。pass のみ in-place promote し、failure/stale identity/ unverifiable evidence は candidate を破棄して元を保持します。2 回目は verification-only なので recursive correction はありません。

plugin セッションとジョブは 疎結合 です: ジョブ途中で Figma を閉じても何も失われず、API/MCP トリガーの generation は materialized_at 未設定のまま、completed でただ待ちます。plugin は ファイルを開くたびに バックエンド に対してディスカバリハンドシェイクを実行します — レディネスはクライアント向けステータスであり、それは PostgreSQL に存在するからです:

  1. 汎用リストエンドポイント(GET …/wf2des、status / phase / screenId / feedbackStatus / materialized / createdFrom / createdTo でフィルタ)は、開いている ファイルについて 2 種類のヒットを返します: 待機中のプレビュー(phase=awaiting_confirm — デザイナーが confirm/reject の判断を再開するか、POST …/wf2des/{id}/cancel でジョブを終了)と 保留中のビルド(materialized_at 未設定の completed 行。保存済みの placement_target を 含む)。同じエンドポイントがプロジェクト履歴も提供します。
  2. 重複ビルドガード は、保留中のビルドが提示される前に必ず実行されます: plugin は wf2des-api 経由で既存の placement 詳細をチェックし、かつ ファイル内を pluginData {jobId} でスキャンします。したがって、placement 詳細と materialized_at フラグの 間でクラッシュしたセッションが 2 つ目のフレームを生むことは決してありません。
  3. auto-confirm ジョブはスキップされたレビューをここで受けます。 API/MCP トリガーのジョブでは、 plugin はビルドを提示する前に、まず parse 記録を取得して memo influence とフラグを表面化します — 誰も座らなかったチェックポイントです。
  4. spec は wf2des-api から取得され、保存済みの placement_target にビルドされ、通常の placement の 詳細 + フラグ ペアでクローズされます。レビューするデザイナーの adopt/fix フィードバックは、このマテリアライズ + レビューの瞬間にアタッチされます — インタラクティブ フローの最後のステップとまったく同じです。

このハンドシェイクこそが、API 呼び出し元のデザインがネイティブ Figma に到達する方法です: 後から そのファイルを開いたどのデザイナーにも、保留中のビルドが提示されます。


4 つの Figma チャネル

Figma とシステムの間のすべては、ちょうど 4 つのチャネルの上を移動します。Figma から呼び込まれる ことは決してありません — 今日 Figma Webhook は存在せず、あらゆる動きは plugin、バックエンド (トリガー時 snapshot)、ワーカー、またはオペレーターによって開始されます。

# チャネル 方向 開始者 認証 運ぶもの
1 Plugin → バックエンド API(コントロールプレーン) Figma の外へ plugin UI プラットフォームの認証(plugin UI でのログイン) trigger(識別子のみ)、フレーム登録 + resync、ステータスポーリング(+ phase)、confirm/reject、cancel、placement + feedback の フラグ、汎用リストエンドポイント経由のディスカバリ
2 Plugin ↔ wf2des-api(データプレーン — internal-api 内の wf2des ルート) 双方向 plugin UI セッション発行トークン {org_id, project_id, exp} ファイル登録(project_figma_file の role/config)、parse プレビュー、spec(自己完結型)、placement + feedback の 詳細
3 サーバー側 → Figma REST(読み込み) システムの中へ ワーカー · トリガー時のバックエンド wf2design サービスアカウントの PAT(wf2design 自身のシークレットストア — プラットフォームの figma_token テーブルでは決してない) コンポーネントのフルファイル巡回、memo フォールバックのノード読み込み、WF 登録 snapshot、トリガー時 WF snapshot(フレーム + 包含する board section)
4 Plugin メインスレッド ↔ 開いているファイル(Figma Plugin API) Figma の内側 plugin メインスレッド なし — デザイナー自身のセッション 選択された WF フレームの読み込み。生成フレームの 書き込み(インスタンス、オーバーライド、pluginData)

チャネル 4 はシステム全体で Figma への唯一の書き込みパス です。チャネル 3 は唯一のサーバー側 読み込みであり、Figma 自身の API 設計により read-only です。1 と 2 の分割はストレージの分割を ミラーします: ステータスの真実は PostgreSQL に存在しバックエンドが提供し、コンテンツは DocumentDB + S3 に存在し wf2des-api が提供します(Internal API 契約 を参照)。plugin はトリガー時に受け取った wf2des_id で両者を縫合します。

plugin の内部では 2 つの半身が互いに素な権限を持つため、あらゆるフローはリレーになります:

flowchart LR
  subgraph FIGMA["Figma editor session"]
    FILE[("open file<br/>WF frames · components ·<br/>generated frames")]
    MAIN["plugin MAIN THREAD<br/>(sandbox — figma.* only, no network)"]
    UI["plugin UI IFRAME<br/>(network only, no figma.*)"]
    FILE <-->|"read selection / build nodes"| MAIN
    MAIN <-->|postMessage| UI
  end
  UI -->|"1 · control: trigger · frame reg + resync ·<br/>poll · confirm · cancel · flags · discovery"| BE["Backend API<br/>(PostgreSQL)"]
  UI <-->|"2 · data: file reg · preview ·<br/>spec · details"| XAPI["wf2des-api<br/>(DocumentDB + S3)"]
  W["wf2des worker"] -->|"3 · REST read (PAT):<br/>component walk · memo fallback ·<br/>WF-registration snapshots"| FC["Figma cloud<br/>(REST API)"]
  BE -->|"3 · REST read at trigger<br/>(service-account PAT)"| FC

チャネル 4 上のマテリアライゼーションの仕組み:

  • 同一ファイル制約。 コンポーネントは node_id で直接インスタンス化されます(Figma の /components は公開済みコンポーネントのみをリストするため、ローカルコンポーネントには publish キーがありません)— placement 先のファイルはソースコンポーネントを含んでいなければならず、 そうでなければマテリアライザは 明確なエラーで拒否 します。
  • オーバーライドはレイヤー名/パスで、決して序数では行わない — spec の layer_path アドレッシングと一致するため、レイヤーリストの並べ替えがオーバーライドのターゲットを誤ることは 決してありません。
  • 常に新しいタグ付きフレーム。 ビルドが上書きすることは決してなく、出力フレームには pluginData {jobId, specVersion} がスタンプされます。
  • JP フォントプリフライト。 plugin は spec のフォントをリストし、エディタでの利用可能性を プローブし、ビルド開始前に警告します。
  • チャンク化されたビルド。 大きな spec はエディタの応答性を保つためにチャンク単位でビルドされます。
  • 識別子であってツリーではない。 WF コンテンツが plugin から移動することは決してありません — バックエンドがトリガー時にサーバー側でフレームを snapshot するため、チャネル 1 はシンなままで、 ジョブの入力は S3 でコンテンツアドレス指定されます。

フィードバック — 記録のみ

デザイナーはマテリアライズされた結果を adopted または fixed としてマークします。plugin は まず詳細を wf2des-api に書き込み — POST /internal/wf2des/{wf2des_id}/feedback に {status, changed_nodes[], diff_url, at, by} を渡します。status は adopted | fixed、 changed_nodes は spec の layer_path 値であり、サイズ超過のインライン diff は S3 にスピルします — その後、バックエンドの feedback エンドポイント経由で wf2des 行の feedback_status を反転 します(フラグを後に)。そしてそれが フローの終わり です: イベントは発行されず、ワーカーの レグは走らず、diff からの自動学習はありません。

保存された adopt/fix の diff は、デザイナー自身のルールおよびコンポーネント改訂のための レビュー材料 であり、それらの改訂は通常のチャネルを通じてシステムに再入場します — ルール編集は 下記のルール更新フローを移動し、コンポーネント編集は次のレジストリ resync に拾われます。


ルール更新

デザイナーが Figma 上のプロジェクトのガイドラインボードを更新し、再登録します: デザイナーが plugin でガイドラインボードのフレームを選択 → バックエンドが wf2des-events に rule-upload イベント({design_rule_id, figma_file_key, board_node_ids[]})を発行 → rule_process が 選択された各ボードを Figma REST(サービスアカウント PAT)でレンダーし(ボードの欠落や レンダー失敗はハードエラー)、ボードごとに 1 回の vision 抽出 を実行し(LLM フェンス 呼び出し 4)、フラグメントを決定論的にマージして、新しい イミュータブルでバージョン付きの design_rule ドキュメント を DocumentDB に直接書き込みます — (design_rule_id, マージ済み RuleSet の content_hash) ごとに 1 ドキュメント、バージョン序数、 ボード画像の参照、抽出 provenance、draft_source="llm_extracted" 付き。抽出結果はデザイナーの レビュー待ちとして着地します: デザイナーは抽出されたルールを確認し、ガイドライン修正後に 再登録します — 同一のマージ済みルールは既存のリビジョンに収束し、変更されたルールは次の バージョンを発行します。この実行は Webhook フリーかつ PG フリーであり、design_rule コレクション が ルールのリビジョン履歴です。

これを飛行中でも安全に保つのがバージョニングです: 実行中の generation は pin 済みの (design_rule_id, content_hash) を保持し — ルール編集が進行中の実行を変えることは決して ありません — 新しい generation はトリガー時に最新リビジョンを pin します。


レジストリ Resync

resync は、AI 所有の EventBridge スケジュール または wf2des-events 上の plugin resync イベントの いずれか から発火します → component_sweep が Figma REST を介してプロジェクトの 登録済みファイルを巡回し、保存済みコンテンツハッシュに対してハッシュ差分を取ることで、変更された コンポーネントだけを再 snapshot します。続いて 2 つの出力:

  • 直接書き込まれる DocDB コンテキスト: design_component ドキュメント(variant プロパティ、 text slot、デフォルトサイズ)。プラットフォーム design 行 id にキー付けされ、 加えて project_figma_file へのフィールドレベルのスタンプ — 最後に components_synced_at、 失敗時に sweep_error。
  • バックエンド側で適用される発見コンポーネントマニフェスト: バックエンドがプラットフォーム design 行(type=component)を upsert します — 共有コンポーネントレジストリ であり、 des2code が消費するのと同じカタログ — これが各コンポーネントの存在、名前、ステータス、削除を 所有します。

single-flight ガード — project_figma_file ドキュメント上の sweep_marker — が、2 つの並行 sweep がレジストリで競合するのを防ぎます。古い参照は、出力ドキュメントの鮮度 (components_synced_at)と次の sweep の再収束を通じて表面化します。 他の内部実行との非対称性に注意してください: wf_parse と rule_process は完全に自己完結 (出力ドキュメントが記録そのもの)ですが、component_sweep は加えてレジストリ upsert のために sweep マニフェストをバックエンドに手渡します — generation 専用の ai-status Webhook とは別の 経路です。


失敗時の挙動

失敗 何が起きるか 誰が何を見るか
ジョブ途中のワーカークラッシュ ワンショット Lambda が死ぬ → SQS 再配送(部分バッチ)。リトライ枯渇または行が '0' でスタック → 現状、復旧経路は存在しない。行は非終端のまま残り、そのワイヤーフレームへの新規トリガーをブロックし続ける ops / plugin の Retry ボタン
Generation の失敗 …-failed.json アーティファクト + failed Webhook {job_id, attempt, nonce, status: failed, error, result_manifest_url → …-failed-manifest.json} → ハンドラが行に error を記録し、status '2' に反転、phase をクリア デザイナーが理由 + Retry を見る
provider credit 枯渇、認証/アクセス無効、設定 model unavailable terminal failed artifact + manifest + safe client-visible failed webhook を生成し、再配信では直らないため message を consume designer は具体的な provider error を見る
通常の provider 429 rate limit / 5xx terminal surface を作らず throw し SQS redelivery temporary retry state
parse が間違って見える デザイナーがプレビューを 却下 — 詳細 {reason_code: wrong_roles \| wrong_memos \| wrong_sections \| other, note} を 先に wf2des-api へ(→ parse.rejected)、その後バックエンドが status '3' rejected に反転(詳細を先に・フラグを後に)。却下後のリトライは parse キャッシュをスキップ クリーンな停止、spec なし。理由は parse-config キュレーションに反映される
awaiting-confirm の忘却 wf2des 行に対するバックエンド側 sweep がタイムアウト(例: 72h)を強制 → fail closed、理由を記録 次のポーリングでデザイナーに通知
プレビューの放置(デザイナーが判断途中で離脱) ジョブは phase=awaiting_confirm で保持 — 何も走らず、何も失われない。何も期限切れにしない 後のどのセッションも汎用リストエンドポイント経由でそれを見つけ、confirm/reject を再開 — または POST …/wf2des/{id}/cancel で終了
Webhook の不達 ワーカーの送信は 失敗時に raise → SQS が再配送し送信がリトライ。ターミナル S3 マニフェストが 永続的な完了記録 であり続ける。リトライを使い切るとマニフェストは適用されない — それをスキャンしてリプレイする stuck-generation sweep は未実装 のため、行は非終端のまま残る。Webhook は generation 専用 — component_sweep は配送を必要としない: そのコンテキストとマニフェストは次の sweep で再収束 / 再適用 再配送が続く間のみ自己修復。DLQ 後は手動
重複トリガー(同じ WF がすでに進行中) オープン generation の 部分ユニーク (figma_file_key, wf_node_id) WHERE status='0' が重複排除 → 409 + 既存の wf2des_id 両方の呼び出し元が同じ行をポーリング
行作成後の SQS 送信失敗 トリガーは 500 を返し、ノードを解放するためベストエフォートで行を '2' に fail する。その fail 処理も失敗した場合は バックストップがない — stuck-'0' sweep は未実装であり、その行は手動で解消されるまでそのワイヤーフレームの以後のトリガーをすべてブロックする 即時エラー。fail 処理が通らなかった場合は手動
内部実行の失敗(wf_parse · rule_process · component_sweep) wf2des-events での SQS 再配送 / EventBridge リトライ + 収束する冪等なコンテンツアドレス指定 / LWW / upsert 書き込み。スタックした実行の可視性 = 出力ドキュメントの鮮度 + SQS デッドレターキュー — バックエンドは内部実行を決して見ない ops(出力の鮮度 + DLQ)
materializer が content を解決/安全適用できない name_fallback、ordinal_fallback、prop_rejected、unmatched、preserved を実際の detail とともに materializer_report に記録 actionable text を持つ visible diagnostic
node をまったく build できない build_error は可視で 静かに削除されることは決してない。substitute font で render できた場合は別の font_fallback visible placeholder または degraded-font warning
ビルドが途中で失敗(例: トリガーとビルドの間にコンポーネントが削除された) pin 済み入力が spec を有効に保つ。解決不能なノードはビルドを中断する代わりに フラグ付け(build_error バッジ) フラグ付きノードを持つビルド済みフレーム — 中途半端な静かな失敗は決してない
placement 詳細とフラグの間のセッションクラッシュ 詳細は wf2des-api に存在するが materialized_at は反転されなかった 重複ビルドガード: 次のセッションが再ビルド前に wf2des-api 経由で placement 詳細をチェックし、かつ ファイル内を pluginData {jobId} でスキャン — 2 つ目のフレームは決して生まれない
pin 済み入力の欠落 / 再水和時のハッシュ不一致 ジョブは fail closed — 古いルールやコンポーネントへの静かなフォールバックは決してない エラー + 修正後の Retry
エディタでフォントが利用不可 JP フォントプリフライト が spec のフォントをリストし、利用可能性をプローブし、ビルド前に警告 何かがビルドされる前のフォント警告
placement 先のファイルにコンポーネントがない コンポーネントは node_id でインスタンス化されるため、placement 先のファイルがソースを含んでいる必要がある(同一ファイル制約) マテリアライザが明確なエラーで拒否。コンポーネントを保持するファイル内でビルドする
wf2des-api に到達不能、バックエンドは稼働 データプレーンの障害。コンテンツ読み込みはバックオフ付きでリトライ — 状態は失われず、各ストアは自身の半分について権威であり続ける ステータスはバックエンドのポーリング経由で可視のまま

この表の下にある回復の背骨: ターミナル S3 マニフェストが永続記録 であり、Webhook は高速経路に すぎません。あらゆる行効果は (job_id, attempt, nonce) の重複排除の下で冪等です。そして完了した generation は決して再処理されません — ターミナル行は最終であり、再実行は新しい行を作成 します。再実行は、コミット済みの人間イベントデータを異なる非決定的な spec で上書きしてしまうから です。


公式のフィールドレベル契約: I/O 定義。