Skip to content

Spec: Console Metrics view for Run metrics, Fleet trends, and failure attribution - #2080

Merged
JacobStephens2 merged 19 commits into
mainfrom
issue-2036
Nov 12, 2026
Merged

JacobStephens2 merged 19 commits into
mainfrom
issue-2036

Conversation

@JacobStephens2

@JacobStephens2 JacobStephens2 commented Nov 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

This makes Metrics a first-class Console view, twinned with metrics and /api/v1/metrics (ADR 0016, ADR 0026):

  • every signal is kept in its own units, never combined into a score;
  • Fleet aggregation is client-side, with per-machine snapshot caching;
  • a holder-bound report token lets a sleeping or NATted machine push its own summary to an always-on Console.
thirdshift metrics [<command>] [--fleet | --machine <name> | --push [<destination>]]
                   [--window 7d|30d|12w|all] [--grain day|week] [--from] [--through]
                   [--repo] [--kind] [--harness] [--model] [--version] [--account] [--json]
  └─ /metrics (nav "Metrics", key m)  ·  /api/v1/metrics (local only: refuses fleet/machine)
sequenceDiagram
    participant Laptop as Laptop VM (report token)
    participant Hub as 24/7 Console
    participant Peer as Paired Fleet peer
    Laptop->>Hub: POST /api/v1/metrics/report (after each Command, metrics --push)
    Hub->>Peer: GET /api/v1/metrics (bounded, parallel)
    Peer-->>Hub: bucketed summary → saved under logs/fleet-metrics/
    Note over Hub: Fleet view merges this machine, live peers, cached peers and reported machines (read-only)
Loading

What each Ticket added:

src/logs/metrics/
├── outcomes.rs   # #2065 Succeeded/Failed/Paused/Stopped stacks; Live apart; Pickup pass-through and skipped Passes excluded
├── scope.rs      # #2065 7d/30d daily, 12w/all weekly, from/through, preceding-window comparison
├── groups.rs     # #2066 yield and PR survival, Bug origins and Scores, Stop/Needs you, succeeded-run efficiency, friction
├── failures.rs   # #2067 Where runs failed: stopped_at and failure_category, with fallback for older or died records
├── versions.rs   # #2067 thirdshift_version per record (service.version from Session traces), transition markers
└── fleet.rs      # #2068 --fleet/--machine merge and peer snapshot cache · #2069 reported snapshots
src/console/report.rs   # #2069 console report add|list|remove, metrics --push, push after each Command
src/console/tokens.rs   # #2069 `report` scope, bound to a Fleet machine name, refused with any other scope

This review added these changes:

 after_command()
-  push default view (all-time, weekly)
+  push whole history by day      # stands in for 7d/30d views on the hub
+  then push default view
 FailedCommand / SummaryCommand
+  repository, url (pull request, else issue)   # Fleet page links an https one for cached/reported machines
 interventions.needs_you
-  Failed | Paused | Stopped
+  Failed | Paused | Stopped unless its Stop requeued
 spark()
-  lone point: "M x y"            # draws nothing
+  lone point: "M x y Lx y"       # round cap draws a dot

The metrics help line now ends in a whole sentence, and the guide lists version among the API filters.

Evidence

Execution evidence

