feat(ci): add workspace integrity, prerelease guard, and export smoke gates#688
feat(ci): add workspace integrity, prerelease guard, and export smoke gates#688diberry wants to merge 16 commits intobradygaster:devfrom
Conversation
Fork-specific workflow skills for PR hygiene: - fork-first-pipeline: review on fork before opening upstream PRs - bleed-check: twice-daily cross-branch audit for stowaway files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds Step 5.5 REBASE with guidance on rebasing feature branches against origin/dev to avoid full-file rewrites on shared files. Includes Shared File Strategy for surgical additions and When Rebase Fails recovery steps. Updates anti-patterns table to flag 'Skip rebase before upstream'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, conventions) - Fork-first-pipeline: Added serial branch operations warning, force-add note for gitignored skills, stale comments check - Bleed-check: Added high-risk shared files callout, convention gate checks, CI path debugging pattern Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Dual Reviewer Gate: both Flight and FIDO must approve - Convention Issues Are Blockers: /docs/ prefix, double blanks, table order, whitespace - Review→Fix→Re-review Loop: both reviewers must re-review after fixes - Reviewer Lockout: locked authors cannot self-fix after rejection - Known PAO Quirks: watch for .squad/ files, /docs/ prefix, verification before push - Review Comments: formal PR comment format and verdict options - Tamir Reviewer Rule: only on upstream PRs with source material - Commit Hygiene: one commit, amend fixups, force-push safely, verify before push Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a PR completes the full fork pipeline (Flight+FIDO approved, bleed check passed, squashed, rebased, moved to upstream targeting Brady's dev), the upstream PR should be undrafted immediately. The upstream PR is the final presentation — draft PRs signal incomplete work, but at this stage all iteration is finished on the fork. Add gh pr ready command to Step 7 (UPSTREAM) as the final action after opening the upstream PR. Add anti-pattern entry for leaving upstream PRs in draft. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Validates that the squad-main-guard.yml workflow was properly removed: - v0.5.4 migration in index.js correctly targets and deletes the guard - TEMPLATE_MANIFEST has no reference to squad-main-guard.yml - SDK init and CLI init/upgrade never create the guard workflow - No remaining workflow templates block .squad/ paths on push to main 10 tests covering guard removal, template manifest, init/upgrade safety, and workflow template scanning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Updated fork-first-pipeline skill with PR bradygaster#640 retrospective learnings - .squad/ decisions and state synced - Merged inbox decisions into decisions.md and decisions-archive.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prevents .squad/ stowaways on feature branches. Learned from 3 incidents on PR bradygaster#640. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 8097c96.
Adds .github/PR_REQUIREMENTS.md (versioned spec with 6 categories, CRUD-on-CLI/SDK user-facing definition, waiver process, exemptions) and .github/PULL_REQUEST_TEMPLATE.md (author-facing checklist). Part 1 of 2 for repo health -- #104 will automate enforcement. Closes Phase 2 of #106 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Part 2 of 2 for repo health. Adds two automated CI enforcement gates to squad-ci.yml: 1. CHANGELOG gate -- requires CHANGELOG.md update when SDK/CLI source changes 2. Exports map check -- verifies package.json exports match barrel files Both feature-flagged (vars.SQUAD_CHANGELOG_CHECK, vars.SQUAD_EXPORTS_CHECK) with skip labels. Includes test coverage for check-exports-map.mjs. Refs #104 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes #103 Adds samples-build CI job that validates all 11 samples compile when SDK changes. - Loops samples/ directories with npm install/build/test - Feature-flagged via skip-samples-build label - 15-minute timeout, --ignore-scripts, npm cache - Accepted Copilot suggestions: cache-dependency-path, workspace root install Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… gates Adds 3 new CI validation gates motivated by the PR bradygaster#640 prerelease version incident where npm silently resolved a stale registry SDK. - workspace-integrity: verifies lockfile has no stale registry entries for workspace packages (zero-install, reads lockfile only) - prerelease-version-guard: blocks prerelease version suffixes from merging to dev/main (zero-install, reads package.json only) - export-smoke-test: verifies all subpath exports resolve to built artifacts after SDK build (lightweight install+build) All gates follow existing patterns: feature flags (vars.SQUAD_*), skip labels, three-dot diff for change detection, ::error:: annotations. Closes #114 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Skip Labels Reference comment block listing all available skip labels (PAO, Flight) - Add local testing instructions to each health gate (Flight, FIDO) - Document change-detection regex patterns for future maintainers (FIDO) - Enhance export smoke test with dynamic import() validation (EECOM) - Add test PR creation hints to gate comments (FIDO) Closes #115 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove duplicate cache-dependency-path in export-smoke-test setup-node step - Remove false-positive else-if branch that flagged workspace packages lacking link:true (npm lockfile v3 workspace entries under node_modules/ use resolved:file: not link:true) Closes #115 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds additional CI “health gates” to reduce workspace/version/export regressions (motivated by prior incidents), plus supporting scripts/tests and documentation/process updates.
Changes:
- Add new CI jobs for workspace lockfile integrity, prerelease version blocking, exports-map validation, and subpath export smoke testing.
- Add scripts + Vitest coverage for exports-map checking and guard workflow removal regression protection.
- Add PR process artifacts (PR template/requirements) and new/updated docs pages.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/squad-ci.yml |
Adds multiple new CI gates (workspace integrity, prerelease guard, exports map check, export smoke test, samples build, changelog gate) and skip-label handling. |
scripts/check-exports-map.mjs |
New script to compare SDK barrel directories vs package.json exports. |
test/check-exports-map.test.ts |
Ensures the exports-map checker script runs and produces structured output. |
test/guard-removal.test.ts |
Adds regression tests ensuring the old “main guard” workflow isn’t recreated by init/upgrade/migrations/templates. |
docs/src/content/docs/features/context-hygiene.md |
Updates Nap description and links to new concept page. |
docs/src/content/docs/concepts/nap-vs-compact.md |
New concept page explaining Nap vs /compact. |
.github/PULL_REQUEST_TEMPLATE.md |
Adds a structured PR template with testing/docs/exports checklists. |
.github/PR_REQUIREMENTS.md |
Adds a versioned PR requirements spec doc. |
.squad/skills/fork-first-pipeline/SKILL.md |
New skill documenting a fork-first PR workflow. |
.copilot/skills/bleed-check/SKILL.md |
New skill documenting bleed-check audit process. |
.squad/decisions.md |
Moves/records decision entries (including CI safety items) and updates decision log content. |
.squad/decisions-archive.md |
Archives older decision entries removed from .squad/decisions.md. |
.squad/decisions/inbox/retro-copilot-git-safety.md |
Removes inbox entry (content moved elsewhere). |
.squad/decisions/inbox/booster-ci-deletion-guard.md |
Removes inbox entry (content moved elsewhere). |
| if ! (cd "$sample_dir" && npm install --ignore-scripts 2>&1); then | ||
| echo "::error::[$sample] npm install failed" |
There was a problem hiding this comment.
The samples gate uses npm install inside each sample directory even though many samples have package-lock.json. Using npm ci when a lockfile is present makes installs reproducible and reduces flakiness from dependency drift (and usually speeds CI up). Consider branching: npm ci if package-lock.json exists, else npm install.
| if ! (cd "$sample_dir" && npm install --ignore-scripts 2>&1); then | |
| echo "::error::[$sample] npm install failed" | |
| if [ -f "$sample_dir/package-lock.json" ]; then | |
| INSTALL_CMD="npm ci --ignore-scripts" | |
| else | |
| INSTALL_CMD="npm install --ignore-scripts" | |
| fi | |
| if ! (cd "$sample_dir" && $INSTALL_CMD 2>&1); then | |
| echo "::error::[$sample] dependency installation failed" |
| # ════════════════════════════════════════════════════════════════════════ | ||
| # Skip Labels Reference | ||
| # ──────────────────────────────────────────────────────────────────────── | ||
| # The following PR labels can be used to bypass specific health gates. | ||
| # Add them via the GitHub UI or `gh pr edit --add-label <label>`. | ||
| # | ||
| # skip-changelog — skip the changelog-gate job | ||
| # skip-exports-check — skip the exports-map-check job | ||
| # skip-samples-ci — skip the samples-build job | ||
| # skip-workspace-check — skip the workspace-integrity job | ||
| # skip-version-check — skip the prerelease-version-guard job | ||
| # skip-export-smoke — skip the export-smoke-test job | ||
| # large-deletion-approved — bypass the large-deletion guard (test job) | ||
| # | ||
| # These labels are compatible with sync-squad-labels.yml — add them | ||
| # to the label sync config to auto-create them in new repos. | ||
| # ════════════════════════════════════════════════════════════════════════ |
There was a problem hiding this comment.
PR description focuses on CI gates, but this workflow file also introduces additional non-CI process artifacts elsewhere in the PR (PR template/requirements and new skill docs). If those are intentionally in-scope, the PR description/title should mention them; otherwise consider splitting them into a separate PR to keep CI changes reviewable and revertible.
| ``` | ||
|
|
||
| In the interactive shell, use `/compact` for the same effect. | ||
| > **Note:** For session-level conversation compression, use `/compact` in the Copilot CLI interactive shell. See [Nap vs /compact](/concepts/nap-vs-compact) for the difference between team state cleanup and session transcript compression. |
There was a problem hiding this comment.
This internal docs link uses an absolute route (/concepts/nap-vs-compact) while most docs in this repo use relative .md links (e.g., ../concepts/github-workflow.md). Using relative links is generally more robust across differing site base paths and keeps link style consistent within the docs set.
| > **Note:** For session-level conversation compression, use `/compact` in the Copilot CLI interactive shell. See [Nap vs /compact](/concepts/nap-vs-compact) for the difference between team state cleanup and session transcript compression. | |
| > **Note:** For session-level conversation compression, use `/compact` in the Copilot CLI interactive shell. See [Nap vs /compact](../concepts/nap-vs-compact.md) for the difference between team state cleanup and session transcript compression. |
| <!-- Approach taken. Key design decisions and trade-offs. --> | ||
|
|
||
| ### Testing | ||
| <!-- What was tested and how. "npm run preflight" output or CI green. --> |
There was a problem hiding this comment.
The template mentions "npm run preflight", but the repo doesn’t define a preflight script. This can confuse contributors filling out the template; consider referencing the actual commands used here (e.g., npm run build && npm test) or adding a real preflight script if that’s intended.
| <!-- What was tested and how. "npm run preflight" output or CI green. --> | |
| <!-- What was tested and how. "npm run build && npm test" output or CI green. --> |
| \\\ | ||
| BRANCH → FORK PR → PREFLIGHT → REVIEW → FIX → BLEED CHECK → CLEAN → UPSTREAM → DONE | ||
| \\\ | ||
|
|
||
| ### Step 1: BRANCH | ||
| Create a feature branch locally: | ||
| \\\ash | ||
| git checkout -b squad/{issue-number}-{slug} | ||
| \\\ | ||
|
|
||
| ### Step 2: FORK PR | ||
| Push to fork and open PR **against your fork's dev branch**: | ||
| \\\ash | ||
| git push origin {branch-name} | ||
| gh pr create --base dev --draft # Opens on fork/dev, not upstream | ||
| \\\ |
There was a problem hiding this comment.
These \\\ / \\\bash blocks aren’t valid Markdown fences and include a stray control character before ash, so the commands won’t render as code blocks. Replace them with standard triple-backtick fences (bash ... ), and remove the backslashes/control character throughout the skill.
| Query: | ||
| \\\ash | ||
| gh pr list --author diberry --repo bradygaster/squad --base dev --state open | ||
| \\\ | ||
|
|
||
| ## Process | ||
|
|
||
| ### 1. List PRs | ||
| Fetch all open PRs from diberry targeting bradygaster/squad. | ||
|
|
||
| ### 2. For Each PR: Check File List | ||
| Retrieve the file list: | ||
| \\\ash | ||
| gh pr view {pr-number} --repo bradygaster/squad --json files | ||
| \\\ |
There was a problem hiding this comment.
These \\\bash / \\\ blocks aren’t valid Markdown fences and include a stray control character before ash, so the example commands won’t render correctly. Use standard triple-backtick code fences (bash ... ), and remove the backslashes/control character throughout this skill file.
| const srcEntries = readdirSync(SRC_DIR, { withFileTypes: true }); | ||
| const barrelDirs = srcEntries | ||
| .filter((entry) => entry.isDirectory()) | ||
| .filter((entry) => existsSync(join(SRC_DIR, entry.name, 'index.ts'))) | ||
| .map((entry) => entry.name); | ||
|
|
||
| const missing = []; | ||
|
|
||
| for (const dir of barrelDirs) { | ||
| const exportKey = `./${dir}`; | ||
| if (!exportsMap[exportKey]) { | ||
| missing.push({ dir, expectedKey: exportKey }); | ||
| } | ||
| } |
There was a problem hiding this comment.
check-exports-map.mjs currently fails if any top-level src/<dir>/index.ts barrel lacks a corresponding exports["./<dir>"] entry. The SDK already has existing barrel dirs (e.g. remote/, platform/, roles/, streams/, upstream/) without exports entries, so this gate will block the next PR that touches packages/squad-sdk/src/ unless those legacy gaps are addressed. Consider scoping the check to only new barrel dirs introduced in the PR (e.g., compute added src/*/index.ts dirs via git diff base...head), or maintain an explicit allowlist/denylist of public barrels.
| for (const [subpath, targets] of Object.entries(exportsMap)) { | ||
| const importPath = subpath === '.' | ||
| ? '@bradygaster/squad-sdk' | ||
| : '@bradygaster/squad-sdk/' + subpath.slice(2); | ||
| const filePath = typeof targets === 'string' | ||
| ? targets | ||
| : (targets.import || targets.default); | ||
| if (!filePath) { | ||
| failures.push({ subpath, importPath, error: 'No import target defined' }); | ||
| continue; | ||
| } | ||
| const resolvedPath = path.resolve('packages/squad-sdk', filePath); | ||
|
|
||
| // Phase 1: File existence check | ||
| if (!fs.existsSync(resolvedPath)) { | ||
| failures.push({ subpath, importPath, filePath, error: 'File not found: ' + resolvedPath }); | ||
| continue; | ||
| } | ||
|
|
||
| // Phase 2: Dynamic import() — verifies the module actually loads | ||
| // without syntax errors, missing dependencies, or broken re-exports. | ||
| try { | ||
| await import(pathToFileURL(resolvedPath).href); | ||
| passed++; | ||
| console.log(' ✅ ' + importPath + ' → ' + filePath + ' (exists + imports OK)'); | ||
| } catch (e) { | ||
| failures.push({ subpath, importPath, filePath, error: 'import() failed: ' + e.message }); | ||
| } |
There was a problem hiding this comment.
The export smoke test computes a package subpath specifier (@bradygaster/squad-sdk/...) but then imports the built file via file://... instead. That bypasses Node’s exports resolution rules, so a malformed/unsupported exports entry key could still pass as long as the target file exists. Importing via the computed package specifier would more accurately validate real consumer behavior.
| LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name' 2>/dev/null || echo "") | ||
| if echo "$LABELS" | grep -q "skip-changelog"; then | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| echo "Skipping CHANGELOG gate (skip-changelog label present)" | ||
| else | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| fi | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} |
There was a problem hiding this comment.
These label checks use gh pr view ... but the workflow’s top-level permissions: currently only grants contents: read. With restricted permissions, the GITHUB_TOKEN often cannot read PR metadata via the API, causing gh pr view to fail and skip labels to be ignored. Prefer using ${{ toJSON(github.event.pull_request.labels.*.name) }} (no API call) like other jobs here, or add pull-requests: read to the workflow permissions.
| LABELS=$(gh pr view ${{ github.event.pull_request.number }} --json labels --jq '.labels[].name' 2>/dev/null || echo "") | |
| if echo "$LABELS" | grep -q "skip-changelog"; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "Skipping CHANGELOG gate (skip-changelog label present)" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| LABELS='${{ toJSON(github.event.pull_request.labels.*.name) }}' | |
| if echo "$LABELS" | grep -q '"skip-changelog"'; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "Skipping CHANGELOG gate (skip-changelog label present)" | |
| else | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| fi |
|
Superseded by new PR with clean branch from upstream/dev (no fork-specific commits). |
Wraps InputPrompt in bordered Box (borderStyle=round, borderColor=cyan) for Copilot/Claude CLI style input zone. Includes NO_COLOR degradation and layout refinement. Closes bradygaster#679
Summary
Adds 3 new CI validation gates to \squad-ci.yml\ motivated by the PR #640 prerelease version incident where npm silently resolved a stale published SDK instead of the local workspace copy.
Gate 1: \workspace-integrity\
Gate 2: \prerelease-version-guard\
Gate 3: ^[xport-smoke-test\
Design
pm ci\ needed)
Closes #114
Merge Order
These 3 PRs form a dependency chain — merge in this order: