概要

項目内容
案件 IDMAS-176 (旧 F-176)
案件名開発者向け AI ツール投資 ROI 管理(D.8 派生)
カテゴリ自動化 / FP&A 派生
PhaseP1.5
優先度★★★
所要時間約 1.5-2 週間(週 10h 前提・Step 1-4 段階適用可)
対象レイヤー🌐 Cross (Domain + Data + Report)
対象ファイル(変更)100_config/101_sys_config.jsBUD_SUBS ヘッダー 2 列追加 + 入力列番号配列の更新)/ 200_data/202_repository.jsSubscriptionRepository 新設・HeadcountRepository.findAll は既存パターン流用)/ 600_report/609_datamart_kpi.js(K7 行追加 + ヘルパー行 35 追加)/ 000_infra/003_contracts.jsSubscriptionDTO JSDoc 型定義追加)
対象ファイル(新規)400_domain/450_ai_tool_roi_engine.jsAiToolRoiEngine 名前空間・約 150 行・新規ファイル番号 450)
新規 03_sys_params キーMAS176_HOURLY_RATE_BASIS (default EXEC_ANNUAL_DIV_HOURS) / MAS176_EXEC_MEMBER_NAME (default 代表取締役) / MAS176_EXEC_ANNUAL_HOURS (default 2000) / MAS176_ROI_WARN_THRESHOLD (default 2.0) / MAS176_ROI_TARGET_THRESHOLD (default 4.0) / FF_UC8_S01_CALC_ROI (default trueADR-0028/0029 Feature Flag 命名規約準拠)
前提案件MAS-003(KPI ダッシュボード基盤・✅ 完了)/ MAS-048(HC TCO シミュレーター・✅ 完了。HeadcountRepository.findAll パターン参考)
後続連携MAS-013(NPV/IRR 一時投資判定)と棲み分け。MAS-230(Jr 採用)後の Jr 学習教材(生産性測定の体感)

目的

月 2.5 万円の AI ツール投資(Cursor Pro / Claude Max 5x / GitHub Copilot Pro / v0 / ChatGPT Plus 等)が月 10 万円相当の生産性を生むことを定量証明し、継続的 AI ツール投資の正当化と予算確保を行う。

  • 23_bud_subscription想定月間削減時間 列と 課金サイクル 列を追加し、ツールごとの主観入力エビデンスを蓄積(年額契約と月額契約の混在を月額換算で正規化)
  • 役員年収÷年間稼働時間で算出した時間単価を乗じて月次の 想定削減金額(≒ 投資効果) を算出
  • 月額コスト(税込)と比較した ROI 倍率93_kpi_dashboardK7 として常時表示
  • ROI 閾値(warn=2.0x / target=4.0x)でアラート表示し、コスパ低下したツールの解約判断を促す
  • MAS-013(大型一時投資 NPV/IRR)と棲み分け: 本案件は月額 SaaS マイクロ継続投資をサブスクマスタ連動で月次自動モニタリング

修正方針

Phase 0: 実データ Read 裏取り(着手前必須・failure_patterns #18-#20 直接対策)

Step 1 着手前に以下のファイルを Read し、仕様書本文の「実データ検証」セクションに Read 結果のコードスニペットを inline 貼付してから実装に進む。検証完了前は Step 1 以降を凍結。

  1. 100_config/101_sys_config.jsBUD_SUBS スキーマ定義部(ヘッダー配列・inputCols / autoCols の実値)
  2. 200_data/202_repository.jsHeadcountRepository.findAll パターンと readSheetAsDtos_ ヘルパー実装
  3. 22_bud_headcountDDL ヘッダー定義(氏名・ポジション が単一列か、氏名 / ポジション / 雇用形態 等が分離列かを確認)
  4. メニュー定義の実在ファイル: grep -rn 'マイグレーション' 000_infra/ 100_config/ 300_ui/ で実存ファイルとデータ構造(MENU_DEFINITION 配列 / onOpen()ui.createMenu() 等)を特定
  5. migrationF66DividendSeed 関数の実在ファイル: grep -rn 'function migrationF66DividendSeed' . で実パスを特定
  6. BUD_SUBS利用ステータス 列の dataValidation.values 実値(解約済解約済み か等の表記揺れ確認)

Step 1: 23_bud_subscription に削減時間列・課金サイクル列を追加(DDL 拡張)

100_config/101_sys_config.jsBUD_SUBS ヘッダー定義(現行 24 列・末尾は X 列「最終決済予定日」)の末尾に 以下 2 列を追加し 26 列構成に拡張する

  • 現行ヘッダー(24 列・A 列〜X 列): ["有効フラグ","管理ID","サービス・ツール名","利用者・部門","費用科目","取引先名","契約形態","開始・契約年月","次回更新・終了年月","税抜金額_計画","消費税額_計画","税区分","決済手段","決済ラグ(月)","支払基準日","休日調整","CF計上","自動更新アラート","利用ステータス","組織名","備考","起票ターゲット月","最終起票年月日","最終決済予定日"]
  • 追加 25 列目(Y 列): 課金サイクル / のいずれか・dataValidation で固定。月額換算のための正規化キー)
  • 追加 26 列目(Z 列): 想定月間削減時間est_hours_saved_per_month 想定 / 単位: hour)

101_sys_config.jsBUD_SUBSinputCols / autoCols 配列は、Phase 0 で取得した実値の 配列末尾に 25 および 26 を追加 する(行番号・現行配列リテラルのハードコードは避け、論理的にロケートする):

  • inputCols25(Y 列・課金サイクル)と 26(Z 列・想定月間削減時間)を追加(手入力)
  • autoCols は不変

整合性チェック: inputColsautoCols の更新は 同一の Edit ブロックで両方更新(failure_patterns #25 並列実装対称性)。

Step 2: SubscriptionRepository 新設 + DTO 定義(既存パターン踏襲)

Step 2-A: 000_infra/003_contracts.js に DTO 型定義追加

ADR-0028/0029 のデータ境界要件に基づき、SubscriptionDTO の JSDoc 型定義を 003_contracts.js に追加:

/**
 * @typedef {Object} SubscriptionDTO
 * @property {boolean} 有効フラグ
 * @property {string} 管理ID
 * @property {string} サービス・ツール名
 * @property {string} 利用者・部門
 * @property {string} 費用科目
 * @property {string} 取引先名
 * @property {string} 契約形態
 * @property {string|Date} 開始・契約年月
 * @property {string|Date} 次回更新・終了年月
 * @property {number} 税抜金額_計画
 * @property {number} 消費税額_計画
 * @property {string} 税区分
 * @property {string} 決済手段
 * @property {number} 決済ラグ(月)
 * @property {number} 支払基準日
 * @property {string} 休日調整
 * @property {string} CF計上
 * @property {string} 自動更新アラート
 * @property {string} 利用ステータス
 * @property {string} 組織名
 * @property {string} 備考
 * @property {string|Date} 起票ターゲット月
 * @property {string|Date} 最終起票年月日
 * @property {string|Date} 最終決済予定日
 * @property {string} 課金サイクル - '月' or '年'
 * @property {number} 想定月間削減時間 - hour/month
 */

Step 2-B: 200_data/202_repository.jsSubscriptionRepository 追加

HeadcountRepository パターンを参考にしつつ、他リポ(OrderRepository 等)の findAll / save / append 3 メソッド構成と対称化(failure_patterns #25):

var SubscriptionRepository = {
  _getSheet: function() {
    return Utils.getSheetByKey('BUD_SUBS', '23_bud_subscription');
  },
  /**
   * @returns {{ headers: string[], dtos: SubscriptionDTO[] }}
   * Read-only entry point for MAS-176. save/append は将来 RPA 拡張時に実装。
   */
  findAll: function() {
    return readSheetAsDtos_(SubscriptionRepository._getSheet());
  },
  /** Read-only by design - MAS-176 v1. RPA 起票ロジックは現状 02_rpa_subscription.js 側で直接シート参照のため未使用。 */
  save: function(dto) {
    throw new Error('SubscriptionRepository.save: not implemented in MAS-176 v1');
  },
  append: function(dto) {
    throw new Error('SubscriptionRepository.append: not implemented in MAS-176 v1');
  }
};

呼び出し側は dto['有効フラグ'] で FALSE スキップ、dto['想定月間削減時間'] で時間取得、dto['課金サイクル'] で月額換算、dto['税抜金額_計画'] + dto['消費税額_計画'] で原価取得。

Step 3: 400_domain/450_ai_tool_roi_engine.js 新設(計算エンジン)

新規ファイル番号 450(449 配当ミックスの次・451 multiyear_planner 手前)。

var AiToolRoiEngine = (function() {
  /** 役員時間単価を算出: 年収 / 年間稼働時間。Infinity/NaN scrub は上流で実施。 */
  function _calcExecHourlyRate_() {
    var basis = String(Constants.getParam('MAS176_HOURLY_RATE_BASIS', 'EXEC_ANNUAL_DIV_HOURS'));
    var execName = String(Constants.getParam('MAS176_EXEC_MEMBER_NAME', '代表取締役'));
    var annualHours = Number(Constants.getParam('MAS176_EXEC_ANNUAL_HOURS', 2000));
    if (!Number.isFinite(annualHours) || annualHours <= 0) {
      Utils.persistLog('WARN', 'AiToolRoiEngine._calcExecHourlyRate_', 'annualHours <= 0, fallback to 2000', '');
      annualHours = 2000;
    }

    var hcResult = HeadcountRepository.findAll();
    var monthlySalarySum = 0;
    // 注: Phase 0 で 22_bud_headcount の実列名を確認。氏名・ポジションが分離列の場合は AND 条件に変更。
    for (var i = 0; i < hcResult.dtos.length; i++) {
      var dto = hcResult.dtos[i];
      var flag = dto['有効フラグ'];
      if (flag === false || String(flag).toUpperCase() === 'FALSE') continue;
      var name = String(dto['氏名・ポジション'] || '').trim();
      if (name !== execName) continue;
      monthlySalarySum += Number(dto['月額給与・報酬']) || 0;
    }
    var annualSalary = monthlySalarySum * 12;
    var hourlyRate = annualHours > 0 ? annualSalary / annualHours : 0;
    // 上流で Infinity/NaN を scrub(per-tool ロジック簡潔化)
    if (!Number.isFinite(hourlyRate)) {
      Utils.persistLog('WARN', 'AiToolRoiEngine._calcExecHourlyRate_', 'Non-finite hourlyRate scrubbed to 0', '');
      hourlyRate = 0;
    }
    return { hourlyRate: hourlyRate, basis: basis, execName: execName, annualHours: annualHours, annualSalary: annualSalary };
  }

  /** 月額正規化: 課金サイクル='年' の場合は 1/12。 */
  function _normalizeMonthlyCost_(dto) {
    var raw = (Number(dto['税抜金額_計画']) || 0) + (Number(dto['消費税額_計画']) || 0);
    var cycle = String(dto['課金サイクル'] || '月').trim();
    return cycle === '年' ? raw / 12 : raw;
  }

  /** 全 AI ツール (有効フラグ=TRUE & 想定月間削減時間 > 0) の月次 ROI を集計 */
  function calculateMonthlyRoi() {
    var enabled = String(Constants.getParam('FF_UC8_S01_CALC_ROI', 'true'));
    if (!/^(true|1|yes)$/i.test(enabled)) {
      return { totalCost: 0, totalSavedHours: 0, totalSavedAmount: 0, roi: 0, hourlyRate: 0, items: [], disabled: true };
    }
    var rateInfo = _calcExecHourlyRate_();
    var subResult = SubscriptionRepository.findAll();
    var totalCost = 0, totalSavedHours = 0, items = [];
    for (var i = 0; i < subResult.dtos.length; i++) {
      var dto = subResult.dtos[i];
      var flag = dto['有効フラグ'];
      if (flag === false || String(flag).toUpperCase() === 'FALSE') continue;
      var status = String(dto['利用ステータス'] || '').trim();
      // Phase 0 で確定した「解約済」相当値で除外(dataValidation 実値に合わせる)
      if (status === '解約済' || status === '解約済み') continue;
      var savedHours = Number(dto['想定月間削減時間']) || 0;
      if (savedHours <= 0) continue;
      var cost = _normalizeMonthlyCost_(dto);
      totalCost += cost;
      totalSavedHours += savedHours;
      var itemRoi = cost > 0 ? (savedHours * rateInfo.hourlyRate) / cost : 0;
      if (!Number.isFinite(itemRoi)) itemRoi = 0;
      items.push({
        toolName: String(dto['サービス・ツール名'] || '').trim(),
        cost: cost,
        savedHours: savedHours,
        savedAmount: savedHours * rateInfo.hourlyRate,
        roi: itemRoi
      });
    }
    var totalSavedAmount = totalSavedHours * rateInfo.hourlyRate;
    var roi = totalCost > 0 ? totalSavedAmount / totalCost : 0;
    if (!Number.isFinite(roi)) roi = 0;
    return { totalCost: totalCost, totalSavedHours: totalSavedHours, totalSavedAmount: totalSavedAmount, roi: roi,
             hourlyRate: rateInfo.hourlyRate, hourlyRateInfo: rateInfo, items: items, disabled: false };
  }

  return { calculateMonthlyRoi: calculateMonthlyRoi };
})();

性能監視: 609_datamart_kpi.js から本エンジンを呼び出す際、メイン処理ブロックを Utils.measureRuntime_ でラップ(GAS 6分制限・将来のパフォーマンス劣化監視)。

Step 4: 93_kpi_dashboardK7 AI ツール ROI を追加

600_report/609_datamart_kpi.jsrenderKpiRows_rows 配列末尾に K7 ブロックを追加する(K6 ランウェイの後)。MAS-003 失敗パターン #21-#24 回避のため、KPI 値は GAS 側で AiToolRoiEngine.calculateMonthlyRoi() を実行し、リテラル数値として setValue() する(数式ではなくスカラー埋め込み)。

  • FF_UC8_S01_CALC_ROI = false 時の Early Return: フラグ OFF 時は K7 セクション自体を生成・描画しない(行追加もスキップ)。
  • K7 main 行: 当月・前月・YTD はいずれも roi 倍率(小数表示)。range.setNumberFormat('0.0"x"')4.0x 形式表示。
  • sub 行 1: ツール総月額コスト(税込・円・年額契約は月額換算後の値)
  • sub 行 2: 想定削減時間合計(時間/月)
  • sub 行 3: 想定削減金額(円/月)
  • sub 行 4: 役員時間単価(円/h)
  • subsub: 各ツールの個別 ROI(ツール名・月額・削減時間・ROI 倍率)

renderKpiHelperRows_ には [helper] K7 行を追加し、SPARKLINE 用列を確保するが、v1 では SPARKLINE 自体を出力しない(過去月スナップショットがないため水平直線になる UX バグ回避)。単一セルで当月 ROI のみ表示し、TODO コメントで「v2 で 99_ai_tool_roi_snap シート新設による月次スナップショット連動」を記録。

applyKpiConditionalFormat_ に以下を追加:

  • ROI < MAS176_ROI_WARN_THRESHOLD(default 2.0)→ 赤系背景
  • ROI ≥ MAS176_ROI_TARGET_THRESHOLD(default 4.0)→ 緑系背景
  • 中間(2.0 ≤ ROI < 4.0)→ 黄系背景

セル保護: K7 行は range.protect().setWarningOnly(true) で「GAS 書込のみ許容」を運用上明示(人手編集時の警告表示)。

Step 5: migrationMAS176SysParams 投入関数(800_ops/810_migration_mas176.js 新設)

Phase 0 で確定したメニュー定義の実在ファイル(MENU_DEFINITION 配列 or onOpen()ui.createMenu())に登録する。migrationF66DividendSeed(Phase 0 で実在パス確認)と同じパターンで、6 キーを冪等投入する。

特権操作の監査要件(ADR-0028/0029 準拠):

  • 実行前: SpreadsheetApp.getUi().alert() で確認ダイアログを表示
  • 実行成功時: Utils.auditLog('RUN', 'migrationMAS176SysParams', ...) で監査ログ記録
  • 各キーの投入時: 既存値 → 新値を 99_audit_log に記録(運用変更時の改定証跡)
キーデフォルト値説明
MAS176_HOURLY_RATE_BASISEXEC_ANNUAL_DIV_HOURS時間単価算出方式(v1 は固定。v2 で MARKET_RATE / MAS071_BLENDED / JR_SUBSTITUTE_RATE 等を追加)
MAS176_EXEC_MEMBER_NAME代表取締役22_bud_headcount の氏名・ポジション列で照合(Phase 0 で実列名確定)
MAS176_EXEC_ANNUAL_HOURS2000年間稼働時間(8h × 250 営業日)
MAS176_ROI_WARN_THRESHOLD2.0ROI < この値で赤系警告
MAS176_ROI_TARGET_THRESHOLD4.0ROI ≥ この値で緑系合格
FF_UC8_S01_CALC_ROItrueFeature Flag(K7 行を非表示にする時は false・ADR-0028/0029 命名規約準拠)

実装スコープ

区分ファイル変更量備考
DDL 拡張100_config/101_sys_config.js+6 行BUD_SUBS ヘッダー +2 列・入力列番号 +2
Contracts000_infra/003_contracts.js+30 行SubscriptionDTO JSDoc 型定義
Repository200_data/202_repository.js+20 行SubscriptionRepository 新設(findAll/save/append 3 メソッド対称)
Domain400_domain/450_ai_tool_roi_engine.js+160 行(新規)AiToolRoiEngine 名前空間・月額正規化ヘルパー追加
KPI Render600_report/609_datamart_kpi.js+60 行K7 main + sub 4 + subsub N + helper 行 35 + Feature Flag Early Return + setNumberFormat
Migration800_ops/810_migration_mas176.js+60 行(新規)6 キー冪等投入 + 確認ダイアログ + auditLog
MenuPhase 0 で特定したメニュー実存ファイル+1 行「🔧 マイグレーション」に登録
設定docs/_config.json+1 行nav に新規仕様書登録(必須・failure_patterns #11)

合計: 約 340 行(うち新規 220 行・既存変更 120 行)。

影響範囲

対象種別影響リスク
23_bud_subscription 既存データ構造変更ヘッダー列 +2(Y/Z 列)既存行は Y/Z 列が空欄 → 課金サイクル空→「月」扱い・想定削減時間 = 0 → ROI 計算対象外(後方互換)
setupAllSchemasDDL 再実行Y/Z 列が新設される冪等性あり・既存値の上書きなし
93_kpi_dashboardレイアウトK6 ランウェイの下に K7 が増える行数増加で印刷レイアウト要確認(人間検討事項 #11)
02_rpa_subscription.js (RPA 起票)影響なしY/Z 列は計算用で起票には不使用サブスク INV 起票ロジックは現状維持
400_rpa_common.js の科目マスタ影響なし既存科目を流用(「ソフトウェア利用料」等)新規科目不要
既存テスト 901_test_runner.js拡張T-MAS176-01〜14 を追加既存テストへの影響なし
appsscript.json変更なしCacheService / LockService 等の新規スコープ不要failure_patterns #26 遵守

注意事項

  1. #18-#20(命名造語禁止): SubscriptionRepository / AiToolRoiEngine / MAS176_* / FF_UC8_S01_CALC_ROI キーは既存命名規則と整合する命名。「想定月間削減時間」「課金サイクル」は実装着手前に Phase 0 で BUD_SUBS ヘッダーを再確認。新設の場合は本ヘッダー名を採用し、勝手に「削減工数」「効率化時間」「請求サイクル」等の同義語に差し替えない(科目マスタ照合と同様の完全一致原則)。
  2. #21-#24(Sheets 数式の Unicode / 列範囲膨張): 93_kpi_dashboard の K7 値は GAS 側で AiToolRoiEngine.calculateMonthlyRoi() を実行してスカラー setValue で埋めるMATCH("AI ツール ROI", ...) のような数式ラベル解決は禁止(絵文字・全角スペース・Unicode 正規化で壊れる)。
  3. #25(並列実装対称性): inputCols / autoCols の更新は片側だけにならないよう 同一の Edit ブロックで両方更新SubscriptionRepository は他リポと対称な findAll / save / append 3 メソッド構成(v1 では save / append は未実装エラー throw・読み取り専用を JSDoc 明示)。
  4. #26(oauthScopes 部分宣言禁止): appsscript.json を一切編集しない。本案件は既存スコープ(spreadsheets)のみで完結。
  5. #27(外部 API 仕様変動): 該当なし(外部 API 不使用)。
  6. #29(V8→Java Infinity null): 役員月額給与が空欄・全員退職等で annualHours = 0 となった場合、hourlyRate = annualSalary / 0 = Infinity の risk あり → _calcExecHourlyRate_ 内で 上流 scrubNumber.isFinite(hourlyRate) ガード)。per-tool / 合算 ROI も同様に scrub。
  7. #31(ID 採番衝突): 新規 MAS176_* / FF_UC8_S01_CALC_ROI パラメータキーは投入前に 03_sys_params で grep。新規ファイル番号 450449_dividend_mix_optimizer.js451_multiyear_planner.js の間。810_migration_mas176.js809_backup_tool.js の次(CLAUDE.md ファイル番号体系遵守)。
  8. HeadcountRepository.findAll の有効フラグスキップ: flag === false || String(flag).toUpperCase() === 'FALSE' のパターンを _calcExecHourlyRate_ 内でも踏襲。
  9. 役員特定ロジック: MAS176_EXEC_MEMBER_NAME で氏名・ポジション列の 完全一致 で照合。Phase 0 で 22_bud_headcount の実列名(単一列か分離列か)を確認し、分離列の場合は AND 条件に変更。複数役員(共同代表)がいる場合は v1 では 1 名のみ対応・v2 で複数役員の合算をサポート(人間検討事項 #5)。
  10. 時間単価のフォールバック: 役員レコードが見つからない or 月額給与=0 の場合、hourlyRate=0 → ROI=0 → ダッシュボード上に「⚠️ 役員時間単価未設定」と表示。算出に失敗したツールも Utils.persistLog('WARN', 'AiToolRoiEngine.calculateMonthlyRoi', ...) でログ出力。
  11. 解約済ツールの除外: Phase 0 で BUD_SUBS.利用ステータスdataValidation.values 実値を確認し、解約済 / 解約済み 等の実値で完全一致除外。
  12. MAS-013 との棲み分け: 本案件は月額 SaaS の継続投資。一時的・大型投資(オフィス開設・サーバー購入)は MAS-013(NPV/IRR)で評価。サブスクの契約形態が「単発」(CTR_SPT)の場合は ROI 計算対象外として 継続 / 月額 のみフィルタ(人間検討事項 #6)。
  13. 月額/年額正規化: BUD_SUBS に 課金サイクル 列(Y 列)を新設し、 の場合は (税抜+税)/12 で月額換算。これにより年額一括契約と月額契約を 1 シートで混在管理可能。
  14. 想定削減時間のエビデンス: 主観入力のため精度担保が課題。Step 1 では 23_bud_subscription備考 列(U 列)にエビデンス記述を運用規約化(人間検討事項 #1)。v2 で 99_ai_tool_evidence シートに「いつ・どんなタスクで・何時間削減したか」のジャーナル化を検討。
  15. 個人情報管理: 役員報酬・氏名を扱うため、KPI ダッシュボードの閲覧権限・出力範囲を内部統制規程で別途明文化。役員時間単価は K7 sub 行で表示されるため、社内開示制限が必要な場合は FF_UC8_S01_CALC_ROI を OFF にするか、表示を集計値のみに変更するオプション運用を検討(人間検討事項追加)。
  16. 改定証跡: ROI 閾値(warn/target)や時間単価ベース変更時は migrationMAS176SysParams 実行時に Utils.auditLog で改定日・改定者・旧値/新値を 99_audit_log に記録(監査耐性)。

エッジケース

実装時に必ず以下 14 件を 901_test_runner.js に追加する単体テストでカバーする(テスト名 T-MAS176-01T-MAS176-14)。

#条件検知方法期待される挙動ログ出力
123_bud_subscription想定月間削減時間 列が空欄`Number(dto['想定月間削減時間'])0`
2想定月間削減時間 がマイナス値(誤入力)savedHours <= 0対象外(>0 ガード)Utils.persistLog('WARN', ...)
3役員レコードが 22_bud_headcount に未登録monthlySalarySum === 0hourlyRate = 0 → ROI = 0 → K7 に「⚠️ 役員時間単価未設定」表示Utils.persistLog('WARN', 'AiToolRoiEngine._calcExecHourlyRate_', '役員未登録: ' + execName, '')
4役員月額給与が 0 円(未入力)annualSalary === 0同上同上
5MAS176_EXEC_ANNUAL_HOURS が 0 以下に設定されたannualHours <= 0フォールバック値 2000 を使用Utils.persistLog('WARN', '...', 'annualHours <= 0, fallback to 2000', '')
6サブスク有効フラグ=FALSE 行`flag === falseString(flag).toUpperCase() === 'FALSE'`
7サブスク利用ステータス=「解約済」String(dto['利用ステータス']).trim() === '解約済'スキップ(無効化と同等扱い)
8税抜金額_計画 + 消費税額_計画 = 0(無料プラン)cost === 0items 配列に含めるが個別 ROI は Infinity 回避のため 0 で表示・WARN ログUtils.persistLog('INFO', '...', '無料ツール: ' + toolName, '')
9roi 値が Infinity / NaN!Number.isFinite(roi)上流(_calcExecHourlyRate_)と最終合算の 2 段階で 0 に scrub(failure_patterns #29 直接対策Utils.persistLog('WARN', '...', 'Non-finite ROI scrubbed', toolName)
10FF_UC8_S01_CALC_ROI = falseConstants.getParam('FF_UC8_S01_CALC_ROI')'false'K7 セクション生成自体をスキップ(Early Return)・計算スキップ
11サブスクシートが存在しない(DDL 未実行)_getSheet() が null{ headers: [], dtos: [] } 返却(readSheetAsDtos_ の null guard)
12想定月間削減時間 / 課金サイクル 列がヘッダーに未追加(DDL 未実行)dto['想定月間削減時間'] === undefined`Number(undefined)
13サブスク全件が ROI 計算対象外items.length === 0K7 main 行に「📋 対象ツール未登録」表示
14課金サイクル = 年 の年額契約cycle === '年'(税抜+税)/12 で月額換算してから totalCost に加算

実データ検証

実装着手前(Phase 0)に MCP / GAS エディタで以下を確認し、Read 結果のコードスニペットを本セクションに inline 貼付する(failure_patterns #18-#20 直接対策・コード未読類推禁止)。

1. 23_bud_subscription 現行ヘッダー実態確認

101_sys_config.js の DDL 定義(24 列)と本番シートのヘッダー行を完全一致確認:

有効フラグ | 管理ID | サービス・ツール名 | 利用者・部門 | 費用科目 | 取引先名 | 契約形態 |
開始・契約年月 | 次回更新・終了年月 | 税抜金額_計画 | 消費税額_計画 | 税区分 | 決済手段 |
決済ラグ(月) | 支払基準日 | 休日調整 | CF計上 | 自動更新アラート | 利用ステータス |
組織名 | 備考 | 起票ターゲット月 | 最終起票年月日 | 最終決済予定日

DDL 定義と実シートが乖離している場合、本案件着手前に setupAllSchemas を dev で実行して同期 してから列追加に進む。inputCols / autoCols の実値(配列リテラル)も Read で取得し本セクションに貼付。

2. 22_bud_headcount 役員レコード存在確認

  • 氏名・ポジション単一列か分離列かを実 DDL 定義で確認(仮に 氏名 / ポジション / 雇用形態 の分離だった場合は AND 条件に変更)
  • 該当役員レコードの 有効フラグ = TRUE か
  • 月額給与・報酬 列が空欄でないか・正の数値か
  • 共同代表など複数役員がいる場合、v1 では 1 名固定の制約を運用ガイドに明記

3. 93_kpi_dashboard 既存 K1-K6 行レイアウト確認

600_report/609_datamart_kpi.jsrenderKpiRows_rows 配列定義を Read で確認し、K7 を追加する正しい位置を特定。renderKpiHelperRows_ の helper 行構造を確認し、行 35 を追加する場所を特定。sheet.hideRows(...) の引数も同期更新する。

4. Constants.getParam のシグネチャ確認

000_infra/002_constants.jsConstants.getParam(key, default) の戻り値型を確認。string で返ってくるため Number(...) / /^(true|1|yes)$/i.test(...) で型変換が必要

5. HeadcountRepository.findAll() の戻り値構造確認

200_data/202_repository.js{ headers: string[], dtos: Object[] } 構造を確認。dto 内のキーはヘッダー名そのまま。

6. 400_domain/449_dividend_mix_optimizer.js の名前空間パターン確認

新規ファイル 450_ai_tool_roi_engine.js の構造参考として、IIFE + 名前空間パターン(var XxxEngine = (function() { ... return { ... }; })();)を Read で確認。名前空間内の private ヘルパーは末尾 _ 付きで定義(failure_patterns #28 反映)。

7. 02_rpa_subscription.js への影響確認

400_domain/402_rpa_subscription.jssubSheet = ss.getSheetByName('23_bud_subscription') と直接シート参照しているが、列追加(Y/Z 列)は RPA 起票ロジックに影響しないことを確認(Y/Z 列は計算用で起票には不使用)。

8. メニュー定義実存ファイル確認

grep -rn 'マイグレーション' 000_infra/ 100_config/ 300_ui/ でメニュー定義の実在ファイルとデータ構造(MENU_DEFINITION 配列 / onOpen()ui.createMenu() 等)を特定し、本セクションに実パス・実行番号を貼付。

9. migrationF66DividendSeed 実在ファイル確認

grep -rn 'function migrationF66DividendSeed' . で実パスを特定し、本セクションに貼付。実在しない場合は別の seed 関数(例: migrationF57Seed 等)をパターン参考にする。

10. BUD_SUBS.利用ステータスdataValidation.values 確認

DDL の validations.values 実値を Read で確認し、解約済 / 解約済み / 停止中 等の表記揺れを _calcExecHourlyRate_ の除外条件に正確に反映。

関連ドキュメント

ドキュメント関連箇所
MAS-003 KPI ダッシュボードdev_mas-003_kpi_dashboard.md — K1-K6 既存実装。K7 追加の参考
MAS-048 HC TCO シミュレーターdev_mas-048_hiring_tco_bep_simulator.mdHeadcountRepository.findAll パターン
MAS-013 NPV/IRR 一時投資判定dev_mas-013_npv_irr_investment.md — 棲み分け(一時投資 vs 月額 SaaS)
MAS-066 配当ミックスdev_mas-066_dividend_mix_optimizer.md — 名前空間パターン参考(449_dividend_mix_optimizer.js
MAS-071 稼働率連動型役員報酬v2 で MAS176_HOURLY_RATE_BASIS = 'MAS071_BLENDED' 連携検討(V_d ベース時間単価)
MAS-230 Jr 採用要件定義後続連携(Jr 学習教材としての ROI 体感)
ADR-0028 / ADR-0029Feature Flag 命名規約(FF_UC{N}_S{NN}_{verb} 形式)・特権操作の監査ログ要件
failure_patternsdocs/_internal/failure_patterns.md — #18-20 / #21-24 / #25 / #26 / #29 / #31
dev_spec_prompt_template.md v1.10+仕様書作成標準テンプレート
CLAUDE.mdファイル番号体系・名前空間規約・列参照ヘッダー名ベース
PRD プロダクトポリシーHuman-in-the-Loop(想定削減時間の主観入力 → 月次レビュー)

人間が検討すべき事項

  1. 想定削減時間のエビデンス管理: 主観入力のため精度担保方法を確立する必要あり。v1 は 備考 列に文章で記述・運用規約化。監査耐性強化のため v2 で 99_ai_tool_evidence シート新設してジャーナル化(入力者・タスク内容・削減時間・実施日・編集者・日付)を強く推奨
  2. 時間単価の計算ベース: 役員年収÷年間稼働時間の計算根拠(年間稼働時間 = 8h × 250 営業日 = 2000h)の妥当性。短時間労働の場合や残業多発の場合の補正を検討。MAS176_HOURLY_RATE_BASIS の v2 拡張案: MARKET_RATE(外注ハードルレート)/ JR_SUBSTITUTE_RATE(Jr 採用代替コスト・MAS-230 連動)/ MAS071_BLENDED(MAS-071 稼働率連動 V_d ベース)。
  3. ROI 閾値(warn=2.0x / target=4.0x)の設定値: 業界標準・社内基準の定義。SaaS 投資は一般に ROI 3-5x が目安だが、AI ツールは初期スピードアップが大きいため 4-10x を期待値とする見方もある。実データ蓄積後にチューニング。閾値変更時は 99_audit_log に改定日・改定理由・旧値/新値を記録(監査証跡)。
  4. Claude Code Max の位置づけ: フルタイム Jr 代替として見るか、個別ツール扱いとするか。月額 $200 で Jr 入社(推定 50-80 万円/月)の 1/15-1/25 のコスト → ROI 計算では月 100h 以上の削減を想定しないと過小評価リスク(個別ツール扱いだと 20-30h/月で 4-6x ROI 程度)。
  5. 複数役員対応: v1 では 1 名固定(MAS176_EXEC_MEMBER_NAME)。共同代表・取締役複数の場合は合算するか、最高位役員の単価を使うか。MAS176_EXEC_MEMBER_NAMES(複数形・カンマ区切り)への拡張を v2 で検討。複数役員体制移行時の手動運用手順を内部統制規程に明記
  6. 契約形態フィルタ: 継続 / 月額 のみ ROI 計算対象とし、スポット(CTR_SPT)は除外するか。スポット契約の AI ツール(年契約一括等)の ROI 計算方法を別途定義。
  7. マイナス ROI の扱い: 削減時間が想定を下回った場合 ROI < 1.0x になる。1.0x 未満のツールは UI 上で「解約候補」アラート表示するか、月次レビュー時の人間判断に委ねるか。
  8. 税抜 vs 税込の選択: 月額コストは税込で計算するが、想定削減金額(人件費換算)は税込/税抜どちらか。役員報酬の 月額給与・報酬 は税込みのため税込ベースで統一が妥当。
  9. 削減時間の上限ガード: 1 ツールあたり想定月間削減時間 > 200h は誤入力疑い(フルタイム超)。Step 1 のバリデーションで warn 表示するか。
  10. 過去月 ROI のスナップショット保存: v1 は SPARKLINE を出さず単一セル表示。v2 で 99_ai_tool_roi_snap シート新設して月次自動保存→ SPARKLINE 連動。
  11. K7 行追加による 93_kpi_dashboard レイアウト影響: K6 ランウェイの下に K7 が増えると印刷 1 ページに収まらなくなるリスク。helper 行の sheet.hideRows(...) の数値も同期更新。
  12. FF_UC8_S01_CALC_ROI = false 時の K7 行削除 vs 非表示 vs Early Return: 本仕様では Early Return(生成自体スキップ)を採用。SPA 版 KPI ダッシュボード(MAS-232)との互換性も確認。
  13. AI ツール以外のサブスク(Slack / Notion 等)の扱い: 想定月間削減時間 を入力できるが、本来 ROI 評価対象は AI ツール限定か。費用科目 で「AI ツール」専用科目を新設するか、現行の「ソフトウェア利用料」のまま 備考 で識別するか。
  14. 個別ツール ROI の表示粒度: subsub 行で各ツールの ROI を出すと行数が爆増(5-10 ツールなら +10 行)。クリッカブルな展開/折りたたみ UI は GAS では困難 → CSV エクスポートまたは別シート(93b_ai_tool_roi_detail)への分離を v2 で検討。
  15. Jr 採用後の評価軸変更: MAS-230 で Jr 採用後は「Jr 1 名分のコスト」が新しい比較基準になるため、MAS176_HOURLY_RATE_BASISJR_SUBSTITUTE_RATE に切り替える運用検討。
  16. 個人情報・権限管理: 役員報酬・氏名を K7 sub 行に表示するため、KPI ダッシュボードの閲覧権限・出力範囲を別途内部統制規程で明文化。社内開示制限が必要な場合は FF_UC8_S01_CALC_ROI を OFF にするか、集計値のみ表示するオプションを検討。
  17. KPI セルの編集競合: K7 行に人手で値を直接入力されると GAS 再実行時に上書きされ混乱の元。range.protect().setWarningOnly(true) で警告表示するか、setProtection で完全保護するかを運用判断。
  18. 月次締後の再集計手順: サブスク変更(解約・想定削減時間修正)後の K7 再集計タイミング。月次締後の遡及修正可否と監査証跡保存方針を運用規定に明記。

実装プロンプト(Claude Code 用)

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-176「開発者向け AI ツール投資 ROI 管理(D.8 派生)」を
Phase 0 → Step 1 → Step 6 の順で段階 PR 化して実装してください。

## Phase 0: 実データ Read 裏取り(必ず最初に実施・failure_patterns #18-#20 直接対策)

以下を Read し、行番号や配列内容を **論理的にロケート** してください
(ハードコードされた行番号は古い可能性があるため、シンボル名で探索):

1. `100_config/101_sys_config.js` の `BUD_SUBS` ヘッダー定義と
   `schemas['BUD_SUBS'].inputCols` / `autoCols` の実値
2. `200_data/202_repository.js` の `HeadcountRepository` パターンと
   `readSheetAsDtos_` 共通ヘルパー
3. `22_bud_headcount` の DDL ヘッダー(氏名関連列が単一か分離か)
4. `grep -rn 'マイグレーション' 000_infra/ 100_config/ 300_ui/` で
   メニュー定義の実在ファイルとデータ構造特定
5. `grep -rn 'function migrationF66DividendSeed' .` で実パス特定
6. `BUD_SUBS.利用ステータス` の `dataValidation.values` 実値
7. `400_domain/449_dividend_mix_optimizer.js` の名前空間 IIFE パターン
8. `600_report/609_datamart_kpi.js` の `renderKpiRows_` の rows 配列と
   `renderKpiHelperRows_` helper 行構造
9. `000_infra/002_constants.js` の `Constants.getParam` シグネチャ
10. `400_domain/402_rpa_subscription.js` で X/Y/Z 列が起票に不使用と確認
11. `docs/_internal/failure_patterns.md` の #18-20 / #21-24 / #25 / #26 / #29 / #31

Phase 0 完了後、仕様書の「実データ検証」セクションに Read 結果のコード
スニペットを inline 貼付してから Step 1 に進む。

## 修正対象ファイル

- `100_config/101_sys_config.js`(変更・BUD_SUBS ヘッダー +2 列)
- `000_infra/003_contracts.js`(変更・SubscriptionDTO JSDoc 追加)
- `200_data/202_repository.js`(変更・SubscriptionRepository 新設)
- `400_domain/450_ai_tool_roi_engine.js`(**新規・約 160 行**)
- `600_report/609_datamart_kpi.js`(変更・K7 ブロック追加)
- `800_ops/810_migration_mas176.js`(**新規・約 60 行**)
- Phase 0 で特定したメニュー実存ファイル(変更・登録 +1 行)
- `docs/_config.json`(変更・nav に新規仕様書登録 +1 行・**failure_patterns #11 必須**)
- `900_test/901_test_runner.js`(変更・T-MAS176-01〜14 追加)

## Step 1: BUD_SUBS DDL 拡張(Sonnet 推奨)

`101_sys_config.js` の `BUD_SUBS` ヘッダー配列末尾に
"課金サイクル" / "想定月間削減時間" の 2 列を順に追加。
`schemas['BUD_SUBS'].inputCols` 配列の末尾に `25` / `26` を追加(Y/Z 列)。
autoCols は不変。**両配列の更新は同一 Edit ブロック**(failure_patterns #25)。

動作確認: dev で `setupAllSchemas` を実行 → `23_bud_subscription` の
Y/Z 列に "課金サイクル" / "想定月間削減時間" が表示され薄青背景
(INPUT_BG)になること。Y 列の dataValidation で「月」「年」固定。

## Step 2: SubscriptionDTO + SubscriptionRepository 新設(Haiku で OK)

`003_contracts.js` に `SubscriptionDTO` JSDoc 型定義を追加。
`202_repository.js` の HeadcountRepository の直後に
`SubscriptionRepository` を追加。`_getSheet` + `findAll` + `save`(未実装エラー)
+ `append`(未実装エラー)の 4 メソッド構成で他リポと対称化。
BUD_SUBS シートキーを使用。

## Step 3: AiToolRoiEngine 新設(Opus 推奨・名前空間設計)

`400_domain/450_ai_tool_roi_engine.js` を新設。IIFE + 名前空間パターン
(449_dividend_mix_optimizer と同形式)。`calculateMonthlyRoi()` を public、
`_calcExecHourlyRate_()` / `_normalizeMonthlyCost_()` を private(末尾 `_`
必須・failure_patterns #28)。

実装ポイント:
- 役員特定は Phase 0 で確認した実列名で完全一致(単一/分離列に応じ条件変更)
- 有効フラグ FALSE スキップ
- `Number.isFinite()` で **上流(hourlyRate 段階)と最終合算の 2 段階 scrub**(failure_patterns #29)
- `FF_UC8_S01_CALC_ROI = false` 時は `disabled: true` を含む空 result 返却
- 課金サイクル='年' は (税抜+税)/12 で月額換算

## Step 4: 93_kpi_dashboard に K7 追加(Opus 推奨・既存パターン横展開)

`609_datamart_kpi.js` の `renderKpiRows_` の rows 配列末尾(K6 ランウェイ後)に
K7 main + sub 4 + subsub N を追加。
**値はすべて GAS 側で `AiToolRoiEngine.calculateMonthlyRoi()` 実行後の
スカラーを `setValue()` で埋める**(数式不使用・failure_patterns #21-24 直接対策)。

- `FF_UC8_S01_CALC_ROI` 評価し OFF なら K7 セクション生成自体スキップ(Early Return)
- `range.setNumberFormat('0.0"x"')` で 4.0x 形式表示
- メイン処理ブロックを `Utils.measureRuntime_` でラップ(性能監視)
- v1 では SPARKLINE 出力せず単一セル表示(TODO コメント残す)
- `range.protect().setWarningOnly(true)` で人手編集警告
- `applyKpiConditionalFormat_` に ROI 閾値ルール(warn=2.0/target=4.0)追加
- `sheet.hideRows(...)` の引数を helper 行 +1 に同期更新

## Step 5: マイグレーション関数 + メニュー登録(Sonnet 推奨)

`800_ops/810_migration_mas176.js` を新設。`migrationMAS176SysParams()` 関数で
6 キーを冪等投入(F66 等の既存 seed パターン参照)。

特権操作の監査要件:
- 実行前: `SpreadsheetApp.getUi().alert()` で確認ダイアログ
- 各キー投入時: 既存値 → 新値を `Utils.auditLog('RUN', ...)` で記録
- 完了時: `99_audit_log` に改定日・改定者・改定理由を記録

Phase 0 で特定したメニュー実存ファイルに
`{ label: 'MAS-176: AI ツール ROI sys_params 投入', funcName: 'migrationMAS176SysParams', ... }` を追加。

## Step 6: 901_test_runner.js テスト追加(Sonnet 推奨)

T-MAS176-01〜14 の 14 件をエッジケース表に対応する形で追加。
特に T-MAS176-09(Infinity scrub 上流/下流 2 段階)、
T-MAS176-12(DDL 未実行検知)、T-MAS176-14(年額契約月額換算)は必須。

## 制約

- `appsscript.json` を一切編集しない(failure_patterns #26 厳守)
- `02_rpa_subscription.js` の RPA 起票ロジックは変更禁止
- 既存 K1-K6 の行番号を変更しない(既存条件付き書式が壊れる)
- `想定月間削減時間` / `課金サイクル` ヘッダー名は **完全一致**(同義語禁止)
- `docs/_config.json` への nav 登録を忘れない(failure_patterns #11)
- 行番号や配列インデックスはハードコードせず、シンボル名で論理的にロケート

## デプロイ手順

- Phase 0 → Step 1-6 を独立 PR 化推奨(rollback 容易性)
- 各 PR で dev push → setupAllSchemas → migrationMAS176SysParams 実行 →
  AiToolRoiEngine.calculateMonthlyRoi() を GAS エディタで実行確認 →
  buildKpiDashboard で K7 描画確認 → prod push
- コミットメッセージ: `feat(MAS-176 Step N): <内容>`

## failure_patterns チェックリスト

- [ ] Phase 0: 実データ Read 完了・スニペット仕様書貼付済み
- [ ] #18-20: SubscriptionRepository / AiToolRoiEngine / MAS176_* / FF_UC8_S01_CALC_ROI キーは Read で裏取り済み
- [ ] #21-24: K7 値は GAS 側スカラー埋め込み(数式不使用)
- [ ] #25: inputCols / autoCols 両方を同一 Edit で更新、SubscriptionRepository は findAll/save/append 対称
- [ ] #26: appsscript.json 不変
- [ ] #29: roi の Number.isFinite() ガード上流/下流 2 段階実装済み
- [ ] #31: 新規ファイル番号 450 / 810 と MAS176_* / FF_UC8_S01_CALC_ROI キーの衝突確認済み

推奨実行モデル

Phase推奨モデル根拠
Phase 0 実データ Read 裏取りClaude Sonnet 4.6機械的な Read + grep 作業
Step 1 BUD_SUBS DDL 拡張(ヘッダー +2 / inputCols 配列更新)Claude Sonnet 4.6パターン適用・既存配列への追加のみ
Step 2 SubscriptionDTO + SubscriptionRepository 新設Claude Haiku 4.5HeadcountRepository を機械的に複製
Step 3 AiToolRoiEngine 新設(名前空間 + 計算ロジック + 月額正規化)Claude Opus 4.7新規ドメインエンジン設計・複数 Repository 横断・Infinity scrub 2 段階
Step 4 93_kpi_dashboard K7 追加Claude Opus 4.7既存 K1-K6 レイアウト整合性 + helper 行更新 + 条件付き書式追加 + Feature Flag Early Return
Step 5 マイグレーション関数 + メニュー登録 + auditLogClaude Sonnet 4.6パターン横展開
Step 6 901_test_runner.js テスト追加Claude Sonnet 4.6エッジケース表既定義・パターン化
仕様書レビュー (scripts/4_review_specs_by_gemini.js)Gemini 3 Pro Preview + Deep ThinkROI 計算ロジック・閾値設定の第三者検証

変更履歴

日時バージョン変更内容
2026-04-30v0.1 (仕様書完了・Draft)初版作成。tasks/prompts/task_F-43.md(手動骨格)を input として Claude Opus 4.7 (1M context) で本体起草。14 セクション全網羅。約 280 行(新規 200 行 + 既存変更 80 行)+ 工数 1.5-2 週間。
2026-05-13v0.2 (3 モデルレビュー Critical/Major 反映)Gemini/Claude Opus/GPT-4.1 の 3 モデルレビューを受けて修正: (a) Feature Flag 命名規約準拠: MAS176_ENABLEDFF_UC8_S01_CALC_ROI(ADR-0028/0029)/ (b) SubscriptionDTO 追加: 003_contracts.js に JSDoc 型定義を追加(データ境界明示)/ (c) Phase 0 実データ Read 裏取り強制化: BUD_SUBS 列番号・22_bud_headcount 実列名・メニュー実存ファイル・migrationF66 実パス・利用ステータス dataValidation を着手前 Read 必須化 / (d) 月額/年額正規化対応: 課金サイクル 列を Y 列に追加(25 列目)、想定月間削減時間 は Z 列(26 列目)に変更 / (e) SubscriptionRepository API 対称化: findAll / save / append 3 メソッド構成(v1 は save/append 未実装エラー throw)/ (f) 特権操作監査要件追加: マイグレーション実行時の確認ダイアログ + Utils.auditLog 記録 / (g) NumberFormat 明記: '0.0"x"' で 4.0x 表示 / (h) SPARKLINE v1 取り下げ: 過去スナップショット未保存のため水平直線 UX バグ回避、単一セル表示に変更 / (i) Infinity scrub 2 段階化: 上流(hourlyRate 段階)+ 最終合算の 2 段階 / (j) 個人情報・改定証跡・セル保護: 人間検討事項 #16-#18 追加 / (k) 実装規模約 340 行(新規 220 行 + 既存変更 120 行)に増加。

仕様書作成プロンプト

展開して表示(投入プロンプト全文・再現性 / 監査性のため記録)
あなたは GAS 会計システム (bizlp-gas-accounting) のシニア開発者兼仕様書ライターです。

## タスク

案件 ID **MAS-176**「開発者向け AI ツール投資 ROI 管理(D.8 派生)」の開発仕様書を作成し、`/Users/ts_kuma/projects/my-gas-project-doc/docs/dev/dev_mas-176_ai_tool_roi.md` に保存してください。

## 案件概要 (todo_master_tables.md L437)

- **Phase**: P1.5 / **★**: ★★★ / **Layer**: 🌐 Cross (Domain + Data + Report)
- **D.8 §2.e-6 抽出案件**
- **目的**: 月 2.5 万円の AI ツール投資が月 10 万円相当の生産性を生むことを定量証明 + 継続的 AI ツール投資の正当化と予算確保
- **実装スコープ**:
  - `23_bud_subscription` シートに `est_hours_saved_per_month`(想定月間削減時間)列を DDL で追加 (`101_sys_config.js` 更新)
  - `22_bud_headcount` の役員年収÷稼働時間で算出した時間単価を乗じて月次 ROI を計算
  - `93_kpi_dashboard` に出力
  - 計算ロジックは `600_report/` または `400_domain/` の既存ファイルへの関数追加で対応 (新規 GAS ファイル不要の可能性あり)
- **対象ツール例**: Cursor Pro $20 / Claude Max 5x $100 / GitHub Copilot Pro $10 / v0 $20 / ChatGPT Plus $20 (合計月 $170 ≒ 25,500 円)
- **MAS-013 (大型一時投資 NPV/IRR) との違い**: 月額 SaaS マイクロ継続投資をサブスクマスタ連動で月次自動モニタリング

## 検討事項

1. 想定削減時間のエビデンス管理 (主観入力の精度担保方法)
2. 時間単価の計算ベース (役員報酬の月間稼働時間換算方法)
3. ROI 閾値と警告アラートの設定値
4. Claude Code Max の位置づけ (フルタイム Jr 代替として見るか個別ツール扱いか)

## 必読ファイル (調査して固有名詞を確定)

実装の前に以下のファイルを `Read` で参照し、関数名・列名・定数名を確実に存在するものに揃えてください:

1. `/Users/ts_kuma/projects/my-gas-project-doc/docs/_internal/dev_spec_prompt_template.md` — 仕様書 14 セクションテンプレート (必須準拠)
2. `/Users/ts_kuma/projects/my-gas-project-doc/docs/_internal/failure_patterns.md` — #18-#31 の失敗パターン (#18-#20 命名造語 / #25 並列実装対称性 / #26 oauthScopes / #29 V8→Java Infinity null / #31 ID 採番衝突 を必ずインライン警告)
3. `/Users/ts_kuma/projects/my-gas-project-doc/100_config/101_sys_config.js` — DDL 定義 (`23_bud_subscription` の現行スキーマ確認)
4. `/Users/ts_kuma/projects/my-gas-project-doc/200_data/202_repository.js` — `SubscriptionRepository` の有無確認 + `findAll()` パターン
5. `/Users/ts_kuma/projects/my-gas-project-doc/600_report/` 配下のマートビルダー (KPI ダッシュボード書込パターン)
6. `/Users/ts_kuma/projects/my-gas-project-doc/CLAUDE.md` — コーディング規約・列参照ヘッダー名ベース・名前空間
7. 参考: `/Users/ts_kuma/projects/my-gas-project-doc/docs/dev/dev_mas-231_gas_perf_optimization.md` — 直近作成した同フォーマット仕様書

## 仕様書フォーマット要件 (14 セクション)

`dev_spec_prompt_template.md` 準拠。以下を必ず含める:
- フロントマター (id: MAS-176, aliases: ["F-176"], type: Story, status: Draft)
- 概要テーブル (案件 ID / カテゴリ / Phase / 優先度 / 所要時間 / 前提案件 / 後続案件)
- 目的 / 修正方針 / 実装スコープ / Step 分割 (3-5 Step 程度)
- 注意事項 (failure_patterns 参照含む)
- エッジケース表 (10 件以上、テーブル化)
- 人間が検討すべき事項 (10 件以上)
- 推奨実行モデル (Step ごとに Haiku/Sonnet/Opus)
- 関連ドキュメント
- 開発プロンプト (Claude Code 向け実装指示)
- 変更履歴
- 末尾に <details><summary>展開して表示</summary> ブロックでこのプロンプト全文を記録

## 必ず守る規約

- 列参照はヘッダー名ベース (`indexOf` / `buildHeaderIndex_`)、列番号ハードコード禁止
- 有効フラグ=FALSE 行スキップ
- 名前空間: `Contracts` / `SubscriptionRepository` / 新設名前空間は `400_domain/` 配下
- ファイル名は番号体系 (000_infra/100_config/200_data/300_ui/400_domain/500_import/600_report/800_ops/900_test) に従う
- failure_patterns #18-#20 (命名造語禁止) — 推測で関数名を作らず Read で実在確認

## アウトプット

ファイル: `docs/dev/dev_mas-176_ai_tool_roi.md`

Step 分割で `Write` → `Edit` で完成させてください (一気に書ききろうとせず、骨格 → 各セクションの順):
1. Step 2-1: 骨格 Write (フロントマター + 14 H2 見出し)
2. Step 2-2: 概要〜注意事項 Edit
3. Step 2-3a: エッジケース〜人間検討事項 Edit
4. Step 2-3b: 開発プロンプト〜変更履歴 Edit
5. Step 2-4: <details> プロンプト全文記録 Edit

最終確認として行数を `wc -l` で測定し報告してください (期待: 350-500 行)。