feat: dashboard forecasts releases, tells what changed and weighs the load
The insights reading grows (GraphQL insights(since:), REST ?since=, MCP): - planned releases with a likely (p50) and safe (p85) finish date from a Monte Carlo over the last six weeks of finished work, one after another, and a verdict against their target; - with `since` (the viewer's last visit, kept in the browser): tickets created, finished and sent back from QA, and their mentions; - each person's open points against what they closed per sprint lately; - open tickets without estimate, area or assignee, or untouched for 90 days, each count opening the list filtered (new noEstimate, noArea and updatedBefore filters). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,64 @@ export class StatusTimeReadModel {
|
||||
overLimit!: boolean;
|
||||
}
|
||||
|
||||
/** When a planned release would be finished, at the team's real pace. */
|
||||
export class ReleaseForecastReadModel {
|
||||
releaseId!: string;
|
||||
name!: string;
|
||||
targetDate!: string | null;
|
||||
/** Tickets in it (cancelled ones aside). */
|
||||
total!: number;
|
||||
finished!: number;
|
||||
/** Half of the simulated futures finish by then (null: no pace yet). */
|
||||
likelyDate!: string | null;
|
||||
/** 85% of them do. */
|
||||
safeDate!: string | null;
|
||||
/**
|
||||
* DONE: nothing left. NO_DATE: no target to compare. UNKNOWN: nothing
|
||||
* finished lately to measure a pace. ON_TRACK: 85% in time. AT_RISK: half
|
||||
* in time. LATE: less than half.
|
||||
*/
|
||||
verdict!: 'DONE' | 'NO_DATE' | 'UNKNOWN' | 'ON_TRACK' | 'AT_RISK' | 'LATE';
|
||||
}
|
||||
|
||||
/** What has happened since a moment (the viewer's last visit). */
|
||||
export class ChangesSinceReadModel {
|
||||
since!: string;
|
||||
created!: InsightIssueReadModel[];
|
||||
createdCount!: number;
|
||||
finished!: InsightIssueReadModel[];
|
||||
finishedCount!: number;
|
||||
/** Sent back from QA. */
|
||||
returned!: InsightIssueReadModel[];
|
||||
returnedCount!: number;
|
||||
/** Times the viewer was mentioned in this project. */
|
||||
mentions!: number;
|
||||
}
|
||||
|
||||
/** One person's open work against what they usually close. */
|
||||
export class TeamLoadReadModel {
|
||||
user!: UserRefReadModel;
|
||||
openTickets!: number;
|
||||
openPoints!: number;
|
||||
/** Their open tickets waiting for another open one. */
|
||||
blocked!: number;
|
||||
/** Points they closed per sprint, over the last three closed sprints. */
|
||||
pointsPerSprint!: number | null;
|
||||
/** More open points than they usually close in a sprint. */
|
||||
overloaded!: boolean;
|
||||
}
|
||||
|
||||
/** Open tickets missing what planning needs. */
|
||||
export class BacklogHealthReadModel {
|
||||
open!: number;
|
||||
unestimated!: number;
|
||||
noArea!: number;
|
||||
unassigned!: number;
|
||||
/** Not updated in `staleDays` days. */
|
||||
stale!: number;
|
||||
staleDays!: number;
|
||||
}
|
||||
|
||||
/** What the project dashboard reads beyond the counts. */
|
||||
export class ProjectInsightsReadModel {
|
||||
generatedAt!: string;
|
||||
@@ -78,4 +136,11 @@ export class ProjectInsightsReadModel {
|
||||
aging!: AgingReadModel;
|
||||
/** In-progress and QA statuses, in board order. */
|
||||
bottlenecks!: StatusTimeReadModel[];
|
||||
/** Planned releases, by target date. */
|
||||
releases!: ReleaseForecastReadModel[];
|
||||
/** Null unless asked with a `since`. */
|
||||
changes!: ChangesSinceReadModel | null;
|
||||
/** Members (and others with open tickets), busiest first. */
|
||||
team!: TeamLoadReadModel[];
|
||||
backlog!: BacklogHealthReadModel;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user