概要

項目内容
案件IDMAS-033
案件名Rule of 40 スコアボード
カテゴリFP&A・レポーティング(BizDev / プロダクト事業メトリクス)
PhaseP3
優先度
ステータス未着手(仕様書のみ作成)
所要時間見積3〜4h(新規実装+MCP実データ検証含む)
対象ファイル600_report/612_dashboard_rule_of_40.js(新規作成)
100_config/101_sys_config.js(Read のみ。実際のメニュー追加は 000_infra/002_constants.jsMENU_DEFINITION
000_infra/002_constants.jsMENU_DEFINITION へ 1 項目追加)
前提案件F-28(ARR/MRR トラッキング・未着手)/F-32(プロダクト別P/L・未着手)
※両案件ともステータス「未着手」のため、F-33 単独で先行実装する場合は「人間が検討すべき事項」で依存判断を確定させる
依存しない科目マスタ 11_mst_account大分類 列)・仕訳42_trn_journal のみを参照し、両案件(F-28/F-32)の実装有無に依存しない最小独立設計を採用する

目的

プロダクト事業の健全性を SaaS 業界標準指標「Rule of 40」(成長率 + 利益率 ≥ 40%)で可視化し、成長速度と収益性のバランスに関する経営意思決定を支援する。

Rule of 40 の背景: Brad Feld (Foundry Group) / Bessemer Venture Partners 等が提唱した SaaS 事業の健全性指標。「成長率を優先するか、利益率を優先するか」の二項対立を、両者の合計で総合評価する考え方で、40% 以上であれば「成長と利益のバランスが健全」とされる。

本機能の出力:

  • プロダクト事業(フィルタ条件は 03_sys_params パラメータで指定)の月次スコアをダッシュボードシート 95_dashboard_rule_of_40 に表示
  • 直近 13 ヶ月(または利用可能な全期間)の TTM 成長率・TTM 営業利益率・Rule of 40 スコアを時系列で並べ、推移を把握できるようにする
  • Human-in-the-Loop: ヘッダー行に計算前提(フィルタ条件・利益率定義・成長率定義)を明記し、利用者が算出根拠を目視で検証できるようにする

現在のコード

新規実装。対応するシート・関数なし。

類似実装の参照元: 600_report/609_datamart_kpi.js(F-03 KPI ダッシュボード)

  • 93_kpi_dashboard タブを ss.insertSheet() で動的生成し sheet.clear() でクリアしてから再描画するパターンを踏襲する
  • 境界月の検出(仕訳から過去方向に非空セル末尾を走査)・行番号を事前計算してから setValues で一括書き込みする方式を踏襲する

関連する既存モジュール:

  • 200_data/202_repository.jsJournalRepository.findAll() — 仕訳帳 DTO 取得(戻り値: { headers: string[], dtos: JournalEntryDTO[] }
  • 200_data/202_repository.jsAccountRepository.findAsMap() — 科目名 → {stmt, cat} マップ(キャッシュ付き。キャッシュは AccountRepository.resetCache() でリセット可能)
  • 000_infra/002_constants.jsConstants.getParam(key, defaultVal)03_sys_params シート(列 A=キー / 列 B=値)からパラメータを読み取り、引数 defaultVal の型に応じて Number or String を返す。未設定時は defaultVal を返す
  • 000_infra/004_utils.jsUtils.parseDateToYm(date) — Date | 文字列を YYYY-MM 形式に正規化するヘルパー(既存の 600_report/ で多用)
  • 000_infra/002_constants.jsConstants.MENU_DEFINITION — メニューは宣言的定義で管理(ハードコードされた ui.createMenu() 呼び出しはない)

修正方針

アーキテクチャ概要

┌─────────────────────┐   ┌─────────────────────┐
│ 42_trn_journal      │   │ 11_mst_account      │
│ (JournalRepository) │   │ (AccountRepository) │
└──────────┬──────────┘   └──────────┬──────────┘
           │ findAll()                │ findAsMap()
           │ {headers, dtos}          │ {科目名: {stmt, cat}}
           ▼                          ▼
┌──────────────────────────────────────────────┐
│ 600_report/612_dashboard_rule_of_40.js       │
│   1. プロダクト事業フィルタ(PJ名/組織名)   │
│   2. 月次 TTM 集計(売上・費用の12ヶ月合計)│
│   3. 前年同月 TTM 成長率・TTM 営業利益率    │
│   4. Rule of 40 = 成長率 + 営業利益率        │
└──────────┬───────────────────────────────────┘
           │ setValues()
           ▼
┌──────────────────────────────────────────────┐
│ 95_dashboard_rule_of_40                      │
│  (動的生成シート・DDL setupAllSchemas 管理外)│
└──────────────────────────────────────────────┘

出力先シート

  • シート名: 95_dashboard_rule_of_40
  • DDL 管理: 管理対象外(setupAllSchemas に追加しない)
  • CLAUDE.md 追記: ## DDL (setupAllSchemas) で管理されないタブ セクションに 95_dashboard_rule_of_40 を追記する(既存の 93_kpi_dashboard に続ける)
  • 生成方法: ss.getSheetByName('95_dashboard_rule_of_40') が null の場合 ss.insertSheet()、存在する場合は sheet.clear() で全消去してから再描画(609_datamart_kpi.jsbuildKpiDashboard と同一パターン)

データ取得

var journal = JournalRepository.findAll();  // {headers, dtos: JournalEntryDTO[]}
var acctMap = AccountRepository.findAsMap(); // {科目名: {stmt: 'P/L'|'B/S', cat: '売上高'|...}}
  • JournalEntryDTO のプロパティは 000_infra/003_contracts.js@typedef に準拠
  • 使用するキー: 発生日(P/L計上日) / 科目名 / 税抜金額_実績 / PJ名 / 組織名 / 仕訳ステータス / 収支区分
  • findAsMap() の戻り値構造は {stmt, cat} であり、findAsMap_() 等の派生関数名は存在しない(Phase 1 で 202_repository.js L323-341 を Read して確認済み)

集計単位(TTM: Trailing Twelve Months)

各基準月 YM について、発生日(P/L計上日)YM - 11ヶ月 から YM の範囲に入る仕訳を集計する。

  • 基準月の列挙: 仕訳の最古月〜最新月を Utils.parseDateToYm() で収集し、ユニーク化して昇順ソート
  • TTM 集計の最小データ要件: YM 時点で基準月を含む 12 ヶ月のデータが必要。不足月は "-" 表示
  • YoY 成長率の最小データ要件: YMYM - 12ヶ月(前年同月)の両時点で TTM が計算可能であること(= 24 ヶ月以上のデータが必要)

計算定義(暫定。「人間が検討すべき事項」確定後に最終化する)

以下 3 点は Phase 1 のコード調査時点での暫定定義。実装前に MCP 実データ検証(後述)と「人間が検討すべき事項」で確定させる。

指標暫定定義
TTM 売上高大分類 が売上に該当する科目(暫定: 大分類 === '売上高')の 税抜金額_実績 の 12 ヶ月合計
TTM 全費用諸表区分 === 'P/L' かつ 大分類 が売上以外の科目の 税抜金額_実績 の 12 ヶ月合計(絶対値で加算)
TTM 営業利益TTM売上 − TTM全費用
成長率`(TTM売上_当月 − TTM売上_前年同月) /
営業利益率(TTM)TTM営業利益 / TTM売上 × 100 (%)
Rule of 40 スコア成長率 + 営業利益率
判定スコア ≥ 40✅ OK / スコア < 40⚠️ 要改善

注記: 「売上」に該当する 大分類 の正確な文字列は 11_mst_account の実データに依存するため、実装時に MCP で確認する(後述「実データ検証」)。

プロダクト事業フィルタ

  • フィルタ対象: JournalEntryDTO.PJ名 または JournalEntryDTO.組織名(どちらを使うかは「人間が検討すべき事項」で確定)
  • パラメータ取得: Constants.getParam('RULE_OF_40_FILTER_KEY', 'PJ名') および Constants.getParam('RULE_OF_40_FILTER_VALUE', '')03_sys_params から読み取る
    • RULE_OF_40_FILTER_KEY: 'PJ名' | '組織名'(デフォルト: 'PJ名'
    • RULE_OF_40_FILTER_VALUE: 完全一致する値(例: 'プロダクト事業')。空文字の場合はフィルタなし(全仕訳が対象)+ Utils.logInfo で警告出力
  • マッチング方式: 完全一致(trim().equals())。複数値対応が必要になったらカンマ区切り対応を検討(将来拡張)

HitL 対応(Human-in-the-Loop)

ダッシュボードのヘッダー領域(A1:H4 程度)に計算前提を明記:

セル例
1Rule of 40 スコアボード (大タイトル)
2フィルタ条件: {RULE_OF_40_FILTER_KEY} = "{RULE_OF_40_FILTER_VALUE}"
3利益率定義: 営業利益率 = (TTM売上 − TTM全費用) / TTM売上 × 100
4`成長率定義: TTM売上 YoY = (当月TTM − 前年同月TTM) /
5(空行)
6表ヘッダー: 基準年月 / TTM売上 / TTM営業利益 / 成長率(%) / 営業利益率(%) / Rule of 40 スコア / 判定 / 備考
7〜直近 13 ヶ月 or 全期間のデータ行

メニュー登録

  • 宣言的定義: 000_infra/002_constants.jsConstants.MENU_DEFINITION に追加する(onOpen() の実装は MENU_DEFINITION を動的展開するため、ui.createMenu() をハードコードしない)
  • 追加先カテゴリ: '📋 サイドバー: 📊 マート更新'(F-03 KPI ダッシュボード等が所属する既存カテゴリ)
  • 追加項目: { label: '📏 Rule of 40 スコア更新', funcName: 'buildRuleOf40Dashboard', description: '95_dashboard_rule_of_40 (Rule of 40 スコアボード) を再計算' }
  • 造語禁止: '📋 サイドバー: 📊 マート更新' は Phase 1 で 002_constants.js L230 を Read して実在を確認した文字列。新規カテゴリは作成しない

影響範囲

変更ファイル

ファイル変更量内容
600_report/612_dashboard_rule_of_40.js新規 約 250 行buildRuleOf40Dashboard() のエントリ関数+TTM集計ヘルパー+出力ヘルパー
000_infra/002_constants.js追記 1 行(MENU_DEFINITION 内)既存カテゴリ '📋 サイドバー: 📊 マート更新' に 1 項目追加
CLAUDE.md追記 1 行## DDL (setupAllSchemas) で管理されないタブ95_dashboard_rule_of_40 を追記

変更しないファイル

  • 42_trn_journal(読み取り専用)
  • 11_mst_account(読み取り専用)
  • 100_config/101_sys_config.jssetupAllSchemas / DDL 定義(95_dashboard_rule_of_40 は DDL 管理外)
  • 100_config/101_sys_config.jsonOpen() 本体(MENU_DEFINITION を動的展開しているため、メニュー追加は 002_constants.js 側のみ)
  • 000_infra/003_contracts.js(既存の JournalEntryDTO @typedef をそのまま使用)
  • 200_data/202_repository.js(既存の JournalRepository / AccountRepository をそのまま使用)

既存動作への影響

  • 読み取り専用・独立ダッシュボード出力のため、トランザクションデータや既存マート(61_pl_monthly 等)への副作用はない
  • AccountRepository.findAsMap() のキャッシュ(_cache)を共有利用するが、Read 専用なので他機能のキャッシュ状態を破壊しない

注意事項

  1. 読み取り専用設計: この機能は 42_trn_journal11_mst_account読み取り専用で集計し、独立したダッシュボードシート 95_dashboard_rule_of_40 に書き出す。トランザクションデータへの二重更新リスクはないため、排他ロック機構(LockService)は不要
  2. 列参照はヘッダー名ベース: 42_trn_journal / 11_mst_account のアクセスは JournalRepository.findAll() / AccountRepository.findAsMap() 経由で行うため、DTO プロパティ名(= ヘッダー名)でアクセスする。列番号ハードコード禁止(CLAUDE.md コーディング規約準拠)
  3. 有効フラグ=FALSE はスキップ: AccountRepository.findAsMap() は内部で 有効フラグFALSE のレコードを既にスキップしている(202_repository.js L330 で確認済み)。JournalRepository有効フラグ 列を持たないため追加スキップ不要
  4. 存在しない関数名を使用しない: AccountRepository.findAsMap_() / findAccountMap_() 等の派生関数名は存在しない。必ず findAsMap()(アンダースコア末尾なし、_cacheresetCache のみ)を使用する
  5. 仕訳ステータスの完全一致判定: 仕訳ステータスコード === "JSTAT_REVERSAL" 等のコード値や 仕訳ステータス === "仕訳振替" の文字列で判定する場合は === の完全一致。実際の格納値は実データ検証で確認(CLAUDE.md「仕訳振替の判定は === "仕訳振替" の完全一致」規約準拠)
  6. メニューは MENU_DEFINITION 経由: 100_config/101_sys_config.jsonOpen() には直接編集を加えず、000_infra/002_constants.jsConstants.MENU_DEFINITION に宣言的に追加する(ハードコード禁止)
  7. 絶対値計算の取り扱い: 費用は 税抜金額_実績 が正値で入っている前提(科目の符号反転は DTO レベルでは行わず、集計時に Math.abs() で絶対値化する)。実際の符号は MCP 実データで確認する
  8. TTM 集計の性能: 仕訳件数 × 月数のループで O(N × M) になるため、前処理で仕訳を YYYY-MMsumMap に集約してから月次ループを回す実装にすること(609_datamart_kpi.js の境界月検出パターンを参考に)

エッジケース

#条件表示値 / 挙動理由
1前年同月 TTM 売上 = 0(成長率の分母ゼロ)成長率 = "-"、スコア = "-"、判定 = "-"ゼロ除算回避。前年データ不足または創業初年度。Math.abs(前年TTM売上) === 0 で判定
2当月 TTM 売上 = 0(利益率の分母ゼロ)利益率 = "-"、スコア = "-"、判定 = "-"ゼロ除算回避。売上未計上月(例: 開発フェーズのみ)
3集計対象が 24 ヶ月未満(YoY 計算不可)成長率 = "-"、スコア = "-"、判定 = "-"TTM 成長率には前年同月の TTM が必要。データ開始から 24 ヶ月未満は算出不能
4集計対象が 12 ヶ月未満(TTM 計算不可)当該行はそもそも出力しないTTM は 12 ヶ月合計のため、12 ヶ月未満では暫定値として意味を持たない
5営業利益 < 0(赤字)マイナス値をそのまま表示(例: -15.2%正常な挙動。Rule of 40 スコアにマイナス値が反映されるのが仕様(Bessemer / Brad Feld の定義通り)
6Rule of 40 スコア ≥ 40✅ OK成長と収益性のバランスが良好
7Rule of 40 スコア < 40⚠️ 要改善改善が必要な状態(成長率を上げるか利益率を改善するか)
8プロダクト事業フィルタに一致するレコード = 0 件全行 "-" + Utils.logInfo で警告フィルタ設定ミス or プロダクト事業未開始の可能性。ヘッダー行のフィルタ条件表示で利用者が気づけるようにする
9RULE_OF_40_FILTER_VALUE が空文字フィルタなし(全仕訳が対象)+ Utils.logInfo('fn', 'フィルタ値未設定のため全仕訳が対象')デフォルトは無フィルタ。運用開始前に 03_sys_params への登録を促すログ出力
10大分類 不明な科目(acctMap[科目名] が undefined)該当仕訳をスキップ + Utils.logInfo で件数集計科目マスタ未登録科目を誤って売上/費用にカウントしないための防御。CLAUDE.md「科目マスタ未登録科目はエラー」規約との整合
11仕訳ステータス'仕訳振替' の仕訳集計から除外仕訳振替は P/L 残高の付け替えで、同一金額の借方/貸方が立つため二重計上を防ぐ必要がある。「人間が検討すべき事項」の仕訳ステータスフィルタ方針に従う
12発生日(P/L計上日) が空 or 不正な Date該当仕訳をスキップ + Utils.logInfo で件数集計Utils.parseDateToYm() が null/空を返す場合は集計対象外
13税抜金額_実績 が空 or 数値以外`Number(...)
14仕訳帳が空(件数 0)シートヘッダーのみ出力 + Utils.logInfo('fn', '仕訳データが存在しません')新規 SS の初回実行時に想定
15成長率・利益率の数値オーバーフロー(例: 過去期売上が極小で当期が巨額)表示はそのまま(上限なし)+ 数値フォーマットで 0.0% を適用Rule of 40 は絶対値の意味を持つため上限クリップしない

実データ検証

実装前に MCP ツールで以下を確認すること(Phase 1 のコード読み取りでは確定できない、実データ依存の情報):

#確認対象確認方法用途
111_mst_account大分類 列の実値一覧mcp__google-spreadsheet__query_range11_mst_account!D:D(または buildHeaderIndex_ 後に 大分類 列)を取得してユニーク化売上に該当する 大分類 文字列の正確な判定('売上高' / '売上' / '売上高(事業)' 等の揺れを確認)
242_trn_journal仕訳ステータス 列の実値一覧mcp__google-spreadsheet__query_range でユニーク値を取得'自動計上' / '手動' / '仕訳振替' の完全一致フィルタ対象を確定
342_trn_journalPJ名 列の実値一覧MCP で頻度集計プロダクト事業識別に使用する値の候補特定(例: 'プロダクト事業' / 'SaaS事業' / '指定なし_共通費など'
442_trn_journal組織名 列の実値一覧MCP で頻度集計PJ名 以外に組織名でフィルタする選択肢を検討する際の参考
5税抜金額_実績 の符号運用42_trn_journal収支区分 === '支出' のレコード 10 件程度をサンプル取得費用側の金額が正値か負値かを確認。正値なら絶対値化不要、負値なら Math.abs() 必須
603_sys_paramsRULE_OF_40_FILTER_KEY / RULE_OF_40_FILTER_VALUE を登録するか運用担当と合意未登録時のデフォルト値(本仕様書では 'PJ名' / '')で期待通りの挙動となるかを利用者と確認

注意: 本仕様書では暫定定義として 大分類 === '売上高' を使うが、実データの実値により Constants.RULE_OF_40_SALES_CATEGORIES = ['売上高', '売上高(本業)'] 等の配列定数に外出しする設計に変更する可能性がある。MCP 検証時に決定すること。

関連ドキュメント

リンク関連箇所
TODO_future.md§3.2.1 プロダクト事業メトリクス(F-28 / F-29 / F-30 / F-31 / F-32 / F-33)。F-33 の案件定義行(優先度★・P3・BizDev)
dev_F-28_arr_mrr_tracking.mdF-28 ARR/MRR トラッキング。成長率指標の代替ソース候補(ARR YoY)。F-33 実装前の依存判断で参照
dev_F-03_kpi_dashboard.mdF-03 KPI ダッシュボード。93_kpi_dashboard 動的生成パターンの参考実装元
CLAUDE.md## DDL (setupAllSchemas) で管理されないタブ セクション(95_dashboard_rule_of_40 追記対象)。コーディング規約(ヘッダー名ベース列参照・有効フラグ=FALSE スキップ)。「変更時の動作確認テスト」(600_report/6*_datamart_*.jsマート更新テスト)
prd.mdHuman-in-the-Loop ポリシー。ダッシュボード計算前提の明示表示方針

人間が検討すべき事項

TODO_future.md の F-33 行から転記: 「利益率の定義(営業利益率 vs EBITDA margin)。成長率の算出期間(MoM vs YoY)」

上記 2 点に加え、以下 5 項目を実装前に確定する必要がある。

1. プロダクト事業の識別方法(実装前の必須判断)

  • 論点: 42_trn_journalPJ名 でフィルタするか、組織名 でフィルタするか、両方の AND/OR 条件が必要か
  • 選択肢:
    • A. PJ名 単独フィルタ(本仕様書のデフォルト): シンプルだが、PJ 名が「プロダクト」プレフィックスで統一されていないと漏れる
    • B. 組織名 単独フィルタ: プロダクト事業部が組織として独立している前提。受託 PJ が同じ組織名で計上されていると混在する
    • C. PJ名 AND 組織名 の複合フィルタ: 最も正確だが運用負荷が高い
    • D. 14_mst_project マスタに「事業区分」列を追加し、マスタ経由で識別: 本格運用向き(F-32 プロダクト別 P/L と共通基盤化可能)
  • 判断タイミング: F-33 実装前。F-32(プロダクト別 P/L)が先行実装される場合は D 案で統合する
  • 本仕様書のデフォルト: A 案。Constants.getParam('RULE_OF_40_FILTER_KEY', 'PJ名') で切替可能にする

2. 利益率の定義(TODO_future.md 元記載項目)

  • 論点: 営業利益率(デフォルト)と EBITDA マージンのどちらを採用するか
  • 選択肢:
    • A. 営業利益率 = (TTM売上 − TTM全費用) / TTM売上 × 100(本仕様書のデフォルト): 一般企業の標準指標
    • B. EBITDA マージン = (営業利益 + 減価償却費 + 無形資産償却費) / TTM売上 × 100: SaaS 業界標準。資本集約度が異なる企業間の比較に有利
  • EBITDA 採用時の追加作業:
    1. 11_mst_account で減価償却費・のれん償却費等に該当する科目名を特定(例: '減価償却費' / '無形固定資産償却費'
    2. 特定科目の金額を営業利益に足し戻す処理を追加
  • 本仕様書のデフォルト: A 案。将来的に B 案に切り替える場合は Constants.getParam('RULE_OF_40_PROFIT_METRIC', 'OPERATING')'OPERATING' / 'EBITDA' を切替可能にする設計を取る

3. 成長率の算出期間(TODO_future.md 元記載項目)

  • 論点: MoM(Month-over-Month、前月比)と YoY(Year-over-Year、前年同月比)のどちらか
  • 選択肢:
    • A. TTM YoY 成長率(本仕様書のデフォルト): (当月TTM − 前年同月TTM) / |前年同月TTM| × 100。季節変動を吸収する SaaS 業界標準
    • B. MoM 成長率: (当月TTM − 前月TTM) / |前月TTM| × 100。変化の検知が早いが季節変動を拾う
    • C. MoM 成長率を年率換算: ((当月TTM / 前月TTM)^12 − 1) × 100。単月の変動が年率に増幅されるため推奨しない
  • 本仕様書のデフォルト: A 案

4. 成長率のデータソース(F-28 との依存判断)

  • 論点: 成長率指標のベースを 42_trn_journal ベースの TTM 売上高 YoY とするか、F-28(ARR/MRR)実装後の ARR YoY とするか
  • 選択肢:
    • A. 42_trn_journal TTM 売上 YoY(本仕様書のデフォルト・F-28 非依存): F-33 を F-28 より先に実装可能。ただしストック収益(MRR)とフロー収益(スポット)が混在した指標になる
    • B. F-28 の ARR YoY 成長率(F-28 依存): SaaS 的には正統。ただし F-28 のステータスは現時点「未着手」(Phase 1 で TODO_future.md L266 を確認済み)
  • 判断タイミング: F-28 のスコープ確定後。F-28 が実装されるまでは A 案で先行運用し、F-28 完了後に B 案へ切り替える(Constants.getParam('RULE_OF_40_GROWTH_SOURCE', 'JOURNAL_TTM_SALES') 切替)
  • 本仕様書のデフォルト: A 案(F-28 非依存で先行実装可能とする)

5. 仕訳ステータスのフィルタ方針

  • 論点: どの 仕訳ステータス を集計対象に含めるか
  • 選択肢:
    • A. '自動計上' のみ: RPA 経由の信頼できる仕訳に限定。手動入力ミスを排除
    • B. '自動計上''手動' の両方を対象、'仕訳振替' のみ除外(本仕様書のデフォルト): 人手確認済みの手動仕訳も含めて実態を把握。仕訳振替は二重計上防止のため除外
    • C. 全ステータスを対象: 最も包括的だが、検討用の仕訳振替まで入ってしまう
  • 判断タイミング: 実データ検証(上記「実データ検証」#2)完了後
  • 本仕様書のデフォルト: B 案

6. 判定閾値の調整可能性

  • 論点: スコア 40 固定で良いか、段階判定(40 未満 = 要改善 / 40〜60 = 良好 / 60 以上 = 優秀)が必要か
  • 選択肢:
    • A. 40 固定の 2 段階(本仕様書のデフォルト): ✅ OK / ⚠️ 要改善
    • B. Constants.getParam('RULE_OF_40_THRESHOLD', 40) で閾値を可変化
    • C. 3 段階 or 5 段階(例: 60 以上 🚀 優秀 / 40〜60 ✅ OK / 20〜40 ⚠️ 要改善 / 0〜20 🔴 警戒 / 0 未満 🚨 緊急
  • 本仕様書のデフォルト: A 案(運用開始後に B/C へ拡張)

7. 再実行頻度と月次スナップショット保存の要否

  • 論点: 毎回 95_dashboard_rule_of_40 を全置換する方式で運用するか、月次でスナップショットを別シートに保存するか
  • 本仕様書のデフォルト: 全置換方式(93_kpi_dashboard と同じ運用)。スナップショットが必要になったら別途 96_snapshot_rule_of_40_YYYYMM 的な保存機能を追加(F-02 前年度 P/L スナップショットに類似)

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

実装時は以下のプロンプトを Claude Code に与えて自律実行させる。行頭 4 スペースインデントで記述(コードブロック不使用):

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-33「Rule of 40 スコアボード」を実装してください。

## 実行前タスク(全て Read で裏取りすること。名前から推測しない)

1. `000_infra/003_contracts.js` を Read し、JournalEntryDTO の全プロパティ名を確認する
   (特に `発生日(P/L計上日)` / `科目名` / `税抜金額_実績` / `PJ名` / `組織名` /
   `仕訳ステータス` / `収支区分` の実際のキー文字列)
2. `200_data/202_repository.js` を Read し、以下を確認する:
   - JournalRepository.findAll() の戻り値型が { headers: string[], dtos: JournalEntryDTO[] }
   - AccountRepository.findAsMap() の戻り値型が { [科目名]: {stmt, cat} }
   - findAsMap_() / findAccountMap_() 等の派生関数名は存在しない
3. `000_infra/002_constants.js` を Read し、以下を確認する:
   - Constants.getParam(key, defaultVal) が 03_sys_params からパラメータを読む
   - MENU_DEFINITION の '📋 サイドバー: 📊 マート更新' カテゴリの配置順
4. `600_report/609_datamart_kpi.js` を Read し、以下のパターンを踏襲する:
   - ss.insertSheet() + sheet.clear() による動的生成
   - 境界月検出・行番号事前計算・一括 setValues()
5. `000_infra/004_utils.js` を Read し、Utils.parseDateToYm() / Utils.logInfo() /
   Utils.logError() のシグネチャを確認する
6. MCP で以下を確認する(実データ依存項目):
   - 11_mst_account の `大分類` 列の実値一覧(売上に該当する文字列特定)
   - 42_trn_journal の `仕訳ステータス` 列の実値一覧
   - 42_trn_journal の `PJ名` / `組織名` 列の実値一覧(プロダクト事業の識別値)
   - 42_trn_journal の `税抜金額_実績` の符号運用(費用が正値か負値か)

## 修正対象ファイル

- `600_report/612_dashboard_rule_of_40.js`(新規作成・約250行)
  - エントリ関数: `buildRuleOf40Dashboard()`
  - 内部ヘルパー: `collectTtmJournal_(journal, acctMap, filterKey, filterVal)` /
    `computeRuleOf40_(ttmByMonth)` / `renderRuleOf40Sheet_(sheet, rows, ctx)`
- `000_infra/002_constants.js`(MENU_DEFINITION への1項目追加のみ)
  - '📋 サイドバー: 📊 マート更新' カテゴリ配列の末尾付近に
    { label: '📏 Rule of 40 スコア更新', funcName: 'buildRuleOf40Dashboard',
      description: '95_dashboard_rule_of_40 (Rule of 40 スコアボード) を再計算' } を追加
- `CLAUDE.md`(1行追記のみ)
  - `## DDL (setupAllSchemas) で管理されないタブ` の列挙に
    `95_dashboard_rule_of_40` を追記

## 実装内容

1. 出力先シート `95_dashboard_rule_of_40` の取得 or 新規作成+ clear()
2. JournalRepository.findAll() で仕訳 DTO 取得(戻り値: {headers, dtos})
3. AccountRepository.findAsMap() で科目マスタ取得
   (戻り値: { [科目名]: {stmt, cat} })
4. プロダクト事業フィルタを適用:
   - Constants.getParam('RULE_OF_40_FILTER_KEY', 'PJ名') で 'PJ名' or '組織名' を取得
   - Constants.getParam('RULE_OF_40_FILTER_VALUE', '') でフィルタ値を取得
   - 値が空なら無フィルタ + Utils.logInfo で警告
   - 値がある場合は完全一致(trim 比較)
5. 仕訳ステータスフィルタ: '仕訳振替' を除外(本仕様書デフォルト方針 B 案)
6. 事前集約: filteredJournal を月(YYYY-MM)×{売上, 費用} の2次元 Map に集計
   (O(N × M) 避けるため。609_datamart_kpi.js の境界月検出パターンを参考)
7. 月次 TTM ループ:
   - 基準月 YM について、YM-11 から YM までの12ヶ月分の売上・費用を集計
   - YM-12 から YM-23 までの12ヶ月分(前年同月 TTM)も集計
8. 成長率・営業利益率・Rule of 40 スコアの計算:
   - エッジケース(当期売上=0 / 前年売上=0 / 24ヶ月未満)はすべて '-' で返す
   - 判定: score >= 40 → '✅ OK' / score < 40 → '⚠️ 要改善'
9. ヘッダー領域(A1:H4)に計算前提を明記:
   - A1: 'Rule of 40 スコアボード'
   - A2: 'フィルタ条件: {key} = "{value}"'
   - A3: '利益率定義: 営業利益率 = (TTM売上 − TTM全費用) / TTM売上 × 100'
   - A4: '成長率定義: TTM売上 YoY = (当月TTM − 前年同月TTM) / |前年同月TTM| × 100'
10. 表ヘッダー(6行目):
    '基準年月' / 'TTM売上' / 'TTM営業利益' / '成長率(%)' /
    '営業利益率(%)' / 'Rule of 40 スコア' / '判定' / '備考'
11. データ行(7行目以降)を直近13ヶ月 or 全期間で出力
12. 数値フォーマット適用:
    - 金額列: Constants.NUMBER_FORMATS.CURRENCY
    - パーセント列: Constants.NUMBER_FORMATS.PERCENT or '0.0%'
13. 002_constants.js の MENU_DEFINITION に 1 項目追加
14. CLAUDE.md の DDL 管理外タブ一覧に追記

## 制約(CLAUDE.md コーディング規約準拠)

- 列参照は必ずヘッダー名ベース(DTO プロパティアクセス)。列番号ハードコード禁止
- 42_trn_journal および 11_mst_account への書き込み禁止(読み取り専用)
- 95_dashboard_rule_of_40 は DDL 管理外。setupAllSchemas に追加しない
- AccountRepository.findAsMap_() 等の存在しない関数名を使用しない
- メニュー追加は MENU_DEFINITION 経由。onOpen() 本体は変更しない
- 仕訳ステータス判定は === の完全一致
- 有効フラグ=FALSE のマスタレコードはスキップ
  (findAsMap() が内部で処理済み。JournalRepository には該当列なし)

## エッジケース実装(本仕様書の「エッジケース」セクション参照)

| 条件 | 処理 |
|------|------|
| 前年 TTM 売上 = 0 | 成長率 = "-" |
| 当年 TTM 売上 = 0 | 利益率 = "-" |
| TTM 計算に 24ヶ月未満のデータ | 成長率 = "-" |
| 営業利益 < 0 | マイナス値をそのまま表示(正常) |
| フィルタ一致レコード = 0件 | "-" + Utils.logInfo で警告出力 |
| 科目マスタ未登録の科目 | 該当仕訳をスキップ + Utils.logInfo で件数集計 |
| 発生日(P/L計上日) が空 | 該当仕訳をスキップ |
| 税抜金額_実績 が数値以外 | Number(...) || 0 で 0 として扱う |

## 動作確認

1. npm run push:dev でデプロイ
2. dev スプレッドシートの `🚀 BizLP` > `操作パネルを開く` からサイドバーを開き、
   「📏 Rule of 40 スコア更新」を実行
3. 95_dashboard_rule_of_40 が生成され、ヘッダー行(計算前提)と
   データ行(直近13ヶ月)が正しく出力されることを確認
4. 以下のエッジケースを目視確認:
   - 売上ゼロ月で利益率 = "-"
   - 24ヶ月未満のデータで成長率 = "-"
   - 03_sys_params に RULE_OF_40_FILTER_VALUE を設定してフィルタ動作を確認
   - 空文字設定時に Utils.logInfo で警告出力されることを確認
5. 数値フォーマット(通貨・パーセント)が適用されていることを確認
6. 動作確認後に git commit → git push → PR → main マージの順で進める

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

| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| Phase 1(調査・設計) | あり | ファイル構造把握・固有名詞裏取り・MCP 実データ検証 |
| Phase 2(実装) | なし | Phase 1 確定内容の書き下しに徹する |

推奨実行モデル

工程推奨モデル理由
仕様書作成(本プロンプト)Claude Sonnet 4.6複数ファイル横断調査・既存パターン適用判断が必要(Opus まで要さない)
Phase 1(実行前タスク・MCP 実データ検証)Claude Sonnet 4.6複数ファイル横断調査・実データ分類判断
Phase 2(612_dashboard_rule_of_40.js 新規実装)Claude Opus 4.6新規ファイル設計・TTM 集計ロジック・エッジケース網羅・会計ロジック(売上/費用/仕訳ステータスの意味理解)が必要
Phase 3(002_constants.js / CLAUDE.md 追記)Claude Haiku 4.5確定した1行追記のみ。判断要素なし

変更履歴

日付変更内容
2026-04-21初版作成。TTMベースのRule of 40スコア(成長率+利益率)計算方針、エッジケース15項目(ゼロ除算・データ不足・科目マスタ未登録等)、Human-in-the-Loop設計(ヘッダー行に計算前提明記)、プロダクト事業識別方法/利益率定義/成長率算出期間/成長率データソース/仕訳ステータスフィルタ/判定閾値/スナップショット保存の人間検討事項7項目を記載。新規ファイル番号は 600_report/609_datamart_kpi.js が既存のため 612_dashboard_rule_of_40.js を採用。メニューは MENU_DEFINITION 経由で 📋 サイドバー: 📊 マート更新 カテゴリに追加。F-28(ARR/MRR)未着手のため F-33 単独実装可能とする A 案(JournalRepository TTM 売上 YoY)をデフォルトに設定し、F-28 完了後に B 案(ARR YoY)へ切替可能な Constants.getParam 切替設計を採用

仕様書作成プロンプト

展開して表示

【タイムアウト回避・実行原則(v1.7・必ず遵守すること)】

  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 実行時に持ち込まないよう、Phase 1 で全項目を確定させる。各 Step の内容は以下に明示してある。

====================================================================== あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者兼仕様書ライターです。 CLIエージェント「Claude Code」として、案件 F-33「Rule of 40 スコアボード」の開発仕様書を作成してください。 仕様書を新規作成したら、docs/_config.jsonnav 配列の適切なセクションにも必ず追記すること。


Phase 1: 実行前タスク(テキスト報告禁止。即座にツール実行)

1-A: 案件定義の読み込み

  • docs/_internal/TODO_future.md で F-33 の行を検索し、案件名・カテゴリ・Phase・優先度・概要・人間が検討すべき事項を取得する。
  • 同ファイルで F-28(ARR/MRR 指標)・F-32(プロダクト別P/L)のステータス(完了/未着手)を確認し、F-33 の前提依存関係を把握する。

1-B: プロジェクト規約の読み込み

  • CLAUDE.md を Read し、コーディング規約(列参照の indexOf/buildHeaderIndex_ 必須、有効フラグ=FALSE スキップ)・ファイル番号体系・DDL 管理対象外タブ一覧(93_kpi_dashboard 等が動的生成タブである旨)を把握する。

1-C: 既存仕様書テンプレートの読み込み

  • docs/dev/ 配下から FP&A・レポート系の既存仕様書を 1 件 Read してフォーマットを把握する。候補(存在するものを選ぶ): dev_F-24_bep_analysis.mddev_F-20_yoy_comparison.mddev_F-01_variance_analysis.md

1-D: 関連コードの調査(Grep で場所を探し、Read で構造を確認する。名前から推測して書かない

以下を Read で確認し、仕様書に書く固有名詞(列名・関数名・定数名・シート名)を全て裏取りすること:

  1. 000_infra/003_contracts.jsJournalEntryDTO の全プロパティ名(発生日(P/L計上日)科目名税抜金額_実績PJ名組織名仕訳ステータス収支区分 の実際のキー文字列)を確認する。
  2. 200_data/202_repository.jsJournalRepository.findAll() の戻り値型({headers, dtos})と、AccountRepository.findAsMap() が返すオブジェクトの構造({stmt, cat} のフィールド名)を確認する。findAsMap_() 等の存在しない派生関数名を使わないこと。
  3. 000_infra/002_constants.jsConstants.getParam(key, defaultVal) の引数・戻り値型を確認する(03_sys_params からパラメータを読み取るヘルパー)。Constants.CONFIG_SHEET の実際の値('01_sys_config')を確認する。
  4. 600_report/ ディレクトリ — 既存のデータマート・ダッシュボード生成スクリプト(601_datamart_ingest.js608_datamart_render.js 等)を 1〜2 件 Read し、シート出力・関数命名・メニュー登録のパターンを把握する。新規ファイルを追加する場合は 609_ 以降の番号を使う想定だが、既存ファイルへの追記が適切か判断する。
  5. 100_config/101_sys_config.jsonOpen()ui.createMenu を Read し、F-33 の実行関数を追加すべきメニュー項目の実在する文字列を確認する(メニュー名は造語しない)。DDL setupAllSchemas での動的タブ扱いのコード上の根拠も確認する。

Phase 2: 仕様書の分割作成

出力先: docs/dev/dev_F-33_rule_of_40_scoreboard.md 【重要】絶対に 1 回のツール呼び出しで全内容を出力しない。以下の Step 順に分割実行すること。

Step 2-1: 骨格の作成(File Write、〜20行)

docs/dev/dev_F-33_rule_of_40_scoreboard.md に以下の見出しのみの骨格を Write で作成する(本文は空で可):

# F-33: Rule of 40 スコアボード
## 概要
## 目的
## 現在のコード
## 修正方針
## 影響範囲
## 注意事項
## エッジケース
## 実データ検証
## 関連ドキュメント
## 人間が検討すべき事項
## 実装プロンプト(Claude Code 用)
## 推奨実行モデル
## 変更履歴
## 仕様書作成プロンプト

Step 2-2: 概要〜注意事項の追記(File Edit または Bash、〜300行)

Phase 1 で裏取り済みの固有名詞のみ使用して以下を書き込む:

概要テーブル: 案件ID=F-33、カテゴリ=FP&A・レポーティング、Phase/優先度/所要時間=TODO_future.md から転記、対象ファイル=Phase 1 で特定したもの、前提案件=F-28・F-32

目的: プロダクト事業の健全性を「Rule of 40」スコアで可視化し、成長率と収益性のバランスに関する経営意思決定を支援する。

現在のコード: 新規実装(対応するシート・関数なし)。類似実装として 600_report/ の既存ダッシュボード生成パターン(Phase 1 で確認したもの)に従うことを明記する。

修正方針: 以下のアーキテクチャを記述する:

  • 出力先シート: 95_dashboard_rule_of_40(CLAUDE.md の「DDL で管理されないタブ」として追記。setupAllSchemas の管理対象外)
  • データ取得: JournalRepository.findAll()42_trn_journal の全仕訳 DTO を取得。AccountRepository.findAsMap() で科目マスタ(戻り値: { [科目名]: {stmt, cat} })を取得し大分類ベースで売上・費用を分類する
  • 集計単位: TTM(Trailing Twelve Months / 直近12ヶ月移動合計)。各基準月において 発生日(P/L計上日) が過去12ヶ月以内の仕訳を Utils.parseDateToYm() で判定・集計する
  • 計算定義(下記3点は「人間が検討すべき事項」確定後に最終化する暫定定義):
    • TTM 売上高: 大分類 が売上に該当する科目の 税抜金額_実績 の12ヶ月合計(実際の 大分類 文字列は実データ検証で確認)
    • 成長率: (TTM売上_当月 − TTM売上_前年同月) / |TTM売上_前年同月| × 100 (%)
    • 営業利益率(TTM): TTM営業利益 / TTM売上 × 100 (%)(営業利益 = TTM売上 − TTM全費用)
    • Rule of 40スコア: 成長率 + 営業利益率。40以上で「✅ OK」、未満で「⚠️ 要改善」と判定
  • プロダクト事業フィルタ: PJ名 または 組織名(実際の列名は Phase 1 で確認した JournalEntryDTO のキー文字列を使用)によるフィルタ。フィルタ値は Constants.getParam(key, defaultVal) 経由で 03_sys_params から読み取りパラメータ化する(フィルタ値は「人間が検討すべき事項」確定後に設定)
  • HitL 対応(Human-in-the-Loop): ダッシュボードのヘッダー行に「計算前提(フィルタ条件・利益率定義・成長率定義)」を記載し、利用者が計算根拠を目視確認できるようにする
  • メニュー登録: 100_config/101_sys_config.jsonOpen() に実行関数を追加(Phase 1 で確認した実在するメニュー文字列の下に追加。造語禁止)

影響範囲: 変更ファイル・変更量・既存動作への影響を Phase 1 の調査結果に基づき記述する。

注意事項:

  • この機能は実績データを読み取り専用で集計し独立したダッシュボードシートに書き出すため、トランザクションデータの二重更新リスクはなく排他ロック機構は不要
  • 列参照は必ずヘッダー名ベース(indexOf または buildHeaderIndex_)。列番号ハードコード禁止
  • 有効フラグ=FALSE の行は全処理でスキップ
  • AccountRepository.findAsMap() の戻り値構造は {stmt, cat} であることを Phase 1 で Read 確認済み。findAsMap_() 等の存在しない関数名を使用しない

Step 2-3a: エッジケース〜人間が検討すべき事項の追記(File Edit または Bash、〜200行)

エッジケーステーブル:

条件表示値理由
前年 TTM 売上 = 0(成長率の分母ゼロ)"-"ゼロ除算。前年データ不足または創業初年度
当年 TTM 売上 = 0(利益率の分母ゼロ)"-"ゼロ除算。売上計上なし
集計対象が 24ヶ月未満(YoY 計算不可)"-"TTM 成長率には前年同月の TTM が必要
営業利益 < 0(赤字)マイナス値をそのまま表示正常な挙動。スコアにマイナス値が反映される
Rule of 40スコア ≥ 40✅ OK成長と収益性のバランスが良好
Rule of 40スコア < 40⚠️ 要改善改善が必要な状態
プロダクト事業フィルタ一致レコード = 0件"-" + Utils.logInfo で警告フィルタ設定ミスの可能性

実データ検証(実装前に MCP で確認すること):

  • 11_mst_account大分類 列の実際の値一覧(売上・費用に相当する大分類の正確な文字列。DDL 定義と実データの乖離チェック)
  • 42_trn_journal仕訳ステータス 列の実際の格納値("自動計上" "手動" 等。フィルタ対象を完全一致で絞り込む)
  • 42_trn_journalPJ名組織名 の実際の格納値(プロダクト事業識別フィルタに使用)
  • 03_sys_params にプロダクト事業識別用のパラメータキーを追加するかどうかの確認

関連ドキュメント: TODO_future.md の F-28・F-32、docs/prd.md(Human-in-the-Loop ポリシー)

人間が検討すべき事項: TODO_future.md の F-33 行から転記した上で、以下 3 点を追記し「実装前に意思決定が必要」と明示する:

  1. プロダクト事業の識別方法: 42_trn_journalPJ名 または 組織名JournalEntryDTO の実際のキー名)のどの値でフィルタするかを確定する。14_mst_project 等のマスタとの連携も考慮する
  2. 利益率の定義: デフォルトは営業利益率。SaaS 事業では EBITDA マージンも選択肢。EBITDA を採用する場合、「減価償却費」に該当する勘定科目を 11_mst_account から特定する必要がある
  3. 成長率のデータソース: デフォルトは 42_trn_journal ベースの TTM 売上高 YoY 成長率。F-28(ARR/MRR)のステータス(Phase 1 で確認)によっては ARR YoY 成長率の方が適切。F-28 完了前に F-33 を実装するか、依存関係として待つかを判断する

Step 2-3b: 実装プロンプト〜変更履歴の追記(File Edit または Bash、〜250行)

実装プロンプトは行頭 4 スペースインデントで記述(コードブロック不使用):

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 F-33「Rule of 40 スコアボード」を実装してください。

## 実行前タスク
- `000_infra/003_contracts.js` を Read し、JournalEntryDTO の全プロパティ名を確認する
- `200_data/202_repository.js` を Read し、JournalRepository.findAll() の戻り値型と
  AccountRepository.findAsMap() の {stmt, cat} 構造を確認する
- `100_config/101_sys_config.js` を Read し、onOpen() のメニュー追加先の実在する文字列を確認する
- `600_report/` の既存ファイルを Read し、新規ファイル番号(609以降)または追記先ファイルを決定する
- MCP で 11_mst_account の大分類実値、42_trn_journal の仕訳ステータス実値、
  PJ名・組織名の実際の格納値を確認する

## 修正対象ファイル
- `600_report/609_rule_of_40.js`(新規作成)または Phase 1 で特定した既存ファイルへの追記
- `100_config/101_sys_config.js`(onOpen() へのメニュー追加のみ)

## 実装内容
1. `95_dashboard_rule_of_40` シートの作成/クリア処理
2. JournalRepository.findAll() で仕訳 DTO 取得(戻り値: {headers, dtos})
3. AccountRepository.findAsMap() で科目マスタ取得(戻り値: { [科目名]: {stmt, cat} })
4. プロダクト事業フィルタを適用(PJ名 または 組織名。Constants.getParam() で取得)
5. 月次 TTM 集計ループ(Utils.parseDateToYm() で発生日(P/L計上日)を YYYY-MM に正規化して判定)
6. 成長率・営業利益率・Rule of 40スコアの計算(エッジケース処理を必ず含める)
7. ダッシュボードヘッダー行に計算前提(フィルタ条件・利益率定義・成長率定義)を記載
8. 101_sys_config.js の onOpen() に実行メニューを追加(Read で確認した実在する文字列の下に追加)

## 制約
- 列参照は必ずヘッダー名ベース(indexOf または buildHeaderIndex_)。列番号ハードコード禁止
- 有効フラグ=FALSE の行はスキップ
- 42_trn_journal および 11_mst_account への書き込み禁止(読み取り専用)
- 95_dashboard_rule_of_40 は DDL 管理外(setupAllSchemas に追加しない)
- AccountRepository.findAsMap_() 等の存在しない関数名を使用しない
- メニュー名は onOpen() を Read して実在する文字列のみ使用する(造語禁止)

## エッジケース
| 条件 | 処理 |
|------|------|
| 前年 TTM 売上 = 0 | 成長率 = "-" |
| 当年 TTM 売上 = 0 | 利益率 = "-" |
| TTM 計算に 24ヶ月未満のデータ | 成長率 = "-" |
| 営業利益 < 0 | マイナス値をそのまま表示(正常) |
| フィルタ一致レコード = 0件 | "-" + Utils.logInfo で警告出力 |

## 動作確認
1. npm run push:dev でデプロイ
2. メニューから Rule of 40 実行関数を起動
3. 95_dashboard_rule_of_40 が生成されヘッダー・データ行が正しく出力されることを確認
4. 売上ゼロ月・24ヶ月未満データ月で "-" が正しく表示されることを確認
5. 動作確認後に git commit → git push → PR → main マージの順で進める

### 拡張思考の使用状況
| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| Phase 1(調査・設計) | あり | ファイル構造把握・固有名詞裏取り |
| Phase 2(実装) | なし | Phase 1 確定内容の書き下しに徹する |

推奨実行モデルテーブル:

工程推奨モデル理由
仕様書作成(本プロンプト)Claude Sonnet 4.6複数ファイル横断調査・設計判断が必要
F-33 実装Claude Opus 4.6新規ファイル設計・複数ファイル横断・会計ロジック理解が必要

変更履歴テーブル:

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

Step 2-4: 仕様書作成プロンプトの記録(File Edit または Bash、最重量・必ず独立 Step)

## 仕様書作成プロンプト セクションに以下の形式で追記する:

<details><summary>展開して表示</summary>

(この <instruction> タグ内の全文をここに転記する)

</details>

Phase 3: 保存・登録・コミット

3-A: ファイル保存確認

docs/dev/dev_F-33_rule_of_40_scoreboard.md が全 Step 分のコンテンツを含んで保存されていることを確認する。

3-B: _config.json への登録(必須・省略禁止)

docs/_config.jsonnav 配列の §E.5(FP&A・レポーティング)セクションに以下を追加する:

{ "file": "dev/dev_F-33_rule_of_40_scoreboard.md", "title": "E.5.X F-33 Rule of 40 スコアボード" }

追加後に JSON 構文チェックを実行する(例: node -e "JSON.parse(require('fs').readFileSync('docs/_config.json','utf8'))" && echo OK)。

3-C: changelog.md への追記

docs/_internal/changelog.md のヘッダー直後に以下を追記する:

| 2026-04-20 | [dev_F-33_rule_of_40_scoreboard.md](dev_mas-033_rule_of_40_scoreboard.md) | 初版作成。TTMベースのRule of 40スコアボード仕様書 |

3-D: コミット&プッシュ

git add docs/dev/dev_F-33_rule_of_40_scoreboard.md docs/_config.json docs/_internal/changelog.md
git commit -m "docs: F-33 Rule of 40 スコアボードの開発仕様書を作成

TTMベースの成長率・営業利益率・Rule of 40スコア計算方針、
エッジケース(ゼロ除算・データ不足)、Human-in-the-Loop設計、
プロダクト事業識別等の人間検討事項3点を記載。

https://claude.ai/code/session_XXXXX"
git push -u origin {現在のブランチ}

📌 取り込み時の注記 (2026-06-02 sub 復元)

本仕様書は旧 F-番号体系で作成され PR 未マージのまま孤立していたドラフトを、origin/docs/dev-* ブランチから内容無改変で復元し、案件ID のみ MAS 体系へ正規化したもの。status: Open(未実装)

⚠️ ファイル番号ドリフト: 本文「対象ファイル」が指す 600_report/610〜612_*.js は現行 main で 別機能に使用済み(610=投資分析/MAS-013・611=財務モデリング/MAS-010・612=採用sim/MAS-012)。 実装時にファイル番号の再割当が必要。