From 4d3f443fe9dd13d46a56cb6001533587adf51043 Mon Sep 17 00:00:00 2001 From: Travis Salas Cox Date: Sun, 12 Apr 2026 11:11:13 -0500 Subject: [PATCH 1/4] Fix up some more folder structure to make the finopspp tool eaiser to maintain --- components/actions/016.md | 9 +- components/actions/019.md | 9 +- components/actions/021.md | 9 +- components/actions/022.md | 9 +- components/actions/030.md | 2 +- components/actions/031.md | 2 +- components/actions/032.md | 2 +- components/actions/033.md | 2 +- components/actions/036.md | 2 +- components/actions/045.md | 9 +- components/actions/046.md | 9 +- components/actions/047.md | 9 +- components/actions/048.md | 9 +- components/actions/056.md | 2 +- components/actions/057.md | 2 +- components/actions/058.md | 4 +- components/actions/059.md | 6 +- components/actions/067.md | 9 +- components/actions/070.md | 9 +- components/actions/071.md | 9 +- components/actions/072.md | 9 +- components/actions/074.md | 9 +- components/actions/075.md | 9 +- components/actions/077.md | 9 +- components/actions/078.md | 9 +- components/actions/085.md | 13 +- components/actions/086.md | 13 +- components/actions/087.md | 13 +- components/actions/088.md | 13 +- components/actions/089.md | 13 +- components/actions/090.md | 13 +- components/actions/092.md | 9 +- components/actions/093.md | 9 +- components/actions/094.md | 9 +- components/actions/095.md | 9 +- components/actions/096.md | 9 +- components/actions/097.md | 9 +- components/actions/098.md | 9 +- components/actions/099.md | 9 +- components/actions/101.md | 9 +- components/actions/107.md | 9 +- components/actions/108.md | 9 +- components/actions/110.md | 9 +- components/actions/111.md | 9 +- components/actions/113.md | 2 +- components/actions/114.md | 2 +- components/actions/115.md | 2 +- components/actions/116.md | 2 +- components/actions/118.md | 9 +- components/actions/119.md | 9 +- components/actions/121.md | 9 +- components/actions/122.md | 9 +- components/actions/123.md | 9 +- components/actions/125.md | 42 ++++- components/actions/126.md | 34 +++- components/actions/129.md | 2 +- components/actions/130.md | 2 +- components/actions/131.md | 2 +- components/actions/132.md | 2 +- components/actions/133.md | 2 +- components/actions/134.md | 2 +- components/actions/136.md | 2 +- components/capabilities/007.md | 12 +- components/capabilities/023.md | 2 +- tools/__main__.py | 16 +- tools/commands/generate/__init__.py | 0 tools/commands/generate/group.py | 133 +++++++++++++++ .../{generate.py => generate/helpers.py} | 147 +++-------------- tools/commands/specifications/__init__.py | 0 .../{ => specifications}/approvals.tcss | 0 .../group.py} | 154 +----------------- tools/commands/specifications/helpers.py | 148 +++++++++++++++++ tools/commands/utils.py | 2 +- tools/commands/version/__init__.py | 0 .../commands/{version.py => version/group.py} | 0 tools/tests/test_composers.py | 0 76 files changed, 621 insertions(+), 499 deletions(-) create mode 100644 tools/commands/generate/__init__.py create mode 100644 tools/commands/generate/group.py rename tools/commands/{generate.py => generate/helpers.py} (55%) create mode 100644 tools/commands/specifications/__init__.py rename tools/commands/{ => specifications}/approvals.tcss (100%) rename tools/commands/{specifications.py => specifications/group.py} (77%) create mode 100644 tools/commands/specifications/helpers.py create mode 100644 tools/commands/version/__init__.py rename tools/commands/{version.py => version/group.py} (100%) create mode 100644 tools/tests/test_composers.py diff --git a/components/actions/016.md b/components/actions/016.md index fb39490a..39ea54d0 100644 --- a/components/actions/016.md +++ b/components/actions/016.md @@ -1,6 +1,6 @@ # 16: Gather Requirements & Define Kpis -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 16-gather-requiremen @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/019.md b/components/actions/019.md index 71a02c7e..c69004e8 100644 --- a/components/actions/019.md +++ b/components/actions/019.md @@ -1,6 +1,6 @@ # 19: Publish Documentation & Support -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 19-document-support @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/021.md b/components/actions/021.md index 701e9783..93fe2ee8 100644 --- a/components/actions/021.md +++ b/components/actions/021.md @@ -1,6 +1,6 @@ # 21: Track Adoption & Manage Changes -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 21-track-adoption @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/022.md b/components/actions/022.md index 7f11be5d..46063fde 100644 --- a/components/actions/022.md +++ b/components/actions/022.md @@ -1,6 +1,6 @@ # 22: Iterate Based On Feedback -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 22-iterate-feedback @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/030.md b/components/actions/030.md index 5852b7c2..76d24674 100644 --- a/components/actions/030.md +++ b/components/actions/030.md @@ -1,6 +1,6 @@ # 30: Define Estimating Scope & Policy -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 30-define-estimating diff --git a/components/actions/031.md b/components/actions/031.md index 963fb6e0..d582bdb9 100644 --- a/components/actions/031.md +++ b/components/actions/031.md @@ -1,6 +1,6 @@ # 31: Agree On Pricing Assumptions -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 31-agree-pricing diff --git a/components/actions/032.md b/components/actions/032.md index 56ab0613..0fc67048 100644 --- a/components/actions/032.md +++ b/components/actions/032.md @@ -1,6 +1,6 @@ # 32: Select Estimation Methods -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 32-select-estimation diff --git a/components/actions/033.md b/components/actions/033.md index 5ed3f549..12fd286c 100644 --- a/components/actions/033.md +++ b/components/actions/033.md @@ -1,6 +1,6 @@ # 33: Include Sustainability Impacts -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 33-sustainability-impact diff --git a/components/actions/036.md b/components/actions/036.md index 1805de2f..4358ed0c 100644 --- a/components/actions/036.md +++ b/components/actions/036.md @@ -1,6 +1,6 @@ # 36: Coach Engineering On Estimates -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 36-coach-engineering diff --git a/components/actions/045.md b/components/actions/045.md index 5084ece3..a22f71c6 100644 --- a/components/actions/045.md +++ b/components/actions/045.md @@ -1,6 +1,6 @@ # 45: Increase Shared Cost Coverage -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 45-allocation-coverage @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/046.md b/components/actions/046.md index 0f826908..8ea089bc 100644 --- a/components/actions/046.md +++ b/components/actions/046.md @@ -1,6 +1,6 @@ # 46: Publish Budget Vs Actual Reports -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 46-publish-budget @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/047.md b/components/actions/047.md index 0db9d9fd..9e544bc5 100644 --- a/components/actions/047.md +++ b/components/actions/047.md @@ -1,6 +1,6 @@ # 47: Automate Budget Threshold Alerts -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 47-budget-alerts @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/048.md b/components/actions/048.md index 363a79ef..c70fc345 100644 --- a/components/actions/048.md +++ b/components/actions/048.md @@ -1,6 +1,6 @@ # 48: Review & Adapt Budget Strategy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 48-review-adapt @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/056.md b/components/actions/056.md index 3bf03a7b..a470ac45 100644 --- a/components/actions/056.md +++ b/components/actions/056.md @@ -1,6 +1,6 @@ # 56: Document Inputs & Formulas -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 56-document-inputs diff --git a/components/actions/057.md b/components/actions/057.md index 9dbe3c53..c0a4028a 100644 --- a/components/actions/057.md +++ b/components/actions/057.md @@ -1,6 +1,6 @@ # 57: Build Unit Cost Dashboards -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 57-build-unitcost diff --git a/components/actions/058.md b/components/actions/058.md index 0e7a2c8c..32b8ee75 100644 --- a/components/actions/058.md +++ b/components/actions/058.md @@ -1,6 +1,6 @@ # 58: Set Review Cadence & Targets -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 58-set-cadence @@ -27,7 +27,7 @@ Reviews are important to capture changes in the business, but also mistakes in t * Review data with stakeholders when metrics exceed acceptable thresholds in either direction * Incorporate metric reviews into the decision making processes of stakeholder departments * The FinOps team should periodically publish an analysis of major changes in the metrics dashboards -as well as facilitate reviews of metrics, adoption data, modifications, and KPI refreshes + as well as facilitate reviews of metrics, adoption data, modifications, and KPI refreshes 10*Ceil(x/4) diff --git a/components/actions/059.md b/components/actions/059.md index c3190eeb..1b1d3f6f 100644 --- a/components/actions/059.md +++ b/components/actions/059.md @@ -1,6 +1,6 @@ # 59: Use Metrics To Prioritize Investments -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 59-use-metrics @@ -25,8 +25,8 @@ None * Scenario plan for which metric values will drive the next decisions, behaviors, or outcomes needed * Investigate root causes for changes in metric values to determine next steps * Add to the backlog which actions are required to get unit metrics to a desired level -* Unit economics influence the decisions in build vs buy, architecture design, - workload placement, migration, sourcing, pricing, etc. +* Unit economics influence the decisions in build vs buy, architecture design, workload placement, + migration, sourcing, pricing, etc. 10*Ceil(x/4) diff --git a/components/actions/067.md b/components/actions/067.md index a005be0e..1725b184 100644 --- a/components/actions/067.md +++ b/components/actions/067.md @@ -1,6 +1,6 @@ # 67: Define Optimization Strategy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 67-define-strategy @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/070.md b/components/actions/070.md index d6bd4f9d..4334ed3c 100644 --- a/components/actions/070.md +++ b/components/actions/070.md @@ -1,6 +1,6 @@ # 70: Partner On Scheduling Actions -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 70-scheduling-actions @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/071.md b/components/actions/071.md index 2ddc9710..b7465bd4 100644 --- a/components/actions/071.md +++ b/components/actions/071.md @@ -1,6 +1,6 @@ # 71: Gamify Adoption & Celebrate Wins -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 71-gamify-adoption @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/072.md b/components/actions/072.md index 105c5f4e..bfb923f5 100644 --- a/components/actions/072.md +++ b/components/actions/072.md @@ -1,6 +1,6 @@ # 72: Track Kpis & Document Playbooks -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 72-track-kpis @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/074.md b/components/actions/074.md index 6f6f70a8..5b3c0681 100644 --- a/components/actions/074.md +++ b/components/actions/074.md @@ -1,6 +1,6 @@ # 74: Document Licensing Models -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 74-document-license @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/075.md b/components/actions/075.md index 7f30344f..c852e5a2 100644 --- a/components/actions/075.md +++ b/components/actions/075.md @@ -1,6 +1,6 @@ # 75: Integrate Billing With Procurement -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 75-integrate-billing @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/077.md b/components/actions/077.md index 5de21292..a1c8aedb 100644 --- a/components/actions/077.md +++ b/components/actions/077.md @@ -1,6 +1,6 @@ # 77: Influence Workload Design Choices -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 77-influence-workloa @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/078.md b/components/actions/078.md index 04863d71..db1e3d93 100644 --- a/components/actions/078.md +++ b/components/actions/078.md @@ -1,6 +1,6 @@ # 78: Publish License Spend & Utilization -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 78-publish-license @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/085.md b/components/actions/085.md index b351fc7f..8f998ae6 100644 --- a/components/actions/085.md +++ b/components/actions/085.md @@ -1,8 +1,8 @@ -# 85: None +# 85: Aggregate Emissions Data -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 85-agg-emissions-data **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/086.md b/components/actions/086.md index f0f54195..0af709ca 100644 --- a/components/actions/086.md +++ b/components/actions/086.md @@ -1,8 +1,8 @@ -# 86: None +# 86: Define Sustainability Metrics -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 86-sustainability-metrics **Implementation Types:** @@ -18,7 +18,7 @@ Example kg CO2 per unit ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/087.md b/components/actions/087.md index 6f7fb112..b94bb434 100644 --- a/components/actions/087.md +++ b/components/actions/087.md @@ -1,8 +1,8 @@ -# 87: None +# 87: Carbon Impact Workload Decisions -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 87-carbon-impact **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/088.md b/components/actions/088.md index 3eecdc8b..2d9fee54 100644 --- a/components/actions/088.md +++ b/components/actions/088.md @@ -1,8 +1,8 @@ -# 88: None +# 88: Carbon Aware Data Guidance -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 88-carbon-aware-guidance **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/089.md b/components/actions/089.md index 930a83bb..cb41517d 100644 --- a/components/actions/089.md +++ b/components/actions/089.md @@ -1,8 +1,8 @@ -# 89: None +# 89: Sustainability Data Continuous Improvement -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 89-improve-eco-data **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/090.md b/components/actions/090.md index d1f505a1..d9c6bd2d 100644 --- a/components/actions/090.md +++ b/components/actions/090.md @@ -1,8 +1,8 @@ -# 90: None +# 90: Tie Sustainability To Cost Efficiency -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 90-sustainability-cost **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/092.md b/components/actions/092.md index 041df868..50653828 100644 --- a/components/actions/092.md +++ b/components/actions/092.md @@ -1,6 +1,6 @@ # 92: Create Implementation Roadmap -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 92-implement-roadmap @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/093.md b/components/actions/093.md index b7b4f42c..11c86793 100644 --- a/components/actions/093.md +++ b/components/actions/093.md @@ -1,6 +1,6 @@ # 93: Integrate Cost Data In Workflows -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 93-integrate-cost @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/094.md b/components/actions/094.md index 5b3e7001..b6e6406d 100644 --- a/components/actions/094.md +++ b/components/actions/094.md @@ -1,6 +1,6 @@ # 94: Publish Finops Kpis -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 94-publish-finops @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/095.md b/components/actions/095.md index 41718049..7f03dadb 100644 --- a/components/actions/095.md +++ b/components/actions/095.md @@ -1,6 +1,6 @@ # 95: Drive Stakeholder Engagement -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 95-drive-stakeholder @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/096.md b/components/actions/096.md index f3a67529..772fa7ea 100644 --- a/components/actions/096.md +++ b/components/actions/096.md @@ -1,6 +1,6 @@ # 96: Automate Tasks & Review Maturity -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 96-automate-tasks @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/097.md b/components/actions/097.md index 9a186fd7..815c01f1 100644 --- a/components/actions/097.md +++ b/components/actions/097.md @@ -1,6 +1,6 @@ # 97: Assess Skills & Training Needs -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 97-assess-skills @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/098.md b/components/actions/098.md index 58705f81..71244c20 100644 --- a/components/actions/098.md +++ b/components/actions/098.md @@ -1,6 +1,6 @@ # 98: Develop Role-Based Learning Content -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 98-develop-role @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/099.md b/components/actions/099.md index 4584c245..78acbef9 100644 --- a/components/actions/099.md +++ b/components/actions/099.md @@ -1,6 +1,6 @@ # 99: Offer Certifications & Informal Learning -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 99-offer-certification @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/101.md b/components/actions/101.md index be30975d..e350f783 100644 --- a/components/actions/101.md +++ b/components/actions/101.md @@ -1,6 +1,6 @@ # 101: Track Training Impact & Refresh Content -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 101-track-training @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/107.md b/components/actions/107.md index c5362e07..544d0b47 100644 --- a/components/actions/107.md +++ b/components/actions/107.md @@ -1,6 +1,6 @@ # 107: Map Invoices To Allocation Model -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 107-map-invoices @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/108.md b/components/actions/108.md index 8d4c14b0..18fb90f6 100644 --- a/components/actions/108.md +++ b/components/actions/108.md @@ -1,6 +1,6 @@ # 108: Reconcile Rates & Track Completion -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 108-reconcile-rates @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/110.md b/components/actions/110.md index 01dee545..d7bbc60b 100644 --- a/components/actions/110.md +++ b/components/actions/110.md @@ -1,6 +1,6 @@ # 110: Automate Chargeback Data Flows -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 110-automate-chargeback @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/111.md b/components/actions/111.md index 4ab196e8..36c5051d 100644 --- a/components/actions/111.md +++ b/components/actions/111.md @@ -1,6 +1,6 @@ # 111: Distribute Reports & Track Accuracy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 111-chargeback-report @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/113.md b/components/actions/113.md index d6980bc5..08816aa6 100644 --- a/components/actions/113.md +++ b/components/actions/113.md @@ -1,6 +1,6 @@ # 113: Define Assessment Scope & Goals -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 113-define-assess diff --git a/components/actions/114.md b/components/actions/114.md index 9709abea..cef46d2d 100644 --- a/components/actions/114.md +++ b/components/actions/114.md @@ -1,6 +1,6 @@ # 114: Collect Standardized Assessment Inputs -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 114-collect-standard diff --git a/components/actions/115.md b/components/actions/115.md index e7f6c239..f06882b9 100644 --- a/components/actions/115.md +++ b/components/actions/115.md @@ -1,6 +1,6 @@ # 115: Establish Baselines & Identify Trends -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 115-establish-baseli diff --git a/components/actions/116.md b/components/actions/116.md index a524268d..a2f483af 100644 --- a/components/actions/116.md +++ b/components/actions/116.md @@ -1,6 +1,6 @@ # 116: Prioritize Actions & Publish Roadmap -**Specification Version:** 0.7.1 +**Specification Version:** 1.0.0 **Slug:** 116-actions-roadmap diff --git a/components/actions/118.md b/components/actions/118.md index be3afe2a..30e1dc45 100644 --- a/components/actions/118.md +++ b/components/actions/118.md @@ -1,6 +1,6 @@ # 118: Create Workload Selection Criteria -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 118-create-workload @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/119.md b/components/actions/119.md index 530cbb94..13a058a2 100644 --- a/components/actions/119.md +++ b/components/actions/119.md @@ -1,6 +1,6 @@ # 119: Define Success Criteria & Scope -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 119-define-success @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/121.md b/components/actions/121.md index dc18000d..793b5724 100644 --- a/components/actions/121.md +++ b/components/actions/121.md @@ -1,6 +1,6 @@ # 121: Align Timelines With Budgets -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 121-align-timelines @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/122.md b/components/actions/122.md index 2ca9b818..c5414173 100644 --- a/components/actions/122.md +++ b/components/actions/122.md @@ -1,6 +1,6 @@ # 122: Start With Lower Environments -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 122-start-lower @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/123.md b/components/actions/123.md index 9dd6e661..e23baa35 100644 --- a/components/actions/123.md +++ b/components/actions/123.md @@ -1,6 +1,6 @@ # 123: Review Outcomes & Adjust Patterns -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 123-review-outcomes @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/125.md b/components/actions/125.md index a1a3d597..a84c2b62 100644 --- a/components/actions/125.md +++ b/components/actions/125.md @@ -1,6 +1,6 @@ # 125: Evaluate Build Vs Buy Options -**Specification Version:** 1.7.0 +**Specification Version:** 2.0.0 **Slug:** 125-evaluate-build @@ -14,7 +14,19 @@ Evaluate build vs buy; map requirements to vendor capabilities. ## Supplemental Guidance -None +CRAWL (0–8) No build-vs-buy process exists. TCO analysis is absent or undocumented, vendor comparisons are +informal, and decisions default to familiarity, relationships, or cost alone. +WALK (9–17) Basic TCO covers License costs and implementation effort. Vendors are scored against a structured +scorecard, and decisions are loosely documented. Finance validation and exec sign-off are inconsistent. + +RUN (18–24) TCO is detailed, covering licensing, implementation, integration, training, support, and build +opportunity cost. A multi-stakeholder evaluation process is used, decisions are formally approved, and a guiding +policy is published and partially enforced. + +FLY (25–29) TCO is Finance-validated and accounts for multi-year trajectories, exit costs, and strategic +optionality. A full RFP with documented rationale is used, decisions are exec-approved and periodically reviewed, +and an enforced org-wide policy governs all future build-vs-buy choices. + ## Scoring Guide @@ -22,18 +34,34 @@ None ### Formula -None +* Weighted sum + TCO (score ×3), + Vendor scoring (score ×3), + Decision document (score ×3), + Rules established (score ×1). + Max Raw Score (29) +* TCO_Analysis - Total Cost of Ownership analysis performed + (0=None, 1=High-level, 2=Detailed, 3=Validated with Finance) +* Vendor_Scored - Vendor options scored against criteria + (0=None, 1=Informal, 2=Structured scorecard, 3=Multi-stakeholder RFP) +* Decision_Doc - Build vs Buy decision formally documented and approved + (0=No, 1=Informal note, 2=Formal doc, 3=Exec-approved+reviewed) +* Rules_Est - Guiding rules / policy established for future decsions + (0=No rules, 1=Draft, 2=Published+Enforced) +* Output Range (0 - Max Raw) ### Scoring Details -**Score Type:** binary +**Score Type:** multi_bucket | Score | Condition | | ----- | --------- | -| 0 | No rules established | -| 10 | Published policy | +| 0 | Low Maturity, Score Range (0-8) | +| 4 | Developing, Score Range (9-17) | +| 7 | Established, Score Range (18-24) | +| 10 | Optimizing, Score Range (25-29) | ## References -* [None](None): None +* [Maturity Definition](https://www.finops.org/framework/maturity-model/): None diff --git a/components/actions/126.md b/components/actions/126.md index 5cc89e02..ef0170bb 100644 --- a/components/actions/126.md +++ b/components/actions/126.md @@ -1,6 +1,6 @@ # 126: Implement & Configure Tools -**Specification Version:** 0.7.0 +**Specification Version:** 1.0.0 **Slug:** 126-implement-config @@ -14,23 +14,45 @@ Implement/configure; tailor access/permissions and integrations. ## Supplemental Guidance -None +CRAWL Partial environment coverage, billing data only, no tag configuration. +WALK 50-79% coverage, usage and billing data, partial tagging, informal config documentation. +RUN 80-94% coverage, tags integrated, full manual tag rules, documented configuration. +FLY 100% coverage, all data sources automated, automated tag enforcement, version-controlled configuration. ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula -None +* Weighted sum + Env coverage (score x4), + Data Integration (score x4), + Tag Config (score x2), + Config Doc (score x2). + Max raw (52) + +* Env_Coverage - % of cloud environments covered by deployed tools + (0=0%, 1<50%, 2=50-79%, 3=80-94%, 4=95-99%, 5=100%) +* Data_Integration - Data sources integrated (billing, usage, tags, business context) + (0=None, 1=Billing Only, 2=+Usage, 3=+Tags, 4=+Business context, 5=All+Automated) +* Tag_Config - Tag/allocation rules configured and enforced tool + (0=No, 1=Partial manual, 2=Full manual, 3=Automated enforcement) +* Config_Doc - Tool configuration documented and version-controlled + (0=No rules, 1=Informal, 2=Documented, 3=Version-controlled+Reviewed) + +* Output Range (0-52) ### Scoring Details -**Score Type:** calculation +**Score Type:** multi_bucket | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Low Maturity, Score Range (0-15) | +| 4 | Developing, Score Range (16-31) | +| 7 | Established, Score Range (32-44) | +| 10 | Optimizing, Score Range (45-52) | ## References diff --git a/components/actions/129.md b/components/actions/129.md index 97a79415..0d4cec63 100644 --- a/components/actions/129.md +++ b/components/actions/129.md @@ -1,6 +1,6 @@ # 129: Establish Cross-Team Collab -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 129-establish-collab diff --git a/components/actions/130.md b/components/actions/130.md index b5c647d0..982c2022 100644 --- a/components/actions/130.md +++ b/components/actions/130.md @@ -1,6 +1,6 @@ # 130: Align Goals & Data Models -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 130-align-goals diff --git a/components/actions/131.md b/components/actions/131.md index bebf4e62..a903446c 100644 --- a/components/actions/131.md +++ b/components/actions/131.md @@ -1,6 +1,6 @@ # 131: Share Reporting & Definitions -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 131-share-reporting diff --git a/components/actions/132.md b/components/actions/132.md index 64a1e758..a17b3405 100644 --- a/components/actions/132.md +++ b/components/actions/132.md @@ -1,6 +1,6 @@ # 132: Provide Technology Metrics To Teams -**Specification Version:** 0.9.0 +**Specification Version:** 1.0.0 **Slug:** 132-provide-metrics diff --git a/components/actions/133.md b/components/actions/133.md index a8dc8bb5..a608e40d 100644 --- a/components/actions/133.md +++ b/components/actions/133.md @@ -1,6 +1,6 @@ # 133: Coordinate Policy Changes Broadly -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 133-coordinate-policy diff --git a/components/actions/134.md b/components/actions/134.md index c7b3563d..72881246 100644 --- a/components/actions/134.md +++ b/components/actions/134.md @@ -1,6 +1,6 @@ # 134: Drive Adoption & Resolve Conflicts -**Specification Version:** 0.8.1 +**Specification Version:** 1.0.0 **Slug:** 134-drive-adoption diff --git a/components/actions/136.md b/components/actions/136.md index 2a6db0ac..1ddff4ba 100644 --- a/components/actions/136.md +++ b/components/actions/136.md @@ -1,6 +1,6 @@ # 136: Establish Executive Sponsorship -**Specification Version:** 0.0.1 +**Specification Version:** 1.0.0 **Slug:** 136-exec-sponsor diff --git a/components/capabilities/007.md b/components/capabilities/007.md index 438bfc11..7c78ad60 100644 --- a/components/capabilities/007.md +++ b/components/capabilities/007.md @@ -8,9 +8,9 @@ None ## Actions -* [85](/components/actions/085.md): None -* [86](/components/actions/086.md): None -* [87](/components/actions/087.md): None -* [88](/components/actions/088.md): None -* [89](/components/actions/089.md): None -* [90](/components/actions/090.md): None +* [85](/components/actions/085.md): Aggregate Emissions Data +* [86](/components/actions/086.md): Define Sustainability Metrics +* [87](/components/actions/087.md): Carbon Impact Workload Decisions +* [88](/components/actions/088.md): Carbon Aware Data Guidance +* [89](/components/actions/089.md): Sustainability Data Continuous Improvement +* [90](/components/actions/090.md): Tie Sustainability to Cost Efficiency diff --git a/components/capabilities/023.md b/components/capabilities/023.md index 1bd493b9..9e1948e1 100644 --- a/components/capabilities/023.md +++ b/components/capabilities/023.md @@ -1,6 +1,6 @@ # 23: Executive Strategy Alignment -**Specification Version:** 0.0.1 +**Specification Version:** 1.0.0 ## Descriptive Statement diff --git a/tools/__main__.py b/tools/__main__.py index edbd9361..717d6304 100644 --- a/tools/__main__.py +++ b/tools/__main__.py @@ -1,20 +1,18 @@ """primary code for the finopspp CLI""" import click -from finopspp.commands import ( - generate, - specifications, - utils, - version -) +from finopspp.commands import utils +from finopspp.commands.version import group as version_group +from finopspp.commands.generate import group as generate_group +from finopspp.commands.specifications import group as specifications_group @click.group(cls=utils.ClickGroup) def cli(): """FinOps++ administration tool""" -cli.add_command(version.version) -cli.add_command(generate.generate) -cli.add_command(specifications.specifications) +cli.add_command(version_group.version) +cli.add_command(generate_group.generate) +cli.add_command(specifications_group.specifications) # include for those running script directly # as a python module. diff --git a/tools/commands/generate/__init__.py b/tools/commands/generate/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/generate/group.py b/tools/commands/generate/group.py new file mode 100644 index 00000000..d99e05b8 --- /dev/null +++ b/tools/commands/generate/group.py @@ -0,0 +1,133 @@ +"""Command file for all Generate group commands""" +import os +import sys +from importlib.resources import files + +import click +import yaml +from pydantic import TypeAdapter + +from finopspp.models import definitions +from finopspp.composers import archive, excel, markdown +from finopspp.commands import utils +from finopspp.commands.generate import helpers + +@click.group(cls=utils.ClickGroup) +def generate(): + """Generate files from YAML specifications""" + + +@generate.command() +@click.option( + '--profile', + default='FinOps++', + type=click.Choice(list(utils.profiles().keys())), + help='Which assessment profile to generate. Defaults to "FinOps++"', +) +def assessment(profile): # pylint: disable=too-many-branches,too-many-statements,too-many-locals + """Generate assessment files from their specifications""" + click.echo(f'Attempting to create assessment for profile={profile}:') + + domain_files = files('finopspp.specifications.domains') + cap_files = files('finopspp.specifications.capabilities') + action_files = files('finopspp.specifications.actions') + with open(utils.ProfilesMap[profile], 'r', encoding='utf-8') as yaml_file: + profile_yaml = yaml.safe_load( + yaml_file + ) + profile_spec = profile_yaml.get('Specification') or {} + profile_metadata = profile_yaml.get('Metadata') or {} + # edits to a specification in code should always be lowercase! + # to help show that it is a transformation from the uppercase + # version used in the actual yaml specification. + profile_spec['version'] = profile_metadata.get('Version') + + if not profile_spec.get('Domains'): + click.secho('Profile includes no domains. Exiting', err=True, fg='red') + sys.exit(1) + + # pull in formatted domains data-dict + domains = helpers.domains_collector( + profile, profile_spec, domain_files, cap_files, action_files + ) + + # check if assessment directory exists for this profile + # and if it does not create it + base_path = os.path.join( + os.getcwd(), + 'assessments', + profile + ) + if not os.path.exists(base_path): + os.mkdir(base_path) + + # create assessment framework overview markdown + markdown.assessment_generate(profile, profile_spec, base_path, domains) + + # next try and create the workbook for this profile. + excel.assessment_generate(profile, base_path, domains) + + # finally, create the assessment archive file for the current version + archive.assessment_generate(profile, profile_spec, base_path, domains) + + +@generate.command() +def documents(): + """Generate schema documents markdown files from code""" + schemas = {} + for definition in [definitions.Action, definitions.Capability, definitions.Domain, definitions.Profile]: + schemas[definition.__name__.lower()] = yaml.dump( + TypeAdapter(definition).json_schema(mode='serialization'), + default_flow_style=False, + sort_keys=False, + indent=2, + width=120 # will always be longer that what is allowed by yamllint + ) + + markdown.schemas_generate(schemas) + + + +@generate.command() +@click.option( + '--specification-type', + default='profiles', + type=click.Choice(list(utils.SpecSubspecMap.keys())), + help='Which specification type to generate. Defaults to "profiles"' +) +def components(specification_type): + """Generate component markdown files from their specifications""" + spec_files = files(f'finopspp.specifications.{specification_type}') + + # get subspec to help fill in names and other important pieces of + # information from the sub specification. + subspec_type = utils.SpecSubspecMap[specification_type] + subspec_files = None + if subspec_type: + subspec_files = files(f'finopspp.specifications.{subspec_type}') + + # iterate over the specification files and generate markdown files + for spec in spec_files.iterdir(): + number, _ = os.path.splitext(spec.name) + # skip over example 0 specs + if not int(number): + continue + + # all added fields to a specification should be in lowercase! + # to help differentiate them against the uppercase fields in + # the specifications itself. + path = spec_files.joinpath(spec.name) + with open(path, 'r', encoding='utf-8') as yaml_file: + full_yaml = yaml.safe_load(yaml_file) + spec = full_yaml.get('Specification') + metadata = full_yaml.get('Metadata') or {} + spec['version'] = metadata.get('Version') + + # update all the immediate subspecs listed on the spec in places + for subspec in spec.get(subspec_type.capitalize(), []): + _, subspec_doc = helpers.sub_specification_collector(subspec, subspec_files) + subspec_id = str(subspec_doc.get('ID')) + subspec['file'] = f'/components/{subspec_type}/{"0"*(3-len(subspec_id))}{subspec_id}.md' + subspec['title'] = subspec_doc.get('Title') + + markdown.components_generate(specification_type, spec) diff --git a/tools/commands/generate.py b/tools/commands/generate/helpers.py similarity index 55% rename from tools/commands/generate.py rename to tools/commands/generate/helpers.py index d987bd27..6410ba4c 100644 --- a/tools/commands/generate.py +++ b/tools/commands/generate/helpers.py @@ -1,23 +1,14 @@ -"""Command file for all Generate group commands""" -import os +"""Helpers file for generate command group""" import sys -from importlib.resources import files import click import yaml -from pydantic import TypeAdapter, ValidationError +from pydantic import ValidationError from rich.progress import track from finopspp.models import definitions -from finopspp.composers import archive, excel, markdown -from finopspp.commands import utils -@click.group(cls=utils.ClickGroup) -def generate(): - """Generate files from YAML specifications""" - - -def sub_specification_helper(spec, spec_file): +def sub_specification_collector(spec, spec_file): """Helps find and pull Specification subsection from a specification Note: metadata is only expected to be returned if it is defined. It might not @@ -44,7 +35,7 @@ def sub_specification_helper(spec, spec_file): sub_spec = full_sub.get('Specification') or {} return sub_metadata, sub_spec -def overrides_helper(spec, profile, override_type='std'): +def overrides_collector(spec, profile, override_type='std'): """Helper for receiving the overrides for a profile if they exist Also ensure that if an override exists, it conforms to the specification of an @@ -83,41 +74,22 @@ def overrides_helper(spec, profile, override_type='std'): return validated_override.model_dump() -@generate.command() -@click.option( - '--profile', - default='FinOps++', - type=click.Choice(list(utils.profiles().keys())), - help='Which assessment profile to generate. Defaults to "FinOps++"', -) -def assessment(profile): # pylint: disable=too-many-branches,too-many-statements,too-many-locals - """Generate assessment files from their specifications""" - click.echo(f'Attempting to create assessment for profile={profile}:') - - domain_files = files('finopspp.specifications.domains') - cap_files = files('finopspp.specifications.capabilities') - action_files = files('finopspp.specifications.actions') - with open(utils.ProfilesMap[profile], 'r', encoding='utf-8') as yaml_file: - profile_yaml = yaml.safe_load( - yaml_file - ) - profile_spec = profile_yaml.get('Specification') or {} - profile_metadata = profile_yaml.get('Metadata') or {} - # edits to a specification in code should always be lowercase! - # to help show that it is a transformation from the uppercase - # version used in the actual yaml specification. - profile_spec['version'] = profile_metadata.get('Version') - - if not profile_spec.get('Domains'): - click.secho('Profile includes no domains. Exiting', err=True, fg='red') - sys.exit(1) +def domains_collector(profile, profile_spec, domain_files, cap_files, action_files): + """Helper designed to collect and return a specific format for a domains dict + + This format is required to work properly with the composers to + generate the different parts of an assessment. + NOTE: there is a UI component to this in the form of the Rich Progress Tracker. + When testing, this will most likely show in your terminal, but can be safely + ignored. + """ domains = [] for domain in track(profile_spec.get('Domains'), 'Loading profile'): capabilities = [] - metadata, spec = sub_specification_helper(domain, domain_files) - domain_override = overrides_helper(spec, profile) + metadata, spec = sub_specification_collector(domain, domain_files) + domain_override = overrides_collector(spec, profile) domain_drops = [drop['ID'] for drop in domain_override.get('DropIDs')] if domain_override.get('TitleUpdate'): @@ -153,14 +125,14 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements for capability in spec.get('Capabilities'): actions = [] - metadata, spec = sub_specification_helper(capability, cap_files) + metadata, spec = sub_specification_collector(capability, cap_files) # continue early if the Capability ID is one to be dropped spec_id = spec.get('ID') if spec_id and spec_id in domain_drops: continue - cap_override = overrides_helper(spec, profile) + cap_override = overrides_collector(spec, profile) cap_drops = [drop['ID'] for drop in cap_override.get('DropIDs')] if cap_override.get('TitleUpdate'): @@ -193,14 +165,14 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements }) spec.get('Actions').extend(cap_override.get('AddIDs')) for action in spec.get('Actions'): - metadata, spec = sub_specification_helper(action, action_files) + metadata, spec = sub_specification_collector(action, action_files) # continue early if the Action ID is one to be dropped spec_id = spec.get('ID') if spec_id and spec_id in cap_drops: continue - act_override = overrides_helper(spec, profile, 'action') + act_override = overrides_collector(spec, profile, 'action') if act_override.get('TitleUpdate'): spec['Title'] = act_override.get('TitleUpdate') @@ -227,83 +199,4 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements 'weighted score': None }) - # check if assessment directory exists for this profile - # and if it does not create it - base_path = os.path.join( - os.getcwd(), - 'assessments', - profile - ) - if not os.path.exists(base_path): - os.mkdir(base_path) - - # create assessment framework overview markdown - markdown.assessment_generate(profile, profile_spec, base_path, domains) - - # next try and create the workbook for this profile. - excel.assessment_generate(profile, base_path, domains) - - # finally, create the assessment archive file for the current version - archive.assessment_generate(profile, profile_spec, base_path, domains) - - -@generate.command() -def documents(): - """Generate schema documents markdown files from code""" - schemas = {} - for definition in [definitions.Action, definitions.Capability, definitions.Domain, definitions.Profile]: - schemas[definition.__name__.lower()] = yaml.dump( - TypeAdapter(definition).json_schema(mode='serialization'), - default_flow_style=False, - sort_keys=False, - indent=2, - width=120 # will always be longer that what is allowed by yamllint - ) - - markdown.schemas_generate(schemas) - - - -@generate.command() -@click.option( - '--specification-type', - default='profiles', - type=click.Choice(list(utils.SpecSubspecMap.keys())), - help='Which specification type to generate. Defaults to "profiles"' -) -def components(specification_type): - """Generate component markdown files from their specifications""" - spec_files = files(f'finopspp.specifications.{specification_type}') - - # get subspec to help fill in names and other important pieces of - # information from the sub specification. - subspec_type = utils.SpecSubspecMap[specification_type] - subspec_files = None - if subspec_type: - subspec_files = files(f'finopspp.specifications.{subspec_type}') - - # iterate over the specification files and generate markdown files - for spec in spec_files.iterdir(): - number, _ = os.path.splitext(spec.name) - # skip over example 0 specs - if not int(number): - continue - - # all added fields to a specification should be in lowercase! - # to help differentiate them against the uppercase fields in - # the specifications itself. - path = spec_files.joinpath(spec.name) - with open(path, 'r', encoding='utf-8') as yaml_file: - full_yaml = yaml.safe_load(yaml_file) - spec = full_yaml.get('Specification') - metadata = full_yaml.get('Metadata') or {} - spec['version'] = metadata.get('Version') - - # update all the immediate subspecs listed on the spec in places - for subspec in spec.get(subspec_type.capitalize(), []): - _, subspec_doc = sub_specification_helper(subspec, subspec_files) - subspec_id = str(subspec_doc.get('ID')) - subspec['file'] = f'/components/{subspec_type}/{"0"*(3-len(subspec_id))}{subspec_id}.md' - subspec['title'] = subspec_doc.get('Title') - - markdown.components_generate(specification_type, spec) + return domains diff --git a/tools/commands/specifications/__init__.py b/tools/commands/specifications/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/approvals.tcss b/tools/commands/specifications/approvals.tcss similarity index 100% rename from tools/commands/approvals.tcss rename to tools/commands/specifications/approvals.tcss diff --git a/tools/commands/specifications.py b/tools/commands/specifications/group.py similarity index 77% rename from tools/commands/specifications.py rename to tools/commands/specifications/group.py index fcdec088..2bdfcbb6 100644 --- a/tools/commands/specifications.py +++ b/tools/commands/specifications/group.py @@ -13,15 +13,10 @@ from pydantic import TypeAdapter, ValidationError from rich.console import Console from rich.syntax import Syntax -from textual import on -from textual.app import App -from textual.containers import Horizontal, Vertical -from textual.events import Mount -from textual.widgets import Footer, Header, Label, Pretty, SelectionList, Switch -from textual.widgets.selection_list import Selection from finopspp.models import definitions, defaults from finopspp.commands import utils +from finopspp.commands.specifications import helpers # if MANPAGER is set to something odd, it will break the pager @@ -478,141 +473,6 @@ def update(selection, specification_type, major, force): sys.exit(1) -def approval_helper(approvers, number, today, specs): - """Helper to add approval to a specification by number""" - path = specs.joinpath(f'{number}.yaml') - with open(path, 'r', encoding='utf-8') as yaml_file: - specification_data = yaml.safe_load(yaml_file) - - # update basic metadata - metadata = specification_data['Metadata'] - metadata['Version'] = '1.0.0' - metadata['Adopted'] = today - metadata['Modified'] = today - metadata['Status'] = definitions.StatusEnum.accepted.value - - # now update metadata approvers list with selected approvers - approval_list = [] - for name, email in approvers: - approval_list.append( - { - 'Name': name, - 'Email': email, - 'Date': today - } - ) - metadata['Approvers'] = approval_list - - with open(path, 'w', encoding='utf-8') as yaml_file: - yaml.dump( - specification_data, - yaml_file, - default_flow_style=False, - sort_keys=False, - indent=2, - width=120 # will always be longer that what is allowed by yamllint - ) - -def approval_options_helper(specification_type): - """Helper to pull information on what should should be approved""" - spec_options = {} - specs = files(f'finopspp.specifications.{specification_type}') - for spec in specs.iterdir(): - number, _ = os.path.splitext(spec.name) - - # skip over example 0 specs - if not int(number): - continue - - path = specs.joinpath(spec.name) - - # load up previous data first - with open(path, 'r', encoding='utf-8') as yaml_file: - specification_data = yaml.safe_load(yaml_file) - - # check if status is Proposed, and if it is, add as option to approve - if specification_data['Metadata']['Status'] != definitions.StatusEnum.proposed.value: - continue - - spec = specification_data['Specification'] - - # only accept specifications with titles - title = spec['Title'] - if not title: - continue - - # new a few additional filters for actions - if specification_type == 'actions': - # skip if weight is still 0 or does not exist. - if not spec['Weight']: - continue - - # also skip if the last score is not 10 - last_score = spec['Scoring'].pop() - if last_score['Score'] != 10: - continue - - spec_options[title] = number - - return spec_options - -def approval_selector_helper(options, specification_type, approval_map): - """Helper for building the selector interface""" - selection_list = [] - for title, serial_number in options.items(): - selection_list.append(Selection(title, serial_number)) - - # now we create the terminal app that allows selections - class Approvals(App): - """Terminal application - - that will provide an selection inferface for specifications to approve""" - CSS_PATH = "approvals.tcss" - - def compose(self): - """Layout of app in terminal""" - yield Header() - with Vertical(): - yield Label( - 'Please select from this list and press [b]ctrl-q[/] to save & exit' - ) - with Horizontal(): - yield SelectionList(*selection_list) - yield Pretty(approval_map[specification_type]) - with Horizontal(id='horizontal-two'): - yield Label('(De)Select All:') - yield Switch() - yield Footer() - - def on_mount(self): - """What to do when the app is loaded""" - self.query_one( - SelectionList - ).border_title = 'Which specifications should be approved?' - self.query_one( - Pretty - ).border_title = 'Selected specifications (by ID)' - self.title = f'Approvals for "{specification_type.title()}"' - - @on(Mount) - @on(SelectionList.SelectedChanged) - def update_selected_view(self): - """How the view should update on selection changes""" - approval_map[specification_type] = self.query_one(SelectionList).selected - self.query_one( - Pretty - ).update( - approval_map[specification_type] - ) - - @on(Switch.Changed) - def update_switched_view(self): - """How the view should update on switch changes""" - self.query_one(SelectionList).toggle_all() - - - Approvals().run() - @specifications.command() def approvals(): """Command to help do mass approvals for each component type @@ -665,13 +525,13 @@ def approvals(): click.secho(f'Skipping {spec_type}...', fg='yellow') continue - spec_options = approval_options_helper(spec_type) + spec_options = helpers.approval_options(spec_type) if not spec_options: click.secho(f'No approvals needed for {spec_type}.') click.pause() else: - approval_selector_helper(spec_options, spec_type, approval_map) + helpers.approval_selector(spec_options, spec_type, approval_map) # get todays date today = str(datetime.date.today()) @@ -679,21 +539,21 @@ def approvals(): # do approvals for profiles first specs = files('finopspp.specifications.profiles') for number in approval_map['profiles']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # next do domains specs = files('finopspp.specifications.domains') for number in approval_map['domains']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # next do capabilities specs = files('finopspp.specifications.capabilities') for number in approval_map['capabilities']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # finally do actions specs = files('finopspp.specifications.actions') for number in approval_map['actions']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) click.secho('Approval completed', fg='green') diff --git a/tools/commands/specifications/helpers.py b/tools/commands/specifications/helpers.py new file mode 100644 index 00000000..14e8b701 --- /dev/null +++ b/tools/commands/specifications/helpers.py @@ -0,0 +1,148 @@ +"""Helpers file for specifications command group""" +import os +from importlib.resources import files + +import yaml +from textual import on +from textual.app import App +from textual.containers import Horizontal, Vertical +from textual.events import Mount +from textual.widgets import Footer, Header, Label, Pretty, SelectionList, Switch +from textual.widgets.selection_list import Selection + +from finopspp.models import definitions + +def approval(approvers, number, today, specs): + """Helper to add approval to a specification by number""" + path = specs.joinpath(f'{number}.yaml') + with open(path, 'r', encoding='utf-8') as yaml_file: + specification_data = yaml.safe_load(yaml_file) + + # update basic metadata + metadata = specification_data['Metadata'] + metadata['Version'] = '1.0.0' + metadata['Adopted'] = today + metadata['Modified'] = today + metadata['Status'] = definitions.StatusEnum.accepted.value + + # now update metadata approvers list with selected approvers + approval_list = [] + for name, email in approvers: + approval_list.append( + { + 'Name': name, + 'Email': email, + 'Date': today + } + ) + metadata['Approvers'] = approval_list + + with open(path, 'w', encoding='utf-8') as yaml_file: + yaml.dump( + specification_data, + yaml_file, + default_flow_style=False, + sort_keys=False, + indent=2, + width=120 # will always be longer that what is allowed by yamllint + ) + +def approval_options(specification_type): + """Helper to pull information on what should should be approved""" + spec_options = {} + specs = files(f'finopspp.specifications.{specification_type}') + for spec in specs.iterdir(): + number, _ = os.path.splitext(spec.name) + + # skip over example 0 specs + if not int(number): + continue + + path = specs.joinpath(spec.name) + + # load up previous data first + with open(path, 'r', encoding='utf-8') as yaml_file: + specification_data = yaml.safe_load(yaml_file) + + # check if status is Proposed, and if it is, add as option to approve + if specification_data['Metadata']['Status'] != definitions.StatusEnum.proposed.value: + continue + + spec = specification_data['Specification'] + + # only accept specifications with titles + title = spec['Title'] + if not title: + continue + + # new a few additional filters for actions + if specification_type == 'actions': + # skip if weight is still 0 or does not exist. + if not spec['Weight']: + continue + + # also skip if the last score is not 10 + last_score = spec['Scoring'].pop() + if last_score['Score'] != 10: + continue + + spec_options[title] = number + + return spec_options + +def approval_selector(options, specification_type, approval_map): + """Helper for building the selector interface""" + selection_list = [] + for title, serial_number in options.items(): + selection_list.append(Selection(title, serial_number)) + + # now we create the terminal app that allows selections + class Approvals(App): + """Terminal application + + that will provide an selection inferface for specifications to approve""" + CSS_PATH = "approvals.tcss" + + def compose(self): + """Layout of app in terminal""" + yield Header() + with Vertical(): + yield Label( + 'Please select from this list and press [b]ctrl-q[/] to save & exit' + ) + with Horizontal(): + yield SelectionList(*selection_list) + yield Pretty(approval_map[specification_type]) + with Horizontal(id='horizontal-two'): + yield Label('(De)Select All:') + yield Switch() + yield Footer() + + def on_mount(self): + """What to do when the app is loaded""" + self.query_one( + SelectionList + ).border_title = 'Which specifications should be approved?' + self.query_one( + Pretty + ).border_title = 'Selected specifications (by ID)' + self.title = f'Approvals for "{specification_type.title()}"' + + @on(Mount) + @on(SelectionList.SelectedChanged) + def update_selected_view(self): + """How the view should update on selection changes""" + approval_map[specification_type] = self.query_one(SelectionList).selected + self.query_one( + Pretty + ).update( + approval_map[specification_type] + ) + + @on(Switch.Changed) + def update_switched_view(self): + """How the view should update on switch changes""" + self.query_one(SelectionList).toggle_all() + + + Approvals().run() diff --git a/tools/commands/utils.py b/tools/commands/utils.py index ace268e7..fbf61911 100644 --- a/tools/commands/utils.py +++ b/tools/commands/utils.py @@ -1,4 +1,4 @@ -"""Command utility file. Include no actual commands""" +"""Common utilities used by the finopspp commands""" from importlib.resources import files import click diff --git a/tools/commands/version/__init__.py b/tools/commands/version/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/version.py b/tools/commands/version/group.py similarity index 100% rename from tools/commands/version.py rename to tools/commands/version/group.py diff --git a/tools/tests/test_composers.py b/tools/tests/test_composers.py new file mode 100644 index 00000000..e69de29b From 8373e630baf07dbd6a196a7cc9421fa5d811cde6 Mon Sep 17 00:00:00 2001 From: Travis Salas Cox Date: Sun, 12 Apr 2026 16:22:17 -0500 Subject: [PATCH 2/4] Update everything based on new test cases and new updates to the specifications --- .github/workflows/testing.yml | 29 ++++ .github/workflows/validation.yml | 2 +- assessments/FinOps Foundation/assessment.xlsx | Bin 35269 -> 36012 bytes assessments/FinOps Foundation/framework.md | 140 +++++++++--------- .../history/2026-04-12.json.gz | Bin 0 -> 10125 bytes assessments/FinOps++/assessment.xlsx | Bin 34561 -> 35395 bytes assessments/FinOps++/framework.md | 116 +++++++-------- .../FinOps++/history/2026-04-12.json.gz | Bin 0 -> 10243 bytes specifications/README.md | 15 +- specifications/capabilities/000.yaml | 8 +- specifications/domains/000.yaml | 8 +- specifications/profiles/000.yaml | 20 +-- tools/commands/generate/group.py | 4 + tools/commands/generate/helpers.py | 22 +-- tools/commands/utils.py | 1 + .../data/expected_example_dataframe.json | 44 ++++++ .../tests/data/expected_example_domains.json | 72 +++++++++ tools/tests/test_assessment_generation.py | 48 ++++++ tools/tests/test_composers.py | 0 19 files changed, 364 insertions(+), 165 deletions(-) create mode 100644 .github/workflows/testing.yml create mode 100644 assessments/FinOps Foundation/history/2026-04-12.json.gz create mode 100644 assessments/FinOps++/history/2026-04-12.json.gz create mode 100644 tools/tests/data/expected_example_dataframe.json create mode 100644 tools/tests/data/expected_example_domains.json create mode 100644 tools/tests/test_assessment_generation.py delete mode 100644 tools/tests/test_composers.py diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 00000000..dcf80501 --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,29 @@ +name: Testing + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.13"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install testing dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[testing]" + - name: Run unit tests for the finopspp tool + run: | + pytest tools + + + diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index f341c5ea..b142d7b2 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -22,7 +22,7 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - name: Install lint and testing dependencies + - name: Install lint and validation dependencies run: | npm install -g cspell-cli@9.6.0 python -m pip install --upgrade pip diff --git a/assessments/FinOps Foundation/assessment.xlsx b/assessments/FinOps Foundation/assessment.xlsx index 5a544c01e430196f6dbbaa7ec52eab9cb46ba498..cfffe537f2e8aeaf5b98cac17edb0a20732fad2c 100644 GIT binary patch delta 20987 zcmZ^~1yo&KvNei3fdqF#@Zj$5?iSo3xI=(*aCZsr?(XjH4hM(e79?oi;p_fy-~Qiw zj0I!t+BJ90nyXfAWiSD%A{DBJ0uiZ24(5oK76JlOJ%JIC1dwU#ye<|0Yiq06klL8B zh;hw!$vJ82Jc=Wp0`^rCf0}-~{+WI{IGx@OTD-qsXZ<~SV^iDe)7jb4=JR~M+Wq(Ond;G|cD>r3 zad~-teX`jCkj>Fv5Ac2y%<|dTd~NvAyn2SC2ix3kKfQVWa5f*TJ74} zaHFtqa_2ntdH4ldFK&NuProz!TbO=Y?A98oXuqV{Y%z>Kb$Pf@>+}2YS9=-5zj{a{Qw=$p!F(&^f-U-i= zafr?t;FnIu>s@0@F7`ufvSIhJSXg)72f>9M!DXWjP7ZhsUwL|?5(@7s3LlKQx!1J( zFj1mwXClvg_xUT(%VWFmqdd#_t_oMiads7_L0KM(dg71IFVzBY^@NHs6as>qdV2bH z(Z#bHw1U}=aM^SK@ua96UFCK4R?&WDTFCx>>X zUwU8L4S2M=c~hQMJHF5CAiX`da!zNxd6!r^ctYyZxmp1u@)^D!@21Ei>+-oi61Tvd z+4{p9KitIOj0NVTm}GL9zn^T6U~v7V_w$nfr!mNF!7GR|E8&5;z&w2K~* z9$;f&(3F_iso+JjtGUx>G*Mvb`^fQN*W=06B` zhgJ#Cvs1SP0)sVwn;d<#Jsyu1Ar>R$e48X7v^sH-q=Wf>@ZjXVH3l9lZEPOOzic#r7#o zVlFQ((UQD^C`@8#c7zXqW5zVs$I$khSq^`MV!HY{>w*CE`Kq?>XP*uu{=RbW1p$n6 z)>(n?YZl_qe4@|(jf?=@A9j}E3t3hTv?|YiYg^dr&rpqwLO9Xi=UU@K--D31$pdEo z3gN)klN&L~euA%GfD(@Olk~^b5LGr&{vy ~CEs1!Ikmjx-Js72d}L*>2G}UNMH0 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¸w^>Elnst=`8Ayl;}X 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 O0HYopWqlX^xA;G`&B-$W diff --git a/assessments/FinOps Foundation/framework.md b/assessments/FinOps Foundation/framework.md index fe08d8b4..319d1b0f 100644 --- a/assessments/FinOps Foundation/framework.md +++ b/assessments/FinOps Foundation/framework.md @@ -1,6 +1,6 @@ # 001: FinOps Foundation Maturity Assessment Framework -**Creation Date:** 2026-04-04 +**Creation Date:** 2026-04-12 **Specification Version:** 1.0.0 @@ -71,25 +71,25 @@ Classic profile featuring controls provided by the finops foundation 113: Define Assessment Scope & Goals -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

