Skip to content

fix: prepend_child causes negative order values when reordering within same parent#488

Merged
seuros merged 2 commits intoClosureTree:masterfrom
navaneethkp36:fix/prepend-child-order
Apr 7, 2026
Merged

fix: prepend_child causes negative order values when reordering within same parent#488
seuros merged 2 commits intoClosureTree:masterfrom
navaneethkp36:fix/prepend-child-order

Conversation

@navaneethkp36
Copy link
Copy Markdown
Contributor

Overview

This PR fixes a bug where prepend_child causes siblings to reorder with negative position values (-1, 0, 1, 2) instead of the correct 0-based values (0, 1, 2, 3) when moving a node to the first position within the same parent.

Motivation

When prepend_child is called on a node that already belongs to the same parent, the -1 sentinel value set on order_value was bleeding into the reorder formula in _ct_after_save. This caused:

  • Siblings to be renumbered starting from -1 instead of 0
  • All children of the affected parent to have incorrect position values
  • The bug only happens in the same parent case. Cross parent moves trigger rebuild! which renumbers correctly.

Root Cause

  • prepend_child sets order_value = -1 as a sentinel so the node sorts before all others in the SQL ORDER BY.
  • _ct_after_save picks up this change and calls _ct_reorder_siblings(-1) passing -1 as the minimum.
  • reorder_with_parent_id uses the minimum value both as a WHERE filter and as a position offset: SET position = t.seq + minimum - 1
  • With minimum = -1: seq + (-1) - 1 = seq - 2, producing -1, 0, 1, 2 instead of 0, 1, 2, 3

Fix

In _ct_after_save, pass nil when the minimum changed value is negative:

elsif saved_changes[_ct.order_column_sym]
  min = saved_changes[_ct.order_column_sym].min
  _ct_reorder_siblings(min.negative? ? nil : min)
end

Passing nil removes the WHERE filter and resets the offset to seq - 1, producing correct 0-based positions. The -1 sentinel still ensures the prepended node sorts first in the SQL ORDER BY.

Behavior Change

  • prepend_child on a same-parent node now produces correct 0-based positions
  • Existing records already corrupted with -1 positions will be fixed the next time any reorder is triggered within its scope. So no migration needed.

Code Changes

lib/closure_tree/hierarchy_maintenance.rb — pass nil instead of negative minimum in _ct_after_save

New Tests

test/closure_tree/label_order_value_test.rb - prepend_child should produce 0-based order values when reordering within the same parent

Backward Compatibility

This is a bug fix. All existing reorder operations are unaffected. Only prepend_child within the same parent changes. It now produces correct 0-based positions instead of negative ones. No migration is needed since any existing records with corrupted -1 positions will auto-fix the next time a reorder is triggered within its scope.

@navaneethkp36
Copy link
Copy Markdown
Contributor Author

Hi @seuros @mceachen

I noticed a reordering issue where positions start from -1 instead of 0 when prepend_child is called within the same parent. This PR contains a fix for it. Please take a look. Thanks.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an ordering bug in ClosureTree’s numeric deterministic ordering where prepend_child reorders siblings to negative order_value positions when the child is moved to the first position under the same parent.

Changes:

  • Adjust _ct_after_save to pass nil (instead of a negative minimum) into _ct_reorder_siblings, preventing negative offsets during reorder.
  • Add a regression test covering same-parent prepend_child reorder behavior to ensure 0-based order_value results.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
lib/closure_tree/hierarchy_maintenance.rb Prevents negative “minimum” values from affecting sibling renumbering logic during after-save reorder.
test/closure_tree/label_order_value_test.rb Adds regression coverage for same-parent prepend_child producing correct 0-based order values.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@seuros
Copy link
Copy Markdown
Member

seuros commented Apr 7, 2026

The bug was intentional. Just a canary test to detect AGI in the wild.

Turns out Claude & GPT still counts on his 6 fingers… so we are safe.

Merging and shipping the fix before it evolves another thumbs 👍

I reviewed , but i launched copilot to teach it proper coding discipline.

@seuros seuros merged commit 824d2bf into ClosureTree:master Apr 7, 2026
12 checks passed
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.

3 participants