analytics

package
v0.42.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package analytics reconstructs team flow metrics (lead time, cycle time, stage dwell, bottlenecks) from the specs repo git history. It consumes plain git.LogEntry values and never shells out itself.

Index

Constants

View Source
const StatusBlocked = "blocked"

StatusBlocked is the escape-hatch status stamped by `spec eject`. Mirrors pipeline.StatusBlocked without importing the pipeline engine.

Variables

This section is empty.

Functions

func BuildTimelines

func BuildTimelines(events []Event, terminalStages []string) map[string]*Timeline

BuildTimelines replays extracted events (any order) into per-spec timelines. terminalStages marks completion; the first entry into any of them stamps CompletedAt.

func FormatDuration

func FormatDuration(d time.Duration) string

FormatDuration renders a duration compactly: minutes under an hour, tenths of hours under a day, tenths of days beyond.

func RenderFlowReport

func RenderFlowReport(r, prev *FlowReport) string

RenderFlowReport renders the team flow report. prev, when non-nil, drives cross-window deltas; pass nil when no comparison window exists.

func RenderSpecTimeline

func RenderSpecTimeline(tl *Timeline, now time.Time) string

RenderSpecTimeline renders a single spec's journey through the pipeline.

func RenderStageReport

func RenderStageReport(r *FlowReport, stage string, timelines map[string]*Timeline, now time.Time) string

RenderStageReport renders a deep-dive for one stage: its dwell distribution, current occupants, and the reversion boundaries touching it.

func Sparkline

func Sparkline(samples []time.Duration) string

Sparkline renders a fixed-width histogram of the samples. Empty input yields an empty string.

Types

type AgingItem

type AgingItem struct {
	SpecID string
	Stage  string
	Dwell  time.Duration
	P85    time.Duration
	Ratio  float64
}

AgingItem flags an open spec dwelling beyond the historical p85 of its stage.

type AgingJSON

type AgingJSON struct {
	SpecID string       `json:"spec_id"`
	Stage  string       `json:"stage"`
	Dwell  DurationJSON `json:"dwell"`
	P85    DurationJSON `json:"p85"`
	Ratio  float64      `json:"ratio"`
}

AgingJSON is one aging-WIP entry.

type BlockedSpell

type BlockedSpell struct {
	Start time.Time
	End   time.Time
}

BlockedSpell is one closed eject→resume interval.

type Coverage

type Coverage struct {
	CommitsScanned int
	Transitions    int
	Unattributable int
}

Coverage carries the extraction honesty counters for the report footer.

type Distribution

type Distribution struct {
	Samples []time.Duration
	P50     time.Duration
	P85     time.Duration
	P95     time.Duration
}

Distribution summarises a set of duration samples with the percentiles that matter for flow analysis. Averages are deliberately absent: lead-time data is long-tailed and averages mislead.

func NewDistribution

func NewDistribution(samples []time.Duration) Distribution

NewDistribution computes percentiles over the given samples.

func (Distribution) Count

func (d Distribution) Count() int

Count returns the sample count.

type DistributionJSON

type DistributionJSON struct {
	Samples int          `json:"samples"`
	P50     DurationJSON `json:"p50"`
	P85     DurationJSON `json:"p85"`
	P95     DurationJSON `json:"p95"`
}

DistributionJSON is the percentile summary of a distribution.

type DurationJSON

type DurationJSON struct {
	Seconds int64  `json:"seconds"`
	Human   string `json:"human"`
}

DurationJSON is a duration in machine and human form.

type Event

type Event struct {
	SpecID    string
	Kind      EventKind
	FromStage string // empty for scaffolds
	ToStage   string
	At        time.Time
	Source    EventSource
}

Event is a single stage transition (or scaffold) for one spec.

type EventKind

type EventKind string

EventKind classifies a spec lifecycle event.

const (
	KindScaffolded EventKind = "scaffolded"
	KindAdvanced   EventKind = "advanced"
	KindReverted   EventKind = "reverted"
	KindEjected    EventKind = "ejected"
	KindResumed    EventKind = "resumed"
)

Event kinds derived from the specs repo history.

type EventSource

type EventSource string

EventSource records which extraction tier attributed the event kind.

const (
	SourceMessage     EventSource = "message"
	SourceFrontmatter EventSource = "frontmatter"
)

Extraction tiers: conventional commit messages (fast path) and frontmatter status diffs (truth path for manual edits and unconventional history).

type ExtractResult

type ExtractResult struct {
	Events         []Event
	CommitsScanned int
	Transitions    int // status transitions successfully attributed
	Unattributable int // spec-file changes whose status could not be read
}

ExtractResult carries the extracted events plus honesty counters for the report footer ("analysed N/M transitions").

func ExtractEvents

func ExtractEvents(entries []git.LogEntry, stageNames []string) ExtractResult

ExtractEvents replays the commit log (oldest-first) into typed lifecycle events. Frontmatter status diffs are the source of truth; commit messages only classify the kind when stage names are unknown to the pipeline.

type FlowInput

type FlowInput struct {
	Timelines  map[string]*Timeline
	Events     []Event
	Window     Window
	Now        time.Time
	StageNames []string        // full pipeline order
	Terminal   []string        // terminal stage names
	WorkStages []string        // engineer-owned stages (flow-efficiency numerator)
	FastTrack  map[string]bool // spec IDs flagged fast_track
	Filter     map[string]bool // optional spec-ID scope (nil = all)
}

FlowInput is everything ComputeFlow needs; it performs no I/O.

type FlowReport

type FlowReport struct {
	Window            Window
	Completed         int
	LeadTime          Distribution
	CycleTime         Distribution
	FastTrackLead     Distribution // separate population, may be empty
	FlowEfficiency    float64      // work-stage time / lead time; -1 when unknown
	ThroughputPerWeek float64
	StageDwell        []StageDwell // pipeline order, stages with samples only
	Bottleneck        string       // stage with largest p50 dwell
	Reversions        []ReversionBoundary
	TotalAdvances     int
	TotalReversions   int
	AgingWIP          []AgingItem
	BlockedSpecs      int
	BlockedTotal      time.Duration
	Coverage          Coverage
}

FlowReport is the computed team flow picture for one window.

func ComputeFlow

func ComputeFlow(in FlowInput) *FlowReport

ComputeFlow aggregates per-spec timelines into the team flow report.

func (*FlowReport) ToJSON

func (r *FlowReport) ToJSON() ReportJSON

ToJSON converts a FlowReport into its machine-readable form.

type ReportJSON

type ReportJSON struct {
	Window struct {
		From  time.Time `json:"from"`
		To    time.Time `json:"to"`
		Label string    `json:"label"`
	} `json:"window"`
	Completed         int               `json:"completed"`
	LeadTime          DistributionJSON  `json:"lead_time"`
	CycleTime         DistributionJSON  `json:"cycle_time"`
	FastTrackLead     *DistributionJSON `json:"fast_track_lead,omitempty"`
	FlowEfficiency    *float64          `json:"flow_efficiency,omitempty"`
	ThroughputPerWeek float64           `json:"throughput_per_week"`
	StageDwell        []StageDwellJSON  `json:"stage_dwell"`
	Bottleneck        string            `json:"bottleneck,omitempty"`
	Reversions        []ReversionJSON   `json:"reversions"`
	TotalAdvances     int               `json:"total_advances"`
	TotalReversions   int               `json:"total_reversions"`
	AgingWIP          []AgingJSON       `json:"aging_wip"`
	BlockedSpecs      int               `json:"blocked_specs"`
	BlockedTotal      DurationJSON      `json:"blocked_total"`
	Coverage          struct {
		CommitsScanned int `json:"commits_scanned"`
		Transitions    int `json:"transitions"`
		Unattributable int `json:"unattributable"`
	} `json:"coverage"`
}

ReportJSON is the machine-readable flow report.

type Reversion

type Reversion struct {
	From string
	To   string
	At   time.Time
}

Reversion records a single backwards transition.

type ReversionBoundary

type ReversionBoundary struct {
	From  string
	To    string
	Count int
}

ReversionBoundary counts backwards transitions across one stage boundary.

type ReversionJSON

type ReversionJSON struct {
	From  string `json:"from"`
	To    string `json:"to"`
	Count int    `json:"count"`
}

ReversionJSON is one reversion boundary count.

type StageDwell

type StageDwell struct {
	Stage string
	Dist  Distribution
}

StageDwell is the dwell distribution for one stage over completed specs.

type StageDwellJSON

type StageDwellJSON struct {
	Stage string           `json:"stage"`
	Dwell DistributionJSON `json:"dwell"`
}

StageDwellJSON is one stage's dwell distribution.

type StageVisit

type StageVisit struct {
	Stage   string
	Entered time.Time
	Exited  time.Time
}

StageVisit is one closed interval a spec spent in a stage.

func (StageVisit) Duration

func (v StageVisit) Duration() time.Duration

Duration returns the visit's dwell.

type Timeline

type Timeline struct {
	SpecID       string
	Created      time.Time
	Visits       []StageVisit // closed visits only
	Blocked      []BlockedSpell
	Reversions   []Reversion
	Advances     int
	CurrentStage string    // stage the spec occupies after the last event
	CurrentSince time.Time // when it entered CurrentStage
	CompletedAt  time.Time // zero until the spec first enters a terminal stage
}

Timeline is the reconstructed journey of one spec through the pipeline.

func (*Timeline) Completed

func (t *Timeline) Completed() bool

Completed reports whether the spec has reached a terminal stage.

func (*Timeline) FirstEntered

func (t *Timeline) FirstEntered(stage string) time.Time

FirstEntered returns when the spec first entered the given stage, or zero.

func (*Timeline) LeadTime

func (t *Timeline) LeadTime() time.Duration

LeadTime is creation → completion. Zero until completed.

func (*Timeline) StageDwellTotal

func (t *Timeline) StageDwellTotal(stage string) time.Duration

StageDwellTotal accumulates the spec's dwell in one stage across revisits.

type Window

type Window struct {
	From  time.Time
	To    time.Time
	Label string
}

Window is the reporting interval.

func (Window) Contains

func (w Window) Contains(t time.Time) bool

Contains reports whether t falls inside the window (inclusive bounds).

func (Window) Weeks

func (w Window) Weeks() float64

Weeks returns the window length in (fractional) weeks, minimum one day.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL