bulk-edit について
レコードの一括編集を慎重に進めるワークフロースキルです。対象範囲を定め、不確かな項目を保留し、スナップショットとドライランを経て、対象・保留・範囲外の変更を検証します。
主な機能
- 01対象範囲と単一フィールドの指定
- 02レコードを OK または HOLD に分類
- 03スナップショットと実データでのドライラン
- 04事前承認と復元スクリプトの準備
- 05ID 単位の台帳による再開
- 06対象・保留・範囲外変更の検証
活用シーン
- 01複数レコードの単一フィールドを整える
- 02曖昧なレコードをレビュー用に保留する
- 03中断したバッチを安全に再開する
- 04対象外の意図しない変更を確認する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bhagyamudgal/ skills --skill bulk-editインストール後、Claude Code で/bulk-editを入力して読み込まれたか確認します。
同梱ファイルreferences/github.mdreferences/worked-example.md
詳細
一括編集
「すべての項目でこのフィールドを変更して」という依頼を、証明できないケースは拒否し、中断後も再開でき、触れるべきでなかったレコードも対象に含めて検証する変換にします。書き込みを許可するのは preflight-mutations であり、その周辺はすべて私が担います。
私が防ごうとしている失敗は、クラッシュではありません。対象集団が頼りにしていた違いをひそかにつぶしておきながら、実行が成功を報告することです。以下のゲートが設けられているのは、どれもこの失敗が気づかれにくいからです。
${CLAUDE_SKILL_DIR}/references/worked-example.md では、これらのゲートが設けられたきっかけとなった、4件の惜しい失敗を含む実際の638レコードの実行をたどります。以下のゲートが余計な手順に見えるときは、そちらを読んでください。
1. 対象集団とフィールドを確定する
対象集団は、ほかの人が再実行できるクエリとして定義し、依頼者の言い回しではなくシステムに照らして特定します。「ボード上のすべてのチケット」と「リポジトリ内のすべてのチケット」は別の集合です。その差は、何よりも先に報告する数値です。編集するのは、フィールド1つだけです。2つのフィールドに触れる実行は、2つの台帳を伴う2回の実行です。
対象集団の件数と、候補となるサブセットの件数は別々に数えます。依頼の多くは対象集団を指定していても、実際に変更されるサブセットを意味しており、この2つの数が一致することはほとんどありません。
ゲート。 対象集団は件数付きの再実行可能なクエリであり、候補となるサブセットにも独自の件数があり、フィールドは名前が明記された単一のものです。
2. 変換を記述する前に、すべてのバリエーションを把握する
依頼にある例は1つのバリエーションであり、仕様ではありません。対象集団全体を一度だけローカルにダンプし、実際の形式を頻度別に列挙します。サンプリングも、目視での判断もしません。
実際に使われている区切り文字、デリミター、接頭辞、連結子の数を数え、出現頻度の低い形式も読み込みます。よくある形式は、すでに把握していたものです。変換を壊すのは、珍しい形式です。 サンプルでは見えません。たとえば、3,000件中4回しか現れない連結子こそ、よくある形式とは異なる意味を持つケースです。
形式ごとに、見た目ではなく意味を確認します。見た目が似ていて意味が異なる2つの形式は、落とし穴です。連鎖を表す矢印とアンパサンドが同じ位置にあり、それぞれ「次に」と「および」を意味する場合、両方を同じようにまとめると、後者の意味が失われます。
ゲート。 対象集団内の形式ごとの件数をすべて示せており、最も少ない形式も確認済みで、変換で意味を保つべき形式を記録しています。
3. 拒否できる変換を書く
パターン検索ではなく、構造に基づいて変換の対象を限定します。固定された端から照合し、定義された境界で止まるルールなら、レコード内の別のデータには届きません。一方、パターンらしきものを探すルールは、いずれ実際の内容の途中でもそれを見つけます。
各レコードを、理由付きで OK または HOLD に分類します。HOLD は実行の出力であり、実行の失敗ではありません。意味を証明できない形式は拒否します。また、誤った推測をするとレコード内で元に戻せなくなる場合も拒否します。
保留件数を減らすために、変換の適用範囲を広げることは決してありません。返却する保留リストのコストはわずかですが、違いをつぶすと元に戻せません。
ゲート。 対象集団内のすべてのレコードが、理由付きの OK または HOLD になっており、両方の件数の合計が対象集団の件数と一致し、依頼者が読める場所に保留リストが記録されています。
4. スナップショットを取得し、ライブシステムに対してドライランする
実行の直前に、全対象の現在値を新たにスナップショットとして取得し、クリーン操作やチェックアウトでアクセスできないよう、作業ツリーの外に保存します。セクション 2 の調査ダンプはスナップショットではありません。数分、あるいは数時間前のものであり、その差分こそ、計画中に他の誰かが編集したレコードです。
ダンプではなく、ライブシステムに対してドライランします。ドライランの目的は、変換が実際に存在するデータにも引き続き適用できることを確認することです。
ここで、空白やエンコーディングのずれによる誤った不一致が見つかることがあります。レコードがダンプとの等価性チェックに失敗した場合は、ライブ値から変換を導き直し、結果を比較します。結果が計画と一致していれば、そのレコードに問題はなく、ガードが厳しすぎたということです。このチェックのほうが等価性チェックより優れています。記憶している内容ではなく、実際に存在するデータに照らして検証できるからです。
ゲート。 スナップショットにタイムスタンプが付与され、作業ツリーの外に保存され、全対象を網羅していること。ドライランがライブ値に対して実行され、その件数がセクション 3 と整合していること。
5. バッチを承認する
バッチを preflight-mutations に渡し、判定が ready になるまで書き込みを行いません。
復旧計画を立てる前に、プラットフォーム独自の履歴を確認します。多くのシステムでは、以前の値を永続的に、しかも無料で保持しています。たとえば、名前変更を記録する課題トラッカー、監査トリガーがあるテーブル、スナップショット機能のあるファイルシステムなどです。この保持機能があれば、復元に必要な取得済みの以前の値を利用できます。そのため、そうと決めつけず、実際のレコード 1 件で確認し、以前の値を読み戻す正確なクエリを記録します。
保持機能が証明するのは、補償が可能だということです。可逆性の分類が変わるわけではありません。その分類は preflight-mutations が担当します。元のイベントや下流への影響が復元後も残る編集は、以前の値をどれほど問題なく読み戻せても、compensating-only のままです。分類は自分で引き上げず、根拠を提示してそのスキルに判定を任せます。自分で引き上げると、必要な最新の確認を省略してしまうためです。
また、復元手順は説明だけでなく、実行可能なスクリプトとして用意します。セクション 4 のスナップショットから復元し、現在値が新しい値でも元の値でもないレコードは拒否することで、同僚が後から行った編集がロールバックで失われないようにします。
ゲート。 判定が ready であり、実際のレコード 1 件で復旧手順が実証され、復元スクリプトが存在し、ドライランモードで実行できること。
6. 実行する
まずカナリアレコードを 1 件書き込み、利用可能なすべての独立した経路で検証します。書き込み応答、新たに取得した値、プラットフォームの履歴を確認します。それから初めて残りを実行します。カナリアは 1 件で済み、そうしなければ全レコードに発生する可能性がある種類のエラーをまとめて検出できます。
API が提供する書き込み経路の中から最もコストの低いものを選び、どのクォータを消費するか確認します。多くのシステムでは、独立して上限に達する複数のバケットに予算が分かれているため、わかりやすいクライアントコマンドと、それが内部で呼び出す REST 呼び出しとで、コストが数倍異なることがあります。書き込み応答で更新後のレコードが返る場合、書き込みと検証を 3 回ではなく 1 回の呼び出しで済ませられます。GitHub の具体的な数値は references/github.md に記載されています。
ループの実行ペースを調整し、残りの予算を定期的に確認します。上限に達して拒否されて初めて気づくのではなく、残りが少なくなったら待機します。対象の途中で拒否されると、一部だけ編集された状態が残ります。これが最もコストの高い失敗です。
台帳はレコード ID をキーにして、試行のたびに追記し、すぐにフラッシュします。再開時には台帳を読み込み、反映済みの項目をスキップします。ゼロから再開すると決めつけることはありません。
ゲート。 カナリアが 2 つ以上の独立した経路で検証され、台帳が候補セットの全レコードを記録し、すべてのエラーに件数だけでなく応答テキストが含まれていること。
7. 対象にしなかったものも含め、対象全体を検証する
信頼できる情報源から対象全体を読み直します。検索インデックスや一覧エンドポイントは、書き込みの反映が数分遅れることが多く、正常な実行を失敗として報告したり、失敗した実行を正常として報告したりします。検索結果ではなく、オブジェクトを読み取ります。
数値は1つではなく、3つ報告します。
| チェック | 確認事項 | 失敗が意味すること |
|---|---|---|
| 対象 | すべての OK レコードに、計画した値が設定されていますか? | 実行が未完了です |
| 保留 | すべての HOLD レコードに変更はありませんか? | 変換が設計した範囲を超えています |
| 付随変更 | 候補セット外のレコードで、値が変わったものはありますか? | 別の処理が同時に編集したか、アンカーの範囲が広がりすぎています |
付随変更のチェックは省略されがちですが、範囲の広すぎる変換を検出できる唯一のチェックです。このチェックなしで「638件中638件の更新に成功」と報告しても、変更されたのが638件だけだとは示せていません。
失敗に見えて実際にはそうではない、少数のレコードが残ることは想定しています。変換が意図的に対象外としている位置に、そのパターンが含まれていたレコードです。それぞれについて変更前と変更後を示し、合計に埋もれさせず、なぜ正しいのかを説明します。
ゲート。 3つのチェックすべてについて数値を報告し、例外に見えるものはすべて変更前と変更後を示しています。
8. これを元に戻す処理の実行元を見つける
定期的なジョブが管理するフィールドは、整理しても再び値が設定されます。作業完了を報告する前に、このフィールドに継続的に書き込んでいるものは何かを確認し、その答えを鵜呑みにせず検証します。
変更履歴に証拠が残っています。代表的なレコードをいくつか選び、変更イベントを取得して、該当するタイムゾーンで曜日と時間ごとにタイムスタンプを分類します。週次ジョブなら、明確なピークが現れます。人手によるメンテナンスなら、変更時刻は分散します。
書き込みに使われたアカウントから、ジョブの存在は分かりますが、実行場所までは分かりません。一般ユーザーアカウントで規則的に実行されている場合、外部ホスト上の cron でも、そのユーザーの保存済みトークンで認証するスケジュール済みワークフローでもありえます。そのため、リポジトリ独自の自動化設定と、実行されている可能性のあるホストを別々に調べ、アカウントの種類だけを根拠にどちらかを除外することはしません。
ジョブの場所を誰かが示した場合は、スケジュールとは別に場所を検証します。この2つの主張はそれぞれ独立して誤りである可能性があり、スケジュールが正しいからといって、パスも正しいとは限りません。利用できないメモリーシステムから思い出したパスは、あくまで仮説です。
ここでは速度よりも順序が重要です。 実行元が、ある情報源とフィールドの値が一致しないレコードを書き換えるのであれば、すべてのレコードをその情報源と不一致にする編集は、そのジョブに最大量の処理対象を渡すことになります。データの整理と実行元の変更には順序があり、逆順に行うと、何もしない場合よりも余計な書き換えが増えます。そのまま進めず、理由を明確に伝えます。
ゲート。 証拠を示して実行元を特定しているか、調査したものの見つからなかったことを明記しています。実行元が存在し、現在も稼働中の場合は、次の編集を勧める前に、必要な順序に関する制約を報告しています。
報告
実行内容、3つの検証数値、理由を添えた保留リスト、バックアップの保存場所と復元コマンド、意図的に実施しなかったことを報告します。理由のない保留件数だけでは、依頼者が対応に活用できません。
完了。 候補セットを編集して検証し、保留対象は未編集のまま報告し、付随変更のチェックは問題なく、ロールバック手順を実行できます。