en un clic
validate-architecture
// Detect drift between architecture.md and the actual code. Validates 15 architectural invariants and flags stale doc claims with suggested edits.
// Detect drift between architecture.md and the actual code. Validates 15 architectural invariants and flags stale doc claims with suggested edits.
| name | validate-architecture |
| description | Detect drift between architecture.md and the actual code. Validates 15 architectural invariants and flags stale doc claims with suggested edits. |
| allowed-tools | ["Read","Grep","Glob","Bash","Agent"] |
| user-invocable | true |
Check the codebase against the 15 Architectural Invariants in @docs/memory/architecture.md, and detect drift between the doc's quantitative and scope claims and the actual code. Output: invariant violations plus suggested architecture.md edits. No files are modified — read-only analysis.
The architecture doc is a living artifact. When counts or scope claims drift, this skill proposes a concrete edit (architecture.md:L<N> — update "X" → "Y") rather than just marking FAIL.
Read the "Architectural Invariants" section from docs/memory/architecture.md to get the current list. Also parse its "Component Details", "Backend", and per-router sections for quantitative claims (line counts, router counts, tool counts, endpoint counts) — these feed the drift checks in Step 2b.
Run the checks below. For each invariant, record PASS or FAIL with evidence.
1. Three-Layer Backend: Router → Service → DB
routers/*.py for raw SQL (execute(, cursor, SELECT, INSERT, UPDATE, DELETE) — routers must not contain SQLrouters/*.py for business logic patterns (complex conditionals, loops over data) beyond simple request/response handlingdb/*.py for HTTP-specific imports (fastapi, Request, Response) — db layer must not know about HTTP2. DB Layer: Class-per-domain with Mixin Composition
src/backend/db/*.py and verify each defines an *Operations classsrc/backend/db/agent_settings/*.py and verify each defines a *Mixin classAgentOperations in db/agents.py uses mixin inheritance3. Schema in db/schema.py, Migrations in db/migrations.py
src/backend/ files (excluding schema.py and migrations.py) for CREATE TABLE — should find nonedb/schema.py and db/migrations.py both exist4. Router Registration Order
src/backend/main.py and find the include_router blockcontext-stats, autonomy-status) are registered before the main agents router with /{name} params5. Agent Server Mirrors Backend (Subset)
docker/base-image/agent_server/routers/*.py and list themsrc/backend/routers/6. Frontend: Store = Domain, View = Page
src/frontend/src/views/*.vue for direct api.get(, api.post(, api.put(, api.delete( calls — views should go through storessrc/frontend/src/views/*.vue for import api or import { api — views should not import the API client directly7. Single API Client (api.js)
src/frontend/src/ for new axios or axios.create — should only be in api.jssrc/frontend/src/ for raw fetch( calls — should find none (except in non-API contexts like file downloads)8. Auth Pattern: Depends(get_current_user) + AuthorizedAgent
src/backend/routers/*.py for route handlers (decorated with @router.get, @router.post, etc.)internal.py, setup.py, auth.py, public.py), verify at least one endpoint uses get_current_user or AuthorizedAgent or OwnedAgentByNameinternal.py does NOT use get_current_userdb.can_user_, db.is_system_agent, current_user.username !=, if not ... owner, and hand-rolled raise HTTPException(status_code=403 blocks. Permission logic should live in a Depends() dependency, not inline in each endpoint. List every occurrence (file:line) and FAIL if more than 5 distinct sites.9. Channel Adapter ABC
src/backend/adapters/base.py exists and defines ChannelAdapter classslack_adapter.py, etc.) inherit from ChannelAdapter10. WebSocket Events for Real-Time
src/frontend/src/ for setInterval or setTimeout patterns that poll API endpoints — flag as potential violations (should use WebSocket instead)src/frontend/src/utils/websocket.js exists11. Docker as Source of Truth
src/backend/ for container state stored in global variables or module-level dicts (e.g., running_agents = {}, container_cache = {}) — should not existdocker_service.py exists as the Docker interaction point12. Credentials: File Injection, Never Stored in DB
db/schema.py for any table that stores credential values (not references/metadata) — should find nonesrc/backend/ for patterns that write credential values to SQLite13. MCP Server = Third Surface in Sync (enforced)
src/mcp-server/src/tools/*.ts and build the MCP tool-module listsrc/backend/routers/*.py and build the backend-domain list, excluding internal.py, setup.py, auth.py, public.py, paid.py (these are not externally accessible via MCP by design)src/mcp-server/src/tools/, OR# mcp: none comment at the top of the router declaring intentional exclusion (with a one-line reason)14. Pydantic Models Centralized in models.py
src/backend/routers/*.py for class.*BaseModel or class.*Model( definitions — models should be in models.py, not routersmodels.py vs scattered across other files15. API URL Nesting Convention
src/backend/routers/*.py for APIRouter(prefix= and list all prefixes/api/agents/{name}//api/agents/These checks compare architecture.md claims against repo reality and propose concrete doc edits, not pass/fail alone.
D1. Quantitative count alignment
For each claim in architecture.md, compute the actual value and compare:
| Claim in arch.md | Actual value (compute) |
|---|---|
main.py line count | wc -l src/backend/main.py |
| Router count / list of routers | ls src/backend/routers/*.py (exclude __init__.py) |
| Service module count | ls src/backend/services/*.py (exclude __init__.py) |
| MCP tool count (modules + total tools) | ls src/mcp-server/src/tools/*.ts plus count of exported tools |
| Per-router endpoint counts | grep -E "@router\.(get|post|put|delete)" src/backend/routers/<name>.py |
For each divergence >10%, emit a suggested doc edit:
architecture.md:L<N> — claim "main.py ... 182 lines" does not match actual (860 lines).
Suggested edit: update to "860 lines" (or drop the parenthetical).
D2. Scope-coherence check
Grep architecture.md for markers: OUT OF SCOPE, dormant, not currently being developed, deprecated. For each match, extract the named module path (e.g., src/backend/services/process_engine/).
Then:
src/backend/main.py for imports of that module or for routers in that areasrc/backend/routers/*.py for imports from that modulemain.py, emit a suggested doc edit:architecture.md:L<N> — Process Engine marked "OUT OF SCOPE" but routers/processes.py, routers/approvals.py, routers/triggers.py are registered in main.py.
Suggested edit: either remove the OUT OF SCOPE tag (if the module is in fact live), or remove the routers (if it is truly dormant).
Before generating the report, validate every file:line citation produced in Steps 2a and 2b against the current working tree. The skill is sometimes run on a snapshot taken just before a large deletion PR lands; without this filter, the report (and any auto-created issue) cites paths that no longer exist.
For each cited path (always quote "$path" — citations may contain spaces or shell metacharacters):
git ls-files --error-unmatch "$path" >/dev/null 2>&1 && echo exists || echo dropped
dropped: remove the citation from the violation. Do not retain "ghost" line numbers from a previous tree.FAIL to PASS (after stale-citation filter) and record the dropped citation count in the report so the reader can see what was removed.This step exists because of issue #479: a 2026-04-24 run cited 11 paths that were deleted by commit e901108 (#430) the same day. None of those P1-critical citations were real on main, but the unfiltered report produced a priority-p1 issue against main.
Output two sections:
## Architecture Validation Report
### Invariant Compliance
| # | Invariant | Status | Details |
|---|-----------|--------|---------|
| 1 | Three-Layer Backend | PASS/FAIL | ... |
...
**Result: X/15 PASS, Y/15 FAIL**
#### Violations
##### [Invariant Name]
- **File**: path/to/file.py:line
- **Issue**: Description
- **Fix**: Suggested remediation
### Doc Drift — Suggested architecture.md Edits
#### D1. Count mismatches
- architecture.md:L<N> — "<claim>" vs actual "<value>". Suggested edit: "<new text>".
#### D2. Scope contradictions
- architecture.md:L<N> — "<section>" marked out-of-scope but <evidence of activity>. Suggested edit: <resolution>.
Create or update a GitHub issue when any of these fire (after the Step 2c stale-citation filter has run):
P0-P1 invariants (critical — break runtime or security):
P1 drift (misleading docs cause cascading downstream errors):
Dedupe guard — required before any gh issue create:
Find any existing open Architecture Validation issue. If one exists, add findings as a comment; only create a new issue when none is open.
COMMIT_SHA=$(git rev-parse --short HEAD)
# Find any open automated arch-validation issue
EXISTING=$(gh issue list --repo abilityai/trinity \
--label "automated" --state open \
--search "\"Architecture Validation\" in:title" \
--json number --jq '.[0].number')
Branch on $EXISTING. The two paths are mutually exclusive — execute exactly one.
Path A — $EXISTING is non-empty (open issue found): COMMENT, then STOP.
gh issue comment "$EXISTING" --repo abilityai/trinity --body "Re-run on \`$COMMIT_SHA\` ($(date -u +%Y-%m-%d)): [N] violations, [M] doc drift findings still present.
### Updated Critical Invariant Violations (P0-P1)
[List each P0-P1 violation with invariant number, file:line, description — Step 2c-filtered, no stale paths]
### Updated Doc Drift — Suggested architecture.md Edits
[List each suggested edit with line number and replacement text]
---
*Generated by scheduled /validate-architecture run*"
After commenting, DO NOT execute Path B. The skill workflow ends here for this run.
Path B — $EXISTING is empty (no open issue found): CREATE a new issue.
Only run this block when Path A did not run.
gh issue create \
--repo abilityai/trinity \
--title "Architecture Validation: [N] critical violations found ($(date -u +%Y-%m-%d))" \
--body "## Automated Architecture Validation Report
**Date**: $(date -u +%Y-%m-%d)
**Commit**: $COMMIT_SHA
### Critical Invariant Violations (P0-P1)
[List each P0-P1 violation with invariant number, file:line, description — Step 2c-filtered, no stale paths]
### Doc Drift — Suggested architecture.md Edits
[List each suggested edit with line number and replacement text]
### Recommended Actions
1. [Prioritized fix for each finding]
### Tracking Notes
- Future runs will comment on this issue rather than open a new one.
- Close this issue once all cited invariants pass on \`main\`.
---
*Generated by scheduled /validate-architecture run*" \
--label "type-bug,priority-p1,automated"
Concurrency caveat: best-effort, not atomic. Two simultaneous runners could both create issues; the next run will comment on the first one found. Close any duplicate manually.
If nothing critical fires after Step 2c's filter, skip issue creation — report only logged to execution history. Do not create an issue solely on pre-filter results.
automated, priority-p1)Generate and update user documentation from code, feature flows, and recent changes into docs/user-docs/
Validate config hygiene — docker-compose env vars vs .env.example vs code references vs architecture docs. Flags missing, stale, or undocumented configuration.
Validate database schema consistency — DDL in schema.py vs migrations.py vs architecture.md. Flags drift between the three sources of truth.
Chief Security Officer audit — infrastructure-first security scan with secrets archaeology, dependency supply chain, CI/CD pipeline, LLM/AI security, OWASP Top 10, STRIDE threat modeling, and active verification. Two modes — daily (8/10 confidence gate) and comprehensive (2/10 bar).
Run the Playwright frontend e2e suite against a live Trinity stack, analyze failures, and update visual regression snapshots. Use after UI changes, before merging a PR with the `ui` label, or when the `frontend-e2e` CI workflow fails.
Validate a pull request against the Trinity development methodology and generate a merge decision report.