name: PR Pipeline — Authorize → Validate → Review → Merge # TRIGGER IS INTENTIONALLY UNFILTERED — DO NOT RE-ADD A `paths:` FILTER. # # This workflow previously carried `paths: ['**.prose.md', 'scripts/**.sh', # '**.yaml', '**.yml']` on both `push` and `pull_request`. Any PR whose diff # touched none of those patterns (for example a `deliverables/`-only PR, or a # `scripts/*.py` / `bin/*` change) therefore produced NO Gitea Actions run at # all: validation, lint, ai-review and the merge gate were silently skipped. # Validation must run for every pull request and every push to master, so the # trigger is deliberately unconditional. on: push: branches: [master] pull_request: types: [opened, synchronize, reopened] jobs: auth: runs-on: ubuntu-latest steps: - name: Checkout repository run: | git clone --depth=50 "http://192.168.68.17:3000/${{ gitea.repository }}.git" . git fetch origin "${{ gitea.ref }}" --depth=50 git checkout "${{ gitea.sha }}" - name: Authorization check run: bash scripts/prose-auth-check.sh validate: runs-on: ubuntu-latest needs: auth steps: - name: Checkout repository run: | git clone --depth=50 "http://192.168.68.17:3000/${{ gitea.repository }}.git" . git fetch origin "${{ gitea.ref }}" --depth=50 git checkout "${{ gitea.sha }}" - name: YAML frontmatter validation run: | echo "=== Prose Contract Frontmatter Validation ===" FAILED=0 for f in $(find . -name "*.prose.md" -not -path "./.git/*" -not -path "./runs/*"); do # NOTE: use herestrings, not `echo "$FM" | grep ...`. Under the runner's # `-e -o pipefail`, `grep -q` exits on first match and can SIGPIPE the # producer, making the pipeline report non-zero and raising a false # "Missing name/description" whose file set varies run to run. FM=$(sed -n '/^---$/,/^---$/p' "$f" | sed '1d;$d') [ -z "$FM" ] && { echo " ❌ $f: No YAML frontmatter"; FAILED=$((FAILED+1)); continue; } KIND=$(grep '^kind:' <<< "$FM" | awk '{print $2}') case "$KIND" in function|responsibility|gateway|pattern|test|template|architecture|enforcement) echo " ✅ $f: kind=$KIND" ;; *) echo " ❌ $f: Invalid kind='$KIND'"; FAILED=$((FAILED+1)) ;; esac grep -q '^name:' <<< "$FM" || { echo " ❌ $f: Missing name"; FAILED=$((FAILED+1)); } grep -q '^description:' <<< "$FM" || { echo " ❌ $f: Missing description"; FAILED=$((FAILED+1)); } done [ $FAILED -gt 0 ] && { echo "❌ FRONTMATTER FAILED ($FAILED error(s))"; exit 1; } echo "✅ Frontmatter validation passed" lint: runs-on: ubuntu-latest needs: validate steps: - name: Checkout repository run: | git clone --depth=50 "http://192.168.68.17:3000/${{ gitea.repository }}.git" . git fetch origin "${{ gitea.ref }}" --depth=50 git checkout "${{ gitea.sha }}" - name: Committed-credential scan (secret guard) run: | # Fails the build on a credential-shaped string in the tree. Patterns # live in scripts/secret-patterns.tsv; the only tolerated literal # examples are in scripts/secret-allowlist.tsv, each with a reason. # Do not turn this into a warning: a warning in a stream nobody reads # is how six live credentials sat in this repo for weeks. bash scripts/secret-scan.sh - name: Secret guard self-test run: bash tests/test_secret_scan.sh - name: Structure + regression + consistency lint run: bash scripts/prose-lint.sh ai-review: runs-on: ubuntu-latest needs: validate steps: - name: Checkout repository run: | git clone --depth=50 "http://192.168.68.17:3000/${{ gitea.repository }}.git" . git fetch origin "${{ gitea.ref }}" --depth=50 git checkout "${{ gitea.sha }}" - name: AI-powered contract review env: LITELLM_URL: ${{ secrets.LITELLM_URL }} LITELLM_KEY: ${{ secrets.LITELLM_KEY }} run: bash scripts/prose-ai-review.sh gate: runs-on: ubuntu-latest needs: [auth, validate, lint, ai-review] if: success() steps: - name: Merge gate run: | echo "╔══════════════════════════════════════╗" echo "║ ALL CHECKS PASSED — SAFE TO MERGE ║" echo "╚══════════════════════════════════════╝" echo "" echo " ✅ auth — authorized agent" echo " ✅ validate — frontmatter valid" echo " ✅ lint — structure + no regressions" echo " ✅ ai-review — no contradictions with ground truth" echo "" echo "Merge this PR to deploy to main."