Automated Architecture Validation Report
Date: 2026-04-27
Result: 6 critical violations requiring attention (10/16 PASS, 6/16 FAIL)
Critical Findings (P0-P1)
#1 Three-Layer Backend: Router → Service → DB (FAIL)
SQL executed directly in router files — violates the Router → Service → DB separation:
routers/voice.py:176,304 — direct cursor.execute() with UPDATE/SELECT SQL
routers/git.py:298 — direct db.execute_query("DELETE FROM agent_git_config ...")
routers/processes.py:781,819,865 — direct cursor.execute() with CREATE TABLE, INSERT, DELETE
routers/triggers.py:254 — direct cursor.execute() with SELECT
#3 Schema in db/schema.py, Migrations in db/migrations.py (FAIL)
CREATE TABLE statements found outside the canonical schema/migration files:
routers/processes.py:781 — CREATE TABLE IF NOT EXISTS process_schedules
services/process_engine/services/templates.py:117 — CREATE TABLE IF NOT EXISTS process_templates
services/process_engine/services/alerts.py:163,178 — CREATE TABLE IF NOT EXISTS cost_thresholds, cost_alerts
#13 Credentials: File Injection, Never Stored in DB (FAIL — CRITICAL)
Slack bot tokens stored in plaintext in SQLite — violates the no-credentials-in-DB rule:
db/schema.py:537 — slack_link_connections table has slack_bot_token TEXT NOT NULL
db/slack.py:38-42 — INSERT writes the plaintext bot token directly
db/slack.py:172 — SELECT returns plaintext token in row results
Correct pattern (used by subscriptions and nevermined): AES-256-GCM encryption via CredentialEncryptionService.
#8 Auth Pattern: Missing Protection (FAIL — Partial)
routers/docs.py — endpoints have no auth dependency, publicly accessible
routers/executions.py:19-27, routers/processes.py, routers/process_templates.py — import directly from process_engine.domain (bypassing service layer)
Non-Critical Findings (P2-P3 — informational)
#6 Frontend Store = Domain, View = Page (FAIL)
ProcessEditor.vue, ProcessList.vue, ProcessWizard.vue — 8 direct api.get()/api.post() calls; views import API client directly instead of going through stores.
#10 Process Engine DDD Isolation (FAIL)
routers/executions.py, routers/processes.py, routers/process_templates.py — import directly from process_engine.domain instead of going through services.
#11 WebSocket Events for Real-Time (FAIL)
views/ExecutionDetail.vue — setInterval polling instead of WebSocket for execution status
stores/notifications.js:226 — setInterval polling for notification count
stores/monitoring.js — setInterval polling for fleet status
#15 Pydantic Models Centralized in models.py (FAIL)
- 10+ router files define inline
BaseModel subclasses (e.g., event_subscriptions.py:359, agent_files.py)
#16 API URL Nesting Convention (FAIL)
event_subscriptions.py:295,334,365 — agent-scoped resources at /api/event-subscriptions/{id}, /api/events (not nested under /api/agents/{name}/)
notifications.py:70,120,177,193 — agent-scoped resources at /api/notifications/...
Recommended Actions (Priority Order)
- [P0] Encrypt Slack bot tokens — Apply
CredentialEncryptionService.encrypt() before writing to slack_link_connections.slack_bot_token; add migration to re-encrypt existing rows.
- [P1] Move ad-hoc
CREATE TABLE to schema.py/migrations.py — Tables: process_schedules, process_templates, cost_thresholds, cost_alerts.
- [P1] Move SQL out of routers —
voice.py, git.py, processes.py, triggers.py need service/db layer extraction.
- [P2] Add auth to
docs.py or document why it should remain public.
- [P2] Move process engine domain imports behind service layer in
routers/executions.py, routers/processes.py, routers/process_templates.py.
- [P3] Migrate polling to WebSocket in ExecutionDetail, notifications, monitoring.
- [P3] Move inline Pydantic models from router files to
models.py.
- [P3] Fix URL nesting for event subscriptions and notifications.
Generated by scheduled /validate-architecture run on 2026-04-27
Automated Architecture Validation Report
Date: 2026-04-27
Result: 6 critical violations requiring attention (10/16 PASS, 6/16 FAIL)
Critical Findings (P0-P1)
#1 Three-Layer Backend: Router → Service → DB (FAIL)
SQL executed directly in router files — violates the Router → Service → DB separation:
routers/voice.py:176,304— directcursor.execute()with UPDATE/SELECT SQLrouters/git.py:298— directdb.execute_query("DELETE FROM agent_git_config ...")routers/processes.py:781,819,865— directcursor.execute()with CREATE TABLE, INSERT, DELETErouters/triggers.py:254— directcursor.execute()with SELECT#3 Schema in
db/schema.py, Migrations indb/migrations.py(FAIL)CREATE TABLEstatements found outside the canonical schema/migration files:routers/processes.py:781—CREATE TABLE IF NOT EXISTS process_schedulesservices/process_engine/services/templates.py:117—CREATE TABLE IF NOT EXISTS process_templatesservices/process_engine/services/alerts.py:163,178—CREATE TABLE IF NOT EXISTS cost_thresholds,cost_alerts#13 Credentials: File Injection, Never Stored in DB (FAIL — CRITICAL)
Slack bot tokens stored in plaintext in SQLite — violates the no-credentials-in-DB rule:
db/schema.py:537—slack_link_connectionstable hasslack_bot_token TEXT NOT NULLdb/slack.py:38-42— INSERT writes the plaintext bot token directlydb/slack.py:172— SELECT returns plaintext token in row resultsCorrect pattern (used by subscriptions and nevermined): AES-256-GCM encryption via
CredentialEncryptionService.#8 Auth Pattern: Missing Protection (FAIL — Partial)
routers/docs.py— endpoints have no auth dependency, publicly accessiblerouters/executions.py:19-27,routers/processes.py,routers/process_templates.py— import directly fromprocess_engine.domain(bypassing service layer)Non-Critical Findings (P2-P3 — informational)
#6 Frontend Store = Domain, View = Page (FAIL)
ProcessEditor.vue,ProcessList.vue,ProcessWizard.vue— 8 directapi.get()/api.post()calls; views import API client directly instead of going through stores.#10 Process Engine DDD Isolation (FAIL)
routers/executions.py,routers/processes.py,routers/process_templates.py— import directly fromprocess_engine.domaininstead of going through services.#11 WebSocket Events for Real-Time (FAIL)
views/ExecutionDetail.vue—setIntervalpolling instead of WebSocket for execution statusstores/notifications.js:226—setIntervalpolling for notification countstores/monitoring.js—setIntervalpolling for fleet status#15 Pydantic Models Centralized in
models.py(FAIL)BaseModelsubclasses (e.g.,event_subscriptions.py:359,agent_files.py)#16 API URL Nesting Convention (FAIL)
event_subscriptions.py:295,334,365— agent-scoped resources at/api/event-subscriptions/{id},/api/events(not nested under/api/agents/{name}/)notifications.py:70,120,177,193— agent-scoped resources at/api/notifications/...Recommended Actions (Priority Order)
CredentialEncryptionService.encrypt()before writing toslack_link_connections.slack_bot_token; add migration to re-encrypt existing rows.CREATE TABLEtoschema.py/migrations.py— Tables:process_schedules,process_templates,cost_thresholds,cost_alerts.voice.py,git.py,processes.py,triggers.pyneed service/db layer extraction.docs.pyor document why it should remain public.routers/executions.py,routers/processes.py,routers/process_templates.py.models.py.Generated by scheduled /validate-architecture run on 2026-04-27