Skip to content

fix(ladder): handle-branch corruption on add/delete/duplicate + QU/QD XML - #893

Merged
thiagoralves merged 1 commit into
developmentfrom
fix/ladder-handle-branch-bugs
Jun 24, 2026
Merged

fix(ladder): handle-branch corruption on add/delete/duplicate + QU/QD XML#893
thiagoralves merged 1 commit into
developmentfrom
fix/ladder-handle-branch-bugs

Conversation

@thiagoralves

@thiagoralves thiagoralves commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes four related handle-branch defects in the ladder editor. The headline bug: adding a coil to a counter block's primary output (e.g. Q/QU) corrupted an existing input-handle branch — the coil didn't appear on the output and the branch contact's connection to the block was severed.

Root causes & fixes

  1. Add-to-primary-output corrupts a handle branch (elements/serial, elements/utils)
    The serial predecessor for a main-line insert was resolved by array index (getPreviousElement). Handle-branch elements (e.g. a contact wired to a counter's R input) are interleaved in the node array between the block and the right rail, so a coil dropped on the output picked the branch contact as its predecessor and was spliced into the branch edge — dropping the coil and breaking the branch. getPreviousElement now skips branchContext nodes (mirroring getPreviousElementsByEdge) and is keyed by element id. Integrated with the existing edgeSourceId keyboard-selection path.

  2. handleBranches lost on load (store/slices/ladder/slice)
    handleBranches is runtime-only state (not persisted in .ld). It's now rebuilt from the graph on project load via deriveHandleBranches, so the first branch-aware edit no longer corrupts diagrams that contain handle branches.

  3. Duplicate rung corrupts branched blocks (store/slices/ladder/utils)
    Duplication rebuilt blocks at default dimensions without re-running the layout solver and dropped branchContext remapping on branch parallel nodes. Now re-runs updateDiagramElementsPosition and remaps the parallel branchContext.

  4. PLCopen XML maps all block-output branches to the primary pin (ladder-xml)
    Every block-output branch was serialized as connected to the primary output (QU). The serializer now resolves the actual edge sourceHandle, distinguishing QU/QD.

Testing

  • New regression unit test for getPreviousElement (branch-aware predecessor).
  • Existing ladder suites pass (ladder-slice, ladder-xml) — 165 tests under jest.
  • Headline fix verified live in the running editor: coil lands on the output, the R branch stays intact, no orphan edges (reproduced before/after).

Companion PR

Mirrored byte-identical to the shared surface in openplc-web: Autonomy-Logic/openplc-web#546 (same commit).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved ladder flow loading so branch connections are restored more reliably after importing and opening diagrams.
    • Duplicated ladder sections now keep parallel branches aligned and positioned correctly.
  • Bug Fixes

    • Fixed predecessor detection when inserting serial items, especially around placeholders and branch elements.
    • Improved ladder XML export for parallel connections so the correct handle information is preserved.
  • Tests

    • Added coverage for predecessor-resolution behavior to prevent regressions.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c7372ab2-d8bd-4a5e-8ded-6db872da5ef1

📥 Commits

Reviewing files that changed from the base of the PR and between 495e49c and 9f46101.

📒 Files selected for processing (7)
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/__tests__/get-previous-element.test.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts
  • src/frontend/store/slices/ladder/slice.ts
  • src/frontend/store/slices/ladder/utils/index.ts
  • src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/tests/get-previous-element.test.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts
  • src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts
  • src/frontend/store/slices/ladder/utils/index.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts
  • src/frontend/store/slices/ladder/slice.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts

Walkthrough

Updates ladder parallel-branch handling across load, serial insertion, rung duplication, and XML export by rebuilding runtime branch state, changing predecessor lookup to id-based traversal, preserving duplicated branch context, and deriving parallel formal parameters from the actual incoming edge.

Changes

Ladder Parallel Branch Correctness

Layer / File(s) Summary
deriveHandleBranches helper and load-time reconstruction
src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts, src/frontend/store/slices/ladder/slice.ts
deriveHandleBranches(rung) rebuilds handleBranches from branchContext data, dedupes by {blockId, handleId}, infers direction from outputHandles, and reconstructs ordered nodeIds. addLadderFlow now stores rebuilt handleBranches on each ingested rung.
getPreviousElement id-based serial spine lookup
src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts, src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts, src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/__tests__/get-previous-element.test.ts
getPreviousElement now takes a node id, filters out branch-context nodes from the serial spine, and returns the preceding main-line node. appendSerialConnection passes newElement.id, and tests cover branch, placeholder, and variable skipping.
duplicateLadderRung branchContext remapping and re-layout
src/frontend/store/slices/ladder/utils/index.ts
Duplicated parallel nodes now remap branchContext.blockId to the duplicated block id, and the duplicated rung is re-laid out with updateDiagramElementsPosition before returning.
XML formal parameter fix for parallel chains
src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts
formalParameterFromParallel(srcNode, parallelChain) resolves the sourceHandle from the incoming rung edge and replaces the previous outputConnector.id derivations at parallel-open and parallel-close serialization sites.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • JoaoGSP

Poem

🐇 I hop through branches, one by one,
IDs now guide my leafy run.
Parallel paths stay neat and true,
With handles found in every view.
A bunny nods: “The ladder sings—
no lost branch bits, no tangled things!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it misses required template sections like Pull request info, References, and the DOD checklist. Add the template sections, including issue/Jira references, the change description in bullet form, and a completed DOD checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main ladder bug fix and mentions the XML serialization change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ladder-handle-branch-bugs

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/frontend/store/slices/ladder/utils/index.ts (1)

5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer the @root/* path alias over deep relative imports.

The new import uses a deep relative path; the repo standard is the @root/* alias for ./src/*.

♻️ Suggested change
-import { updateDiagramElementsPosition } from '../../../../components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/diagram'
+import { updateDiagramElementsPosition } from '`@root/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/diagram`'

As per coding guidelines: "Use path alias @root/* to reference ./src/*".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/store/slices/ladder/utils/index.ts` at line 5, The import in the
ladder utils module uses a deep relative path instead of the repo-standard
`@root/`* alias. Update the import in index.ts to reference
updateDiagramElementsPosition through the `@root` alias so it points to the same
module under src/* without relative traversal.

Source: Coding guidelines

src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts (1)

1342-1359: 🎯 Functional Correctness | 🔵 Trivial

Use branchContext.direction directly

branchContext.direction is already persisted on each branch node, so this reconstruction can use it instead of inferring direction from outputHandles. That removes an unnecessary dependency on block handle metadata.

♻️ Use the persisted direction
-  const seen = new Map<string, { blockId: string; handleId: string }>()
+  const seen = new Map<string, { blockId: string; handleId: string; direction: 'input' | 'output' }>()
   for (const node of rung.nodes) {
     const ctx = (node.data as BasicNodeData).branchContext
     if (ctx?.blockId && ctx?.handleId) {
-      seen.set(`${ctx.blockId}::${ctx.handleId}`, { blockId: ctx.blockId, handleId: ctx.handleId })
+      seen.set(`${ctx.blockId}::${ctx.handleId}`, {
+        blockId: ctx.blockId,
+        handleId: ctx.handleId,
+        direction: ctx.direction,
+      })
     }
   }

   const branches: HandleBranch[] = []
-  for (const { blockId, handleId } of seen.values()) {
+  for (const { blockId, handleId, direction } of seen.values()) {
     const block = rung.nodes.find((n) => n.id === blockId)
     if (!block) continue
-    const data = block.data as BasicNodeData
-    const isOutput = (data.outputHandles ?? []).some((h) => h.id === handleId)
-    const direction: 'input' | 'output' = isOutput ? 'output' : 'input'
     const nodeIds = reconcileBranchNodeIds(rung, { blockId, handleId, direction, nodeIds: [] })
     if (nodeIds.length > 0) branches.push({ blockId, handleId, direction, nodeIds })
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts`
around lines 1342 - 1359, The branch direction reconstruction in
deriveHandleBranches is inferring input/output from block outputHandles instead
of using the persisted branchContext.direction on each node. Update the logic
that builds the seen map and branches to carry branchContext.direction through,
then pass that saved direction into reconcileBranchNodeIds and branch creation
so the code no longer depends on BasicNodeData.outputHandles.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts`:
- Around line 65-67: The predecessor lookup in serial element insertion can
still resolve to undefined, which later causes the connect logic to dereference
previousNode.id. In the serial ladder utilities around getPreviousElement and
the connectNodes flow, add a short-circuit guard immediately after resolving
previousNode so the function exits or skips linking when no predecessor is
found, and keep the existing path unchanged when a valid node is returned.

In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts`:
- Around line 89-97: getPreviousElement currently assumes a predecessor always
exists, but it can return undefined when newElementId is missing or is the first
serial node, while appendSerialConnection dereferences previousNode.id
unconditionally. Update getPreviousElement in the ladder utils index to return
Node | undefined, then add a guard in appendSerialConnection before calling
connectNodes so the fallback path handles the missing predecessor safely.

In `@src/frontend/store/slices/ladder/utils/index.ts`:
- Around line 223-231: The duplicate-rung flow in ladder/utils/index.ts needs
test coverage for the branch-specific remap and relayout behavior. Update
src/frontend/store/__tests__/ladder-slice.test.ts to add a case for
duplicateRung where branchContext.blockId is remapped correctly, and another
case that exercises the post-duplication updateDiagramElementsPosition pass so a
branch-expanded block re-expands and its nodes/edges are repositioned against
the duplicated rung’s actual node set.

In `@src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts`:
- Around line 101-104: The formal parameter resolution in
formalParameterFromParallel is ambiguous because it only matches on srcNode.id
plus membership in the parallel chain, so duplicate edges from the same source
can serialize to the wrong pin. Update the parallel traversal in ladder-xml.ts
to carry the конкретный selected edge/sourceHandle (or equivalent target-handle
context) into the nodes.map entries and use that instead of a broad
rungEdges.find lookup. Make the same disambiguation wherever this helper is
used, and add a regression test covering a block feeding multiple branches in
the same parallel chain.

---

Nitpick comments:
In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts`:
- Around line 1342-1359: The branch direction reconstruction in
deriveHandleBranches is inferring input/output from block outputHandles instead
of using the persisted branchContext.direction on each node. Update the logic
that builds the seen map and branches to carry branchContext.direction through,
then pass that saved direction into reconcileBranchNodeIds and branch creation
so the code no longer depends on BasicNodeData.outputHandles.

In `@src/frontend/store/slices/ladder/utils/index.ts`:
- Line 5: The import in the ladder utils module uses a deep relative path
instead of the repo-standard `@root/`* alias. Update the import in index.ts to
reference updateDiagramElementsPosition through the `@root` alias so it points to
the same module under src/* without relative traversal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f5ac2cca-f694-4e4a-8d0a-734c578db228

📥 Commits

Reviewing files that changed from the base of the PR and between b86c4f2 and 495e49c.

📒 Files selected for processing (7)
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/handle-branch/index.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/__tests__/get-previous-element.test.ts
  • src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts
  • src/frontend/store/slices/ladder/slice.ts
  • src/frontend/store/slices/ladder/utils/index.ts
  • src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts

Comment on lines 65 to 67
if (!previousNode) {
previousNode = getPreviousElement(
{ ...rung, nodes: newNodes, edges: newEdges },
newNodes.findIndex((n) => n.id === newElement.id),
)
previousNode = getPreviousElement({ ...rung, nodes: newNodes, edges: newEdges }, newElement.id)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against an undefined predecessor here.

getPreviousElement can return undefined (see root-cause note in utils/index.ts), after which previousNode.id at Line 93 would throw. A short-circuit before the connectNodes calls keeps this path safe.

🛡️ Bail out when no predecessor is resolved
   if (!previousNode) {
     previousNode = getPreviousElement({ ...rung, nodes: newNodes, edges: newEdges }, newElement.id)
   }
+  if (!previousNode) return { nodes: newNodes, edges: newEdges }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!previousNode) {
previousNode = getPreviousElement(
{ ...rung, nodes: newNodes, edges: newEdges },
newNodes.findIndex((n) => n.id === newElement.id),
)
previousNode = getPreviousElement({ ...rung, nodes: newNodes, edges: newEdges }, newElement.id)
}
if (!previousNode) {
previousNode = getPreviousElement({ ...rung, nodes: newNodes, edges: newEdges }, newElement.id)
}
if (!previousNode) return { nodes: newNodes, edges: newEdges }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts`
around lines 65 - 67, The predecessor lookup in serial element insertion can
still resolve to undefined, which later causes the connect logic to dereference
previousNode.id. In the serial ladder utilities around getPreviousElement and
the connectNodes flow, add a short-circuit guard immediately after resolving
previousNode so the function exits or skips linking when no predecessor is
found, and keep the existing path unchanged when a valid node is returned.

Comment on lines +89 to +97
export const getPreviousElement = (rung: RungLadderState, newElementId: string): Node => {
const serialSpine = rung.nodes.filter(
(n) =>
n.type !== 'placeholder' &&
n.type !== 'parallelPlaceholder' &&
n.type !== 'variable' &&
!(n.data as BasicNodeData).branchContext,
)
return nodesWithNoPlaceholderAndVariables[nodeIndex - 1]
return serialSpine[serialSpine.findIndex((n) => n.id === newElementId) - 1]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP -C4 'getPreviousElement\s*\(' src/frontend

Repository: Autonomy-Logic/openplc-editor

Length of output: 5467


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,140p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts
printf '\n---\n'
sed -n '1,180p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts
printf '\n---\n'
sed -n '1,180p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/__tests__/get-previous-element.test.ts
EOF

Repository: Autonomy-Logic/openplc-editor

Length of output: 12013


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,140p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts
printf '\n---\n'
sed -n '1,180p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/serial/index.ts
printf '\n---\n'
sed -n '1,180p' src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/__tests__/get-previous-element.test.ts

Repository: Autonomy-Logic/openplc-editor

Length of output: 11972


Guard the predecessor lookup before dereferencing

getPreviousElement() can return undefined when the id is missing or is the first serial node, but it is typed as Node. appendSerialConnection() then calls previousNode.id without a guard, which can throw. Return Node | undefined and handle the fallback before connectNodes().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/frontend/components/_molecules/graphical-editor/ladder/rung/ladder-utils/elements/utils/index.ts`
around lines 89 - 97, getPreviousElement currently assumes a predecessor always
exists, but it can return undefined when newElementId is missing or is the first
serial node, while appendSerialConnection dereferences previousNode.id
unconditionally. Update getPreviousElement in the ladder utils index to return
Node | undefined, then add a guard in appendSerialConnection before calling
connectNodes so the fallback path handles the missing predecessor safely.

Comment thread src/frontend/store/slices/ladder/utils/index.ts
Comment thread src/frontend/utils/PLC/xml-generator/codesys/language/ladder-xml.ts
@thiagoralves
thiagoralves force-pushed the fix/ladder-handle-branch-bugs branch from 495e49c to be36234 Compare June 24, 2026 14:11
@thiagoralves
thiagoralves changed the base branch from development to revert/ladder-shortcuts June 24, 2026 14:11
@thiagoralves

Copy link
Copy Markdown
Contributor Author

Rebased onto the revert branch revert/ladder-shortcuts (base retargeted), byte-identical to openplc-web#546. tsc + ladder suites pass (164). Merge order: revert #894 first, then this.

@thiagoralves
thiagoralves force-pushed the fix/ladder-handle-branch-bugs branch from be36234 to e6817da Compare June 24, 2026 14:27
Base automatically changed from revert/ladder-shortcuts to development June 24, 2026 14:28
…cate and XML

Four related handle-branch defects in the ladder editor:

- Adding an element to a block's primary output (e.g. a coil on a
  counter's Q/QU) spliced it into a secondary-handle branch edge,
  dropping the element and breaking the branch (the originally reported
  "coil doesn't show on Q, contact-to-R connection breaks"). The serial
  predecessor was resolved by array index, which picks the handle-branch
  contact interleaved between the block and the right rail.
  getPreviousElement now skips branchContext nodes (mirroring
  getPreviousElementsByEdge) and is keyed by element id.

- handleBranches is runtime-only state (not persisted in .ld); rebuild it
  from the graph on project load via deriveHandleBranches so the first
  branch-aware edit no longer corrupts diagrams that contain handle
  branches.

- Duplicating a rung rebuilt blocks at default dimensions without
  re-running the layout solver, and dropped branchContext remapping on
  branch parallel nodes; re-run updateDiagramElementsPosition and remap.

- PLCopen XML serialized every block-output branch to the primary output
  pin (QU); resolve the actual edge sourceHandle so QU/QD are
  distinguished.

Adds a regression test for getPreviousElement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@thiagoralves
thiagoralves force-pushed the fix/ladder-handle-branch-bugs branch from e6817da to 9f46101 Compare June 24, 2026 14:30
@thiagoralves
thiagoralves merged commit 299b25e into development Jun 24, 2026
12 checks passed
@thiagoralves
thiagoralves deleted the fix/ladder-handle-branch-bugs branch June 24, 2026 14:39
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