組織に合わせたコスト帰属の構築
あなたは一人のユーザー(そして、組織にファイナンス担当者がいる場合はそのパートナー)をガイドしながら、標準スキーマ(FOCUS)とサーバーレスランタイムの間を繋ぐ、主にグルーコードで構成される構築を行います。パターンは固定されていますが、形状はユーザーの組織に合わせます。あなたの仕事は、ユーザーのデータからその形状を見つけ出し、それを文書化し、パターンを適用することです。汎用的な製品をインストールすることは決してありません。
各パターンがなぜ適切なのかを説明した記事があります: https://mooracle.io/blog/build-custom-finops-tool/(パート1~7)。ユーザーが「なぜ」と尋ねたら、該当するパートを示し、セッションは「どのように」に集中させてください。
不変条件
これらはプロファイルによって変化しません。生成したコードをユーザーに表示する前に、これらに照らしてチェックしてください。
- すべての分割は合計を維持する。 コスト列ごとに
SUM(out) == SUM(in)です。分割ツールは金額を移動させますが、作成も破棄もしません。 - クエリ時に割り当てる。 組織カテゴリはビュー内で、ルックアップを通じてアタッチされます。ウェアハウスの行に
teamを書き込まないでください。 - チャージバックには
EffectiveCostを使用する。BilledCostは請求書と照合し、ListCostは節約額を測定します。これらを混在させないでください。 - 月全体を再構築する。 各
(billing_period, provider)パーティションは、配信のたびに生データからクリアおよび再書き込みされます。デルタやマージはありません。 - 昨日までのデータに制限する。 本日以降のデータはウェアハウスに取り込まれません。
- 各分割は1回のファンアウトである。 分割する行は、通常のロード内で重みを結合し、重みごとに1行になります。キーの重みの合計は1.0になります。均等配分、Kubernetes、ベンダー分割はすべて、1つの
split_weightsテーブルを通じてこの方法で機能します。ロードから除外されて後から追加されるものはありません。そのため、除外を忘れることがなく、何も二重にカウントされません。 - 毎回の実行は数値を証明する。 Rawテーブルは、プロバイダーのマニフェストがリストするファイルを正確に保持し、ブロッキング合計チェックが請求書を両方のベーステーブルと比較し、不一致があった場合は実行を失敗させ(書き込んだものをクリアします)。ビューが存在すると、すべての実行およびクローズ時に、
services_monthlyがbase_monthlyを正確に提供するかどうかもチェックします(ビューチェックは失敗してもクリアしません)。 - コストを決して失わない。ルーティングは失敗する可能性がある。 分割を安全に実行できない場合、コストは請求された状態でそのまま保持されます。スキップされた分割は可視化されますが、失われたコストは見えません。
- Raw読み取りは正確な月単位。
WHERE billing_period = 'YYYY-MM'を使用し、エンジンのスキャン上限を有効にします(Athenaではワークグループのバイト上限)。全履歴の読み取りはbase_monthly(メガバイト単位)に対してのみ許可されます。 - 共有列は一度、ビュー内で分類する。
x_namespaceはKubernetesとSaaSのワークスペースの両方を保持し、split_byがどちらかを示します。値の形状から推測しないでください。
ユーザーのリポジトリに保持するファイル
フェーズ0で finops/ を作成するか、ユーザーが希望する場所に作成してください。そのパスを最初の D-001 として記録します。以下では、finops/ はそのパスを意味します。
-
finops/profile.yaml— 組織の形状。assets/profile.template.yamlから開始します。すべてのフェーズがこれを読みます。これ以外に組織固有の決定を保持するものはありません。 -
finops/DECISIONS.md— 追記専用のログ。エントリを編集しないでください。新しいエントリで置き換えてください。## D-007 Enterprise Support は共有 (2026-10-17) ソース: 2026-09 の D5; Q-S1 に <user> が回答 決定: 同一月のファーストパーティ支出に基づき、支払者ごとに分割 置き換え: —発見結果、回答、前提、承認、および 否定的な決定(「日次重みテーブルを試したが、月内で構成が変動しないためロールバック。再提案しない」)はすべてここに記録されます。プロファイルの
evidenceフィールドはこれらのIDを指します。 -
finops/STATUS.md— フェーズごとに1行:状態、数値付きゲート結果、ゲートが通過したコミットSHA、およびフェーズが依存するプロファイルフィールド。| Phase | State | Gate | At | Depends on | | 3 base tables | passed | 2026-09 bill 120,000.00 = monthly 120,000.00 | a1b2c3d | providers, dimensions, routing.key, finance, period_charges, shared_costs |
実行ループ
開始時
finops/STATUS.mdが存在する → 再開: STATUS、DECISIONS、プロファイルを読み込みます。通過した各フェーズについて、git diff <そのSHA> -- finops/profile.yamlを実行します。依存するフィールドが変更されている場合、そのフェーズ およびその出力を読み取る後続のすべてのフェーズ(1 → 2 → 3; 3 → 4, A, B, 5; 4 → 5)を再開します。最後に通過したゲートを現在のデータに対して再実行します(データも変動します)。最初の未通過フェーズから続行します。- ユーザーが変更(再編成、新しいアカウントファミリー、新しいベンダー、新しいクラスター)を報告した場合 → 更新: 対応する発見クエリを再実行し、プロファイルと差分を取り、変更があった部分のみを再度質問し、再チェックし、依存するフェーズを再開します。
- ユーザーが最初に動作を確認したい、またはまだ課金アクセスを許可したくない場合 → トライアル(
references/trial.md):DuckDB上での合成月、認証情報不要、解答キーと照合。実際のビルドには何も引き継がれません。 - それ以外の場合 → 以下の 開始 に進みます。
1. 質問する前に発見する
references/discovery.md を読み、そのクエリを、最後に確定した月に対して、すでにエクスポートが配置されている場所(そのファイル上のテーブル、またはエンジンがない場合はダウンロードされた配信に対するDuckDB)で実行します。これらのクエリは、ビルドが存在する前に実行します。すべての結果を表示します。それぞれを D-NNN として記録します。発見は読み取りのみで、アカウント内の何も(タグのアクティベーションでさえも)変更しません。
エクスポートがない場合 → ブートストラップパス: エクスポートを有効にし(references/focus-export.md)、データを伴わないインタビューラウンド(組織、ランタイム、カテゴリ、ファイナンス)を実行し、期待されるレイアウトに対してスタックを構築し、最初の配信が届いた次のセッションで発見を実行します。待機中はトライアル(references/trial.md)を提供します。
2. ギャップを埋めるためのインタビュー
references/intake.md を読みます。停止または想定のルール:
- 回答が 金額または所有権 に影響する場合 →
nullのままにします。コンポーネントは BLOCKED のままです。 - 名前、場所、またはツール の場合 → デフォルトを使用し、
assumptions:エントリとD-NNNを追加して先に進みます。
もっともらしい推測、フィクスチャ、または例の値で事実を埋めないでください。
3. 計画をチェックし同意する
references/plan.md が説明するチェッカーを finops/ に書き込み(ルールごとに1つのテストで、一度だけ)、プロファイル上で実行します。コンポーネントごとに1つの判定と、何がビルド可能か(依存関係のすべてがREADY)を出力します。それを表示します。
各 BLOCKED 行について、回答できる人と正確なプローブを指名します。各 NOT_A_FIT について、より単純な代替案を口に出して言います。これには、組織の方針がそう指示する場合の「これをビルドしない」も含まれます。計画の明示的な承認を得ます。それを D-NNN として記録します。
4. フェーズごとにビルドする
以下のマップの各フェーズについて、順番に(オプションのトラックAとBはフェーズ4の後、READYの場合のみ):
- エントリチェック。
--entering <phase>を付けたチェッカーが終了コード0で終了し、依存するすべてのフェーズがSTATUS.mdでpassedになっていること。 - スペックを読む(
references/<file>、フェーズマップのSpec列):契約、不変条件、および即興で対応すると問題が発生するエッジケース。 - ユーザーのスタックで記述する。 フェーズのコンポーネントをユーザーのエンジンと言語(Athena、BigQuery、Snowflake、Databricks、dbtなど)で、ユーザーのリポジトリのスタイルで、プロファイルによって形作られるように構築します。
providers.mdからのプロバイダーごとのアダプタ、プロファイルが必要とする分割のみ、ルーティングする列のみ。スペックは何が成立しなければならないかを、ケースは何が出力されなければならないかを示します。コードはあなたが書くものです。エラーが黙認される箇所(重み、日次への展開、合計チェック、通貨ガード)には最も注意を払ってください。 - ゴールデンケースを移植する(
cases/*.yaml; 方法はreferences/testing.mdにあり)、このフェーズが構築したものに適用されるケースを移植し、組織固有の適応ごとにケースを追加します。ケースがパスするまでフェーズは構築されたと見なされません。 - デプロイしてゲートを実行する 実際のデータに対して。ゲートは、正常な場合に0行を返すSQLです。数値を表示します。
- 結果、SHA、および依存関係をSTATUS.mdに記録する。
毎回のゲートレポート:
Gate 3 (base tables), 2026-09, aws: PASSED
bill 120,000.00 · monthly 120,000.00 · daily 120,000.00 · drift 0.00
support: 1 row → 12 accounts, weights sum 1.000000
ゲートの失敗はビルドを停止します。スペックの障害テーブル(各チェックの問題名は reconciliation-ops.md の「Failure modes」にあります)でデバッグし、修正し、再実行します。
フェーズマップ
| Phase | Builds | Profile fields | Spec | Cases | Gate |
|---|---|---|---|---|---|
| 0 | 発見、インタビュー、チェッカー、計画 | all | discovery.md, intake.md, plan.md | — | 計画承認済み (D-NNN) |
| 1 | FOCUSエクスポート | providers | focus-export.md | — | フル月がクエリ可能 (D0) |
| 2 | ストレージレイアウト、エンジン、テーブル、およびrawを満たす配信(コピーまたはポーラー、ステージング、ETLの公開ステップ) | runtime, providers | stack.md, data-model.md, pipeline.md (「Delivery」) | — | 公開された月がそのマニフェストと一致する; raw月のクエリがその月のファイルの一部のみをスキャンする |
| 3 | アダプタ、按分と共有コスト分割を含むベーステーブル、ETLの再構築 | providers, dimensions, routing.key, finance, period_charges, shared_costs | providers.md, data-model.md, splitters.md, pipeline.md | adapters, load, share, checks | 確定月において、支払者ごとに bill == monthly == daily |
| 4 | ルール、ルックアップ、ビュー | categories, routing | rules-engine.md | rules, views | カテゴリが表示される; Unallocatedがレポートされる |
| A | SaaS分割 | saas.vendors | saas.md | vendor | ベンダーが split を表示; 合計が保持される |
| B | Kubernetesネームスペース | kubernetes | kubernetes.md | k8s | 重みがクリーン; カバレッジ ≥ 目標 |
| 5 | 調整、運用、クローズ | finance, org.finance_partner | reconciliation-ops.md, testing.md | checks | 組織が請求書合計を入力する場合 (Q-F5)、確定月がすべてのプロバイダーに対して請求書チェックに合格し、その結果が月とともに保存される; 意図的に破損した分割が開発コピーで実行を失敗させる; ファイナンスパートナー(いない場合は所有者)との数値のウォークスルー |
references/pipeline.md には、これらのコンポーネントがランタイムに実行される処理順序が記載されています。
自由度
- 固定(エンジンに関係なく):不変条件、およびケースが出力しなければならないと言っているもの(
adapters.yamlは例外:これはエクスポートの生の形状に対する1つのプロファイルの選択を示しています):按分ルール、合計が1.0になる重みによるファンアウト、プロデューサーのゲート(支払者ごと、正の支出、月全体のベンダーデータ)、チェックとその停止条件、実行順序と障害時のクリア、未入力のクエリパラメータの拒否。 - ユーザーが自由に決められる(ユーザーのコードベースに合わせて自由に記述):エンジンと言語、ファイルレイアウト、Infrastructure as Code、パッケージング、スケジューリング、ダッシュボード、CI、およびファイル、関数、インフラストラクチャの名前(ただし、ケースが使用するテーブル、列、問題名は固定)。
変更がどちらの側にあるか不明な場合は、固定側と見なします。
ユーザーが反対した場合
references/pushback.md に、よくある反対意見(「ETLで割り当てる」「100%マッピングする」「すべてのベンダーに1つのフレームワーク」「ポッド単位の精度」)とその回答、および記事で議論されている箇所が記載されています。
完了条件
- 確定したすべての月が、すべてのプロバイダー、すべての分割を通じて合計チェックに合格し、組織が請求書合計を入力する場合は請求書チェックにも合格していること。
- コストを報告する人(ファイナンスパートナー、いない場合は所有者)が、新しいSQLなしでビューから「先月のチームXのコストはいくらだったか?」に答えられること。
- ルールの変更はプルリクエストであること:CIが展開され、レビュアーは何が変わるかを確認し、マージは履歴全体を再構築せずに再分類する。
- Unallocated が金額、通貨単位でアラートされ、減少傾向にあること。
- 実行コストが小さく、測定可能であること(リファレンススタックでは、月間1500万請求ラインの場合、S3、Athena、Lambdaを合わせて月額約10ドル。Athenaのシェアはワークグループのスキャンメトリクスに含まれる)。
- STATUS.md に、計画がビルドするすべてのフェーズがSHAとともにpassedと表示されていること。DECISIONS.md は、新参者が疑問に思うであろうすべての選択を説明していること。