Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
id: repo.ecosystem.canonical_navigation_boundary
status: accepted
date: 2026-04-24
affects:
- ecosystem.signal_transport
- unified_ui.signals
- unified_ui.compiler
- unified_iur.interactions
- live_ui.runtime
- elm_ui.server_runtime
- desktop_ui.runtime
- terminal_ui.runtime
---

# Canonical Navigation Is Authored as Screen-Transition Intent

## Context

The ecosystem already treats navigation as a canonical interaction family, but
the runtime libraries do not share one host model. Web runtimes can integrate
with page or route transitions, the desktop runtime owns screen navigation
inside windows, and the terminal runtime may need screen replacement or bounded
section changes without any URL model at all.

The authored DSL and canonical IUR therefore need a durable navigation boundary
that can preserve cross-runtime meaning without importing browser-route syntax,
runtime module identities, or host-specific router APIs into the authored
surface.

## Decision

1. Canonical navigation remains part of the authored interaction and signal
surface rather than becoming a dedicated routing subsystem inside
`unified_ui`.
2. When authored navigation changes the active top-level UI surface, it is
expressed as canonical screen-transition intent using transition actions
such as `navigate_to`, `replace_with`, `go_back`, `go_forward`,
`open_modal`, and `close_modal`.
3. Canonical transition targets are symbolic screen identifiers plus optional
params and metadata rather than URLs, host-router names, or runtime-module
references.
4. Local navigation-like interactions such as tab changes or other in-screen
destination changes may remain canonical interaction descriptors without
being forced into browser-route semantics.
5. Runtime libraries translate canonical navigation intent into their own host
models:
- `live_ui` and `elm_ui` may resolve canonical screen transitions through
host page or route integration
- `desktop_ui` resolves them through screen registry and navigation
controller primitives
- `terminal_ui` resolves them through terminal-appropriate screen
replacement, modal transitions, or bounded section/history behavior

## Consequences

- `UnifiedUi` can author portable navigation meaning without becoming a router.
- `UnifiedIUR` must preserve canonical navigation descriptors in a
renderer-independent shape.
- Runtime libraries may integrate with their own host navigation systems, but
those host systems remain runtime concerns rather than authored DSL
concerns.
- Browser-style route syntax is not the cross-runtime navigation contract of
the ecosystem.
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Phase 2 - Compiler, UnifiedIUR, and Signal Transport Alignment

Back to index: [README](./README.md)

## Relevant Shared APIs / Interfaces
- `UnifiedUi.Compiler`
- `UnifiedUi.Compiler.Pipeline`
- `UnifiedIUR`
- `Jido.Signal`
- package inspection and export surfaces

## Relevant Assumptions / Defaults
- `unified_ui` remains the authored source of canonical navigation intent and
`unified_iur` remains the renderer-independent interchange boundary.
- Navigation lowering must preserve transition action, symbolic screen target,
modal target, params, and source-context meaning without introducing
host-specific route syntax.
- The shared signal transport contract continues to use `Jido.Signal` with
CloudEvents-compatible semantics at package boundaries.

[x] 2 Phase 2 - Compiler, UnifiedIUR, and Signal Transport Alignment
Implement the canonical lowering, interchange representation, and transport
semantics that carry screen-transition intent from authored `UnifiedUi`
modules to runtime libraries.

[x] 2.1 Section - UnifiedUi Compiler Navigation Lowering
Implement compiler support that lowers authored navigation transitions into
stable canonical descriptors.

[x] 2.1.1 Task - Lower authored navigation into canonical descriptors
Extend the compiler pipeline so authored screen transitions become
portable canonical interaction data.

[x] 2.1.1.1 Subtask - Lower navigation actions, symbolic screen targets, modal targets, params, and source-context fields into canonical interaction descriptors.
[x] 2.1.1.2 Subtask - Preserve payload mapping and binding references for navigation transitions without introducing renderer-local callback logic.
[x] 2.1.1.3 Subtask - Ensure canonical lowering stays deterministic across equivalent authored modules so navigation diffs remain review-friendly.

[x] 2.1.2 Task - Preserve compatibility for non-transition interactions
Keep existing canonical interaction lowering intact while introducing the
new screen-transition model.

[x] 2.1.2.1 Subtask - Ensure non-navigation interaction families continue to compile without depending on the new transition fields.
[x] 2.1.2.2 Subtask - Ensure local navigation-like descriptors that do not change the top-level screen remain representable without being forced into the screen-transition shape.
[x] 2.1.2.3 Subtask - Add normalization rules that keep older generic target-intent usage reviewable while preserving the newer canonical transition contract.

[x] 2.2 Section - UnifiedIUR Interaction Representation
Implement the canonical `unified_iur` representation needed for runtimes to
consume screen-transition meaning without host-router assumptions.

[x] 2.2.1 Task - Extend canonical interaction descriptors
Add the renderer-independent fields and invariants needed for canonical
screen transitions.

[x] 2.2.1.1 Subtask - Extend the canonical interaction model to represent transition action, symbolic screen target, modal target, params, and related metadata.
[x] 2.2.1.2 Subtask - Keep canonical interaction storage renderer-independent and free from browser path syntax, host-router names, or runtime-module references.
[x] 2.2.1.3 Subtask - Define how targetless navigation actions, such as `go_back` or `close_modal`, are represented without inventing fake screen ids.

[x] 2.2.2 Task - Update canonical inspection, export, and fixture support
Make the new canonical navigation representation visible and testable
through package tooling.

[x] 2.2.2.1 Subtask - Update inspect and export surfaces so canonical navigation descriptors print their transition fields clearly.
[x] 2.2.2.2 Subtask - Add canonical fixtures that exercise ordinary transitions, replacement transitions, history traversal, and modal transitions.
[x] 2.2.2.3 Subtask - Ensure serialized or review-friendly output stays stable enough for diff-oriented tooling and conformance snapshots.

[x] 2.3 Section - Shared Signal Transport Alignment
Implement the shared transport semantics that move canonical screen
transitions across runtime package boundaries.

[x] 2.3.1 Task - Define canonical boundary signal shape for transitions
Establish how canonical screen transitions are represented when they cross
package boundaries as `Jido.Signal` values.

[x] 2.3.1.1 Subtask - Define the event-family and payload expectations for transition actions, symbolic screen targets, modal targets, and params.
[x] 2.3.1.2 Subtask - Define how targetless actions, such as `go_back`, `go_forward`, and `close_modal`, are encoded without host-specific routing assumptions.
[x] 2.3.1.3 Subtask - Define how runtimes receive canonical transition data while keeping their local native signal models free to differ internally.

[x] 2.3.2 Task - Add shared transport validation and fixtures
Provide the shared validation and reference fixtures that runtime
implementers can consume consistently.

[x] 2.3.2.1 Subtask - Add validation for malformed transition payloads, leaked router syntax, and missing required canonical fields.
[x] 2.3.2.2 Subtask - Add shared transport fixtures that web, desktop, and terminal runtimes can use to prove canonical transition fidelity.
[x] 2.3.2.3 Subtask - Add review-oriented transport summaries that make it obvious which transition action and symbolic screen target crossed the package boundary.

[x] 2.4 Section - Phase 2 Integration Tests
Validate canonical lowering, `unified_iur` representation, and boundary
transport behavior end to end before runtime-specific mapping begins.

[x] 2.4.1 Task - Compiler and canonical descriptor scenarios
Verify authored transitions compile into stable canonical descriptors that
carry the required cross-runtime meaning.

[x] 2.4.1.1 Subtask - Verify authored screen transitions lower into canonical interaction descriptors with action, symbolic screen target, modal target, params, and payload mapping preserved.
[x] 2.4.1.2 Subtask - Verify targetless transitions compile without fake screen ids or host-router placeholders.
[x] 2.4.1.3 Subtask - Verify canonical descriptor output remains deterministic across equivalent authored modules and review exports.

[x] 2.4.2 Task - Shared transport boundary scenarios
Verify canonical transition meaning survives the package-boundary signal
contract without route leakage.

[x] 2.4.2.1 Subtask - Verify `Jido.Signal` boundary fixtures preserve transition action, symbolic screen target, and params where applicable.
[x] 2.4.2.2 Subtask - Verify malformed transition envelopes and leaked route syntax fail with shared validation diagnostics.
[x] 2.4.2.3 Subtask - Verify runtimes can consume the shared transition fixtures without requiring browser-only fields or runtime-module identifiers.
14 changes: 14 additions & 0 deletions .spec/specs/signal_transport.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ status: active
summary: Shared Jido.Signal and CloudEvents-compatible boundary contract across the DSL, IUR consumers, and runtime libraries with native signal translation.
surface:
- packages/unified-ui
- packages/unified_iur
- packages/live_ui
- packages/elm_ui
- packages/desktop_ui
- packages/terminal_ui
- .spec/specs/signal_transport.spec.md
decisions:
- repo.ecosystem.contract_model
- repo.ecosystem.canonical_navigation_boundary
- repo.ecosystem.elm_ui_naming
```

Expand Down Expand Up @@ -61,6 +63,16 @@ decisions:
statement: Renderer-specific local state and native signal mechanics may vary by library, but cross-package event meanings shall remain canonical at the signal contract boundary.
priority: must
stability: stable

- id: ecosystem.signal_transport.navigation_transition_meaning
statement: Navigation interactions that cross ecosystem package boundaries shall preserve canonical screen-transition meaning, including transition action, symbolic screen target when applicable, and params, without requiring browser-route syntax or runtime-specific identifiers.
priority: must
stability: stable

- id: ecosystem.signal_transport.shared_transition_validation_and_fixtures
statement: The ecosystem shall expose shared canonical transition fixtures, validation rules, and review summaries that runtime packages can consume consistently when transporting screen transitions across package boundaries.
priority: must
stability: stable
```

## Exceptions
Expand Down Expand Up @@ -91,4 +103,6 @@ decisions:
- ecosystem.signal_transport.terminal_bridge
- ecosystem.signal_transport.native_signal_models_allowed
- ecosystem.signal_transport.local_state_not_contract
- ecosystem.signal_transport.navigation_transition_meaning
- ecosystem.signal_transport.shared_transition_validation_and_fixtures
```
13 changes: 13 additions & 0 deletions .spec/specs/unified-iur/interactions.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ surface:
- .spec/specs/unified-iur/interactions.spec.md
decisions:
- repo.ecosystem.contract_model
- repo.ecosystem.canonical_navigation_boundary
```

## Requirements
Expand Down Expand Up @@ -49,6 +50,16 @@ decisions:
statement: The package shall represent dynamic data references, bound values, and authored dependency relationships needed for runtime libraries to reconstruct current UI meaning from canonical IUR.
priority: must
stability: stable

- id: unified_iur.interactions.navigation_transition_representation
statement: The package shall represent canonical screen-transition descriptors including transition action, symbolic screen target, modal target, and params in a renderer-independent form.
priority: must
stability: stable

- id: unified_iur.interactions.no_host_router_assumptions
statement: Canonical navigation descriptors shall not encode browser path syntax, host-router names, or runtime-module references as the cross-runtime navigation contract.
priority: must
stability: stable
```

## Scenarios
Expand All @@ -71,5 +82,7 @@ decisions:
- unified_iur.interactions.renderer_independent_payload_mapping
- unified_iur.interactions.standard_interaction_families
- unified_iur.interactions.data_binding_representation
- unified_iur.interactions.navigation_transition_representation
- unified_iur.interactions.no_host_router_assumptions
- unified_iur.interactions.form_submission_descriptor
```
6 changes: 6 additions & 0 deletions .spec/specs/unified-iur/tooling.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ decisions:
statement: The package shall document its canonical construct families, metadata model, styling and theming model, interaction descriptor model, and runtime-library interoperability expectations as part of package development.
priority: must
stability: stable

- id: unified_iur.tooling.navigation_transition_review_surfaces
statement: The package shall expose maintained fixtures and inspection or export workflows that make canonical navigation transition descriptors reviewable without requiring a runtime library or host router.
priority: must
stability: stable
```

## Scenarios
Expand All @@ -65,5 +70,6 @@ decisions:
- unified_iur.tooling.introspection_helpers
- unified_iur.tooling.validation_workflow
- unified_iur.tooling.documentation_surface
- unified_iur.tooling.navigation_transition_review_surfaces
- unified_iur.tooling.inspect_canonical_fixture
```
7 changes: 7 additions & 0 deletions .spec/specs/unified-ui/compiler.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ surface:
- .spec/specs/unified-ui/compiler.spec.md
decisions:
- repo.ecosystem.contract_model
- repo.ecosystem.canonical_navigation_boundary
```

## Requirements
Expand Down Expand Up @@ -55,6 +56,11 @@ decisions:
statement: The package shall not define platform-specific compile targets for `elm_ui`, `live_ui`, or `desktop_ui`; renderer libraries consume canonical IUR instead.
priority: must
stability: stable

- id: unified_ui.compiler.navigation_transition_lowering
statement: The compiler shall lower authored screen-transition navigation intent into canonical `unified_iur` interaction descriptors that preserve transition action, symbolic screen target, modal target, and params without embedding host-router semantics.
priority: must
stability: stable
```

## Scenarios
Expand Down Expand Up @@ -83,6 +89,7 @@ decisions:
- unified_ui.compiler.runtime_independent_bindings
- unified_ui.compiler.introspection_surface
- unified_ui.compiler.no_renderer_output_modes
- unified_ui.compiler.navigation_transition_lowering
- unified_ui.compiler.compile_screen_to_iur
- unified_ui.compiler.inspect_compiled_artifact
```
15 changes: 14 additions & 1 deletion .spec/specs/unified-ui/signals.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ surface:
- .spec/specs/unified-ui/signals.spec.md
decisions:
- repo.ecosystem.contract_model
- repo.ecosystem.canonical_navigation_boundary
```

## Requirements
Expand Down Expand Up @@ -50,6 +51,16 @@ decisions:
statement: The authored signal surface shall not require `elm_ui`, `live_ui`, or `desktop_ui` local event names, local payload keys, or local transport envelopes in authored modules.
priority: must
stability: stable

- id: unified_ui.signals.navigation_transition_actions
statement: The authored navigation interaction surface shall support canonical screen-transition actions such as `navigate_to`, `replace_with`, `go_back`, `go_forward`, `open_modal`, and `close_modal` without requiring host-router syntax.
priority: must
stability: stable

- id: unified_ui.signals.navigation_symbolic_screen_targets
statement: When a navigation interaction changes the active top-level surface, the authoring model shall express the target as a symbolic screen identifier with optional params or metadata rather than URLs, Phoenix route helpers, runtime modules, or browser-history instructions.
priority: must
stability: stable
```

## Scenarios
Expand All @@ -61,7 +72,7 @@ decisions:
then: The package produces canonical signal descriptors that runtime libraries can translate without the author naming renderer-local events

- id: unified_ui.signals.author_navigation_intent
given: A developer authors a navigation interaction such as opening a dialog, changing a tab, or invoking a command palette action
given: A developer authors a navigation interaction such as opening a dialog, changing a tab, or transitioning to another screen
when: The interaction is declared in the DSL
then: The package records canonical event meaning and payload mapping without coupling the author to one renderer runtime
```
Expand All @@ -77,6 +88,8 @@ decisions:
- unified_ui.signals.standard_interaction_families
- unified_ui.signals.validation_and_introspection
- unified_ui.signals.no_runtime_local_event_leakage
- unified_ui.signals.navigation_transition_actions
- unified_ui.signals.navigation_symbolic_screen_targets
- unified_ui.signals.author_form_interaction
- unified_ui.signals.author_navigation_intent
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule DesktopUi.CanonicalNavigationTransportIntegrationTest do
use ExUnit.Case, async: true

alias Jido.Signal
alias DesktopUi.Transport
alias UnifiedIUR.Interactions.Transport, as: BoundaryTransport

test "consumes shared modal-transition fixtures without runtime-module identifiers" do
fixture = BoundaryTransport.boundary_fixture!("modal_transition--settings_dialog")

assert {:ok, translation} =
Transport.from_interaction(
fixture.interaction,
platform_target: :linux,
widget_id: "settings-trigger",
runtime_id: "desktop-ui:workspace",
screen: "workspace",
payload: fixture.signal_data
)

assert :ok = BoundaryTransport.validate_boundary_fixture(fixture)
assert translation.target == fixture.descriptor.target
assert %Signal{} = translation.signal
assert translation.signal.data == fixture.signal_data
assert translation.signal.extensions.desktop_ui_target == fixture.descriptor.target
refute translation.signal.extensions.desktop_ui_target.navigation[:module]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule LiveUi.CanonicalNavigationTransportIntegrationTest do
use ExUnit.Case, async: true

alias Jido.Signal
alias UnifiedIUR.Interactions.Transport, as: BoundaryTransport

test "consumes shared screen-transition fixtures without browser-only route fields" do
fixture = BoundaryTransport.boundary_fixture!("screen_transition--settings_profile")

assert {:ok, translation} =
LiveUi.Signals.from_interaction(
fixture.interaction,
screen: :workspace,
mode: :screen,
boundary: :boundary,
payload: fixture.signal_data
)

assert :ok = BoundaryTransport.validate_boundary_fixture(fixture)
assert translation.target == fixture.descriptor.target
assert %Signal{} = translation.signal
assert translation.signal.data == fixture.signal_data
assert translation.signal.extensions.live_ui_target == fixture.descriptor.target
refute translation.signal.extensions.live_ui_target.navigation[:route]
end
end
Loading
Loading