# Post-deploy production verification (read-only)
#
# Generated by `devaudit install` / `devaudit update` from sdlc-config.json.
# Do not edit manually — re-run the CLI (`devaudit update`) to regenerate.
#
# Production verification is READ-ONLY and starts only after a successful
# production deployment status. It must not join the main-push check suite a
# host evaluates before deciding whether to deploy.
# No E2E tests, no database operations, no API mutations.
#
# Promotes EVERY in-scope release (each requirement with a pending release
# ticket), not just the first REQ found — a develop→main PR that bundles
# several requirements must advance all of them to the terminal status, else
# the requirements not picked first stay stuck at uat_approved with no
# production evidence. `workflow_dispatch` allows re-running / catching up a
# single release via the `release` input.
#
# In sdlc-v1.22.0+ the terminal release status is configurable via
# sdlc-config.json `production_review.terminal_status`:
#   - "prod_review" (default, Option A) — stop at prod_review; human in the
#     portal clicks "Approve Production" then "Mark as Released".
#   - "released" (Option B) — preserves v1.21.x auto-release behaviour.

name: Post-Deploy Production Evidence

on:
  workflow_dispatch:
    inputs:
      release:
        description: 'Optional REQ-XXX / version to promote (blank = all in-scope from pending release tickets).'
        required: false
      runner_label:
        description: 'Optional one-run runner label override. Blank uses repository CI_RUNNER_LABEL.'
        required: false
  deployment_status:
    types: [created]

