Agents Builders

Fix deployment duration metrics

Shipped
fix-deployment-duration-metrics

Created

Jul 07, 22:30

Started

Jul 07, 22:31

Completed

Jul 08, 07:12

DevOps handoff

Type

Feature

Shape

ui+db

Worktree Slug

fix-deployment-duration-metrics

Repositories

mcritchie-studio

Release Slug

Branch

feat/fix-deployment-duration-metrics

QA URL

Production URL

dashboard,ui,metrics,data-quality

Acceptance Criteria

  • Table cells show per-stage timestamp range and duration
  • New tested_at stamp via migration and QA-release conductor
  • Active stage shows live count-up from stage start
  • Columns Tested Assembled Confirmed Deployed Total Tasks
  • Timeline SOPs and docs updated for tested stage

Expected Test Plan

  • unit

Checks Run

  • [unit] release_test: deployment_stage_span/total/averages + review_tests stamps tested_at
  • [unit] application_helper_test: deployment_clock/range + precise_stage_duration
  • [component] deployment_stage_cell (completed/in-progress/missing) + 5-tile card
  • [integration] releases_controller per-stage columns + timestamps render
  • [e2e] release_duration_dashboard.spec aligned to 5 tiles (full-suite)
  • [full-suite@d62d614a6aa5d96e9c9d1a80f2bd4c54aba80ddd] bin/rails test green
  • [rubocop@d62d614a6aa5d96e9c9d1a80f2bd4c54aba80ddd] bin/rubocop clean

Agent Context

