feat: dashboard reads the sprint, the risks, aging and bottlenecks
A new projectInsights reading (GraphQL Project.insights, REST
/projects/{project}/insights, MCP get_project_insights), computed by pure
functions over the read model and the timeline:
- the sprint under way: days left, points done, pace against what is
needed and whether it gets there;
- tickets at risk: overdue, due within a week, blocked by an open ticket
and stuck (7 days or more in an in-progress or QA status);
- aging of the work under way against the p85 cycle time;
- average days in each in-progress and QA status, with WIP limits.
The dashboard shows them in four new panels, reloaded on every change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -285,6 +285,26 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/projects/{project}/insights": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* The dashboard
|
||||
* @description Sprint under way (pace against time), overdue, due-soon, blocked and stuck tickets, aging of the work under way against the p85 cycle time, and average days in each in-progress and QA status.
|
||||
*/
|
||||
get: operations["ProjectsController_insights"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/projects/{project}/activity": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -2100,6 +2120,72 @@ export interface components {
|
||||
throughput: components["schemas"]["ThroughputDto"][];
|
||||
qa: components["schemas"]["QaStatsDto"];
|
||||
};
|
||||
SprintPulseDto: {
|
||||
/** Format: uuid */
|
||||
sprintId: string;
|
||||
name: string;
|
||||
/** Format: date */
|
||||
startDate: string;
|
||||
/** Format: date */
|
||||
endDate: string;
|
||||
totalDays: number;
|
||||
/** @description Days left after today */
|
||||
daysLeft: number;
|
||||
tickets: number;
|
||||
doneTickets: number;
|
||||
points: number;
|
||||
donePoints: number;
|
||||
pacePerDay: number | null;
|
||||
neededPerDay: number | null;
|
||||
/** @description At this pace, what is open would not be closed */
|
||||
behind: boolean;
|
||||
};
|
||||
InsightIssueDto: {
|
||||
/** Format: uuid */
|
||||
id: string;
|
||||
/** @example WEB-12 */
|
||||
key: string;
|
||||
title: string;
|
||||
/** @description Status id */
|
||||
status: string;
|
||||
assignee: components["schemas"]["UserRefDto"] | null;
|
||||
/** @description Days in its status (aging, stuck) or past / to its due date */
|
||||
days: number;
|
||||
/** Format: date */
|
||||
dueDate: string | null;
|
||||
};
|
||||
RisksDto: {
|
||||
overdue: components["schemas"]["InsightIssueDto"][];
|
||||
overdueCount: number;
|
||||
/** @description Due within 7 days */
|
||||
dueSoon: components["schemas"]["InsightIssueDto"][];
|
||||
dueSoonCount: number;
|
||||
blocked: components["schemas"]["InsightIssueDto"][];
|
||||
blockedCount: number;
|
||||
stuck: components["schemas"]["InsightIssueDto"][];
|
||||
stuckCount: number;
|
||||
stuckDays: number;
|
||||
};
|
||||
AgingDto: {
|
||||
issues: components["schemas"]["InsightIssueDto"][];
|
||||
/** @description p85 cycle time of the last 90 days */
|
||||
thresholdDays: number | null;
|
||||
};
|
||||
StatusTimeDto: {
|
||||
status: string;
|
||||
averageDays: number | null;
|
||||
samples: number;
|
||||
open: number;
|
||||
wipLimit: number | null;
|
||||
overLimit: boolean;
|
||||
};
|
||||
ProjectInsightsDto: {
|
||||
generatedAt: string;
|
||||
sprint: components["schemas"]["SprintPulseDto"] | null;
|
||||
risks: components["schemas"]["RisksDto"];
|
||||
aging: components["schemas"]["AgingDto"];
|
||||
bottlenecks: components["schemas"]["StatusTimeDto"][];
|
||||
};
|
||||
ActivityDto: {
|
||||
id: string;
|
||||
/** @example IssueStatusChanged */
|
||||
@@ -3882,6 +3968,64 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
ProjectsController_insights: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
/** @description Project id or key */
|
||||
project: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ProjectInsightsDto"];
|
||||
};
|
||||
};
|
||||
/** @description Invalid input */
|
||||
400: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Missing or rejected Pireon token */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not a member of the project (or not allowed) */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
/** @description Not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiErrorDto"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
ProjectsController_activity: {
|
||||
parameters: {
|
||||
query?: {
|
||||
|
||||
Reference in New Issue
Block a user