Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/widen-changelog-gate-template-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@bradygaster/squad-cli': patch
---

Widen changelog-gate coverage for template and scaffolding paths

Previously, the changelog gate only required a changeset for `packages/squad-(sdk|cli)/src/` changes, so template and scaffolding updates under `packages/squad-(sdk|cli)/templates/`, `.squad-templates/`, top-level `templates/`, and agent charters under `.squad/agents/*/charter.md` could reach users with no release-note entry. This widens the gate to those paths so user-facing template and scaffolding changes are no longer silently omitted from release notes. Closes #1156.
14 changes: 10 additions & 4 deletions .github/workflows/squad-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,18 @@ jobs:
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
CHANGED=$(git diff --name-only "$BASE"..."$HEAD")
SDK_CLI_CHANGED=$(echo "$CHANGED" | grep -E '^packages/squad-(sdk|cli)/src/' || true)
# Paths whose changes ship behavioral or scaffolding updates and therefore
# require a changeset (or CHANGELOG update). The top-level templates/ tree is
# the primary sync target of .squad-templates/ (see scripts/sync-templates.mjs) —
# an append-only superset whose entire tree is governed squad scaffolding, not
# unrelated content. Agent charters under .squad/agents/ are likewise governed.
SDK_CLI_PATH_REGEX='^packages/squad-(sdk|cli)/src/|^packages/squad-(sdk|cli)/templates/|^\.squad-templates/|^templates/|^\.squad/agents/.+/charter\.md$'
SDK_CLI_CHANGED=$(echo "$CHANGED" | grep -E "$SDK_CLI_PATH_REGEX" || true)
if [ -z "$SDK_CLI_CHANGED" ]; then
echo "✅ Not applicable (no SDK/CLI source changes)" >> $GITHUB_STEP_SUMMARY
echo "✅ Not applicable (no SDK/CLI source or template changes)" >> $GITHUB_STEP_SUMMARY
exit 0
fi
echo "SDK/CLI source files changed:"
echo "SDK/CLI source or template files changed:"
echo "$SDK_CLI_CHANGED"
CHANGESET_ADDED=$(git diff --diff-filter=AM --name-only "$BASE"..."$HEAD" | grep -E '^\.changeset/[^/]+\.md$' | grep -vxF '.changeset/README.md' || true)
CHANGELOG_CHANGED=$(echo "$CHANGED" | grep -E '^CHANGELOG\.md$' || true)
Expand All @@ -236,7 +242,7 @@ jobs:
echo "✅ CHANGELOG.md updated" >> $GITHUB_STEP_SUMMARY
exit 0
fi
echo "::error::No changeset or CHANGELOG.md update found, but SDK/CLI source files were changed."
echo "::error::No changeset or CHANGELOG.md update found, but SDK/CLI source or template files were changed."
echo "::error::Run 'npx changeset add' or edit CHANGELOG.md. Escape hatch: add 'skip-changelog' label."
echo "❌ No changeset or CHANGELOG.md update" >> $GITHUB_STEP_SUMMARY
exit 1
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ External contributors don't have write access, so the review-to-merge flow has a

**Your side (contributor):**

1. ✅ All required CI checks are green (build, test, lint; changeset/CHANGELOG gate only applies when `packages/squad-cli/src/` or `packages/squad-sdk/src/` files change)
1. ✅ All required CI checks are green (build, test, lint; the changeset/CHANGELOG gate applies when `packages/squad-(sdk|cli)/src/` **or** governed template/scaffolding paths change — `packages/squad-(sdk|cli)/templates/`, `.squad-templates/`, top-level `templates/`, and `.squad/agents/*/charter.md`)
2. ✅ PR is no longer a draft — mark as **"Ready for review"**
3. ✅ Copilot reviewer bot posts its review automatically
4. ✅ Review Copilot's suggestions and manually apply any you agree with in your fork
Expand All @@ -214,7 +214,7 @@ An automated readiness check runs on every PR and posts a checklist comment. Add
| **Not in draft** | Mark your PR as "Ready for review" when it's done |
| **Branch up to date** | Rebase on latest `dev` (`git fetch upstream && git rebase upstream/dev`) |
| **Copilot review** | Wait for the Copilot reviewer bot to post its review |
| **Changeset present** | Run `npx changeset add` if you changed files in `packages/squad-sdk/src/` or `packages/squad-cli/src/` |
| **Changeset present** | Run `npx changeset add` if you changed `packages/squad-(sdk|cli)/src/` or governed template/scaffolding paths (`packages/squad-(sdk|cli)/templates/`, `.squad-templates/`, top-level `templates/`, `.squad/agents/*/charter.md`) |
| **No merge conflicts** | Resolve any conflicts with the target branch |
| **CI passing** | All CI checks (build, test, lint) must be green |

Expand Down Expand Up @@ -277,7 +277,7 @@ npm unlink -w packages/squad-cli

Squad uses [@changesets/cli](https://github.com/changesets/changesets) for independent package versioning.

**When your PR changes SDK or CLI source files** (`packages/squad-sdk/src/` or `packages/squad-cli/src/`), add a changeset file instead of editing `CHANGELOG.md` directly. Changesets prevent merge conflicts when multiple PRs are open simultaneously and are the preferred workflow.
**When your PR changes SDK or CLI source files** (`packages/squad-sdk/src/` or `packages/squad-cli/src/`) **or governed template/scaffolding paths** (`packages/squad-(sdk|cli)/templates/`, `.squad-templates/`, top-level `templates/`, or `.squad/agents/*/charter.md`), add a changeset file instead of editing `CHANGELOG.md` directly. Changesets prevent merge conflicts when multiple PRs are open simultaneously and are the preferred workflow.

### Adding a Changeset

Expand Down Expand Up @@ -321,7 +321,7 @@ Add streaming support to agent orchestration. Update CLI to display stream progr

### CI Changelog Gate

The `changelog-gate` CI check enforces that PRs touching SDK/CLI source files include either:
The `changelog-gate` CI check enforces that PRs touching SDK/CLI source files — or governed template/scaffolding paths (`packages/squad-(sdk|cli)/templates/`, `.squad-templates/`, top-level `templates/`, `.squad/agents/*/charter.md`) — include either:
- A `.changeset/*.md` file (preferred), **or**
- A direct `CHANGELOG.md` edit (backward-compatible)

Expand Down
63 changes: 63 additions & 0 deletions test/ci/changelog-gate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Reads the live SDK_CLI_PATH_REGEX from the changelog gate workflow so this
* suite stays in sync with CI path matching. Guards issue #1156 against
* regressions in governed source/template paths.
*/

import { describe, it, expect, beforeAll } from 'vitest';
import { readFileSync } from 'node:fs';
import path, { dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const workflowPath = path.resolve(__dirname, '..', '..', '.github', 'workflows', 'squad-ci.yml');
const workflow = readFileSync(workflowPath, 'utf-8');
const patternMatch = workflow.match(/SDK_CLI_PATH_REGEX='([^']+)'/);

let regex: RegExp;

beforeAll(() => {
expect(
patternMatch,
'Expected changelog gate SDK_CLI_PATH_REGEX to be defined in squad-ci.yml',
).not.toBeNull();
regex = new RegExp(patternMatch?.[1] ?? '');
});

const governedPaths = [
'packages/squad-sdk/src/index.ts',
'packages/squad-cli/src/cli/core/bootstrap.ts',
'packages/squad-sdk/templates/skills/example/SKILL.md',
'packages/squad-cli/templates/scaffold/agent.md',
'.squad-templates/squad.agent.md',
'.squad-templates/scribe-charter.md',
'templates/skills/release-process/SKILL.md',
'templates/casting/policy.json',
'.squad/agents/troi/charter.md',
];

const unrelatedPaths = [
'README.md',
'docs/guide/intro.md',
'.github/workflows/squad-ci.yml',
'package.json',
'packages/squad-cli/package.json',
'scripts/bump-build.mjs',
'test/ci/changelog-gate.test.ts',
'.squad/agents/troi/notes.md',
'.squad/routing.md',
];

describe('changelog gate path matching', () => {
describe('governed paths require a changeset', () => {
it.each(governedPaths)('%s', filePath => {
expect(regex.test(filePath)).toBe(true);
});
});

describe('unrelated paths are not gated', () => {
it.each(unrelatedPaths)('%s', filePath => {
expect(regex.test(filePath)).toBe(false);
});
});
});
Loading