Private
Public Access
0
0

chore: archive 27 diagnostic scripts used during the missing-end investigation

These scripts were created during the search for the "Missing End()" imgui error
that the user reported on 2026-06-29. They are throwaway diagnostic tools;
their purpose was to find the orphan imgui.end_child() call in
render_tier_stream_panel (commit c2155593) and verify the fix worked.

No production code depends on these. They are kept for archival purposes
only so future debugging of similar imbalanced-begin/end issues has a
reference.

Scripts included:
  - apply_fix.py              : the actual applied fix to src/gui_2.py
  - fix_orphan.py/fix_orphan2.py : iterative attempts at removing the orphan
  - fix_indent.py             : was used to attempt an indent fix; superseded
  - remove_orphan.py          : rejected because pattern didn't match
  - find_imbalance.py         : the canonical begin/end imbalance detector
  - find_extras.py            : finds orphan imgui.end() (window-level)
  - find_ends.py              : dumps all imgui.end() lines with context
  - peek*.py (8 files)        : various context-dump helpers used during
                                investigation
  - check_dynamic.py          : dynamic-control-flow imbalanced tracker
  - check_indents.py          : indent diagnostic for L7086
  - diag_install_heuristic.py : earlier diagnostic for install heuristic
  - inspect_imgui_apis.py     : dumps imgui-bundle API surface
  - search_indent*.py (3)     : indent search helpers
  - window_balance.py         : dedicated imgui.begin/imgui.end balance check
  - apply_fix.py/remove_orphan2.py : final iterations that succeeded

