Skip to content

fix: Architecture invariant — model centralization (INV-14) + doc-drift (INV-7→#1309, INV-8→#1310 split out) #654

Description

@vybe

Architecture Validation Report (2026-05-05)

Automated validation detected 3 invariant violations and 2 doc-drift items requiring attention.

Invariant Compliance

# Invariant Status Details
1 Three-Layer Backend: Router → Service → DB PASS pipe.execute() calls are Redis pipeline, not SQL. DB layer has no FastAPI imports.
2 DB Layer: Class-per-domain with Mixin Composition PASS All db/*.py files define *Operations classes; all db/agent_settings/*.py define *Mixin classes.
3 Schema in db/schema.py, Migrations in db/migrations.py PASS No CREATE TABLE found outside schema.py/migrations.py.
4 Router Registration Order PASS /context-stats, /autonomy-status, /sync-health, /slots, /permissions-edges all registered before /{agent_name} catch-all.
5 Agent Server Mirrors Backend (Subset) PASS Agent server has snapshot.py, trinity.py, info.py, activity.py, dashboard.py which are legitimate agent-internal-only routers (not required to have backend mirrors).
6 Frontend: Store = Domain, View = Page PASS No views import api directly or make API calls via stores.
7 Single API Client (api.js) FAIL 20+ files bypass api.js singleton: stores (agents.js, auth.js, operatorQueue.js, monitoring.js, network.js, etc.) import raw axios with manual auth headers; views (AgentDetail.vue, ApiKeys.vue) use raw fetch() with manual Authorization headers. This means token refresh logic in api.js interceptors is skipped.
8 Auth Pattern: Depends(get_current_user) + AuthorizedAgent FAIL 51 inline raise HTTPException(status_code=403) calls across 19 router files (threshold: 5). internal.py correctly has no get_current_user. High-volume sites: agent_config.py (6 sites), avatar.py (5 sites), agent_files.py (3 sites), chat.py (2 sites), plus 14 more files. Authorization logic is duplicated across routers instead of being centralized in AuthorizedAgent/dependency factories.
9 Channel Adapter ABC PASS adapters/base.py defines ChannelAdapter ABC. SlackAdapter, TelegramAdapter, and WhatsappAdapter all inherit from it.
10 WebSocket Events for Real-Time WARN operatorQueue.js uses setInterval polling the /api/operator-queue REST endpoint every N seconds. HostTelemetry.vue polls /api/telemetry/host every 5000ms with raw fetch. websocket.js exists. These are minor exceptions (operator queue and host telemetry are not agent-state push events) but are noted.
11 Docker as Source of Truth PASS No module-level container state dicts found. docker_service.py exists.
12 Credentials: File Injection, Never Stored in DB PASS No credential values in schema.py tables.
13 MCP Server = Third Surface in Sync PASS 16 tool modules present. tags.ts is registered in server.ts but not exported from tools/index.ts (stale re-export index — minor).
14 Pydantic Models Centralized in models.py FAIL 71 Pydantic BaseModel class definitions found scattered across router files (audit_log.py, fan_out.py, git.py, internal.py, avatar.py, event_subscriptions.py, image_generation.py, logs.py, messages.py, and more). These should live in models.py.
15 API URL Nesting Convention PASS Agent-scoped resources correctly nest under /api/agents/{name}/.

Result: 11/15 PASS, 3/15 FAIL, 1/15 WARN

Violations

INV-8 (P1): Auth Sprawl — Inline Authorization in 19 Router Files (51 sites)

51 raise HTTPException(status_code=403) calls spread across 19 router files including agent_config.py, avatar.py, chat.py, logs.py, sharing.py, slack.py, operator_queue.py, settings.py, system_agent.py, system_views.py, whatsapp.py, and more.

Fix: Consolidate into AuthorizedAgent, OwnedAgentByName, or require_role() dependencies in dependencies.py. Each new inline auth check increases the attack surface for auth bypass and makes auditing harder.

INV-7 (P1): API Client Fragmentation — Raw axios and fetch Bypass api.js

20+ frontend files import axios directly or use fetch() instead of the api.js Axios singleton that handles token refresh and auth interceptors:

  • Stores: agents.js, auth.js, operatorQueue.js, monitoring.js, observability.js, network.js, settings.js, notifications.js, systemViews.js
  • Components: ChatPanel.vue, PlaybooksPanel.vue, PublicLinksPanel.vue, SkillsPanel.vue, CreateAgentModal.vue, NavBar.vue, HostTelemetry.vue (raw fetch)
  • Views: AgentDetail.vue (raw fetch for autonomy, read-only, rename endpoints), ApiKeys.vue (raw fetch for all MCP key operations)

Fix: Migrate all API calls to import and use the api singleton from src/api.js.

INV-14 (P1): Pydantic Model Sprawl — 71 Models Outside models.py

71 BaseModel class definitions found in router files. Significant violators: fan_out.py (4 models), git.py (5 models), internal.py (5 models), audit_log.py (4 models), logs.py (2 models).

Fix: Move all request/response models to src/backend/models.py and import from there.

Doc Drift — Suggested architecture.md Edits

Item Doc Claims Actual Action
D1: Service module count "37 service modules" 47 modules Update to 47; document 10 undocumented services: fan_out_service, fleet_audit_service, gemini_voice, github_pat_propagation_service, platform_audit_service, platform_prompt_service, pre_check_service, subscription_auto_switch, upload_service, validation_service, ws_ticket_service, sync_waiter, telegram_media
D1: Router module count "53 router modules" 52 modules Update to 52
D1: MCP tool count "62 tools" (CLAUDE.md) 73 tools Update to 73; chat.ts has 4 tools (doc says 3 — fan_out is undocumented)
D1: agents.py line count "642 lines" 799 lines Update to ~800 lines
D1: main.py reference (implicit) 973 lines No claim to update, but doc says "300+ endpoints across 40+ routers" — routers is now 52
D2: audit_retention_service Listed in Background Services table but missing from Services module list Exists as active service Add to Services section
D2: sync_health_service Listed in Background Services table but missing from Services module list Exists as active service Add to Services section

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions