Skip to content

[fix] keep parent links after convertNode, do not share state with subtree copies - #38

Merged
mwasiluk merged 1 commit into
SilverDecisions:modernizefrom
mwasiluk:fix/subtree-copies-and-conversion
Oct 31, 2026
Merged

mwasiluk merged 1 commit into
SilverDecisions:modernizefrom
mwasiluk:fix/subtree-copies-and-conversion

Conversation

@mwasiluk

Copy link
Copy Markdown
Member

Base: modernize (5fd01a4, after #35 and #37). Third of the stacked sd-model PRs: A (#35,
merged) → B (#37, merged) → D (this, fixes) → C (lodash in the lockfile). One commit on top
of modernize.

Likely root cause of SilverDecisions/SilverDecisions#234 (see below).

Bugs

  1. replaceNode (used by convertNode) did not set $parent of the children to the new node;
    they kept pointing to the removed node.
  2. cloneSubtree / cloneNode copied nodes and edges shallowly: the copy shared $fieldStatus
    (and the nested computed values of the nodes) with the original.

Reproduced in the SilverDecisions app (2f77351, demo page, through the TreeDesigner methods the
UI calls) with sd-model 0.2.11 from npm:

  • A: R → chance C → [decision D → T (payoff p*10), T2], global code p = 1; convert C to a
    decision node, change the code to p = 2, recompute: the payoff below the converted node stays
    10 (scope of D initialised from the removed node), expected 20.
  • B: after the conversion, select the converted subtree (4 nodes), copy, paste: 7 nodes are
    pasted (the children were treated as separate subtrees).
  • C: copy and paste a chance subtree, mark the probability of a pasted edge as invalid (what the
    sidebar does on input), recompute: the original edge is invalid too, its probability is not
    evaluated and the original tree reports invalidProbability, probabilityDoNotSumUpTo1.

Related issue: SilverDecisions/SilverDecisions#234

The issue reports that nodes in a copied part of the tree keep an old value of a global variable
(Day = 3200 after the global code was changed to Day=-3200) and do not see a variable added
later (Expert). In the attached file (saved by SilverDecisions 1.2.1) every node with the stale
scope is a descendant of one chance node ("Sam") whose own scope is current — the pattern bug 1
leaves when that node's type was converted after its children existed: the children keep the
removed node as $parent, and sd-computations initialises their scope from its frozen scope.

  • With sd-model 0.2.11 in SilverDecisions 2f77351: global Day=3200, R → chance C → decision →
    terminal (payoff Day); convert C, then change the code to Day=-3200 / Expert=-20000 and
    recompute: the payoff stays 3200 and the scopes below C have Day: 3200 and no Expert — the
    symptom of the issue. With this PR: -3200, Expert present.
  • Copy / paste alone (copy C, paste twice) does not reproduce it, with or without this PR, and
    the issue's steps mention only copying; so the conversion is the likely, not a proven, cause.
  • Reopening the saved file and recomputing with code evaluation gives correct scopes in both
    versions (the links are rebuilt on load), so files are not damaged. The issue can be closed
    once SilverDecisions is released with this sd-model.

Fix

replaceNode sets e.childNode.$parent; cloneNode deep-copies computed and $fieldStatus,
cloneSubtree deep-copies the edges' $fieldStatus. Values stay the same, only the sharing ends.
Regression tests in test/contract/data-model-edit.test.js (fail on 0.2.11 and on this branch
without the fix, pass with it). CHANGELOG entry.

Results

  • npm run test:build (Node 24 and 22): contract tests standalone(.min) 58, module(.min) 59,
    source 57, consumer bundle 57, Jasmine 8/8; also with the unreleased sd-utils of
    SilverDecisions/sd-utils modernize 2a3af7c (--sd-utils=<tgz>).
  • SilverDecisions scenarios with this branch: A 20, B 4 nodes, C original edge valid, no errors;
    undo/redo and save/load of the edited diagram unchanged. Same with the sd-utils 2a3af7c
    tarball. App.serialize of 13 diagram files identical to 0.2.11 (the fix does not change
    loading/saving).
  • Consumers, tarball installed with --no-save: sd-computations 196/196, sd-tree-designer 12/12,
    SilverDecisions build + Karma 37/37 (also with the sd-utils 2a3af7c tarball).

Not changed (open question): convertNode copies only the name of the node, not its code,
folded state or computed values.

Squash merge

[fix] keep parent links after convertNode, do not share state with subtree copies

convertNode left the children's $parent pointing to the replaced node (stale expression scope
in sd-computations, duplicated nodes on copy/paste in sd-tree-designer); cloneSubtree shared
$fieldStatus and computed values with the original (invalid copies marked the original invalid).

…btree copies

replaceNode (used by convertNode) did not set $parent of the children to the
new node, so they pointed to the replaced node that is no longer in the
model. cloneSubtree copied nodes and edges shallowly and shared their
$fieldStatus and the nested computed values of the nodes with the original.

Regression tests in test/contract/data-model-edit.test.js.
@mwasiluk
mwasiluk merged commit bf08b12 into SilverDecisions:modernize Oct 31, 2026
4 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.

2 participants