114: Collect Standardized Assessment Inputs -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

115: Establish Baselines & Identify Trends -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

116: Prioritize Actions & Publish Roadmap -

Specification Version: 0.7.1

+

Specification Version: 1.0.0

@@ -111,19 +111,19 @@ Classic profile featuring controls provided by the finops foundation 097: Assess Skills & Training Needs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

098: Develop Role-Based Learning Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

099: Offer Certifications & Informal Learning -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -135,7 +135,7 @@ Classic profile featuring controls provided by the finops foundation 101: Track Training Impact & Refresh Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -157,31 +157,31 @@ Classic profile featuring controls provided by the finops foundation 092: Create Implementation Roadmap -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

093: Integrate Cost Data in Workflows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

094: Publish FinOps KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

095: Drive Stakeholder Engagement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

096: Automate Tasks & Review Maturity -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -203,13 +203,13 @@ Classic profile featuring controls provided by the finops foundation 125: Evaluate Build vs Buy Options -

Specification Version: 1.7.0

+

Specification Version: 2.0.0

126: Implement & Configure Tools -

Specification Version: 0.7.0

+

Specification Version: 1.0.0

@@ -237,37 +237,37 @@ Classic profile featuring controls provided by the finops foundation 129: Establish Cross-Team Collab -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

130: Align Goals & Data Models -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

