Skip to content

Metrics signal groups: kept PR survival, post-merge quality, survivorship-safe efficiency and friction (#2066) - #2081

Merged
JacobStephens2 merged 2 commits into
issue-2036from
issue-2066
Nov 12, 2026
Merged

JacobStephens2 merged 2 commits into
issue-2036from
issue-2066

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

metrics --json, /api/v1/metrics and the Console Metrics page now carry four paired signal groups. All three surfaces read the same document, built from local files only: Command records, github-inbox.json (read from the file alone, with no quota check and no gh), and the Scoring checkpoints.

 trends()
   facts = outcomes::facts(snapshot)           # each counted Command + its Stop request
   outcomes::read(facts)                       # existing stacks and comparison
+  groups::read(facts, window, Sources {
+      origins, kept_pull_requests(logs_dir), read_scores(), filters })
+    yield.pull_requests      merged / closed / open / not kept, survival rate
+    quality.bug_origins      merged PRs by merge bucket, confirmed Bug origins
+    quality.scores           weekly Grader pass rates, shown once calibrated (blind Scores withheld)
+    quality.interventions    Needs you and Stop counts
+    efficiency               Succeeded-only minutes/Repairs/CI rounds, first-pass clean,
+                             minutes and $ per success, wasted time/spend share (by Harness and Model)
+    friction                 % stages above Usual range, per-Session friction, Security holds
   weeks|days rows            repairs / ci_rounds / total_minutes now cover Succeeded only

Each group line holds its whole-window totals, a value for each bucket (drawn as a static SVG sparkline) and, for a bounded window, current/prior/delta. The Change column also shows each window's n. Raw Standards and Spec finding counts appear in no group; only security > 0 holds do.

Evidence

Execution evidence

  • Before (review findings, on 495bc84):

    • cargo test --test console metrics_page_heads_the_yield_group_without_a_bounded_window → panicked at tests/console/metrics.rs:296:5 … test result: FAILED. 0 passed; 1 failed
    • cargo test --bin thirdshift logs::metrics::scope::tests → panicked at src/logs/metrics/scope.rs:256:9 … FAILED. 2 passed; 1 failed
    • cargo test --test metrics confirmed_bug_origins_show_without_a_kept_answer → FAILED
    • cargo test --test console metrics_page_heads_the_yield_group_when_unbounded → FAILED

    After (6cd1945):

    • cargo test --test metrics → test result: ok. 44 passed; 0 failed; 1 ignored
    • cargo test --test console metrics → ok. 9 passed; 0 failed
    • cargo test --bin thirdshift metrics → ok. 14 passed; 0 failed
  • Acceptance tests (tests/metrics.rs), all pass:

    • fast_failures_and_crashes_raise_waste_without_flattering_succeeded_means: after 2 fast failures and a crash, the Succeeded means are unchanged, failures rise to 3, and both wasted shares rise.
    • kept_pull_requests_bug_origins_and_calibrated_scores_read_no_github: gh_calls() is empty.
    • stage_bottlenecks_friction_and_interventions_compare_with_the_preceding_window.
  • Parity: tests/console/api.rs still checks that /api/v1/metrics equals metrics --json for every window. metrics_page_shows_the_four_signal_groups_as_the_api_reports_them checks the page against the API.

  • scripts/affected-tests origin/issue-2036:

    • npm test --prefix site: PASS
    • cargo build --locked: PASS
    • npm test --prefix console: PASS. The Metrics browser test checks all four headings and sparkline strokes in every Theme at 390px, with no motion.
    • cargo test: FAIL, only in tests/process.rs findings::*. Which test fails changes from run to run (merged_fix_is_rechecked_after_a_later_fix_fails, fixing_dispatches_each_reproduced_finding_…), and they pass alone. The same binary fails the same way on the base commit: in a temporary worktree at origin/issue-2036, cargo test --test process findings:: -- --test-threads=4 gave FAILED. 17 passed; 1 failed. Every test target after process passed with cargo test --no-fail-fast --test <each>.

Visual evidence

I made captures at 1440×900 and 390×844 of /metrics?window=7d with the same fixture: 42 Ticket and Review runs, kept PR states, and Session signals. Publishing them to a secret Gist was refused by this session's permission settings, so they are not embedded here. They are on the build machine at /tmp/before-desktop.VuQdxp.png, /tmp/after-desktop.PLooVg.png, /tmp/before-mobile.1aNEdW.png and /tmp/after-mobile.JO7U1Z.png.

  • Before: Outcomes, plus one Signals table with outcome-blind means.
  • After: four group sections, each with sparkline and Change columns. At 390px the tables scroll inside their regions and the page does not overflow.

Merge Danger

Door: two-way

The change is read-only. Nothing is written to records, github-inbox.json or the Scoring checkpoints.

Blast Radius: consumers

  • The weeks/days rows' repairs, ci_rounds and total_minutes now cover Succeeded Commands only. A consumer that relied on outcome-blind means will see different numbers.
  • groups is a new required field in the OpenAPI document.

Review coverage

  • Reports: .thirdshift-review-QXR9uO/standards.md and spec.md. Counts: Standards 11, Spec 6.
  • Excluded as generated: site/docs/index.html, whose source is docs/guide.md; npm run docs:check --prefix site passes.
  • Changed files the review left unread: src/logs/reading.rs, src/logs/reading/attribution.rs and docs/code-navigation.md. These changed only in the review-fix commit 6cd1945, which shares the pull-request URL identity and updates the code-navigation rows.

Unaddressed findings

Standards

  • 1 (part): needs_you is narrower than the glossary's Needs you. I renamed the "attention" group to interventions. I kept the narrower count on purpose: Q1.4 in docs/grill-with-docs/2036-console-metrics.md leaves live operational state (Needs you's Silent and Slow rows) on the existing Inbox pages. docs/guide.md now says the count covers Commands that entered Needs you when they ended.
  • 6 (part): kept_pull_requests repeats load's decode, and Store::scores repeats measure's pair closure.
    • load also asks GitHub::quota_waits(), and the issue requires that metrics make zero GitHub calls, so the file-only read is the point of the separate function. I did fix the legacy row-only History keys this finding exposed: kept_pull_requests now skips keys without ::.
    • The two pair closures build pairs from different types (the full Week and the narrow Sampled projection), and both now feed the one shared calibration::measure_pairs.
  • 8: message chains into command.record.run_metrics. These follow how the existing reader in src/logs/metrics/trends.rs (collect) walks the same fields. Moving them onto Command would be a refactor outside Survivorship-safe efficiency, session friction, kept PR survival, and post-merge quality signals #2066.
  • 9: attempt_minutes reads the filesystem, and trends() calls read_scores() directly. This matches the existing ConfirmedCounts::read in src/logs/metrics/bug_origins.rs, which calls crate::scoring::read_confirmed_origins() the same way. The reviewer rated it minor.