jobs:
  production-evidence:
    name: Production Evidence
    if: >-
      github.event_name == 'workflow_dispatch' ||
      (github.event.deployment_status.state == 'success' &&
       (github.event.deployment.environment == 'production' ||
        github.event.deployment.environment == 'prod' ||
        endsWith(github.event.deployment.environment, '/ production') ||
        endsWith(github.event.deployment.environment, '/production')))
    runs-on: {{RUNNER}}
    permissions:
      contents: read
      deployments: read
      issues: write
    env:
      DEVAUDIT_BASE_URL_VAR: ${{ vars.DEVAUDIT_BASE_URL }}
      DEVAUDIT_API_KEY: ${{ secrets.{{API_KEY_SECRET}} }}
      PROD_URL: ${{ secrets.{{PRODUCTION_URL_SECRET}} }}
      PROJECT_SLUG: {{PROJECT_SLUG}}
      GIT_SHA: ${{ github.event.deployment.sha || github.sha }}
      CI_RUN: ${{ github.run_id }}
      RELEASE_INPUT: ${{ github.event.inputs.release }}

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0   # full history so merged commits' REQ tags are readable

      - name: Resolve DevAudit base URL and post-deploy terminal status
        run: |
          # Prefer sdlc-config.json (visible in PR review) over repo Variable.
          CONFIG_URL=""
          TERMINAL_STATUS="prod_review"
          if [ -f sdlc-config.json ]; then
            CONFIG_URL=$(jq -r '.devaudit.base_url // empty' sdlc-config.json 2>/dev/null || true)
            CONFIG_TERMINAL=$(jq -r '.production_review.terminal_status // empty' sdlc-config.json 2>/dev/null || true)
            if [ -n "$CONFIG_TERMINAL" ]; then
              TERMINAL_STATUS="$CONFIG_TERMINAL"
            fi
          fi
          if [ -n "$CONFIG_URL" ]; then
            BASE="$CONFIG_URL"
            echo "Using devaudit.base_url from sdlc-config.json: $BASE"
          elif [ -n "$DEVAUDIT_BASE_URL_VAR" ]; then
            BASE="$DEVAUDIT_BASE_URL_VAR"
            echo "::warning::Using repo Variable DEVAUDIT_BASE_URL (deprecated in v1.23.0). Move base_url to sdlc-config.json devaudit.base_url."
          else
            echo "::error::No DevAudit base URL configured. Set devaudit.base_url in sdlc-config.json."
            exit 1
          fi
          if [ -z "${DEVAUDIT_API_KEY}" ]; then
            echo "::error::DEVAUDIT_API_KEY secret must be set."
            exit 1
          fi
          case "$TERMINAL_STATUS" in
            prod_review|released) ;;
            *)
              echo "::error::Invalid production_review.terminal_status '${TERMINAL_STATUS}'. Must be 'prod_review' or 'released'."
              exit 1
              ;;
          esac
          echo "Post-deploy terminal status: ${TERMINAL_STATUS}"
          echo "BASE=${BASE%/}" >> "$GITHUB_ENV"
          # Export for upload-evidence.sh, which reads $DEVAUDIT_BASE_URL directly.
          echo "DEVAUDIT_BASE_URL=${BASE%/}" >> "$GITHUB_ENV"
          echo "TERMINAL_STATUS=${TERMINAL_STATUS}" >> "$GITHUB_ENV"

      - name: Resolve in-scope releases
        run: |
          # The releases being PROMOTED are the requirements with a pending
          # release ticket (the same set the dev/UAT pipeline versioned via
          # derive-release-version.sh → REQ-XXX). A bundled develop→main PR
          # carries several; promote ALL of them, not just the first. A manual
          # dispatch can target one via the `release` input. A ticketless
          # main promotion is only valid when its explicit standalone
          # housekeeping declaration is present and valid.
          if [ -n "${RELEASE_INPUT}" ]; then
            REQS="${RELEASE_INPUT}"
            if [[ "$REQS" =~ ^v[0-9]{4}\.[0-9]{2}\.[0-9]{2}(\.[0-9]+)?$ ]]; then
              chmod +x scripts/standalone-housekeeping-release.sh 2>/dev/null || true
              DECLARATION="compliance/standalone-housekeeping/STANDALONE-HOUSEKEEPING-${REQS}.json"
              bash scripts/standalone-housekeeping-release.sh validate "$REQS" "$DECLARATION"
              echo "STANDALONE_DECLARATION=${DECLARATION}" >> "$GITHUB_ENV"
              echo "TERMINAL_STATUS=released" >> "$GITHUB_ENV"
            fi
          else
            REQS=""
            if [ -d compliance/pending-releases ]; then
              for T in compliance/pending-releases/RELEASE-TICKET-REQ-*.md; do
                [ -f "$T" ] || continue
                REQS="${REQS} $(basename "$T" .md | sed 's/^RELEASE-TICKET-//')"
              done
            fi
            REQS=$(echo ${REQS} | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/[[:space:]]*$//')
            if [ -z "${REQS}" ]; then
              chmod +x scripts/derive-release-version.sh scripts/standalone-housekeeping-release.sh 2>/dev/null || true
              VERSION=$(bash scripts/derive-release-version.sh)
              DECLARATION="compliance/standalone-housekeeping/STANDALONE-HOUSEKEEPING-${VERSION}.json"
              if [ -f "$DECLARATION" ]; then
                bash scripts/standalone-housekeeping-release.sh validate "$VERSION" "$DECLARATION"
                REQS="$VERSION"
                echo "STANDALONE_DECLARATION=${DECLARATION}" >> "$GITHUB_ENV"
                # Standalone housekeeping is reviewed in its GitHub promotion
                # PR. It does not enter the portal UAT/production queue unless
                # a project deliberately supplies a tracked REQ instead.
                echo "TERMINAL_STATUS=released" >> "$GITHUB_ENV"
              else
                echo "No tracked release ticket or standalone declaration: ordinary housekeeping has no portal promotion."
              fi
            fi
          fi
          echo "In-scope releases to promote: ${REQS}"
          echo "REQS=${REQS}" >> "$GITHUB_ENV"

      - name: Start production deployment executions
        run: |
          chmod +x scripts/report-test-execution.sh 2>/dev/null || true
          for PREFIX in ${REQS}; do
            RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
              "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
            VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
            [ -n "$VERSION" ] || VERSION="$PREFIX"
            bash scripts/report-test-execution.sh start \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --sdlc-stage 5 --environment production --suite-kind deployment \
              --provider github_actions --external-run-id "$CI_RUN" \
              --external-run-attempt "${{ github.run_attempt }}" --external-job-id "host-deployment" \
              --idempotency-key "github:${{ github.repository }}:post-deploy-prod.deployment:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
              --commit-sha "$GIT_SHA" --branch main \
              --workflow-name "Post-Deploy Production" \
              --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          done

      - name: Probe production health independently
        id: production_probe
        run: |
          set -euo pipefail
          DEPLOY_READY=false
          LAST_HTTP_CODE=000
          for i in $(seq 1 30); do
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${PROD_URL}/" || echo "000")
            LAST_HTTP_CODE="$HTTP_CODE"
            if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then
              echo "::notice::production health probe attempt=${i}/30 url=${PROD_URL}/ http_status=${HTTP_CODE}"
              DEPLOY_READY=true
              break
            fi
            echo "::notice::production health probe attempt=${i}/30 url=${PROD_URL}/ http_status=${HTTP_CODE}"
            sleep 10
          done
          if [ "$DEPLOY_READY" = "true" ]; then
            echo "outcome=success" >> "$GITHUB_OUTPUT"
            echo "verification=production_health_success" >> "$GITHUB_OUTPUT"
          else
            echo "outcome=failure" >> "$GITHUB_OUTPUT"
            echo "verification=production_health_timeout" >> "$GITHUB_OUTPUT"
            echo "::error::production_health_timeout: ${PROD_URL}/ did not return a successful response after 30 attempts (last HTTP ${LAST_HTTP_CODE})."
          fi
          echo "http_code=${LAST_HTTP_CODE}" >> "$GITHUB_OUTPUT"

      - name: Confirm terminal host deployment success
        id: host_deployment
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set +e
          chmod +x scripts/check-host-deployment.sh 2>/dev/null || true
          bash scripts/check-host-deployment.sh \
            --repo="${{ github.repository }}" \
            --sha="${GIT_SHA}" \
            --max-attempts=30 \
            --poll-seconds=10 \
            --output-file=host-deployment-result.env
          SCRIPT_EXIT=$?
          set -e
          if [ -f host-deployment-result.env ]; then
            for KEY in verification deployment_id deployment_state target_url environment elapsed_seconds; do
              VALUE=$(sed -n "s/^${KEY}=//p" host-deployment-result.env | head -n 1)
              echo "${KEY}=${VALUE}" >> "$GITHUB_OUTPUT"
            done
          fi
          exit "$SCRIPT_EXIT"

      - name: Complete production deployment executions
        if: always() && env.BASE != ''
        run: |
          HOST_VERIFICATION="${{ steps.host_deployment.outputs.verification || 'not_run' }}"
          PROBE_VERIFICATION="${{ steps.production_probe.outputs.verification || 'not_run' }}"
          if [ "$HOST_VERIFICATION" = "success" ] && [ "$PROBE_VERIFICATION" = "production_health_success" ]; then
            OUTCOME=passed
            CHECK_STATUS=successful
          elif [ "$HOST_VERIFICATION" = "deployment_status_timeout" ]; then
            OUTCOME=timed_out
            CHECK_STATUS=failed
          elif [ "${{ steps.host_deployment.outcome }}" = "cancelled" ]; then
            OUTCOME=cancelled
            CHECK_STATUS=cancelled
          else
            OUTCOME=failed
            CHECK_STATUS=failed
          fi
          chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
          for PREFIX in ${REQS}; do
            RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
              "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
            VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
            [ -n "$VERSION" ] || VERSION="$PREFIX"
            bash scripts/report-test-execution.sh complete \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --sdlc-stage 5 --environment production --suite-kind deployment \
              --provider github_actions --external-run-id "$CI_RUN" \
              --external-run-attempt "${{ github.run_attempt }}" --external-job-id "host-deployment" \
              --idempotency-key "github:${{ github.repository }}:post-deploy-prod.deployment:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
              --commit-sha "$GIT_SHA" --branch main \
              --workflow-name "Post-Deploy Production" \
              --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --outcome "$OUTCOME" --outcome-reason "host verification=${HOST_VERIFICATION}; production probe=${PROBE_VERIFICATION}; last HTTP=${{ steps.production_probe.outputs.http_code || 'unknown' }}"
            bash scripts/report-release-check.sh \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --check-key "production-deployment:${CI_RUN}:${{ github.run_attempt }}" \
              --label "Production Deployment" --provider github_actions --status "$CHECK_STATUS" \
              --external-run-id "$CI_RUN" \
              --external-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --commit-sha "$GIT_SHA" --branch main \
              --details-json "$(jq -cn \
                --arg hostVerification "$HOST_VERIFICATION" \
                --arg hostOutcome "${{ steps.host_deployment.outcome }}" \
                --arg deploymentId "${{ steps.host_deployment.outputs.deployment_id || '' }}" \
                --arg deploymentState "${{ steps.host_deployment.outputs.deployment_state || '' }}" \
                --arg targetUrl "${{ steps.host_deployment.outputs.target_url || '' }}" \
                --arg probeVerification "$PROBE_VERIFICATION" \
                --arg probeHttpCode "${{ steps.production_probe.outputs.http_code || '' }}" \
                '{hostVerification:$hostVerification,hostOutcome:$hostOutcome,deploymentId:$deploymentId,deploymentState:$deploymentState,targetUrl:$targetUrl,probeVerification:$probeVerification,probeHttpCode:$probeHttpCode}')"
          done
          if [ "$CHECK_STATUS" != "successful" ]; then
            echo "::error::Production deployment verification is not successful: host=${HOST_VERIFICATION}, probe=${PROBE_VERIFICATION}. Investigate the hosting provider before retrying."
            exit 1
          fi

      - name: Start production smoke executions
        if: steps.production_probe.outputs.verification == 'production_health_success' && steps.host_deployment.outputs.verification == 'success'
        run: |
          chmod +x scripts/report-test-execution.sh 2>/dev/null || true
          for PREFIX in ${REQS}; do
            RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
              "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
            VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
            [ -n "$VERSION" ] || VERSION="$PREFIX"
            bash scripts/report-test-execution.sh start \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --sdlc-stage 5 --environment production --suite-kind smoke \
              --provider github_actions --external-run-id "$CI_RUN" \
              --external-run-attempt "${{ github.run_attempt }}" --external-job-id "production-smoke" \
              --idempotency-key "github:${{ github.repository }}:post-deploy-prod.smoke:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
              --commit-sha "$GIT_SHA" --branch main \
              --workflow-name "Post-Deploy Production" \
              --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          done

      - name: Production smoke tests (read-only)
        id: production_smoke
        if: steps.production_probe.outputs.verification == 'production_health_success' && steps.host_deployment.outputs.verification == 'success'
        run: |
          echo "=== Production Smoke Tests ==="
          HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${PROD_URL}/")
          echo "Health check: HTTP ${HTTP_CODE}"
          if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 400 ]; then
            echo "::error::Production health check failed"
            exit 1
          fi
          curl -s -I "${PROD_URL}/" | grep -iE 'x-frame-options|x-content-type|strict-transport|content-security' || true
          echo "=== Smoke Tests Passed ==="
          cat > prod-smoke-results.json << RESULTS_EOF
          {
            "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
            "production_url": "${PROD_URL}",
            "git_sha": "${GIT_SHA}",
            "tests": [
              {"name": "health_check", "status": "passed"},
              {"name": "security_headers", "status": "checked"}
            ]
          }
          RESULTS_EOF

      - name: Complete production smoke executions
        if: always() && env.BASE != '' && steps.host_deployment.outcome == 'success'
        run: |
          case "${{ steps.production_smoke.outcome }}" in
            success) OUTCOME=passed; CHECK_STATUS=successful ;;
            cancelled) OUTCOME=cancelled; CHECK_STATUS=cancelled ;;
            skipped) OUTCOME=skipped; CHECK_STATUS=skipped ;;
            *) OUTCOME=failed; CHECK_STATUS=failed ;;
          esac
          chmod +x scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
          for PREFIX in ${REQS}; do
            RESP=$(curl -fsS -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
              "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
            VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
            [ -n "$VERSION" ] || VERSION="$PREFIX"
            bash scripts/report-test-execution.sh complete \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --sdlc-stage 5 --environment production --suite-kind smoke \
              --provider github_actions --external-run-id "$CI_RUN" \
              --external-run-attempt "${{ github.run_attempt }}" --external-job-id "production-smoke" \
              --idempotency-key "github:${{ github.repository }}:post-deploy-prod.smoke:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
              --commit-sha "$GIT_SHA" --branch main \
              --workflow-name "Post-Deploy Production" \
              --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --outcome "$OUTCOME" --outcome-reason "production smoke: ${{ steps.production_smoke.outcome }}"
            bash scripts/report-release-check.sh \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --check-key "production-smoke:${CI_RUN}:${{ github.run_attempt }}" \
              --label "Production Smoke" --provider github_actions --status "$CHECK_STATUS" \
              --external-run-id "$CI_RUN" \
              --external-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --commit-sha "$GIT_SHA" --branch main \
              --details-json '{"executionSource":"steps.production_smoke.outcome"}'
          done

      - name: File incident on smoke failure
        if: steps.production_smoke.outcome == 'failure'
        env:
          # GitHub issue/label mutations use the workflow token. DevAudit
          # user tokens are portal PATs, not guaranteed GitHub PATs (#305).
          GH_TOKEN: ${{ github.token }}
          PROD_URL: ${{ secrets.{{PRODUCTION_URL_SECRET}} }}
          GIT_SHA: ${{ github.sha }}
          CI_RUN: ${{ github.run_id }}
          WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
        run: |
          # DevAudit-Installer#210 §10a — file an incident issue when the
          # production smoke tests fail. The `incident` label ensures
          # incident-export.yml fires on close → incident_report evidence
          # lands on the portal → ISO29119.3.5.4 + SOC2.CC7.2 flip to COVERED.
          #
          # testExecutionId (CI run ID) cross-references #209 so the incident
          # report traces back to the specific test execution that detected
          # the production failure.
          #
          # Ensure the incident label exists (idempotent).
          gh label list --json name --jq '.[].name' | grep -qx incident || \
            gh label create incident --color 'B60205' \
              --description 'Operational, test, or compliance incident; close to auto-archive as portal evidence'

          SMOKE_RESULTS="No prod-smoke-results.json file found (smoke tests failed before producing output)."
          if [ -f prod-smoke-results.json ]; then
            SMOKE_RESULTS=$(cat prod-smoke-results.json)
          fi

          DATE=$(date -u +%Y-%m-%d)
          ISSUE_BODY=$(cat <<BODY_EOF
          ## Production Smoke Test Failure

          **Production URL:** ${PROD_URL}
          **Git SHA:** ${GIT_SHA}
          **testExecutionId:** ${CI_RUN}
          **Workflow run:** ${WORKFLOW_URL}
          **Date:** ${DATE}

          ### Smoke Results

          \`\`\`json
          ${SMOKE_RESULTS}
          \`\`\`

          This incident was detected by the autonomous post-deploy production smoke workflow. The production health check failed after deploying ${GIT_SHA}. Page the on-call per the project's incident playbook. The \`incident\` label ensures an \`incident_report\` will be generated on close.

          ### Framework attribution

          This defect, once closed with the \`incident\` label, will be auto-exported as \`incident_report\` evidence and attribute to:

          - [x] \`ISO29119.3.5.4\` (baseline — every incident_report)
          - [x] \`SOC2.CC7.2\` — ops impact: production health check failure means the app is down or degraded
          - [ ] \`GDPR.Art-33\` — personal data scope: <REPLACE — yes/no>
          - [ ] \`GDPR.Art-34\` — data-subject notification required: <REPLACE — yes/no>
          - [ ] \`EUAIA.Art-9 / Art-14 / Art-15\` — AI failure: <REPLACE — yes/no, which article(s)>

          Once closed, the \`incident-export.yml\` workflow exports this issue's body to \`compliance/governance/incident-report-<N>.md\`. Routing depends on the Framework attribution ticks (DevAudit-Installer#200 Fix 1):
          - **Path A (baseline-only — only \`ISO29119.3.5.4\` ticked):** direct-committed to \`develop\`, no PR. GDPR triage pre-filled as N/A. Next \`compliance-evidence.yml\` run uploads as \`incident_report\`.
          - **Path B (any of SOC2/GDPR/EUAIA ticked):** auto-files a PR with the GDPR triage + sign-off sections to fill in. Merge that PR → \`compliance-evidence.yml\` uploads as \`incident_report\`.
          BODY_EOF
          )

          # Check for existing open incident issue for this smoke failure (dedup).
          EXISTING=$(gh issue list --label incident --state open --search "[PROD-SMOKE] ${DATE}" --json number --jq '.[0].number' || true)
          if [ -n "$EXISTING" ]; then
            echo "Existing incident #$EXISTING already open for this date — posting comment instead of filing duplicate."
            gh issue comment "$EXISTING" --body "Additional smoke failure detected in workflow run ${WORKFLOW_URL} (SHA ${GIT_SHA})."
          else
            gh issue create \
              --title "[PROD-SMOKE] Production health check failed — ${DATE}" \
              --label "incident,blocker" \
              --body "$ISSUE_BODY"
            echo "Incident issue filed for production smoke failure."
          fi

      - name: Promote in-scope releases (evidence + status)
        if: steps.production_smoke.outcome == 'success'
        run: |
          chmod +x scripts/upload-evidence.sh scripts/report-test-execution.sh scripts/report-release-check.sh 2>/dev/null || true
          PROMOTED=0
          EVIDENCE_FAILURES=0
          for PREFIX in ${REQS}; do
            echo "=== Promoting ${PREFIX} ==="
            RESP=$(curl -s -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
              "${BASE}/api/ci/releases/resolve?projectSlug=${PROJECT_SLUG}&versionPrefix=${PREFIX}")
            VERSION=$(echo "$RESP" | jq -r '.latest.version // empty')
            [ -z "$VERSION" ] && VERSION="${PREFIX}"
            RELEASE_ID=$(echo "$RESP" | jq -r '.latest.id // empty')
            EVIDENCE_SCOPE="${VERSION}"
            if [ -n "${STANDALONE_DECLARATION:-}" ] && [ "$VERSION" = "${REQS}" ]; then
              EVIDENCE_SCOPE="_compliance-docs"
            fi
            REQ_FAILURES=0
            LINEAGE_FLAGS=(--test-execution "${CI_RUN}")
            EXECUTION_OUT="$(mktemp)"
            bash scripts/report-test-execution.sh start \
              --project-slug "${PROJECT_SLUG}" \
              --release "${VERSION}" \
              --sdlc-stage 5 \
              --environment production \
              --suite-kind smoke \
              --provider github_actions \
              --external-run-id "${CI_RUN}" \
              --external-run-attempt "${{ github.run_attempt }}" \
              --external-job-id "production-smoke" \
              --idempotency-key "github:${{ github.repository }}:post-deploy-prod.smoke:${CI_RUN}:${{ github.run_attempt }}:5:${VERSION}" \
              --commit-sha "${GIT_SHA}" \
              --branch main \
              --workflow-name "Post-Deploy Production" \
              --workflow-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --output-file "$EXECUTION_OUT"
            . "$EXECUTION_OUT"
            rm -f "$EXECUTION_OUT"
            if [ "${execution_supported:-false}" = "true" ] && [ -n "${execution_record_id:-}" ]; then
              LINEAGE_FLAGS+=(--evidence-scope execution --test-execution-record-id "${execution_record_id}")
            fi
            # Production smoke evidence (whole-app health) attached to this release.
            if [ -f prod-smoke-results.json ]; then
              bash scripts/upload-evidence.sh \
                "${PROJECT_SLUG}" "${EVIDENCE_SCOPE}" smoke_test prod-smoke-results.json \
                --release "${VERSION}" --create-release-if-missing --environment production \
                --category smoke_test --sdlc-stage 5 --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
                "${LINEAGE_FLAGS[@]}" \
                || { echo "Warning: smoke upload failed for ${VERSION}"; REQ_FAILURES=$((REQ_FAILURES + 1)); }
            fi
            # Carry the release ticket into the production environment so the
            # prod-review gate is self-contained.
            TICKET=""
            if [ -n "${STANDALONE_DECLARATION:-}" ] && [ "$VERSION" = "${REQS}" ]; then
              TICKET="$STANDALONE_DECLARATION"
            fi
            for DIR in compliance/pending-releases compliance/approved-releases; do
              if [ -f "${DIR}/RELEASE-TICKET-${VERSION}.md" ]; then
                TICKET="${DIR}/RELEASE-TICKET-${VERSION}.md"; break
              fi
            done
            if [ -n "$TICKET" ]; then
              bash scripts/upload-evidence.sh \
                "${PROJECT_SLUG}" "${EVIDENCE_SCOPE}" release_ticket "$TICKET" \
                --release "${VERSION}" --create-release-if-missing --environment production \
                --category release_artifact --sdlc-stage 5 --git-sha "${GIT_SHA}" --ci-run-id "${CI_RUN}" --branch main \
                || { echo "Warning: ticket upload failed for ${VERSION}"; REQ_FAILURES=$((REQ_FAILURES + 1)); }
            else
              echo "No RELEASE-TICKET-${VERSION}.md found — skipping ticket (date-versioned or archived)."
            fi
            if [ "$REQ_FAILURES" -gt 0 ]; then
              EVIDENCE_STATUS=failed
              EVIDENCE_FAILURES=$((EVIDENCE_FAILURES + 1))
            else
              EVIDENCE_STATUS=successful
            fi
            bash scripts/report-release-check.sh \
              --project-slug "$PROJECT_SLUG" --release "$VERSION" \
              --check-key "production-evidence:${CI_RUN}:${{ github.run_attempt }}" \
              --label "Production Evidence Completeness" --provider devaudit_installer \
              --status "$EVIDENCE_STATUS" --external-run-id "$CI_RUN" \
              --external-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
              --commit-sha "$GIT_SHA" --branch main \
              --details-json "{\"uploadFailures\":${REQ_FAILURES}}"
            if [ "$REQ_FAILURES" -gt 0 ]; then
              echo "::error::Skipping status promotion for ${VERSION}: production evidence is incomplete"
              continue
            fi
            # Advance status (idempotent — re-PATCHing an already-promoted release is a no-op).
            if [ -n "$RELEASE_ID" ]; then
              if [ -n "${STANDALONE_DECLARATION:-}" ] && [ "$VERSION" = "${REQS}" ]; then
                chmod +x scripts/standalone-housekeeping-release.sh 2>/dev/null || true
                bash scripts/standalone-housekeeping-release.sh promote \
                  "$PROJECT_SLUG" "$VERSION" "$STANDALONE_DECLARATION"
              fi
              curl -s -o /dev/null -w "  ${VERSION} status patch: HTTP %{http_code}\n" \
                -X PATCH "${BASE}/api/ci/releases/${RELEASE_ID}" \
                -H "Authorization: Bearer ${DEVAUDIT_API_KEY}" \
                -H "Content-Type: application/json" \
                -d "{\"status\":\"${TERMINAL_STATUS}\"}"
              echo "  ${VERSION} → ${TERMINAL_STATUS}"
              PROMOTED=$((PROMOTED + 1))
            else
              echo "::warning::No release_id resolved for ${PREFIX} — skipping status patch"
            fi
          done
          if [ "$EVIDENCE_FAILURES" -gt 0 ]; then
            echo "::error::${EVIDENCE_FAILURES} release(s) have incomplete production evidence"
            exit 1
          fi
          echo "Promoted ${PROMOTED} release(s) to ${TERMINAL_STATUS}."
          if [ "${TERMINAL_STATUS}" = "prod_review" ]; then
            echo "Next: a human in the portal clicks 'Approve Production' then 'Mark as Released' for each."
            echo "Audit trail captures both events with reviewer identity per compliance/risk-register.md."
          fi
