Skip to content

squad upgrade overwrites local edits to .github/workflows/* #1493

Closed

Description

@tamirdresher

Split off from #1436.

Problem

squad upgrade currently rewrites .github/workflows/* from bundled CLI templates without preserving user edits. Projects that have hand-tuned their CI (added a job, changed a trigger, tightened permissions, pinned an action to a SHA) lose those edits silently on every upgrade. There is no prompt, no diff, no backup.

This is the operative CI surface, so overwriting it silently is a real regression risk (and a permissions / supply-chain risk in repos that pin action SHAs).

Scope

Only the operative workflow files under .github/workflows/ are in scope. The stale template copies under .squad/templates/workflows/ are being addressed separately in #1436.

Options to evaluate

  1. .local-backup pattern. Before overwriting foo.yml, copy the current file to foo.yml.local-backup (or foo.yml.pre-upgrade-<timestamp>.bak) so the user always has the previous version on disk. Pros: zero UX changes, always safe. Cons: leaves clutter, does not stop the overwrite.
  2. Preserve sentinel. Recognize a leading marker in the workflow file (e.g. # squad-preserve: true on line 1 or line 2) and skip overwriting any workflow that carries it. Pros: explicit user opt-in per file, no config surface. Cons: relies on users knowing the sentinel exists.
  3. Preserve flag. New squad upgrade flag (--preserve-workflows, or --preserve <glob>) plus a config entry under .squad/config.json (e.g. upgrade.preserve: [".github/workflows/**"]). Pros: declarative, discoverable, per-repo. Cons: bigger surface to design and document.
  4. Diff + prompt. Detect a hash mismatch between the on-disk file and the last-known-shipped template, and either prompt in interactive mode or emit a .diff file and skip the write in non-interactive mode. Pros: safest default. Cons: needs a shipped-template manifest and hash tracking.

Options are not mutually exclusive. A reasonable minimum viable fix could be (1)+(2): always drop a .local-backup and honor a sentinel, with (3) as a follow-up if we want declarative config, and (4) as the long-term correct answer.

Acceptance

  • squad upgrade on a project with a modified .github/workflows/*.yml file does not silently discard the modification.
  • Regression test that starts from a modified workflow file, runs squad upgrade, and asserts the modification survived (or was preserved to a .local-backup alongside a clear log line).
  • Docs updated with the chosen mechanism.

Related

Activity

  1. jmservera commented on Jul 20, 2026

    @jmservera

    The file most commonly affected is squad-ci.yml, as Squad tends to place all CI-related logic there unless instructed otherwise. Given the way this file is structured (it appears to be little more than a scaffold for a GitHub Action) it is very likely to be customized. However, the squad upgrade process always rewrites it, which can overwrite those modifications.

  2. omercangumus commented on Aug 13, 2026

    @omercangumus
    Contributor

    reproduced this in est/workflows.test.cjs: a modified .github/workflows/squad-ci.yml is overwritten by squad upgrade and no .local-backup is left behind. taking the small backup path here and adding coverage around the pre-upgrade workflow invariant.

  3. added
    squadSquad triage inbox — Lead will assign to a member
    on Aug 23, 2026
  4. bradygaster commented on Aug 23, 2026

    @bradygaster
    Collaborator

    Owner & rationale

    Owner: Booster (squad:booster). This is workflow-generation safety: squad upgrade writes conventional .github/workflows/*.yml files from bundled templates and currently discards local CI hardening. The implementation touches CLI upgrade code, but the acceptance gate is “workflow edits are not silently lost.”

    This targets the templated workflow surface copied by squad upgrade, not gh-aw source files in workflows/*.md.

    Files in scope

    Verified existing paths:

    • packages/squad-cli/src/cli/core/upgrade.ts
    • packages/squad-cli/src/cli/core/templates.ts
    • index.cjs
    • test/workflows.test.cjs
    • templates/workflows/squad-ci.yml
    • packages/squad-cli/templates/workflows/squad-ci.yml
    • packages/squad-sdk/templates/workflows/squad-ci.yml
    • .github/workflows/squad-workflow-lint.yml

    Reproduction / measurement

    Execution-tested command, using a scratch directory inside the repo workspace and no output suppression:

    $root = (Get-Location).Path
    $sandbox = Join-Path $root '.squad\triage-sandbox\issue-1493'
    New-Item -ItemType Directory -Force $sandbox | Out-Null
    Set-Content -Path (Join-Path $sandbox 'package.json') -Value '{"name":"triage-1493","version":"1.0.0"}'
    Push-Location $sandbox
    node (Join-Path $root 'index.cjs')
    node (Join-Path $root 'index.cjs') upgrade
    $workflow = Join-Path $sandbox '.github\workflows\squad-ci.yml'
    Set-Content -Path $workflow -Value "name: Local Custom CI`non: [push]`njobs:`n  custom:`n    runs-on: ubuntu-latest`n    steps:`n      - run: echo custom workflow edit`n"
    node (Join-Path $root 'index.cjs') upgrade
    $backup = "$workflow.local-backup"
    "custom_survived=$((Get-Content -Raw $workflow).Contains('custom workflow edit'))"
    "backup_exists=$(Test-Path $backup)"
    "workflow_first_line=$((Get-Content $workflow -TotalCount 1))"
    Pop-Location
    Remove-Item -Recurse -Force $sandbox

    Observed output:

    custom_survived=False
    backup_exists=False
    workflow_first_line=name: Squad CI
    

    Verification bar

    A fix is acceptable only if the same scenario produces either custom_survived=True or backup_exists=True, plus a clear upgrade log line telling the user where the original content went. Add/keep a regression test in test/workflows.test.cjs that fails against current dev and passes with the preservation mechanism.

    Traps

    • PR fix(cli): back up workflows during upgrade #1709 implements the .local-backup minimum path and adds test coverage, but it does not update user-facing docs for the chosen mechanism.
    • .github/workflows/squad-workflow-lint.yml already exists. If workflow-template validation changes while fixing this, extend that workflow rather than adding a parallel lint path.
    • Do not route this to workflows/*.md; gh-aw compilation is not the code path. The overwrite happens in writeWorkflowFile/template-copy logic.
    • Be careful with non-npm stubs: generateProjectWorkflowStub writes generated content that must get the same preservation behavior as verbatim template copies.

    Out of scope

    Session-stall guard

    Before starting: this repo has phantom CRLF diffs (#1793). If git status shows files modified that you did not touch, run git diff --ignore-cr-at-eol. If that output is empty, the diff is line-ending churn, not real edits — run git add --renormalize . and continue. Do not halt and ask for confirmation; halting on phantom diffs is the documented cause of stuck sessions in this repo.

  5. bradygaster commented on Aug 23, 2026

    @bradygaster
    Collaborator

    PR #1709 triage verdict

    I inspected PR #1709’s diff and status.

    Finding: PR #1709 partially addresses #1493. It implements the minimum .local-backup path in both packages/squad-cli/src/cli/core/upgrade.ts and index.cjs, and adds regression coverage in test/workflows.test.cjs. That is the right direction and should stop silent loss of local edits by preserving the pre-upgrade file.

    CI/mergeability: gh pr view 1709 --json ... reports mergeable: MERGEABLE; visible check runs on commit 3f6b0d6 are successful/skipped, not genuinely failing. The automated nudge says the branch is now behind dev, so this looks like stale green CI that needs a rebase/rerun, not a failing-test investigation.

    Recommendation: request changes before merge:

    1. Add user-facing documentation/help text for the chosen .local-backup mechanism, because squad upgrade overwrites local edits to .github/workflows/* #1493 acceptance includes “Docs updated with the chosen mechanism.”
    2. Rebase or update from dev and rerun CI so the green checks are current.

    I would not close the PR as superseded; it is a valid partial fix once those two items are addressed.

  6. added a commit that references this issue on Aug 24, 2026
    6a4ff10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    go:spec-readyReady for formal spec/proposalhelp wantedExtra attention is neededpriority:p1This sprintsquadSquad triage inbox — Lead will assign to a membertriagedIssue has been triagedtype:bugSomething brokenwave:1-nextWave 1 — immediate next sprintworkflows

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions