feat: volume/cadence context and time-bucketed trends (#4) - #14
Merged
Conversation
Adds the cadence metric concern: flattens the subject's own timestamped events (PRs authored, reviews given, review comments written) scoped to the coverage window, and counts distinct active UTC days plus total contributions. Extends the report Cadence sub-struct accordingly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds TrendBucket and Cadence.Trend to the report schema (additive) and a generic monthly bucketing helper over timestamped subject events, clamped to the half-open coverage window. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins timezone normalization to UTC, partial first/last buckets, and half-open boundary instants for the cadence trend series. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cadence renders after collaboration in a muted context-styled section with a small trend table; numbers never get headline stat treatment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
End-to-end test now asserts the report carries cadence with trend buckets and that no score field appears in the JSON. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
Review round 1 — approvedVerified independently in a clean worktree:
Formal approval is not possible on a same-account PR; merging by squash. 🤖 Generated with Claude Code |
gkanitz
added a commit
that referenced
this pull request
Jun 12, 2026
After rebasing onto main (which merged PR #14 cadence and PR #15 collaboration metrics), two gaps surfaced: 1. provider/github now fetches /pulls/{n}/reviews and /pulls/comments for every PR in every repo; the multi-repo and org-scoped fixture servers were missing those routes, so FetchActivity returned 404s. Added empty-array handlers for all PR numbers exercised by those servers. 2. resolveRepos used a plain strings.Split for the -repo flag, silently passing leading/trailing whitespace into repo names. Ported main's splitRepos trimming logic into resolveRepos so TestRunTrimsRepoListWhitespace continues to pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
10 tasks
gkanitz
added a commit
that referenced
this pull request
Jun 12, 2026
* wip: checkpoint before session limit reset
Incomplete TDD state; resume from here.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(github): enumerate repos visible to org and installation tokens
ListOrgRepos pages through /orgs/{org}/repos; ListInstallationRepos
pages through /installation/repositories. Both return owner/name lists
that feed the report's coverage stamp.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(github): classify token scope for the coverage stamp
ClassifyToken maps token prefix (ghs_/github_pat_/ghp_/gho_) and the
reported X-OAuth-Scopes header to a scope class, never echoing the
token itself.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(report): stamp token scope class; require non-empty coverage repo list
Coverage gains token_scope_class, set via the new additive Build
option WithTokenScopeClass. Validate now rejects reports whose
coverage stamp lists no repos, so an empty enumeration cannot pass
silently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(github): GitHub App installation-token exchange
AppAuth turns app ID + private key into a signed RS256 app JWT
(stdlib only, no new deps), discovers the sole installation when none
is chosen, and mints the short-lived installation token. Fixture
tests verify the JWT signature server-side and pin the ambiguity
error when several installations exist.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(github): pin cross-repo aggregation with multi-repo fixtures
PRs authored in acme/widgets and acme/gadgets sum into one
ActivitySet: per-repo counts, total, merged count, and the repo list
that feeds the coverage stamp.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(cli): org-scoped runs and GitHub App credentials
New flags: -org enumerates every repo of the org visible to the
token; -app-id/-app-key (+optional -installation-id) mint an App
installation token instead of -token; under an App token with no
-repo/-org the run covers the whole installation. One run aggregates
the subject's activity across all enumerated repos into a single
report, and the coverage stamp carries the token scope class.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(render): show repo count and token scope class in coverage section
The coverage section now reads "N repositories covered" and names the
credential class alongside the raw scopes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(github): drop needless empty body reader in app-token requests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(rebase): wire review/comment fixtures and restore whitespace trim
After rebasing onto main (which merged PR #14 cadence and PR #15
collaboration metrics), two gaps surfaced:
1. provider/github now fetches /pulls/{n}/reviews and /pulls/comments
for every PR in every repo; the multi-repo and org-scoped fixture
servers were missing those routes, so FetchActivity returned 404s.
Added empty-array handlers for all PR numbers exercised by those
servers.
2. resolveRepos used a plain strings.Split for the -repo flag, silently
passing leading/trailing whitespace into repo names. Ported main's
splitRepos trimming logic into resolveRepos so TestRunTrimsRepoListWhitespace
continues to pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.
Closes #4
Summary
Adds the volume/cadence context section: active days, contribution counts, and a monthly time-bucketed trend series scoped to the half-open coverage window — presented strictly as context, subordinate to collaboration metrics, with no composite score anywhere.
metrics/cadence.go— cadence concern registered into the metrics registry; flattens the subject's own timestamped events (PRs authored, reviews given, review comments written; activity received is excluded) and computes active UTC days + contribution counts.metrics/buckets.go— generic monthly bucketing over timestamped events: UTC calendar-month buckets clamped to the window (partial first/last buckets), half-open[start, end)semantics. Generic over the event series tag, so collaboration trend series can be wired after Full GitHub collaboration metrics (reviews, comments, merge timing, rework) #3 lands without touching Full GitHub collaboration metrics (reviews, comments, merge timing, rework) #3's files.report—Cadencesub-struct gainscontributionsandtrend(TrendBucket{start, end, counts}); additive only.render—40-cadence.tmplrenders cadence as a mutedsection.contextafter collaboration, with a small trend table; numbers never get the headline stat treatment. Additive CSS inlayout.tmpl, additivetotalhelper in the funcs map.cmd/coderepute/main.go— pre-approved one-line wiring: passes&result.Cadenceinstead ofnil.Acceptance criteria
TestCadenceJSONShape,TestRunEndToEnd)TestHTMLCadenceIsSubordinateContext: ordering after collaboration, context styling, no headline stat classes)metricstests (TestComputeCadenceTrendBucketBoundaries: UTC normalization of offset timestamps, partial first/last buckets, inclusive bucket starts / exclusive window until, sub-month windows)Parallel-work contract
ActivitySetuntouched; collaboration metric files, coverage/verification sub-structs, repo-enumeration/token-auth, CI YAML untouched.report/report.go(Cadence fields + TrendBucket),render/render.go(one FuncMap entry),render/templates/layout.tmpl(CSS lines appended),cmd/coderepute/main.go(the pre-approved one-liner), plus additive test cases inreport_test.go,render_test.go,main_test.go.Test output
🤖 Generated with Claude Code