Context
Surfaced during sprint resolution of #479 (closed as superseded by #430). The stale automated report in #479 hid three real, ongoing architectural drift patterns. Filing them here so they're tracked separately from the false-positives.
This issue is a tracking + scoping ticket — actual fixes will land in follow-up PRs.
Findings (verified 2026-04-26 against commit 81a78f4)
1. Invariant #8 — Inline authorization sprawl (P1, security-adjacent)
The /validate-architecture skill classifies inline-auth sprawl as P0-P1 critical because it scatters security logic across 20+ files instead of in Depends() dependencies.
Evidence:
grep -rE "db\.can_user_|db\.is_system_agent|current_user\.username !=|raise HTTPException\(status_code=403" src/backend/routers/
Returns 85 matches across 20 files. Threshold is 5. Worst offenders:
| File |
Matches |
routers/slack.py |
14 |
routers/agent_config.py |
13 |
routers/subscriptions.py, routers/notifications.py |
7 each |
routers/avatar.py, routers/agents.py, routers/system_views.py, routers/nevermined.py |
6 each |
| (others) |
1-4 each |
Fix shape: Extract permission patterns into Depends() dependencies in dependencies.py. Replace the 85 inline sites in waves (one router per PR). Reuse the existing AuthorizedAgent, OwnedAgentByName patterns.
2. Invariant #14 — Pydantic models scattered across routers (P2, decaying)
Models should live in models.py, not in routers.
Evidence:
grep -rE "^class \w+\((BaseModel|.*Model)\)" src/backend/routers/
Returns 71 BaseModel classes across 25 router files (was 63/20 in #479's 2026-04-24 run — drift is growing, not stable). Worst offenders:
| File |
Models |
routers/settings.py |
9 |
routers/telegram.py |
7 |
routers/git.py |
6 |
routers/voice.py, routers/audit_log.py, routers/sharing.py, routers/messages.py |
4 each |
Fix shape: Phased migration. Move route-local request/response models into models.py grouped by router (e.g., models.py gets class VoiceStartRequest(...) etc.). Update router imports. ~25 PRs of 2-5 models each.
Target: ≤30 BaseModels in routers within 2 cycles.
3. Invariants #6 / #7 — Raw fetch() and api.* calls in views (P2, decaying)
Views should call Pinia stores, not API directly.
Evidence:
grep -cE "fetch\(|api\.(get|post|put|delete)\(" src/frontend/src/views/*.vue
Returns 14 matches across 5 view files:
| File |
Matches |
views/ApiKeys.vue |
6 |
views/AgentDetail.vue |
3 |
views/FileManager.vue |
3 |
views/PublicChat.vue |
1 |
views/ExecutionDetail.vue |
1 |
Fix shape: For each view, extract direct API calls into the matching Pinia store (stores/agents.js, stores/auth.js, etc.). Add store actions, switch view to use them.
Why filed as priority-p1
Invariant #8 is classified P0-P1 by the validator skill itself (security logic scattered = scattered security). Bundling #14 + #6/#7 (P2) into the same tracking issue is a scope choice; the fixes can ship in any order.
Acceptance criteria
References
Context
Surfaced during sprint resolution of #479 (closed as superseded by #430). The stale automated report in #479 hid three real, ongoing architectural drift patterns. Filing them here so they're tracked separately from the false-positives.
This issue is a tracking + scoping ticket — actual fixes will land in follow-up PRs.
Findings (verified 2026-04-26 against commit
81a78f4)1. Invariant #8 — Inline authorization sprawl (P1, security-adjacent)
The
/validate-architectureskill classifies inline-auth sprawl as P0-P1 critical because it scatters security logic across 20+ files instead of inDepends()dependencies.Evidence:
Returns 85 matches across 20 files. Threshold is 5. Worst offenders:
routers/slack.pyrouters/agent_config.pyrouters/subscriptions.py,routers/notifications.pyrouters/avatar.py,routers/agents.py,routers/system_views.py,routers/nevermined.pyFix shape: Extract permission patterns into
Depends()dependencies independencies.py. Replace the 85 inline sites in waves (one router per PR). Reuse the existingAuthorizedAgent,OwnedAgentByNamepatterns.2. Invariant #14 — Pydantic models scattered across routers (P2, decaying)
Models should live in
models.py, not in routers.Evidence:
Returns 71 BaseModel classes across 25 router files (was 63/20 in #479's 2026-04-24 run — drift is growing, not stable). Worst offenders:
routers/settings.pyrouters/telegram.pyrouters/git.pyrouters/voice.py,routers/audit_log.py,routers/sharing.py,routers/messages.pyFix shape: Phased migration. Move route-local request/response models into
models.pygrouped by router (e.g.,models.pygetsclass VoiceStartRequest(...)etc.). Update router imports. ~25 PRs of 2-5 models each.Target: ≤30 BaseModels in routers within 2 cycles.
3. Invariants #6 / #7 — Raw
fetch()andapi.*calls in views (P2, decaying)Views should call Pinia stores, not API directly.
Evidence:
Returns 14 matches across 5 view files:
views/ApiKeys.vueviews/AgentDetail.vueviews/FileManager.vueviews/PublicChat.vueviews/ExecutionDetail.vueFix shape: For each view, extract direct API calls into the matching Pinia store (
stores/agents.js,stores/auth.js, etc.). Add store actions, switch view to use them.Why filed as
priority-p1Invariant #8 is classified P0-P1 by the validator skill itself (security logic scattered = scattered security). Bundling #14 + #6/#7 (P2) into the same tracking issue is a scope choice; the fixes can ship in any order.
Acceptance criteria
src/frontend/src/views/*.vue(Invariant Feature/process engine #6/security: Fix token logging and add HTML reports to gitignore #7 passes)References
/validate-architectureskill correctness fix (prevents future false-positives like Architecture Validation: 4 violations found (2 critical) #479)