Weekly adoption metrics¶
The team's weekly numbers are defined once, in flightdeck: flightdeck docs/metrics.md (Sean-Koval/flightdeck#6). This page does not restate them. It says what hangar records, how hangar computes the same numbers for the standup line, and where the two differ.
| Metric | Definition | hangar's field in GET /api/metrics/weekly |
|---|---|---|
| Agent PR yield | #yield | yield (merged, terminal) |
| Agent share of merged PRs | #agent-share | agent_share |
| Review wait | #review-wait | review_wait (p50, p90 hours) |
| Green to merge | #green-to-merge | green_to_merge (p50, p90 hours) |
| Human steers per merged PR | #steer-rate | steer_rate (corrections once steers are classified, #162) |
| Abandon rate | #abandon | abandon_rate (by_reason, unlabelled) |
| 14-day revert rate | #revert | revert_rate (the cohort merged two weeks earlier) |
| Cost per merged PR | #cost-per-merged-pr | cost_per_merged_pr |
| Active delegators | #active-delegators | active_delegators (value of of) |
| Reviewer attention per merged agent PR | hangar's own (KAN-70): reviewer attention | reviewer_attention |
hangar follows flightdeck's conventions:
- Weeks are ISO weeks in UTC.
- An agent job is delegated work. Experiment trials and review jobs are not agent jobs.
- Terminal means merged, closed unmerged, abandoned, or (with no PR) ended.
- Counted means the job ran a turn and its last run isn't
infra_error. Infra errors are reported asexcluded_infra_error.
hangar computes no intervals and no trends. Flightdeck does that over the outbox's Parquet. The team reads flightdeck's report (reports/weekly/latest.json, flightdeck.weekly/v1) when it exists. hangar's numbers exist so the standup line works from week 1, and with flightdeck off.
What hangar records for them¶
On job.landing (landing.md):
| Field | Meaning |
|---|---|
opened_at |
When hangar opened (or found) the PR. |
ready_at |
When the PR was opened non-draft or marked ready. Review wait starts here. |
first_review_at, first_review_by |
The first review a human submitted, in any state. Bots never count, including the App's own AI review (#161) (reviewtrust.is_bot). |
merge_commit_sha |
What the merge put on the base branch (a squash merge's own commit). |
abandon_reason, abandon_by, abandon_at |
Why a closed PR was abandoned: one of wrong_approach, bad_ticket, flaky_env, too_big, superseded, other. |
reverted_at, revert_sha |
A commit on the default branch reverts merge_commit_sha: Revert "…" / This reverts commit <sha>. |
fix_forward_pr, fix_forward_at |
A later merged PR whose body says Fixes-forward: #N. |
review_activity |
The merged PR's author, commit authors, reviews and review comments, read once by the nightly pass. |
Abandon reasons. A trusted person comments /hangar abandon <reason> on the agent PR, or adds the label agent-abandon:<reason>. hangar refuses an unknown reason and replies with the list. The inbox lists agent PRs closed without a reason in the last week (kind abandon), and so does the digest (#158). Each reason is audited (landing.abandon_reason).
The nightly pass runs after 03:00 in HANGAR_STANDUP_TIMEZONE, on the control plane, with the control token, read-only. For each repo in HANGAR_GITHUB_REPOS it does three things:
- It caches every PR merged in the last 37 days, humans' and bots' included. This is the denominator of agent share.
- It scans the last 14 days of the default branch for reverts of merged agent PRs' merge commits. It matches GitHub's
merge_commit_sha, so squash merges work. It also reads merged PRs forFixes-forward:. - It reads the reviews and review comments of each agent PR merged in the last 37 days, once per PR.
Outbox job events (outbox.md) carry these to flightdeck:
pr.ready(ready_at).- The first human review on
review.first(first_review_at,first_review_by). revert(sha), for reverts the nightly pass finds, including those pushed straight to the branch.abandonwith the reason when one is set on a closed PR.
Reviewer attention¶
Reviewer attention is the scarce resource, so hangar counts what agent PRs cost reviewers.
| Primary measure | events_per_merged_pr: review events on the week's merged agent PRs, divided by the merged agent PRs whose reviews were read (measured). Also p50, p90, unreviewed and reviewers. |
| A review event | A submitted review (any state) or a review comment on the diff. Conversation comments don't count. |
| Left out | Bots (type: Bot or a [bot] login) and hangar's own AI review. Also the agent's account: one the PR's commits are attributed to, unless it opened the PR or is linked to a hangar person. |
| The PR's author | Counts as a reviewer. hangar often opens PRs with the delegator's own account, and their reviews are human attention. |
| Secondary measure | active_minutes: active review minutes. Always null with a note today: GitHub records when a review was submitted, not how long it took. |
| Data source | The GitHub API: pulls/N, pulls/N/commits, pulls/N/reviews and pulls/N/comments, read by the nightly pass into landing.review_activity. |
| Until it is read | A merged PR the nightly pass hasn't read yet is left out of the rate, and note says so. |
The definition is the ticket owner's default and may change. It lives in one place, weekly.attention, over the raw events kept. Take a repo's baseline before hangar works there.
Where hangar's numbers differ from flightdeck's today¶
- Review wait. hangar measures
ready_at(elseopened_at) to the first human review in any state. Flightdeck measurespr.opento the first counting approval until it switches to thepr.readyand first-human-review fields above (flightdeck#6). - Revert. hangar sees reverts pushed straight to the branch. Flightdeck reads PR text until it reads the
revertevents. - Cost per merged PR. hangar adds a merged job's linked review job (
landing.ai_review.job, #161) once that link exists.
Reading them¶
hangar metrics weekly [--week 2026-W43] [--repo owner/repo] [--person P] [--json]
hangar metrics standup # the line as it would be posted now
GET /api/metrics/weekly?week=2026-W43&repo=…&person=… returns hangar.metrics.weekly/v1, and GET /api/metrics/standup returns the line. Team and repo numbers are open to any person's token. A person= query answers only for that person and admins (403 otherwise), and the standup line is team-level only.
The standup line¶
Set HANGAR_STANDUP_CHANNEL to a Slack channel id (the bot, HANGAR_SLACK_BOT_TOKEN_CREDENTIAL, must be in it).
hangar then posts once per weekday at HANGAR_STANDUP_TIME (default 10:18) in HANGAR_STANDUP_TIMEZONE (default America/New_York). It posts from the control plane, so the line goes out even when the pools are late. A line not posted by 13:00 is skipped for the day. The line looks like this:
Yesterday: 14 agent PRs merged, 3 abandoned (2 flaky_env, 1 unlabelled), review wait p50 2.1 h, 5 jobs held for review capacity, top failure: verify_failed
On Mondays the line covers last week (UTC) instead and adds yield, active delegators and review events per merged PR. The line also says how many new jobs review-aware admission (#159) holds for review capacity. Unset the channel to stop the line.