diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/after/dataproducts/analytics/prod/product.yaml
deleted file mode 100644
index 54f7923..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/after/dataproducts/analytics/prod/product.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-name: analytics
-kind: source-aligned
-rover_group: dataverse-source-analytics
-warehouses:
-- type: user
- size: MEDIUM
-- type: service_account
- size: XSMALL
-service_account:
- dbt: true
-tags:
- data_product: analytics
-data_product_db:
-- database: analytics_db
- presentation_schemas:
- - name: marts
- consumers:
- - kind: data_product
- name: journey
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/after/dataproducts/analytics/prod/product.yaml
deleted file mode 100644
index 75f3726..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/after/dataproducts/analytics/prod/product.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
----
-name: customer-analytics
-kind: source-aligned
-rover_group: dataverse-source-analytics
-warehouses:
-- type: user
- size: SMALL
-- type: service_account
- size: XSMALL
-service_account:
- dbt: true
-tags:
- data_product: analytics
- pii: true
-data_product_db:
-- database: customer_analytics_db
- presentation_schemas:
- - name: marts
- consumers: []
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/before/dataproducts/analytics/prod/product.yaml
deleted file mode 100644
index 5206e87..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/before/dataproducts/analytics/prod/product.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
----
-name: customer-analytics
-kind: source-aligned
-rover_group: dataverse-source-analytics
-warehouses:
-- type: user
- size: SMALL
-- type: service_account
- size: XSMALL
-service_account:
- dbt: true
-tags:
- data_product: analytics
-data_product_db:
-- database: customer_analytics_db
- presentation_schemas:
- - name: marts
- consumers: []
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/scenario.yaml b/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/scenario.yaml
deleted file mode 100644
index c1d6650..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_product_sections/scenario.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: "Metadata-only product sections update"
-description: "Changes only to metadata sections (name, tags, rover_group) in product.yaml should be auto-approved"
-
-expected:
- decision: Approve
- reason: "All files passed validation - all changes covered by approved rules"
- approved: true
-
- comment_contains:
- - "✅ **Auto-approved**"
- - "TOC approval check"
-
-mr_metadata:
- title: "Update product metadata sections"
- author: "testuser"
- source_branch: "metadata/update-product-info"
- target_branch: "main"
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_readme/scenario.yaml b/e2e/testdata/scenarios/01_single_rule_single_file/metadata_readme/scenario.yaml
deleted file mode 100644
index c03597f..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/metadata_readme/scenario.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: "README documentation update"
-description: "README.md updates should be auto-approved as metadata-only changes"
-
-expected:
- decision: Approve
- reason: "All files passed validation - all changes covered by approved rules"
- approved: true
-
- rules_evaluated:
- - name: metadata_rule
- section: documentation
- decision: Approve
-
- comment_contains:
- - "✅ **Auto-approved**"
- - "Metadata validated"
-
-mr_metadata:
- title: "Update analytics product documentation"
- author: "testuser"
- source_branch: "docs/update-analytics-readme"
- target_branch: "main"
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/expected_comment.txt b/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/expected_comment.txt
deleted file mode 100644
index b69fc75..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/expected_comment.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-
-✅ **Auto-approved**
-
-**What was checked:**
-• ✅ Metadata validated
-• ✅ TOC approval check
-• ✅ Warehouse configuration validated
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/expected_comment.txt b/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/expected_comment.txt
deleted file mode 100644
index 4fe6a89..0000000
--- a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/expected_comment.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-
-⚠️ **Manual review required**
-
-**Why manual review is needed:**
-One or more files require manual review
-
-**What was checked:**
-• ✅ Metadata validated
-• ✅ TOC approval check
-• 🚫 Warehouse configuration validated: Warehouse size increase detected: user warehouse: SMALL → MEDIUM
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/after/dataproducts/marketing/prod/product.yaml b/e2e/testdata/scenarios/01_warehouse_decrease/after/dataproducts/marketing/prod/product.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/after/dataproducts/marketing/prod/product.yaml
rename to e2e/testdata/scenarios/01_warehouse_decrease/after/dataproducts/marketing/prod/product.yaml
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/before/dataproducts/marketing/prod/product.yaml b/e2e/testdata/scenarios/01_warehouse_decrease/before/dataproducts/marketing/prod/product.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/before/dataproducts/marketing/prod/product.yaml
rename to e2e/testdata/scenarios/01_warehouse_decrease/before/dataproducts/marketing/prod/product.yaml
diff --git a/e2e/testdata/scenarios/01_warehouse_decrease/expected_comment.txt b/e2e/testdata/scenarios/01_warehouse_decrease/expected_comment.txt
new file mode 100644
index 0000000..26c87c0
--- /dev/null
+++ b/e2e/testdata/scenarios/01_warehouse_decrease/expected_comment.txt
@@ -0,0 +1,12 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+• ✅ Warehouse configuration validated: Warehouse size decrease saves costs and is safe to auto-approve
+
+
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/scenario.yaml b/e2e/testdata/scenarios/01_warehouse_decrease/scenario.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_decrease/scenario.yaml
rename to e2e/testdata/scenarios/01_warehouse_decrease/scenario.yaml
diff --git a/e2e/testdata/scenarios/02_toc_approval/toc_new_prod/scenario.yaml b/e2e/testdata/scenarios/02_toc_approval/toc_new_prod/scenario.yaml
deleted file mode 100644
index ea0865c..0000000
--- a/e2e/testdata/scenarios/02_toc_approval/toc_new_prod/scenario.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: "NEW product in prod environment"
-description: "Brand new product.yaml file in prod requires TOC approval before deployment"
-
-expected:
- decision: ManualReview
- reason: "One or more files require manual review"
- approved: false
-
- rules_evaluated:
- - name: toc_approval_rule
- section: full_file
- decision: ManualReview
- reason: "New data product being promoted to prod environment requires TOC"
-
- comment_contains:
- - "⚠️ **Manual review required**"
- - "TOC (Technical Oversight Committee) approval"
- - "prod environment"
-
-mr_metadata:
- title: "Deploy new sales analytics product to production"
- author: "testuser"
- source_branch: "feature/new-sales-product"
- target_branch: "main"
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/after/dataproducts/marketing/prod/product.yaml b/e2e/testdata/scenarios/02_warehouse_increase/after/dataproducts/marketing/prod/product.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/after/dataproducts/marketing/prod/product.yaml
rename to e2e/testdata/scenarios/02_warehouse_increase/after/dataproducts/marketing/prod/product.yaml
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/before/dataproducts/marketing/prod/product.yaml b/e2e/testdata/scenarios/02_warehouse_increase/before/dataproducts/marketing/prod/product.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/before/dataproducts/marketing/prod/product.yaml
rename to e2e/testdata/scenarios/02_warehouse_increase/before/dataproducts/marketing/prod/product.yaml
diff --git a/e2e/testdata/scenarios/02_warehouse_increase/expected_comment.txt b/e2e/testdata/scenarios/02_warehouse_increase/expected_comment.txt
new file mode 100644
index 0000000..dbf0619
--- /dev/null
+++ b/e2e/testdata/scenarios/02_warehouse_increase/expected_comment.txt
@@ -0,0 +1,15 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+One or more files require manual review
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+• 🚫 Warehouse configuration validated: Warehouse size increase detected: user warehouse: SMALL → MEDIUM
+
+
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/scenario.yaml b/e2e/testdata/scenarios/02_warehouse_increase/scenario.yaml
similarity index 100%
rename from e2e/testdata/scenarios/01_single_rule_single_file/warehouse_increase/scenario.yaml
rename to e2e/testdata/scenarios/02_warehouse_increase/scenario.yaml
diff --git a/e2e/testdata/scenarios/02_toc_approval/toc_new_prod/after/dataproducts/sales/prod/product.yaml b/e2e/testdata/scenarios/03_toc_new_prod/after/dataproducts/sales/prod/product.yaml
similarity index 76%
rename from e2e/testdata/scenarios/02_toc_approval/toc_new_prod/after/dataproducts/sales/prod/product.yaml
rename to e2e/testdata/scenarios/03_toc_new_prod/after/dataproducts/sales/prod/product.yaml
index fbd6800..0382314 100644
--- a/e2e/testdata/scenarios/02_toc_approval/toc_new_prod/after/dataproducts/sales/prod/product.yaml
+++ b/e2e/testdata/scenarios/03_toc_new_prod/after/dataproducts/sales/prod/product.yaml
@@ -1,7 +1,7 @@
---
name: sales-analytics
kind: source-aligned
-rover_group: dataverse-source-sales
+rover_group: example-sales
warehouses:
- type: user
size: LARGE
@@ -11,8 +11,8 @@ service_account:
dbt: true
tags:
data_product: sales
+ tier: production
data_product_db:
-- database: sales_db
presentation_schemas:
- - name: marts
+ - name: reporting
consumers: []
diff --git a/e2e/testdata/scenarios/03_toc_new_prod/expected_comment.txt b/e2e/testdata/scenarios/03_toc_new_prod/expected_comment.txt
new file mode 100644
index 0000000..4221a5d
--- /dev/null
+++ b/e2e/testdata/scenarios/03_toc_new_prod/expected_comment.txt
@@ -0,0 +1,15 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+One or more files require manual review
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• 🚫 TOC approval check: Manual review required: New data product being promoted to prod environment requires TOC (Technical Oversight Committee) approval before deployment
+• 🚫 Warehouse configuration validated: Warehouse size increase detected: New user warehouse: LARGE, New service_account warehouse: XSMALL
+
+
diff --git a/e2e/testdata/scenarios/03_toc_new_prod/scenario.yaml b/e2e/testdata/scenarios/03_toc_new_prod/scenario.yaml
new file mode 100644
index 0000000..5cf0da5
--- /dev/null
+++ b/e2e/testdata/scenarios/03_toc_new_prod/scenario.yaml
@@ -0,0 +1,17 @@
+name: "New product.yaml in prod"
+description: "New product.yaml in prod environment requires TOC approval"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+ - "TOC approval"
+
+mr_metadata:
+ title: "Deploy new sales-analytics product to production"
+ author: "testuser"
+ source_branch: "feature/new-sales-product"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/04_consumer_only_prod/after/dataproducts/analytics/prod/product.yaml
similarity index 70%
rename from e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/before/dataproducts/analytics/prod/product.yaml
rename to e2e/testdata/scenarios/04_consumer_only_prod/after/dataproducts/analytics/prod/product.yaml
index b0792fe..b9e15d4 100644
--- a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/before/dataproducts/analytics/prod/product.yaml
+++ b/e2e/testdata/scenarios/04_consumer_only_prod/after/dataproducts/analytics/prod/product.yaml
@@ -1,7 +1,7 @@
---
name: analytics
kind: source-aligned
-rover_group: dataverse-source-analytics
+rover_group: example-analytics
warehouses:
- type: user
size: MEDIUM
@@ -12,7 +12,10 @@ service_account:
tags:
data_product: analytics
data_product_db:
-- database: analytics_db
presentation_schemas:
- name: marts
+ consumers:
+ - name: journey
+ kind: data_product
+ - name: staging
consumers: []
diff --git a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/04_consumer_only_prod/before/dataproducts/analytics/prod/product.yaml
similarity index 79%
rename from e2e/testdata/scenarios/04_integration/integration_uncovered_lines/before/dataproducts/analytics/prod/product.yaml
rename to e2e/testdata/scenarios/04_consumer_only_prod/before/dataproducts/analytics/prod/product.yaml
index b0792fe..75478cd 100644
--- a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/before/dataproducts/analytics/prod/product.yaml
+++ b/e2e/testdata/scenarios/04_consumer_only_prod/before/dataproducts/analytics/prod/product.yaml
@@ -1,7 +1,7 @@
---
name: analytics
kind: source-aligned
-rover_group: dataverse-source-analytics
+rover_group: example-analytics
warehouses:
- type: user
size: MEDIUM
@@ -12,7 +12,8 @@ service_account:
tags:
data_product: analytics
data_product_db:
-- database: analytics_db
presentation_schemas:
- name: marts
consumers: []
+ - name: staging
+ consumers: []
diff --git a/e2e/testdata/scenarios/04_consumer_only_prod/expected_comment.txt b/e2e/testdata/scenarios/04_consumer_only_prod/expected_comment.txt
new file mode 100644
index 0000000..480cb9e
--- /dev/null
+++ b/e2e/testdata/scenarios/04_consumer_only_prod/expected_comment.txt
@@ -0,0 +1,10 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+
+
diff --git a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/scenario.yaml b/e2e/testdata/scenarios/04_consumer_only_prod/scenario.yaml
similarity index 60%
rename from e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/scenario.yaml
rename to e2e/testdata/scenarios/04_consumer_only_prod/scenario.yaml
index 145d808..939cced 100644
--- a/e2e/testdata/scenarios/01_single_rule_single_file/consumer_only_prod/scenario.yaml
+++ b/e2e/testdata/scenarios/04_consumer_only_prod/scenario.yaml
@@ -1,19 +1,13 @@
name: "Consumer-only changes in prod"
-description: "Adding consumers to product.yaml should auto-approve (owner approval sufficient, no TOC needed)"
+description: "Consumer-only changes should be auto-approved (owner approval sufficient)"
expected:
decision: Approve
reason: "All files passed validation - all changes covered by approved rules"
approved: true
- rules_evaluated:
- - name: dataproduct_consumer_rule
- section: consumers
- decision: Approve
-
comment_contains:
- "✅ **Auto-approved**"
- - "TOC approval check"
mr_metadata:
title: "Grant journey product access to analytics data"
diff --git a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/scenario.yaml b/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/scenario.yaml
deleted file mode 100644
index 0083e1d..0000000
--- a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/scenario.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: "Uncovered lines trigger manual review"
-description: "Files with lines not covered by any validation rule must require manual review (strict coverage policy)"
-
-expected:
- decision: ManualReview
- reason: "One or more files require manual review"
- approved: false
-
- rules_evaluated:
- - name: metadata_rule
- section: name
- decision: Approve
- - name: metadata_rule
- section: tags
- decision: Approve
-
- comment_contains:
- - "⚠️ **Manual review required**"
- - "manual review"
-
-mr_metadata:
- title: "Update product with uncovered sections"
- author: "testuser"
- source_branch: "feature/product-with-uncovered-sections"
- target_branch: "main"
diff --git a/e2e/testdata/scenarios/05_metadata_readme/after/dataproducts/analytics/README.md b/e2e/testdata/scenarios/05_metadata_readme/after/dataproducts/analytics/README.md
new file mode 100644
index 0000000..4866414
--- /dev/null
+++ b/e2e/testdata/scenarios/05_metadata_readme/after/dataproducts/analytics/README.md
@@ -0,0 +1,18 @@
+# Analytics Product
+
+## Overview
+
+This is the analytics data product for our organization.
+
+## Setup Instructions
+
+New and improved setup instructions with detailed steps:
+
+1. Clone the repository
+2. Configure your environment
+3. Run the initialization script
+4. Verify the setup
+
+## Contact
+
+Data Team - analytics@example.com
diff --git a/e2e/testdata/scenarios/05_metadata_readme/before/dataproducts/analytics/README.md b/e2e/testdata/scenarios/05_metadata_readme/before/dataproducts/analytics/README.md
new file mode 100644
index 0000000..6d446be
--- /dev/null
+++ b/e2e/testdata/scenarios/05_metadata_readme/before/dataproducts/analytics/README.md
@@ -0,0 +1,13 @@
+# Analytics Product
+
+## Overview
+
+This is the analytics data product for our organization.
+
+## Setup Instructions
+
+Old setup instructions go here.
+
+## Contact
+
+Data Team
diff --git a/e2e/testdata/scenarios/05_metadata_readme/expected_comment.txt b/e2e/testdata/scenarios/05_metadata_readme/expected_comment.txt
new file mode 100644
index 0000000..673b692
--- /dev/null
+++ b/e2e/testdata/scenarios/05_metadata_readme/expected_comment.txt
@@ -0,0 +1,10 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+
+
diff --git a/e2e/testdata/scenarios/05_metadata_readme/scenario.yaml b/e2e/testdata/scenarios/05_metadata_readme/scenario.yaml
new file mode 100644
index 0000000..fe42cff
--- /dev/null
+++ b/e2e/testdata/scenarios/05_metadata_readme/scenario.yaml
@@ -0,0 +1,16 @@
+name: "README.md update"
+description: "README updates should be auto-approved (documentation only)"
+
+expected:
+ decision: Approve
+ reason: "All files passed validation - all changes covered by approved rules"
+ approved: true
+
+ comment_contains:
+ - "✅ **Auto-approved**"
+
+mr_metadata:
+ title: "Update README with new setup instructions"
+ author: "testuser"
+ source_branch: "feature/update-docs"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/06_metadata_product_sections/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/06_metadata_product_sections/after/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..312d29f
--- /dev/null
+++ b/e2e/testdata/scenarios/06_metadata_product_sections/after/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,7 @@
+---
+name: analytics-v2
+kind: source-aligned
+rover_group: example-analytics
+tags:
+ data_product: analytics
+ pii: true
diff --git a/e2e/testdata/scenarios/06_metadata_product_sections/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/06_metadata_product_sections/before/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..9cad609
--- /dev/null
+++ b/e2e/testdata/scenarios/06_metadata_product_sections/before/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,6 @@
+---
+name: analytics
+kind: source-aligned
+rover_group: example-analytics
+tags:
+ data_product: analytics
diff --git a/e2e/testdata/scenarios/06_metadata_product_sections/expected_comment.txt b/e2e/testdata/scenarios/06_metadata_product_sections/expected_comment.txt
new file mode 100644
index 0000000..9b7a68b
--- /dev/null
+++ b/e2e/testdata/scenarios/06_metadata_product_sections/expected_comment.txt
@@ -0,0 +1,11 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+
+
diff --git a/e2e/testdata/scenarios/06_metadata_product_sections/scenario.yaml b/e2e/testdata/scenarios/06_metadata_product_sections/scenario.yaml
new file mode 100644
index 0000000..e46f3d0
--- /dev/null
+++ b/e2e/testdata/scenarios/06_metadata_product_sections/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Metadata-only sections in product.yaml"
+description: "Metadata-only sections (name, tags) should be auto-approved"
+
+expected:
+ decision: Approve
+ reason: "All files passed validation - all changes covered by approved rules"
+ approved: true
+
+ comment_contains:
+ - "✅ **Auto-approved**"
+
+mr_metadata:
+ title: "Update product name and add PII tag"
+ author: "testuser"
+ source_branch: "feature/update-metadata"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/07_integration_uncovered_lines/after/migrations/001_add_column.sql b/e2e/testdata/scenarios/07_integration_uncovered_lines/after/migrations/001_add_column.sql
new file mode 100644
index 0000000..0f628cb
--- /dev/null
+++ b/e2e/testdata/scenarios/07_integration_uncovered_lines/after/migrations/001_add_column.sql
@@ -0,0 +1,3 @@
+-- Migration: Add email column to users table
+ALTER TABLE users ADD COLUMN email VARCHAR(255);
+CREATE INDEX idx_users_email ON users(email);
diff --git a/e2e/testdata/scenarios/07_integration_uncovered_lines/expected_comment.txt b/e2e/testdata/scenarios/07_integration_uncovered_lines/expected_comment.txt
new file mode 100644
index 0000000..e4e51de
--- /dev/null
+++ b/e2e/testdata/scenarios/07_integration_uncovered_lines/expected_comment.txt
@@ -0,0 +1,14 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+
+This MR contains files that Naysayer doesn't know how to validate.
+
+
+Files requiring manual approval (no automated rules configured) (1 file)
+
+**No validation rules configured for SQL migrations** (1 file)
+- `migrations/001_add_column.sql`
+
+
diff --git a/e2e/testdata/scenarios/07_integration_uncovered_lines/scenario.yaml b/e2e/testdata/scenarios/07_integration_uncovered_lines/scenario.yaml
new file mode 100644
index 0000000..e2e14f0
--- /dev/null
+++ b/e2e/testdata/scenarios/07_integration_uncovered_lines/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Unknown file type requires manual review"
+description: "Files without section configuration should trigger manual review (strict coverage policy)"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+
+mr_metadata:
+ title: "Add SQL migration script"
+ author: "testuser"
+ source_branch: "feature/add-migration"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/after/dataproducts/analytics/prod/product.yaml
similarity index 56%
rename from e2e/testdata/scenarios/04_integration/integration_uncovered_lines/after/dataproducts/analytics/prod/product.yaml
rename to e2e/testdata/scenarios/08_warehouse_multiple_mixed/after/dataproducts/analytics/prod/product.yaml
index 4d72151..58d76c0 100644
--- a/e2e/testdata/scenarios/04_integration/integration_uncovered_lines/after/dataproducts/analytics/prod/product.yaml
+++ b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/after/dataproducts/analytics/prod/product.yaml
@@ -1,7 +1,7 @@
---
name: analytics
kind: source-aligned
-rover_group: dataverse-source-analytics
+rover_group: example-analytics
warehouses:
- type: user
size: LARGE
@@ -11,8 +11,3 @@ service_account:
dbt: true
tags:
data_product: analytics
-data_product_db:
-- database: analytics_db
- presentation_schemas:
- - name: marts
- consumers: []
diff --git a/e2e/testdata/scenarios/08_warehouse_multiple_mixed/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/before/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..fa80c01
--- /dev/null
+++ b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/before/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,13 @@
+---
+name: analytics
+kind: source-aligned
+rover_group: example-analytics
+warehouses:
+- type: user
+ size: MEDIUM
+- type: service_account
+ size: SMALL
+service_account:
+ dbt: true
+tags:
+ data_product: analytics
diff --git a/e2e/testdata/scenarios/08_warehouse_multiple_mixed/expected_comment.txt b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/expected_comment.txt
new file mode 100644
index 0000000..5c5c864
--- /dev/null
+++ b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/expected_comment.txt
@@ -0,0 +1,15 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+One or more files require manual review
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+• 🚫 Warehouse configuration validated: Warehouse size increase detected: user warehouse: MEDIUM → LARGE
+
+
diff --git a/e2e/testdata/scenarios/08_warehouse_multiple_mixed/scenario.yaml b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/scenario.yaml
new file mode 100644
index 0000000..2f4ae94
--- /dev/null
+++ b/e2e/testdata/scenarios/08_warehouse_multiple_mixed/scenario.yaml
@@ -0,0 +1,17 @@
+name: "Multiple warehouses with mixed changes"
+description: "Multiple warehouses - some increase, some decrease - should require manual review (conservative)"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+ - "Warehouse"
+
+mr_metadata:
+ title: "Rebalance warehouse resources"
+ author: "testuser"
+ source_branch: "feature/rebalance-warehouses"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/09_toc_new_preprod/after/dataproducts/sales/preprod/product.yaml b/e2e/testdata/scenarios/09_toc_new_preprod/after/dataproducts/sales/preprod/product.yaml
new file mode 100644
index 0000000..c3d5871
--- /dev/null
+++ b/e2e/testdata/scenarios/09_toc_new_preprod/after/dataproducts/sales/preprod/product.yaml
@@ -0,0 +1,14 @@
+---
+name: sales-analytics
+kind: source-aligned
+rover_group: example-sales
+warehouses:
+- type: user
+ size: MEDIUM
+- type: service_account
+ size: XSMALL
+service_account:
+ dbt: true
+tags:
+ data_product: sales
+ tier: preprod
diff --git a/e2e/testdata/scenarios/09_toc_new_preprod/expected_comment.txt b/e2e/testdata/scenarios/09_toc_new_preprod/expected_comment.txt
new file mode 100644
index 0000000..5b83aa4
--- /dev/null
+++ b/e2e/testdata/scenarios/09_toc_new_preprod/expected_comment.txt
@@ -0,0 +1,15 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+One or more files require manual review
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• 🚫 TOC approval check: Manual review required: New data product being promoted to preprod environment requires TOC (Technical Oversight Committee) approval before deployment
+• 🚫 Warehouse configuration validated: Warehouse size increase detected: New user warehouse: MEDIUM, New service_account warehouse: XSMALL
+
+
diff --git a/e2e/testdata/scenarios/09_toc_new_preprod/scenario.yaml b/e2e/testdata/scenarios/09_toc_new_preprod/scenario.yaml
new file mode 100644
index 0000000..4e10bb0
--- /dev/null
+++ b/e2e/testdata/scenarios/09_toc_new_preprod/scenario.yaml
@@ -0,0 +1,17 @@
+name: "New product.yaml in preprod"
+description: "New product.yaml in preprod environment requires TOC approval"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+ - "TOC approval"
+
+mr_metadata:
+ title: "Deploy new sales-analytics product to preprod"
+ author: "testuser"
+ source_branch: "feature/new-sales-preprod"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/10_consumer_multiple_schemas/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/10_consumer_multiple_schemas/after/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..7e5ea7c
--- /dev/null
+++ b/e2e/testdata/scenarios/10_consumer_multiple_schemas/after/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,14 @@
+---
+name: analytics
+kind: source-aligned
+rover_group: example-analytics
+data_product_db:
+ presentation_schemas:
+ - name: marts
+ consumers:
+ - name: journey
+ kind: data_product
+ - name: staging
+ consumers:
+ - name: reporting
+ kind: data_product
diff --git a/e2e/testdata/scenarios/10_consumer_multiple_schemas/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/10_consumer_multiple_schemas/before/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..b1ad217
--- /dev/null
+++ b/e2e/testdata/scenarios/10_consumer_multiple_schemas/before/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,10 @@
+---
+name: analytics
+kind: source-aligned
+rover_group: example-analytics
+data_product_db:
+ presentation_schemas:
+ - name: marts
+ consumers: []
+ - name: staging
+ consumers: []
diff --git a/e2e/testdata/scenarios/10_consumer_multiple_schemas/expected_comment.txt b/e2e/testdata/scenarios/10_consumer_multiple_schemas/expected_comment.txt
new file mode 100644
index 0000000..480cb9e
--- /dev/null
+++ b/e2e/testdata/scenarios/10_consumer_multiple_schemas/expected_comment.txt
@@ -0,0 +1,10 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+
+
diff --git a/e2e/testdata/scenarios/10_consumer_multiple_schemas/scenario.yaml b/e2e/testdata/scenarios/10_consumer_multiple_schemas/scenario.yaml
new file mode 100644
index 0000000..a2666e8
--- /dev/null
+++ b/e2e/testdata/scenarios/10_consumer_multiple_schemas/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Consumers in multiple presentation schemas"
+description: "Consumer changes in multiple schemas should be auto-approved"
+
+expected:
+ decision: Approve
+ reason: "All files passed validation - all changes covered by approved rules"
+ approved: true
+
+ comment_contains:
+ - "✅ **Auto-approved**"
+
+mr_metadata:
+ title: "Grant access to journey and reporting products"
+ author: "testuser"
+ source_branch: "feature/add-multiple-consumers"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/11_metadata_sourcebinding/after/dataproducts/analytics/prod/sourcebinding.yaml b/e2e/testdata/scenarios/11_metadata_sourcebinding/after/dataproducts/analytics/prod/sourcebinding.yaml
new file mode 100644
index 0000000..8d6be6b
--- /dev/null
+++ b/e2e/testdata/scenarios/11_metadata_sourcebinding/after/dataproducts/analytics/prod/sourcebinding.yaml
@@ -0,0 +1,4 @@
+---
+source: new_database
+table: customers_v2
+schema: public
diff --git a/e2e/testdata/scenarios/11_metadata_sourcebinding/before/dataproducts/analytics/prod/sourcebinding.yaml b/e2e/testdata/scenarios/11_metadata_sourcebinding/before/dataproducts/analytics/prod/sourcebinding.yaml
new file mode 100644
index 0000000..268beb6
--- /dev/null
+++ b/e2e/testdata/scenarios/11_metadata_sourcebinding/before/dataproducts/analytics/prod/sourcebinding.yaml
@@ -0,0 +1,3 @@
+---
+source: old_database
+table: customers_v1
diff --git a/e2e/testdata/scenarios/11_metadata_sourcebinding/expected_comment.txt b/e2e/testdata/scenarios/11_metadata_sourcebinding/expected_comment.txt
new file mode 100644
index 0000000..673b692
--- /dev/null
+++ b/e2e/testdata/scenarios/11_metadata_sourcebinding/expected_comment.txt
@@ -0,0 +1,10 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+
+
diff --git a/e2e/testdata/scenarios/11_metadata_sourcebinding/scenario.yaml b/e2e/testdata/scenarios/11_metadata_sourcebinding/scenario.yaml
new file mode 100644
index 0000000..61df220
--- /dev/null
+++ b/e2e/testdata/scenarios/11_metadata_sourcebinding/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Update sourcebinding.yaml"
+description: "Sourcebinding updates should be auto-approved (safe config file)"
+
+expected:
+ decision: Approve
+ reason: "All files passed validation - all changes covered by approved rules"
+ approved: true
+
+ comment_contains:
+ - "✅ **Auto-approved**"
+
+mr_metadata:
+ title: "Update source binding configuration"
+ author: "testuser"
+ source_branch: "feature/update-sourcebinding"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/CHANGELOG.md b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/CHANGELOG.md
new file mode 100644
index 0000000..9bc8c71
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/CHANGELOG.md
@@ -0,0 +1,7 @@
+# Changelog
+
+## v1.1
+- Updated documentation
+
+## v1.0
+- Initial release
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/README.md b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/README.md
new file mode 100644
index 0000000..8f2ef95
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/README.md
@@ -0,0 +1,3 @@
+# Analytics Product
+
+New documentation with updated instructions
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..446a732
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/after/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,5 @@
+---
+name: analytics
+warehouses:
+- type: user
+ size: SMALL
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/CHANGELOG.md b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/CHANGELOG.md
new file mode 100644
index 0000000..43b61f4
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/CHANGELOG.md
@@ -0,0 +1,4 @@
+# Changelog
+
+## v1.0
+- Initial release
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/README.md b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/README.md
new file mode 100644
index 0000000..ea86ee6
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/README.md
@@ -0,0 +1,3 @@
+# Analytics Product
+
+Old documentation
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/prod/product.yaml b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/prod/product.yaml
new file mode 100644
index 0000000..c71d693
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/before/dataproducts/analytics/prod/product.yaml
@@ -0,0 +1,5 @@
+---
+name: analytics
+warehouses:
+- type: user
+ size: MEDIUM
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/expected_comment.txt b/e2e/testdata/scenarios/12_integration_multi_file_approve/expected_comment.txt
new file mode 100644
index 0000000..2371c35
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/expected_comment.txt
@@ -0,0 +1,17 @@
+
+✅ **Auto-approved**
+
+
+📋 Analysis Details (click to expand)
+
+**Files in this MR:**
+• `dataproducts/analytics/CHANGELOG.md` ✅
+• `dataproducts/analytics/README.md` ✅
+• `dataproducts/analytics/prod/product.yaml` ✅
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• ✅ TOC approval check: Changes to existing products in non-critical environments
+• ✅ Warehouse configuration validated: Warehouse size decrease saves costs and is safe to auto-approve
+
+
diff --git a/e2e/testdata/scenarios/12_integration_multi_file_approve/scenario.yaml b/e2e/testdata/scenarios/12_integration_multi_file_approve/scenario.yaml
new file mode 100644
index 0000000..15ed9fb
--- /dev/null
+++ b/e2e/testdata/scenarios/12_integration_multi_file_approve/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Multiple files all auto-approve"
+description: "Multiple files where all should auto-approve (documentation release)"
+
+expected:
+ decision: Approve
+ reason: "All files passed validation - all changes covered by approved rules"
+ approved: true
+
+ comment_contains:
+ - "✅ **Auto-approved**"
+
+mr_metadata:
+ title: "Documentation release with multiple updates"
+ author: "testuser"
+ source_branch: "feature/docs-release"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/analytics/README.md b/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/analytics/README.md
new file mode 100644
index 0000000..4fd0e6e
--- /dev/null
+++ b/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/analytics/README.md
@@ -0,0 +1,3 @@
+# Analytics Product
+
+Updated documentation
diff --git a/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/newproduct/prod/product.yaml b/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/newproduct/prod/product.yaml
new file mode 100644
index 0000000..83d04ba
--- /dev/null
+++ b/e2e/testdata/scenarios/13_integration_multi_file_mixed/after/dataproducts/newproduct/prod/product.yaml
@@ -0,0 +1,6 @@
+---
+name: newproduct
+kind: source-aligned
+warehouses:
+- type: user
+ size: LARGE
diff --git a/e2e/testdata/scenarios/13_integration_multi_file_mixed/before/dataproducts/analytics/README.md b/e2e/testdata/scenarios/13_integration_multi_file_mixed/before/dataproducts/analytics/README.md
new file mode 100644
index 0000000..fcd637f
--- /dev/null
+++ b/e2e/testdata/scenarios/13_integration_multi_file_mixed/before/dataproducts/analytics/README.md
@@ -0,0 +1,3 @@
+# Analytics Product
+
+Documentation
diff --git a/e2e/testdata/scenarios/13_integration_multi_file_mixed/expected_comment.txt b/e2e/testdata/scenarios/13_integration_multi_file_mixed/expected_comment.txt
new file mode 100644
index 0000000..c8fd8a6
--- /dev/null
+++ b/e2e/testdata/scenarios/13_integration_multi_file_mixed/expected_comment.txt
@@ -0,0 +1,15 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+One or more files require manual review
+
+
+📋 Analysis Details (click to expand)
+
+**What was checked:**
+• ✅ Metadata validated: Product metadata changes (name, tags, kind) are safe and don't affect infrastructure
+• 🚫 TOC approval check: Manual review required: New data product being promoted to prod environment requires TOC (Technical Oversight Committee) approval before deployment
+• 🚫 Warehouse configuration validated: Warehouse size increase detected: New user warehouse: LARGE
+
+
diff --git a/e2e/testdata/scenarios/13_integration_multi_file_mixed/scenario.yaml b/e2e/testdata/scenarios/13_integration_multi_file_mixed/scenario.yaml
new file mode 100644
index 0000000..32c310d
--- /dev/null
+++ b/e2e/testdata/scenarios/13_integration_multi_file_mixed/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Multiple files with mixed decisions"
+description: "Multiple files where one requires manual review (conservative MR decision)"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+
+mr_metadata:
+ title: "Documentation update with new production product"
+ author: "testuser"
+ source_branch: "feature/mixed-changes"
+ target_branch: "main"
diff --git a/e2e/testdata/scenarios/14_integration_unknown_filetype/after/scripts/process_data.py b/e2e/testdata/scenarios/14_integration_unknown_filetype/after/scripts/process_data.py
new file mode 100644
index 0000000..ac08ccb
--- /dev/null
+++ b/e2e/testdata/scenarios/14_integration_unknown_filetype/after/scripts/process_data.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python3
+"""Data processing script"""
+
+def process_data():
+ print("Processing data...")
+
+if __name__ == "__main__":
+ process_data()
diff --git a/e2e/testdata/scenarios/14_integration_unknown_filetype/expected_comment.txt b/e2e/testdata/scenarios/14_integration_unknown_filetype/expected_comment.txt
new file mode 100644
index 0000000..624c6c2
--- /dev/null
+++ b/e2e/testdata/scenarios/14_integration_unknown_filetype/expected_comment.txt
@@ -0,0 +1,14 @@
+
+⚠️ **Manual review required**
+
+**Why manual review is needed:**
+
+This MR contains files that Naysayer doesn't know how to validate.
+
+
+Files requiring manual approval (no automated rules configured) (1 file)
+
+**No validation rules configured for Python scripts** (1 file)
+- `scripts/process_data.py`
+
+
diff --git a/e2e/testdata/scenarios/14_integration_unknown_filetype/scenario.yaml b/e2e/testdata/scenarios/14_integration_unknown_filetype/scenario.yaml
new file mode 100644
index 0000000..1239e5b
--- /dev/null
+++ b/e2e/testdata/scenarios/14_integration_unknown_filetype/scenario.yaml
@@ -0,0 +1,16 @@
+name: "Unknown file type Python script"
+description: "Unknown file types (.py) should require manual review (strict policy)"
+
+expected:
+ decision: ManualReview
+ reason: "One or more files require manual review"
+ approved: false
+
+ comment_contains:
+ - "⚠️ **Manual review required**"
+
+mr_metadata:
+ title: "Add Python data processing script"
+ author: "testuser"
+ source_branch: "feature/add-python-script"
+ target_branch: "main"
diff --git a/internal/webhook/messages.go b/internal/webhook/messages.go
index 62a40e7..d3c86a5 100644
--- a/internal/webhook/messages.go
+++ b/internal/webhook/messages.go
@@ -79,6 +79,10 @@ func (mb *MessageBuilder) buildBasicSummary(result *shared.RuleEvaluation) strin
func (mb *MessageBuilder) buildDetailedSummary(result *shared.RuleEvaluation) string {
var summary strings.Builder
+ // Collapsible section for cleaner comments
+ summary.WriteString("\n")
+ summary.WriteString("📋 Analysis Details (click to expand)
\n\n")
+
// File list if 3+ files
if result.TotalFiles >= 3 {
if filesSummary := mb.buildFilesSummary(result); filesSummary != "" {
@@ -92,6 +96,8 @@ func (mb *MessageBuilder) buildDetailedSummary(result *shared.RuleEvaluation) st
summary.WriteString("**What was checked:**\n")
summary.WriteString(mb.buildRulesSummary(result.FileValidations))
+ summary.WriteString("\n ")
+
return summary.String()
}
@@ -145,7 +151,8 @@ func (mb *MessageBuilder) buildRulesSummary(fileValidations map[string]*shared.F
case shared.Approve:
// Only store if not already present
if _, exists := ruleMessages[ruleName]; !exists {
- ruleMessages[ruleName] = fmt.Sprintf("✅ %s", ruleName)
+ explanation := mb.getApprovalExplanation(ruleResult.RuleName, ruleResult.Reason)
+ ruleMessages[ruleName] = fmt.Sprintf("✅ %s%s", ruleName, explanation)
}
case shared.ManualReview:
// Manual review messages always override
@@ -188,10 +195,11 @@ func (mb *MessageBuilder) isNoiseMessage(message string) bool {
// formatRuleName converts internal rule names to user-friendly descriptions
func (mb *MessageBuilder) formatRuleName(ruleName string) string {
friendlyNames := map[string]string{
- "warehouse_rule": "Warehouse configuration validated",
- "service_account_rule": "Service account validated",
- "toc_approval_rule": "TOC approval check",
- "metadata_rule": "Metadata validated",
+ "warehouse_rule": "Warehouse configuration validated",
+ "service_account_rule": "Service account validated",
+ "toc_approval_rule": "TOC approval check",
+ "metadata_rule": "Metadata validated",
+ "dataproduct_consumer_rule": "Consumer access changes validated",
}
if friendly, ok := friendlyNames[ruleName]; ok {
@@ -200,6 +208,27 @@ func (mb *MessageBuilder) formatRuleName(ruleName string) string {
return ruleName
}
+// getApprovalExplanation provides meaningful context for why a rule auto-approved
+func (mb *MessageBuilder) getApprovalExplanation(ruleName string, reason string) string {
+ switch ruleName {
+ case "metadata_rule":
+ return ": Product metadata changes (name, tags, kind) are safe and don't affect infrastructure"
+ case "dataproduct_consumer_rule":
+ return ": Data product owner can grant consumer access without TOC approval"
+ case "warehouse_rule":
+ if strings.Contains(reason, "decrease") {
+ return ": Warehouse size decrease saves costs and is safe to auto-approve"
+ }
+ return ": Warehouse configuration changes validated"
+ case "toc_approval_rule":
+ return ": Changes to existing products in non-critical environments"
+ case "service_account_rule":
+ return ": Service account configuration is valid"
+ default:
+ return ""
+ }
+}
+
// hasUncoveredFiles checks if there are files without validation rules
func (mb *MessageBuilder) hasUncoveredFiles(result *shared.RuleEvaluation) bool {
for _, fileValidation := range result.FileValidations {
@@ -274,7 +303,16 @@ func (mb *MessageBuilder) buildDetailedRulesSummary(fileValidations map[string]*
func (mb *MessageBuilder) buildFilesSummary(result *shared.RuleEvaluation) string {
var summary strings.Builder
- for filePath, fileValidation := range result.FileValidations {
+ // Collect and sort file paths
+ var filePaths []string
+ for filePath := range result.FileValidations {
+ filePaths = append(filePaths, filePath)
+ }
+ sort.Strings(filePaths)
+
+ // Output sorted file list
+ for _, filePath := range filePaths {
+ fileValidation := result.FileValidations[filePath]
summary.WriteString(fmt.Sprintf("• `%s`", filePath))
// Add decision status
@@ -295,7 +333,16 @@ func (mb *MessageBuilder) buildFilesSummary(result *shared.RuleEvaluation) strin
func (mb *MessageBuilder) buildDetailedFilesSummary(result *shared.RuleEvaluation) string {
var summary strings.Builder
- for filePath, fileValidation := range result.FileValidations {
+ // Collect and sort file paths
+ var filePaths []string
+ for filePath := range result.FileValidations {
+ filePaths = append(filePaths, filePath)
+ }
+ sort.Strings(filePaths)
+
+ // Output sorted detailed file list
+ for _, filePath := range filePaths {
+ fileValidation := result.FileValidations[filePath]
summary.WriteString(fmt.Sprintf("**File: `%s`**\n", filePath))
summary.WriteString(fmt.Sprintf("• Total lines: %d\n", fileValidation.TotalLines))
summary.WriteString(fmt.Sprintf("• Decision: %s\n", fileValidation.FileDecision))
@@ -372,26 +419,71 @@ func (mb *MessageBuilder) buildDetailedManualReviewSummary(result *shared.RuleEv
// Enhanced decision with WHY explanation
if mb.hasUncoveredFiles(result) {
- summary.WriteString("**Why manual review is needed:**\n")
- summary.WriteString("This MR contains files that Naysayer doesn't know how to validate\n\n")
+ summary.WriteString("**Why manual review is needed:**\n\n")
+ summary.WriteString("This MR contains files that Naysayer doesn't know how to validate.\n\n")
- summary.WriteString("**Files needing review:**\n")
+ // Group files by reason
+ filesByReason := make(map[string][]string)
for filePath, fileValidation := range result.FileValidations {
if fileValidation.FileDecision == shared.ManualReview && len(fileValidation.RuleResults) == 0 {
- summary.WriteString(fmt.Sprintf("• `%s` - %s\n", filePath, mb.getUncoveredReason(filePath)))
+ reason := mb.getUncoveredReason(filePath)
+ filesByReason[reason] = append(filesByReason[reason], filePath)
}
}
+
+ // Sort reasons alphabetically
+ var reasons []string
+ for reason := range filesByReason {
+ reasons = append(reasons, reason)
+ }
+ sort.Strings(reasons)
+
+ // Use collapsible details for long lists
+ totalFiles := 0
+ for _, files := range filesByReason {
+ totalFiles += len(files)
+ }
+
+ // Always use collapsible section for file lists
+ summary.WriteString("\n")
+ summary.WriteString(fmt.Sprintf("Files requiring manual approval (no automated rules configured) (%d file", totalFiles))
+ if totalFiles != 1 {
+ summary.WriteString("s")
+ }
+ summary.WriteString(")
\n\n")
+
+ for _, reason := range reasons {
+ files := filesByReason[reason]
+ sort.Strings(files)
+ summary.WriteString(fmt.Sprintf("**%s** (%d file", reason, len(files)))
+ if len(files) != 1 {
+ summary.WriteString("s")
+ }
+ summary.WriteString(")\n")
+ for _, filePath := range files {
+ summary.WriteString(fmt.Sprintf("- `%s`\n", filePath))
+ }
+ summary.WriteString("\n")
+ }
+ summary.WriteString(" \n")
} else {
- summary.WriteString(fmt.Sprintf("**Why manual review is needed:**\n%s\n", result.FinalDecision.Reason))
+ summary.WriteString(fmt.Sprintf("**Why manual review is needed:**\n%s\n\n", result.FinalDecision.Reason))
+
+ // Collapsible section for analysis details
+ summary.WriteString("\n")
+ summary.WriteString("📋 Analysis Details (click to expand)
\n\n")
// Show file list if 3+ files
if result.TotalFiles >= 3 {
- summary.WriteString("\n**Files in this MR:**\n")
+ summary.WriteString("**Files in this MR:**\n")
summary.WriteString(mb.buildFilesSummary(result))
+ summary.WriteString("\n")
}
- summary.WriteString("\n**What was checked:**\n")
+ summary.WriteString("**What was checked:**\n")
summary.WriteString(mb.buildRulesSummary(result.FileValidations))
+
+ summary.WriteString("\n ")
}
return summary.String()