App / headless robustness fixes (non-graphics, from #2109)#2118
Merged
Conversation
jcelerier
added a commit
that referenced
this pull request
Jul 17, 2026
Eight regression tests guarding the panel/presenter/drop/app-lifecycle robustness fixes on this branch and #2117 below it — each names the commit it guards in the CMake comment. All enabled: the fixes they exercise are present in the chain (planb is in-stack under planc..planf2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
A factory returning nullptr (missing hardware, headless environment) put a null unique_ptr in ApplicationComponents::panels, crashing every later panels() iteration (e.g. Explorer::findDeviceExplorerWidgetInstance from Scenario::SearchWidget). Warn and skip instead. (cherry picked from commit 4175e6c) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
…null Same defect as registerPanel: a declining PanelDelegateFactory put a null unique_ptr in ApplicationComponents::panels and setModel dereferenced it. (cherry picked from commit 874d5ee) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
ctx.panels()'s indirect iteration dereferences every slot unconditionally, which aborts on hardened stdlibs (_GLIBCXX_ASSERTIONS) when a slot is empty; findPanel<T>() does the same lookup null-safely. (cherry picked from commit 844a4ab) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
…ownload The first-run library-download question is modal; in non-interactive sessions (tests/CI) it blocks the main thread forever. Same escape hatch refresh() already uses. (cherry picked from commit 26f6eb6) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
(cherry picked from commit b940d06) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A8dWKnm8kgkuDaMMjapgFT
Both Minimal[GUI]Application destructors ran QApplication::processEvents() after delete m_presenter, so deferred slots queued during plugin load (e.g. Scenario::SearchWidget's deferred init reaching into the device explorer) were dispatched against the freed presenter-owned application context — a use-after-free seen as a shutdown crash on headless harnesses. Drain the queue while the presenter is still alive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Some dropCustom overrides run parsers that can throw on malformed input, but getCustomDrops is noexcept, so an exception terminated the editor. Drop noexcept from the dropCustom contract and catch in getCustomDrops. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
Drain the event queue before deleting the presenter: deferred slots queued during plugin load read the presenter-owned application context, so running them after the presenter is gone is a use-after-free. Also take the settings models down while the QApplication is still alive, which Qt >= 6.11 requires. (cherry picked from commit e4dafbc) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
(cherry picked from commit 295a0e4)
(cherry picked from commit ed5afbb)
(cherry picked from commit 52b1bc5)
Eight regression tests guarding the panel/presenter/drop/app-lifecycle robustness fixes on this branch and the test infrastructure below it — each names the fix it guards in the CMake comment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014rZgzE8JjWvHDtaVUhxpLE
jcelerier
force-pushed
the
planb/app-robustness
branch
from
July 18, 2026 13:37
e42885c to
a14d79a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extracts the non-graphics robustness fixes made during the GPU-graphics work (#2109) so they can land on master independently as small, reviewed commits. Each is a self-contained fix to headless/app-lifetime/drop code — no graphics changes.
Stacked on #2117 (the test infrastructure) so the harness that exercises these paths is available. Will retarget to master once #2117 merges.
Commits
PanelDelegateFactorydeclines — a factory returning null (missing hardware / headless) stored a null pointer that crashed every laterpanels()iteration.loadPluginDatapanel-instantiation path against the same null.findDeviceExplorerWidgetInstance(consumer side of the above; aborted on hardened stdlibs).SCORE_SANITIZE_SKIP_CHECKSinfirstTimeLibraryDownloadso a non-interactive session doesn't block on the first-run modal.Minimal[GUI]Applicationdestructors dispatched deferred plugin-load slots against the freed presenter context (use-after-free on shutdown).noexceptdrop API —dropCustomoverrides can run parsers that throw on malformed input; catch ingetCustomDrops.Notes on the dataflow analysis
The split branch's
5d26b13ba("don't let settings models outlive the QApplication") was deliberately skipped: master already solves that bug more cleanly by declaringm_appas aunique_ptrfirst (destroyed last), so settings models are torn down before the QApplication via member order. Only the still-presentprocessEvents-ordering use-after-free was extracted.Validation
ctest: 17/17.🤖 Generated with Claude Code