Skip to content

feat(propose_new_doc_card): canonical widget Stage 1-4 (:layer_shell_and_callout family — new)#148

Merged
ty13r merged 3 commits into
mainfrom
claude/phase-41.6-propose-new-doc-card-canonical-widget
May 29, 2026
Merged

feat(propose_new_doc_card): canonical widget Stage 1-4 (:layer_shell_and_callout family — new)#148
ty13r merged 3 commits into
mainfrom
claude/phase-41.6-propose-new-doc-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.6 — adds :propose_new_doc_card as a NEW canonical widget in family :layer_shell_and_callout (NEW family — this PR creates the aggregator). Stage 1-4 of the canonical-widget pipeline; Stage 5 (AshUI Screen DSL adoption) deferred.

Renders the :propose_new_doc SessionEvent emitted by propose_new_project_document MCP (shipped in PR 41.3a feat(phase-41.3a)).

Implementation

Stage 1 — UnifiedUi catalog + DSL entity + DSL pipeline + verifier:

  • packages/unified_ui/lib/unified_ui/widget_components.ex — register :propose_new_doc_card with family: :layer_shell_and_callout
  • packages/unified_ui/lib/unified_ui/dsl/entities/widget_components.ex — DSL leaf
  • packages/unified_ui/lib/unified_ui/compiler/pipeline.ex + dsl/node.ex + dsl/verifiers/validate_widget_components.ex — lowering + verifier wiring

Stage 2 — UnifiedIUR constructor + validation:

  • packages/unified_iur/lib/unified_iur/widgets/components.expropose_new_doc_card/1 constructor + @layer_callout_kinds
  • packages/unified_iur/lib/unified_iur/validate.ex:invalid_propose_new_doc_card validation
  • packages/unified_iur/lib/unified_iur/fixtures.ex — fixture coverage

Stage 3 — LiveUi Phoenix.Component:

  • packages/live_ui/lib/live_ui/widgets/propose_new_doc_card.ex — Phoenix.Component with live-ui-* BEM, full aria coverage (aria-controls + aria-expanded + descriptive labels), Accept/Reject/Preview actions
  • packages/live_ui/lib/live_ui/renderer.ex — dedicated :propose_new_doc_card clause placed above generic fallback

Stage 4 — Adapter routings + NEW family aggregator:

  • lib/ash_ui/rendering/iur_adapter.ex — canonical IUR path
  • lib/ash_ui/rendering/live_ui_adapter.ex — string-HTML fallback
  • packages/live_ui/lib/live_ui/widgets/layer_shell_and_callout.ex — NEW family aggregator (Codex created this file because :layer_shell_and_callout didn't exist as an aggregator yet; will be extended in PR 41.7 with escalation_card)

Spec ref (ariston-ui)

  • .spec/specs/canonical_widget_propose_new_doc_card.spec.md
  • ADR-0044 §"PR 6 — propose_new_doc_card"

Spec ↔ shipped payload reconciliation needed (Pascal): spec contract uses proposed_path/proposed_title/rationale/proposed_kind/decision. Implementation uses target_path/title/body_md_preview/conversation_seed_md/status to match the Phase 41.3a SessionEvent payload (lib/ariston_ui/mcp/tools/propose_new_project_document.ex:80-88,107-111) that's already shipped. Recommend amending the spec to match the working payload — the SessionEvent ships those exact field names and any rename would ripple back through PRs 41.3a/3b.

Test plan

  • IUR constructor: happy path + invalid-payload rejections (blanks, unknown status, invalid actions)
  • LiveUi.Widgets.ProposeNewDocCard render: status variants / Accept-Reject button rendering for :pending / preview link / aria-expanded + aria-controls linkage
  • Both adapter dispatch tests
  • Catalog test asserts family: :layer_shell_and_callout
  • DSL verifier rejects malformed widget payloads
  • aria-controls target remains in DOM with hidden attribute when collapsed (post-Opus-review fix)

97/97 tests pass on changed files; 117 total pass across the widget surface.

Reviews

  • Codex implementer: 1 round, no WIP. 25 files, 1399 insertions.
  • Opus Max independent review: APPROVE for DRAFT. No P1. 1 P2 (aria-controls dangling reference) fixed inline post-review; 1 P2 (stateless widget contract documentation) deferred as design clarification.

Deferred P3 follow-ons

  • Spec vs shipped payload field naming reconciliation (see above; Pascal decision).
  • status: :archived added to widget enum — not in spec; aligns with Phase 41.3a lifecycle. Pascal to confirm.
  • CSS asset assets/css/widgets/propose_new_doc_card.css not present (DRAFT acceptable).
  • 6 pre-existing test failures unrelated to this PR (collection_picker / thread_card / composer_query_preview missing-from-fixture, pre-existed on main).

Notes for Pascal

  • NEW :layer_shell_and_callout family aggregator at packages/live_ui/lib/live_ui/widgets/layer_shell_and_callout.ex. PR 41.7 (escalation_card) will extend this aggregator with its widget.
  • Spec ↔ payload reconciliation — see above. Implementation is internally consistent; aligned to actual SessionEvent emission shape.
  • Stateless widget contract: :expanded? and :seed_expanded? state lives in the consuming LiveView, not the widget. Widget emits body_toggled and conversation_seed_toggled events; consumer flips state.

Generated with Claude Code

ty13r and others added 2 commits May 27, 2026 10:49
…_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>
…bute

Opus Max review P2: aria-controls references @seed_id; when collapsed via
:if={@seed_expanded?}, the target node is removed from DOM, leaving a
dangling aria-controls reference. ARIA disclosure pattern recommends the
target exist with the hidden attribute.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ty13r added a commit that referenced this pull request May 29, 2026
…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>
…and_callout)

Phase31DocsConformanceTest asserts each AshUI.WidgetComponents.kinds() name
appears in the user guide. propose_new_doc_card was added as a canonical
widget in Phase 41.6 but not documented in UG-0003, causing a net-new
conformance regression on PR #148.

Added propose_new_doc_card to the Layer shell and callout family table row
and added a descriptive paragraph covering its key props (target_path, title,
body_md_preview, status, actor_handle, expanded?) and canonical interactions
(accept, reject, preview, body_toggled, conversation_seed_toggled).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ty13r ty13r marked this pull request as ready for review May 29, 2026 03:12
@ty13r ty13r merged commit 2231c71 into main May 29, 2026
8 checks passed
ty13r added a commit that referenced this pull request May 29, 2026
…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>
ty13r added a commit that referenced this pull request May 29, 2026
…allout sibling to propose_new_doc_card) (#149)

* feat(propose_new_doc_card): add canonical widget for Phase 41 propose_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>

* feat(widget): add :escalation_card canonical widget to :layer_shell_and_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>

* fix(escalation_card): P1 layer_callout_kinds assertion + P2 unified_ui 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>

* docs(conformance): add propose+escalation_card to UG-0003; fix Phase31PackageBoundaryTest

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>

* fix(format): remove extra blank line in unified_iur components

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>

---------

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