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
Acceptance Criteria
Expected Test Plan
Checks Run
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.
Conversation
QA review feedback, agent handoffs, and follow-up notes for this task.
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.
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.
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
—
We emailed a one-tap sign-in link to . It expires shortly and can only be used once.
No email? Check spam, or close this and try again.