From 9543da587cbae88db9741a3686c1163689d1d798 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Mon, 21 Sep 2026 13:04:18 +0100 Subject: [PATCH 1/4] Add Task Manager for Omarchy 0.0.3 preview --- .../.omarchy/package.json | 11 +++++++ pkgbuilds/omarchy-task-manager/PKGBUILD | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 pkgbuilds/omarchy-task-manager/.omarchy/package.json create mode 100644 pkgbuilds/omarchy-task-manager/PKGBUILD diff --git a/pkgbuilds/omarchy-task-manager/.omarchy/package.json b/pkgbuilds/omarchy-task-manager/.omarchy/package.json new file mode 100644 index 0000000..8b66e2d --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/.omarchy/package.json @@ -0,0 +1,11 @@ +{ + "source": "local", + "channels": ["edge"], + "upstream": { + "watch": { + "github": "tcballard/omarchy-task-manager", + "pattern": "v(?P[0-9]+\\.[0-9]+\\.[0-9]+)", + "allow_prerelease": true + } + } +} diff --git a/pkgbuilds/omarchy-task-manager/PKGBUILD b/pkgbuilds/omarchy-task-manager/PKGBUILD new file mode 100644 index 0000000..3aecb4c --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/PKGBUILD @@ -0,0 +1,33 @@ +# Canonical recipe. scripts/package-source.sh fills the release source checksum. +# Maintainer: Tom Ballard (tcballard) +pkgname=omarchy-task-manager +pkgver=0.0.3 +pkgrel=1 +url='https://github.com/tcballard/omarchy-task-manager' +pkgdesc='Floating native task manager for Omarchy (preview)' +arch=('x86_64') +license=('MIT') +depends=('qt6-base' 'qt6-declarative' 'qt6-wayland' 'qt6-svg' 'hicolor-icon-theme' 'gcc-libs' 'glibc' 'glib2' 'systemd' 'coreutils') +optdepends=('gdb: live process core dumps' 'nvidia-utils: NVIDIA device telemetry') +makedepends=('cmake' 'ninja' 'rust' 'cargo') +checkdepends=('python' 'desktop-file-utils') +source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") +sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85') + +build() { + cmake -S "$srcdir/$pkgname-$pkgver" -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_INSTALL_LIBDIR=lib + cmake --build build +} + +check() { + cd "$srcdir/$pkgname-$pkgver" + cargo test --locked + ctest --test-dir "$srcdir/build" --output-on-failure + desktop-file-validate packaging/io.github.tcballard.TaskManager.desktop + python tests/protocol.py "$srcdir/build/omarchy-task-manager-core" +} + +package() { + DESTDIR="$pkgdir" cmake --install build +} From 7043dc72d32a3a7fba8688da0dce715a68142615 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Mon, 21 Sep 2026 17:41:21 +0100 Subject: [PATCH 2/4] Backport pause/resume test observation fix for 0.0.3-2 --- pkgbuilds/omarchy-task-manager/PKGBUILD | 14 ++++- .../pause-resume-test.patch | 62 +++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 pkgbuilds/omarchy-task-manager/pause-resume-test.patch diff --git a/pkgbuilds/omarchy-task-manager/PKGBUILD b/pkgbuilds/omarchy-task-manager/PKGBUILD index 3aecb4c..51b4065 100644 --- a/pkgbuilds/omarchy-task-manager/PKGBUILD +++ b/pkgbuilds/omarchy-task-manager/PKGBUILD @@ -2,7 +2,7 @@ # Maintainer: Tom Ballard (tcballard) pkgname=omarchy-task-manager pkgver=0.0.3 -pkgrel=1 +pkgrel=2 url='https://github.com/tcballard/omarchy-task-manager' pkgdesc='Floating native task manager for Omarchy (preview)' arch=('x86_64') @@ -11,8 +11,16 @@ depends=('qt6-base' 'qt6-declarative' 'qt6-wayland' 'qt6-svg' 'hicolor-icon-them optdepends=('gdb: live process core dumps' 'nvidia-utils: NVIDIA device telemetry') makedepends=('cmake' 'ninja' 'rust' 'cargo') checkdepends=('python' 'desktop-file-utils') -source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz") -sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85') +source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz" + "pause-resume-test.patch") +sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85' + 'ee97d57fed9f9d8b542b934f177560c8a6f0801642767d4d0787a183deafd09a') + +# Backport the test observation fix from upstream PR #11. +prepare() { + cd "$srcdir/$pkgname-$pkgver" + patch -Np1 -i "$srcdir/pause-resume-test.patch" +} build() { cmake -S "$srcdir/$pkgname-$pkgver" -B build -G Ninja \ diff --git a/pkgbuilds/omarchy-task-manager/pause-resume-test.patch b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch new file mode 100644 index 0000000..7bf3fb1 --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch @@ -0,0 +1,62 @@ +diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp +index bab79f2..9c9a38e 100644 +--- a/tests/bridge_test.cpp ++++ b/tests/bridge_test.cpp +@@ -79,37 +79,52 @@ private slots: + const auto before = bridge.history().size(); + QTest::qWait(1100); + QCOMPARE(bridge.history().size(), before); +- QSignalSpy samples(&bridge, &Bridge::snapshotChanged); ++ // Capture at emission: QTRY processes events, so another timer sample can ++ // replace bridge.snapshot() before the waiting assertion runs. ++ QVariantList samples; ++ QList historySizes; ++ connect(&bridge, &Bridge::snapshotChanged, &bridge, [&] { ++ samples.append(bridge.snapshot()); ++ historySizes.append(bridge.history().size()); ++ }); + bridge.setPaused(false); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); +- QCOMPARE(bridge.history().size(), 1); ++ QCOMPARE(historySizes.first(), 1); + bridge.active(false); + QTest::qWait(600); + samples.clear(); ++ historySizes.clear(); + bridge.active(true); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); + QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000); + samples.clear(); ++ historySizes.clear(); + bridge.refresh(); + QVERIFY(bridge.busy()); + bridge.setPaused(true); + bridge.setPaused(false); + QTRY_VERIFY_WITH_TIMEOUT(samples.count() > 0, 8000); +- QVERIFY(!bridge.snapshot() ++ QVERIFY(!samples.first().toMap() + .value("system") + .toMap() + .value("continuous") + .toBool()); ++ QCOMPARE(historySizes.first(), 1); ++ // Keep automatic refresh enabled. The baseline must be followed by a ++ // continuous sample; inspecting only the latest snapshot misses this order. ++ QTRY_VERIFY_WITH_TIMEOUT(samples.count() >= 2, 8000); ++ QVERIFY(samples.at(1).toMap().value("system").toMap() ++ .value("continuous").toBool()); + QTRY_VERIFY_WITH_TIMEOUT(!bridge.busy(), 8000); + QVERIFY(bridge.prepareManagement({{"category", "startup"}}).isEmpty()); + QVERIFY( From 45585daeaa7e9ff59194a9e2dea82a8f88bf7d23 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Mon, 21 Sep 2026 17:43:22 +0100 Subject: [PATCH 3/4] Include recorder lifetime guard in pause/resume test backport --- pkgbuilds/omarchy-task-manager/PKGBUILD | 2 +- pkgbuilds/omarchy-task-manager/pause-resume-test.patch | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkgbuilds/omarchy-task-manager/PKGBUILD b/pkgbuilds/omarchy-task-manager/PKGBUILD index 51b4065..8d43690 100644 --- a/pkgbuilds/omarchy-task-manager/PKGBUILD +++ b/pkgbuilds/omarchy-task-manager/PKGBUILD @@ -14,7 +14,7 @@ checkdepends=('python' 'desktop-file-utils') source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz" "pause-resume-test.patch") sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85' - 'ee97d57fed9f9d8b542b934f177560c8a6f0801642767d4d0787a183deafd09a') + 'ce191fc8dc7e4f7018193aa4982d58fd463f66b37e3129b3acce46b7a1c9a89e') # Backport the test observation fix from upstream PR #11. prepare() { diff --git a/pkgbuilds/omarchy-task-manager/pause-resume-test.patch b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch index 7bf3fb1..e256547 100644 --- a/pkgbuilds/omarchy-task-manager/pause-resume-test.patch +++ b/pkgbuilds/omarchy-task-manager/pause-resume-test.patch @@ -1,8 +1,8 @@ diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp -index bab79f2..9c9a38e 100644 +index bab79f2..9bed7be 100644 --- a/tests/bridge_test.cpp +++ b/tests/bridge_test.cpp -@@ -79,37 +79,52 @@ private slots: +@@ -79,37 +79,53 @@ private slots: const auto before = bridge.history().size(); QTest::qWait(1100); QCOMPARE(bridge.history().size(), before); @@ -11,7 +11,8 @@ index bab79f2..9c9a38e 100644 + // replace bridge.snapshot() before the waiting assertion runs. + QVariantList samples; + QList historySizes; -+ connect(&bridge, &Bridge::snapshotChanged, &bridge, [&] { ++ QObject sampleObserver; // Disconnect before the captured lists are destroyed. ++ connect(&bridge, &Bridge::snapshotChanged, &sampleObserver, [&] { + samples.append(bridge.snapshot()); + historySizes.append(bridge.history().size()); + }); From e768c4b1ca946ff5aa88d99e9ce72f8d6dc55937 Mon Sep 17 00:00:00 2001 From: Tom Ballard Date: Mon, 21 Sep 2026 18:27:38 +0100 Subject: [PATCH 4/4] Backport verified Bridge shutdown fix as 0.0.3-3; bound Qt test crashes --- pkgbuilds/omarchy-task-manager/PKGBUILD | 11 ++- .../worker-shutdown.patch | 90 +++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 pkgbuilds/omarchy-task-manager/worker-shutdown.patch diff --git a/pkgbuilds/omarchy-task-manager/PKGBUILD b/pkgbuilds/omarchy-task-manager/PKGBUILD index 8d43690..94f01cd 100644 --- a/pkgbuilds/omarchy-task-manager/PKGBUILD +++ b/pkgbuilds/omarchy-task-manager/PKGBUILD @@ -2,7 +2,7 @@ # Maintainer: Tom Ballard (tcballard) pkgname=omarchy-task-manager pkgver=0.0.3 -pkgrel=2 +pkgrel=3 url='https://github.com/tcballard/omarchy-task-manager' pkgdesc='Floating native task manager for Omarchy (preview)' arch=('x86_64') @@ -12,14 +12,17 @@ optdepends=('gdb: live process core dumps' 'nvidia-utils: NVIDIA device telemetr makedepends=('cmake' 'ninja' 'rust' 'cargo') checkdepends=('python' 'desktop-file-utils') source=("$url/releases/download/v$pkgver/$pkgname-$pkgver.tar.gz" - "pause-resume-test.patch") + "pause-resume-test.patch" + "worker-shutdown.patch") sha256sums=('cc3e24a0bb8fad2b7ce0d4fb780aa774d32b53b8de924b488b55674bdb936c85' - 'ce191fc8dc7e4f7018193aa4982d58fd463f66b37e3129b3acce46b7a1c9a89e') + 'ce191fc8dc7e4f7018193aa4982d58fd463f66b37e3129b3acce46b7a1c9a89e' + '1142aaedf8739bf2ed1cb823a03ed7e406d7ff59d7dfb72748815cc81cfda96b') -# Backport the test observation fix from upstream PR #11. +# Backport upstream PR #11 (sample observation) and PR #12 (worker shutdown). prepare() { cd "$srcdir/$pkgname-$pkgver" patch -Np1 -i "$srcdir/pause-resume-test.patch" + patch -Np1 -i "$srcdir/worker-shutdown.patch" } build() { diff --git a/pkgbuilds/omarchy-task-manager/worker-shutdown.patch b/pkgbuilds/omarchy-task-manager/worker-shutdown.patch new file mode 100644 index 0000000..d71f21b --- /dev/null +++ b/pkgbuilds/omarchy-task-manager/worker-shutdown.patch @@ -0,0 +1,90 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c940e5e..d1cd40f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -29,7 +29,8 @@ if(BUILD_TESTING) + target_include_directories(bridge-test PRIVATE ui) + target_link_libraries(bridge-test PRIVATE Qt6::Core Qt6::Gui Qt6::Test) + add_dependencies(bridge-test core) +- add_test(NAME bridge COMMAND bridge-test) ++ add_test(NAME bridge COMMAND bridge-test -nocrashhandler) ++ set_tests_properties(bridge PROPERTIES TIMEOUT 60) + endif() + if(BUILD_TESTING) + qt_add_executable(ui-test tests/ui_test.cpp ui/bridge.cpp ui/bridge.h) +@@ -37,5 +38,6 @@ if(BUILD_TESTING) + qt_add_resources(ui-test test_qml PREFIX "/" FILES ${TASK_MANAGER_QML}) + target_link_libraries(ui-test PRIVATE Qt6::Core Qt6::Gui Qt6::Quick Qt6::Qml Qt6::QuickControls2 Qt6::Test) + add_dependencies(ui-test core) +- add_test(NAME ui COMMAND ui-test) ++ add_test(NAME ui COMMAND ui-test -nocrashhandler) ++ set_tests_properties(ui PROPERTIES TIMEOUT 60) + endif() +diff --git a/tests/bridge_test.cpp b/tests/bridge_test.cpp +index 9bed7be..d6737a4 100644 +--- a/tests/bridge_test.cpp ++++ b/tests/bridge_test.cpp +@@ -3,9 +3,46 @@ + #include + #include + #include ++#include ++#include + class BridgeTest : public QObject { + Q_OBJECT + private slots: ++ void shutdownDoesNotPublish_data() { ++ QTest::addColumn("inFlight"); ++ QTest::addColumn("stopped"); ++ QTest::newRow("idle") << false << false; ++ QTest::newRow("sample-in-flight") << true << false; ++ QTest::newRow("stopped-worker") << true << true; ++ } ++ void shutdownDoesNotPublish() { ++ QFETCH(bool, inFlight); ++ QFETCH(bool, stopped); ++ auto bridge = std::make_unique(); ++ QTRY_VERIFY_WITH_TIMEOUT(!bridge->snapshot().isEmpty(), 8000); ++ bridge->m_timer.stop(); ++ QTRY_VERIFY_WITH_TIMEOUT(!bridge->busy(), 8000); ++ // Stop only the disposable worker owned by this bridge, forcing shutdown ++ // through terminate/kill rather than the normal stdin-EOF exit. ++ if (stopped) { ++ const auto workerPid = bridge->m_worker.processId(); ++ QVERIFY(workerPid > 0); ++ QCOMPARE(::kill(workerPid, SIGSTOP), 0); ++ } ++ if (inFlight) { ++ bridge->refresh(); ++ QVERIFY(bridge->busy()); ++ } ++ QSignalSpy snapshots(bridge.get(), &Bridge::snapshotChanged); ++ QSignalSpy statuses(bridge.get(), &Bridge::statusChanged); ++ QSignalSpy busy(bridge.get(), &Bridge::busyChanged); ++ // Closing the app must not publish late samples or worker-exit errors while ++ // its bridge and UI are being destroyed. Spies outlive the bridge on purpose. ++ bridge.reset(); ++ QCOMPARE(snapshots.count(), 0); ++ QCOMPARE(statuses.count(), 0); ++ QCOMPARE(busy.count(), 0); ++ } + void inspectionErrorsAndDismissal() { + Bridge bridge; + QTRY_VERIFY_WITH_TIMEOUT(!bridge.snapshot().isEmpty(), 8000); +diff --git a/ui/bridge.cpp b/ui/bridge.cpp +index 86b5b47..b1fb303 100644 +--- a/ui/bridge.cpp ++++ b/ui/bridge.cpp +@@ -82,7 +82,12 @@ Bridge::Bridge(QObject *p) : QObject(p), m_rows(this) { + m_clock.start(); + } + Bridge::~Bridge() { ++ // waitForFinished() can emit readyRead/error/finished synchronously. Quiesce ++ // callbacks before waiting, including if QProcess outlives our final wait and ++ // emits from its destructor after the other Bridge members are gone. + m_timer.stop(); ++ m_timeout.stop(); ++ m_worker.disconnect(this); + m_worker.closeWriteChannel(); + if (!m_worker.waitForFinished(600)) { + m_worker.terminate();