Skip to content

[HIGH] upgrade overwrites customized .squad templates without backup and is not idempotent #2297

Closed

Description

@tamirdresher

Summary

squad upgrade overwrites repo-committed policy content under .squad/templates/ without creating a .local-backup. In the dogfood repo, .squad/templates/model-selection-reference.md contained active modelPolicy.allowedModels rules and a test asserting them; upgrade replaced it with the stock template and caused that test to fail.

This contradicts the upgrade help text that says .squad/ team state is left untouched. The same upgraded repo immediately reports 53 files to overwrite on squad upgrade --dry-run, so the upgrade is not idempotent. On Windows, adding one .gitignore line also rewrote the entire file from LF to CRLF.

Related but not duplicate: #2047 covers stale template snapshots; this report covers destructive overwrite, missing backup, idempotence, and line-ending churn.

Environment

  • Squad CLI 2.0.0-preview.1, installed from the exact published release tarball
  • Windows 11
  • Node.js 22
  • CFS-only npm host (https://packagefeedproxy.microsoft.io/npm/)

Repro

git status --short
squad --help
squad upgrade
git diff -- .squad/templates/model-selection-reference.md .gitignore
Get-ChildItem -Recurse -Filter *.local-backup
squad upgrade --dry-run

Observed

Help states:

Never touches: .squad/ or .ai-team/

Upgrade reports:

✓ refreshed .squad/templates/

But the customized, committed model-selection-reference.md was replaced and no corresponding .local-backup was created. A repository test then failed because the expected text was gone:

Expected 'modelPolicy\.allowedModels' to match '# Model Selection Reference …'

Immediately after the successful upgrade, dry-run still reported:

Would overwrite 53 squad-owned file(s)
squad.agent.md is unmodified — would refresh with latest template

The listed targets included 21 templates, 19 skills, 11 workflows, and three legacy .squad/casting-*.json paths. Separately, .gitignore changed from 0 CR bytes to 22 CR bytes for a single added line, producing a whole-file diff.

Expected

  • Customized committed files under .squad/templates/ are preserved or backed up before replacement.
  • Help accurately distinguishes mutable team state from Squad-owned template snapshots.
  • A second upgrade/dry-run at the same version is a no-op.
  • Existing line endings are preserved when appending .squad/backend/ to .gitignore.

Impact / Severity

HIGH. Upgrade can silently destroy repository policy text and break tests, while the advertised preservation contract says the opposite. Non-idempotent output and line-ending churn make it difficult to audit the actual changes.

Proposed fix

  1. Apply content-hash/customization detection and .local-backup behavior to every overwritten .squad/templates/** file.
  2. Correct the help text to document exactly which .squad/ paths are managed.
  3. Compare generated bytes before reporting/writing overwrites and add a second-upgrade no-op regression test.
  4. Preserve the existing newline style for edited files.
  5. Stop listing obsolete root-level casting paths after migration.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions