概要

項目内容
案件IDMAS-008
カテゴリFP&A / 資金繰り予測
PhasePhase 2
優先度P1 (★★★)
所要時間2-3時間
対象ファイル600_report/605_datamart_cf.js(CF マートに計算ロジック追加+出力行追加)、600_report/608_datamart_render.js(条件付き書式)、100_config/101_sys_config.js03_sys_params にパラメータ追加)、docs/spec/spec_cf.md(仕様追記)
前提案件なし(既存 CF マート 605 の cashEnd / cfOp を再利用)
後続案件MAS-008 拡張(日次ランウェイ・シナリオ別ランウェイ・MAS-177 多年度対応)

目的

既存の CF(キャッシュフロー計算書)出力は単月 / YTD の CF 内訳までは可視化しているが、経営判断に直結する「あと何ヶ月で現預金が枯渇するか」を直接示す指標は未実装。本仕様では CF 出力タブの下部に以下 3 指標を追加し、経営陣が一目で資金危機の深刻度を把握できるダッシュボードへ昇格させる:

  1. 月末現預金推移予測: 月ごとの予測現預金残高(過去月=実績、将来月=ローリング予測)
  2. バーンレート(Burn Rate): 過去 N ヶ月の営業 CF の流出超過平均(財務 CF の一時的な調達額は除外)
  3. ランウェイ(Cash Runway, ヶ月): 予測月末現預金 ÷ バーンレート

安全水準(例: 6 ヶ月)を下回った月のセルを条件付き書式で赤色ハイライトし、経営陣への強烈なアラートとする。

現在のコード

1. CF マートの既存アウトプット(600_report/605_datamart_cf.js

dmCalcCf_(ctx) は ctx に以下を書き込む(L21-33):

  • cfOp[0..12]: 営業 CF 月別(index 0 = 通期 Total、1-12 = 月別)
  • cfInv[0..12]: 投資 CF 月別
  • cfFin[0..12]: 財務 CF 月別
  • cfNet[0..12]: ネット CF 月別
  • cashStart[0..12]: 各月の期首現預金残高
  • cashEnd[0..12]: 各月の期末現預金残高B/S 現預金と完全一致)

dmBuildCfOutput_(ctx) の末尾(L103-105)で既に以下を出力している:

buildCfRow('🌟 現金及び現金同等物の増減額', filterWithRecalcTotal(cfNet), 'profit');
buildCfRow(' + 期首残高 (前月末の現預金)', filterValues(cashStart), 'subtotal');
buildCfRow('🏦 期末現金残高 (B/S現預金と完全一致)', filterValues(cashEnd), 'cash_plug');

本仕様で追加するセクションは cashEndcfOp を再利用し、新規ループ計算は最小限に抑える。

2. 既存のフィルタ関数(L60-79)

const filterValues = function(arr) {
  if (!isActualOnly) return arr;
  var res = [arr[0]];
  for (var i = 0; i < 12; i++) {
    res.push(targetMonths[i] >= boundaryMonthStr ? '' : arr[i + 1]);
  }
  return res;
};

const filterWithRecalcTotal = function(arr) {
  if (!isActualOnly) return arr;
  var res = filterValues(arr);
  var total = 0;
  for (var i = 1; i <= 12; i++) {
    if (res[i] !== '') total += Number(res[i]) || 0;
  }
  res[0] = total;
  return res;
};

加算可能項目(CF 金額・残高)は filterWithRecalcTotal または filterValues非加算項目(比率・ランウェイ月数)は filterValues + Total 列を独自再計算する規約が既に確立されている(失敗パターン #1 参照)。

3. 出力先

現状 dmBuildCfOutput_outC / fmtC(単月)と outCY / fmtCY(YTD)の 2 組を返す。本仕様は outC / fmtC の末尾にのみ追記する(YTD には Runway 概念がないため追加しない)。

4. 03_sys_params のパラメータ化基盤

100_config/101_sys_config.js03_sys_params タブが存在し、Utils.getSystemParam(key, defaultValue) で値を取得可能。本仕様では 2 つのパラメータを追加:

  • CASH_RUNWAY_AVG_MONTHS: バーンレート算出対象月数(default = 3)
  • CASH_RUNWAY_MIN_THRESHOLD: 安全水準ランウェイ(default = 6)

修正方針

全体像: CF マート計算の末尾に dmCalcCashRunway_(ctx) を追加、dmBuildCfOutput_ の末尾に 3 行追加、608_datamart_render.js に条件付き書式ルールを追加する。

Step 1: バーンレート・ランウェイ計算ロジックの追加

605_datamart_cf.jsdmCalcCf_(ctx) の末尾(L33 の直前)に以下を追記:

// --- MAS-008: バーンレート・ランウェイ計算 ---
dmCalcCashRunway_(ctx);

同ファイル内に新規関数 dmCalcCashRunway_(ctx) を追加:

/**
 * MAS-008: 月次バーンレートとランウェイ(ヶ月)を計算
 * - バーンレートは営業CF(cfOp)の「過去 N ヶ月の流出超過平均」。財務 CF は除外。
 * - ランウェイは当月末現預金 ÷ バーンレート。
 * - キャッシュ増加月(cfOp >= 0)は流出平均から除外(∞ 扱い)。
 * @param {Object} ctx - 共有コンテキスト(cfOp・cashEnd を利用)
 */
function dmCalcCashRunway_(ctx) {
  var N = Number(Utils.getSystemParam('CASH_RUNWAY_AVG_MONTHS', 3)) || 3;

  var cfOp = ctx.cfOp;
  var cashEnd = ctx.cashEnd;

  // 月別バーンレート(その月を含まない過去 N ヶ月の「流出分」の平均)
  // 流出分 = Math.max(-cfOp[m], 0)  (営業 CF マイナス = 流出)
  var burnRate = Array(13).fill(0);
  for (var m = 1; m <= 12; m++) {
    var sumBurn = 0;
    var cnt = 0;
    for (var k = 1; k <= N; k++) {
      var idx = m - k;
      if (idx < 1) continue;            // 過去月が不足している場合はスキップ
      var v = Math.max(-cfOp[idx], 0);  // 流出分のみ(流入月は 0)
      sumBurn += v;
      cnt++;
    }
    burnRate[m] = cnt > 0 ? sumBurn / cnt : 0;  // 参照可能な過去月がなければ 0
  }
  // 通期(index 0) は 12 月末時点の burnRate を採用(代表値・非加算)
  burnRate[0] = burnRate[12];

  // ランウェイ(ヶ月)= cashEnd / burnRate
  // burnRate=0(資金増加中 or 計算不可)→ Infinity、cashEnd<=0 → 0
  var runway = Array(13).fill(0);
  for (var i = 1; i <= 12; i++) {
    if (cashEnd[i] <= 0) {
      runway[i] = 0;              // 資金ショート月は 0 ヶ月
    } else if (!isFinite(burnRate[i]) || burnRate[i] <= 0) {
      runway[i] = Infinity;       // バーンレート 0 以下 → 算出不能(安全)
    } else {
      runway[i] = cashEnd[i] / burnRate[i];
    }
  }
  // 通期(index 0) は 12 月末時点の runway(非加算・代表値)
  runway[0] = runway[12];

  ctx.burnRate = burnRate;
  ctx.runway = runway;
  ctx.runwayMinThreshold = Number(Utils.getSystemParam('CASH_RUNWAY_MIN_THRESHOLD', 6)) || 6;
}

Step 2: CF タブへの「💰 資金繰り予測 / Cash Runway」セクション追加

dmBuildCfOutput_(ctx) の末尾、buildCfRow('🏦 期末現金残高 ... の直後に以下を追加:

// --- MAS-008: 💰 資金繰り予測 / Cash Runway セクション ---
const burnRate = ctx.burnRate;
const runway = ctx.runway;

// 非加算指標(比率・代表値)の Total 独自再計算ヘルパー
// - 月末現預金: Total = 末月の値(filterValues でフィルタ後、最後の非空月を Total へ)
// - バーンレート: Total = 平均 (filterValues でフィルタ後、非空月の平均)
// - ランウェイ: Total = 末月の値(時点指標)
const filterNonAdditiveAsLast = function(arr) {
  var res = filterValues(arr);
  var lastVal = 0;
  for (var i = 12; i >= 1; i--) {
    if (res[i] !== '' && res[i] !== undefined) { lastVal = res[i]; break; }
  }
  res[0] = lastVal;
  return res;
};
const filterNonAdditiveAsAvg = function(arr) {
  var res = filterValues(arr);
  var sum = 0, cnt = 0;
  for (var i = 1; i <= 12; i++) {
    if (res[i] !== '' && typeof res[i] === 'number' && isFinite(res[i])) { sum += res[i]; cnt++; }
  }
  res[0] = cnt > 0 ? sum / cnt : 0;
  return res;
};

pushCf(Array(cfHeaders.length).fill(''), 'empty'); cfRowIdx++;
buildCfRow('■ 4. 資金繰り予測 (MAS-008 Cash Runway)', Array(13).fill(''), 'mega_header');
buildCfRow('  月末現預金残高 (予測)', filterNonAdditiveAsLast(cashEnd), 'balance');
buildCfRow('  営業CFベース バーンレート (過去N月平均)', filterNonAdditiveAsAvg(burnRate), 'rate');
buildCfRow('  ランウェイ (ヶ月)', filterNonAdditiveAsLast(runway.map(v => isFinite(v) ? v : '')), 'runway');
buildCfRow('  ※ ランウェイ空白 = バーンレート 0 以下(キャッシュ増加中・安全)', Array(13).fill(''), 'note');

補足: runway 配列の Infinity は Sheet にそのまま書き込めないため、空白文字 '' に置換してから出力する。読者には「計算不能(安全)」と注記行で示す。

Step 3: 条件付き書式による警告ハイライトとパラメータ化

03_sys_params へのパラメータ追加

100_config/101_sys_config.js03_sys_params 初期データに以下 2 行を追加(既存 Utils.getSystemParam 経由で取得):

キーデフォルト値用途
CASH_RUNWAY_AVG_MONTHS3バーンレート算出対象月数
CASH_RUNWAY_MIN_THRESHOLD6安全水準ランウェイ(これ未満で赤色警告)

条件付き書式の追加(600_report/608_datamart_render.js

「ランウェイ (ヶ月)」行に対して以下のルールを追加:

  • 数値 0: 背景 #D32F2F(濃い赤)+ 文字白 = 資金ショート月
  • 数値 0 < x < CASH_RUNWAY_MIN_THRESHOLD: 背景 #FFE0B2(薄オレンジ)= 警告水準
  • 数値 >= CASH_RUNWAY_MIN_THRESHOLD: デフォルト(書式なし)
  • 空白('': デフォルト(Infinity は安全扱いで無色)

CASH_RUNWAY_MIN_THRESHOLD はシート書き込み時に ctx.runwayMinThreshold として参照し、条件付き書式のルール値へ動的に埋め込む。

影響範囲

変更対象変更内容変更量
600_report/605_datamart_cf.jsdmCalcCashRunway_ 新規追加 + dmBuildCfOutput_ 末尾に 5 行追加+70行
600_report/608_datamart_render.jsランウェイ行への条件付き書式ルール追加+30行
100_config/101_sys_config.js03_sys_params 初期データに 2 キー追加+2行
docs/spec/spec_cf.mdMAS-008 セクションの仕様追記+20行
  • 既存動作への影響なし: 既存 CF 出力(単月・YTD)の行順序・書式は不変。末尾に追加のみ
  • DTO / DDL への影響なし: CF 出力タブ自体は動的生成(DDL 管理外)
  • 既存 cfOp / cashEnd の計算ロジック不変: 読み取り専用で再利用

注意事項

  1. filterWithRecalcTotal vs filterNonAdditiveAsLast/Avg の使い分け: 既存 CF 金額・残高は filterWithRecalcTotal(加算可能)、月末現預金残高・バーンレート・ランウェイは非加算なので新規の filterNonAdditiveAsLast/Avg を使う(失敗パターン #1 継承)
  2. ゼロ除算の防止: burnRate = 0 または NaN/Infinity の場合は runway = Infinity に設定し、Sheet 書き込み時は空白化する(失敗パターン #2 継承)
  3. 資金ショート月の表現: cashEnd <= 0 の月は runway = 0(Infinity でも空白でもない)。条件付き書式で濃赤ハイライト
  4. 過去月不足時のバーンレート: 創業直後など過去 N ヶ月分の実績がない場合、参照可能な過去月のみで平均(cnt カウンタで割る)。1 ヶ月もなければ 0
  5. 列インデックスのハードコード禁止: cfRowIdx の管理は既存 buildCfRow に委譲、手動で行番号を数えない(失敗パターン #18 系)
  6. 条件付き書式の範囲指定: ランウェイ行の「E 列〜P 列」(12 ヶ月)のみ対象。Total 列(D 列)は平均や代表値なので閾値判定から除外
  7. 過去の実績月(境界月以前)も warning 判定対象: isActualOnly フィルタで空白化される月は自動的に判定対象外となる
  8. Infinity を Sheet に書けない: runway.map(v => isFinite(v) ? v : '') で空白化必須。条件付き書式は空白を無視する
  9. Utils.getSystemParam のキャッシュ: 計算中に 2 回以上呼ばない。ctx.runwayMinThreshold として 1 回で取得しておく
  10. 03_sys_params の型: 値は文字列で返るため必ず Number(...) でラップし、|| デフォルト でフォールバック
  11. YTD 出力には Runway を追加しない: YTD(累積)ではランウェイ概念が成立しない。本仕様では単月 outC のみに追加
  12. 選択月スナップ(B 列): 新規 3 行も既存 buildCfRow の仕組みで B 列に当月値が表示される(=INDEX(...MATCH($B$2...)) の数式が自動付与される)

エッジケース

#条件表示値理由
1バーンレート = 0(過去 N ヶ月すべて営業 CF >= 0)ランウェイ = 空白キャッシュ増加中・危機なし。ゼロ除算(Infinity)の可視化を避ける
2バーンレート < 0 は数学的に発生しない(Math.max(-cfOp, 0)-cfOp >= 0 のみ加算するため下限 0 で安全
3予測月末現預金 = 0 または負値(資金ショート月)ランウェイ = 0 ヶ月「既にショート」を明示。濃赤ハイライトで強警告
4過去 N ヶ月の実績が 0 件(創業月・dev 環境初期など)バーンレート = 0 → ランウェイ = 空白参照可能データなし。計算不能として安全側に倒す
5過去 N ヶ月の一部のみ実績あり(例: 2 ヶ月分)バーンレート = 実在月平均(1-2 月の流出の平均)cnt カウンタで割るため実在月のみ採用
6cfOp[i] = 0(ブレークイーブン月)流出分 = 0(バーンレート算出に寄与せず)中立月を流出扱いしない・流入扱いしない
7非加算(Total 列): 月末現預金残高Total = 末月の値時点残高なので単純加算不可。filterNonAdditiveAsLast
8非加算(Total 列): バーンレートTotal = 非空月の平均平均 of 月次平均。filterNonAdditiveAsAvg
9非加算(Total 列): ランウェイTotal = 末月の値(非空時)時点指標。filterNonAdditiveAsLast
10isActualOnly=true で境界月以降を空白化空白月はバーンレート・ランウェイとも空白filterValues の挙動を継承、追加ロジック不要
11ランウェイ >= 120 ヶ月(10 年超)の異常値そのまま表示(丸めなし)計算根拠を透明にし、外れ値を隠さない
12過去 N ヶ月に 1 ヶ月だけ突発的な高額流出(例: 退職金)平均が歪む後続案件で「メディアン or 外れ値除外平均」を検討(人間検討事項参照)

実データ検証(事前確認項目)

確認項目確認方法確認結果(Phase 1 で調査済)
CF マートの cfOp / cashEnd 配列の型と範囲605_datamart_cf.js:21-33 を ReadArray(13) で index 0 = Total、1-12 = 月別。number 型
filterValues / filterWithRecalcTotal の動作605_datamart_cf.js:60-79 を Read✅ 仕様書内でそのまま引用可
buildCfRow の数式埋め込み605_datamart_cf.js:84 を Read✅ B 列の選択月スナップは自動付与される
03_sys_params の初期データ書込箇所100_config/101_sys_config.js の該当 DDL🔄 Phase 1 Step 5 で対象タブ確認、初期データ領域に 2 キー追加
Utils.getSystemParam の存否000_infra/004_utils.js✅ 既存案件で利用実績あり(CF_DAILY_PLAN_HORIZON 等)
608_datamart_render.js の条件付き書式パターンファイルを Read して既存 addConditionalFormatRules 呼出し🔄 実装時に既存パターンに沿って追加

実行前に運用で追加確認すべき項目:

  • 03_sys_params に既存の運用値(CF_DAILY_PLAN_HORIZON 等)があれば書式・列構成を揃える
  • 条件付き書式の重複設定に注意(既存 CF シートに類似の警告色があるか)

プロダクトポリシー

経営アラートの可視化(必須)

  • 資金ショート月(ランウェイ = 0): 背景 #D32F2F 濃赤 + 文字白。強警告
  • 警告水準(0 < ランウェイ < CASH_RUNWAY_MIN_THRESHOLD: 背景 #FFE0B2 薄オレンジ。早期アラート
  • 安全水準以上: 書式なし

パラメータ化(03_sys_params)

ユーザーが業種・資金状況に応じて閾値を調整できるよう、以下を必ず 03_sys_params に登録:

  • CASH_RUNWAY_AVG_MONTHS(default = 3): バーンレート算出対象月数
  • CASH_RUNWAY_MIN_THRESHOLD(default = 6): 安全水準ランウェイ

ログ

計算時に Utils.logInfo(FUNC, ...) で「最新月ランウェイ = N ヶ月」を記録。月次締めの監査証跡とする。

関連ドキュメント

仕様書関連箇所
spec/spec_cf.mdCF(間接法)の既存仕様。本仕様の追加セクションはこの末尾に追記
spec/spec_daily_cf.md日次 CF(84_cf_daily_plan)。将来の「日次ランウェイ」拡張の土台
dev_mas-024_bep_analysis.md非加算指標(BEP = 固定費 ÷ 限界利益率)の Total 独自計算パターン
dev_mas-001_variance_analysis.mdマート拡張の実装パターン(filterWithRecalcTotal の使い分け)
dev_mas-093_cf_actual_only.mdisActualOnly / filterValues / filterWithRecalcTotal の既存ルール
CLAUDE.mdコーディング規約、列参照ヘッダー名ベース、84_cf_daily_plan のハイブリッド方針
failure_patterns.md#1(非加算指標の Total 壊れ)、#2(売上ゼロ月の BEP がゼロ除算)、#18-#20(固有名詞の Read 裏取り)

人間が検討すべき事項

#項目詳細
1安全水準ランウェイの閾値CASH_RUNWAY_MIN_THRESHOLD 初期値は 6 ヶ月。業種・成長ステージにより 12 ヶ月推奨(シード VC 標準)・3 ヶ月(成熟企業)等の調整余地あり。運用で調整
2バーンレート算出月数CASH_RUNWAY_AVG_MONTHS 初期値は 3 ヶ月。6 ヶ月平均にすると季節変動を吸収、1 ヶ月だと直近の急変(退職金支払等)で乱高下。運用で検証
3突発流出の扱い(外れ値)退職金・年次支払等で 1 ヶ月だけ異常値が出るとバーンレートが歪む。メディアン or ±2σ 外れ値除外平均への置換を後続案件で検討
4予測月(将来月)の cfOp の扱い本仕様では既存 cfOp 配列をそのまま使用(過去=実績、将来=予算)。バーンレートは過去実績のみから算出すべきか(予算由来の将来月を除外すべきか)は要検討
5日次ランウェイ84_cf_daily_plan(日次 CF)のデータを使って「あと X 日」表現も可能。本仕様では月次のみ対応、日次は MAS-177(多年度基盤)or 別案件で
6シナリオ別ランウェイBase/Best/Worst シナリオごとにランウェイを算出するダッシュボード。§4.8.2(シナリオ比較)の実装後に拡張
7調達計画との連動財務 CF(資金調達)を故意に除外しているが、「調達なしでの純粋ランウェイ」と「調達込みのランウェイ」を並列表示する選択肢あり
8警告色の ADA アクセシビリティ赤/オレンジは色覚特性で区別しにくい場合あり。アイコン(🚨/⚠️)併用 or パターン塗りを検討
9ランウェイ < 0 の表現現状 cashEnd <= 0 は 0 に丸める。マイナス月数(既にショート後)を別表現(「-2 ヶ月超過」)にする案もあり

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

【タイムアウト回避・実行原則(v1.7)】
1. Phase 1(設計)では拡張思考フル活用・Read で裏取り。Phase 2(清書)の各 Step は最小限思考で書き下し。
2. 「〜作成します」等の text のみで tool_use なしに turn 終了しない。
3. 実装は 骨格 Write → 追記 Edit/Bash を分割実行。1 回あたり ~300 行以内。
4. 各 Step で書く内容を事前に洗い出してから tool_use へ進む。

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-008「資金繰り予測の高度化(Cash Runway)」を実装してください。

## 実行前タスク
以下のファイルを読み込み、既存パターンを把握してください:
1. `docs/dev/dev_mas-008_cash_runway.md` — 本仕様書
2. `600_report/605_datamart_cf.js` — 特に `dmCalcCf_`(L12-34)と `dmBuildCfOutput_`(L41-131)、`filterValues` / `filterWithRecalcTotal`(L60-79)
3. `600_report/608_datamart_render.js` — 既存の条件付き書式パターン(`addConditionalFormatRules` 呼出箇所)を把握
4. `100_config/101_sys_config.js` — `03_sys_params` の初期データ定義位置と列構造
5. `000_infra/004_utils.js` — `Utils.getSystemParam(key, defaultValue)` の使い方
6. `CLAUDE.md` — 列参照ヘッダー名ベース規約、非加算指標の Total 独自計算ルール
7. `docs/_internal/failure_patterns.md` — #1(filterWithRecalcTotal 非加算壊れ)、#2(ゼロ除算)、#18-#20(Read 裏取り)
8. `docs/dev/dev_mas-024_bep_analysis.md` — 非加算指標の Total 独自計算パターンの先行例
9. `docs/dev/dev_mas-093_cf_actual_only.md` — `isActualOnly` / `filterValues` / `filterWithRecalcTotal` の既存ルール

## 修正対象ファイル
- `600_report/605_datamart_cf.js` — `dmCalcCashRunway_` 追加 + `dmBuildCfOutput_` 末尾に 5 行追加
- `600_report/608_datamart_render.js` — ランウェイ行への条件付き書式追加
- `100_config/101_sys_config.js` — `03_sys_params` 初期データに 2 キー追加
- `docs/spec/spec_cf.md` — MAS-008 セクション仕様追記

## 実装内容

### A. `600_report/605_datamart_cf.js` への追記

1. `dmCalcCf_(ctx)` の末尾(L33 の直前、return 前)に以下を挿入:

       dmCalcCashRunway_(ctx);

2. ファイル末尾に新規関数を追加:

       function dmCalcCashRunway_(ctx) {
         var N = Number(Utils.getSystemParam('CASH_RUNWAY_AVG_MONTHS', 3)) || 3;
         var cfOp = ctx.cfOp;
         var cashEnd = ctx.cashEnd;
         // 月別バーンレート(過去 N ヶ月の流出平均、流入月は 0)
         var burnRate = Array(13).fill(0);
         for (var m = 1; m <= 12; m++) {
           var sumBurn = 0;
           var cnt = 0;
           for (var k = 1; k <= N; k++) {
             var idx = m - k;
             if (idx < 1) continue;
             var v = Math.max(-cfOp[idx], 0);
             sumBurn += v;
             cnt++;
           }
           burnRate[m] = cnt > 0 ? sumBurn / cnt : 0;
         }
         burnRate[0] = burnRate[12];
         // ランウェイ(ヶ月)= cashEnd / burnRate
         var runway = Array(13).fill(0);
         for (var i = 1; i <= 12; i++) {
           if (cashEnd[i] <= 0) runway[i] = 0;
           else if (!isFinite(burnRate[i]) || burnRate[i] <= 0) runway[i] = Infinity;
           else runway[i] = cashEnd[i] / burnRate[i];
         }
         runway[0] = runway[12];
         ctx.burnRate = burnRate;
         ctx.runway = runway;
         ctx.runwayMinThreshold = Number(Utils.getSystemParam('CASH_RUNWAY_MIN_THRESHOLD', 6)) || 6;
       }

3. `dmBuildCfOutput_(ctx)` の末尾、L105 `buildCfRow('🏦 期末現金残高 ...` の直後に以下を追加(本仕様書 Step 2 参照):

       // --- MAS-008: 💰 資金繰り予測 / Cash Runway ---
       const burnRate = ctx.burnRate;
       const runway = ctx.runway;
       const filterNonAdditiveAsLast = function(arr) { /* 仕様書参照 */ };
       const filterNonAdditiveAsAvg = function(arr) { /* 仕様書参照 */ };
       pushCf(Array(cfHeaders.length).fill(''), 'empty'); cfRowIdx++;
       buildCfRow('■ 4. 資金繰り予測 (MAS-008 Cash Runway)', Array(13).fill(''), 'mega_header');
       buildCfRow('  月末現預金残高 (予測)', filterNonAdditiveAsLast(cashEnd), 'balance');
       buildCfRow('  営業CFベース バーンレート (過去N月平均)', filterNonAdditiveAsAvg(burnRate), 'rate');
       buildCfRow('  ランウェイ (ヶ月)', filterNonAdditiveAsLast(runway.map(v => isFinite(v) ? v : '')), 'runway');
       buildCfRow('  ※ ランウェイ空白 = バーンレート 0 以下(キャッシュ増加中・安全)', Array(13).fill(''), 'note');

### B. `100_config/101_sys_config.js` への追記

`03_sys_params` の初期データ定義に以下 2 行を追加:

       ['CASH_RUNWAY_AVG_MONTHS', '3', 'MAS-008: バーンレート算出対象月数'],
       ['CASH_RUNWAY_MIN_THRESHOLD', '6', 'MAS-008: 安全水準ランウェイ(これ未満で警告)'],

### C. `600_report/608_datamart_render.js` への追記

既存の条件付き書式登録箇所に、CF タブのランウェイ行(「ランウェイ (ヶ月)」を A 列に含む行)の E:P 列に対して以下 2 ルールを追加:

- Rule 1: 値 = 0 → 背景 `#D32F2F`、文字白
- Rule 2: 値 > 0 かつ < `ctx.runwayMinThreshold` → 背景 `#FFE0B2`

行番号の特定は「A 列値が `'  ランウェイ (ヶ月)'` に一致する行」を動的に検索(ハードコード禁止)。

### D. `docs/spec/spec_cf.md` への追記

末尾に MAS-008 セクションを追加し、「バーンレート」「ランウェイ」の業務定義・計算式・警告閾値を記載。

## 制約
- **`cfOp` / `cashEnd` の計算ロジックは変更しない**(読み取り専用で再利用)
- **`filterValues` / `filterWithRecalcTotal` は変更しない**(本仕様は新規の `filterNonAdditiveAsLast/Avg` を追加するのみ)
- **列インデックスのハードコード禁止**。行位置は A 列テキストで動的検索
- **`Infinity` は Sheet に書かない**。必ず空白化
- **`Utils.getSystemParam` の戻り値は必ず `Number()` でラップ**
- **YTD 出力(`outCY`)には Runway セクションを追加しない**
- **条件付き書式は `ctx.runwayMinThreshold` を動的に参照**(ハードコード禁止)

## エッジケース
1. バーンレート 0 → ランウェイ空白(ゼロ除算回避)
2. 現預金 <= 0 → ランウェイ 0(ショート月)
3. 過去 N ヶ月実績なし → バーンレート 0 → ランウェイ空白
4. 過去一部のみ → 実在月のみ平均
5. 非加算 Total: 残高=末月値、バーンレート=平均、ランウェイ=末月値
6. `isActualOnly` 境界月以降 → `filterValues` で空白化、判定対象外
7. ランウェイ > 120 ヶ月 → そのまま表示
8. 突発流出 → 平均が歪む(人間検討事項 3 で将来拡張)
9. 予算由来の将来月 → バーンレートに含まれる(人間検討事項 4)

## 実データ検証
- `cfOp` / `cashEnd` は `dmCalcCf_` で既に計算済み(`605_datamart_cf.js:21-33`)
- `filterValues` / `filterWithRecalcTotal` のパターン(`605_datamart_cf.js:60-79`)
- `buildCfRow` は B 列に `=INDEX/MATCH` を自動付与(L84)
- `03_sys_params` の初期データ領域を Read で確認してから挿入

## 動作確認
`npm run push:dev` 後:
1. `setupAllSchemas` を実行し `03_sys_params` に 2 キーが追加されていることを確認
2. `updateDatamartMain` を実行し CF タブを更新
3. **検証**: CF タブ末尾に「■ 4. 資金繰り予測 (MAS-008 Cash Runway)」セクションが表示
4. **検証**: 月末現預金残高・バーンレート・ランウェイの 3 行が月別に表示
5. **検証**: ランウェイ < 6 ヶ月の月セルが薄オレンジ、= 0 の月が濃赤ハイライト
6. **検証**: キャッシュ増加月(`cfOp >= 0`)の次月以降のランウェイが空白(バーンレート 0 平均)
7. **検証**: 資金ショート月(`cashEnd <= 0`)の表示が 0 ヶ月
8. **検証**: `isActualOnly=true` に切替えると境界月以降が空白、Total 列も実績月だけで再計算
9. **検証**: 通期 Total 列が「末月の値(残高・ランウェイ)」「非空月の平均(バーンレート)」になっている
10. **検証**: `03_sys_params` の `CASH_RUNWAY_MIN_THRESHOLD` を 12 に変更 → 再実行で警告色範囲が拡大

### 拡張思考の使用状況

| フェーズ | 拡張思考 | 備考 |
|---------|:--------:|------|
| `dmCalcCashRunway_` 実装 | あり | 過去月不足時の境界条件、ゼロ除算・Infinity の扱い |
| 非加算 Total 再計算 | あり | `filterNonAdditiveAsLast/Avg` の正確な実装 |
| 条件付き書式 | なし | 既存 `addConditionalFormatRules` パターンに沿う |
| 03_sys_params 追記 | なし | 定型 2 行追加 |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本ドキュメント)Claude Opus 4.6非加算指標の Total 独自計算、バーンレート算出の境界条件、ゼロ除算フォールバックの複合設計
実装Claude Sonnet 4.6関数シグネチャ・計算式が仕様書で確定済み。過去 N ヶ月不足時の境界条件と filterNonAdditiveAsLast/Avg の正確な実装に中程度の判断が必要
動作確認ユーザー手動CF タブの目視確認と 03_sys_params の値変更による再計算確認

変更履歴

日付変更内容
2026-04-18初版作成

仕様書作成プロンプト(再現性・監査性のため記録)

仕様書作成プロンプト(再現性・監査性のため記録)

展開して表示
<instruction>
【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】
Claude Code が Phase 2 で API ストリーム idle timeout を起こさないための装備:

1. **拡張思考の使い分け**:
   - Phase 1(設計)では拡張思考をフル活用し、ファイル名形式・エッジケース一覧・Step 分割粒度・固有名詞(関数名/シート名/列名/行番号)を完全に確定させる。
   - Phase 2(清書)の各 Step 内では拡張思考を最小限に抑え、Phase 1 で確定済みの内容の書き下しに徹する。出力途中で再考しない。

2. **テキスト報告の禁止**:
   - 「〜を作成します」等の text のみで tool_use なしに turn を終了しない。
   - 説明は 1 文以内。直ちに tool を呼ぶ。

3. **4-5 分割の Write/Edit 実行**:
   - 仕様書作成は以下の Step に分けて実行する:
     - 2-1 骨格 Write(~20行)
     - 2-2 概要〜注意事項 Edit/Bash(~300行)
     - 2-3a エッジケース〜人間検討事項 Edit/Bash(~200行)
     - 2-3b 実装プロンプト〜変更履歴 Edit/Bash(~250行)
     - 2-4 `<details>` にプロンプト全文記録 Edit/Bash(最重量・必ず独立 Step)
   - 1 回の Write/Edit は約 300 行以内を目安にする。

4. **各 Step で何を書くかを具体指示**:
   - 設計判断を Phase 2 実行時に持ち込まないよう、プロンプト内で指定された各 Step の内容(アーキテクチャ・エッジケース等)を忠実に書き下すこと。

======================================================================

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。
CLIエージェントである「Claude Code」として、上記の原則と以下のフェーズに従い、案件 MAS-008「資金繰り予測の高度化 (Cash Runway)」の開発仕様書を作成してください。

## Phase 1: 実行前タスク(必読・必ずツールを使用して順次実行)
(※テキストでの状況報告は一切行わず、直ちにツールの使用を開始してください)

1. `CLAUDE.md` と `docs/_internal/failure_patterns.md` を読む。
2. 類似するFP&A機能の仕様書として、`docs/dev/dev_mas-001_variance_analysis.md` または `docs/dev/dev_mas-024_bep_analysis.md` などを特定して読み、マート生成や指標追加の設計方針を把握する。
3. 資金繰り・現預金推移の集計基盤となるデータマート生成ロジック(`600_report/605_datamart_cf.js` や `600_report/604_datamart_bs.js` 等)を読む。
4. 関連するデータアクセス層(`200_data/202_repository.js`、`000_infra/003_contracts.js`)を読む。
5. 関連する定数やマスタ(`000_infra/002_constants.js`、`100_config/101_sys_config.js`)を読み、現預金科目やキャッシュフロー計算の仕組みを確認する。
6. `docs/_internal/dev_spec_prompt_template.md` の Phase 2 構成と実装プロンプトフォーマットを読む。
7. ツール(MCP等)を使って、対象となる CF/BS レポート出力先シートの実データ(科目構成や集計列の配置)と DDL 乖離がないか事前確認する。

## 既存実装の前提知識(車輪の再発明を防ぐ)
- キャッシュフロー計算と月末現預金残高の推移計算は、シートのRangeを直接ループするのではなく、マート集計ロジック内でインメモリ計算して2次元配列として一括出力すること。
- 日付の比較や正規化は独自ロジックを書かず、`Utils.parseDateToYm()` などの既存のユーティリティを使用すること。

## Phase 2: 仕様書の分割作成
出力先: `docs/dev/dev_mas-008_cash_runway.md`
**【重要】絶対に1回のツール呼び出しで全内容を出力せず、以下の Step 2-1 〜 2-4 に厳密に分割して実行してください。**

### Step 2-1: 骨格の作成 (File Write)
対象ファイルに、仕様書テンプレートに準拠した見出し(`## 概要`, `## 目的`, `## 現在のコード`, `## 修正方針` 等)の骨格のみを Write ツールで作成して保存してください。本文は空で構いません。

### Step 2-2: 前半セクションの追記 (File Edit または Bash)
「概要」「目的」「現在のコード」「修正方針」「影響範囲」「注意事項」を追記してください。以下を必ず含めること:
- **アーキテクチャの決定事項**:
  - 現行のキャッシュフロー計算タブ(例: 81_cf 等)の下部、またはサマリー領域に「月末現預金推移予測」「バーンレート」「ランウェイ(ヶ月)」を表示するエリアを拡張する。
  - **計算式**:
    - `月末現預金推移予測 = 前月末の現預金残高 + 当月のネットCF予測`(過去月は確定実績ベース、将来月はローリングで推移予測)。
    - `バーンレート = 過去Nヶ月の「営業CFのマイナス額(流出超過)」の平均`。財務CFなどの一時的な調達額は歪みを防ぐため意図的に除外する。
    - `ランウェイ(ヶ月) = 月末現預金推移予測 ÷ バーンレート`。
- **フィルタ連携(重要)**:
  - `isActualOnly` などのフィルタが関連する場合、CF額や残高は単純加算(filterValues)の処理で良いが、ランウェイは「非加算項目」である。小計・合計列では単純な足し算を絶対に避け、必ず集計後に比率を再計算するロジック(`filterWithRecalcTotal` 等)を選択すること。

### Step 2-3a: エッジケース〜人間検討事項の追記 (File Edit または Bash)
「エッジケース」「実データ検証」「関連ドキュメント」「人間が検討すべき事項」を追記してください。
- **エッジケース(テーブル形式で必須)**:
  1. **計算式(ゼロ除算・比率異常値)**: バーンレートが 0 またはプラス(キャッシュ増加傾向)の場合、ランウェイを「∞(算出不能、または安全)」としてゼロ除算エラー(Infinity)を安全にハンドリングする。
  2. **加算可能/非加算の区別**: ランウェイ(ヶ月)は時点・比率指標であるため非加算項目とし、残高・CF額は加算可能項目として扱う旨を明記。
  3. **資金ショート時の表現**: 予測残高がマイナス(資金ショート)に突入する月の場合、ランウェイを「0ヶ月」等として扱う処理。
  4. 過去の実績がNヶ月分存在しない(創業直後など)場合の代替バーンレートの算出ロジック。
- **プロダクトポリシー**:
  - ランウェイが安全水準(人間が指定する最低ライン)を下回る月、または資金ショート予測月に、対象セルを条件付き書式等で警告色(赤色等)にハイライト表示し、経営陣に強烈なアラートを出す可視化を必須とする。
- **人間が検討すべき事項**:
  - ランウェイの最低ライン(何ヶ月分を安全水準とするかの閾値)。
  - バーンレートの計算に用いる「過去Nヶ月」の定義(例: 直近3ヶ月平均、6ヶ月平均など)と `03_sys_params` へのパラメータ化の要否。
- **実データ検証**:
  - Phase 1(Step 7)で確認したマート実データの構成結果や DDL 乖離チェック結果を記載する。

### Step 2-3b: 実装プロンプト〜変更履歴の追記 (File Edit または Bash)
「実装プロンプト(Claude Code用)」「推奨実行モデル」「変更履歴」を追記してください。
- **実装プロンプト**:
  - バッククォート(```)で囲まず、全行を行頭4スペースインデントで出力すること。
  - 過去の失敗パターン(列インデックスのハードコード禁止、再計算時のゼロ除算考慮など)を踏まえた注意事項を盛り込むこと。
- **変更履歴**: 当日の日付で「初版作成」と記載する。

### Step 2-4: 仕様書作成プロンプトの記録 (File Edit または Bash)
対象ファイルの末尾に `<details><summary>展開して表示</summary>` を設け、**この `<instruction>` タグの最初から最後まで(今あなたが読んでいるプロンプト全文)**を一言一句そのまま追記して `<details>` を閉じてください。
※この処理が最も出力トークンを消費し重いため、必ず独立したステップとして実行してください。

## Phase 3: `_config.json` への追記と構文チェック
1. `docs/_config.json` の該当箇所(FP&A・ダッシュボードセクション等)に今回の仕様書へのリンクと説明を追記して保存。
2. 保存後、ターミナルで `node -e "require('./docs/_config.json')"` 等を実行し、JSONの構文エラー(カンマ抜け、括弧の不整合など)がないか自己チェック・修正する。
</instruction>