Skip to content
Merged
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
1 change: 0 additions & 1 deletion org-tools/governance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Defines hierarchical roles with integer clearance tiers. Approvals from higher l
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
4 changes: 2 additions & 2 deletions org-tools/governance/docs/validation_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ For example, if the hierarchy is defined as:

1. `devops-maintainers` (Level 1)
2. `maintainers` (Level 2)
3. `tech-council` / Domain Tech Councils (`shopping-tech-council`, `food-tech-council`, `lodging-tech-council`, `payments-tech-council`) (Level 3)
3. 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`, any Tech Council (`tech-council`, `shopping-tech-council`, `food-tech-council`, `lodging-tech-council`, `payments-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 (`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
1 change: 0 additions & 1 deletion org-tools/governance/rules/.github-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/conformance-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/js-sdk-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/meeting-minutes-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/python-sdk-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/samples-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/ucp-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
1 change: 0 additions & 1 deletion org-tools/governance/rules/ucp-schema-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ proxy_reviewers:
team_hierarchy:
devops-maintainers: 1
maintainers: 2
tech-council: 3
shopping-tech-council: 3
food-tech-council: 3
lodging-tech-council: 3
Expand Down
128 changes: 106 additions & 22 deletions org-tools/governance/tests/test_governance_config_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,35 @@ def test_parse_valid_dict(self):
"team_hierarchy": {
"devops": 1,
"maintainers": 2,
"tech-council": 3,
"shopping-tech-council": 3,
"food-tech-council": 3,
"lodging-tech-council": 3,
"payments-tech-council": 3,
},
"proxy_reviewers": ["proxy-user"],
"fallback": {"requires": [{"min_team": "maintainers", "min_approvals": 1}]},
"rules": [
{
"name": "Core Rule",
"patterns": ["source/**/*.py"],
"excluded_patterns": ["source/special/**/*.py"],
"requires": [{"team": "tech-council", "min_approvals": 2}],
}
"name": "Shopping Tech Council Rule",
"patterns": ["tc/shopping/**"],
"excluded_patterns": ["tc/shopping/special/**/*.py"],
"requires": [{"team": "shopping-tech-council", "min_approvals": 1}],
},
{
"name": "Food Tech Council Rule",
"patterns": ["tc/food/**"],
"requires": [{"team": "food-tech-council", "min_approvals": 1}],
},
{
"name": "Lodging Tech Council Rule",
"patterns": ["tc/lodging/**"],
"requires": [{"team": "lodging-tech-council", "min_approvals": 1}],
},
{
"name": "Payments Tech Council Rule",
"patterns": ["tc/payments/**"],
"requires": [{"team": "payments-tech-council", "min_approvals": 1}],
},
],
}

Expand All @@ -68,7 +86,10 @@ def test_parse_valid_dict(self):
{
"devops": Team("devops", 1),
"maintainers": Team("maintainers", 2),
"tech-council": Team("tech-council", 3),
"shopping-tech-council": Team("shopping-tech-council", 3),
"food-tech-council": Team("food-tech-council", 3),
"lodging-tech-council": Team("lodging-tech-council", 3),
"payments-tech-council": Team("payments-tech-council", 3),
},
)

Expand All @@ -82,14 +103,47 @@ def test_parse_valid_dict(self):
self.assertIsNone(config.fallback[0].team)

# Assert Rules
self.assertEqual(len(config.rules), 1)
rule = config.rules[0]
self.assertEqual(rule.name, "Core Rule")
self.assertEqual(rule.patterns, ["source/**/*.py"])
self.assertEqual(rule.excluded_patterns, ["source/special/**/*.py"])
self.assertEqual(len(rule.requires_all), 1)
self.assertEqual(rule.requires_all[0].min_approvals, 2)
self.assertEqual(rule.requires_all[0].team, Team("tech-council", 3))
self.assertEqual(len(config.rules), 4)

rule_shopping = config.rules[0]
self.assertEqual(rule_shopping.name, "Shopping Tech Council Rule")
self.assertEqual(rule_shopping.patterns, ["tc/shopping/**"])
self.assertEqual(
rule_shopping.excluded_patterns, ["tc/shopping/special/**/*.py"]
)
self.assertEqual(len(rule_shopping.requires_all), 1)
self.assertEqual(rule_shopping.requires_all[0].min_approvals, 1)
self.assertEqual(
rule_shopping.requires_all[0].team, Team("shopping-tech-council", 3)
)

rule_food = config.rules[1]
self.assertEqual(rule_food.name, "Food Tech Council Rule")
self.assertEqual(rule_food.patterns, ["tc/food/**"])
self.assertEqual(rule_food.excluded_patterns, [])
self.assertEqual(len(rule_food.requires_all), 1)
self.assertEqual(rule_food.requires_all[0].min_approvals, 1)
self.assertEqual(rule_food.requires_all[0].team, Team("food-tech-council", 3))

rule_lodging = config.rules[2]
self.assertEqual(rule_lodging.name, "Lodging Tech Council Rule")
self.assertEqual(rule_lodging.patterns, ["tc/lodging/**"])
self.assertEqual(rule_lodging.excluded_patterns, [])
self.assertEqual(len(rule_lodging.requires_all), 1)
self.assertEqual(rule_lodging.requires_all[0].min_approvals, 1)
self.assertEqual(
rule_lodging.requires_all[0].team, Team("lodging-tech-council", 3)
)

rule_payments = config.rules[3]
self.assertEqual(rule_payments.name, "Payments Tech Council Rule")
self.assertEqual(rule_payments.patterns, ["tc/payments/**"])
self.assertEqual(rule_payments.excluded_patterns, [])
self.assertEqual(len(rule_payments.requires_all), 1)
self.assertEqual(rule_payments.requires_all[0].min_approvals, 1)
self.assertEqual(
rule_payments.requires_all[0].team, Team("payments-tech-council", 3)
)

def test_parse_file_not_found(self):
"""Test parse_file raises FileNotFoundError for missing file."""
Expand Down Expand Up @@ -179,17 +233,35 @@ def test_parse_mixed_case_normalization(self):
"""Test that team names in hierarchy, rules, and fallback are normalized to lowercase."""
yaml_data = {
"team_hierarchy": {
"Tech-Council": 3,
"Shopping-Tech-Council": 3,
"Food-Tech-Council": 3,
"Lodging-Tech-Council": 3,
"Payments-Tech-Council": 3,
"MAINTAINERS": 2,
},
"proxy_reviewers": ["proxy-user"],
"fallback": {"requires": [{"min_team": "MAINTAINERS", "min_approvals": 1}]},
"rules": [
{
"name": "Core Rule",
"patterns": ["source/**/*.py"],
"requires": [{"team": "Tech-Council", "min_approvals": 2}],
}
"name": "Shopping Tech Council Rule",
"patterns": ["tc/shopping/**"],
"requires": [{"team": "Shopping-Tech-Council", "min_approvals": 1}],
},
{
"name": "Food Tech Council Rule",
"patterns": ["tc/food/**"],
"requires": [{"team": "Food-Tech-Council", "min_approvals": 1}],
},
{
"name": "Lodging Tech Council Rule",
"patterns": ["tc/lodging/**"],
"requires": [{"team": "Lodging-Tech-Council", "min_approvals": 1}],
},
{
"name": "Payments Tech Council Rule",
"patterns": ["tc/payments/**"],
"requires": [{"team": "Payments-Tech-Council", "min_approvals": 1}],
},
],
}

Expand All @@ -200,14 +272,26 @@ def test_parse_mixed_case_normalization(self):
self.assertEqual(
config.teams,
{
"tech-council": Team("tech-council", 3),
"shopping-tech-council": Team("shopping-tech-council", 3),
"food-tech-council": Team("food-tech-council", 3),
"lodging-tech-council": Team("lodging-tech-council", 3),
"payments-tech-council": Team("payments-tech-council", 3),
"maintainers": Team("maintainers", 2),
},
)

# Verify fallback and rules resolved team references are lowercased
self.assertEqual(config.fallback[0].min_team.name, "maintainers")
self.assertEqual(config.rules[0].requires_all[0].team.name, "tech-council")
self.assertEqual(
config.rules[0].requires_all[0].team.name, "shopping-tech-council"
)
self.assertEqual(config.rules[1].requires_all[0].team.name, "food-tech-council")
self.assertEqual(
config.rules[2].requires_all[0].team.name, "lodging-tech-council"
)
self.assertEqual(
config.rules[3].requires_all[0].team.name, "payments-tech-council"
)


class TestGovernanceConfigValidator(unittest.TestCase):
Expand Down
18 changes: 9 additions & 9 deletions org-tools/governance/tests/test_pr_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_pull_request_lowercase_normalization(self):
changed_files=["README.md"],
reviews=[review_input],
assigned_user_names=["Dave", "EVE"],
assigned_team_names=["Tech-Council", "ADMINS"],
assigned_team_names=["Shopping-Tech-Council", "ADMINS"],
)

expected = PullRequest(
Expand All @@ -63,39 +63,39 @@ def test_pull_request_lowercase_normalization(self):
changed_files=["README.md"],
reviews=[Review(user="bob", state=ReviewState.APPROVED)],
assigned_user_names=["dave", "eve"],
assigned_team_names=["tech-council", "admins"],
assigned_team_names=["shopping-tech-council", "admins"],
)
self.assertEqual(expected, pr)

def test_team_memberships_lowercase_normalization(self):
"""Test that TeamMemberships keys and member sets are normalized to lowercase."""
memberships = TeamMemberships.create(
members_by_team={
"Tech-Council": {"Alice", "BOB"},
"Shopping-Tech-Council": {"Alice", "BOB"},
"ADMINS": {"charlie", "Dave"},
},
teams={
"tech-council": Team.create("tech-council", 3),
"shopping-tech-council": Team.create("shopping-tech-council", 3),
"admins": Team.create("admins", 2),
},
)
self.assertEqual(
memberships.members_by_team,
{
Team.create("tech-council", 3): {"alice", "bob"},
Team.create("shopping-tech-council", 3): {"alice", "bob"},
Team.create("admins", 2): {"charlie", "dave"},
},
)

def test_user_create_factory(self):
"""Test that User.create resolves teams, level, and normalizes username."""
teams = {
"tech-council": Team.create("tech-council", 3),
"shopping-tech-council": Team.create("shopping-tech-council", 3),
"admins": Team.create("admins", 2),
}
memberships = TeamMemberships.create(
members_by_team={
"tech-council": {"alice", "bob"},
"shopping-tech-council": {"alice", "bob"},
"admins": {"bob", "charlie"},
},
teams=teams,
Expand All @@ -104,13 +104,13 @@ def test_user_create_factory(self):
# User in multiple teams, max level should be the highest (3)
user_bob = User.create("BOB", memberships)
self.assertEqual(user_bob.username, "bob")
self.assertEqual(user_bob.teams, {"tech-council", "admins"})
self.assertEqual(user_bob.teams, {"shopping-tech-council", "admins"})
self.assertEqual(user_bob.level, 3)

# User in one team
user_alice = User.create("Alice", memberships)
self.assertEqual(user_alice.username, "alice")
self.assertEqual(user_alice.teams, {"tech-council"})
self.assertEqual(user_alice.teams, {"shopping-tech-council"})
self.assertEqual(user_alice.level, 3)

# User in no teams
Expand Down
Loading
Loading