MAS-008: 資金繰り予測の高度化(Cash Runway)
概要
| 項目 | 内容 |
|---|---|
| 案件ID | MAS-008 |
| カテゴリ | FP&A / 資金繰り予測 |
| Phase | Phase 2 |
| 優先度 | P1 (★★★) |
| 所要時間 | 2-3時間 |
| 対象ファイル | 600_report/605_datamart_cf.js(CF マートに計算ロジック追加+出力行追加)、600_report/608_datamart_render.js(条件付き書式)、100_config/101_sys_config.js(03_sys_params にパラメータ追加)、docs/spec/spec_cf.md(仕様追記) |
| 前提案件 | なし(既存 CF マート 605 の cashEnd / cfOp を再利用) |
| 後続案件 | MAS-008 拡張(日次ランウェイ・シナリオ別ランウェイ・MAS-177 多年度対応) |
目的
既存の CF(キャッシュフロー計算書)出力は単月 / YTD の CF 内訳までは可視化しているが、経営判断に直結する「あと何ヶ月で現預金が枯渇するか」を直接示す指標は未実装。本仕様では CF 出力タブの下部に以下 3 指標を追加し、経営陣が一目で資金危機の深刻度を把握できるダッシュボードへ昇格させる:
- 月末現預金推移予測: 月ごとの予測現預金残高(過去月=実績、将来月=ローリング予測)
- バーンレート(Burn Rate): 過去 N ヶ月の営業 CF の流出超過平均(財務 CF の一時的な調達額は除外)
- ランウェイ(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');
本仕様で追加するセクションは cashEnd と cfOp を再利用し、新規ループ計算は最小限に抑える。
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.js に 03_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.js の dmCalcCf_(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.js の 03_sys_params 初期データに以下 2 行を追加(既存 Utils.getSystemParam 経由で取得):
| キー | デフォルト値 | 用途 |
|---|---|---|
CASH_RUNWAY_AVG_MONTHS | 3 | バーンレート算出対象月数 |
CASH_RUNWAY_MIN_THRESHOLD | 6 | 安全水準ランウェイ(これ未満で赤色警告) |
条件付き書式の追加(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.js | dmCalcCashRunway_ 新規追加 + dmBuildCfOutput_ 末尾に 5 行追加 | +70行 |
600_report/608_datamart_render.js | ランウェイ行への条件付き書式ルール追加 | +30行 |
100_config/101_sys_config.js | 03_sys_params 初期データに 2 キー追加 | +2行 |
docs/spec/spec_cf.md | MAS-008 セクションの仕様追記 | +20行 |
- 既存動作への影響なし: 既存 CF 出力(単月・YTD)の行順序・書式は不変。末尾に追加のみ
- DTO / DDL への影響なし: CF 出力タブ自体は動的生成(DDL 管理外)
- 既存
cfOp/cashEndの計算ロジック不変: 読み取り専用で再利用
注意事項
filterWithRecalcTotalvsfilterNonAdditiveAsLast/Avgの使い分け: 既存 CF 金額・残高はfilterWithRecalcTotal(加算可能)、月末現預金残高・バーンレート・ランウェイは非加算なので新規のfilterNonAdditiveAsLast/Avgを使う(失敗パターン #1 継承)- ゼロ除算の防止:
burnRate = 0またはNaN/Infinityの場合はrunway = Infinityに設定し、Sheet 書き込み時は空白化する(失敗パターン #2 継承) - 資金ショート月の表現:
cashEnd <= 0の月はrunway = 0(Infinity でも空白でもない)。条件付き書式で濃赤ハイライト - 過去月不足時のバーンレート: 創業直後など過去 N ヶ月分の実績がない場合、参照可能な過去月のみで平均(
cntカウンタで割る)。1 ヶ月もなければ 0 - 列インデックスのハードコード禁止:
cfRowIdxの管理は既存buildCfRowに委譲、手動で行番号を数えない(失敗パターン #18 系) - 条件付き書式の範囲指定: ランウェイ行の「E 列〜P 列」(12 ヶ月)のみ対象。Total 列(D 列)は平均や代表値なので閾値判定から除外
- 過去の実績月(境界月以前)も warning 判定対象:
isActualOnlyフィルタで空白化される月は自動的に判定対象外となる Infinityを Sheet に書けない:runway.map(v => isFinite(v) ? v : '')で空白化必須。条件付き書式は空白を無視するUtils.getSystemParamのキャッシュ: 計算中に 2 回以上呼ばない。ctx.runwayMinThresholdとして 1 回で取得しておく03_sys_paramsの型: 値は文字列で返るため必ずNumber(...)でラップし、|| デフォルトでフォールバック- YTD 出力には Runway を追加しない: YTD(累積)ではランウェイ概念が成立しない。本仕様では単月
outCのみに追加 - 選択月スナップ(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 カウンタで割るため実在月のみ採用 |
| 6 | cfOp[i] = 0(ブレークイーブン月) | 流出分 = 0(バーンレート算出に寄与せず) | 中立月を流出扱いしない・流入扱いしない |
| 7 | 非加算(Total 列): 月末現預金残高 | Total = 末月の値 | 時点残高なので単純加算不可。filterNonAdditiveAsLast |
| 8 | 非加算(Total 列): バーンレート | Total = 非空月の平均 | 平均 of 月次平均。filterNonAdditiveAsAvg |
| 9 | 非加算(Total 列): ランウェイ | Total = 末月の値(非空時) | 時点指標。filterNonAdditiveAsLast |
| 10 | isActualOnly=true で境界月以降を空白化 | 空白月はバーンレート・ランウェイとも空白 | filterValues の挙動を継承、追加ロジック不要 |
| 11 | ランウェイ >= 120 ヶ月(10 年超)の異常値 | そのまま表示(丸めなし) | 計算根拠を透明にし、外れ値を隠さない |
| 12 | 過去 N ヶ月に 1 ヶ月だけ突発的な高額流出(例: 退職金) | 平均が歪む | 後続案件で「メディアン or 外れ値除外平均」を検討(人間検討事項参照) |
実データ検証(事前確認項目)
| 確認項目 | 確認方法 | 確認結果(Phase 1 で調査済) |
|---|---|---|
CF マートの cfOp / cashEnd 配列の型と範囲 | 605_datamart_cf.js:21-33 を Read | ✅ Array(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.md | CF(間接法)の既存仕様。本仕様の追加セクションはこの末尾に追記 |
| 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.md | isActualOnly / 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>