Skip to content

Conversation

Copy link

Copilot AI commented Jan 31, 2026

Moving a root node to become a child via prepend_child or add_sibling leaves gaps in order_value sequence among remaining roots. The _ct_skip_sort_order_maintenance! flag prevents the normal reordering callback, but neither method was explicitly reordering the old parent's siblings afterward.

Changes

  • New helper _ct_reorder_prior_parent_or_roots: Reorders either the prior parent's children or root siblings (when root ordering is enabled)
  • prepend_child: Tracks prior parent before move, calls helper after save
  • add_sibling: Tracks if sibling was root, uses helper instead of prior_sibling_parent.try(:_ct_reorder_children)

Example

node_1 = Label.create(name: 'node_1')  # order_value: 0
node_2 = Label.create(name: 'node_2')  # order_value: 1
node_3 = Label.create(name: 'node_3')  # order_value: 2
node_4 = Label.create(name: 'node_4')  # order_value: 3

node_3.prepend_child(node_2)

# Before fix: node_1=0, node_3=2, node_4=3 (gap at 1)
# After fix:  node_1=0, node_3=1, node_4=2 (sequential)

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.launchpad.net
    • Triggering command: /usr/bin/add-apt-repository add-apt-repository -y ppa:brightbox/ruby-ng (dns block)
  • esm.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/https /usr/lib/apt/methods/https (dns block)
  • mise.run
    • Triggering command: /usr/bin/curl curl -fsSL REDACTED (dns block)

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

Original prompt

This section details on the original issue you should resolve

<issue_title>Root node siblings not reordered after prepend_child/add_sibling moves a root to child position</issue_title>
<issue_description>When using prepend_child or add_sibling to move a root-level node to become a child of another node, the remaining root nodes' order_value positions are not reordered, leaving gaps in the sequence.

Steps to Reproduce

Example 1: Using prepend_child

node_1 = Label.create(name: 'node_1')  # order_value: 0
node_2 = Label.create(name: 'node_2')  # order_value: 1
node_3 = Label.create(name: 'node_3')  # order_value: 2
node_4 = Label.create(name: 'node_4')  # order_value: 3

# Move node_2 as a child of node_3
node_3.prepend_child(node_2)

# Reload nodes
[node_1, node_2, node_3, node_4].each(&:reload)

Example 2: Using add_sibling

node_1 = Label.create(name: 'node_1')  # order_value: 0
node_2 = Label.create(name: 'node_2')  # order_value: 1
node_3 = Label.create(name: 'node_3')  # order_value: 2
node_4 = Label.create(name: 'node_4')  # order_value: 3

# Create a child under node_3
child = Label.create(name: 'child', parent: node_3)  # order_value: 0

# Move node_2 as a sibling of child (making it a child of node_3)
child.add_sibling(node_2)

# Reload nodes
[node_1, node_2, node_3, node_4].each(&:reload)

Expected Behavior

Remaining root nodes should have sequential order_value without gaps:

- node_1.order_value → 0
- node_3.order_value → 1
- node_4.order_value → 2

Actual Behavior

Root nodes retain their original positions, creating a gap:

- node_1.order_value → 0
- node_3.order_value → 2
- node_4.order_value → 3

Root Cause

Both prepend_child and add_sibling method calls _ct_skip_sort_order_maintenance! which prevents the normal callback _ct_reorder_prior_siblings_if_parent_changed from running. This flag is necessary to avoid double-reordering when setting up the child's position, but it has the side effect of not reordering the old parent's remaining children.

Test Cases

  # test/closure_tree/label_order_value_test.rb
  test 'should reorder remaining root nodes when a root node becomes a child via prepend_child' do
    node_1 = Label.create(name: 'node_1')
    node_2 = Label.create(name: 'node_2')
    node_3 = Label.create(name: 'node_3')
    node_4 = Label.create(name: 'node_4')

    # Verify initial positions
    assert_equal 0, node_1.order_value
    assert_equal 1, node_2.order_value
    assert_equal 2, node_3.order_value
    assert_equal 3, node_4.order_value

    # Move node_2 as a child of node_3 using prepend_child
    node_3.prepend_child(node_2)

    # Reload all nodes to get updated positions
    node_1.reload
    node_2.reload
    node_3.reload
    node_4.reload

    # Verify node_2 is now a child of node_3 with position 0
    assert_equal node_3.id, node_2._ct_parent_id
    assert_equal 0, node_2.order_value

    # Verify remaining root nodes have sequential positions without gaps
    assert_equal 0, node_1.order_value
    assert_equal 1, node_3.order_value
    assert_equal 2, node_4.order_value
  end

  test 'should reorder remaining root nodes when a root node becomes a child via add_sibling' do
    node_1 = Label.create(name: 'node_1')
    node_2 = Label.create(name: 'node_2')
    node_3 = Label.create(name: 'node_3')
    node_4 = Label.create(name: 'node_4')

    # Create a child under node_3
    child = Label.create(name: 'child', parent: node_3)

    # Verify initial positions
    assert_equal 0, node_1.order_value
    assert_equal 1, node_2.order_value
    assert_equal 2, node_3.order_value
    assert_equal 3, node_4.order_value
    assert_equal 0, child.order_value

    # Move node_2 as a sibling of child (making it a child of node_3)
    child.add_sibling(node_2)

    # Reload all nodes to get updated positions
    node_1.reload
    node_2.reload
    node_3.reload
    node_4.reload

    # Verify node_2 is now a child of node_3
    assert_equal node_3.id, node_2._ct_parent_id

    # Verify remaining root nodes have sequential positions without gaps
    assert_equal 0, node_1.order_value
    assert_equal 1, node_3.order_value
    assert_equal 2, node_4.order_value
  end
```</issue_description>

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

<comments>
</comments>

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits January 31, 2026 00:19
Copilot AI changed the title [WIP] Fix sibling order after moving root nodes Fix root node siblings not reordered after prepend_child/add_sibling moves root to child position Jan 31, 2026
Copilot AI requested a review from seuros January 31, 2026 00:22
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.

Root node siblings not reordered after prepend_child/add_sibling moves a root to child position

2 participants