131: Share Reporting & Definitions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

132: Provide Technology Metrics to Teams -

Specification Version: 0.9.0

+

Specification Version: 1.0.0

133: Coordinate Policy Changes Broadly -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

134: Drive Adoption & Resolve Conflicts -

Specification Version: 0.8.1

+

Specification Version: 1.0.0

@@ -283,13 +283,13 @@ Classic profile featuring controls provided by the finops foundation 107: Map Invoices to Allocation Model -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

108: Reconcile Rates & Track Completion -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -301,13 +301,13 @@ Classic profile featuring controls provided by the finops foundation 110: Automate Chargeback Data Flows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

111: Distribute Reports & Track Accuracy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -322,14 +322,14 @@ Classic profile featuring controls provided by the finops foundation 023: Executive Strategy Alignment -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

@@ -409,13 +409,13 @@ Classic profile featuring controls provided by the finops foundation @@ -427,19 +427,19 @@ Classic profile featuring controls provided by the finops foundation
136: Establish Executive Sponsorship -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

118: Create Workload Selection Criteria -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

119: Define Success Criteria & Scope -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

121: Align Timelines with Budgets -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

122: Start with Lower Environments -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

123: Review Outcomes & Adjust Patterns -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -454,38 +454,38 @@ Classic profile featuring controls provided by the finops foundation
- 085: Aggregate emissions data by region/service/account with lineage. -

