From 019c5f0ba5b926229545c565b07aedb34ab87655 Mon Sep 17 00:00:00 2001 From: Harshit Date: Fri, 5 Jun 2026 15:11:15 +0530 Subject: [PATCH] fix(observability): send TRA build-stop at test completion, before post-run work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Test Observability build duration (builds_th.duration = finished_at - started_at) was inflated by post-test CLI work. builds_th.finished_at is stamped server-side when the collector receives the build-stop event, and in sync mode browserstack-cypress-cli sent that event only AFTER a 5s safety wait, artifact download, and HTML report generation — counting all of it as "test time" (~37s in the reported case). Fire the build-stop (printBuildLink) right after polling resolves — the build has finished running on BrowserStack at that point — before the post-run work, so the build window reflects the test run. Split the printBuildLink buildStopped guard so the later handleSyncExit call still honors the process exit code (failing-build exit preserved). Scope: removes the post-session tail only. The pre-session provisioning window (started_at is stamped server-side at the early build-init, which must be sent early for session linking) is unchanged and is a separate Observability-backend concern affecting all SDKs. SDK-6211 Co-Authored-By: Claude Opus 4.8 --- bin/commands/runs.js | 9 +++++++++ bin/testObservability/helper/helper.js | 11 ++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bin/commands/runs.js b/bin/commands/runs.js index a4e21b5a..4d13d51e 100644 --- a/bin/commands/runs.js +++ b/bin/commands/runs.js @@ -364,6 +364,15 @@ module.exports = function run(args, rawArgs) { // stop the Local instance if (!turboScaleSession) await utils.stopLocalBinary(bsConfig, bs_local, args, rawArgs, buildReportData); + // SDK-6211: send the Test Observability build-stop now — polling has resolved, so + // the build has finished running on BrowserStack. builds_th.finished_at is stamped + // server-side when the collector receives this stop event, so firing it here (before + // the 5s safety wait, artifact download and HTML report generation below) keeps the + // TRA build "Duration" aligned with the test window instead of the full CLI wall-clock. + // printBuildLink no-ops on non-observability runs and is idempotent (buildStopped + // guard); the later handleSyncExit stop becomes a no-op that still honors the exit code. + await printBuildLink(true); + // waiting for 5 secs for upload to complete (as a safety measure) await new Promise(resolve => setTimeout(resolve, 5000)); diff --git a/bin/testObservability/helper/helper.js b/bin/testObservability/helper/helper.js index 264639b5..67586927 100644 --- a/bin/testObservability/helper/helper.js +++ b/bin/testObservability/helper/helper.js @@ -93,7 +93,16 @@ const supportFileCleanup = () => { exports.buildStopped = false; exports.printBuildLink = async (shouldStopSession, exitCode = null) => { - if(!this.isTestObservabilitySession() || exports.buildStopped) return; + if(!this.isTestObservabilitySession()) return; + // SDK-6211: the build-stop may be sent early (runs.js fires it at poll-resolution, before the + // post-test 5s wait + artifact download + report generation, so builds_th.finished_at — which + // the collector stamps at stop-event receipt — reflects the test window rather than the full CLI + // wall-clock). A later call here must therefore still honor the exit code instead of returning + // silently, preserving the original failing-build exit behaviour. + if(exports.buildStopped) { + if(exitCode) process.exit(exitCode); + return; + } exports.buildStopped = true; try { if(shouldStopSession) {