From acf5732d09e78ddde166928cc7aa80991ad88fd0 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 17 Oct 2026 18:39:40 +0700 Subject: [PATCH 1/3] fix(studio): keep SMV/PTP start direct and remove unsolicited firmware dialogs --- apps/arstack_studio/qml/PtpExpertPanel.qml | 12 ++++++------ apps/arstack_studio/qml/StudioTimingTray.qml | 4 ++-- apps/arstack_studio/qml/WorkflowBar.qml | 19 +++++++++---------- .../tests/check_operator_workspace.py | 12 ++++++++++++ 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/apps/arstack_studio/qml/PtpExpertPanel.qml b/apps/arstack_studio/qml/PtpExpertPanel.qml index e4dae1fc1..14fb81763 100644 --- a/apps/arstack_studio/qml/PtpExpertPanel.qml +++ b/apps/arstack_studio/qml/PtpExpertPanel.qml @@ -177,7 +177,7 @@ SurfacePanel { Layout.fillWidth: true theme: panel.theme uiFont: panel.uiFont - model: ["Source — provide lab PTP", "Follower — auto-discover external GM", "Monitor — observe only"] + model: ["Source — transmit PTP", "Follower — follow PTP source", "Monitor — receive only"] currentIndex: panel.roleIndex(panel.activeRole) enabled: panel.session && panel.device.ptpAvailable && !panel.device.ptpRunning && !panel.startPending @@ -220,7 +220,7 @@ SurfacePanel { // The firmware already marks overrides as simulated, independently // of PTP clock discipline and without pretending sample phase lock. Rectangle { Layout.fillWidth: true; height: 1; color: panel.theme.lineSoft } - Label { text: "SV Sample Synchronization · lab simulation"; color: panel.theme.text; font.family: panel.uiFont; font.pixelSize: 12; font.weight: Font.DemiBold } + Label { text: "SV sample synchronization"; color: panel.theme.text; font.family: panel.uiFont; font.pixelSize: 12; font.weight: Font.DemiBold } RowLayout { Layout.fillWidth: true StudioDarkComboBox { @@ -228,13 +228,13 @@ SurfacePanel { Layout.fillWidth: true theme: panel.theme uiFont: panel.uiFont - model: ["AUTO — measured PTP only", "0 — Not synchronized", - "1 — Local sync (simulation)", "2 — Global sync (simulation)"] + model: ["AUTO — follow measured timing", "0 — Not synchronized", + "1 — Local flag (manual)", "2 — Global flag (manual)"] currentIndex: panel.syncIndex(panel.device.smpSynchMode) enabled: panel.session && panel.device.deviceVerified && !panel.session.updatingFirmware ToolTip.visible: hovered - ToolTip.text: "SV flag simulation only · not sample-clock lock" + ToolTip.text: "Manual 0/1/2 changes the transmitted SV flag, not the sample clock" onActivated: { const requested = ["AUTO", "0", "1", "2"][currentIndex] const accepted = panel.session && panel.session.requestSmpSynch(requested) @@ -259,7 +259,7 @@ SurfacePanel { } Label { Layout.fillWidth: true - text: panel.svTimingEvidence + "\nAUTO uses measured evidence; 0 / 1 / 2 only simulates the on-wire flag." + text: panel.svTimingEvidence + "\nManual 0/1/2 sets only the transmitted flag." wrapMode: Text.WordWrap color: panel.theme.textSoft font.family: panel.uiFont diff --git a/apps/arstack_studio/qml/StudioTimingTray.qml b/apps/arstack_studio/qml/StudioTimingTray.qml index 3ef3848b9..ef6af4eb7 100644 --- a/apps/arstack_studio/qml/StudioTimingTray.qml +++ b/apps/arstack_studio/qml/StudioTimingTray.qml @@ -32,7 +32,7 @@ Rectangle { readonly property bool ptpReady: Boolean(session && device && device.deviceVerified && device.ptpAvailable && !session.updatingFirmware) readonly property string stateText: !boardOnline - ? (offlinePtpPreview ? "PTP PLAN · OFFLINE" : "OFFLINE LAB") + ? (offlinePtpPreview ? "PTP PREVIEW" : "NO DEVICE") : !device.ptpAvailable ? "PTP UNAVAILABLE" : busy ? (device.ptpStatus.indexOf("unverified") !== -1 ? "UNVERIFIED" : "PENDING") : !device.ptpRunning ? "PTP OFF" @@ -87,7 +87,7 @@ Rectangle { if (!tray.boardOnline) { offlineSettings.previewPtp = wantsRunning if (tray.controller) - tray.controller.showMessage("Offline PTP plan only — no network packets or clock discipline.", false) + tray.controller.showMessage("PTP preview only. Connect ESP32-P4 to transmit.", false) } else if (wantsRunning) { if (tray.controller) tray.controller.startPtpRole(tray.roleValues[tray.selectedRoleIndex]) } else if (tray.session && !tray.session.requestStopPtp() && tray.controller) { diff --git a/apps/arstack_studio/qml/WorkflowBar.qml b/apps/arstack_studio/qml/WorkflowBar.qml index dcf3b93ba..e49593114 100644 --- a/apps/arstack_studio/qml/WorkflowBar.qml +++ b/apps/arstack_studio/qml/WorkflowBar.qml @@ -163,15 +163,11 @@ SurfacePanel { if (!progressDialog.opened) Qt.callLater(progressDialog.open) return } - if (smartSession.firmwareInstallRequired && !ribbon.installPromptDeferred && - !installDialog.opened && !progressDialog.opened) { - Qt.callLater(installDialog.open) - return - } - if (smartSession.firmwareUpdateRequired && !ribbon.updatePromptDeferred && - !updateDialog.opened && !progressDialog.opened) { - Qt.callLater(updateDialog.open) - } + // Firmware readiness is shown in the ribbon. Do not interrupt + // connection/discovery with unsolicited setup dialogs. If the + // operator presses Start with an incompatible/unprovisioned board, + // requestStart() offers the necessary action exactly then. + // Explicit firmware installation still owns its progress UI. } function onFirmwareUpdateFinished(success) { if (progressDialog.opened) progressDialog.close() @@ -189,7 +185,10 @@ SurfacePanel { : "Studio could not verify the firmware installation. Open Advanced > Firmware > Copy diagnostics." controller.showMessage("Firmware installation was not verified.", true) } - Qt.callLater(resultDialog.open) + // Installation completion is a normal successful transition, + // not another mandatory acknowledgment dialog. Retain full + // diagnostics and a modal only when verification failed. + if (!success) Qt.callLater(resultDialog.open) } } diff --git a/apps/arstack_studio/tests/check_operator_workspace.py b/apps/arstack_studio/tests/check_operator_workspace.py index af471e7e1..65c08f484 100644 --- a/apps/arstack_studio/tests/check_operator_workspace.py +++ b/apps/arstack_studio/tests/check_operator_workspace.py @@ -63,6 +63,18 @@ def main() -> None: require("1.75" in (STUDIO.parent.parent / ".github/workflows/arstack-studio-windows-lifecycle.yml").read_text(encoding="utf-8"), "reuse Windows executable for 100%, 150% and 175% DPI acceptance") workflow = source("WorkflowBar.qml") + require("Qt.callLater(installDialog.open)" not in workflow + and "Qt.callLater(updateDialog.open)" not in workflow + and "if (!success) Qt.callLater(resultDialog.open)" in workflow + and "installDialog.open()" in workflow + and "updateDialog.open()" in workflow, + "connection must not pop setup/update dialogs; Start may offer firmware repair") + require("provide lab PTP" not in expert + and "lab simulation" not in expert + and "Local flag (manual)" in expert + and 'SV sample synchronization' in expert + and '"OFFLINE LAB"' not in timing_ui, + "PTP operator controls must use direct understandable language") firmware_ui = source("FirmwarePanel.qml") timing_ui = source("StudioTimingTray.qml") ribbon = source("ModernRibbon.qml") From daa89fa4066fa6f4af3e17d3d31ebf1dc1058457 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 17 Oct 2026 18:39:53 +0700 Subject: [PATCH 2/3] test(studio): use already loaded timing tray in operator language guard --- apps/arstack_studio/tests/check_operator_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/arstack_studio/tests/check_operator_workspace.py b/apps/arstack_studio/tests/check_operator_workspace.py index 65c08f484..f298609c4 100644 --- a/apps/arstack_studio/tests/check_operator_workspace.py +++ b/apps/arstack_studio/tests/check_operator_workspace.py @@ -73,7 +73,7 @@ def main() -> None: and "lab simulation" not in expert and "Local flag (manual)" in expert and 'SV sample synchronization' in expert - and '"OFFLINE LAB"' not in timing_ui, + and '"OFFLINE LAB"' not in tray, "PTP operator controls must use direct understandable language") firmware_ui = source("FirmwarePanel.qml") timing_ui = source("StudioTimingTray.qml") From e4bbf6fc15dba969f24f3a1b0aa93a25bf85e577 Mon Sep 17 00:00:00 2001 From: Ari Sulistiono Date: Sat, 17 Oct 2026 18:41:53 +0700 Subject: [PATCH 3/3] test(studio): guard offline preview semantics without noisy copy --- apps/arstack_studio/tests/check_operator_workspace.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/arstack_studio/tests/check_operator_workspace.py b/apps/arstack_studio/tests/check_operator_workspace.py index f298609c4..48f76a6fc 100644 --- a/apps/arstack_studio/tests/check_operator_workspace.py +++ b/apps/arstack_studio/tests/check_operator_workspace.py @@ -103,7 +103,8 @@ def main() -> None: and "startWatchdog" in workflow and "onRunningChanged()" in workflow, "reconnect must not auto-transmit, but one-click Start must stage a bounded scenario") require("offline-ptp-lab" in tray and "if (!tray.boardOnline)" in tray - and "no network packets or clock discipline" in tray, + and "PTP preview only. Connect ESP32-P4 to transmit." in tray + and "offlineSettings.previewPtp = wantsRunning" in tray, "PTP/smpSynch offline controls must be plans only, not device writes") require('case "verify": return 0' in workflow and 'case "write": return 1' in workflow