fix: align auditor channel perms, extend desk gate to tests, drop stale usage-event doc

- permissions: the Auditor is a silent, read-only observer with no say/dm in
  its verb surface, so can_write_channel now returns False for it — matching
  the role's real capabilities instead of granting an unreachable channel
  write (test updated to assert read-only).
- Makefile: make lint and make gate now type-check mypy roboco/ tests/, matching
  make quality / make quality-fast, so the developer-desk gate also catches test
  type errors before submit (tests/ is already mypy-clean).
- docs: CLAUDE.md no longer lists USAGE_UPDATE — only USAGE_SNAPSHOT is published
  to /ws/system.
This commit is contained in:
Renn F
2026-06-15 08:13:53 +02:00
parent ba74eb4fd2
commit 77771c280c
4 changed files with 10 additions and 9 deletions
+2 -2
View File
@@ -472,7 +472,7 @@ The orchestrator exposes WebSocket endpoints under `/ws` (router in
| Endpoint | Purpose |
|----------|---------|
| `/ws/channels/{id}`, `/ws/agents/{id}`, `/ws/sessions/{id}`, `/ws/notifications/{id}` | Per-resource live streams |
| `/ws/system` | Operator/system-wide stream (no per-agent keying) — the rate-limit lifecycle (`RATE_LIMIT_HIT` / `RATE_LIMIT_LIFTED`) and live usage (`USAGE_UPDATE` / `USAGE_SNAPSHOT`, pushed to the usage dashboard) |
| `/ws/system` | Operator/system-wide stream (no per-agent keying) — the rate-limit lifecycle (`RATE_LIMIT_HIT` / `RATE_LIMIT_LIFTED`) and live usage (`USAGE_SNAPSHOT`, pushed to the usage dashboard) |
Server-side events reach these sockets through `roboco/api/websocket_bridge.py`,
which subscribes to the `StreamEventBus` and forwards each event to the matching
@@ -493,7 +493,7 @@ stand up a parallel endpoint or client stack.
`agent_spawn_sessions``daily_usage_rollups` → dashboard). Cost uses
provider-aware pricing in `roboco/billing/pricing.py` (Anthropic priced;
local/Ollama intentionally `$0`). The token sweep also publishes
`USAGE_UPDATE`/`USAGE_SNAPSHOT` to `/ws/system`, so the dashboard's
`USAGE_SNAPSHOT` to `/ws/system`, so the dashboard's
"Token Usage & Cost" panel updates live and falls back to HTTP polling when
the stream is down.
+2 -2
View File
@@ -156,7 +156,7 @@ restart: stop start-example
lint:
@echo 'Formatting w/ Ruff...' ; echo '' ; uv run ruff format .
@echo '' ; echo '' ; echo 'Linting w/ Ruff...' ; echo '' ; uv run ruff check .
@echo '' ; echo '' ; echo 'Type checking w/ Mypy...' ; echo '' ; uv run mypy roboco/
@echo '' ; echo '' ; echo 'Type checking w/ Mypy...' ; echo '' ; uv run mypy roboco/ tests/
@echo '' ; echo '' ; echo 'Finding dead code w/ Vulture...' ; echo '' ; uv run vulture vulture_whitelist.py
# Fix code
@@ -285,7 +285,7 @@ quality-fast:
gate:
@uv run ruff format --check .
@uv run ruff check .
@uv run mypy roboco/
@uv run mypy roboco/ tests/
@uv run xenon --max-absolute B --max-modules A --max-average A roboco/
# Run all analysis tools
+3 -2
View File
@@ -215,9 +215,10 @@ class PermissionService(SingletonService):
if agent.role == AgentRole.CEO:
return True
# Auditor can write but usually doesn't (to maintain cover)
# Auditor is a silent, read-only observer (no say/dm in its verb surface);
# deny channel writes so this layer matches the role's actual capabilities.
if agent.role == AgentRole.AUDITOR:
return True
return False
# Main PM has access to all channels
if agent.role == AgentRole.MAIN_PM:
+3 -3
View File
@@ -65,10 +65,10 @@ def test_ceo_can_write_any_channel(svc: PermissionService) -> None:
assert svc.can_write_channel(ceo, "backend-cell")
def test_auditor_can_write_any_channel(svc: PermissionService) -> None:
"""Auditor write returns True (cover-maintenance is a convention)."""
def test_auditor_cannot_write_any_channel(svc: PermissionService) -> None:
"""Auditor is a silent, read-only observer — it cannot write to channels."""
auditor = _ctx(AgentRole.AUDITOR)
assert svc.can_write_channel(auditor, "backend-cell")
assert not svc.can_write_channel(auditor, "backend-cell")
def test_main_pm_can_write_any_channel(svc: PermissionService) -> None: