Files
werkregie/doc/prs/2026-10-14-PR#78-merge-stats.md
claude-code 14f72372cd
werkator build succeeded after 00:56
werkator/deploy build succeeded after 00:24
The merges per day, week and month of the configured authors (#78)
Co-authored-by: claude-code <claude-code@javagil.de>
2026-10-14 19:58:19 +02:00

161 lines
8.5 KiB
Markdown

# PR#78: The Merges per Day, Week and Month of the Configured Authors
> **WARNING:** This document describes only the change applied in this PR.
> It may already be outdated once the next PR is merged.
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
## Problem
The human wants to see how many pull requests reach the default branch per day, week and month:
those of the human and those of `claude-code`, on git.javagil.de and on dev.hostsharing.net.
The dashboard reads only the open pull requests and the last 50 activities of the token user;
it keeps no history of merges.
Gitea has the data: its issue search finds the closed pull requests of an author (`created_by`),
each with `merged` and `merged_at`, and the pull request itself names its base branch and the default branch of its repository.
## Non-Goals
- No pull requests of all authors; only the configured ones count.
- No other measures, e.g. the time from opening to merge.
- No storage of its own; Gitea holds the history.
- No merges in the public demo; see Follow-up PRs.
## Specification
### Feature: the merges per day, week and month of the configured authors
#### Scenario#78.01: a merge of a configured author counts on its day
&hellip; so that the human sees how many pull requests reach the default branch.
- **Given** a Gitea with `mergeAuthors: [mi, claude-code]`
and a pull request of `claude-code`, merged into the default branch today
- **When** the human opens the dashboard
- **Then** the merge chart of that Gitea counts it today for `claude-code`
##### Verified by
- [MergeHistoryTest "the merges of each configured author, oldest first, in seconds since the epoch"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
- manual check in the browser, see "Solution"
- [DashboardTest "carries the load, the costs and the merges the sources report"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/DashboardTest.kt)
#### Scenario#78.02: the authors stack in each bar
&hellip; so that the human sees the share of each author.
- **Given** a Gitea with `mergeAuthors: [mi, claude-code]`, and on one day 2 merges of `mi` and 3 of `claude-code`
- **When** the human opens the dashboard
- **Then** the bar of that day stacks 2 for `mi` on 3 for `claude-code`, each in its own colour,
and the legend names the authors
##### Verified by
- [MergeHistoryTest "the merges of each configured author, oldest first, in seconds since the epoch"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
- manual check in the browser, see "Solution"
#### Scenario#78.03: the human switches between days, weeks and months
&hellip; so that the human sees the pace over a short and a long time.
- **Given** the merge chart, showing the last 30 days
- **When** the human selects "Wochen"
- **Then** the chart shows the last 12 weeks, from Monday to Sunday, and keeps this choice on the next visit
##### Verified by
- [MergeHistoryTest "the search starts a day before the first of the oldest month shown, for a time zone ahead of UTC"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
- manual check in the browser, see "Solution"
#### Scenario#78.04: only a merge into the default branch counts
&hellip; so that a stacked pull request does not count twice.
- **Given** a pull request of a configured author, merged into the branch of another pull request
- **When** the human opens the dashboard
- **Then** the merge chart does not count it
##### Verified by
- [MergeHistoryTest "only a merge into the default branch counts, and a closed pull request without a merge not at all"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
- [MergeHistoryTest "a pull request without a base or a default branch does not base on the default branch"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
#### Scenario#78.05: a closed pull request without a merge does not count
&hellip; so that the chart counts delivered work only.
- **Given** a pull request of a configured author, closed without a merge
- **When** the human opens the dashboard
- **Then** the merge chart does not count it
##### Verified by
- [MergeHistoryTest "only a merge into the default branch counts, and a closed pull request without a merge not at all"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt)
#### Scenario#78.06: without `mergeAuthors` the token user counts
&hellip; so that an existing configuration shows the merges of its user without a change.
- **Given** a Gitea without `mergeAuthors`, with the token of `hsh-michaelhoennig`
- **When** the human opens the dashboard
- **Then** the merge chart of that Gitea counts the merges of `hsh-michaelhoennig`
##### Verified by
- [GiteaSourceFetchTest "without mergeAuthors the merges of the token's user count, with an empty list none"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/GiteaSourceFetchTest.kt)
## Solution
- `GiteaConfig.mergeAuthors`: the logins whose merges count; without it the token user, an empty list shows no chart.
- Each 10 minutes, `GiteaSource` searches the closed pull requests of each author, updated since the start of the
oldest month shown, and keeps those with a `merged_at` in that range;
[MergeHistoryTest "a merge before the oldest period does not count, though the pull request was updated later"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt).
It runs in a thread of its own, so that the first read after a start does not hold up the open pull requests.
For each of them it asks the pull request once whether its base is the default branch of its repository,
and keeps the answer, as a merged pull request no longer changes.
- The snapshot carries the time of each merge in seconds since the epoch;
the page counts them by the days, weeks from Monday and months of the browser's time zone.
The server would need `journal.zone` from `Main.kt`, whose old methods are above a Mutation-CRAP of 15
and would fail the gate of this branch; the human chose the time zone of the browser.
The search starts a day before the first of the oldest month in UTC, so that a zone ahead of UTC gets its first day too.
- The page shows a tile per Gitea next to the review costs:
a bar per period, the authors stacked, a switch between 30 days, 12 weeks and 12 months.
- Each merged pull request is asked once, so the first read after a start costs a request per merge;
the search reads at most 20 pages of 50 per author.
[MergeHistoryTest "the search reads page after page until one is not full"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt) and
["each merged pull request is asked once for its base, a failed answer again"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/sources/MergeHistoryTest.kt) verify both.
- Version 0.49.0.
### Manual Check
On 2026-10-14, a local dashboard with `mergeAuthors: [mi, claude-code]` for git.javagil.de
and without `mergeAuthors` for dev.hostsharing.net counted:
| Gitea | Author | 12 months | 30 days |
|----------------------|----------------------|-----------|---------|
| git.javagil.de | `mi` | 188 | 127 |
| git.javagil.de | `claude-code` | 247 | 247 |
| dev.hostsharing.net | `hsh-michaelhoennig` | 122 | 53 |
The tile showed the bars stacked by author in desktop width, and in phone width in the dark theme;
the switch to months stayed after a reload.
## Open Questions
- [x] In which time zone do the days begin?
In the browser's; see Solution.
## Additional Changes
- `stateDir` of `Config.kt` takes `XDG_STATE_HOME` as a parameter and gets a test,
as this branch changes `Config.kt` and the method was at a Mutation-CRAP of 72:
[ConfigLoaderTest "the state directory: the configured one, with ~ for the home directory, else the user's XDG state"](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/ConfigLoaderTest.kt).
- The example configurations name `mergeAuthors`.
## Follow-up PRs
- The demo data get merges, so that the public demo shows the chart.
`Demo.kt` stays unchanged: its method `snapshot` is above a Mutation-CRAP of 15.
- A test framework for the JavaScript of `index.html`, e.g. for the counting by day, week and month of `mergeSeries`.
werkregie has none yet; it is a new technology and needs the consent of the human.