diff --git a/scripts/check-no-git-push.mjs b/scripts/check-no-git-push.mjs index 5cdc06b8ae7..7254f6cafed 100644 --- a/scripts/check-no-git-push.mjs +++ b/scripts/check-no-git-push.mjs @@ -69,11 +69,18 @@ function stripLineComment(line) { for (let index = 0; index < line.length; index += 1) { const char = line[index]; - const prev = index > 0 ? line[index - 1] : ""; + // A character is escaped only if it's preceded by an odd number of + // backslashes; e.g. `"foo\\"` ends a string because the trailing `\\` + // is a single escaped backslash, leaving the closing `"` unescaped. + let backslashes = 0; + for (let scan = index - 1; scan >= 0 && line[scan] === "\\"; scan -= 1) { + backslashes += 1; + } + const isEscaped = backslashes % 2 === 1; - if (!inDouble && !inBacktick && char === "'" && prev !== "\\") inSingle = !inSingle; - else if (!inSingle && !inBacktick && char === '"' && prev !== "\\") inDouble = !inDouble; - else if (!inSingle && !inDouble && char === "`" && prev !== "\\") inBacktick = !inBacktick; + if (!inDouble && !inBacktick && char === "'" && !isEscaped) inSingle = !inSingle; + else if (!inSingle && !inBacktick && char === '"' && !isEscaped) inDouble = !inDouble; + else if (!inSingle && !inDouble && char === "`" && !isEscaped) inBacktick = !inBacktick; else if (!inSingle && !inDouble && !inBacktick && char === "/" && line[index + 1] === "/") { return line.slice(0, index); } diff --git a/scripts/check-no-git-push.test.mjs b/scripts/check-no-git-push.test.mjs index 90db672160f..b7917783603 100644 --- a/scripts/check-no-git-push.test.mjs +++ b/scripts/check-no-git-push.test.mjs @@ -67,6 +67,14 @@ test("findGitPushOffenses flags args-array form passed to spawn/execFile", () => } }); +test("findGitPushOffenses ignores `git push` in a comment after a string ending with a literal backslash", () => { + // The closing `"` after `\\` should end the string (even literal count of + // backslashes leaves the quote unescaped), so the `// git push` that + // follows is comment text and must be stripped. + const text = 'const path = "C:\\\\"; // git push origin master\nconst y = 2;\n'; + assert.deepEqual(findGitPushOffenses(text), []); +}); + test("findGitPushOffenses does not flag args-array form when allow marker is present", () => { const text = `// ${ALLOW_MARKER}: release tooling adapter\nspawn("git", ["push", "origin", "main"]);\n`; assert.deepEqual(findGitPushOffenses(text), []); diff --git a/server/src/services/issues.ts b/server/src/services/issues.ts index edd88106b3e..1a20f62b885 100644 --- a/server/src/services/issues.ts +++ b/server/src/services/issues.ts @@ -711,9 +711,12 @@ async function listIssueDependencyReadinessMap( // execution workspace hasn't recorded a successful workspace_finalize. // Treat the dependent as not-ready until sync-back lands (or the run // finalizes); a subsequent finalize wake will re-evaluate readiness. + // `allBlockersDone` is cleared too so that callers using it as a + // proxy for "this dependent can proceed" still see the gate. current.unresolvedBlockerIssueIds.push(row.blockerIssueId); current.unresolvedBlockerCount += 1; current.pendingFinalizeBlockerIssueIds.push(row.blockerIssueId); + current.allBlockersDone = false; current.isDependencyReady = false; } readinessMap.set(row.issueId, current);