From 278ba9059db64a9180bb5b1885cdf74fc931a609 Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 2 Oct 2026 21:38:34 +0300 Subject: [PATCH 1/2] fix(ci): resolve dev-to-main promotion merge conflicts and false whitespace failures squad-promote.yml's dev-to-main merge (-X theirs) was failing with ~76 unresolved conflicts, diagnosed down to concrete, mechanical causes: 1. Git's rename-detection heuristic falsely paired newly-added template/ charter files (new on dev, absent at merge-base) with unrelated files independently deleted on main, producing ~51 spurious conflicts. Fix: add -X no-renames. 2. The remaining ~25 conflicts were genuine delete(main)/modify(dev) conflicts purely on .squad/.ai-team/etc paths that the script's own next step deletes from the release tree regardless of merge outcome. Fix: run that git rm --cached cleanup before the unmerged-conflict check instead of after, so these non-decisions don't block the merge. 3. .github/workflows/squad-preview.yml was hardened independently on main while being deliberately removed on dev as part of the preview/ stable release-channel consolidation (83612744, which folded its logic into squad-release.yml). Fix: accept dev's removal explicitly. Also fixes a second, independent latent blocker that would hit the real v1.0.0 promotion even after the conflicts above are resolved: the 'git diff --cached --check' validation step flags every added line in the repo's ~95 intentionally CRLF-encoded .ts files (see .gitattributes) as 'trailing whitespace', since they're deliberately excluded from eol=lf normalization to avoid a disruptive renormalization commit. Fix: run the check with core.whitespace=cr-at-eol, which still catches real trailing spaces/tabs and blank-line-at-EOF issues. That real issue was present too: test/gh-aw-hosted-e2e.test.ts had one stray trailing blank line at EOF, now removed (141/141 tests still pass for that file via vitest). Validated end-to-end against live origin/main and origin/dev in an isolated clone: merge -X theirs -X no-renames -> git rm cleanup for .squad/.ai-team/etc -> git rm for squad-preview.yml yields 0 unresolved conflicts, 0 forbidden paths remaining, and a clean cr-at-eol whitespace check. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/squad-promote.yml | 41 ++++++++++++++++++++++------- test/gh-aw-hosted-e2e.test.ts | 1 - 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/.github/workflows/squad-promote.yml b/.github/workflows/squad-promote.yml index 9683ec83a..b14b877f3 100644 --- a/.github/workflows/squad-promote.yml +++ b/.github/workflows/squad-promote.yml @@ -59,7 +59,31 @@ jobs: fi git checkout -B main origin/main - git merge origin/dev --no-commit --no-ff -X theirs || true + # -X no-renames: dev has many newly-added template/charter files that + # don't exist at the merge-base; without this, git's rename-detection + # heuristic falsely pairs them with unrelated files independently + # deleted on main, producing spurious rename/delete conflicts. + git merge origin/dev --no-commit --no-ff -X theirs -X no-renames || true + + # Strip internal-only paths *before* checking for unresolved + # conflicts. These paths are always removed from the release tree + # regardless of merge outcome, so a delete(main)/modify(dev) + # conflict on them isn't a real decision — resolve it here so it + # doesn't block the dev-to-main promotion below. + git rm -rf --cached --ignore-unmatch \ + .ai-team/ \ + .squad/ \ + .ai-team-templates/ \ + team-docs/ \ + docs/proposals/ + + # squad-preview.yml was intentionally removed on dev (83612744, + # "finalize preview and stable release channels", which consolidated + # preview/stable release logic into squad-release.yml) while main + # independently hardened it with SHA-pinned action refs before that + # redesign landed. Accept dev's removal; the equivalent + # functionality now lives in squad-release.yml. + git rm -f --cached --ignore-unmatch .github/workflows/squad-preview.yml unmerged="$(git diff --name-only --diff-filter=U)" if [ -n "$unmerged" ]; then @@ -68,13 +92,6 @@ jobs: exit 1 fi - git rm -rf --cached --ignore-unmatch \ - .ai-team/ \ - .squad/ \ - .ai-team-templates/ \ - team-docs/ \ - docs/proposals/ - forbidden="$(git ls-files | grep -E '^(\.(ai-team|squad|ai-team-templates)/|team-docs/|docs/proposals/)' || true)" if [ -n "$forbidden" ]; then echo "::error::Forbidden internal files remain in the release tree:" @@ -108,7 +125,13 @@ jobs: exit 1 fi - git diff --cached --check + # cr-at-eol: the repo deliberately keeps ~95 .ts files CRLF-encoded + # (see .gitattributes) to avoid a disruptive renormalization commit. + # Without this, --check flags every added line in those files as + # "trailing whitespace" merely for ending in \r\n, which would fail + # every promotion that touches them. This still catches genuine + # trailing spaces/tabs and blank-line-at-EOF issues. + git -c core.whitespace=cr-at-eol diff --cached --check echo "changed=true" >> "$GITHUB_OUTPUT" echo "version=$version" >> "$GITHUB_OUTPUT" echo "Prepared sanitized stable release v$version." diff --git a/test/gh-aw-hosted-e2e.test.ts b/test/gh-aw-hosted-e2e.test.ts index 8c8a72cd4..86ec06fba 100644 --- a/test/gh-aw-hosted-e2e.test.ts +++ b/test/gh-aw-hosted-e2e.test.ts @@ -1213,4 +1213,3 @@ describe('Squad gh-aw hosted E2E manual pull request fallback outcome', () => { expect(hostedBody).toMatch(/gh run download[\s\S]*?--repo[\s\S]*?--name squad-gh-aw-hosted-e2e-/); }); }); - From 5029ea1594998875fbf7ca7e9f9003ee5e0324bb Mon Sep 17 00:00:00 2001 From: Copilot Date: Fri, 2 Oct 2026 22:07:22 +0300 Subject: [PATCH 2/2] test: update stable-promotion merge command assertion for -X no-renames The 'stable promotion' contract test hardcoded the exact pre-fix merge command string ('git merge origin/dev --no-commit --no-ff -X theirs || true'). PR #2152's fix added '-X no-renames' to that command to eliminate spurious rename-detection-heuristic conflicts during the dev-to-main sanitized merge, so the literal substring no longer matched, failing CI run 37048896755. Updated the expected string to match the new, intentional command: 'git merge origin/dev --no-commit --no-ff -X theirs -X no-renames || true'. Verified via px vitest run test/standalone-release-workflow.test.ts (32/32 passed). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- test/standalone-release-workflow.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/standalone-release-workflow.test.ts b/test/standalone-release-workflow.test.ts index cd0f44125..5ec5c53cc 100644 --- a/test/standalone-release-workflow.test.ts +++ b/test/standalone-release-workflow.test.ts @@ -208,7 +208,9 @@ describe('stable promotion', () => { expect(Object.keys(promoteWorkflow.jobs)).toEqual(['dev-to-main']); const promotion = serialized(promoteWorkflow.jobs['dev-to-main']); - expect(promotion).toContain('git merge origin/dev --no-commit --no-ff -X theirs || true'); + expect(promotion).toContain( + 'git merge origin/dev --no-commit --no-ff -X theirs -X no-renames || true', + ); expect(promotion).toContain('git diff --name-only --diff-filter=U'); expect(promotion).toContain('git rm -rf --cached --ignore-unmatch'); expect(promotion).toContain('.ai-team/');