Metrics Dashboard
Aggregates pipeline run data to track velocity, success rates, and identify improvement areas.
Configuration
Uses the production API by default. No configuration needed for read operations.
Defaults (no setup required):
- API URL:
https://api-gateway-856475788601.us-central1.run.app - Read-only endpoints at
/public/*require no authentication
For write operations, set:
export PIPELINE_API_KEY="..." # Get from GCP Secret Manager or ask admin
Workflow
1. Fetch Ticket Data from API
import { PipelineClient, PipelineAPIError } from '@pipeline/client'
const client = new PipelineClient({
apiUrl: process.env.PIPELINE_API_URL || 'https://api-gateway-856475788601.us-central1.run.app',
agentId: process.env.AGENT_ID!,
})
// Fetch all tickets for metrics calculation
const { tickets, total } = await client.listTickets({
limit: 1000,
})
// Filter by state if needed
const { tickets: completedTickets } = await client.listTickets({
state: 'done',
limit: 1000,
})
// Filter by agent
const { tickets: myTickets } = await client.listTickets({
agent_id: process.env.AGENT_ID,
})
2. Calculate Metrics
interface TicketMetrics {
total: number
completed: number
failed: number
inProgress: number
successRate: number
avgCycleTimeHours: number
minCycleTimeHours: number
maxCycleTimeHours: number
}
function calculateMetrics(tickets: Ticket[]): TicketMetrics {
const completed = tickets.filter((t) => t.state === 'done')
const failed = tickets.filter((t) => t.state === 'failed')
const inProgress = tickets.filter((t) =>
['intake', 'research', 'planning', 'implementation', 'pr_created'].includes(t.state)
)
// Calculate cycle times from timestamps
const cycleTimes = completed
.filter((t) => t.created_at && t.state_changed_at)
.map((t) => {
const start = new Date(t.created_at).getTime()
const end = new Date(t.state_changed_at).getTime()
return (end - start) / (1000 * 60 * 60) // hours
})
const avgCycleTime = cycleTimes.length > 0 ? cycleTimes.reduce((a, b) => a + b, 0) / cycleTimes.length : 0
return {
total: tickets.length,
completed: completed.length,
failed: failed.length,
inProgress: inProgress.length,
successRate:
completed.length + failed.length > 0 ? (completed.length / (completed.length + failed.length)) * 100 : 0,
avgCycleTimeHours: avgCycleTime,
minCycleTimeHours: cycleTimes.length > 0 ? Math.min(...cycleTimes) : 0,
maxCycleTimeHours: cycleTimes.length > 0 ? Math.max(...cycleTimes) : 0,
}
}
const metrics = calculateMetrics(tickets)
3. Generate Dashboard
# Pipeline Metrics Dashboard
**Period:** Last 30 days | **Generated:** {timestamp}
## Summary
| Metric | Value | Trend |
| ----------------- | ------- | ---------- |
| Tickets Completed | 12 | ↑ +3 |
| Success Rate | 83% | ↑ +5% |
| Avg Cycle Time | 2.5 hrs | ↓ -0.5 hrs |
## Cycle Time Breakdown
Intake ████ 5 min | Research ████████ 15 min | Planning ██████ 10 min
Implement █████████████ 25 min | Quality ████ 8 min | Review ██████ 12 min
## Success by Type
| Type | Count | Success | Avg Time |
| -------- | ----- | ------- | -------- |
| Bug Fix | 5 | 100% | 1.2 hrs |
| Feature | 4 | 75% | 3.5 hrs |
| Refactor | 3 | 67% | 2.8 hrs |
## Failure Analysis
| Failure Point | Count | % |
| ---------------- | ----- | --- |
| Quality gates | 2 | 40% |
| CI failures | 1 | 20% |
| Review rejection | 1 | 20% |
## Recommendations
1. **Reduce implementation time** - Consider more parallel subagents
2. **Improve quality gates** - 40% of failures at this stage
4. Save Dashboard Locally
import { writeFileSync, appendFileSync } from 'fs'
// Save markdown dashboard
writeFileSync('metrics-dashboard.md', dashboardMarkdown)
// Save JSON summary
const summary = {
generatedAt: new Date().toISOString(),
period: '30d',
...metrics,
}
writeFileSync('metrics-summary.json', JSON.stringify(summary, null, 2))
// Append to history
appendFileSync(
'metrics-history.jsonl',
JSON.stringify({
date: new Date().toISOString().split('T')[0],
metrics: summary,
}) + '\n'
)
5. Error Handling
try {
const { tickets } = await client.listTickets({ limit: 1000 })
const metrics = calculateMetrics(tickets)
} catch (error) {
if (error instanceof PipelineAPIError) {
console.error(`API error: ${error.message} (status: ${error.status})`)
}
throw error
}
Custom Queries
// Tickets by agent
const ticketsByAgent = tickets.reduce(
(acc, t) => {
const agent = t.agent_id ?? 'unassigned'
acc[agent] = (acc[agent] ?? 0) + 1
return acc
},
{} as Record<string, number>
)
// Slowest tickets
const slowestTickets = tickets
.filter((t) => t.created_at && t.state_changed_at)
.map((t) => ({
id: t.id,
title: t.title,
cycleTime: (new Date(t.state_changed_at!).getTime() - new Date(t.created_at).getTime()) / (1000 * 60 * 60),
}))
.sort((a, b) => b.cycleTime - a.cycleTime)
.slice(0, 5)
// Failed tickets by last state
const failedByState = tickets
.filter((t) => t.state === 'failed')
.reduce(
(acc, t) => {
const phase = t.previous_state ?? 'unknown'
acc[phase] = (acc[phase] ?? 0) + 1
return acc
},
{} as Record<string, number>
)
Output Artifacts (Local)
| File | Description |
|---|---|
| metrics-dashboard.md | Point-in-time dashboard |
| metrics-summary.json | Latest metrics JSON |
| metrics-history.jsonl | Historical trends |
Notes
- Metrics are calculated client-side from ticket data fetched via API
- Allows flexible aggregation across all agents in the distributed pipeline
- Historical trends require local storage of metrics-history.jsonl
