Repository navigation
feat(charts): draw a growth forecast chart per top repository - #186
Merged
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The forecast maths was already per repo: computeForecast fits linear regression and a weighted moving average to each Top Repository's own reconstructed history, and both report dialects print those figures as tables. Only the chart was aggregate-only, so the one visual in the Growth Forecast section plotted the Tracked Set's total while the tables underneath it talked about individual repositories. Emit forecast-<owner>-<repo>.svg per top repository, plotted over the history the forecast was actually fitted to, and link it from both reports under that repository's table. A repository the domain fell back to the aggregate for gets its table and no chart: that projection describes the whole Tracked Set's shape, not the repository named above it, and drawing it would be the same drift the two per-repo accessors already exist to prevent. RepoForecast gains `source` (ForecastSource: OWN | AGGREGATE). It earns a type under ADR 0022 on all three counts: presentation reads it, it crosses the domain boundary, and the alternative is re-deriving the fallback rule against MIN_SNAPSHOTS_FOR_FORECAST in a second place. PER_REPO_FORECAST is a fifth ChartKind rather than an optional field on FORECAST, mirroring how PER_REPO sits beside STAR_HISTORY; both forecast builders are two lines over one private projectionSpec, so they cannot drift apart in anything but which series they read. The report model takes the drawn filenames as one ReadonlySet and composes the names it asks about, replacing what would have been a pair of adjacent, same-typed, non-interchangeable predicates. resolveChartHistories now memoises reconstructedForRepo. Five consumers ask for the same repository in one run and buildStarHistory buckets every starred_at each time, so the second per-repo chart would otherwise have turned three reconstructions per repository into five. The filename prefixes rather than suffixes the stem: forecast-user-repo cannot collide with the star history chart of a repository named "*-forecast", which is a plausible name, only with an owner called "forecast-...", which is not. The sample in docs/examples is rendered by the real pipeline rather than drawn by hand, over a curve whose recent acceleration separates the two methods, which is the difference between them a single sample can show.
fbuireu
force-pushed
the
fbuireu/regression-moving-average-per-repo-9rnyf0
branch
from
September 6, 2026 20:29
f80c96b to
7a0b2b1
Compare
fbuireu
deleted the
fbuireu/regression-moving-average-per-repo-9rnyf0
branch
September 6, 2026 20:32
fbuireu
pushed a commit
that referenced
this pull request
Sep 6, 2026
# [1.27.0](v1.26.4...v1.27.0) (2026-09-06) ### Features * **charts:** draw a growth forecast chart per top repository ([#186](#186)) ([ddf63da](ddf63da))
Owner
Author
|
🎉 This PR is included in version 1.27.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The forecast maths was already per repo —
computeForecastfits both Forecast Methods to each Top Repository's own reconstructed history, and both Report dialects already printed those figures as tables. Only the chart was aggregate-only:forecastSpecreadforecastData.aggregateand plottedsnapshot.totalStars, so the single visual in the Growth Forecast section described the Tracked Set's total while the tables underneath it talked about individual repositories.This closes that gap: one Forecast Chart per Top Repository, plotted over the history its projection was actually fitted to.
Type of Change
Related Issue
Changes Made
charts/forecast-<owner>-<repo>.svgper Top Repository, linked from both Reports under that repository's Forecast table.buildChartFilesplots each overchartHistories.reconstructedForRepo(name)— the very History@domain/forecastfitted — so the observed curve and the projection continuing it cannot describe different series.forRepovsreconstructedForRepo) already exist to prevent.RepoForecastgainssource(ForecastSource:OWN|AGGREGATE). It earns a type under ADR 0022 on all three counts:@presentationreads it, it crosses the domain boundary, and the alternative is re-deriving the fallback rule againstMIN_SNAPSHOTS_FOR_FORECASTin a second place.PER_REPO_FORECASTis a fifthChartKind, mirroring howPER_REPOsits besideSTAR_HISTORY. Both forecast builders are two lines over one privateprojectionSpec, which owns the guard, the dated x-axis, the week labels and the three-series layout, so the two kinds cannot drift apart in anything but which series they read. Neither adapter changes —svg-chart.tsandchart.tstake any request — which is what ADR 0014 was for.model.perRepoForecastsbecomes the single list both dialects iterate for the per-repo half of the section, each entry carrying the History its chart was drawn from (ornullwhen the run drew none), following the existing "the linked set is the drawn set" rule.buildReportModeltakes the drawn filenames as oneReadonlySet<string>and composes the names it asks about itself. A second chart family costs one more lookup, not one more parameter; the alternative was a pair of adjacent, same-typed, non-interchangeable predicates — thehistory/velocityHistoryhazard the presentation guide already documents.resolveChartHistoriesmemoisesreconstructedForRepo(nullresults included). Five consumers ask for the same repository in one run andbuildStarHistorybuckets everystarred_ateach time, so the second per-repo chart would otherwise have turned three reconstructions per repository into five. The closure captures every input, so the cache is sound.forecast-would collide with the star-history chart of any repository actually named*-forecast, a plausible name; as a prefix it only collides with an owner calledforecast-….Testing
pnpm test:ut)pnpm build)1026 tests pass; coverage 99.2% statements / 95.12% branches / 100% functions / 99.84% lines, against the 85% threshold.
New cases:
sourceresolution inforecast.test.ts; the fifth kind in both enumerations plus its own series and the unknown-reponullinchart-spec.test.ts; theOWN-only file emission and the once-per-repo reconstruction incharts.test.ts;perRepoForecastsand its three chart-history outcomes inreport-model.test.ts; the link inmarkdown.test.ts, the decoded QuickChart title inhtml.test.ts, and drawn-set/linked-set parity inrun.test.ts.The sample below is rendered by the real pipeline (
computeForecast+renderSvgChart), not drawn by hand, and it came out byte-identical before and after the fifth-kind refactor. Its curve accelerates recently on purpose: with linear growth the two methods projected 414 and 415 and the lines overlapped, which shows nothing. Here the weighted moving average follows the recent weeks (427) and the linear regression averages the whole span (404), so they fan out.Checklist
pnpm verifypasses (format check, typecheck, coverage and build)dist/withpnpm buildand committed it, if I touched anything undersrc/action.yml, the wiki and the READMECLAUDE.md,CONTEXT.md,ARCHITECTURE.md, ADR or wiki page my change affects, in this same PR, andpnpm test:docspassesThe unchecked input/output box is not an omission: this PR adds no action input, output or default. The new charts are gated by the existing
include-chartsandtop-repos.Documentation updated in the same commit:
src/presentation/CLAUDE.md,src/domain/CLAUDE.md,src/application/CLAUDE.md(whoseresolveChartHistoriesbullet did not namereconstructedForRepo, and was already incomplete before this change), the Chart entry inCONTEXT.md(now five kinds), the data-branch tree inARCHITECTURE.md, five wiki pages, and the sample gallery.Additional Notes
No ADR. The decisions here meet two of the three bars — surprising without context, a real trade-off — but not the third: each is reversible by deleting one condition, one
caseor oneMap. The rationale lives in the two layer guides instead.Why a chart is withheld rather than drawn from the Stored History.
forRepofalls back to the Stored History, whererepoStarSeriesyields0for every Snapshot taken before the repository joined the Tracked Set. Plotting a projection over that fabricated0 → totalramp is the failure modesrc/domain/CLAUDE.mdalready documents for the forecast figures; this keeps the chart honest the same way.GIF (mandatory)
Thanks for contributing! ✨