Specification Version: 0.5.0

+ 085: Aggregate Emissions Data +

Specification Version: 0.6.0

- 086: Define sustainability metrics/targets. -

Specification Version: 0.5.0

+ 086: Define Sustainability Metrics +

Specification Version: 0.6.0

- 087: Include carbon impact in migration/optimization decisions and PRDs. -

Specification Version: 0.5.0

+ 087: Carbon Impact Workload Decisions +

Specification Version: 0.6.0

- 088: Enable engineering with carbon-aware data and scheduling guidance. -

Specification Version: 0.5.0

+ 088: Carbon Aware Data Guidance +

Specification Version: 0.6.0

- 089: Continuously improve data quality and report progress on dashboards. -

Specification Version: 0.5.0

+ 089: Sustainability Data Continuous Improvement +

Specification Version: 0.6.0

- 090: Tie sustainability wins to cost efficiency where possible. -

Specification Version: 0.5.0

+ 090: Tie Sustainability to Cost Efficiency +

Specification Version: 0.6.0

@@ -507,13 +507,13 @@ Classic profile featuring controls provided by the finops foundation 074: Document Licensing Models -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

075: Integrate Billing with Procurement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -525,13 +525,13 @@ Classic profile featuring controls provided by the finops foundation 077: Influence Workload Design Choices -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

