Skip to content

feat(escalation_card): canonical widget Stage 1-4 (:layer_shell_and_callout sibling to propose_new_doc_card)#149

Draft
ty13r wants to merge 1 commit into
claude/phase-41.6-propose-new-doc-card-canonical-widgetfrom
claude/phase-41.7-escalation-card-canonical-widget
Draft

feat(escalation_card): canonical widget Stage 1-4 (:layer_shell_and_callout sibling to propose_new_doc_card)#149
ty13r wants to merge 1 commit into
claude/phase-41.6-propose-new-doc-card-canonical-widgetfrom
claude/phase-41.7-escalation-card-canonical-widget

Conversation

@ty13r
Copy link
Copy Markdown
Member

@ty13r ty13r commented May 27, 2026

Summary (DRAFT — Pascal-review required)

Phase 41 PR 41.7 — adds :escalation_card as a NEW canonical widget in family :layer_shell_and_callout. Sibling of 41.6 — extends the family aggregator created in PR ash_ui#148. Stage 1-4 of the canonical-widget pipeline; Stage 5 deferred.

This PR is based on the 41.6 branch (claude/phase-41.6-propose-new-doc-card-canonical-widget). When 41.6 lands, 41.7 rebases naturally. Review the diff against 41.6 (not main) for 41.7-specific changes only.

Renders the :escalation_raised SessionEvent emitted by escalate_to_team MCP (shipped in PR 41.3a).

Implementation

Stage 1 — UnifiedUi catalog + DSL:

  • packages/unified_ui/lib/unified_ui/widget_components.ex — register :escalation_card with family: :layer_shell_and_callout
  • DSL entity, lowering pipeline, verifier — all mirror 41.6's pattern

Stage 2 — UnifiedIUR:

  • packages/unified_iur/lib/unified_iur/widgets/components.exescalation_card/1 constructor; added to @layer_callout_kinds; @escalation_severities = [:p1, :p2, :p3]
  • packages/unified_iur/lib/unified_iur/validate.exvalidate_escalation_shape/1 rejecting blank/invalid severity/missing required
  • packages/unified_iur/lib/unified_iur/fixtures.ex — fixture coverage with :p2 severity

Stage 3 — LiveUi Phoenix.Component:

  • packages/live_ui/lib/live_ui/widgets/escalation_card.exrole="alert", aria-labelledby to title, severity-aware action button aria-labels, acknowledged state cleanly hides actions + shows status text
  • packages/live_ui/lib/live_ui/renderer.ex — dedicated :escalation_card clause + escalation_attributes/escalation_action_attrs helpers

Stage 4 — Adapters + family aggregator extension:

  • lib/ash_ui/rendering/iur_adapter.ex — canonical IUR conversion + escalation_card_opts/1
  • lib/ash_ui/rendering/live_ui_adapter.ex — string-HTML fallback with BEM --<severity> modifier + acknowledged? flip on aria-label
  • packages/live_ui/lib/live_ui/widgets/layer_shell_and_callout.exEscalationCard ADDED to @modules (existing ProposeNewDocCard from 41.6 untouched)

Spec ref (ariston-ui)

  • .spec/specs/canonical_widget_escalation_card.spec.md
  • ADR-0044 §"PR 7 — escalation_card"

Spec vs shipped payload reconciliations (4 items in P3 below — same pattern as 41.6's reconciliation).

Test plan

  • IUR constructor: happy path + invalid (missing required, invalid severity)
  • Validation: :invalid_escalation_card errors for each invariant
  • Phoenix.Component render: severity variants (p1/p2/p3), acknowledged state, severity-aware action labels
  • Both adapter dispatches
  • Catalog test asserts family classification

25 unit tests + 204 rendering tests pass.

Reviews

  • Codex implementer: 1 round, no WIP. 12 files, 1103 insertions.
  • Opus Max independent review: APPROVE for DRAFT. No P1, no P2. 4 P3s deferred for Pascal — all spec-vs-payload reconciliation (consistent with 41.6).

Deferred P3 follow-ons (Pascal review)

  1. Spec evidence field reconciliation — spec requires evidence: [{ref, anchor}] and validation; shipped widget uses escalate_to_team MCP payload (no evidence field). Same pattern Pascal accepted for 41.6's spec-vs-payload trade.
  2. BEM --severity- infix — spec wants .ash-escalation-card--severity-p1; shipped emits .ash-escalation-card--p1. Matches 41.6's .ash-propose-new-doc-card--{status} convention.
  3. CSS assetassets/css/widgets/escalation_card.css not present (spec marks "planned").
  4. Required title / actor_handle — spec lists both required; shipped uses hardcoded "Escalation" + optional actor_handle (matches escalate_to_team payload reality).

Notes for Pascal

  • :layer_shell_and_callout family now holds 2 widgets (propose_new_doc_card from 41.6 + escalation_card from 41.7). Aggregator extension is additive — no 41.6 churn.
  • Branch base: this PR depends on 41.6 (PR ash_ui#148). When 41.6 lands, this rebases.
  • Spec-vs-payload reconciliations mirror 41.6's pattern — recommend amending specs rather than reshaping the contract.

Generated with Claude Code

…nd_callout family

Extends the family aggregator from PR 41.6 with EscalationCard — a
severity-bearing inline callout for :escalation_raised SessionEvents.

Stage 1-4 pipeline:
- family aggregator: add LiveUi.Widgets.EscalationCard to @modules
- unified_iur: escalation_card/1 constructor + @escalation_severities [:p1,:p2,:p3]
  + put_escalation_interactions (acknowledge + route_to_rail defaults)
- unified_iur validate: :invalid_escalation_card guidance + validate_escalation_shape/1
  (required target_project_id/text/severity; optional string + optional severity checks)
- unified_iur fixtures: coverage entry with p2 severity
- live_ui renderer: native :escalation_card clause before generic fallback;
  escalation_attributes/1 + escalation_action_attrs/2 helpers
- iur_adapter: base_attributes(:escalation_card) + escalation_card_opts/1
- live_ui_adapter: generate_heex("escalation_card") with BEM classes,
  severity modifier, acknowledged? aria-label flip

Tests: 25 unit (live_ui + unified_iur widgets) + 204 rendering (0 failures).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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