diff --git a/shared/egg_restrictions/patterns.py b/shared/egg_restrictions/patterns.py index 80a95f1013..bb0ed47ae9 100644 --- a/shared/egg_restrictions/patterns.py +++ b/shared/egg_restrictions/patterns.py @@ -12,36 +12,13 @@ import posixpath from dataclasses import dataclass, field +# Re-export the canonical AgentRole StrEnum from egg_contracts so the +# gateway sees a single source of truth — adding a role in egg_contracts +# is now automatically visible here, removing the silent-drift failure +# mode from #2066. +from egg_contracts.agent_roles import AgentRole -class AgentRole: - """Agent role identifiers. - - Note: Mirrors egg_contracts.agent_roles.AgentRole to avoid import - complexity in the gateway module. Values must be kept in sync. - """ - - CODER = "coder" - TESTER = "tester" - DOCUMENTER = "documenter" - # Analysis roles - ARCHITECT = "architect" - TASK_PLANNER = "task_planner" - RISK_ANALYST = "risk_analyst" - REFINER = "refiner" - # Review roles - REVIEWER_CODE = "reviewer_code" - REVIEWER_CONTRACT = "reviewer_contract" - REVIEWER_AGENT_DESIGN = "reviewer_agent_design" - REVIEWER_REFINE = "reviewer_refine" - REVIEWER_PLAN = "reviewer_plan" - REVIEWER_SECURITY = "reviewer_security" - REVIEWER_CONCURRENCY = "reviewer_concurrency" - # Utility roles - AUTOFIXER = "autofixer" - CONFLICT_RESOLVER = "conflict_resolver" - # Interface roles - OVERSEER = "overseer" - INSPECTOR = "inspector" +__all__ = ["AGENT_PATTERNS", "AgentFilePattern", "AgentRole"] @dataclass diff --git a/shared/pyproject.toml b/shared/pyproject.toml index ad4ca6b3fc..57d360d075 100644 --- a/shared/pyproject.toml +++ b/shared/pyproject.toml @@ -3,7 +3,7 @@ name = "egg-shared" version = "0.1.0" description = "Shared modules for egg (enrichment, logging, notifications, etc.)" requires-python = ">=3.11" -dependencies = ["pyyaml>=6.0", "anthropic>=0.50,<1.0", "httpx>=0.25.0", "markdownify>=0.13.1"] +dependencies = ["pyyaml>=6.0", "anthropic>=0.50,<1.0", "httpx>=0.25.0", "markdownify>=0.13.1", "egg-contracts"] [build-system] requires = ["setuptools>=61.0"] diff --git a/tests/gateway/test_agent_restrictions.py b/tests/gateway/test_agent_restrictions.py index 58552fed21..781a263d31 100644 --- a/tests/gateway/test_agent_restrictions.py +++ b/tests/gateway/test_agent_restrictions.py @@ -25,39 +25,19 @@ class TestAgentRoleConsistency: - """Verify gateway AgentRole matches shared library AgentRole.""" + """Tripwire: gateway AgentRole must be the canonical egg_contracts enum. - def test_role_values_match_shared_library(self): - """Gateway AgentRole values must match egg_contracts.agent_roles.AgentRole. - - This test prevents subtle bugs from enum value drift between the two modules. - """ - from egg_contracts.agent_roles import AgentRole as SharedAgentRole + Issue #2066: replaced the per-package AgentRole duplicate with a + re-export so a new role added in egg_contracts is automatically + visible to the gateway. The identity assertion below catches anyone + re-introducing a parallel class or enum in egg_restrictions or the + gateway. + """ - # Verify all shared library roles exist in gateway with same values - for shared_role in SharedAgentRole: - assert hasattr(AgentRole, shared_role.name), ( - f"Gateway AgentRole missing role: {shared_role.name}" - ) - gateway_value = getattr(AgentRole, shared_role.name) - assert gateway_value == shared_role.value, ( - f"Role value mismatch for {shared_role.name}: " - f"gateway={gateway_value}, shared={shared_role.value}" - ) - - def test_all_gateway_roles_in_shared_library(self): - """All gateway roles must exist in the shared library.""" + def test_gateway_agent_role_is_canonical(self): from egg_contracts.agent_roles import AgentRole as SharedAgentRole - gateway_roles = { - AgentRole.CODER, - AgentRole.TESTER, - AgentRole.DOCUMENTER, - } - shared_values = {r.value for r in SharedAgentRole} - - for role in gateway_roles: - assert role in shared_values, f"Gateway role '{role}' not found in shared library" + assert AgentRole is SharedAgentRole class TestPathTraversalPrevention: diff --git a/tests/shared/egg_contracts/test_agent_roles.py b/tests/shared/egg_contracts/test_agent_roles.py index 5dfac9c1c8..d9758ec7a4 100644 --- a/tests/shared/egg_contracts/test_agent_roles.py +++ b/tests/shared/egg_contracts/test_agent_roles.py @@ -741,23 +741,19 @@ def test_all_canonical_roles_in_types(self): class TestRoleSyncWithGateway: - """Verify gateway/agent_restrictions.py AgentRole stays in sync.""" + """Tripwire: the gateway must use the canonical AgentRole enum. - def test_all_canonical_roles_in_gateway(self): - """Every canonical AgentRole should have a constant in gateway AgentRole.""" + Issue #2066: gateway/agent_restrictions.py and + egg_restrictions.patterns now re-export this enum rather than + redefining it, eliminating the silent-drift failure mode that + PR #2061 surfaced. The identity check fails if anyone re-adds + a parallel class or enum in either module. + """ + + def test_gateway_agent_role_is_canonical(self): from agent_restrictions import AgentRole as GatewayAgentRole - canonical_values = {r.value for r in AgentRole} - # Gateway AgentRole is a plain class with string constants - gateway_values = { - v - for k, v in vars(GatewayAgentRole).items() - if not k.startswith("_") and isinstance(v, str) - } - missing = canonical_values - gateway_values - assert not missing, ( - f"gateway/agent_restrictions.py AgentRole is missing constants: {missing}" - ) + assert GatewayAgentRole is AgentRole # ---------------------------------------------------------------------------