Skip to content

AI Code Scan Bugfixes

mjcmatrix edited this page Oct 8, 2026 · 4 revisions

AI Code Scan Bugfixes

Bugs found by an AI-assisted code scan of the fork on 2026-09-28, and the fix bundles raised for them. Unlike the other pages, nothing here is a new feature: these are correctness fixes, nearly all in code inherited from upstream.

How the scan was done

  • Scope: qmlui (the QLC+ 5 UI) and engine. The QtWidgets v4 UI (ui/), plugins and webaccess were not scanned.
  • Method: targeted, not exhaustive. Pattern searches (lookups used without a null check, unchecked casts, signed-byte sign extension, index bounds, divisions by values that can be zero, lock and lifetime issues), then close reading of the riskiest files: the Function Manager tree, undo/redo, VC Cue List and Frame, Simple Desk, ShowRunner, ChaserRunner, GenericFader/FadeChannel, Universe, Scene, Collection, EFX, RGB Matrix, Doc and MasterTimer.
  • Verification: every fixed bug was first reproduced on unmodified upstream/master, either in the running app (headless session driven by xdotool) or with a regression unit test that fails on upstream and passes with the fix. Where neither was practical, the finding is marked code review.
  • Tests: the full engine unit test suite passes with all bundles applied, on both upstream/master and the fork's master. The exceptions are qlcfile and rgbtext, which fail the same way on unmodified upstream (test resources and fonts in the test environment).

Column meanings:

  • Confidence: how sure the scan was that the defect is real before verifying it.
  • Patch risk: how likely the fix is to disturb something else.
  • Verified: how the bug was confirmed on upstream.
  • Origin: where the buggy code came from, per git blame.

Findings

qmlui (first pass)

# Where Bug Origin Confidence Patch risk Verified Fork PR
1 treemodel.cpp Deleting, moving or renaming a Function removed a folder with the same name (and every Function in it) from the tree; the deleted Function's row stayed behind with no icon Upstream, 2018 TreeModel High Medium App + model harness #80
2 functionmanager.cpp addFunctiontoDoc() A new Function was added to the existing selection, so Delete also removed the Functions selected before Upstream, 2018. The fork's container-from-selection inherits it High Low App #80
3 tardis.cpp After undoing everything, a new action didn't discard the undone ones, so further undos replayed them Upstream, 2017 Tardis High Low App (log) #81
3b tardis.cpp redoAction() One redo also applied the first action of the next batch Upstream, 2017 Tardis High Low App (log) #81
4 tardis.cpp Undo history changed on two threads without a lock Upstream, 2017 Tardis High (race exists); low (how often) Medium Code review #81
5 vccuelist.cpp Previous while stopped (or Next on a backward Chaser) went to step -2: "Run next" started with no step shown, "Select" lost the selection Upstream, 2018 (the v4 UI handles it) High Low App #82
6 simpledesk.cpp Unpatched channel values above 127 shown as ~4 billion after a universe switch Upstream, 2020 High Very low Code review #83
7 functionmanager.cpp createFolder() Duplicate check used the wrong path separator and a bare prefix match: nested duplicates allowed, "Foo" refused next to "Foobar" Upstream, 2018 High Low App #80
8 functionmanager.cpp deleteSelectedFolders() Deleting an empty folder left its empty subfolders registered; recreating them was refused Upstream: the 2026-07 fix for #2058 only covered folders with Functions Medium-high Low App #80
9 contextmanager.cpp Fixture distribution sorted positions as unsigned integers (negative/fractional positions misordered) Upstream, 2018 High Very low Code review #83
10 listmodel.cpp Role bound check let one role past the end through; negative rows not rejected Upstream, 2016 High (defect); low (impact) Very low Code review #83

Found while fixing the first pass

# Where Bug Origin Confidence Patch risk Verified Fork PR
A treemodel.cpp addItem() Items added to a newly created empty folder didn't appear until the tree was rebuilt Upstream High Low App #80
B contextmanager.cpp setFixturesDistribution() Vertical distribution in Front/Side views (and horizontal in Right side) wrote positions without undoing the 2D axis flip: fixtures landed mirrored or off the grid Upstream, 2018 High Low App #83

Engine and remaining qmlui (second pass)

# Where Bug Origin Confidence Patch risk Verified Fork PR
E1 showrunner.cpp Two Show items using the same Function (back-to-back, e.g. after Split, or overlapping on two tracks): the second item never played Upstream, 2014–2016 High Medium Unit test + app #84
E2 rgbmatrix.cpp Deleting the fixture group of a running (or previewed) RGB Matrix left it using freed memory Upstream High Low Unit test #85
E3 doc.cpp + qmlui moveFixture() Moving a fixture left its HTP/LTP types and defaults at the old address (none at the new one); deleting one left them set up and its last LTP value on the output Upstream, old High Medium Unit test #86
E4 chaserrunner.cpp Random order could report a step that doesn't exist (single step, or going backward) Upstream High Low Unit test #87
E5 chaserrunner.cpp Random order not rebuilt when steps were added or removed while running Upstream Medium-high Low Unit test #87
E6 collection.cpp A Collection's duration was the sum of its members, though they run in parallel Upstream, 2015 High Low Unit test #87
E7 collection.cpp adjustAttribute() Intensity overrides looked up by position in the current member list; out of range if members change while running Upstream, 2015/2017 Medium Low Code review Not yet fixed
E8 efx.cpp Only the first head of a deleted multi-head fixture was removed; EFX fixture entries could be deleted while the MasterTimer thread used them Upstream, 2012/2013 High (heads); medium (race) Low (heads); medium (lock) Unit test (heads), code review (race) #85
E9 showrunner.cpp Starting a Show mid-way compared beat-based items with a time in ms: below 60 BPM items still to come were skipped Upstream, 2012 condition reused for beats Medium-high Low Unit test #84
E10 efx.cpp loopDuration() A fade in not shorter than the duration wrapped the loop to ~49 days, freezing the EFX (reachable from the EFX editor) Upstream High Low Unit test #87
E11 rgbmatrix.cpp setTotalDuration() Integer division by zero (crash) with a 0-step algorithm. Only the v4 Show Manager calls it Upstream High Very low Unit test (SIGFPE on upstream) #87
Q1 chasereditor.cpp setTempoType() Switching a Chaser's tempo type overflowed the duration of infinite-hold steps, so cues no longer held Upstream, 2016 High Very low Code review #87
Q2 vcframe.cpp Lowering a VC Frame's pages number left widgets hidden on removed pages, still active and saved with the project Upstream Medium-high Medium App #88
Q2b vcframe.cpp The frame's page selector jumped back to "Page 1" when the pages changed, while showing another page (found while testing Q2) Upstream High Very low App #88

Noted but not fixed

  • E7 above.
  • FunctionEditor::setFadeInSpeed() changes the fade in without keeping the hold. This is the root cause of E10 being easy to reach; changing it affects every editor that uses it.
  • EFX ignores the Beats tempo type (not reachable in qmlui: the EFX editor has no tempo control).
  • The qmlui EFX editor and undo remove EFX fixture entries without deleting them (small leak).
  • Chaser::setTotalDuration() per-step path can divide by zero (v4 UI only).
  • Scene::slotFixtureRemoved() edits the Scene values without the lock write() holds.
  • Universe::requestFaderPriority() orders equal-priority faders inconsistently.
  • ChaserRunner's start offset uses per-step durations for the "Default" duration mode, while playback uses the Chaser's duration.

Bundles for upstream

Each bundle is a fork PR for review first. All are branched from upstream/master, with a regression test or an app test for every fix, so they can go upstream as they are.

Fork PR Upstream branch Title Fixes Notes
#80 fix-functiontree-name-clash qmlui: fix Function Manager tree when folder and Function share a name 1, 2, 7, 8, A One-line conflict with mcallegari/qlcplus#2175, now merged upstream, (both add return on the same line). Fork PR uses fork/fix-functiontree-name-clash, adapted to the fork's Function Manager
#81 fix-tardis-history qmlui: fix Tardis undo/redo history handling 3, 3b, 4
#82 fix-cuelist-prev-when-stopped qmlui: fix Cue List previous/next step when not playing 5
#83 fix-qmlui-value-bounds qmlui: fix 2D fixture distribution, Simple Desk sign extension and ListModel bounds 6, 9, 10, B
#84 fix-showrunner-shared-functions engine: fix Show items sharing a Function and beat items on mid-way start E1, E9 Fork PR uses fork/fix-showrunner-shared-functions, adapted to the fork's tempo map and output hold. The second half of a split Chaser restarts from step 1 (as a separate item does)
#85 fix-running-function-stale-refs engine: fix running Functions using deleted groups and fixtures E2, E8
#86 fix-universe-fixture-patching engine: keep universe channel setup in step with fixture patching E3 Changes output behaviour after moving or deleting fixtures, towards what a freshly loaded project does
#87 fix-small-engine-qmlui engine/qmlui: fix random Chasers, Collection/EFX durations and a crash E4, E5, E6, E10, E11, Q1 Fork PR uses fork/fix-small-engine-qmlui, merged with the fork's tempo map ChaserRunner
#88 fix-vcframe-page-reduction qmlui: don't lose VC Frame widgets when reducing the pages number Q2, Q2b Widgets are never deleted: the pages can't go below the highest page with widgets

➖ Upstream: none sent yet, pending review on the fork

Clone this wiki locally