From 155855313f924c80565171ff71620730ea56888c Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Sat, 18 Jul 2026 07:58:47 -0700 Subject: [PATCH] fix(drift): pass --report to Auto-fix step so it reads the pinned report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FIX #F3 moved the collector's drift report to $RUNNER_TEMP (outside the repo checkout) and pointed the Assert and Create-PR steps at the pinned copy, but the Auto-fix step still ran fix-drift.ts with no --report. It fell back to the repo-root drift-report.json that FIX #F3 no longer writes, so readDriftReport threw "Drift report not found" and the fixer died with exit 3 before running — the recurring drift-job failure (e.g. run 29634448013). Pass --report "${PINNED_REPORT}" to the Auto-fix step, matching the Assert and Create-PR steps, so the fixer reads the same out-of-repo pinned report the integrity gate uses. Add regression tests locking the wiring. --- .github/workflows/fix-drift.yml | 12 ++++++++- src/__tests__/fix-drift-workflow.test.ts | 33 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/fix-drift.yml b/.github/workflows/fix-drift.yml index 6c80672b..8a03713f 100644 --- a/.github/workflows/fix-drift.yml +++ b/.github/workflows/fix-drift.yml @@ -159,6 +159,15 @@ jobs: echo "Pinned pre-fix drift report to $PINNED_REPORT (outside the LLM-writable repo checkout)" # Step 3: Invoke Claude Code to fix + # + # --report reads the PINNED pre-fix report (Step 2b), NOT the default + # in-repo `drift-report.json`. The collector writes its report to + # $RUNNER_TEMP (Step 1, FIX #F3), which is OUTSIDE the repo checkout, so + # the repo-root `drift-report.json` that fix-drift.ts defaults to does not + # exist — without this flag readDriftReport throws "Drift report not + # found" and the fixer never runs. Reading the pinned copy (same source + # the Assert and Create PR steps read) also keeps the fixer's view of the + # drift consistent with the downstream integrity gate. - name: Auto-fix drift id: autofix if: steps.check.outputs.skip != 'true' @@ -167,7 +176,8 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} - run: npx tsx scripts/fix-drift.ts + PINNED_REPORT: ${{ runner.temp }}/drift-report.pinned.json + run: npx tsx scripts/fix-drift.ts --report "${PINNED_REPORT}" # Upload Claude Code output for debugging - name: Upload Claude Code logs diff --git a/src/__tests__/fix-drift-workflow.test.ts b/src/__tests__/fix-drift-workflow.test.ts index 39cf930d..e48a5263 100644 --- a/src/__tests__/fix-drift-workflow.test.ts +++ b/src/__tests__/fix-drift-workflow.test.ts @@ -97,6 +97,39 @@ describe("fix-drift.yml — F-A: PRE-fix report pinned outside the LLM-writable }); }); +// --------------------------------------------------------------------------- +// report-path — the Auto-fix step must pass --report from the PINNED copy. +// +// FIX #F3 moved the collector's report to $RUNNER_TEMP (outside the repo +// checkout), so the repo-root drift-report.json that fix-drift.ts defaults to no +// longer exists. The Assert and Create-PR steps were updated to read --report +// from the pinned copy, but the Auto-fix step still ran `fix-drift.ts` with NO +// --report, so it fell back to the missing repo-root path and died with "Drift +// report not found" (readDriftReport) — the recurring drift-job failure. These +// lock that the Auto-fix step reads --report from the pinned copy, matching the +// downstream integrity gate. +// --------------------------------------------------------------------------- +describe("fix-drift.yml — report-path: Auto-fix step reads --report from the pinned copy", () => { + it("passes --report from the PINNED copy into the Auto-fix `fix-drift.ts` invocation", () => { + expect(wfFlat).toMatch(/scripts\/fix-drift\.ts --report "\$\{PINNED_REPORT\}"/); + }); + + it("does NOT run the Auto-fix step against the default (missing) repo-root drift-report.json", () => { + // The bare `npx tsx scripts/fix-drift.ts` (no --report) is the regression: + // it defaults to the repo-root drift-report.json which FIX #F3 no longer + // writes. Ensure the autofix invocation always carries a --report flag. + expect(wfFlat).not.toMatch(/npx tsx scripts\/fix-drift\.ts(?! --)/); + }); + + it("exposes PINNED_REPORT as an env in the Auto-fix step", () => { + const idx = wf.indexOf("name: Auto-fix drift"); + expect(idx).toBeGreaterThan(-1); + const nextStep = wf.indexOf("\n - name:", idx + 1); + const stepBlock = wf.slice(idx, nextStep === -1 ? undefined : nextStep); + expect(stepBlock).toContain("PINNED_REPORT: ${{ runner.temp }}/drift-report.pinned.json"); + }); +}); + // --------------------------------------------------------------------------- // FIX (round-4, user-approved) — HUMAN-APPROVAL BACKSTOP. The drift path opens a // PR but must NEVER auto-merge: the predicate is a strong AUTO-FILTER, not a