Each review fix was written test-first:

  • Before: cargo test --test console an_automatic_push_shows_the_worker_in_daily_fleet_windows
    panicked at tests/console/metrics_report.rs:397:9:
    7d: {"comparison":{...},"machines":[...,{"address":null,"error":"no saved snapshot for ...
    test result: FAILED. 0 passed; 1 failed
    
    After: test result: ok. 5 passed; 0 failed (cargo test --test console metrics_report)
  • Before: cargo test --test console metrics_push_shows_the_worker (asserting failed["repository"] == "acme/widgets")
    panicked at tests/console/metrics_report.rs:241:5
    test result: FAILED. 0 passed; 1 failed
    
    After: cargo test --test console metrics → test result: ok. 20 passed; 0 failed
  • Before: cargo test --test metrics a_requeued_stop_is_a_stop_but_not_needs_you
    left: Object {"commands": Number(1), "needs_you": Number(1), "stops": Number(1)}
    right: Object {"commands": Number(1), "needs_you": Number(0), "stops": Number(1)}
    
    After: test a_requeued_stop_is_a_stop_but_not_needs_you ... ok
  • Before: cargo test --bin thirdshift logs::metrics::groups::tests::sparklines
    left: "M0.00 2.00 M100.00 10.00"
    right: "M0.00 2.00 L0.00 2.00 M100.00 10.00 L100.00 10.00"
    
    After: test result: ok. 3 passed; 0 failed
  • Before: cargo test --test command_surface help_says_what_metrics_push_sends
    the metrics description stops mid-sentence: ... here or across the Fleet; --push sends this machine's
    
    After: test result: ok. 21 passed; 0 failed

Whole-branch validation:

  • scripts/affected-tests main passed npm test --prefix site, cargo build --locked and npm test --prefix console.
  • cargo test --no-fail-fast passed with no failures.
  • npm run docs:check --prefix site and cargo clippy --all-targets came back clean.

One existing test is flaky on main as well as here: tests/process/findings.rs merged_fix_is_rechecked_after_a_later_fix_fails failed 8 of 10 runs on origin/main, and this branch doesn't touch it.

Visual evidence

Both pairs use the same fixture of twelve Commands over 30 days. Before is main's /metrics, which is a raw JSON dump; main has no window key, so it is shown at plain /metrics. After is /metrics?window=30d.

Desktop 1440×900, This machine:

Before
After

Mobile 390×844, This machine:

Before
After

Unaddressed findings

Standards

  • S3–S11 and S13–S16 (smells: Data Clumps, Primitive Obsession, Duplicated Code, Repeated Switches, Mysterious Name, Divergent Change, Message Chains, tests through internals, long tests). All of these are judgement calls under the review's smell baseline, and none says behaviour is wrong. They are left as is: refactoring the filter and target plumbing across args.rs, scope.rs, fleet.rs, console/metrics.rs and openapi.rs would touch every Ticket's code without changing behaviour. For S15, CODING_STANDARDS points to codebase-design. Its rules are a preference, and the same behaviours are also covered through the CLI and Console in tests/console/metrics.rs and tests/console/metrics_report.rs.
  • S18 (UTC-midnight flake risk in tests/metrics.rs). Not reproduced. The reviewer gave no failing command, since each child process restarts the clock, and the full suite passed.

Spec

  • P1 (Fleet views lack signal groups and version slicing), P4 (Fleet page Failed counts and rows don't link to History), P5 (browser tests miss Fleet selection, version markers, desktop and drilldown clicks) and P6 (no harness-version filter). Filed as Fleet Metrics: signal groups, History drilldowns and harness-version slicing across machines #2087 (needs-triage).
    • Ticket Fleet Metrics aggregation (metrics --fleet), default Fleet scope, and local peer-snapshot cache #2068's acceptance criteria scope the Fleet view to merged buckets, deltas and Where runs failed, with details links for a reachable peer, and FleetPage::details in src/console/metrics.rs provides those links.
    • Widening the Fleet and report wire format with signal groups, giving History a machine= scope, and adding a harness-version filter (which the Spec's Implementation Decisions filter list leaves out) are Spec decisions.
  • (c) An interrupted run with no Stop counts as Failed. The reviewer did not assert this is wrong. The Spec defines Stopped as "human Stop" and Failed as including crashed runs, and outcomes.rs classify follows that.

Security

  • None. A guidance-mode Security review of the change found no introduced findings. It covered the report token scope and /api/v1/metrics/report receiver, the binding of each report to its holder and its saved slot, the outbound push's address and redirect checks, escaping in the Metrics and Fleet templates, and the https-only links to reported failures.

Security review outcome

No unaddressed introduced findings.

Review coverage

Both reviewers read every changed file. site/docs/index.html was not read: it is generated from docs/guide.md by site/generate-docs.mjs, and npm run docs:check --prefix site passes. src/logs/metrics/fleet/tests.rs lines 120–415 were skimmed by test names and assertions rather than read line by line.

Merge Danger

Door: two-way

Rolling back is safe for stored data. New Command record fields (thirdshift_version, stopped_at, failure_category) are optional, older readers ignore them, and logs/fleet-metrics/ snapshots are a cache. A report token issued while this is deployed stays in the token store, though, and should be revoked if the change is rolled back.

Blast Radius: moderate

  • A new token scope and the report-token POST /api/v1/metrics/report endpoint widen the Console's API surface, though each holder writes only its own slot.
  • Each finished Command now makes up to two bounded (5 s each) outbound pushes when report destinations are configured.
  • The Metrics page now opens on the Fleet by default.
  • The metrics --json shape changes.

Closes #2036

Built with claude 2.1.314 · opus · default effort

Tickets

…ccount Commands (#2065)

Turn /metrics into a server-rendered Console page (jump m) backed by
metrics --json and /api/v1/metrics: daily or weekly buckets, 7d/30d/12w/all
presets, preceding-window comparisons with n and observed/missing counts,
per-kind Succeeded/Failed/Paused/Stopped stacks that leave out pass-through
Pickup runs and skipped Passes, kind/harness/model/account filters, and
readable Other-account Commands tagged by OS account.
… form keeps repeated filters (#2065)

Signal rows now leave out pass-through Pickup runs and skipped Passes and
count a crashed record as died, leaving missing_outcomes to live runs. The
Metrics form shows one field per repeated filter value. The outcome kind
list is shared with the page, Snapshot capture names its past-metrics flag,
and docs/code-navigation.md points at the new owners.
Console Metrics page: outcome stacks, daily/weekly windows, deltas and Other-account Commands (#2065)
…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.
Metrics signal groups: kept PR survival, post-merge quality, survivorship-safe efficiency and friction (#2066)
…drilldown (#2067)

Command records keep thirdshift_version at start and stopped_at with a
failure_category (held, waiting, crashed, stage_failed) at a failure
ending. Older records recover their version from Session traces through
the retained Session-signals cache and fall back to their last stage and
died or failed. metrics and /metrics filter by --version / version=,
break outcomes down by version, mark version (and, when narrowed,
Harness and Model) transitions on the timeline, and group Failed
Commands as Where runs failed, linking each cluster and Failed bucket to
History filtered to those Commands and each name to its details.
…he view, failed_commands in JSON, Ending type (#2067)
Record thirdshift version and failure attribution, with a Where runs failed drilldown (#2067)
… trimmed to the window, another window's snapshot stands in, a machine named fleet is choosable (#2068)
… whose read saved it (#2068)

A peer refuses a Console person missing from its sign-in list, but once it
went offline or was removed, its saved summary showed to every viewer.
Snapshots are now kept per machine and reader.
Fleet Metrics: metrics --fleet and --machine, Fleet as the Metrics page default, and saved peer snapshots (#2068)
…--push, a push after each Command, and reported machines read-only on Metrics (#2069)
…ch snapshot slot's merge, share the whoami probe, list writer and view keys, name person scopes (#2069)
Outbound Fleet reporting: report token scope, metrics --push, push after each Command (#2069)
…lures carry repository and link, requeued Stops leave Needs you, lone sparkline points draw, help line ends whole (#2036)

- After a Command, push the whole history by day before the default view,
  so a reporting machine appears in 7d and 30d Fleet windows too.
- Failure summaries carry the repository and the pull request's or issue's
  URL; the Fleet page links an https one for cached or reported machines.
- A Stop that requeued its issue counts as a Stop but not as Needs you.
- A sparkline point without neighbours draws a dot, not a bare moveto.
- `metrics` help ends in a whole sentence; the guide lists `version` among
  the API filters, with an API/CLI parity row for it.
@JacobStephens2
JacobStephens2 marked this pull request as ready for review November 12, 2026 08:59
@JacobStephens2
JacobStephens2 merged commit 5335a4e into main Nov 12, 2026
24 checks passed
@JacobStephens2
JacobStephens2 deleted the issue-2036 branch November 12, 2026 09:10
@JacobStephens2 JacobStephens2 mentioned this pull request Nov 12, 2026
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.

Spec: Console Metrics view for Run metrics, Fleet trends, and failure attribution

1 participant