078: Publish License Spend & Utilization -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -593,7 +593,7 @@ Classic profile featuring controls provided by the finops foundation 067: Define Optimization Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -611,19 +611,19 @@ Classic profile featuring controls provided by the finops foundation 070: Partner on Scheduling Actions -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

071: Gamify Adoption & Celebrate Wins -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

072: Track KPIs & Document Playbooks -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -707,25 +707,25 @@ Classic profile featuring controls provided by the finops foundation 045: Increase Shared Cost Coverage -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

046: Publish Budget vs Actual Reports -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

047: Automate Budget Threshold Alerts -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

048: Review & Adapt Budget Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -787,25 +787,25 @@ Classic profile featuring controls provided by the finops foundation 030: Define Estimating Scope & Policy -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

031: Agree on Pricing Assumptions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

032: Select Estimation Methods -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

033: Include Sustainability Impacts -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -823,7 +823,7 @@ Classic profile featuring controls provided by the finops foundation 036: Coach Engineering on Estimates -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -845,25 +845,25 @@ Classic profile featuring controls provided by the finops foundation 056: Document Inputs & Formulas -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

057: Build Unit Cost Dashboards -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

058: Set Review Cadence & Targets -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

059: Use Metrics to Prioritize Investments -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -1063,7 +1063,7 @@ Classic profile featuring controls provided by the finops foundation 016: Gather Requirements & Define KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -1081,7 +1081,7 @@ Classic profile featuring controls provided by the finops foundation 019: Publish Documentation & Support -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -1093,13 +1093,13 @@ Classic profile featuring controls provided by the finops foundation 021: Track Adoption & Manage Changes -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

022: Iterate Based on Feedback -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

diff --git a/assessments/FinOps Foundation/history/2026-04-12.json.gz b/assessments/FinOps Foundation/history/2026-04-12.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..65b613488719879fd27799dc40dbd541a1e2a95f GIT binary patch literal 10125 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 z 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 N*@hwpLV^GQ006IT&Yb`N delta 19702 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 diff --git a/assessments/FinOps++/framework.md b/assessments/FinOps++/framework.md index 8a603c25..217640f8 100644 --- a/assessments/FinOps++/framework.md +++ b/assessments/FinOps++/framework.md @@ -1,6 +1,6 @@ # 002: FinOps++ Maturity Assessment Framework -**Creation Date:** 2026-04-04 +**Creation Date:** 2026-04-12 **Specification Version:** 0.0.1 @@ -31,25 +31,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 113: Define Assessment Scope & Goals -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

114: Collect Standardized Assessment Inputs -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

115: Establish Baselines & Identify Trends -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

116: Prioritize Actions & Publish Roadmap -

Specification Version: 0.7.1

+

Specification Version: 1.0.0

@@ -71,19 +71,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 097: Assess Skills & Training Needs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

098: Develop Role-Based Learning Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

099: Offer Certifications & Informal Learning -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -95,7 +95,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 101: Track Training Impact & Refresh Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -117,31 +117,31 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 092: Create Implementation Roadmap -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

093: Integrate Cost Data in Workflows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

094: Publish FinOps KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

095: Drive Stakeholder Engagement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

096: Automate Tasks & Review Maturity -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -163,13 +163,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 125: Evaluate Build vs Buy Options -

Specification Version: 1.7.0

+

Specification Version: 2.0.0

126: Implement & Configure Tools -

Specification Version: 0.7.0

+

Specification Version: 1.0.0

@@ -197,37 +197,37 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 129: Establish Cross-Team Collab -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

130: Align Goals & Data Models -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

131: Share Reporting & Definitions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

132: Provide Technology Metrics to Teams -

Specification Version: 0.9.0

+

Specification Version: 1.0.0

133: Coordinate Policy Changes Broadly -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

134: Drive Adoption & Resolve Conflicts -

Specification Version: 0.8.1

+

Specification Version: 1.0.0

@@ -243,13 +243,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 107: Map Invoices to Allocation Model -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

108: Reconcile Rates & Track Completion -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -261,13 +261,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 110: Automate Chargeback Data Flows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

111: Distribute Reports & Track Accuracy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -282,14 +282,14 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 023: Executive Strategy Alignment -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

@@ -369,13 +369,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio @@ -387,19 +387,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio
136: Establish Executive Sponsorship -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

118: Create Workload Selection Criteria -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

119: Define Success Criteria & Scope -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

121: Align Timelines with Budgets -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

122: Start with Lower Environments -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

123: Review Outcomes & Adjust Patterns -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -421,13 +421,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 074: Document Licensing Models -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

075: Integrate Billing with Procurement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -439,13 +439,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 077: Influence Workload Design Choices -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

078: Publish License Spend & Utilization -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -507,7 +507,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 067: Define Optimization Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -525,19 +525,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 070: Partner on Scheduling Actions -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

071: Gamify Adoption & Celebrate Wins -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

072: Track KPIs & Document Playbooks -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -721,7 +721,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 016: Gather Requirements & Define KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -739,7 +739,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 019: Publish Documentation & Support -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -751,13 +751,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 021: Track Adoption & Manage Changes -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

022: Iterate Based on Feedback -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -841,25 +841,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 045: Increase Shared Cost Coverage -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

046: Publish Budget vs Actual Reports -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

047: Automate Budget Threshold Alerts -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

048: Review & Adapt Budget Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -921,25 +921,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 030: Define Estimating Scope & Policy -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

031: Agree on Pricing Assumptions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

032: Select Estimation Methods -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

033: Include Sustainability Impacts -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -957,7 +957,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 036: Coach Engineering on Estimates -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -979,25 +979,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 056: Document Inputs & Formulas -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

057: Build Unit Cost Dashboards -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

058: Set Review Cadence & Targets -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

059: Use Metrics to Prioritize Investments -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

diff --git a/assessments/FinOps++/history/2026-04-12.json.gz b/assessments/FinOps++/history/2026-04-12.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6f4f90fd3c820d2920da385791d917b75a0de6da GIT binary patch literal 10243 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 Date: Sun, 12 Apr 2026 16:23:26 -0500 Subject: [PATCH 3/4] clean up newlines in the github actions --- .github/workflows/testing.yml | 3 --- .github/workflows/validation.yml | 1 - 2 files changed, 4 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index dcf80501..6b95cd17 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -24,6 +24,3 @@ jobs: - name: Run unit tests for the finopspp tool run: | pytest tools - - - diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index b142d7b2..621c0ddc 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -51,4 +51,3 @@ jobs: - name: Validate all action specifications with finopspp tool run: | finopspp specifications validate --specification-type="actions" all - From fd31149f5f67ca2339a6f663c275a2bde6d6fe58 Mon Sep 17 00:00:00 2001 From: Travis Salas Cox Date: Sun, 12 Apr 2026 16:28:01 -0500 Subject: [PATCH 4/4] clean up linting --- components/actions/125.md | 12 ++++++------ specifications/actions/125.yaml | 16 ++++++++-------- tools/tests/test_assessment_generation.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/actions/125.md b/components/actions/125.md index a84c2b62..8487435f 100644 --- a/components/actions/125.md +++ b/components/actions/125.md @@ -1,6 +1,6 @@ # 125: Evaluate Build Vs Buy Options -**Specification Version:** 2.0.0 +**Specification Version:** 2.0.1 **Slug:** 125-evaluate-build @@ -16,6 +16,7 @@ Evaluate build vs buy; map requirements to vendor capabilities. CRAWL (0–8) No build-vs-buy process exists. TCO analysis is absent or undocumented, vendor comparisons are informal, and decisions default to familiarity, relationships, or cost alone. + WALK (9–17) Basic TCO covers License costs and implementation effort. Vendors are scored against a structured scorecard, and decisions are loosely documented. Finance validation and exec sign-off are inconsistent. @@ -27,7 +28,6 @@ FLY (25–29) TCO is Finance-validated and accounts for multi-year trajectories, optionality. A full RFP with documented rationale is used, decisions are exec-approved and periodically reviewed, and an enforced org-wide policy governs all future build-vs-buy choices. - ## Scoring Guide **Weight:** 1.0 @@ -35,10 +35,10 @@ and an enforced org-wide policy governs all future build-vs-buy choices. ### Formula * Weighted sum - TCO (score ×3), - Vendor scoring (score ×3), - Decision document (score ×3), - Rules established (score ×1). + TCO (score ×3), + Vendor scoring (score ×3), + Decision document (score ×3), + Rules established (score ×1). Max Raw Score (29) * TCO_Analysis - Total Cost of Ownership analysis performed (0=None, 1=High-level, 2=Detailed, 3=Validated with Finance) diff --git a/specifications/actions/125.yaml b/specifications/actions/125.yaml index dfbe5abe..5de28153 100644 --- a/specifications/actions/125.yaml +++ b/specifications/actions/125.yaml @@ -2,7 +2,7 @@ Metadata: Proposed: '2025-09-16' Adopted: '2025-11-01' Modified: '2026-04-04' - Version: 2.0.0 + Version: 2.0.1 Status: Accepted Approvers: - Name: Victoria Levy @@ -19,12 +19,12 @@ Specification: Implementation Types: - null Weight: 1.0 - Formula: |- + Formula: | * Weighted sum - TCO (score ×3), - Vendor scoring (score ×3), - Decision document (score ×3), - Rules established (score ×1). + TCO (score ×3), + Vendor scoring (score ×3), + Decision document (score ×3), + Rules established (score ×1). Max Raw Score (29) * TCO_Analysis - Total Cost of Ownership analysis performed (0=None, 1=High-level, 2=Detailed, 3=Validated with Finance) @@ -50,7 +50,7 @@ Specification: Link: https://www.finops.org/framework/maturity-model/ Comment: null Supplemental Guidance: - - |- + - | CRAWL (0–8) No build-vs-buy process exists. TCO analysis is absent or undocumented, vendor comparisons are informal, and decisions default to familiarity, relationships, or cost alone. - | @@ -60,7 +60,7 @@ Specification: RUN (18–24) TCO is detailed, covering licensing, implementation, integration, training, support, and build opportunity cost. A multi-stakeholder evaluation process is used, decisions are formally approved, and a guiding policy is published and partially enforced. - - | + - |- FLY (25–29) TCO is Finance-validated and accounts for multi-year trajectories, exit costs, and strategic optionality. A full RFP with documented rationale is used, decisions are exec-approved and periodically reviewed, and an enforced org-wide policy governs all future build-vs-buy choices. diff --git a/tools/tests/test_assessment_generation.py b/tools/tests/test_assessment_generation.py index 07c8d2ef..8dcc34ec 100644 --- a/tools/tests/test_assessment_generation.py +++ b/tools/tests/test_assessment_generation.py @@ -28,7 +28,7 @@ def test_normalize(): domains = domains_collector( profile, profile_spec, domain_files, cap_files, action_files ) - assert domains is not [] + assert domains data_path = os.path.join(os.getcwd(), 'tools/tests/data/expected_example_domains.json') with open(data_path, mode='r', encoding='utf-8') as in_file: