Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion org-tools/governance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
```

Expand Down Expand Up @@ -79,7 +83,7 @@ rules:
patterns:
- "source/**"
requires:
- min_team: "tech-council"
- min_team: "shopping-tech-council"
min_approvals: 1
```

Expand Down
10 changes: 5 additions & 5 deletions org-tools/governance/docs/validation_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ This section aggregates and deduplicates all requirements across all changed fil
If your PR modifies multiple files, the validator doesn't just add up all the approvals blindly. Instead, it uses a **Venn Diagram model**:

- If multiple modified files require approval from the **same team or hierarchical level**, the requirements are merged.
- The validator keeps the **maximum** number of approvals (`min_approvals`) required by any single file for that target. E.g., if File A requires 1 approval from `tech-council` and File B requires 2 approvals from `tech-council`, the global requirement is merged to require **2 approvals** from `tech-council` (not 3).
- The validator keeps the **maximum** number of approvals (`min_approvals`) required by any single file for that target. E.g., if File A requires 1 approval from `shopping-tech-council` and File B requires 2 approvals from `shopping-tech-council`, the global requirement is merged to require **2 approvals** from `shopping-tech-council` (not 3).

### Understanding Requirement Items

Expand All @@ -69,7 +69,7 @@ Each requirement in the summary is formatted with detailed progress:

#### Key Fields:

1. **Requirement Line**: Describes the rule (e.g., `* **2 approvals** from team 'tech-council'` or `* **1 approval** from team 'tech-council' or higher in the UCP governance hierarchy`).
1. **Requirement Line**: Describes the rule (e.g., `* **2 approvals** from team 'shopping-tech-council'` or `* **1 approval** from team 'shopping-tech-council' or higher in the UCP governance hierarchy`).
2. **Met Line**:
- **🟢 Yes**: The requirement is fully satisfied.
- **🔴 No**: The requirement is not yet satisfied.
Expand Down Expand Up @@ -112,11 +112,11 @@ For example, if the hierarchy is defined as:

1. `devops-maintainers` (Level 1)
2. `maintainers` (Level 2)
3. `tech-council` (Level 3)
3. `tech-council` / Domain Tech Councils (`shopping-tech-council`, `food-tech-council`, `lodging-tech-council`, `payments-tech-council`) (Level 3)
4. `governance-council` (Level 4)

- A requirement for **`team 'maintainers' or higher in the UCP governance hierarchy`** (Level 2+) can be satisfied by approvals from members of `maintainers`, `tech-council`, or `governance-council`.
- A requirement for **`team 'tech-council' or higher in the UCP governance hierarchy`** (Level 3+) can be satisfied by approvals from members of `tech-council` or `governance-council`.
- A requirement for **`team 'maintainers' or higher in the UCP governance hierarchy`** (Level 2+) can be satisfied by approvals from members of `maintainers`, any Tech Council (`tech-council`, `shopping-tech-council`, `food-tech-council`, `lodging-tech-council`, `payments-tech-council`), or `governance-council`.
- A requirement for **`team 'shopping-tech-council' or higher in the UCP governance hierarchy`** (Level 3+) can be satisfied by approvals from members of any Tech Council or `governance-council`.
- A requirement for an exact team (e.g., **`team 'devops-maintainers'`**) must be approved by an explicit member of that team, regardless of hierarchy.

---
Expand Down
4 changes: 4 additions & 0 deletions org-tools/governance/rules/.github-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
6 changes: 5 additions & 1 deletion org-tools/governance/rules/conformance-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ rules:
- "shopping-agent-test.json"
- "test_data/**"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 2
Expand All @@ -48,4 +48,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
6 changes: 5 additions & 1 deletion org-tools/governance/rules/js-sdk-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ rules:
- "tsconfig.json"
- "tsconfig.types.json"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 1
Expand All @@ -55,4 +55,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
43 changes: 36 additions & 7 deletions org-tools/governance/rules/meeting-minutes-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,38 @@ rules:
min_approvals: 1

# ---------------------------------------------------------
# Technical Council Minutes
# Technical Council Minutes - Domain Specific
# ---------------------------------------------------------
- name: "tech_council_minutes"
- name: "shopping_tech_council_minutes"
patterns:
- "tc/**"
- "tc/shopping/**"
requires:
# Grants access to tech-council (L3) and governance-council (L4)
- min_team: "tech-council"
# Grants access to shopping-tech-council (L3) and governance-council (L4)
- min_team: "shopping-tech-council"
min_approvals: 1

- name: "food_tech_council_minutes"
patterns:
- "tc/food/**"
requires:
# Grants access to food-tech-council (L3) and governance-council (L4)
- min_team: "food-tech-council"
min_approvals: 1

- name: "lodging_tech_council_minutes"
patterns:
- "tc/lodging/**"
requires:
# Grants access to lodging-tech-council (L3) and governance-council (L4)
- min_team: "lodging-tech-council"
min_approvals: 1

- name: "payments_tech_council_minutes"
patterns:
- "tc/payments/**"
requires:
# Grants access to payments-tech-council (L3) and governance-council (L4)
- min_team: "payments-tech-council"
min_approvals: 1

# ---------------------------------------------------------
Expand All @@ -36,7 +60,7 @@ rules:
min_approvals: 1

# ---------------------------------------------------------
# Infrastructure, Tooling & General Files
# Infrastructure, Tooling, General Files & Shared Templates
# ---------------------------------------------------------
- name: "infra_and_tooling"
patterns:
Expand All @@ -47,8 +71,9 @@ rules:
- "README.md"
- "CONTRIBUTING.md"
- "SECURITY.md"
- "tc/*.md"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 1
Expand All @@ -67,4 +92,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
6 changes: 5 additions & 1 deletion org-tools/governance/rules/python-sdk-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ rules:
- "tests/**"
- "**/*.py"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 1
Expand All @@ -51,4 +51,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
6 changes: 5 additions & 1 deletion org-tools/governance/rules/samples-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ rules:
- "a2a/**"
- "rest/**"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 2
Expand All @@ -49,4 +49,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
8 changes: 6 additions & 2 deletions org-tools/governance/rules/ucp-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ rules:
patterns:
- "docs/specification/**"
requires:
# Grants access to maintainers (L2), tech-council (L3) and
# Grants access to maintainers (L2), tech councils (L3) and
# governance-council (L4)
- min_team: "maintainers"
min_approvals: 2
Expand Down Expand Up @@ -69,7 +69,7 @@ rules:
- "docs/specification/**" # Covered by Documentation Specification
- ".github/CODEOWNERS"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 2
Expand All @@ -88,4 +88,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
6 changes: 5 additions & 1 deletion org-tools/governance/rules/ucp-schema-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ rules:
- "src/**"
- "tests/**"
requires:
# Grants access to devops (L1), maintainers (L2), tech-council (L3),
# Grants access to devops (L1), maintainers (L2), tech councils (L3),
# and governance-council (L4)
- min_team: "devops-maintainers"
min_approvals: 2
Expand All @@ -50,4 +50,8 @@ team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
payments-tech-council: 3
governance-council: 4
13 changes: 10 additions & 3 deletions org-tools/governance/scripts/pr_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,16 @@ def fetch_team_memberships(
members = {m.login for m in team.get_members()}
members_by_team[team_slug] = members
except GithubException as e:
raise RuntimeError(
f"Could not fetch members for team '{team_slug}': {e}"
) from e
if e.status == 404:
print(
f"⚠️ WARNING: Team '{team_slug}' not found in organization '{org_name}'. Treating as empty.",
file=sys.stderr,
)
members_by_team[team_slug] = set()
else:
raise RuntimeError(
f"Could not fetch members for team '{team_slug}': {e}"
) from e
return TeamMemberships.create(
members_by_team=members_by_team, teams=config.teams
)
Expand Down
29 changes: 27 additions & 2 deletions org-tools/governance/tests/test_pr_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1008,13 +1008,13 @@ def test_fetch_org_fails(self):
self.assertIn("Failed to fetch organization 'my-org'", str(ctx.exception))

def test_fetch_team_fails(self):
"""Test fetch_team_memberships raises RuntimeError when a team fetch fails."""
"""Test fetch_team_memberships raises RuntimeError when a team fetch fails with non-404."""
mock_github = MagicMock()
mock_org = MagicMock()
mock_github.get_organization.return_value = mock_org

mock_org.get_team_by_slug.side_effect = MockGithubException(
status=404, data={"message": "Team Not Found"}
status=500, data={"message": "Internal Server Error"}
)

config = GovernanceConfig(
Expand All @@ -1030,6 +1030,31 @@ def test_fetch_team_fails(self):

self.assertIn("Could not fetch members for team 'devops'", str(ctx.exception))

def test_fetch_team_not_found_handled(self):
"""Test fetch_team_memberships handles 404 by treating team as empty with warning."""
mock_github = MagicMock()
mock_org = MagicMock()
mock_github.get_organization.return_value = mock_org

mock_org.get_team_by_slug.side_effect = MockGithubException(
status=404, data={"message": "Team Not Found"}
)

config = GovernanceConfig(
teams={"shopping-tech-council": Team("shopping-tech-council", 3)},
rules=[],
fallback=[],
proxy_reviewers=set(),
)

github_client = GitHubClient(mock_github)
with patch("sys.stderr"):
memberships = github_client.fetch_team_memberships("my-org", config)
self.assertEqual(
memberships.members_by_team,
{Team("shopping-tech-council", 3): set()},
)


class TestPRValidatorMain(unittest.TestCase):
"""Tests for the main function of pr_validator."""
Expand Down
Loading