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

Labels

automatedAutomatically generated by scheduled taskscomplexity-lowComplexity: low (board points 1-3)priority-p2Importantstatus-in-devMerged to dev, awaiting release cut to maintheme-reliabilityTheme: Reliabilitytype-bugBug fix

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