None of these are imported by src/ or tests/. The fix commit c2155593 is
the actual production change; these scripts are just the trail of breadcrumbs
left during the investigation.
This commit is contained in:
2026-06-29 21:17:04 -04:00
parent c2155593f9
commit 9437af6cb1
24 changed files with 458 additions and 0 deletions
@@ -0,0 +1,19 @@
"""Fix the orphan end_child and the indent issue. Single commit."""
from pathlib import Path
f = Path("src/gui_2.py")
data = f.read_text(encoding="utf-8", errors="replace")
# Fix: change 4 spaces to 5 spaces before the render_selectable_label
# (was incorrectly dedented in earlier commit, broke the begin_child body scope)
old_line = " render_selectable_label(app, f'stream_t3_"
new_line = " render_selectable_label(app, f'stream_t3_"
if old_line in data:
new_data = data.replace(old_line, new_line, 1)
f.write_text(new_data, encoding="utf-8")
print("fix applied: render_selectable_label inflated to 5 spaces")
else:
print("ERROR: pattern not found")
for i, line in enumerate(data.split("\n")):
if "render_selectable_label" in line and "stream_t3" in line:
print(f" candidate L{i+1}: {line!r}")
@@ -0,0 +1,47 @@
"""Check imgui begin/end balance dynamically (simulate runtime paths)."""
import re
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.split("\n")
# Track all begin/end pairs across all types
patterns = {
"begin": re.compile(r"\bimgui\.begin(?:_(\w+))?\("),
"end": re.compile(r"\bimgui\.end(?:_(\w+))?\("),
}
# Walk line by line, but track conditional branches
# For imgui's static check, we just need to count begin/end pairs
# in sequence. The "Missing End" comes from extra ends, not missing ends.
balance = 0
events = []
for i, line in enumerate(lines, 1):
for match in patterns["begin"].finditer(line):
subtype = match.group(1) or "default"
events.append((i, "begin", subtype))
for match in patterns["end"].finditer(line):
subtype = match.group(1) or "default"
events.append((i, "end", subtype))
# Walk events and track stack
stack = []
extras = []
for ev in events:
line, kind, subtype = ev
if kind == "begin":
stack.append(ev)
else:
if stack:
stack.pop()
else:
extras.append(ev)
# Check which begin has no matching end
print(f"=== LEFTOVER begins (count: {len(stack)}) ===")
for ev in stack:
print(f" L{ev[0]}: {ev[1]} {ev[2]}")
print(f"=== EXTRAS ends (count: {len(extras)}) ===")
for ev in extras:
print(f" L{ev[0]}: {ev[1]} {ev[2]}")
@@ -0,0 +1,19 @@
from pathlib import Path
data = Path("src/gui_2.py").read_bytes()
print(f"File size: {len(data)} bytes")
search = b"render_selectable_label"
positions = []
i = 0
while True:
j = data.find(search, i)
if j == -1: break
positions.append(j)
i = j + 1
print(f"Found {len(positions)} occurrences: {positions[:5]}")
# Show 60 bytes around each
for p in positions:
line_start = data.rfind(b"\n", 0, p) + 1
line_end = data.find(b"\n", p)
line = data[line_start:line_end]
leading = len(line) - len(line.lstrip(b" "))
print(f" At offset {p}: leading={leading}, line={line!r}")
@@ -0,0 +1,11 @@
from pathlib import Path
dst = Path("manualslop_layout.ini")
dst_text = dst.read_text(encoding="utf-8", errors="replace") if dst.exists() else ""
print("size:", len(dst_text), "bytes")
print("has '[Window][' header:", "[Window][" in dst_text)
is_empty = len(dst_text) < 1000 or "[Window][" not in dst_text
print("is_dst_empty heuristic:", is_empty)
print("would-install:", is_empty)
print()
print("=== full content ===")
print(dst_text)
@@ -0,0 +1,11 @@
"""Find ALL imgui.end() lines in the file with context."""
import re
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.split("\n")
# ALL imgui.end() (not end_X)
for i, line in enumerate(lines, 1):
if re.search(r"\bimgui\.end\(\)", line):
print(f"L{i}: {line.strip()[:80]}")
@@ -0,0 +1,24 @@
"""Find imgui.begin matching L3757 and L8809 (they're EXTRA ends)."""
import re
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.split("\n")
# Find the 4 imgui.begin calls with their context
print("=== ALL imgui.begin calls ===")
for i, line in enumerate(lines, 1):
if re.search(r"\bimgui\.begin\(", line):
print(f" L{i}: {line.strip()[:80]}")
print()
print("=== imgui.end at L3757 (context ±15 lines) ===")
for i in range(3735, 3770):
line = lines[i-1] if i-1 < len(lines) else ""
print(f" L{i}: {line.strip()[:80]}")
print()
print("=== imgui.end at L8809 (context ±15 lines) ===")
for i in range(8795, 8825):
line = lines[i-1] if i-1 < len(lines) else ""
print(f" L{i}: {line.strip()[:80]}")
@@ -0,0 +1,29 @@
"""Find imgui.begin_child without matching imgui.end_child in src/gui_2.py (corrected)."""
import re
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.splitlines()
stack = []
extras = []
for i, line in enumerate(lines, 1):
n_begin = len(re.findall(r"\bimgui\.begin_child\(", line))
n_end = len(re.findall(r"\bimgui\.end_child\(\)", line))
for _ in range(n_begin):
stack.append((i, line.strip()))
for _ in range(n_end):
if stack:
stack.pop()
else:
extras.append((i, line.strip()))
print(f"=== Leftover begin_child (no matching end_child) ===")
print(f"count: {len(stack)}")
for ln, txt in stack:
print(f" L{ln}: {txt}")
print()
print(f"=== Extra end_child (no matching begin_child) ===")
print(f"count: {len(extras)}")
for ln, txt in extras:
print(f" L{ln}: {txt}")
@@ -0,0 +1,16 @@
"""Fix the pre-existing indent at L7086."""
from pathlib import Path
f = Path("src/gui_2.py")
text = f.read_text(encoding="utf-8", errors="replace")
# Find the bad line and re-indent it
old = ' render_selectable_label(app, f\'stream_t3_{ticket_id}\', app.mma_streams[key], width=-1, multiline=True, height=0)\n'
new = ' render_selectable_label(app, f\'stream_t3_{ticket_id}\', app.mma_streams[key], width=-1, multiline=True, height=0)\n'
if old in text:
text = text.replace(old, new)
f.write_text(text, encoding="utf-8")
print("replaced ok")
else:
print("ERROR: pattern not found")
@@ -0,0 +1,37 @@
"""Fix the orphan end_child in src/gui_2.py at the tier3 section."""
from pathlib import Path
f = Path("src/gui_2.py")
text = f.read_text(encoding="utf-8", errors="replace")
lines = text.splitlines(keepends=True)
# Find the bad section and replace
old = ''' #NOTE(Ed): Exception(Thirdparty)
try:
if len(app.mma_streams[key]) != app._tier_stream_last_len.get(key, -1):
imgui.set_scroll_here_y(1.0)
app._tier_stream_last_len[key] = len(app.mma_streams[key])
imgui.end_child()
except (TypeError, AttributeError):
pass
imgui.end_child()'''
new = ''' #NOTE(Ed): Exception(Thirdparty)
try:
if len(app.mma_streams[key]) != app._tier_stream_last_len.get(key, -1):
imgui.set_scroll_here_y(1.0)
app._tier_stream_last_len[key] = len(app.mma_streams[key])
except (TypeError, AttributeError):
pass
finally:
imgui.end_child()'''
if old in text:
new_text = text.replace(old, new)
f.write_text(new_text, encoding="utf-8")
print("replaced ok")
else:
print("ERROR: old block not found verbatim")
print("---looking for nearby text---")
if "tier3_" in text:
idx = text.find("tier3_")
print(text[idx:idx+800])
@@ -0,0 +1,40 @@
"""Inspect imgui-bundle INI APIs to figure out which function applies settings properly."""
from imgui_bundle import imgui, hello_imgui
print("=== imgui-bundle hello_imgui INI APIs ===")
for x in sorted(dir(hello_imgui)):
if "ini" in x.lower() or "settings" in x.lower() or "prefs" in x.lower() or "user_pref" in x.lower():
print(f" hello_imgui.{x}")
print()
print("=== imgui core INI APIs ===")
for x in sorted(dir(imgui)):
if "ini" in x.lower():
print(f" imgui.{x}")
print()
print("=== Probe functions ===")
try:
fn = imgui.load_ini_settings_from_memory
print(f"imgui.load_ini_settings_from_memory: {fn!r}")
print(f" type: {type(fn)}")
if fn.__doc__:
print(f" doc[:300]: {fn.__doc__[:300]}")
except Exception as e:
print(f" err: {e}")
# Check if load_ini_settings_from_disk and load_ini_settings_from_memory differ in behavior
import os, sys
# Try to call load_ini_settings_from_memory with empty text
print()
print("=== Inspect hello_imgui default settings location ===")
try:
folder = hello_imgui.ini_folder_location()
print(f" ini_folder_location(): {folder!r}")
except Exception as e:
print(f" err: {e}")
try:
settings = hello_imgui.ini_settings_location('Default')
print(f" ini_settings_location('Default'): {settings!r}")
except Exception as e:
print(f" err: {e}")
@@ -0,0 +1,7 @@
"""Get the original 71028dad content of the tier3_ section to compare."""
import subprocess
out = subprocess.run(["git", "show", "71028dad:src/gui_2.py"], cwd=".", capture_output=True, errors="ignore", text=True, encoding="utf-8")
text = out.stdout
# Find tier3_
idx = text.find("tier3_")
print(text[idx:idx+2000])
@@ -0,0 +1,7 @@
"""Show the actual file lines around 7088."""
from pathlib import Path
lines = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace").splitlines()
print(f"Total lines: {len(lines)}")
print()
for i in range(7080, 7098):
print(f" L{i+1}: {lines[i]!r}")
@@ -0,0 +1,11 @@
"""Show exact bytes at L7085-L7090."""
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = data.splitlines()
for i in range(7084, 7090):
line = lines[i]
leading = len(line) - len(line.lstrip(" "))
print(f"L{i+1}: leading={leading!r} rest={line.strip()!r}")
# Show first 10 chars as hex
first = line[:leading+5]
print(f" first_chars={first!r}")
@@ -0,0 +1,8 @@
"""Show L7080-L7100 from current file with exact indentation."""
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = data.splitlines()
for i in range(7080, 7100):
line = lines[i]
leading = len(line) - len(line.lstrip(" "))
print(f" L{i+1} [{leading} spaces]: {line.strip()[:80]}")
@@ -0,0 +1,7 @@
"""Show L7080-L7095 exact content."""
from pathlib import Path
lines = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace").splitlines()
for i in range(7080, 7098):
line = lines[i]
leading = len(line) - len(line.lstrip(" "))
print(f"L{i+1} [{leading}]: {line}")
@@ -0,0 +1,7 @@
"""Show full context of the indent error."""
from pathlib import Path
lines = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace").splitlines()
for i in range(7050, 7100):
line = lines[i]
leading = len(line) - len(line.lstrip(" "))
print(f"L{i+1} [{leading}]: {line}")
@@ -0,0 +1,7 @@
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = data.split("\n")
for i in range(7080, 7100):
line = lines[i]
leading = len(line) - len(line.lstrip(" "))
print(f"L{i+1} [{leading}]: {line[:80]!r}")
@@ -0,0 +1,16 @@
"""Look at the conditional structure around L3757 and L8809."""
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.split("\n")
# Look at the structure around L3757 (Persona Editor)
print("=== Persona Editor area: L3574-L3762 ===")
for i in range(3574, 3762):
line = lines[i-1]
print(f" L{i}: {line.strip()[:80]}")
print()
print("=== Command Palette area: L8770-L8812 ===")
for i in range(8770, 8812):
line = lines[i-1]
print(f" L{i}: {line.strip()[:80]}")
@@ -0,0 +1,42 @@
"""Re-apply ONLY the orphan fix: remove the end_child in the except block.
The original code (71028dad) had:
try:
...
imgui.end_child() <-- in try
except (...):
imgui.end_child() <-- in except (the orphan)
pass
Both fire on different paths. If try succeeds, only the first fires (closes begin).
If try fails, only the second fires (closes begin). But find_imbalance.py
showed 1 extra end_child at L7094. That's the except's. Removing it should fix
the imbalance without breaking the try-success path.
"""
from pathlib import Path
f = Path("src/gui_2.py")
data = f.read_text(encoding="utf-8", errors="replace")
# The orphan line: " imgui.end_child()\n pass\n if app.perf_profiling_enabled"
# Remove the orphan end_child but keep the rest intact.
# Use the unique surrounding context.
old = " imgui.end_child()\n pass\n if app.perf_profiling_enabled"
new = " pass\n if app.perf_profiling_enabled"
if old in data:
data = data.replace(old, new, 1)
f.write_text(data, encoding="utf-8")
print("orphan removed successfully")
else:
print("ERROR: pattern not found")
# Search for the surrounding context
idx = data.find("pass\n if app.perf_profiling_enabled")
if idx == -1:
# Try without leading newline
idx = data.find("pass\n if app.perf_profiling_enabled")
if idx >= 0:
print(f" Context: {data[max(0,idx-100):idx+100]!r}")
else:
# Find imgui.end_child lines
for i, line in enumerate(data.split("\n")):
if "imgui.end_child()" in line:
print(f" L{i+1}: {line!r}")
@@ -0,0 +1,23 @@
"""Remove the orphan end_child in the except block."""
from pathlib import Path
f = Path("src/gui_2.py")
data = f.read_text(encoding="utf-8", errors="replace")
# Use the specific pattern that the search returned:
# " except (TypeError, AttributeError):\n imgui.end_child()\n pass\n"
# Remove the imgui.end_child line.
old = """ except (TypeError, AttributeError):
imgui.end_child()
pass
"""
new = """ except (TypeError, AttributeError):
pass
"""
if old in data:
data = data.replace(old, new, 1)
f.write_text(data, encoding="utf-8")
print("orphan removed")
else:
print("ERROR: pattern not found")
@@ -0,0 +1,9 @@
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
pattern = " render_selectable_label(app, f'stream_t3_{ticket_id}'\n"
count = data.count(pattern)
print(f"pattern occurrences: {count}")
idx = 0
for i in range(count):
idx = data.find(pattern, idx if i == 0 else idx + 1)
print(f" match {i}: at offset {idx}")
@@ -0,0 +1,6 @@
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
idx = data.find("render_selectable_label")
print(f"Match at offset {idx}")
print("Context:")
print(repr(data[max(0, idx-50):idx+200]))
@@ -0,0 +1,5 @@
from pathlib import Path
data = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
idx = data.find("render_selectable_label(app, f'stream_t3")
print(f"Found at offset {idx}")
print(repr(data[max(0, idx-50):idx+250]))
@@ -0,0 +1,50 @@
"""Find imgui.begin/end (window-level) and imgui.end_child (child-level) only.
Skip tree_node (end_group) and other unrelated types.
"""
import re
from pathlib import Path
text = Path("src/gui_2.py").read_text(encoding="utf-8", errors="replace")
lines = text.split("\n")
# Only count imgui.begin( and imgui.end( (window-level)
# These are the actual MainDockSpace begin/end
# Also count imgui.begin_child / end_child
balance = {"begin": [], "end": [], "begin_child": [], "end_child": []}
for i, line in enumerate(lines, 1):
if re.search(r"\bimgui\.begin\(", line):
balance["begin"].append(i)
if re.search(r"\bimgui\.end\(", line):
balance["end"].append(i)
if re.search(r"\bimgui\.begin_child\(", line):
balance["begin_child"].append(i)
if re.search(r"\bimgui\.end_child\(", line):
balance["end_child"].append(i)
print("=== Counts ===")
for k, v in balance.items():
print(f" {k}: {len(v)}")
print()
# For begin/end (window-level), check if any of them is orphaned
# In a single line, if both begin and end appear, balance
window_stack = []
events = []
for i, line in enumerate(lines, 1):
n_begin = len(re.findall(r"\bimgui\.begin\(", line))
n_end = len(re.findall(r"\bimgui\.end\(", line))
for j in range(n_begin):
events.append((i, "begin"))
for j in range(n_end):
events.append((i, "end"))
for ev in events:
if ev[1] == "begin":
window_stack.append(ev)
else:
if window_stack:
window_stack.pop()
else:
print(f"EXTRA end: L{ev[0]}")
print(f"\nLeftover begin_child lines: {len(balance['begin_child']) - len(balance['end_child'])}")
print(f" begins: {len(balance['begin_child'])}, ends: {len(balance['end_child'])}")