Architecture
System architecture, agent design, data flow, and technology stack for TINAA MSP v2.0
Architecture
TINAA MSP is an agent-based continuous quality platform. It fuses automated Playwright browser testing with application performance monitoring (APM) through a multi-agent architecture, delivering continuous quality intelligence across every deployment.
System Overview
Agent-Based Architecture
TINAA MSP is built around seven purpose-specific agents that communicate through an event-driven orchestration layer. Each agent has a single well-defined responsibility and is independently testable.
Orchestrator
File: tinaa/agents/orchestrator.py
The Orchestrator is the lead agent. It receives external events, translates them into AgentTask objects, and dispatches work to the appropriate sub-agent. No business logic lives in the orchestrator — it is purely a router and task dispatcher.
Events handled
| Event type | Trigger | Agents invoked |
|---|---|---|
product_registered |
New product added via API or MCP | Explorer |
deployment_detected |
GitHub deployment webhook received | Test Runner, APM Agent |
pr_opened |
GitHub pull request opened or updated | Test Designer, Test Runner |
schedule_triggered |
Cron-based periodic scan | APM Agent, Test Runner |
anomaly_detected |
APM Agent flags a metric anomaly | Analyst, Reporter |
manual_request |
User-triggered run via API or MCP | Test Runner |
Sub-agents register themselves with orchestrator.register_agent(agent). Additional event handlers can be added with orchestrator.on_event(event_type, handler).
Explorer
File: tinaa/agents/explorer.py
The Explorer provides codebase intelligence. When a product is first registered — or when triggered manually — the Explorer clones or reads the repository and builds a map of:
- HTTP routes and API endpoints
- HTML forms and their fields
- Inferred user journeys (sequences of pages that form a complete workflow)
- Authentication boundaries
This information populates the product’s endpoint registry and seeds test suggestions. The Explorer output is the primary input for the Test Designer.
Test Designer
File: tinaa/agents/test_designer.py
The Test Designer generates Playwright playbooks from Explorer output or changed-file diffs. Given a list of routes, user journeys, or modified files, it produces structured playbook definitions with:
- Ordered step sequences (
navigate,click,fill,assert_text, etc.) - Assertion rules for expected state
- Performance gates (LCP, CLS thresholds)
- Tagging by suite type (
smoke,regression,accessibility)
Playbooks are validated by the PlaybookValidator before being saved.
Test Runner
File: tinaa/agents/test_runner.py
The Test Runner executes playbooks against target environments using Microsoft Playwright. It drives Chromium in headless mode and captures:
- Pass/fail status for every step
- Step-level screenshots on failure
- Browser console logs
- Network request/response pairs
- Core Web Vitals (LCP, CLS, INP) via the CDP performance API
Execution results are persisted to test_runs and test_results tables. Live step progress is pushed to connected WebSocket clients via ConnectionManager.
APM Agent
File: tinaa/agents/analyst.py (shared with Analyst)
The APM Agent continuously monitors registered endpoints by polling their URLs and recording MetricDatapoint records for:
- Response time (P50, P95, P99)
- Time to First Byte (TTFB)
- First Contentful Paint (FCP)
- Largest Contentful Paint (LCP)
- Cumulative Layout Shift (CLS)
- Availability
- Error rate
- HTTP status codes
The BaselineManager computes statistical baselines from historical data (minimum 30 samples). The AnomalyDetector fires an anomaly_detected event when a metric deviates beyond a configured number of standard deviations from baseline.
Analyst
File: tinaa/agents/analyst.py
The Analyst correlates test results, APM metrics, security findings, and accessibility scores to produce the composite Quality Score. The score is computed by QualityScorer using a weighted formula:
| Component | Weight | Inputs |
|---|---|---|
| Test health | 35% | Pass rate, flakiness rate, run frequency |
| Performance health | 30% | P95 response time, LCP, SLO compliance |
| Security posture | 20% | Open findings by severity, last scan age |
| Accessibility | 15% | WCAG violations, warnings, coverage |
The Analyst also identifies trends (7-day and 30-day) and generates prioritised recommendations.
Reporter
File: tinaa/agents/reporter.py
The Reporter delivers quality intelligence to humans through multiple channels:
- Dashboard — quality scores, trends, and issue lists rendered by the SPA
- WebSocket — live push of test progress, quality updates, alerts
- Alert engine — fires configured alert rules and delivers notifications
- API responses — quality reports available via
GET /api/v1/quality/{id}/report
Data Flow
The end-to-end lifecycle from product registration to quality report:
POST /api/v1/products (REST) or register_product (MCP)→ Orchestrator receives
product_registered event→ Explorer scans repository → discovers routes, APIs, forms, journeys
→ Generates playbook definitions for each discovered journey
→ PlaybookValidator checks steps
→ Playbooks saved to database
deployment_detectedor Cron schedule →
schedule_triggeredor Manual API call →
POST /api/v1/test-runs→ Orchestrator dispatches to Test Runner
→ Playwright drives Chromium against target environment
→ Each step: execute → capture screenshot / logs / metrics
→ Live progress → WebSocket → Dashboard
→ Results persisted to
test_runs, test_results tables
→ MetricDatapoints saved to TimescaleDB hypertable
→ BaselineManager recomputes baselines
→ AnomalyDetector compares to baseline
→ If anomaly:
anomaly_detected → Orchestrator → Reporter
→ QualityScorer computes weighted composite score
→ QualityScoreSnapshot saved to database
→
quality_score updated on Product record
→ Dashboard:
quality_update pushed via WebSocket→ Alerts: alert rules evaluated → notifications sent
→ API:
GET /api/v1/quality/{id}/report returns full report
Technology Stack
| Layer | Technology | Version | Purpose |
|---|---|---|---|
| Language | Python | 3.11+ | Application runtime |
| Web framework | FastAPI | 0.115+ | REST API, WebSocket |
| ASGI server | Uvicorn | 0.30+ | Production server |
| Data validation | Pydantic | 2.x | Request/response schemas |
| ORM | SQLAlchemy | 2.0 | Database models, async queries |
| Migrations | Alembic | 1.13+ | Schema versioning |
| Primary database | PostgreSQL | 16 | Relational data store |
| Time-series extension | TimescaleDB | 2.x | Hypertables for metrics |
| Cache / queue | Redis | 7 | Response caching, task queuing |
| Browser automation | Playwright | 1.46+ | Test execution (Chromium) |
| MCP protocol | FastMCP | 2.0+ | AI / IDE integration |
| Container runtime | Docker | 24+ | Packaging and deployment |
| Orchestration | Kubernetes | 1.28+ | Production cluster deployment |
| Frontend | Vanilla JS | ES2022 | SPA (no build step) |
| HTTP client | httpx | 0.27+ | Outbound HTTP requests |
| Auth | PyJWT + cryptography | 2.8+ / 42+ | API key and JWT validation |
Database Schema
TINAA MSP manages thirteen tables across three logical groups.
Schema overview
Key relationships
TimescaleDB hypertable
metric_datapoints is a TimescaleDB hypertable partitioned by the timestamp column. This provides:
- Automatic chunk management for time-series workloads
- Native compression after a configurable interval
- Efficient time-range queries with chunk exclusion
time_bucket()functions for downsampling
The TimescaleDB extension is installed automatically when the database is first created using the timescale/timescaledb:latest-pg16 Docker image.
Frontend Architecture
The dashboard is a single-page application built with vanilla JavaScript and Web Components. It requires no build step, no npm install, and no bundler — the HTML, CSS, and JavaScript are served directly by FastAPI as static files.
Design principles
- Zero dependencies — standard browser APIs only; no React, Vue, or Angular
- Web Components — custom elements encapsulate dashboard widgets
- ES modules — native
import/exportsyntax, no transpilation needed - Progressive enhancement — dashboard degrades gracefully without JavaScript
File layout
WebSocket integration
The frontend maintains a single persistent WebSocket connection per page load. The ConnectionManager on the server routes push messages back to each connected client by client_id. The dashboard subscribes to product-scoped events with:
1
ws.send(JSON.stringify({ type: 'subscribe', product_id: productId }));
Updates are applied reactively — quality score cards update without page refresh when a quality_update message arrives.
MCP Integration Architecture
TINAA MSP runs the FastMCP 2.0 server on the same port (8765) as the REST API, using a different transport path. AI assistants (Claude, Cursor, VS Code with MCP extensions) connect to the MCP endpoint and interact with TINAA through the 14 registered tools.
Tools are plain async functions decorated with standard Python type annotations. FastMCP generates the JSON schema for each tool’s parameter list from the type hints and docstrings. This means every MCP tool is also directly callable in unit tests without going through the protocol wrapper.
MCP server mode
Start in MCP-only mode (no REST API) with:
1
TINAA_MODE=mcp uvicorn tinaa.api.app:app --host 0.0.0.0 --port 8765
Or in Docker:
1
docker run -e TINAA_MODE=mcp ghcr.io/aj-geddes/tinaa-playwright-msp:latest
Event-Driven Architecture
The Orchestrator implements a lightweight event bus. Events flow in one direction: external trigger -> Orchestrator -> agent tasks -> results.
Event lifecycle
orchestrator.handle_event(event_type, payload)
_build_tasks_for_event()
dispatch_task(task)
agent.execute(task)
Registering a custom handler
1
2
3
4
5
6
7
8
from tinaa.agents.orchestrator import Orchestrator, DEPLOYMENT_DETECTED
orchestrator = Orchestrator()
@orchestrator.on_event(DEPLOYMENT_DETECTED)
async def notify_slack(payload: dict) -> None:
# custom side-effect after deployment detected
await slack_client.post(channel="#releases", text=f"Deployed {payload['commit_sha']}")
Extending with new agents
- Subclass
BaseAgentfromtinaa/agents/base.py - Implement
execute(task: AgentTask) -> AgentTask - Register with
orchestrator.register_agent(MyAgent()) - Add a new event constant and task builder in
orchestrator._build_tasks_for_event()
The base class provides structured logging, status tracking, and error handling so custom agents only need to implement their core logic.
Security Architecture
TINAA MSP follows a defence-in-depth approach:
| Layer | Control |
|---|---|
| Network | Kubernetes NetworkPolicy restricts pod-to-pod traffic; Ingress enforces TLS |
| Authentication | API key via X-API-Key header; HMAC-SHA256 webhook signature validation |
| Input validation | Pydantic v2 validates all request bodies at the API boundary |
| Database | SQLAlchemy parameterised queries prevent SQL injection |
| Secrets | Kubernetes Secrets / environment variables; never hardcoded or logged |
| Dependencies | pip audit / safety scans in CI; Dependabot auto-updates |
GitHub webhook payloads are validated against GITHUB_WEBHOOK_SECRET using HMAC-SHA256 before any processing begins. Requests with an invalid or missing signature return 401 Unauthorized.