Skip to content

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

Draft
ty13r wants to merge 2 commits into
mainfrom
claude/phase-41.6-propose-new-doc-card-canonical-widget
Draft

feat(propose_new_doc_card): canonical widget Stage 1-4 (:layer_shell_and_callout family — new)#148
ty13r wants to merge 2 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>
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