Fixes /deployments/all (ReleasesController#index + app/views/releases/index.html.erb) and the /deployments card (app/views/tasks/_release_duration_card.html.erb). Root problems: (1) DEPLOY column is mislabeled — it reads deployment_seconds = created_at->shipped_at, i.e. the TOTAL, not the deploy; (2) the real prod-deploy stamp prod_deploy_started_at->shipped_at is unused; (3) summary cards (BUILDING/REVIEWING/ASSEMBLING/SHIPPING) come from per-task task_events spans while columns come from per-release stamps — two engines, same labels, different numbers; (4) active releases fall back to live_seconds (created->now) inflating DEPLOY (an 8h in-progress next to shipped 15-55m). Operator chose 'release-centric & consistent': cards AND columns both derive from releases.* stamps. New release_metrics: assembly_seconds(created->assembled)=ASSEMBLE, confirmation_seconds(assembled->confirmed)=CONFIRM, deploy_seconds(prod_deploy_started->shipped, fallback confirmed->shipped)=DEPLOY, deployment_seconds(created->shipped)=TOTAL. averages_from rebuilt to average those four release-level fields over recent shipped releases (bump limit 3->10 for stability). Active releases: TOTAL renders live elapsed with a distinct muted 'running' treatment, not a comparable total. Also tighten compact_stage_duration to compound units (1h30m) to stop hour-boundary truncation. Key files: app/models/release/duration_cache.rb, app/views/releases/index.html.erb, app/views/tasks/_release_duration_card.html.erb, app/helpers/application_helper.rb.

Stage Timeline

Who handled each stage, the time it took (measured), and the model / tokens / cost reported (best-effort) — plus who's on it right now. means the agent didn't report that metric.

Sizing Avi · PO SMALL Dev SMALL Actual XL ≠ forecast
  1. Created Designed
    S Squirtle
    Squirtle
    Model
    Duration
    Tokens
    Cost
    Completed Jul 07, 22:30 · about 15 hours ago
    api
  2. Designed Building
    S Squirtle
    Squirtle
    Model
    claude-opus-4-8
    Duration
    under a minute
    Tokens
    666,657
    Cost
    ~$0.53
    Started Jul 07, 22:30
    Completed Jul 07, 22:31 · about 15 hours ago
    cli
  3. Building Submitted
    W Wartortle
    Wartortle
    Model
    claude-opus-4-8
    Duration
    about 7 hours
    Tokens
    295,253,598
    Cost
    ~$192.18
    Started Jul 07, 22:31
    Completed Jul 08, 05:30 · about 8 hours ago
    cli
  4. Submitted Blocked
    A Avi
    Avi
    Model
    claude-fable-5
    Duration
    20 minutes
    Tokens
    Cost
    Started Jul 08, 05:30
    Completed Jul 08, 05:50 · about 8 hours ago
    cli
  5. Blocked Submitted
    W Wartortle
    Wartortle
    Model
    claude-opus-4-8
    Duration
    13 minutes
    Tokens
    57,883,345
    Cost
    ~$32.09
    Started Jul 08, 05:50
    Completed Jul 08, 06:04 · about 7 hours ago
    cli
  6. Submitted Reviewed Review Activity
    S Shannon
    Shannon primary
    C Carl
    Carl light
    Model
    claude-fable-5
    Duration
    12 minutes
    Tokens
    882,526
    Cost
    ~$1.30
    Started Jul 08, 06:04
    Completed Jul 08, 06:16 · about 7 hours ago
    cli
  7. Evolve
    W Wartortle
    Wartortle
    B Blastoise
    Blastoise
    triggered by
    S Shannon
    Shannon
    Model
    Duration
    12 minutes
    Tokens
    Cost
    Started Jul 08, 06:04
    Completed Jul 08, 06:16 · about 7 hours ago
  8. Reviewed Assembled
    S Steffon
    Steffon
    Model
    claude-fable-5
    Duration
    40 minutes
    Tokens
    5,426,052
    Cost
    ~$9.94
    Started Jul 08, 06:16
    Completed Jul 08, 06:56 · about 7 hours ago
  9. Assembled Shipped
    A Avi
    Avi
    Model
    claude-fable-5
    Duration
    15 minutes
    Tokens
    2,986,704
    Cost
    ~$7.21
    Started Jul 08, 06:56
    Completed Jul 08, 07:12 · about 6 hours ago

Conversation

QA review feedback, agent handoffs, and follow-up notes for this task.

Comment carl about 8 hours ago

Scout report: merge-ready - Backend light read clean: reversible nullable tested_at migration, best-effort conductor stamp bracket, nil-safe span math, honest legacy-row handling Reporter: carl Outcome: merge-ready Findings: - deployment_span handles all stamp states: legacy shipped rows w/o tested_at read missing/em-dash and drop out of averages via filter_map (per-stage sample_count honest) - Tracker safe: RELEASE_TRACKER_STAGES is name-keyed, tested inserts into STAGES without adding a node; dev advance toy skips it - record_release_event rescues SystemExit+StandardError so a board outage cannot abort prepare; tested not in STAGES_THAT_MAY_OPEN so no ghost RC - Non-blocker: reopen! keeps tested_at (first-write-wins), so a re-prepared RC keeps the first test run's Tested span — consistent with kept testing/assembling starts - Non-blocker: releases/show Stage Averages still reads legacy DurationCache; detail page can disagree with new table until migrated Questions: - none Checks: - gh pr view 453: OPEN, not draft, base release, CI green (test/lint/scans) - e2e Release Steps heading matches current origin/release

Comment shannon about 8 hours ago

Scout report: request-changes - Deep review: model/migration/conductor wiring, live ticker, docs all solid; certs verified at PR tree; one data-quality defect in the new averages chart (nil average renders as <1m with a bar instead of the em-dash the sibling partials use) Reporter: shannon Outcome: request-changes Findings: - BLOCKER _deployment_average_chart.html.erb:25 — stages.dig(key,'average_seconds').to_i coerces nil→0 → label '<1m' + 2%-floor bar for a stage with NO samples; post-deploy every shipped release lacks tested_at so both charts show a false 'Tested <1m' while the pinned average rows/tiles on the same page correctly show '—'. Fix: keep nil, render '—' and skip the bar (add a helper-test case). - MERGE-SEQUENCING: PR #454 inserts helpers at the same application_helper.rb anchor (after compact_stage_duration) and same test file region — whichever merges second conflicts; #454 also carries the OLD release_duration_label body as context, resolve toward #453's precise version - NIT: deployment_clock (this PR) duplicates #454's clock_12h — consolidate post-merge - NIT: Release#deployment_span is public but internal-only; unknown key in deployment_stage_span raises NoMethodError on nil rather than ArgumentError Questions: - none Checks: - gh pr view: OPEN, not draft, base release, CI green (scan_ruby/scan_js/lint/test), MERGEABLE CLEAN - cert tree verified: origin/feat/fix-deployment-duration-metrics^{tree} = e9e1645... = full-suite+rubocop fingerprints (dor-check STALE = primary-checkout false positive) - ticker contract verified: data-prefix honored in default mode; range-script clock matches deployment_clock noon/midnight - no dual render path: DeploymentsBroadcaster only re-renders current_release/last_release/task_card; Last Release empty-state untouched

QA Feedback avi about 8 hours ago

BLOCKING (shannon primary; carl light approved the model/conductor layer, which is nil-safe - the defect is in the view): app/views/releases/_deployment_average_chart.html.erb:25 - stages.dig(stage[:key], "average_seconds").to_i coerces a nil average to 0, and line 26's 2%-floor draws a colored bar, so the chart renders a false "Tested <1m" for every pre-migration release (all currently-shipped releases have nil Tested spans since tested_at is brand new). The pinned average rows and summary tiles on the same page correctly pass nil through release_duration_label and show an em-dash, so the dashboard contradicts itself on the exact surface this data-quality task exists to fix. FIX: keep nil (drop the .to_i coercion), render the em-dash, skip the bar for nil averages, and add one helper/component test covering the nil-average case. AT RESUBMIT: post bin/task note --resolves-feedback (PR #453 is not draft; no other gate work needed). CARRY AS NON-BLOCKERS: (1) merge-sequencing with PR #454 - both PRs insert helpers at the same application_helper.rb anchor after compact_stage_duration; whichever merges second conflicts, and resolution must keep this PR's precise_stage_duration body; (2) deployment_clock here near-duplicates #454's clock_12h - consolidation follow-up once both land; (3) reopen! keeps the first run's tested_at (consistent with keep-true-origin doctrine, noting only); (4) releases/show Stage Averages still reads legacy DurationCache and can disagree with the new table until migrated. Everything else verified clean: dor-check STALE cert was a confirmed false positive (branch tree equals recorded fingerprint e9e164569040b6c7e7f4fd57119d13ab9c0feba9), migration reversible, conductor wiring correct and rescue-guarded, tracker monotonic-safe, CI fully green. This is a one-line-plus-test rework; resubmit should be quick.

Comment about 7 hours ago

Resolved Shannon's block (chart false <1m): the average chart no longer coerces a nil per-stage average to 0 via .to_i. nil now renders a zero-width bar + em-dash value; the 2%-floor applies only to real non-nil values. A stage with no completed samples (e.g. Tested on pre-migration releases) reads em-dash, not a phantom <1m bar. Added a regression test for the nil-average case. Re-certified green at d62d614a: 3105 runs, 0 failures, rubocop clean, DoR met.

Comment carl about 7 hours ago

Scout report: merge-ready - LIGHT re-review (Carl): prior blocker fix verified — chart partial keeps nil averages nil (zero-width bar + em-dash, 2%-floor only for real values) with a regression test; model/conductor layer byte-identical since prior approval; cert tree d62d614a matches branch head tree; CI 4/4 green including test lane. Reporter: carl Outcome: merge-ready Findings: - Blocker fix present+tested: _deployment_average_chart.html.erb no longer coerces nil via .to_i; release_duration_label(nil) renders em-dash; regression test asserts width:0% + no false 2% bar - Delta since prior review is exactly 2 files (chart partial + helper test); release.rb, bin/release.rb, migration, controllers untouched — no regression in the approved model/conductor layer - Non-defect verified: partial coerces max_seconds via .to_f internally, so pct math is float division despite integer chart_max at call site - Nit (non-blocking): new ERB comment contains % chars (2%-floor) inside <%# %> body — parses fine (no %> sequence) and suite is green, but house comment rules discourage it Questions: - none Checks: - git diff 5c6d5296..82e107d4 --stat = chart partial + application_helper_test only - branch head tree d62d614a6aa5... == claimed cert fingerprint (tree equality, not stale) - gh pr checks 453: lint, scan_js, scan_ruby, test all pass at head 82e107d4

Comment shannon about 7 hours ago

Scout report: merge-ready - Resubmission fixes the blocker exactly as required: the average chart keeps nil per-stage averages (no .to_i), renders a zero-width bar + em-dash, and floors to 2% only for real values; a component regression test pins the nil path (asserts width:0%, no width:2%, em-dash). Cert fresh by tree equality (branch tree d62d614a == full-suite/rubocop fingerprints); CI fully green at head 82e107d4 including the test lane (4m18s). Deep review of the full diff (model spans, averages, conductor review_tests bracket, table/card/chart views, docs, tests) found no new blockers. Reporter: shannon Outcome: merge-ready Findings: - Blocker RESOLVED: _deployment_average_chart.html.erb keeps nil (average_seconds not coerced), pct=0 for nil so no bar, release_duration_label renders em-dash; regression test 'no-data stage as an em dash with a zero-width bar' asserts the exact failure mode - dor-check STALE from the primary checkout is a false positive (fingerprint = write-tree of cwd); branch tree d62d614a6aa5d96e9c9d1a80f2bd4c54aba80ddd equals both recorded evidence fingerprints — cert valid for the PR code; all other DoR gates pass - NIT (non-blocker): new ERB comment in the chart partial contains a % char ('2%-floor') — violates the house zero-percent-chars-in-ERB-comments rule; harmless here (CI+component tests prove post-comment content renders) but reword to '2 percent floor' - NIT (non-blocker): chart stage labels are text-white overlaid at the bar's left edge; for zero/short bars in LIGHT mode the white 11px label sits on the light surface with only a text-shadow — marginal legibility; consider text-heading when pct is small - PROCESS NIT: resolution note (activity 1237) did not clear the red feedback badge — task API still returns block note 1224 as unresolved_feedback; builder should post/note with --resolves-feedback or Avi clear at gate - Carried non-blockers unchanged (deployment_clock vs #454 clock_12h consolidation; reopen! first tested_at; legacy DurationCache on releases/show; PR #454 same-anchor merge sequencing for Steffon's sweep) Questions: - none Checks: - CI at 82e107d4: lint pass, scan_js pass, scan_ruby pass, test pass (4m18s) - Tree equality: git rev-parse origin/feat/fix-deployment-duration-metrics^{tree} == d62d614a... == [full-suite@]/[rubocop@] fingerprints - Acceptance 5/5 verified: per-stage timestamp+duration cells, tested_at migration+conductor bracket, live count-up, Tested/Assembled/Confirmed/Deployed/Total/Tasks columns, SOP+API+design docs updated

Handoff avi about 7 hours ago

Avi review approved (shannon primary + carl light, both merge-ready; nil-average blocker verified fixed with regression test, CI green at 82e107d4); ready for Steffon's qa-release sweep.

Sealed-bid sizing

Edit →

Alex (PM)

Avi (PO)

SMALL

Dev

SMALL

Actual

XL