From 203c734340ffc60be1352f968004440586c5deb6 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 7 Jul 2026 10:25:11 -0700 Subject: [PATCH] fix(engine): require exact trailer line, not substring, for proven landed commit (Greptile P1) findProvenLandedCommit now keeps --grep as a prefilter but verifies each candidate carries an actual 'Fusion-Task-Id: ' trailer line via git show -s --format=%B, so a later commit that merely mentions the trailer text in its body cannot be selected. Regression covers a body-mention intervening commit. --- .../workspace-merger-idempotency.test.ts | 18 ++++++++++----- .../engine/src/workspace-land-predicate.ts | 22 +++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/engine/src/__tests__/workspace-merger-idempotency.test.ts b/packages/engine/src/__tests__/workspace-merger-idempotency.test.ts index 12021975a8..ee6e066b02 100644 --- a/packages/engine/src/__tests__/workspace-merger-idempotency.test.ts +++ b/packages/engine/src/__tests__/workspace-merger-idempotency.test.ts @@ -374,14 +374,20 @@ describeIfGit("landWorkspaceTask — DB-failure resilience (Phase C review A1/A4 expect(store.task.status ?? null).toBeNull(); /* - FNXC:Workspace 2026-07-07-10:30 (Phase C A1 precision regression — Greptile P1): - Simulate an intervening sub-repo land: advance repo-a's integration tip with an UNRELATED - commit AFTER the task's squash landed (tipAfterFirst) but BEFORE the lost-persist retry. The - recovered landedSha must be the task's OWN landing commit (tipAfterFirst), NOT the later - unrelated tip — otherwise finalize would attribute a wrong commit to this repo. + FNXC:Workspace 2026-07-07-10:55 (Phase C A1 precision regression — Greptile P1, two surfaces): + Advance repo-a's integration tip with an intervening commit whose MESSAGE BODY mentions the + trailer text "Fusion-Task-Id: FN-2002" (a changelog/diagnostic-style mention, NOT a real trailer + line) AFTER the task's squash landed (tipAfterFirst) but BEFORE the lost-persist retry. This + covers both precision surfaces: (1) the recovered landedSha must be the task's OWN landing commit + (tipAfterFirst), not the later tip; (2) the substring --grep prefilter must NOT select the + body-mention commit — findProvenLandedCommit requires an actual trailer line, so it skips the + mention and returns the real squash commit. */ configureIdentity(fx.repoPath("repo-a")); - fx.git("repo-a", 'git commit --allow-empty -m "unrelated intervening land (no Fusion-Task-Id trailer)"'); + fx.git( + "repo-a", + 'git commit --allow-empty -m "unrelated intervening land" -m "changelog: relates to Fusion-Task-Id: FN-2002 (body mention, not a trailer line)"', + ); const tipAfterIntervening = fx.git("repo-a", "git rev-parse refs/heads/main"); expect(tipAfterIntervening).not.toBe(tipAfterFirst); diff --git a/packages/engine/src/workspace-land-predicate.ts b/packages/engine/src/workspace-land-predicate.ts index 64ffcd31cb..769e037c7c 100644 --- a/packages/engine/src/workspace-land-predicate.ts +++ b/packages/engine/src/workspace-land-predicate.ts @@ -116,13 +116,27 @@ export async function findProvenLandedCommit( if (base) range = `${base.trim()}..${intRef}`; } const trailer = `${FUSION_TASK_ID_TRAILER_KEY}: ${taskId}`; - const found = await gitCapture( + /* + FNXC:Workspace 2026-07-07-10:50 (Phase C A1 precision — Greptile P1, trailer-line verification): + `git log --grep= --fixed-strings` is a substring search over the WHOLE commit message, + so a later changelog/diagnostic commit that merely mentions the trailer text in its body would + be selected over the actual squash commit. Use --grep only as a prefilter, then require an actual + trailer LINE (a line whose trimmed text is exactly the trailer) via `git show -s --format=%B`. + Candidates are reverse-chronological, so the first one with an exact trailer line is the task's + own landing commit. + */ + const candidates = await gitCapture( ["log", "--format=%H", `--grep=${trailer}`, "--fixed-strings", range], repoRootDir, ); - if (found) { - const firstSha = found.trim().split("\n")[0]; - if (firstSha) return firstSha; + if (candidates) { + for (const sha of candidates.trim().split("\n")) { + if (!sha) continue; + const body = await gitCapture(["show", "-s", "--format=%B", sha], repoRootDir); + if (body && body.split("\n").some((line) => line.trim() === trailer)) { + return sha; + } + } } } return undefined;