Skip to content

Fix ?. + chained || literal fallback parsing to preserve left-to-right short-circuit semantics#94

Merged
aarne merged 4 commits intomainfrom
copilot/fix-query-output-logics
Mar 5, 2026
Merged

Fix ?. + chained || literal fallback parsing to preserve left-to-right short-circuit semantics#94
aarne merged 4 commits intomainfrom
copilot/fix-query-output-logics

Conversation

Copy link
Contributor

Copilot AI commented Mar 5, 2026

source?.path || "A" || "B" was resolving to "B" instead of "A" in cases where the safe pull produced undefined. The parser was collapsing chained || alternatives in a way that lost first-truthy behavior for literal fallbacks.

  • Problem scope

    • Safe navigation (?.) correctly converts source errors to undefined, but chained || literal fallbacks were not preserving authored short-circuit order.
    • This caused later literals to override earlier truthy literals in some parsed wire shapes.
  • Parser behavior update (packages/bridge-parser/src/parser/parser.ts)

    • While collecting nullAlt (||) alternatives for a wire:
      • Stop processing additional alternatives once a truthy literal fallback is encountered.
      • Clear a previously captured literal fallback when a later source/control alt is parsed, so stale falsy literals do not survive chain continuation.
  • Regression coverage (packages/bridge/test/coalesce-cost.test.ts)

    • Added an end-to-end case for chained literals after safe navigation to ensure first-truthy fallback wins.
    • Added an additional mixed-chain regression to confirm strict left-to-right semantics across || and ??:
      • const.lorem.kala || const.lorem.ipsums?.mees || "B" ?? "C" resolves to "B".
version 1.5
const lorem = {
  "ipsum": "dolor sit amet",
  "consetetur": 8.9
}

bridge Query.lookup {
  with const
  with output as o

  o.label <- const.lorem.ipsums?.kala || "A" || "B"
}

Expected result for o.label: "A".

  • Release metadata
    • Added a user-facing changeset for this fix:
      • .changeset/tough-cups-smoke.md
      • patch bumps for @stackables/bridge and @stackables/bridge-parser.

> [!WARNING]
>
>


🔒 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: 9d0f23d

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

This PR includes changesets to release 2 packages
Name Type
@stackables/bridge Patch
@stackables/bridge-parser 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

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 9d0f23d Commit Preview URL

Branch Preview URL
Mar 05 2026, 05:47 PM

Co-authored-by: aarne <82001+aarne@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix logic error in Query.greet output Fix ?. + chained || literal fallback parsing to preserve left-to-right short-circuit semantics Mar 5, 2026
Copilot finished work on behalf of aarne March 5, 2026 17:35
@aarne
Copy link
Contributor

aarne commented Mar 5, 2026

@copilot

version 1.5
const lorem = {
"ipsum": "dolor sit amet",
"consetetur": 8.9
}

bridge Query.lookup {
with const
with output as o

o.label <- const.lorem.kala || const.lorem.ipsums?.mees || "B" ?? "C"
}

now the answer is B, right?

|| and ?? chain is always processed strictly from left to right.

=====

you forgot to add a changeset as this needs to be released to users also

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

Copilot AI commented Mar 5, 2026

@copilot

version 1.5
...

