mcpField
X
xstate-v5

xstate-v5

github.com/johnlombardo-dev開発142 回インストール37 週間前に更新
リポジトリ

xstate-v5 について

xstate-v5は、TypeScriptとReactで型付きXState v5ステートマシンを設計、実装、リファクタリング、レビューするための厳格なCodexスキルです。コードを書く前のステートチャート設計を必須とし、アクター境界、イベント型、v4の旧パターン回避を定めています。

主な機能

  1. 01ステートチャート設計資料の作成
  2. 02XState v5 API規約の厳守
  3. 03イベント、コンテキスト、入力の型付け
  4. 04invokeと子アクターの境界明確化
  5. 05Reactアクターコンテキストの指針

活用シーン

  1. 01新しいXState v5ステートマシンの設計
  2. 02XState v5コードのリファクタリングやレビュー
  3. 03XState v4旧パターンからの移行
  4. 04Reactアプリでのアクター境界の設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/johnlombardo-dev/xstate-skill --skill xstate-v5

インストール後、Claude Code で/xstate-v5を入力して読み込まれたか確認します。

詳細

xstate-v5(厳格)スキル

厳格で型安全性の高いルールセットに従って、XState v5 のステートマシン(TypeScript および React)の構築、リファクタリング、レビューを行います。

信頼できる唯一の情報源: references/xstate-v5-rules.md(このリポジトリ内。スキルのルートから解決されます)。このスキルの内容がルールファイルと矛盾する場合は、ルールファイルに従ってください。

インライン化された必須要件(エージェントは必ず従うこと)

エージェントが外部のリファレンスを先に読み込まなくても要件を遵守できるよう、以下の要件を意図的に重複して記載しています。

  • まず設計し、その後にコードを記述します。
    • 実装前に計画資料(状態一覧、イベントカタログ、遷移表、非同期処理/アクターのマップ、受け入れテストのシナリオ)を作成します。
    • 境界と分解に関する決定記録(何を分割し、何をオーケストレーションするか、およびその理由)を含めます。
  • 何でも詰め込む巨大なステートマシン構成にしません。
    • 関連のないドメイン(認証、トースト、ナビゲーション、ワークフロー、通信/再試行ポリシー)を、1つのステートマシンですべてモデル化してはいけません。
    • 単一のステートマシンが許容されるのは、薄いオーケストレーションルートとして、または密接に連携するアプリシェルの並列リージョンとしてのみです。
  • コンテキストに状態を重複させるフラグを作成しません。
    • state.value がすでにモードを表している場合、コンテキストのブール値(isLoading、isAuthenticated など)でモードを重複させてはいけません。
    • 一時的な移行フラグがある場合は、その根拠と削除計画を記録します。
  • XState v5 の厳格なルールを守ります。
    • setup({...}).createMachine({...}) を使用します。
    • actions/guards/actors は setup(...) 内で実装します。
    • イベントオブジェクトのみを送信します。
    • 禁止されている v4 のパターン(interpret、Machine、cond、send、pure、choose など)は使用しません。
  • 非同期処理とアクターの境界を明示します。
    • リクエスト/レスポンスには、onDone と onError を指定した invoke を使用します。
    • 長期間連携するコンポーネントには spawnChild/stopChild を使用し、sendTo で明示的にイベントをルーティングします。

フェイルクローズルール:

  • 詳細が曖昧な場合は、ステートマシンのコードを記述またはレビューする前に references/xstate-v5-rules.md を読み込んでください。
  • そのファイルが存在しない、または読み取れない場合は、推測せずに停止し、問題を報告してください。

使用する場面

次のようなタスクでこのスキルを使用します。

  • XState v5 のステートマシン/アクター(xstate、@xstate/react)
  • XState のコードを、より型安全で v5 の慣用的な書き方にリファクタリングする
  • XState v4 のパターン(Machine/interpret/cond/send/pure/choose など)を v5 の相当するパターンに移行する
  • XState v5 のステートマシンとして実装する前に、ステートチャートを計画/設計する
  • アクターの境界(invoke と spawnChild の使い分け)や、createActorContext を介した React 連携を設計する

必須要件(遵守必須)

  • すべてのステートマシンで setup({...}).createMachine({...}) を優先します。
  • すべての actions、guards、actors を setup({...}) オブジェクト内で実装します。
  • イベント由来のデータは、型付きの params を介してアクション/ガードに渡すことを優先します。
  • 文字列イベントは送信しません。常にイベントオブジェクトを送信します。例:actor.send({ type: '...' })
  • references/xstate-v5-rules.md に記載されている XState v4 のレガシー API とパターンは禁止します。
  • 薄いオーケストレーションステートマシンを除き、関連のないドメイン(認証、通知/トースト、ナビゲーション、データのライフサイクルなど)を1つのステートマシン内でモデル化しません。
  • state.value がすでにそのモードを表している場合、コンテキストのブール値(isLoading、isAuthenticated など)に状態を重複させません。

ルール目次

クイックスタート

最小限の型付きマシンのスケルトン:

import { setup, assign } from "xstate"

type Ctx = { count: number }

type Ev = { type: "inc" } | { type: "add"; amount: number } | { type: "reset" }

type Input = { initialCount?: number }

export const counterMachine = setup({
  types: {
    context: {} as Ctx,
    events: {} as Ev,
    input: {} as Input,
  },
  actions: {
    inc: assign({ count: ({ context }) => context.count + 1 }),
    add: assign({
      count: ({ context }, params: { amount: number }) => context.count + params.amount,
    }),
    reset: assign({ count: ({ input }) => input.initialCount ?? 0 }),
  },
}).createMachine({
  id: "counter",
  context: ({ input }) => ({ count: input.initialCount ?? 0 }),
  on: {
    inc: { actions: "inc" },
    add: {
      actions: {
        type: "add",
        params: ({ event }) => ({ amount: event.amount }),
      },
    },
    reset: { actions: "reset" },
  },
})

React 共有状態のスケルトン:

import { createActorContext, shallowEqual } from "@xstate/react"
import { type SnapshotFrom } from "xstate"
import { counterMachine } from "./counterMachine"

const CounterCtx = createActorContext(counterMachine)

export const CounterProvider = CounterCtx.Provider
export const useCounterSelector = CounterCtx.useSelector
export const useCounterActorRef = CounterCtx.useActorRef

const selectCount = (s: SnapshotFrom<typeof counterMachine>) => s.context.count
export const useCount = () => useCounterSelector(selectCount)

const selectCtx = (s: SnapshotFrom<typeof counterMachine>) => s.context
export const useCounterContext = () => useCounterSelector(selectCtx, shallowEqual)

ワークフロー(毎回行うこと)

  1. まず references/xstate-v5-rules.md を読みます。
  2. 対象範囲を特定します:
    • プレーンなマシン/アクター(React 以外)
    • React 統合(createActorContext)またはローカルの useActor
    • v4 パターンからの移行
  3. まずステートチャートを設計します:
    • references/xstate-v5-rules.md#0-statechart-design-before-you-code を参照してください。
    • 以下を書き出します:状態一覧、イベントカタログ(ペイロード+発生元)、遷移表、非同期/アクターの境界、受け入れテストのシナリオ。
    • 型設計の前に、境界/分解の判断を記録します:
      • ライフサイクル、所有者、障害モードに基づいて、どの関心事を別々のアクター/マシンに分けるか。
      • どの関心事をトップレベルで意図的にオーケストレーションするか(およびその理由)。
      • ロジックを1つのマシンにまとめる場合は、その例外について記録します。
    • 既定ではこの手順を行います。ただし、ユーザーから計画を省略するよう明示的に求められた場合を除きます。
  4. まず型を設計します:
    • types.context、types.events、types.input(該当する場合は types.output も)
    • コードベースですでに Zod を使用している場合、またはユーザーから求められた場合に限り、zod スキーマを使用します。
  5. 実装は setup({ actions, guards, actors }) に記述します:
    • イベント由来のデータが必要なアクション/ガードには、型付きの params を渡します。
    • 実装内でイベント固有のフィールドを読み取るのは必要な場合に限り、その際は assertEvent を使用します。
  6. 非同期処理の境界を選びます:
    • リクエスト/レスポンスのフローには、型付きの input を使用する invoke を優先します。
    • 長期間存続する子アクターには spawnChild/stopChild を使用します。
  7. v5 のランタイム API を使用します:
    • createActor(machine)(interpret ではなく)
    • raise/sendTo(send アクションではなく)
    • enqueueActions(pure/choose ではなく)
  8. 検証します:
    • tsc が成功し、params に any が漏れていないこと。
    • 文字列によるイベント送信がないこと。
    • 禁止されている v4 のインポートや設定キー(cond、withContext、withConfig など)がないこと。
    • 非同期アクターのテストには waitFor を、モデル/グラフ駆動のテスト生成には xstate/graph(createTestModel、getShortestPaths など)を優先します。

モノリスが許容される場合

子アクター、または密接に結合した並列のアプリシェル関連の関心事を調整する、薄いオーケストレーションルートである場合に限り、単一のトップレベルマシンを使用します。その場合でも、無関係なドメインロジックは別々のアクター/マシンに置き、明示的なイベントを介して通信させます。

レビューチェックリスト(PR の監査時に使用)

  • すべてのマシンが setup(...).createMachine(...) になっている。
  • マシンに明確なステートチャートの計画(状態/イベント/遷移表)がある、または PR の説明に記載されている。
  • ドメイン設計上の問題がない:1つのマシンが1つの責務、または明示されたオーケストレーション範囲を担っている。
  • 根拠が文書化されていない、状態をミラーリングするコンテキストフラグがない。
  • types 内に実装がない。
  • actions/guards が、params または assertEvent を介してのみイベントフィールドを読み取っている。
  • interpret、Machine、withConfig、withContext、cond、send、pure、choose がない。
  • invoke に onError があり(該当する場合は onDone もある)。
  • React の使用では、モジュールスコープの createActorContext を優先し、セレクターが安定しており、オブジェクトには shallowEqual を使用している。

よくある修正

  • アクション内でイベントのペイロードを使用する場合:

    • 優先する方法:params でペイロードを計算し、アクションの実装が params に依存するようにします。
    • それ以外の場合:実装内に assertEvent(event, 'someType') を追加します。
  • v4 の cond:

    • guard に名前を変更し、ガードの実装を setup({ guards: { ... } }) に移します。
  • v4 の interpret:

    • 必要に応じて createActor(machine) と .start() に置き換えます。

注記

公式ドキュメントが曖昧に感じられる場合は、推測するのではなく、XState v5 のソース、課題/ディスカッション、または最小限の再現コード/型テストで動作を確認し、ルールファイルの指針に従ってください。

コメント