Spec

  • 2: attempt time is missing for ended Commands without Run metrics. The reviewer's own test passes as written: cargo test --test metrics a_failed_command_without_run_metrics_still_counts_its_attempt_time → test … ok. Snapshot reading already rebuilds run_metrics (with total_minutes from ended_at) for every ended record that lacks them; see src/logs/reading.rs RunMetrics::historical. The test stays in the suite.
  • 5: no single fixture runs through all three surfaces. tests/console/api.rs (the parity(...) loop over /api/v1/metrics windows) already asserts that the API equals metrics --json for the whole document, groups included. metrics_page_shows_the_four_signal_groups_as_the_api_reports_them then checks the page against the API.

Closes #2066

Built with claude 2.1.314 · opus · default effort

Security review outcome

No unaddressed introduced findings.

…ship-safe efficiency and friction (#2066)

Add four paired signal groups to metrics --json, /api/v1/metrics and the
Console Metrics page: per-kind kept pull-request survival read from
github-inbox.json alone, confirmed Bug origins by merge, calibrated weekly
Scores pass rates, Needs you and Stop counts, Succeeded-only duration,
Repairs and CI rounds with first-pass clean rate, time and spend per
succeeded delivery by Harness and Model and wasted share, and stages above
their Usual range with per-Session friction and Security holds. Each line
carries per-bucket values for a static sparkline and, when bounded, its
change from the preceding window. Setup rows' means now cover Succeeded
Commands only.
…Scores and merged cohorts in daily windows, n beside deltas (#2066)

Open the Yield group whatever the window, filter Scores as exactly as
Commands, bucket a Scoring week by its last day, date an unkept merged
pull request by its Command's end, show each window's n with the changes,
name intervention counts as the glossary allows, give headline values a
unit, share the pull-request URL identity and a template macro, and point
code navigation at the new owners.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant