Skip to content

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

Merged
ty13r merged 5 commits into
mainfrom
claude/phase-41.7-escalation-card-canonical-widget
May 29, 2026
Merged

feat(escalation_card): canonical widget Stage 1-4 (:layer_shell_and_callout sibling to propose_new_doc_card)#149
ty13r merged 5 commits into
mainfrom
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

ty13r added a commit that referenced this pull request May 29, 2026
…1PackageBoundaryTest

Phase31DocsConformanceTest asserts each AshUI.WidgetComponents.kinds() name
appears in UG-0003. Both propose_new_doc_card and escalation_card were
missing, causing net-new conformance regressions on PR #149.

Phase31PackageBoundaryTest layer_shell_and_callout families assertion
lacked :escalation_card. The code legitimately adds it; this is a correct
test-list update, not test weakening.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ty13r and others added 4 commits May 28, 2026 23:13
…_new_doc SessionEvents

Full 4-stage canonical widget pipeline for :propose_new_doc_card in the
:layer_shell_and_callout family, supporting pending/accepted/rejected/archived
status, accept/reject/preview actions, body expansion, and conversation seed
collapsible slot.

Stage 1 (catalog): widget_components.ex entry with family/summary/aliases
Stage 2 (DSL): dsl/entities/widget_components.ex leaf entity with all attrs
Stage 3 (IUR): components.ex constructor + validate.ex + fixture coverage
Stage 4 (LiveUI): ProposeNewDocCard Phoenix.Component, layer_shell_and_callout
  aggregation, renderer.ex clause, iur_adapter.ex routing,
  live_ui_adapter.ex fallback markup

Fixture coverage: propose_new_doc_card added to components--accessibility_and_safety
fixture; 6 pre-existing missing-kind failures (collection_picker, thread_card,
composer_query_preview) are unrelated to this PR.

DRAFT: spec contract discrepancy — canonical_widget_propose_new_doc_card.spec.md
uses proposed_path/proposed_title/rationale/proposed_kind/decision but this
implementation follows Phase 41 SessionEvent payload shape (target_path/title/
body_md_preview/status). Pascal review required before merge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…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>
…i threading + pre-existing coverage gaps

P1: append :escalation_card to the layer_callout_kinds() assertion in
components_test.exs — the code had it in @layer_callout_kinds but the
test assertion still ended at :propose_new_doc_card.

P2: thread escalation_card into unified_ui as a resource-authorable
widget, mirroring what propose_new_doc_card (#148) did:
- widget_components.ex catalog entry (:layer_shell_and_callout family)
- dsl/entities/widget_components.ex: leaf(:escalation_card) with all
  required/optional fields (target_project_id, text, severity, etc.)
- dsl/node.ex: 10 new fields in @type t(), defstruct, and summary/1
- dsl/verifiers/validate_widget_components.ex: validate_node/1 clause
  with @escalation_severities + optional field checks
- compiler/pipeline.ex: :escalation_card arm calling
  Widgets.Components.escalation_card via common_opts

Test updates (all asserting correct runtime values, not weakening):
- unified_iur components_test: layer_callout_kinds list includes :escalation_card
- unified_ui catalog/operational/phase_2/advanced_widget_families tests:
  kinds lists updated to include :escalation_card
- compiler lowering test: escalation_card DSL node + lowering assertions

Also adds 3 pre-existing coverage gaps (collection_picker, thread_card,
composer_query_preview) to unified_iur/fixtures.ex so this branch
does not carry main redness.

Result: unified_iur 196 tests 0 failures; unified_ui 162 tests 29
failures (identical to the 29 pre-existing failures on #148 baseline).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…1PackageBoundaryTest

Phase31DocsConformanceTest asserts each AshUI.WidgetComponents.kinds() name
appears in UG-0003. Both propose_new_doc_card and escalation_card were
missing, causing net-new conformance regressions on PR #149.

Phase31PackageBoundaryTest layer_shell_and_callout families assertion
lacked :escalation_card. The code legitimately adds it; this is a correct
test-list update, not test weakening.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ty13r ty13r force-pushed the claude/phase-41.7-escalation-card-canonical-widget branch from ca80983 to 6b23a90 Compare May 29, 2026 04:15
@ty13r ty13r changed the base branch from claude/phase-41.6-propose-new-doc-card-canonical-widget to main May 29, 2026 04:33
Trailing double blank line before normalize_query_preview_state!/1 fails
mix format --check-formatted in the unified_iur package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ty13r ty13r marked this pull request as ready for review May 29, 2026 04:45
@ty13r ty13r merged commit 7556bae into main May 29, 2026
8 checks passed
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