マキモノ
Google Workspace無料✅ 公式検証済みv1.0.0 / 更新

本番共有スプレッドシートを壊さず一括更新する型(GAS)

複数人が使っている本番シートに対し、AIが担当者名の一括置換や誤登録行の削除を安全に行うための設計。dryRun→照合キー付き適用→独立read-backの3段と、GASのコード雛形・実際に踏んだ落とし穴つき。

出品者: kim@orgiast.jp3 DL📖 読込 約2,806トークン (約4円)💰 コスパ 12
トークン節約メーター73%節約
ゼロからAIに作らせた場合4.5万トークン
このMDを読ませた場合1.2万トークン

3.3万トークンの節約 (API料金換算で約50円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。

この巻物について

本番共有スプレッドシートを壊さず一括更新する型(GAS)」は、Google WorkspaceカテゴリのAI指示書(MDファイル)です。複数人が使っている本番シートに対し、AIが担当者名の一括置換や誤登録行の削除を安全に行うための設計。dryRun→照合キー付き適用→独立read-backの3段と、GASのコード雛形・実際に踏んだ落とし穴つき。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.3万トークン(API料金換算で約50円)・73%のトークンを節約できます。

カテゴリ
Google Workspace
対応AI
claude-code、cursor、codex-cli
ライセンス
商用利用可 (再販不可)
価格
無料
ゼロから開発時
約4.5万トークン
この巻物使用時
約1.2万トークン
節約量
約3.3万トークン (約50円)
更新日
2026-08-19

使い方 (AIに渡す3つの方法)

いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/gas/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

他人が使っている本番スプレッドシートを壊さずに一括更新する(GAS)

複数人が日常業務で使っている共有スプレッドシートに対して、AI エージェントが 「担当者名を一括置換する」「誤登録した行を消す」といった破壊的な変更を安全に行うための型。

対象: Google Apps Script(clasp 管理 / Drive コマンドキュー方式など、AI から関数を呼べる構成)。 前提知識: dryRunread-back verify を必ずセットで設計すること。


1. 設計原則(これを外すと事故る)

原則理由
変更はホワイトリスト登録した専用関数にする汎用の「任意セルに書く」コマンドを AI に持たせない。事故の上限を関数の仕様で縛る
opts.dryRun を用意し、先に必ず dryRun で対象一覧を出す何件・どの行が変わるかを人間が読める形で確認してから適用する
期待値の照合キーを引数で必須にする「この行は本当に消していい行か」を関数側で検証する。行番号だけを信じない
件数上限を関数内に持つ(例 50件)ロジックのバグで全行書き換えるのを物理的に防ぐ
行削除は降順で行う昇順だと削除のたびに行番号がずれる
書き込み後 SpreadsheetApp.flush()同じ関数内で読み直して照合setValue は保護範囲・データ検証・結合セルで silent ignore されることがある
別コマンドでもう一度読み直す(独立 read-back)関数の戻り値だけを信じない。呼び出し側から見た最終状態を確認する
隣接列・隣接行に触れないことを明示して検証する「担当A列だけ変えるつもりが担当B列も変えた」を防ぐ

2. 一括置換コマンドの雛形

/**
 * 指定シートの1行分(横並びのブロック構造)を走査し、対象セルだけを置換する。
 * @param {string[]} oldValues 置換対象の値(表記ゆれは正規化して比較)
 * @param {string}   newValue  新しい値
 * @param {{dryRun:boolean}} opts
 */
function admin_replaceInRow(oldValues, newValue, opts) {
  if (!Array.isArray(oldValues) || oldValues.length === 0) throw new Error('oldValues が空です');
  const replacement = String(newValue || '').trim();
  if (!replacement) throw new Error('newValue は必須です');
  const dryRun = Boolean(opts && opts.dryRun === true);

  const TARGET_ROW  = 8;   // 書き換える行
  const FIRST_COL   = 13;  // ブロック開始列
  const BLOCK_WIDTH = 3;   // ブロックの列数(1列目だけを対象にする)
  const LABEL_ROW   = 2;   // 空ブロック判定に使う行
  const MAX_CHANGES = 50;

  // 全角/半角スペースを除去して比較する(「山田 太郎」と「山田太郎」を同一視)
  const norm = function (v) { return String(v || '').replace(/[\s ]+/g, ''); };
  const targets = {};
  oldValues.forEach(function (v) { targets[norm(v)] = true; });

  const sheet = SpreadsheetApp.openById('<スプレッドシートID>').getSheetByName('<シート名>');
  if (!sheet) throw new Error('シートが見つかりません');
  const lastCol = sheet.getLastColumn();
  const width = Math.max(0, lastCol - FIRST_COL + 1);
  const labels = sheet.getRange(LABEL_ROW, FIRST_COL, 1, width).getValues()[0];
  const values = sheet.getRange(TARGET_ROW, FIRST_COL, 1, width).getValues()[0];

  const changes = [];
  for (let col = FIRST_COL; col + BLOCK_WIDTH - 1 <= lastCol; col += BLOCK_WIDTH) {
    const idx = col - FIRST_COL;
    if (!String(labels[idx] || '').trim()) continue;          // 未使用ブロックは飛ばす
    const before = String(values[idx] || '');
    // 「担当:山田太郎」のようなプレフィックスは保持したまま氏名部分だけ差し替える
    const m = before.match(/^(<プレフィックス>[::])/);
    const prefix = m ? m[1] : '';
    if (!targets[norm(prefix ? before.slice(prefix.length) : before)]) continue;
    changes.push({ col: col, before: before, after: prefix + replacement });
  }
  if (changes.length > MAX_CHANGES) throw new Error('上限超過: ' + changes.length);

  const failed = [];
  if (!dryRun && changes.length) {
    changes.forEach(function (c) { sheet.getRange(TARGET_ROW, c.col).setValue(c.after); });
    SpreadsheetApp.flush();
    const after = sheet.getRange(TARGET_ROW, FIRST_COL, 1, width).getValues()[0];
    changes.forEach(function (c) {
      const actual = String(after[c.col - FIRST_COL] || '');
      if (actual !== c.after) failed.push({ col: c.col, expected: c.after, actual: actual });
    });
  }
  return { ok: true, dryRun: dryRun, changes: changes, failed: failed };
}

呼び出し順:

  1. admin_replaceInRow([...], '<新しい値>', {dryRun: true})changes を人間が確認
  2. 同じ引数で {dryRun: false}
  3. 別コマンドで対象行を読み直し、旧値が 0 件になったことを確認する

3. 誤登録行を消すコマンドの雛形(照合ガード付き)

行番号だけで消さない。「消していい行の目印」を引数で受け取り、一致した行だけ消す。

function admin_deleteRows(rowNums, expectPrefix) {
  if (!Array.isArray(rowNums) || rowNums.length > 5) throw new Error('一度に消せるのは5行までです');
  const prefix = String(expectPrefix || '').trim();
  if (!prefix) throw new Error('expectPrefix は必須です');

  const sheet = SpreadsheetApp.openById('<スプレッドシートID>').getSheetByName('<シート名>');
  const deleted = [], skipped = [];
  rowNums.map(Number).sort(function (a, b) { return b - a; })   // 降順で削除
    .forEach(function (row) {
      const key = String(sheet.getRange(row, 1).getDisplayValue() || '');
      if (key.indexOf(prefix) !== 0) { skipped.push({ row: row, key: key, reason: '照合キー不一致' }); return; }
      sheet.deleteRow(row);
      deleted.push({ row: row, key: key });
    });
  return { ok: true, deleted: deleted, skipped: skipped, lastRowAfter: sheet.getLastRow() };
}

4. 実際に踏んだ落とし穴

  • タイムスタンプ列は文字列で照合できないgetValue()Date を返すので、 JSON で見た 2026-08-18T11:18:16.825Z を照合キーにすると必ず不一致になる。 getDisplayValue() を使い、シート表示どおりの形式(例 2026/08/18 20:18:17)で照合する。 ※ガードが効いて「削除0件・スキップ2件」で返ってきたのは正しい挙動。ここで安易にガードを外さない。
  • 人名・ラベルの表記ゆれ山田 太郎山田太郎、全角コロンと半角コロンが混在する。 比較時だけ正規化し、書き戻すときは元のプレフィックスを保持する。
  • 長時間ジョブは結果が返らない。AI から関数を実行する仕組み(コマンドキュー等)では 実行環境の時間上限(GAS は6分)に当たると結果ファイルが書かれず「無応答」に見える。 重い生成処理と一括更新を同じ実行に載せない。
  • 同じ作業ツリー・同じキューを別の作業者/エージェントが使っていることがある。 結果ファイルが自分のものか、コマンド名で必ず突き合わせる。

5. 完了報告の基準

次の3つが揃うまで「完了」と言わない。

  1. dryRun の対象一覧と、実行後の changes 件数が一致している
  2. failed が空
  3. 別コマンドで読み直した最新状態に、変更前の値が残っていない(件数で示す)

よくある質問

「本番共有スプレッドシートを壊さず一括更新する型(GAS)」とは何ですか?

複数人が使っている本番シートに対し、AIが担当者名の一括置換や誤登録行の削除を安全に行うための設計。dryRun→照合キー付き適用→独立read-backの3段と、GASのコード雛形・実際に踏んだ落とし穴つき。

どれくらいトークン(費用)を節約できますか?

ゼロから開発すると約4.5万トークンかかりますが、この巻物を使えば約1.2万トークンで済みます。差し引き約3.3万トークン(API料金換算で約50円)・73%の節約です。

どうやって使いますか?

無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。

どのAIツールに対応していますか?

claude-code、cursor、codex-cli に対応しています。

商用利用できますか?

ライセンスは「商用利用可 (再販不可)」です。

🤝 自分でAIを動かすのは、まだ不安…という方へ

この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア