Skip to content

emit_streams_for silently drops AES67 streams on card-slot interfaces #10

@reidwwall

Description

@reidwwall

Summary

emit_streams_for in crates/patchlang/src/builder/canvas_emit.rs:742 silently drops AES67 streams whose interface_id references a card-slot interface (i.e., a port contributed by an installed expansion card rather than the chassis template).

Repro

Setup: any device with cardSlotGroups and an installed AES67 card. Concrete example from a real project — Riedel Artist 64 with an AES67-108 G2 card installed in Client_Slot[1]. User adds an AES67 stream on the card's AES67_Out port.

The frontend serializes the stream as:

{
  "label": "Artist_64_to_QSYS",
  "protocol": "AES67",
  "channel_count": 8,
  "interface_id": "slot::Client::0__1__pl::AES67_108_G2::AES67_Out"
}

Expected: a stream declaration appears in the emitted .patch referencing the card-slot port.

Actual: no stream declaration is emitted. The AES67 stream silently disappears, and reload from the resulting .patch returns no streams for that instance.

Root cause

// canvas_emit.rs:742
for stream in streams {
    let Some(iface) = inst.interfaces.iter().find(|i| i.id == stream.interface_id) else {
        continue;  // ← drops the stream
    };
    ...
}

inst.interfaces contains only chassis interfaces. The compound interface_id produced by computeEffectiveInterfaces (frontend) for card ports doesn't match anything in that list, so the else continue fires and the AES67 stream is silently lost. There's no diagnostic, error, or warning.

This contradicts the design-guide spec (docs/patchlang-design-guide/compiler.md:464) which says card ports flat-merge into the instance's effective namespace, and the corresponding S03/S04/S05 rules that route/connect/bus emission already use effective ports.

Verification

Diagnosis cross-checked against:

  • patchlang-architecture skill — confirmed the sidecar-as-fallback frontend workaround is an explicit anti-pattern and that "fix the root cause" belongs in PatchLang
  • signalcanvas-patchlang skill (v0.2.8) — confirmed the spec says card ports flat-merge with no slot-qualified syntax, consistent with this bug being an implementation gap rather than a spec ambiguity

Live diagnostics from a running project (browser DevTools, in-memory canvasScene store) confirmed:

  • Card-slot AES67 streams have compound interfaceId containing __ (e.g., slot::Client::0__1__pl::AES67_108_G2::AES67_Out)
  • Chassis-port streams have interfaceId of the form pl::TemplateName::PortName (no __) and round-trip cleanly — only card-slot AES67 streams are affected

Likely broader scope

Other stream-capable card protocols (Dante, QLAN, Ravenna) on card-slot ports almost certainly hit the same path. Beyond streams, the same pattern probably needs review in:

  • emit_connections_for — connections to/from card-slot ports
  • emit_channel_labels — labels on card-slot ports
  • emit_routes_for — internal routes on card-slot ports

We haven't confirmed those break — only flagged them. May warrant a sweep through canvas_emit.rs.

Suggested fix

Resolve compound interface_id values against installed_cards before falling back to "drop." Either:

  1. Build an effective interface lookup at the top of emit_streams_for (chassis + installed-card ports), keyed by the same compound ID format the frontend uses, OR
  2. Parse the compound form {slotId}__{cardIfaceId} and look up the card port directly via the slot/card lookup tables.

Should also probably emit a diagnostic if a stream's port can't be resolved at all, instead of silent continue.

Frontend workaround in place

To unblock users, the SignalCanvas frontend now stores card-slot AES67 streams in the .layout.json sidecar and restores them on load (restoreSidecarCardSlotStreams in loadFromPatchLangHelpers.ts). This violates the patchlang-architecture principle that the sidecar holds positions only — it's marked TEMP and pointed at this issue. Delete the workaround once the Rust fix ships.

Affected version

WASM rebuilt from SignalCanvasLang master at SignalCanvas commit cfff868 (May 5, 2026). Master HEAD: 7bd94ed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions