Skip to content

Migrate wire shape to unified fallbacks array#98

Merged
aarne merged 6 commits intowire_fix99from
copilot/change-wire-shape-fallbacks
Mar 5, 2026
Merged

Migrate wire shape to unified fallbacks array#98
aarne merged 6 commits intowire_fix99from
copilot/change-wire-shape-fallbacks

Conversation

Copy link
Contributor

Copilot AI commented Mar 5, 2026

Replaces the separate falsyFallback/falsyFallbackRefs/falsyControl/nullishFallback/nullishFallbackRefs/nullishControl properties on Wire with a single ordered fallbacks: WireFallback[] array. This enables mixing || and ?? operators in any order within a single wire expression.

o.label <- a.label ?? b.label || c.label ?? "default"
export interface WireFallback {
  type: "falsy" | "nullish";
  ref?: NodeRef;
  value?: string;
  control?: ControlFlowInstruction;
}

Type system (bridge-core)

  • Added WireFallback interface, replaced 6 per-variant fallback properties with fallbacks?: WireFallback[]
  • resolveWires.ts: Collapsed applyFalsyGate + applyNullishGate into applyFallbackGates — iterates array left-to-right, checking gate type per entry
  • tree-utils.ts: Simplified getSimplePullRef fast-path to !w.fallbacks?.length

Parser (bridge-parser)

  • New coalesceChainItem grammar rule wrapping || alt and ?? alt into a single OR, replacing the two separate MANY blocks that forced all || before all ??
  • All 4 grammar contexts updated (wire, element, scope, alias)
  • Visitor produces ordered fallbacks[] preserving authored interleaving
  • Serializer reads from fallbacks[], emitting || or ?? based on type

Compiler (bridge-compiler)

  • applyFallbacks method iterates w.fallbacks sequentially, emitting || or ?? JS operators per entry

Tests

  • 994 tests pass (was 987 — added 7 mixed-chain integration tests)
  • Gate unit tests rewritten for applyFallbackGates with 5 new mixed-chain cases
  • Round-trip serializer tests for mixed chains
  • Parser assertion tests verifying fallbacks array shape
Original prompt

This section details on the original issue you should resolve

<issue_title>Change wire shape</issue_title>
<issue_description>Migrate away from falsyFallback and nullishFallback to a unified fallbacks array that will allow mixing ?? and || in any order.

fallback logic will be processed as

      // Layer 1: Execution
      let value = await evaluateWireSource(ctx, w, pullChain);

      // Layer 2: Fallback Gates
      if (w.fallbacks) {
        for (const fallback of w.fallbacks) {
          const isFalsyGateOpen = fallback.type === 'falsy' && !value;
          const isNullishGateOpen = fallback.type === 'nullish' && value == null;

          if (isFalsyGateOpen || isNullishGateOpen) {
            if (fallback.control) return applyControlFlow(fallback.control);
            if (fallback.ref) {
              value = await ctx.pullSingle(fallback.ref, pullChain);
            } else if (fallback.value !== undefined) {
              value = coerceConstant(fallback.value);
            }
          }
        }
      }

new wire types

/**
 * A wire connects a data source (from) to a data sink (to).
 * Execution is pull-based: when "to" is demanded, "from" is resolved.
 *
 * Constant wires (`=`) set a fixed value on the target.
 * Pull wires (`<-`) resolve the source at runtime.
 * Pipe wires (`pipe: true`) are generated by the `<- h1:h2:source` shorthand
 * and route data through declared tool handles; the serializer collapses them
 * back to pipe notation.
 * Spread wires (`spread: true`) merge source object properties into the target.
 */
export interface WireFallback {
  type: "falsy" | "nullish";
  ref?: NodeRef;
  value?: string;
  control?: ControlFlowInstruction;
}

export type Wire =
  | {
      from: NodeRef;
      to: NodeRef;
      pipe?: true;
      /** When true, this wire merges source properties into target (from `...source` syntax). */
      spread?: true;
      safe?: true;
      fallbacks?: WireFallback[];
      catchFallback?: string;
      catchFallbackRef?: NodeRef;
      catchControl?: ControlFlowInstruction;
    }
  | { value: string; to: NodeRef }
  | {
      cond: NodeRef;
      thenRef?: NodeRef;
      thenValue?: string;
      elseRef?: NodeRef;
      elseValue?: string;
      to: NodeRef;
      fallbacks?: WireFallback[];
      catchFallback?: string;
      catchFallbackRef?: NodeRef;
      catchControl?: ControlFlowInstruction;
    }
  | {
      /** Short-circuit logical AND: evaluate left first, only evaluate right if left is truthy */
      condAnd: {
        leftRef: NodeRef;
        rightRef?: NodeRef;
        rightValue?: string;
        safe?: true;
        rightSafe?: true;
      };
      to: NodeRef;
      fallbacks?: WireFallback[];
      catchFallback?: string;
      catchFallbackRef?: NodeRef;
      catchControl?: ControlFlowInstruction;
    }
  | {
      /** Short-circuit logical OR: evaluate left first, only evaluate right if left is falsy */
      condOr: {
        leftRef: NodeRef;
        rightRef?: NodeRef;
        rightValue?: string;
        safe?: true;
        rightSafe?: true;
      };
      to: NodeRef;
      fallbacks?: WireFallback[];
      catchFallback?: string;
      catchFallbackRef?: NodeRef;
      catchControl?: ControlFlowInstruction;
    };

```</issue_description>

<agent_instructions>this is a rather large change :)

- make sure to rely on tests
- write a changeset
- add new tests for cases of mixed wires like A ?? B || C ?? D
- make sure playground has examples for mixed wires
- syntax highlighting in LSP and code-mirror needs updates also

good luck ... i believe in you
gemini 3.1 pro already failed on tis one</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@changeset-bot
Copy link

changeset-bot bot commented Mar 5, 2026

🦋 Changeset detected

Latest commit: f248857

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@stackables/bridge-core Minor
@stackables/bridge-parser Minor
@stackables/bridge-compiler Minor
@stackables/bridge Minor
@stackables/bridge-graphql Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

… tests

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 5, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bridge f248857 Commit Preview URL

Branch Preview URL
Mar 05 2026, 08:10 PM

Copilot AI and others added 4 commits March 5, 2026 19:51
…] pattern

Replace old separate falsyFallback, falsyFallbackRefs properties with the
unified fallbacks array in test assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace all separate falsyFallback*/nullishFallback* properties across
~13 locations in parser.ts with a single fallbacks: WireFallback[] array.
Also update bridge-format.ts serializer and export WireFallback from
bridge-core.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lbacks array

Update bridge-compiler codegen.ts and related test files to use the new
Wire.fallbacks?: WireFallback[] property instead of the old separate
falsyFallback/nullishFallback/falsyControl/nullishControl/falsyFallbackRefs/
nullishFallbackRefs properties.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Copilot AI changed the title [WIP] Update fallback logic to unify falsy and nullish handling Migrate wire shape to unified fallbacks array Mar 5, 2026
Copilot finished work on behalf of aarne March 5, 2026 20:11
@aarne aarne marked this pull request as ready for review March 5, 2026 20:20
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

🐰 Bencher Report

Branchcopilot/change-wire-shape-fallbacks
Testbedubuntu-latest

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkLatencynanoseconds (ns)
compiled: absolute baseline (passthrough, no tools)📈 view plot
⚠️ NO THRESHOLD
0.00 ns
compiled: array + tool-per-element 10📈 view plot
⚠️ NO THRESHOLD
0.01 ns
compiled: array + tool-per-element 100📈 view plot
⚠️ NO THRESHOLD
0.03 ns
compiled: chained 3-tool fan-out📈 view plot
⚠️ NO THRESHOLD
0.00 ns
compiled: flat array 10 items📈 view plot
⚠️ NO THRESHOLD
0.01 ns
compiled: flat array 100 items📈 view plot
⚠️ NO THRESHOLD
0.01 ns
compiled: flat array 1000 items📈 view plot
⚠️ NO THRESHOLD
0.07 ns
compiled: nested array 10x10📈 view plot
⚠️ NO THRESHOLD
0.02 ns
compiled: nested array 20x10📈 view plot
⚠️ NO THRESHOLD
0.03 ns
compiled: nested array 5x5📈 view plot
⚠️ NO THRESHOLD
0.01 ns
compiled: short-circuit (overdefinition bypass)📈 view plot
⚠️ NO THRESHOLD
0.00 ns
compiled: simple chain (1 tool)📈 view plot
⚠️ NO THRESHOLD
0.00 ns
exec: absolute baseline (passthrough, no tools)📈 view plot
⚠️ NO THRESHOLD
0.00 ns
exec: array + tool-per-element 10📈 view plot
⚠️ NO THRESHOLD
0.08 ns
exec: array + tool-per-element 100📈 view plot
⚠️ NO THRESHOLD
0.68 ns
exec: chained 3-tool fan-out📈 view plot
⚠️ NO THRESHOLD
0.02 ns
exec: flat array 10 items📈 view plot
⚠️ NO THRESHOLD
0.01 ns
exec: flat array 100 items📈 view plot
⚠️ NO THRESHOLD
0.07 ns
exec: flat array 1000 items📈 view plot
⚠️ NO THRESHOLD
0.66 ns
exec: nested array 10x10📈 view plot
⚠️ NO THRESHOLD
0.12 ns
exec: nested array 20x10📈 view plot
⚠️ NO THRESHOLD
0.24 ns
exec: nested array 5x5📈 view plot
⚠️ NO THRESHOLD
0.05 ns
exec: short-circuit (overdefinition bypass)📈 view plot
⚠️ NO THRESHOLD
0.00 ns
exec: simple chain (1 tool)📈 view plot
⚠️ NO THRESHOLD
0.01 ns
parse: large bridge (20 handles x 5 wires)📈 view plot
⚠️ NO THRESHOLD
1.10 ns
parse: simple bridge📈 view plot
⚠️ NO THRESHOLD
0.04 ns
🐰 View full continuous benchmarking report in Bencher

@aarne aarne merged commit 347824d into wire_fix99 Mar 5, 2026
7 of 10 checks passed
@aarne aarne deleted the copilot/change-wire-shape-fallbacks branch March 5, 2026 20:26
aarne added a commit that referenced this pull request Mar 5, 2026
* Fix multi ??

* Migrate wire shape to unified fallbacks array (#98)

* Initial plan

* Migrate Wire type to unified fallbacks array, update runtime and gate tests

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>

* Update resilience test assertions to use new fallbacks: WireFallback[] pattern

Replace old separate falsyFallback, falsyFallbackRefs properties with the
unified fallbacks array in test assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Refactor parser to use unified fallbacks: WireFallback[] pattern

Replace all separate falsyFallback*/nullishFallback* properties across
~13 locations in parser.ts with a single fallbacks: WireFallback[] array.
Also update bridge-format.ts serializer and export WireFallback from
bridge-core.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Migrate codegen from separate falsy/nullish properties to unified fallbacks array

Update bridge-compiler codegen.ts and related test files to use the new
Wire.fallbacks?: WireFallback[] property instead of the old separate
falsyFallback/nullishFallback/falsyControl/nullishControl/falsyFallbackRefs/
nullishFallbackRefs properties.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add mixed wire chain tests, playground example, and changeset

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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.

2 participants