Yes — with strict left-to-right evaluation, that expression resolves to "B" (|| chain yields "B", then ?? "C" does not apply). I added a regression test for this exact case and added the missing user-facing changeset in commit 9d0f23d.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • api.open-meteo.com
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA est/*.test.ts (dns block)
  • dummyjson.com
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA est/*.test.ts (dns block)
  • nominatim.openstreetmap.org
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA est/*.test.ts (dns block)
  • transport.opendata.ch
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA est/*.test.ts (dns block)
  • v6.db.api.bahn.guru
    • Triggering command: /opt/hostedtoolcache/node/24.14.0/x64/bin/node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --heap-prof-interval=524288 --cpu-prof-interval=1000 --test-coverage-functions=0 --test-concurrency=0 --inspect-publish-uid=stderr,http --inspect-port=127.0.0.1:9229 --experimental-transform-types --report-signal=SIGUSR2 --test-coverage-lines=0 --test-isolation=process --conditions=source --tls-cipher-list=TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA est/*.test.ts (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot finished work on behalf of aarne March 5, 2026 17:46
@aarne aarne marked this pull request as ready for review March 5, 2026 17:46
@aarne aarne merged commit 93bbb94 into main Mar 5, 2026
2 checks passed
@aarne aarne deleted the copilot/fix-query-output-logics branch March 5, 2026 17:52
@github-actions github-actions bot mentioned this pull request Mar 5, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 5, 2026

🐰 Bencher Report

Branchcopilot/fix-query-output-logics
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
nanoseconds (ns)
(Result Δ%)
Upper Boundary
nanoseconds (ns)
(Limit %)
compiled: absolute baseline (passthrough, no tools)📈 view plot
🚷 view threshold
0.00 ns
(-0.22%)Baseline: 0.00 ns
0.00 ns
(91.81%)
compiled: array + tool-per-element 10📈 view plot
🚷 view threshold
0.01 ns
(+0.11%)Baseline: 0.01 ns
0.01 ns
(94.09%)
compiled: array + tool-per-element 100📈 view plot
🚷 view threshold
0.03 ns
(-4.62%)Baseline: 0.03 ns
0.04 ns
(89.83%)
compiled: chained 3-tool fan-out📈 view plot
🚷 view threshold
0.00 ns
(-0.41%)Baseline: 0.00 ns
0.00 ns
(92.89%)
compiled: flat array 10 items📈 view plot
🚷 view threshold
0.00 ns
(-0.77%)Baseline: 0.01 ns
0.01 ns
(92.75%)
compiled: flat array 100 items📈 view plot
🚷 view threshold
0.01 ns
(-1.32%)Baseline: 0.01 ns
0.01 ns
(93.28%)
compiled: flat array 1000 items📈 view plot
🚷 view threshold
0.07 ns
(-1.60%)Baseline: 0.07 ns
0.07 ns
(93.85%)
compiled: nested array 10x10📈 view plot
🚷 view threshold
0.02 ns
(-0.43%)Baseline: 0.02 ns
0.02 ns
(93.77%)
compiled: nested array 20x10📈 view plot
🚷 view threshold
0.03 ns
(+0.26%)Baseline: 0.03 ns
0.04 ns
(94.89%)
compiled: nested array 5x5📈 view plot
🚷 view threshold
0.01 ns
(-1.05%)Baseline: 0.01 ns
0.01 ns
(93.16%)
compiled: short-circuit (overdefinition bypass)📈 view plot
🚷 view threshold
0.00 ns
(-0.53%)Baseline: 0.00 ns
0.00 ns
(91.65%)
compiled: simple chain (1 tool)📈 view plot
🚷 view threshold
0.00 ns
(-0.28%)Baseline: 0.00 ns
0.00 ns
(91.84%)
exec: absolute baseline (passthrough, no tools)📈 view plot
🚷 view threshold
0.00 ns
(+5.81%)Baseline: 0.00 ns
0.00 ns
(86.02%)
exec: array + tool-per-element 10📈 view plot
🚷 view threshold
0.08 ns
(+1.90%)Baseline: 0.08 ns
0.10 ns
(76.37%)
exec: array + tool-per-element 100📈 view plot
🚷 view threshold
0.71 ns
(+4.07%)Baseline: 0.68 ns
0.93 ns
(75.82%)
exec: chained 3-tool fan-out📈 view plot
🚷 view threshold
0.02 ns
(+15.15%)Baseline: 0.01 ns
0.02 ns
(84.82%)
exec: flat array 10 items📈 view plot
🚷 view threshold
0.01 ns
(-8.56%)Baseline: 0.02 ns
0.04 ns
(40.29%)
exec: flat array 100 items📈 view plot
🚷 view threshold
0.07 ns
(-24.26%)Baseline: 0.10 ns
0.32 ns
(23.14%)
exec: flat array 1000 items📈 view plot
🚷 view threshold
0.68 ns
(-30.92%)Baseline: 0.98 ns
3.87 ns
(17.58%)
exec: nested array 10x10📈 view plot
🚷 view threshold
0.12 ns
(-16.58%)Baseline: 0.15 ns
0.39 ns
(31.40%)
exec: nested array 20x10📈 view plot
🚷 view threshold
0.24 ns
(-17.60%)Baseline: 0.29 ns
0.79 ns
(29.90%)
exec: nested array 5x5📈 view plot
🚷 view threshold
0.05 ns
(-8.32%)Baseline: 0.05 ns
0.11 ns
(44.21%)
exec: short-circuit (overdefinition bypass)📈 view plot
🚷 view threshold
0.00 ns
(+6.96%)Baseline: 0.00 ns
0.00 ns
(94.22%)
exec: simple chain (1 tool)📈 view plot
🚷 view threshold
0.01 ns
(+13.62%)Baseline: 0.01 ns
0.01 ns
(87.46%)
parse: large bridge (20 handles x 5 wires)📈 view plot
🚷 view threshold
1.19 ns
(+20.09%)Baseline: 0.99 ns
1.36 ns
(87.16%)
parse: simple bridge📈 view plot
🚷 view threshold
0.04 ns
(+26.25%)Baseline: 0.03 ns
0.05 ns
(88.59%)
🐰 View full continuous benchmarking report in Bencher

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