From 0c943efa4a4f6743383e233315dfc30d55a2ca84 Mon Sep 17 00:00:00 2001 From: Brad Cunningham Date: Sat, 16 May 2026 00:48:33 -0400 Subject: [PATCH] fix(v0.53.1): restore execute.ts mutation-count threading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #268's squash merge dropped the execute.ts changes that wire domMutationCount from MUTATION_OBSERVER_STOP_SCRIPT through to PostState. types.ts (field declaration) and state-change.ts (classifier check) both landed correctly, but without execute.ts setting the field the classifier always sees undefined and falls through to legacy behavior. Spoonworks v0.53 validation (run xkyzfn64ao7w5yia4891my4p) confirmed: postState had mutationObserverWindowMs=137 (observer ran) but no domMutationCount field → missing_state_change FP persisted at 1/5 clusters. This commit re-applies the mutPayload extraction + childList mutation count + PostState assignment. No new tests needed (state-change.test.ts already covers the classifier path). --- packages/cli/src/phases/execute.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/phases/execute.ts b/packages/cli/src/phases/execute.ts index d676417..d438099 100644 --- a/packages/cli/src/phases/execute.ts +++ b/packages/cli/src/phases/execute.ts @@ -1107,7 +1107,16 @@ async function executeUiTestInner( } const mutResult = await scope.evaluate(MUTATION_OBSERVER_STOP_SCRIPT).catch(() => null); - const mutWindowMs = (mutResult?.value as { durationMs?: number } | undefined)?.durationMs ?? 0; + const mutPayload = mutResult?.value as { durationMs?: number; mutations?: Array<{ type?: string; addedCount?: number; removedCount?: number }> } | undefined; + const mutWindowMs = mutPayload?.durationMs ?? 0; + // v0.53: count meaningful (childList) mutations so classifyMissingStateChange + // can distinguish "the action did nothing" from "the action mutated DOM but + // not URL/network/aria/portal". Spoonworks Remove-row case. Threading was + // dropped during PR #268's squash merge; restored here. + const mutations = Array.isArray(mutPayload?.mutations) ? mutPayload.mutations : []; + const domMutationCount = mutations.filter(m => + (m.addedCount ?? 0) + (m.removedCount ?? 0) > 0, + ).length; const consoleResult = await scope.evaluate( '(window.__bhConsoleErrors || []).map(e => ({ level: "error", text: e.text, stack: e.stack }))' @@ -1232,6 +1241,7 @@ async function executeUiTestInner( // V24: set the real value instead of hardcoded false, so classifyMissingStateChange sees it. domErrorTextDetected: postDomErrFound, mutationObserverWindowMs: mutWindowMs, + domMutationCount, ariaSnapshot: postAriaSnapshot, newPortalCount, };