mcpField
C
correctness-and-error-handling

correctness-and-error-handling

github.com/cognitedata開発337 回インストール
リポジトリ

correctness-and-error-handling について

アプリの正確性とエラー処理を点検し、データフローや UI 状態、実行時の安全性に関する修正を行うコーディングスキルです。手順に沿ったレビューと、未解決項目の報告を支援します。

主な機能

  1. 01非同期データフローの確認
  2. 02重要経路にある不具合マーカーの修正
  3. 03レンダーエラーバウンダリーの追加
  4. 04非同期エラーと UI 状態の処理
  5. 05外部データの検証と境界条件の保護

活用シーン

  1. 01React アプリのエラー状態の不足を確認
  2. 02CDF のデータ取得と描画を強化
  3. 03effect のリソース解放を確認

インストール

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

$npx skills add https://github.com/cognitedata/dune-skills --skill correctness-and-error-handling

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

詳細

正確性とエラー処理の修正

$ARGUMENTS(引数が指定されていない場合はアプリ全体)の正確性に関する問題と不足しているエラー処理を見つけて修正します。以下のすべての手順に沿って作業してください。各手順で問題を検索し、その場で修正します。自動修正できない問題のみを報告してください。


手順 1 — データフローを把握して既知の不具合を修正する

確認を始める前に、以下のファイルを読み込んでください。

  • src/main.tsx / src/App.tsx — トップレベルのエラーバウンダリと認証フロー
  • **/hooks/*.ts、**/contexts/*.tsx に一致するすべてのファイル — 共有される非同期状態
  • **/api/*.ts、**/services/*.ts に一致するすべてのファイル — CDF SDK の呼び出し箇所

各非同期データソースについて、以下を確認してください。

  • リクエストが失敗した場合(ネットワークエラー、CDF 403、タイムアウト)にどうなるか
  • 読み込み中に UI に何が表示されるか
  • 結果が空の場合に UI に何が表示されるか
重要な処理経路にある既知の不具合を見つけて修正する
# Find TODO/FIXME/HACK in critical code paths (not test files)
grep -rn --include="*.ts" --include="*.tsx" -E "(TODO|FIXME|HACK|XXX):" src/ | grep -v ".test." | grep -v ".spec."

# Find "fix" or "broken" or "workaround" markers
grep -rn --include="*.ts" --include="*.tsx" -i -E "(TODO.*fix|workaround|broken|known.?bug|temporary.?hack)" src/

データ取得、レンダリング、認証、ナビゲーションなど、重要な処理経路で一致した箇所ごとに、以下を行ってください。

  1. 前後のコードを読んで、未完了または壊れた動作を把握します。
  2. 根本的な問題を修正します。不足しているロジックを実装するか、壊れた動作を直すか、適切なエラー処理を追加してください。
  3. 修正にこのスキルの範囲を超える大幅なアーキテクチャ変更が必要な場合は、TODO を安全なフォールバックに置き換えます。適切にエラーを処理し、妥当な代替値を返すか、機能が制限されることをユーザーに明示するメッセージを表示してください。
  4. 修正後は TODO/FIXME/HACK コメントを削除してください。コード自体で動作が分かるようにします。

重要な処理経路に TODO を残さないでください。すべて解決するか、安全なフォールバックに置き換える必要があります。


手順 2 — トップレベルのエラーバウンダリを追加する

すべての Dune アプリには、メインコンテンツを囲む React Error Boundary が少なくとも 1 つ必要です。これにより、予期しないレンダリング時の例外が発生しても、空白画面ではなくユーザーに分かりやすいメッセージを表示できます。

grep -rn --include="*.tsx" --include="*.ts" -E "ErrorBoundary|componentDidCatch|getDerivedStateFromError" src/

エラーバウンダリが存在しない場合は、ErrorFallback コンポーネントを作成して App.tsx に ErrorBoundary ラッパーを追加してください。インストールされていない場合は react-error-boundary をインストールします。

pnpm add react-error-boundary

次に App.tsx に追加してください。

import { ErrorBoundary } from "react-error-boundary";

function ErrorFallback({ error }: { error: Error }) {
  return (
    <div role="alert" className="p-8 text-center">
      <p className="text-lg font-semibold">Something went wrong</p>
      <pre className="mt-2 text-sm text-muted-foreground">{error.message}</pre>
    </div>
  );
}

// Wrap the main content:
<ErrorBoundary FallbackComponent={ErrorFallback}>
  <MainContent />
</ErrorBoundary>

ErrorBoundary を配置した状態で、更新済みの App.tsx を書き込んでください。提案するだけでなく、実際に編集してください。


手順 3 — 未処理の非同期関数を try/catch で囲む

エラー処理がない async 関数と Promise チェーンをすべて検索してください。

# Find async functions
grep -rn --include="*.ts" --include="*.tsx" -E "async\s+function|async\s+\(" src/

# Find .then() without .catch()
grep -rn --include="*.ts" --include="*.tsx" -E "\.then\(" src/ | grep -v "\.catch\("

それぞれ修正してください。

  • try/catch がない単独の async 関数は、関数本体を try/catch で囲みます。エラーをコンテキストとともにログに記録し、呼び出し元やクエリレイヤーで処理できるよう再スローしてください。
async function fetchAssets(sdk: CogniteClient) {
  try {
    const result = await sdk.assets.list({ limit: 100 });
    return result.items;
  } catch (error) {
    console.error("Failed to fetch assets:", error);
    throw error;
  }
}
  • .catch() のない .then() には、チェーンに .catch() を追加します。
somePromise.then(handleResult).catch((error) => {
  console.error("Operation failed:", error);
});
  • エラー処理のない TanStack Query の利用箇所(useQuery/useMutation)には、コンポーネントに エラーチェックとエラー UI を追加します。
const { data, isLoading, isError, error } = useQuery({
  queryKey: ["assets"],
  queryFn: () => fetchAssets(sdk),
});

if (isError) return <ErrorMessage error={error} />;

各ファイルを読み、編集して書き戻してください。


手順 4 — コンポーネントに不足している読み込み中・エラー・空状態を追加する

データを取得する各コンポーネントには、以下の 3 つの UI 状態が必要です。

状態必須の UI
読み込み中スピナー、スケルトン、または読み込みインジケーター
エラーユーザーが読めるメッセージ(生のエラーオブジェクトや空白ではないもの)
空「結果がありません」/「まだ何もありません」というメッセージ(空のリストではないもの)

読み込み状態を確認せずにデータをレンダリングしているコンポーネントを検索してください。

grep -rn --include="*.tsx" -E "\.(map|filter|find)\(" src/ | grep -v "isLoading\|isPending\|skeleton\|Skeleton"

該当するコンポーネントごとにコードを読み、不足している状態を直接追加してください。

  • 読み込み中の状態が不足している場合 — データのレンダリングより前に追加します。
if (isLoading) {
  return <div className="flex items-center justify-center p-8"><Spinner /></div>;
}
  • エラー状態が不足している場合 — 読み込みチェックの後に追加します。
if (isError) {
  return (
    <div role="alert" className="p-4 text-center text-destructive">
      <p>Failed to load data. Please try again.</p>
    </div>
  );
}
  • 空状態が不足している場合 — エラーチェックの後、.map() の前に追加します。
if (!data || data.length === 0) {
  return (
    <div className="p-8 text-center text-muted-foreground">
      <p>No results found.</p>
    </div>
  );
}

読み込み中、エラー、空の順に、既存のデータレンダリングより前に適切なチェックを挿入してください。修正した各ファイルを書き戻してください。


ステップ 5 — 外部データに型の絞り込みを追加する

外部データ(CDF のレスポンス、URL パラメーター、localStorage、JSON.parse)は、使用前に検証する必要があります。TypeScript の型だけでは、実行時の保証にはなりません。

# Find JSON.parse without validation
grep -rn --include="*.ts" --include="*.tsx" -E "JSON\.parse\(" src/

# Find localStorage reads
grep -rn --include="*.ts" --include="*.tsx" -E "localStorage\.(get|set)Item" src/

# Find useSearchParams usage
grep -rn --include="*.ts" --include="*.tsx" -E "useSearchParams|searchParams\.get" src/

それぞれ修正してください:

  • JSON.parse(x) as T — Zod の safeParse に置き換えます:
import { z } from "zod";

const MySchema = z.object({ /* fields */ });
const parseResult = MySchema.safeParse(JSON.parse(raw));
if (!parseResult.success) {
  console.warn("Invalid stored data, using defaults:", parseResult.error);
  return defaultValue;
}
const validated = parseResult.data;
  • searchParams.get("id") に null チェックがない場合 — nullish フォールバックを追加します:
const id = searchParams.get("id") ?? defaultId;
  • localStorage.getItem(key) をそのまま使用している場合 — 型ガードとフォールバックを追加します:
const raw = localStorage.getItem(key);
if (raw === null) return defaultValue;
try {
  const parsed = JSON.parse(raw);
  // validate parsed shape
  return isValidShape(parsed) ? parsed : defaultValue;
} catch {
  return defaultValue;
}

外部データを as MyType でキャストしないでください。実行時の安全性が損なわれます。各ファイルを読み込み、修正して、書き戻してください。


ステップ 6 — null、undefined、安全でない配列アクセスを修正する

CDF から返されたデータ、または props で渡されたデータのプロパティにアクセスするすべてのコンポーネントを読み込みます。

grep -rn --include="*.tsx" --include="*.ts" -E "\w+\[0\]\." src/

見つかった安全でないパターンをそれぞれ修正してください:

  • 安全でないネストされたプロパティアクセス — オプショナルチェーンと nullish coalescing を追加します:
// Before: asset.properties.space.Asset.name
// After:
const name = asset.properties?.["my-space"]?.["Asset"]?.name ?? "Unknown";
  • undefined の可能性がある配列に対するガードのない .map() — nullish フォールバックを追加します:
// Before: items.map(renderItem)
// After:
(items ?? []).map(renderItem)
  • 安全でない配列インデックスアクセス — オプショナルチェーンとともに .at() を使用します:
