feat: add durable issue development loop #17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Issue dev loop evidence | |
| on: | |
| pull_request: | |
| branches: [dev] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: issue-dev-loop-evidence-${{ github.event.pull_request.number }} | |
| cancel-in-progress: true | |
| jobs: | |
| evidence: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Check out exact PR head | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| fetch-depth: 0 | |
| - name: Resolve loop run | |
| id: run | |
| run: node loops/issue-dev-loop/scripts/resolve-run.mjs --branch "${{ github.head_ref }}" | |
| - name: Assert immutable head | |
| run: test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha }}" | |
| - name: Protect append-only loop history | |
| if: steps.run.outputs.has_run == 'true' | |
| run: node loops/issue-dev-loop/scripts/validate-history.mjs --base-ref origin/dev | |
| - name: Set up pnpm | |
| uses: pnpm/action-setup@v6 | |
| - name: Set up Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run authoritative verification | |
| id: verify | |
| shell: bash | |
| run: | | |
| mkdir -p "${RUNNER_TEMP}/issue-dev-evidence" | |
| started_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| set +e | |
| pnpm verify 2>&1 | tee "${RUNNER_TEMP}/issue-dev-evidence/pnpm-verify.log" | |
| exit_code="${PIPESTATUS[0]}" | |
| set -e | |
| finished_at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" | |
| if [[ "${exit_code}" == "0" ]]; then verdict=passed; else verdict=failed; fi | |
| echo "started_at=${started_at}" >> "${GITHUB_OUTPUT}" | |
| echo "finished_at=${finished_at}" >> "${GITHUB_OUTPUT}" | |
| echo "exit_code=${exit_code}" >> "${GITHUB_OUTPUT}" | |
| echo "verdict=${verdict}" >> "${GITHUB_OUTPUT}" | |
| - name: Generate exact-head manifest | |
| if: steps.run.outputs.has_run == 'true' && always() | |
| run: >- | |
| node loops/issue-dev-loop/scripts/generate-evidence.mjs --run-id "${{ steps.run.outputs.run_id }}" --head-sha "${{ github.event.pull_request.head.sha }}" --status "${{ steps.verify.outputs.verdict || 'blocked' }}" --started-at "${{ steps.verify.outputs.started_at || github.event.pull_request.updated_at }}" --finished-at "${{ steps.verify.outputs.finished_at || github.event.pull_request.updated_at }}" --output "${RUNNER_TEMP}/issue-dev-evidence/manifest.json" | |
| - name: Upload review evidence | |
| if: steps.run.outputs.has_run == 'true' && always() | |
| id: artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: issue-dev-loop-${{ steps.run.outputs.run_id }}-${{ github.event.pull_request.head.sha }} | |
| path: | | |
| ${{ runner.temp }}/issue-dev-evidence | |
| loops/issue-dev-loop/screen-shots/${{ steps.run.outputs.run_id }} | |
| loops/issue-dev-loop/logs/runs/${{ steps.run.outputs.run_id }} | |
| if-no-files-found: error | |
| retention-days: 30 | |
| - name: Publish artifact link | |
| if: steps.run.outputs.has_run == 'true' && always() | |
| run: echo "Evidence artifact — ${{ steps.artifact.outputs.artifact-url }}" >> "${GITHUB_STEP_SUMMARY}" | |
| - name: Enforce verification result | |
| if: steps.verify.outputs.exit_code != '0' | |
| run: exit 1 |