diff --git a/test/gh-aw-review-workflow.test.ts b/test/gh-aw-review-workflow.test.ts index b8522e675..8bce2a555 100644 --- a/test/gh-aw-review-workflow.test.ts +++ b/test/gh-aw-review-workflow.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, it } from 'vitest'; -import { cpSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs'; -import { execFileSync } from 'node:child_process'; +import { cpSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'; +import { execFileSync, spawnSync } from 'node:child_process'; import { tmpdir } from 'node:os'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -209,11 +209,18 @@ describe('gh-aw advisory Squad reviewer', () => { } execFileSync('git', ['init', '--quiet'], { cwd: workspace }); for (const name of installOrder) { - execFileSync( + const result = spawnSync( 'gh', ['aw', 'compile', name.slice(0, -3), '--strict', '--approve', '--no-check-update'], { cwd: workspace, encoding: 'utf8', stdio: 'pipe' }, ); + const diagnostics = `${result.stdout}\n${result.stderr}`; + expect(result.error, `failed to launch gh aw for ${name}`).toBeUndefined(); + expect(result.status, `strict compile failed for ${name}:\n${diagnostics}`).toBe(0); + expect( + diagnostics, + `${name} must not emit the workflow_dispatch concurrency warning in a clean consumer install`, + ).not.toContain('concurrency.job-discriminator'); } const installed = readdirSync(workflowDir, { withFileTypes: true }) @@ -232,6 +239,32 @@ describe('gh-aw advisory Squad reviewer', () => { ]); }, 30000); + it('detects a missing workflow_dispatch job discriminator during strict compilation', () => { + const workspace = mkdtempSync(resolve(tmpdir(), 'squad-review-discriminator-mutation-')); + compileWorkspaces.push(workspace); + const workflowDir = resolve(workspace, '.github', 'workflows'); + mkdirSync(workflowDir, { recursive: true }); + cpSync(resolve(ROOT, 'workflows'), workflowDir, { recursive: true }); + + const workerPath = resolve(workflowDir, 'squad-deps-worker.md'); + const worker = readFileSync(workerPath, 'utf8').replace(/\r\n/g, '\n'); + expect(worker).toContain(' job-discriminator: ${{ github.run_id }}\n'); + writeFileSync(workerPath, worker.replace(' job-discriminator: ${{ github.run_id }}\n', '')); + execFileSync('git', ['init', '--quiet'], { cwd: workspace }); + + const result = spawnSync( + 'gh', + ['aw', 'compile', 'squad-deps-worker', '--strict', '--no-check-update', '--no-emit'], + { cwd: workspace, encoding: 'utf8', stdio: 'pipe' }, + ); + const diagnostics = `${result.stdout}\n${result.stderr}`; + expect(result.error, 'failed to launch gh aw for discriminator mutation').toBeUndefined(); + expect(result.status, `mutated workflow must remain compilable:\n${diagnostics}`).toBe(0); + expect(diagnostics).toContain( + 'workflow_dispatch workflow has no concurrency.job-discriminator', + ); + }, 20000); + it('keeps all consumer install surfaces on the coherent four-workflow order', () => { for (const surface of [GUIDE, README, AGENT_GUIDE, SHARED_BOOTSTRAP]) { const orders = installOrders(surface); diff --git a/workflows/squad-deps-worker.md b/workflows/squad-deps-worker.md index 730f29e3c..ca8ff45cd 100644 --- a/workflows/squad-deps-worker.md +++ b/workflows/squad-deps-worker.md @@ -25,6 +25,7 @@ permissions: concurrency: group: "squad-deps-${{ github.event.inputs.issue_number }}" cancel-in-progress: false + job-discriminator: ${{ github.run_id }} network: allowed: - defaults diff --git a/workflows/squad-implement-worker.md b/workflows/squad-implement-worker.md index 220b52434..f013caf3f 100644 --- a/workflows/squad-implement-worker.md +++ b/workflows/squad-implement-worker.md @@ -31,6 +31,7 @@ permissions: concurrency: group: "squad-implement-${{ github.event.inputs.issue_number || github.event.pull_request.number }}" cancel-in-progress: false + job-discriminator: ${{ github.run_id }} network: allowed: - defaults diff --git a/workflows/squad-review.md b/workflows/squad-review.md index 6b9b9b90e..43a9b1f38 100644 --- a/workflows/squad-review.md +++ b/workflows/squad-review.md @@ -36,6 +36,7 @@ permissions: concurrency: group: "squad-review-${{ github.event.inputs.issue_number || github.event.pull_request.number || github.run_id }}" cancel-in-progress: true + job-discriminator: ${{ github.run_id }} network: allowed: - defaults diff --git a/workflows/squad.md b/workflows/squad.md index 8feae2d89..5eb8660d9 100644 --- a/workflows/squad.md +++ b/workflows/squad.md @@ -34,6 +34,7 @@ permissions: concurrency: group: "squad-${{ github.event.inputs.issue_number || github.event.issue.number || github.event.pull_request.number || github.run_id }}" cancel-in-progress: false + job-discriminator: ${{ github.run_id }} network: allowed: - defaults