Fix 25+ bugs across rounds 4-8 of comprehensive code review

history_tree.py:
- Cycle protection in generate_graph() parent walk
- KeyError → .get() for malformed node data in commit() and generate_graph()
- UUID collision check with for/else raise in commit() and _migrate_legacy()
- RuntimeError → ValueError for consistent exception handling

tab_timeline_ng.py:
- Re-parent children walks to surviving ancestor for batch deletes
- Branch tip deletion re-points to parent instead of removing branch
- Cycle protection in _walk_branch_nodes and _find_branch_for_node
- Full data.clear() restore instead of merge in _restore_node
- Safe .get('data', {}) in restore and preview
- Reset stale branch selection after node deletion
- json.dumps for safe JS string escaping in graphviz renderer

tab_batch_ng.py:
- NaN/inf rejection in dict_number with math.isfinite()
- _safe_int used in recalc_vace, update_mode_label, frame_to_skip
- Uncaught ValueError from htree.commit() caught with user notification

tab_comfy_ng.py:
- asyncio.get_event_loop() → get_running_loop()

utils.py:
- Atomic writes for save_config and save_snippets
- save_config extra_data can't override explicit last_dir/favorites

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 10:23:14 +01:00
parent 04b9ed0e27
commit b042fe4368
5 changed files with 117 additions and 37 deletions

View File

@@ -114,14 +114,17 @@ def save_config(current_dir, favorites, extra_data=None):
existing = load_config()
data.update(existing)
data["last_dir"] = str(current_dir)
data["favorites"] = favorites
if extra_data:
data.update(extra_data)
with open(CONFIG_FILE, 'w') as f:
# Force-set explicit params last so extra_data can't override them
data["last_dir"] = str(current_dir)
data["favorites"] = favorites
tmp = CONFIG_FILE.with_suffix('.json.tmp')
with open(tmp, 'w') as f:
json.dump(data, f, indent=4)
os.replace(tmp, CONFIG_FILE)
def load_snippets():
if SNIPPETS_FILE.exists():
@@ -133,8 +136,10 @@ def load_snippets():
return {}
def save_snippets(snippets):
with open(SNIPPETS_FILE, 'w') as f:
tmp = SNIPPETS_FILE.with_suffix('.json.tmp')
with open(tmp, 'w') as f:
json.dump(snippets, f, indent=4)
os.replace(tmp, SNIPPETS_FILE)
def load_json(path: str | Path) -> tuple[dict[str, Any], float]:
path = Path(path)