Agents Builders

Live deployments board updates

Archived
deployments-live-updates

Created

Jun 24, 17:39

Started

Jun 24, 18:07

Completed

Jun 24, 19:29

DevOps handoff

Type

Feature

Shape

ui+db

Worktree Slug

deployments-live-updates

Repositories

mcritchie-studio

Release Train

2026-06-23-devops-intent-ui

Branch

feat/deployments-live-updates

QA URL

Production URL

ui devops realtime

Acceptance Criteria

  • DeploymentsChannel broadcasts stage changes and intent events
  • Alpine board animates card moves, not jumps
  • A new intent pops in avatar plus ticker live
  • Board updates with no full page reload
  • Reconnect replays state so the board stays consistent

Expected Test Plan

  • [unit] broadcaster builds stage_change and intent payloads
  • [integration] a stage move and an intent enqueue a broadcast
  • [component] board Alpine applies a broadcast payload
  • [e2e] board updates live on a new intent without reload

Checks Run

  • [review] RE-REVIEW after rework aff4692 — Shannon (heavy/UI) APPROVE: all 3 block findings resolved (refreshCounts→updateCounts, e2e count-badge assertion + seed fixture, try/catch wrap); Carl (light/backend) APPROVE: backend byte-identical, no regression

Agent Context

Increment 2 of 2 on release train 2026-06-23-devops-intent-ui. DEPENDS ON agent-intent-timeline (PR #161) shipping first — it proves the intent data shape this layer broadcasts. (No --dependency CLI flag exists; conductor should add the dependency edge, or just sequence this after agent-intent-timeline.) Operator-locked decision: use a CUSTOM ActionCable channel broadcasting JSON that the EXISTING Alpine kanbanBoard() consumes — NOT Turbo Streams — so transitions animate (FLIP a card to its new column, fade in a new avatar, start the ticker) instead of a jumpy DOM swap. TWO event types: (2a) task STAGE CHANGE (from->to), (2b) task EVENT/INTENT update (agent intent started). Server side: broadcast from Task after_commit on record_stage_event (transition) AND record_intent_event (intent) — both already chokepoints from increment 1; build a small broadcaster that emits {type:'stage_change',task,from,to,...} and {type:'intent',task,stage,agents,live_since}. cable.yml is already configured (async dev, redis prod, channel_prefix mcritchie_studio_production); app/channels has only the empty ApplicationCable base — add DeploymentsChannel. Client side: app/views/tasks/_board.html.erb kanbanBoard() (Alpine) subscribes via @hotwired/turbo-rails' cable or a raw ActionCable consumer; reuse window.elapsedLabel (now in components/_elapsed_label_script) and the crew/intent rendering. Helper hooks to reuse for payloads: StageAgentsHelper#in_progress_work, #stage_timeline, #crew_columns. Verify WebSockets work on Heroku (see contest-chat ActionCable precedent). Keep it additive: a no-JS/no-cable page must still render the server-rendered tickers from increment 1.

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.

  1. Created Designed
    O Oddish
    Oddish
    Model
    Duration
    Tokens
    Cost
    Completed Jun 24, 17:39 · 3 days ago
    api
  2. Designed Building
    O Oddish
    Oddish
    Model
    claude-opus-4-8
    Duration
    28 minutes
    Tokens
    Cost
    Started Jun 24, 17:39
    Completed Jun 24, 18:07 · 3 days ago
    cli
  3. Building Submitted
    O Oddish
    Oddish
    Model
    claude-opus-4-8
    Duration
    33 minutes
    Tokens
    96,479,326
    Cost
    ~$58.09
    Started Jun 24, 18:07
    Completed Jun 24, 18:39 · 3 days ago
    cli
  4. Submitted Blocked
    Model
    Duration
    9 minutes
    Tokens
    Cost
    Started Jun 24, 18:39
    Completed Jun 24, 18:48 · 3 days ago
    api
  5. Blocked Submitted
    O Oddish
    Oddish
    Model
    claude-opus-4-8
    Duration
    10 minutes
    Tokens
    23,979,821
    Cost
    ~$14.16
    Started Jun 24, 18:48
    Completed Jun 24, 18:58 · 3 days ago
    cli
  6. Submitted Reviewed
    S Shannon
    Shannon primary
    C Carl
    Carl light
    Model
    claude-opus-4-8
    Duration
    9 minutes
    Tokens
    Cost
    Started Jun 24, 18:58
    Completed Jun 24, 19:07 · 3 days ago
    cli
  7. Reviewed Assembled
    S Steffon
    Steffon
    Model
    Duration
    under a minute
    Tokens
    Cost
    Started Jun 24, 19:07
    Completed Jun 24, 19:07 · 3 days ago
  8. Assembled Shipped
    A Avi
    Avi
    Model
    Duration
    21 minutes
    Tokens
    Cost
    Started Jun 24, 19:07
    Completed Jun 24, 19:29 · 3 days ago
  9. Shipped Archived
    8
    81fad33e-98b5-4075-8ed6-d0efe6cb5804
    Model
    claude-opus-4-8
    Duration
    1 day
    Tokens
    Cost
    Started Jun 24, 19:29
    Completed Jun 25, 21:16 · 1 day ago
    cli

Conversation

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

Handoff 3 days ago

Increment 2 (websockets) complete. DeploymentsChannel + DeploymentsBroadcaster re-render one card on TaskEvent after_commit (transitions + intents); board card extracted to _task_card; Alpine kanbanBoard() applies pushes with FLIP/fade/in-place. All tiers green (1452 runs, 0 fail), rubocop clean, dor-check ✓, e2e is a REAL cable round-trip. Review: http://localhost:3026/deployments (port 3020 collides with rolio — flagged in PR). 2 pre-existing release e2e failures (session_resume stale + smoke flake) confirmed unrelated.

QA Feedback 3 days ago

Shannon (heavy/UI) BLOCK: applyLiveUpdate() in _board.html.erb calls this.refreshCounts() at 2 sites but the method is updateCounts() — every cable broadcast throws TypeError after the DOM mutation, so column count badges + empty-states go stale + console error per update (breaks accept #4/#5). Tests stayed green: throw lands post-mutation, no test drives the Alpine client. Fix: rename refreshCounts→updateCounts at both call sites; add a count-badge assertion to e2e/deployments_live.spec.js; wrap the received/applyLiveUpdate body in try/catch per the 'cable failure must never break the board' comment. Backend half (broadcaster/channel/after_create_commit) passed review — UI client only.

Handoff 3 days ago

Addressed Shannon's heavy/UI BLOCK (all 3 points): (1) renamed refreshCounts→updateCounts at both applyLiveUpdate call sites — count badges + empty-states now update per broadcast; (2) wrapped applyLiveUpdate body in try/catch (warn, never throw) per the cable-must-never-break-the-board rule; (3) e2e hardened — new stage-change test asserts the card FLIPs columns AND the Reviewed count badge increments (direct regression guard), and the intent test now fails on any uncaught pageerror/console error. Both would fail against the old code. Full suite 1452/0, rubocop clean, e2e green. Pushed aff4692 to PR #171. Backend half was already approved; this was UI-client only.

Sealed-bid sizing

Edit →

Alex (PM)

Avi (PO)

Dev

Actual