Rename TOOL_ALLOW to TOOL_EGRESS_ALLOW
lint / lint (push) Successful in 3m50s
test / unit (push) Successful in 38s
test / integration (push) Successful in 23s
Update Quality Badges / update-badges (push) Successful in 1m38s

The constant and its MCP tool name ("allow" → "egress-allow") were the
only supervise tools without an egress-scoped identifier, despite the
tool being egress-only (routes.yaml payload, COMPONENT_FOR_TOOL maps
it to "egress", always grouped with TOOL_EGRESS_BLOCK). The rename
brings it in line with TOOL_EGRESS_BLOCK and TOOL_EGRESS_TOKEN_ALLOW,
and adds TOOL_EGRESS_ALLOW and TOOL_EGRESS_BLOCK to __all__ (both were
previously absent).
This commit is contained in:
2026-06-25 01:23:10 +00:00
parent d2d50be65a
commit a421d1d688
6 changed files with 20 additions and 18 deletions
+3 -3
View File
@@ -51,7 +51,7 @@ from ..supervise import (
STATUS_MODIFIED, STATUS_MODIFIED,
STATUS_REJECTED, STATUS_REJECTED,
TOOL_CAPABILITY_BLOCK, TOOL_CAPABILITY_BLOCK,
TOOL_ALLOW, TOOL_EGRESS_ALLOW,
TOOL_EGRESS_BLOCK, TOOL_EGRESS_BLOCK,
TOOL_GITLEAKS_ALLOW, TOOL_GITLEAKS_ALLOW,
TOOL_EGRESS_TOKEN_ALLOW, TOOL_EGRESS_TOKEN_ALLOW,
@@ -145,7 +145,7 @@ def _detail_lines(
def _suffix_for_tool(tool: str) -> str: def _suffix_for_tool(tool: str) -> str:
if tool == TOOL_CAPABILITY_BLOCK: if tool == TOOL_CAPABILITY_BLOCK:
return ".dockerfile" return ".dockerfile"
if tool in (TOOL_ALLOW, TOOL_EGRESS_BLOCK): if tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
return ".yaml" return ".yaml"
if tool in (TOOL_GITLEAKS_ALLOW, TOOL_EGRESS_TOKEN_ALLOW): if tool in (TOOL_GITLEAKS_ALLOW, TOOL_EGRESS_TOKEN_ALLOW):
return ".txt" return ".txt"
@@ -177,7 +177,7 @@ def approve(
# diff_before, diff_after = apply_capability_change( # diff_before, diff_after = apply_capability_change(
# qp.proposal.bottle_slug, file_to_apply, # qp.proposal.bottle_slug, file_to_apply,
# ) # )
if qp.proposal.tool in (TOOL_ALLOW, TOOL_EGRESS_BLOCK): if qp.proposal.tool in (TOOL_EGRESS_ALLOW, TOOL_EGRESS_BLOCK):
diff_before, diff_after = apply_routes_change( diff_before, diff_after = apply_routes_change(
qp.proposal.bottle_slug, qp.proposal.bottle_slug,
file_to_apply, file_to_apply,
+5 -3
View File
@@ -50,14 +50,14 @@ SUPERVISE_PORT = 9100
TOOL_CAPABILITY_BLOCK = "capability-block" TOOL_CAPABILITY_BLOCK = "capability-block"
TOOL_EGRESS_BLOCK = "egress-block" TOOL_EGRESS_BLOCK = "egress-block"
TOOL_ALLOW = "allow" TOOL_EGRESS_ALLOW = "egress-allow"
TOOL_GITLEAKS_ALLOW = "gitleaks-allow" TOOL_GITLEAKS_ALLOW = "gitleaks-allow"
# Written directly by the egress addon (not an agent-facing MCP tool) when an # Written directly by the egress addon (not an agent-facing MCP tool) when an
# outbound DLP token block is routed to the operator for override (PRD 0062). # outbound DLP token block is routed to the operator for override (PRD 0062).
TOOL_EGRESS_TOKEN_ALLOW = "egress-token-allow" TOOL_EGRESS_TOKEN_ALLOW = "egress-token-allow"
TOOL_LIST_EGRESS_ROUTES = "list-egress-routes" TOOL_LIST_EGRESS_ROUTES = "list-egress-routes"
TOOLS: tuple[str, ...] = ( TOOLS: tuple[str, ...] = (
TOOL_ALLOW, TOOL_EGRESS_ALLOW,
TOOL_CAPABILITY_BLOCK, TOOL_CAPABILITY_BLOCK,
TOOL_EGRESS_BLOCK, TOOL_EGRESS_BLOCK,
TOOL_GITLEAKS_ALLOW, TOOL_GITLEAKS_ALLOW,
@@ -80,7 +80,7 @@ EGRESS_INTROSPECT_URL = "http://_egress.local/allowlist"
# here — those changes are captured by git history + the rebuild record # here — those changes are captured by git history + the rebuild record
# laid down in PRD 0016. # laid down in PRD 0016.
COMPONENT_FOR_TOOL: dict[str, str] = { COMPONENT_FOR_TOOL: dict[str, str] = {
TOOL_ALLOW: "egress", TOOL_EGRESS_ALLOW: "egress",
TOOL_EGRESS_BLOCK: "egress", TOOL_EGRESS_BLOCK: "egress",
} }
@@ -559,6 +559,8 @@ __all__ = [
"EGRESS_FORWARD_PROXY", "EGRESS_FORWARD_PROXY",
"EGRESS_INTROSPECT_URL", "EGRESS_INTROSPECT_URL",
"TOOL_CAPABILITY_BLOCK", "TOOL_CAPABILITY_BLOCK",
"TOOL_EGRESS_ALLOW",
"TOOL_EGRESS_BLOCK",
"TOOL_GITLEAKS_ALLOW", "TOOL_GITLEAKS_ALLOW",
"TOOL_EGRESS_TOKEN_ALLOW", "TOOL_EGRESS_TOKEN_ALLOW",
"TOOL_LIST_EGRESS_ROUTES", "TOOL_LIST_EGRESS_ROUTES",
+4 -4
View File
@@ -148,7 +148,7 @@ TOOL_DEFINITIONS: list[dict[str, object]] = [
"allowlist. Returns JSON with one entry per allowed host, " "allowlist. Returns JSON with one entry per allowed host, "
"each carrying its matches rules (if any) and whether " "each carrying its matches rules (if any) and whether "
"the proxy injects Authorization for the route. Use this " "the proxy injects Authorization for the route. Use this "
"before composing an `allow` or `egress-block` proposal so " "before composing an `egress-allow` or `egress-block` proposal so "
"the new routes file extends the live one rather than " "the new routes file extends the live one rather than "
"replacing it." "replacing it."
), ),
@@ -159,7 +159,7 @@ TOOL_DEFINITIONS: list[dict[str, object]] = [
}, },
}, },
{ {
"name": _sv.TOOL_ALLOW, "name": _sv.TOOL_EGRESS_ALLOW,
"description": ( "description": (
"Request operator approval to change the bottle's egress " "Request operator approval to change the bottle's egress "
"allowlist. Pass the full proposed routes.yaml content, not " "allowlist. Pass the full proposed routes.yaml content, not "
@@ -276,7 +276,7 @@ TOOL_DEFINITIONS: list[dict[str, object]] = [
# Map each proposal tool to the input field that carries the agent's # Map each proposal tool to the input field that carries the agent's
# payload (stored in Proposal.proposed_file). # payload (stored in Proposal.proposed_file).
PROPOSED_FILE_FIELD: dict[str, str] = { PROPOSED_FILE_FIELD: dict[str, str] = {
_sv.TOOL_ALLOW: "routes_yaml", _sv.TOOL_EGRESS_ALLOW: "routes_yaml",
_sv.TOOL_CAPABILITY_BLOCK: "dockerfile", _sv.TOOL_CAPABILITY_BLOCK: "dockerfile",
_sv.TOOL_EGRESS_BLOCK: "routes_yaml", _sv.TOOL_EGRESS_BLOCK: "routes_yaml",
} }
@@ -295,7 +295,7 @@ def validate_proposed_file(tool: str, content: str) -> None:
# Dockerfiles are too varied to validate syntactically beyond # Dockerfiles are too varied to validate syntactically beyond
# non-empty. The operator reads the diff in the TUI. # non-empty. The operator reads the diff in the TUI.
pass pass
elif tool in (_sv.TOOL_ALLOW, _sv.TOOL_EGRESS_BLOCK): elif tool in (_sv.TOOL_EGRESS_ALLOW, _sv.TOOL_EGRESS_BLOCK):
try: try:
load_routes(content) load_routes(content)
except ValueError as e: except ValueError as e:
+2 -2
View File
@@ -318,7 +318,7 @@ class TestToolConstants(unittest.TestCase):
def test_tools_tuple_matches_individual_constants(self): def test_tools_tuple_matches_individual_constants(self):
self.assertEqual( self.assertEqual(
( (
supervise.TOOL_ALLOW, supervise.TOOL_EGRESS_ALLOW,
TOOL_CAPABILITY_BLOCK, TOOL_CAPABILITY_BLOCK,
supervise.TOOL_EGRESS_BLOCK, supervise.TOOL_EGRESS_BLOCK,
TOOL_GITLEAKS_ALLOW, TOOL_GITLEAKS_ALLOW,
@@ -341,7 +341,7 @@ class TestToolConstants(unittest.TestCase):
def test_component_map_has_egress_entries(self): def test_component_map_has_egress_entries(self):
self.assertEqual( self.assertEqual(
{ {
supervise.TOOL_ALLOW: "egress", supervise.TOOL_EGRESS_ALLOW: "egress",
supervise.TOOL_EGRESS_BLOCK: "egress", supervise.TOOL_EGRESS_BLOCK: "egress",
}, },
supervise.COMPONENT_FOR_TOOL, supervise.COMPONENT_FOR_TOOL,
+1 -1
View File
@@ -33,7 +33,7 @@ FIXED = datetime(2026, 5, 25, 12, 0, 0, tzinfo=timezone.utc)
def _proposal(slug: str = "dev", tool: str = TOOL_CAPABILITY_BLOCK) -> Proposal: def _proposal(slug: str = "dev", tool: str = TOOL_CAPABILITY_BLOCK) -> Proposal:
payloads = { payloads = {
TOOL_CAPABILITY_BLOCK: "FROM python:3.13\n", TOOL_CAPABILITY_BLOCK: "FROM python:3.13\n",
supervise.TOOL_ALLOW: "routes:\n - host: example.com\n", supervise.TOOL_EGRESS_ALLOW: "routes:\n - host: example.com\n",
supervise.TOOL_EGRESS_BLOCK: "routes:\n - host: example.com\n", supervise.TOOL_EGRESS_BLOCK: "routes:\n - host: example.com\n",
TOOL_GITLEAKS_ALLOW: "file: tests/test_fixture.py\nline: 3\n", TOOL_GITLEAKS_ALLOW: "file: tests/test_fixture.py\nline: 3\n",
TOOL_EGRESS_TOKEN_ALLOW: "host: api.example.com\ndetector: token\n", TOOL_EGRESS_TOKEN_ALLOW: "host: api.example.com\ndetector: token\n",
+5 -5
View File
@@ -59,7 +59,7 @@ class TestValidation(unittest.TestCase):
def test_egress_routes_yaml_is_validated(self): def test_egress_routes_yaml_is_validated(self):
validate_proposed_file( validate_proposed_file(
_sv.TOOL_ALLOW, _sv.TOOL_EGRESS_ALLOW,
"routes:\n - host: example.com\n", "routes:\n - host: example.com\n",
) )
@@ -147,7 +147,7 @@ class TestHandleToolsList(unittest.TestCase):
names = [t["name"] for t in result["tools"]] # type: ignore[index] names = [t["name"] for t in result["tools"]] # type: ignore[index]
self.assertEqual( self.assertEqual(
sorted([ sorted([
_sv.TOOL_ALLOW, _sv.TOOL_EGRESS_ALLOW,
_sv.TOOL_CAPABILITY_BLOCK, _sv.TOOL_CAPABILITY_BLOCK,
_sv.TOOL_EGRESS_BLOCK, _sv.TOOL_EGRESS_BLOCK,
_sv.TOOL_LIST_EGRESS_ROUTES, _sv.TOOL_LIST_EGRESS_ROUTES,
@@ -181,7 +181,7 @@ class TestHandleToolsList(unittest.TestCase):
self.assertNotIn("required", schema) # type: ignore[operator] self.assertNotIn("required", schema) # type: ignore[operator]
def test_egress_tools_take_routes_yaml_and_justification(self): def test_egress_tools_take_routes_yaml_and_justification(self):
for tool_name in (_sv.TOOL_ALLOW, _sv.TOOL_EGRESS_BLOCK): for tool_name in (_sv.TOOL_EGRESS_ALLOW, _sv.TOOL_EGRESS_BLOCK):
with self.subTest(tool_name=tool_name): with self.subTest(tool_name=tool_name):
tool = next(t for t in TOOL_DEFINITIONS if t["name"] == tool_name) tool = next(t for t in TOOL_DEFINITIONS if t["name"] == tool_name)
schema = tool["inputSchema"] schema = tool["inputSchema"]
@@ -244,7 +244,7 @@ class TestHandleToolsCall(unittest.TestCase):
try: try:
result = handle_tools_call( result = handle_tools_call(
{ {
"name": _sv.TOOL_ALLOW, "name": _sv.TOOL_EGRESS_ALLOW,
"arguments": { "arguments": {
"routes_yaml": "routes:\n - host: example.com\n", "routes_yaml": "routes:\n - host: example.com\n",
"justification": "need example.com", "justification": "need example.com",
@@ -451,7 +451,7 @@ class TestHttpEndToEnd(unittest.TestCase):
self.assertEqual(1, result["id"]) self.assertEqual(1, result["id"])
names = [t["name"] for t in result["result"]["tools"]] # type: ignore[index] names = [t["name"] for t in result["result"]["tools"]] # type: ignore[index]
self.assertIn(_sv.TOOL_CAPABILITY_BLOCK, names) self.assertIn(_sv.TOOL_CAPABILITY_BLOCK, names)
self.assertIn(_sv.TOOL_ALLOW, names) self.assertIn(_sv.TOOL_EGRESS_ALLOW, names)
self.assertIn(_sv.TOOL_EGRESS_BLOCK, names) self.assertIn(_sv.TOOL_EGRESS_BLOCK, names)
def test_unknown_method_returns_jsonrpc_error(self): def test_unknown_method_returns_jsonrpc_error(self):