// Before: items[0].name
// After:
const first = items.at(0)?.name ?? "—";

該当箇所がある各ファイルを読み込み、修正を適用して、書き戻してください。


ステップ 7 — useEffect のクリーンアップ関数を追加する

サブスクリプション、タイマー、イベントリスナー、またはコンポーネントの終了後も実行される可能性がある非同期処理を設定するすべての useEffect は、クリーンアップ関数を返す必要があります。

grep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/

各 useEffect について、クリーンアップが必要か確認し、ない場合はクリーンアップ関数を追加してください:

パターン追加する修正
addEventListenerreturn () => removeEventListener(...) を追加
setInterval / setTimeoutreturn () => clearInterval(id) / clearTimeout(id) を追加
CDF ストリーミング / SSEreturn () => stream.close() を追加
fetch / CDF SDK 呼び出しAbortController を追加します:先頭に const controller = new AbortController() を置き、controller.signal を fetch に渡し、return () => controller.abort() を追加して、状態更新を if (!controller.signal.aborted) でガードします
Zustand / イベントエミッターのサブスクリプションreturn () => unsubscribe() を追加

非同期エフェクトの参考パターン:

useEffect(() => {
  const controller = new AbortController();

  async function load() {
    try {
      const data = await fetchWithSignal(controller.signal);
      if (!controller.signal.aborted) setState(data);
    } catch (err) {
      if (err instanceof Error && err.name !== "AbortError") {
        setError(err);
      }
    }
  }

  load();
  return () => controller.abort();
}, [id]);

各エフェクトを読み込み、不足しているクリーンアップを追加して、ファイルを書き戻してください。


ステップ 8 — エッジケースのガードを追加する

各機能について確認し、次のガードを追加してください:

  • データが空の場合:0 件のリストが処理されていない場合は、レンダリング前に空状態のチェックを追加します。
  • 単一アイテム:リストのレンダリングで、エントリーが 1 件の場合に境界値のバグが発生するなら、ロジックを修正します。
  • 最大データ数 / ページネーション:CDF が limit 件すべてを返し、さらにページがある場合は、ページネーションがユーザーに伝わるようにします。不足していれば、「さらに読み込む」またはページネーションのインジケーターを追加します。
  • 同時リクエスト / 古い結果:前のリクエストの完了前にユーザーが新しいリクエストを実行できる場合は、古いリクエストをキャンセルする仕組み(AbortController またはリクエスト ID チェック)を追加します。
  • ネットワークオフライン:オフライン時にアプリが何も通知せず失敗する場合は、適切なエラーメッセージを追加します。

Atlas ツールの execute 関数については、すべての execute 関数の先頭に引数の検証を追加してください:

execute: async (args) => {
  if (!args.assetId || typeof args.assetId !== "string") {
    return { output: "Missing or invalid assetId", details: null };
  }
  // ... safe to proceed
}

execute 関数を検索し、それぞれを読み込み、検証を追加して、ファイルを書き戻してください。


ステップ 9 — 残りの指摘事項を報告する

次の内容を含む構造化レポートを作成してください:

  1. 各ステップで修正した内容 — 実施した変更(編集したファイル、修正したパターン)を要約します。
  2. 残りの問題 — 自動修正できなかった問題のみを記載します(例:アーキテクチャの変更が必要な問題、プロダクト上の判断が必要な問題、このスキルの範囲外の問題)。
重要度ファイル行問題状態
HIGHsrc/hooks/useAssets.ts34未処理の Promise 拒否FIXED — try/catch でラップ済み
MEDIUMsrc/components/AssetList.tsx12空状態がないFIXED — 空状態のチェックを追加済み
MEDIUMsrc/auth/flow.ts45認証エラーの処理にプロダクト上の判断が必要UNFIXED — チームの判断が必要

あるステップで問題が見つからなかった場合は、そのステップに「問題は見つかりませんでした」と記載してください。ステップを黙って省略しないでください。


完了

修正内容を重大度別にまとめてください。データ損失、本番環境でのクラッシュ、または誤解を招く UI の状態につながる可能性がある、未解決の HIGH の問題があれば、最優先で対応できるよう最初に列挙してください。

コメント