name: Squad Protected Branch Guard on: pull_request: branches: [main, preview] types: [opened, synchronize, reopened] permissions: contents: read pull-requests: read jobs: guard: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check for forbidden paths uses: actions/github-script@v7 with: script: | // Fetch all files changed in this PR (paginated) const files = []; let page = 1; while (true) { const resp = await github.rest.pulls.listFiles({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.payload.pull_request.number, per_page: 100, page }); files.push(...resp.data); if (resp.data.length < 100) break; page++; } // Check each file against forbidden path rules // Allow removals — deleting forbidden files from protected branches is fine const forbidden = files .filter(f => f.status !== 'removed') .map(f => f.filename) .filter(f => { // .ai-team/** — ALL team state files, zero exceptions if (f === '.ai-team' || f.startsWith('.ai-team/')) return true; // team-docs/** — ALL internal team docs, zero exceptions if (f.startsWith('team-docs/')) return true; return false; }); if (forbidden.length === 0) { core.info('✅ No forbidden paths found in PR — all clear.'); return; } // Build a clear, actionable error message const lines = [ '## 🚫 Forbidden files detected in PR to main', '', 'The following files must NOT be merged into `main`.', '`.ai-team/` is runtime team state — it belongs on dev branches only.', '`team-docs/` is internal team content — it belongs on dev branches only.', '', '### Forbidden files found:', '', ...forbidden.map(f => `- \`${f}\``), '', '### How to fix:', '', '```bash', '# Remove tracked .ai-team/ files (keeps local copies):', 'git rm --cached -r .ai-team/', '', '# Remove tracked team-docs/ files:', 'git rm --cached -r team-docs/', '', '# Commit the removal and push:', 'git commit -m "chore: remove forbidden paths from PR"', 'git push', '```', '', '> ⚠️ `.ai-team/` is committed on `dev` and feature branches by design.', '> The guard workflow is the enforcement mechanism that keeps these files off `main` and `preview`.', '> `git rm --cached` untracks them from this PR without deleting your local copies.', ]; core.setFailed(lines.join('\n'));