Skip to content

Studio binding/bus: indicators, bus pane, URI refs, declarative defaults (M0–M5)#86

Merged
Yona-Appletree merged 41 commits into
mainfrom
claude/studio-binding-design
Jul 16, 2026
Merged

Studio binding/bus: indicators, bus pane, URI refs, declarative defaults (M0–M5)#86
Yona-Appletree merged 41 commits into
mainfrom
claude/studio-binding-design

Conversation

@Yona-Appletree

@Yona-Appletree Yona-Appletree commented Jul 14, 2026

Copy link
Copy Markdown
Member

Executes the 2026-07-06 studio-binding-bus roadmap M0–M5 + M4 plus the D9 naming/syntax outcome. Every milestone is validated (model/engine/studio suites, fw emu render, schema drift, story baselines); M5's visual gate passed on the live fyeah-sign demo and M4's flows were driven end-to-end in the simulator.

Highlights

  • M0 — playlist entry bindings flattened to node roots (trigger_ids per entry); "bindings live at node-def roots" is now a model-wide norm.
  • M1 — truthful per-slot binding indicators; violet status-bound family (bound ≠ green).
  • M2 — binding-graph probe: one wire surface for effective bindings with origin: authored|default (ADR 2026-07-06-binding-graph-probe).
  • M3 (+rework) — bus pane on SlotPane sub-panes: tight value heroes, wiring in the detail popup with clickable focus chips and multi-writer merge semantics spelled out.
  • URI-style refs (ADR 2026-07-08-binding-ref-syntax-and-channel-naming): bus:<channel> + node:<path>#<slot.path>; slashful node paths; channels unitless (time, was time.seconds). Hard cutover, one atomic commit.
  • M5 (ADR 2026-07-09-declarative-default-bindings): slots declare their own defaults (#[slot(default_bind = "bus:…")] / ShaderSlotDef.default_bind); the five loader helpers are deleted; defaults register unconditionally (unfilled channels surface on the bus); fixture.input/output.input become declared consumed slots with defaults — a blank project with basic nodes wires itself. Node cards show DEF-badged wiring with popover origin explanations, backed by catalog-wide direction guardrails.
  • M4 — bus-only binding authoring: Bind…/Retarget…/Unbind in every bindable slot's detail popover, dispatching ordinary slot-edit gestures on the node's bindings map (no new wire surface). The channel picker lists observed ∪ well-known channels (new registry in lpc-model) with kind tags, registry docs, kind-mismatch hints, and validated free text. Unbinding re-enables slot-declared defaults.

Known follow-ups (recorded in the roadmap)

  • The running engine keeps an old binding until save/reload — bindings register at load; incremental runtime apply doesn't re-register them yet.
  • Bus-pane warning for unfilled channels (readers, no writer); visual.out/2 instance proposals; transport.* channels — all M4-adjacent, syntax-ready.
  • M6 (primary-product gallery consumption) and M7 (delete dead Bus struct, docs) remain.

🤖 Generated with Claude Code

Yona-Appletree and others added 30 commits July 6, 2026 15:43
…gger_ids

Authored bindings now live only at node-def roots (see ADR). PlaylistDef
gains the root ByKey trigger slot; PlaylistEntry loses its bindings and
per-entry trigger map and gains trigger_ids config (lowest entry index
wins duplicate claims). The loader's playlist entry-binding special case
is deleted; trigger registers like time. detect_triggered_entry resolves
one root slot and edge-detects per message id.

Supporting: U32List slot value newtype + SlotValue derive support for
Vec<u32> newtype fields; examples and checked-in schemas regenerated.

ADR: docs/adr/2026-07-06-bindings-at-node-roots.md
Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus (M0)

Co-Authored-By: Claude Fable 5 <[email protected]>
…ublish routes

Fixes the long-standing 'unbound when bound' indicator: SlotController now
parses the def root's bindings map into per-slot binding facts and the
node controller distributes them across def (consumed source) and state
(published target) roots on every refresh, clearing on binding removal.
Bound state wins over an unset authored fallback in ui_source().

UI: compact accent BindingChip inline on config rows and produced
value/product rows (channel name, bus# in tooltip/popover; publish
direction wears a → glyph); popovers say Bound from / Published as via
the existing aspect wording. New published-value stories; baselines
regenerated (chips now appear across node/project/shell stories).

Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus (M1)

Co-Authored-By: Claude Fable 5 <[email protected]>
…ments

Binding/bus-linked state is a system concept, not a success state — green
was overloaded (accent, good-text, and bound all shared mint). New
--studio-status-bound-{bg,border,text} violet tokens; IconMenuTone::Bound,
DetailSectionTint::Bound, AspectTone::Bound; bound row tint, chips, popover
sections and headings now wear violet while Valid/No-changes stay green.
Story baselines regenerated.

Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus (M1 gate)

Co-Authored-By: Claude Fable 5 <[email protected]>
…bus channels

New BindingGraphProbeRequest/WireBindingGraph surface (ADR): one probe
returns every registered binding (authored and default, including implicit
runtime consumed slots like fixture.input) plus bus channel summaries with
providers/consumers as indices into the binding list and on-demand resolved
values. Identity travels as NodeId + slot path; origin derives from
priority (authored 0 / default_fallback). Deletes the never-implemented
ExplainSlot probe.

Engine: NodeBindingIndex gains a symmetric bus_sources map and channel
enumeration; NodeTree exposes bindings_with_refs/consumers_for_bus/
bus_channels; Engine::resolve_bus_channel_value resolves outside the tick
reusing the frame cache. Probe covered by harness tests (topology, value
resolution, priority ordering, origin) and a fyeah-sign end-to-end test
(two trigger writers, implicit fixture input consumer, default clock
provider).

Studio-core: ProjectSync carries a binding-graph subscription — reads
attach the probe while subscribed, results cache on the sync, unsubscribe
drops the snapshot; ProjectController exposes binding_graph()/
set_binding_graph_subscribed for the M3 bus pane.

ADR: docs/adr/2026-07-06-binding-graph-probe.md
Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus (M2)

Co-Authored-By: Claude Fable 5 <[email protected]>
…rows

New Bus pane (temporary home: main column under the Project pane; final
placement is an open UX question per the roadmap). UiBusView projects the
binding-graph snapshot through ProjectController: channels with kind and
live value, writers → readers as violet site chips (node label + slot,
DEF badge for default-origin), primary visual.out highlighted, and every
site dispatches the node's Focus action — the bus links back to nodes
(D7). Empty state explains the bus concept. The binding-graph probe now
rides along on every loaded-project read.

Node cards gain binding-derived rows: effective bindings anchored to
slots with no backing row (implicit runtime consumed slots like
fixture.input, output.input) render as read-only bound rows — a node's
visible surface is its authored slots, state slots, and its wiring.

Tests: bus view projection (labels, focus, origins, primary flag),
binding-derived rows (synthesis + no-duplicate), pane registration.
Stories: fyeah-shaped bus, empty state; baselines regenerated.

Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus (M3)

Co-Authored-By: Claude Fable 5 <[email protected]>
The minimal examples/basic demo (clock→shader→fixture→output) exercised
only three bus channels. fyeah-sign adds a button + radio bridge (both
writing bus#trigger) and a playlist switching idle/blast visuals, so the
demo now shows off the trigger channel, multi-writer arbitration, and
playlist switching — a much fuller tour of the system. Button/radio are
virtual in the browser sim (nothing physically fires), but every binding
registers, so the bus pane shows the real topology.

STUDIO_DEMO_PROJECT_ID renamed to examples/fyeah-sign; demo file set and
its tests updated (storage id 'studio' unchanged).

Plan: ~/.photomancer/planning/lp2025/2026-07-06-studio-binding-bus

Co-Authored-By: Claude Fable 5 <[email protected]>
…-design

# Conflicts:
#	lp-app/lpa-studio-core/src/app/node/ui_config_slot.rs
#	lp-app/lpa-studio-core/src/app/studio/studio_controller.rs
#	lp-app/lpa-studio-web/assets/tailwind.css
#	lp-app/lpa-studio-web/src/app/node/config_slot_row.rs
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__simulator-ready__lg.png
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__simulator-ready__md.png
#	lp-app/lpa-studio-web/story-images/studio__layout__studio-shell__simulator-ready__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__all-states__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__all-states__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__all-states__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__editable-clean-controls__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__live-detail-popup__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__live-detail-popup__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__map-add-immediate__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__optional-scalar-included__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__unsaved-detail-popup__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__unsaved-detail-popup__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__unsaved-detail-popup__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__config-slot-row__unset-value__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__node__dirty-live-header-tint__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__node__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__node__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__node__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-product__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-product__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-product__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-value__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-value__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__produced-value__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__nested-record__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__nested-record__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__nested-record__sm.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__node__slot-record-editor__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-pane__change-list-overflow__md.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-pane__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-pane__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__overview__lg.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__overview__md.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__overview__sm.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__project-ready__lg.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__project-ready__md.png
#	lp-app/lpa-studio-web/story-images/studio__project__project-workspace__project-ready__sm.png
Rebuilds the generated artifacts against the merged tree: tailwind.css
from source (main's classes + our violet status-bound tokens) and 38
story baselines reflecting main's option-presence rows and our violet
binding treatment + bus pane rendering together.

Co-Authored-By: Claude Fable 5 <[email protected]>
The binding-graph subscription (set at mark_ready so the bus pane's probe
rides every read) was wiped by begin_initial_sync, which reset the whole
ProjectSync to defaults. In the demo flow (mark_ready → sync_loaded_project
→ begin_initial_sync) the subscription was set and then immediately nuked
before the first read, so the probe never went out and the bus pane showed
'Reading' forever with 'No bus channels yet'.

The subscription is a UI preference (the bus pane ships with every loaded
project), not per-read data, so begin_initial_sync now preserves it while
still resetting the mirror/previews/overlay. Regression test added:
binding_graph_subscription_survives_begin_initial_sync.

Co-Authored-By: Claude Fable 5 <[email protected]>
Union-merges the two feature threads through the node walk:
ui_node_with_product_previews now takes both extra_config (M3
binding-derived rows) and asset_editor (main's inline GLSL editor).

Produced value/product views keep main's SlotPane structure and regain
the branch's binding surface: bus-published slots wear
SlotPaneTreatment::Bound with the publish BindingChip in the body.

SlotPaneTreatment::Bound is re-pointed from the green status-good
family to the violet status-bound family (bound=violet convention;
green is reserved for good/valid). assets/tailwind.css regenerated
(tailwindcss 4.1.5) — additive: status-bound tokens + branch utilities.

Story-image conflicts resolved to main's copies pending baseline regen.

Co-Authored-By: Claude Fable 5 <[email protected]>
Captures the merged SlotPane surfaces: bound produced values/products
wear the violet status-bound treatment (border, header wash, publish
chip); bus-pane and node stories re-baselined against main's SlotPane
layout.

Co-Authored-By: Claude Fable 5 <[email protected]>
Implements ADR 2026-07-08-binding-ref-syntax-and-channel-naming (the
D9 norms conversation outcome):

- bus:<channel> replaces bus#…; BusSlotRef stores ChannelName (the bus
  is no longer a pretend node with slots); # reserved in bus refs for
  future field-within-channel fragments.
- node:<path>#<slot.path> replaces slashless dot refs: RelativeNodeRef
  is filesystem-style (../shader, ..#, child/grandchild, multi-hop
  ../..), slot paths stay dotted. Unknown schemes fail loudly.
- Channels go unitless: time (was time.seconds); unit truth lives in
  slot metadata + the future well-known registry. ChannelName docs now
  describe the real convention (purpose[.in|.out][/instance]) instead
  of the dead <kind>/<dir>/<index> letter.
- Hard cutover: examples, test projects, embedded test JSON, story
  fixtures, smoke project, loader default-binding helpers, UI display
  strings. No dual-parse (no wire/disk compat during heavy dev).

Validation: lpc-model 351, lpc-engine 287, studio-core 384, wire/
registry/shared suites, fw-tests scene_render_emu, schema-gen no
drift, just check green.

Co-Authored-By: Claude Fable 5 <[email protected]>
…rename

Also: raise the story ready-state cap to 30s (polls every 50ms, so fast
stories pay nothing; heavy popover stories kept blowing the 10s cap and
killing baseline runs) and note the syntax change atop the two ADRs
that still show the old forms.

Co-Authored-By: Claude Fable 5 <[email protected]>
Each channel now wears the shared slot-pane language (D10 — one binding
treatment everywhere): violet Bound frame, channel name + kind/PRIMARY
badges in the title bar, live value as the centered hero, wiring chips
(writers → readers, DEF badges, click-to-focus) beneath.

The channel detail popup gains UiBusChannelView::visible_aspects: a
Channel info section (bus:<name>, kind, value, primary role) and a
Wiring section that spells out multi-writer merge semantics (D11 —
merge modes and multiple sources clearly indicated).

SlotPane gains an optional `badges` title-bar slot — the shared home
for kind tags and PRIMARY/DEF badges across binding surfaces (M5's
honest indicator will reuse it).

Co-Authored-By: Claude Fable 5 <[email protected]>
…o popup

Gate-review feedback: the main display shows just the value hero; the
writers → readers mapping lives in the detail popup. To keep D11 (no
dead ends), UiSlotAspectRow gains an optional `action` and the popup
renders action-carrying rows as clickable violet focus chips —
on_action now threads SlotPane → SlotDetailButton → rows, which is the
same conduit M4's popover actions will use. The Wiring aspect renders
as a list section instead of the single-endpoint "Bound from" header.

Adds a channel-detail story (popup open on the two-writer trigger
channel) so the wiring popup has baseline coverage.

Co-Authored-By: Claude Fable 5 <[email protected]>
…ault

ADR-2 for the binding/bus roadmap M5, user-approved at the gate:
default_bind as a first-class field-shape descriptor field (attr for
static fields, data field for dynamic slot-defs), bus:-only endpoints
with direction derived from slot direction, D8 declared fixture/output
inputs with defaults, one generic unconditional materialization pass,
and the poc helper deletion list.

Co-Authored-By: Claude Fable 5 <[email protected]>
ShaderState.output and FixtureState.output were direction Local — latent
wrong metadata that direction-driven default policy (ADR 2026-07-09)
would turn into wrong wiring. Marked #[slot(produced)] and added the
missing default_policy container attrs; shape dumps regenerated.

Two guardrails so it cannot rot again:
- shape_guardrails test walks the entire static catalog and asserts
  every product-carrying field declares a direction (verified it
  catches the original mislabel).
- load-time assertion in the project loader: a binding draft sourcing a
  produced slot (or targeting a consumed slot) must match the declared
  shape direction; undeclared/dynamic slots pass through.

Co-Authored-By: Claude Fable 5 <[email protected]>
Pins the five loader helpers' observable behavior: minimal-clock time
default (seconds only), authored targets/sources suppressing defaults,
shader time + visual.out defaults, the shader-without-clock case, the
fluid has_default_time_bus gate (flagged inline as deliberately
flipping under ADR 2026-07-09), and playlist entry children being
ownership-suppressed from default-publishing visual.out.

Co-Authored-By: Claude Fable 5 <[email protected]>
…s (M5)

Implements ADR 2026-07-09-declarative-default-bindings:

- #[slot(default_bind = "bus:…")] on static fields surfaces into the
  slot shape (new first-class field on SlotFieldShape /
  StaticSlotFieldShape beside semantics+policy); the derive validates
  the bus: prefix lexically, the shape-walk guardrails validate the
  real grammar and require a declared direction.
- ShaderSlotDef gains default_bind (typed BindingRef); the create
  templates and all convention-reliant example shaders declare
  "default_bind": "bus:time" explicitly — the global "any unbound f32
  time" convention is retired.
- Declared defaults: clock seconds → bus:time; shader/fluid/playlist
  outputs → bus:visual.out; fixture output → bus:control.out; NEW
  fixture.input ← bus:visual.out and output.input ← bus:control.out
  (D8: the last implicit slots become declared schema; authored inputs
  now optional — basic nodes wire themselves).
- One generic materialization pass replaces the five loader helpers:
  authored wins, entry-owned children suppress produced defaults,
  everything registers unconditionally at fallback priority (the
  fluid has_default_time_bus gate is deleted — an unfilled channel is
  surfaced on the bus instead of hidden; fluid node tests gain the
  clock they always semantically required).
- origin: default on the wire is priority-derived — M2 contract
  untouched. Characterization tests updated where the ADR deliberately
  changed behavior; schemas + shape dumps regenerated.

Co-Authored-By: Claude Fable 5 <[email protected]>
…(M5)

Per-slot indicators now tell the truth about defaults: after every
applied view, ProjectController overlays graph-derived origin:default
bindings onto the node controllers through the existing binding-facts
channel. Slots the authored pass left direct/unpublished get a
default-origin endpoint — authored facts always win, so authored
overrides drop the DEF automatically.

UiBindingEndpoint carries default_origin: the BindingChip wears a DEF
badge (tooltip explains), the config-slot popover gains an "Origin:
default binding" row ("declared by the slot (default_bind); authoring a
binding overrides it"), and produced popups append the same detail.

Controller tests cover fill + authored-wins; bound_default story added;
baselines regenerated.

Co-Authored-By: Claude Fable 5 <[email protected]>
…try (M4)

- lpc-model gains the well-known channel registry (name, kind, doc for
  time/trigger/visual.out/control.out) — the D9 norms outcome: channels
  stay lazily created, the picker teaches the norm.
- Bindable rows (root def config slots, produced values/products, and
  M3 binding-derived rows) carry UiBindingAuthoring: binding key,
  derived direction (consumed → source, produced → target), the
  bindings-map address, and the authored endpoint when one exists —
  default-origin wiring offers Bind, not Retarget, and unbinding an
  authored entry re-enables the default.
- ProjectEditorView carries channel choices (observed ∪ well-known).
- Derived rows fixed along the way: default-origin wiring now wears the
  DEF endpoint flag (closes the idle-shader DEF gap from M5's DoD) and
  literal-sourced loader plumbing (the output demand constant) no
  longer leaks a row.

Co-Authored-By: Claude Fable 5 <[email protected]>
The slot detail popover gains a Bind section for every bindable row
(config slots, produced values/products, binding-derived rows): Bind…
opens the channel picker; authored entries offer Retarget…/Unbind.
Everything dispatches the existing slot-edit gestures against the
node's bindings map (EnsurePresent entry → EnsurePresent endpoint
option → SetValue; Unbind = RemoveValue, which re-enables any
slot-declared default) — no new wire surface, edits ride the normal
overlay/save path.

The picker lists observed ∪ well-known channels (kind tags, registry
docs, WK markers, kind-mismatch hints against the current channel) and
accepts validated free text — channels stay lazily created; the picker
teaches the norm (D9). Choices flow from ProjectEditorView through a
workspace-provided context so every popover shares one list.

Stories: unbound / authored / open-picker states.

Co-Authored-By: Claude Fable 5 <[email protected]>
Unbound / authored / open-picker states, plus popover-affected story
refreshes.

Co-Authored-By: Claude Fable 5 <[email protected]>
Bindings were load-time materializations: the incremental apply's
deliberate `NodeDefChangeKind::Body => {}` no-op is right for
resolver-read values but silently ignored `bindings` map edits, so M4's
bind/unbind/retarget (and any default_bind change) only took effect on
reload — a new channel never appeared on the bus, unbinding never
stopped motion.

Option C (user-approved): the loader's binding registration is split
out of the attach arms into a dedicated binding phase
(register_projected_bindings), and apply_project_changes now ends every
non-empty apply by clearing the binding index and re-running that phase
against current defs. Cheap (dozens of entries), no per-node diffing,
runtime node state untouched, and by construction identical to a fresh
load — pinned by a load-parity assertion.

Covers the selective-reattach path too (reattached nodes previously
re-registered via their arms; now the rebuild does it). Error-projected
defs keep their attach-arm tolerance. Tests: authored binding applies
incrementally + suppresses the declared default + node not recreated +
parity with fresh load; unbind re-materializes the slot-declared
default. Full characterization suite unchanged and green.

Co-Authored-By: Claude Fable 5 <[email protected]>
…t locking

Semantic conflict resolutions beyond the textual ones:
- studio_controller: main's home-gallery view() with the bus pane inserted
  (panes = project, bus, device, firmware).
- embedded_example: main hardcoded the demo as "Basic"/examples/basic, but
  this branch's demo is examples/fyeah-sign — gallery entry renamed to
  "Fyeah Sign" and home tests updated to match.
- studio-story-pngs.mjs: main's BeginFrame kick for headless capture kept
  alongside this branch's 30s CDP call cap.
- tailwind.css regenerated with pinned [email protected]; drifted story
  baselines re-captured.

Co-Authored-By: Claude Fable 5 <[email protected]>
…iltins

Conflicts were three story-baseline PNGs (both sides re-captured) — resolved
by a fresh capture. The auto-merged generated tailwind.css had lost a closing
brace; regenerated with pinned [email protected].

Co-Authored-By: Claude Fable 5 <[email protected]>
CI-only breakage (schema_gen is feature-gated, so plain cargo test never
compiled it): a lpc-view test initializer missed the new default_bind
field, and relative_node_ref_schema still described the old dotted
grammar. The pattern now mirrors RelativeNodeRef::parse (optional ./,
leading .. hops, slash-separated names), and the conformance cases match
the parser's actual accept/reject behavior.

Co-Authored-By: Claude Fable 5 <[email protected]>
The build script walked up from OUT_DIR to find the workspace Cargo.toml,
which breaks under a configured `build.build-dir` (cargo ≥1.89): OUT_DIR
then lives outside the workspace entirely and the walk panics
("workspace root"). CARGO_MANIFEST_DIR is always inside the workspace,
so walk up from there instead; the two-levels-up OUT_DIR heuristic goes
away. The embedded lps-builtins-emu-app ELF is a final artifact and is
still read from <workspace>/target/, which build-dir does not relocate.

Co-Authored-By: Claude Fable 5 <[email protected]>
Eight items from Yona's 2026-07-15 gate review (record in
Planning/lp2025/2026-07-15-1000-continue-binding-design/):

- DEF badge leaves the main-UI chip; the detail popup explains default
  wiring explicitly (shared Origin row: "Declared by the slot itself;
  authoring a binding overrides it").
- Incoming bindings mirror the outgoing treatment: `⟷ → chan` publishes,
  `⟷ ← chan` reads.
- New bus glyph (Lucide Waypoints) on binding chips, bus channel card
  titles (SlotPane title_icon), and picker rows.
- "Bound from" → "Reading from" on chips and popup headings.
- Bus card values use the shared fixed-decimal display (new
  value_display.rs) so live readings stop jittering.
- The slot popup's binding story is one block: BindingAuthoringSection
  now folds in the current Binding aspect (headline + origin + routes)
  above Bind…/Edit…/Unbind. Retarget… is renamed Edit…; default wiring
  offers Edit-to-override.
- Enter submits the free-text channel field.
- Produced-value popups drop the live Value row (the pane hero owns the
  changing reading); popup keeps identity/shape/unit.

Also fixes the WK-badge clipping in picker rows (overflow containment).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds the new binding-authoring default-bound story captures; the wider
churn matches the usual whole-set regen behavior.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Yona-Appletree and others added 5 commits July 15, 2026 16:10
… editor UX

Brings in PR #98 (fw-browser: enable naga feature so the standalone
sidecar compiles shaders — fixes every non-clock node erroring in
studio-dev), PR #95 (DeviceSession refactor), PR #78 (GLSL editor UX),
and the interp.f32 filetest work.

Conflicts: import unions in lpa-studio-core lib.rs/studio_controller.rs
(main dropped the old link types; bus-pane exports kept, UiPaneView
re-added for bus_pane()); lpvm-cranelift build.rs (same fix on both
sides — kept the commented HEAD copy); story-baseline PNGs taken from
main and re-captured after the merge. The auto-merged generated
tailwind.css was discarded and regenerated with pinned [email protected]
(braces verified balanced).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Every non-clock node erroring with no visible cause (gate follow-up):
the runtime always carried the message (NodeRuntimeStatus::Error/
InitError/Warn(String)) and node_status_view kept it, but ui_node_view
dropped the detail when building UiNodeHeader — the popover already
rendered header.detail and the tree-row tooltip already appended it.
Populate header.detail from the status detail so the popup answers WHY.

Adds the error-detail-popup story (open popup on an erroring shader) and
a controller test pinning the pipeline. Also carries the post-merge
UiPaneView import fix and the tailwind class the new story needs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…l story

Includes the new error-detail-popup captures and refreshes the panes the
main merge and error-detail work touched.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Clicking anything on home could kill the whole app with "keyed siblings
must each have a unique key": device cards were keyed by NAME, and names
repeat — erasing and re-provisioning a board registers a new dev_… uid
under the same name. Dioxus only diffs on re-render, so the gallery
mounted fine and died on the first interaction, which read as "delete
does nothing / example cards do nothing".

- Key device cards by uid (render_key: uid, falling back to name for
  the single identity-less live card).
- Defensively dedupe device/project cards by render key at view build
  (warn + drop) so a corrupt registry or store degrades to a missing
  card, never a dead UI.

Verified in the sim: remix create → gallery → menu → Delete works
("Project deleted", card gone).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@Yona-Appletree
Yona-Appletree marked this pull request as ready for review July 16, 2026 07:18
Yona-Appletree and others added 6 commits July 16, 2026 00:20
…gate

Brings in PR #92 (shader auto-apply + split-bar editor chrome), PR #102
(popover chrome as one SVG merged-outline path), PR #101 (auto-apply ADR),
PR #100 (path-gated wgpu CI job), PR #99 (interp.f32 in DEFAULT_TARGETS).

Conflicts were story-baseline PNGs only (both sides re-captured) — taken
from main and re-captured from the merged tree in the follow-up commit.
The auto-merged generated tailwind.css was discarded and regenerated with
pinned [email protected] (braces verified balanced).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Brings in PR #105 (GLSL editor autocomplete — generated builtin manifest
+ uniforms) and PR #97 (device-link M5: native espflash-lib manage(),
unified cli_connect, fwcheck/calibrate on shared abstractions).

Conflicts: lpa-studio-core lib.rs export list — a pure union (this
branch's binding-authoring types + main's UiShaderUniform); one story
baseline PNG taken from main and re-captured in the follow-up commit.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Brings in PR #103 (weld popover panel to the trigger's visible edge) and
PR #104 (deployed projects survive reboot + host disconnect).

Conflicts were story-baseline PNGs only (main's popover change re-captured
them) — taken from main and re-captured from the merged tree in the
follow-up commit.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@Yona-Appletree
Yona-Appletree merged commit d9bcfe1 into main Jul 16, 2026
3 checks passed
Yona-Appletree added a commit that referenced this pull request Jul 16, 2026
…rapper

The inner fn owns them now; the path-gated clippy-gfx job (-D warnings)
rejected the leftovers. Also merges origin/main (PR #86 landed).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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