Skip to content

scratch: fix C and prove both freshness branches in CI #2

scratch: fix C and prove both freshness branches in CI

scratch: fix C and prove both freshness branches in CI #2

name: scratch — deploy alarm proof
# THROWAWAY. Lives only on `scratch/deploy-alarm-proof`; it is not on the
# branch proposed for merge.
#
# It exercises the real `scripts/check-prod-deploy.mjs` against the real
# GitHub Actions `needs`/`if: always()` semantics, with the ref check
# retargeted from `main` to this scratch branch and the Vercel steps replaced
# by an echo. What it proves:
#
# A. A failing gate skips the deploy job, and the alarm job STILL RUNS
# (`if: always()`), sees `skipped`, and fails red.
# B. A passing gate runs the deploy job, and the alarm job passes green.
# C. The `cancel-in-progress` expression from ci.yml parses and evaluates.
#
# DRY_RUN=1 everywhere: the alarm's decision and the exact `gh` commands are
# printed, but no issue is created in this repo.
on:
push:
branches: ["scratch/deploy-alarm-proof"]
concurrency:
# Copied verbatim from ci.yml to prove the expression is accepted.
group: scratch-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
# ---------- Scenario A: a gate fails, exactly like 2026-08-14 ----------
a-gate:
name: A · gate (deliberately failing test)
runs-on: ubuntu-latest
steps:
- run: |
echo "simulating the flaky test that failed in run 31806388257"
exit 1
a-deploy:
name: A · Deploy → Vercel (production)
needs: [a-gate]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/scratch/deploy-alarm-proof' && github.event_name == 'push'
steps:
- run: echo "would deploy to Vercel here"
a-alarm:
name: A · Production deploy — did it actually happen?
needs: [a-deploy]
runs-on: ubuntu-latest
if: >-
always() &&
github.ref == 'refs/heads/scratch/deploy-alarm-proof' &&
github.event_name == 'push'
permissions:
contents: read
issues: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: echo "needs.a-deploy.result == '${{ needs.a-deploy.result }}'"
- name: Check that production got this commit
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: "1"
DEPLOY_RESULT: ${{ needs.a-deploy.result }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: node ./scripts/check-prod-deploy.mjs --mode=run
# ---------- Scenario B: everything green ----------
b-gate:
name: B · gate (passing test)
runs-on: ubuntu-latest
steps:
- run: echo "green"
b-deploy:
name: B · Deploy → Vercel (production)
needs: [b-gate]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/scratch/deploy-alarm-proof' && github.event_name == 'push'
steps:
- run: echo "would deploy to Vercel here"
b-alarm:
name: B · Production deploy — did it actually happen?
needs: [b-deploy]
runs-on: ubuntu-latest
if: >-
always() &&
github.ref == 'refs/heads/scratch/deploy-alarm-proof' &&
github.event_name == 'push'
permissions:
contents: read
issues: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- run: echo "needs.b-deploy.result == '${{ needs.b-deploy.result }}'"
- name: Check that production got this commit
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: "1"
DEPLOY_RESULT: ${{ needs.b-deploy.result }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: node ./scripts/check-prod-deploy.mjs --mode=run
# ---------- Scenario C: the freshness monitor against the live site ----------
# Unlike the real workflow this checks out the scratch branch (main does not
# have the script yet) and reads main's head over the wire.
c-freshness:
name: C · Production freshness vs the live site (dry run)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- id: head
run: |
git fetch --depth=1 origin main
echo "sha=$(git rev-parse FETCH_HEAD)" >> "$GITHUB_OUTPUT"
echo "iso=$(git log -1 --format=%cI FETCH_HEAD)" >> "$GITHUB_OUTPUT"
- name: Compare production against main
# Expected to ALARM today: /version.json does not exist on production
# until this change merges. That is the point — an unverifiable
# production is reported, not assumed healthy.
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: "1"
SITE_URL: https://pretable.ai
EXPECTED_SHA: ${{ steps.head.outputs.sha }}
HEAD_COMMIT_ISO: ${{ steps.head.outputs.iso }}
GRACE_MINUTES: "25"
run: node ./scripts/check-prod-deploy.mjs --mode=freshness
# ---------- Scenario D: freshness against a stand-in "production" ----------
# Proves both branches of the monitor inside CI by serving a /version.json the
# test controls, since the real site cannot yet serve one.
d-freshness-both-branches:
name: D · Production freshness — current vs stale
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Serve a stand-in production
run: |
mkdir -p /tmp/fake-prod
echo '{"commit":"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef","builtAt":"2026-08-14T00:00:00Z"}' \
> /tmp/fake-prod/version.json
(cd /tmp/fake-prod && python3 -m http.server 8099 > /tmp/fake-prod.log 2>&1 &)
for i in $(seq 1 20); do
curl -sSf -o /dev/null http://127.0.0.1:8099/version.json && break
sleep 1
done
curl -sS http://127.0.0.1:8099/version.json
- name: Production serving main's head — must NOT alarm
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: "1"
SITE_URL: http://127.0.0.1:8099
EXPECTED_SHA: deadbeefdeadbeefdeadbeefdeadbeefdeadbeef
HEAD_COMMIT_ISO: "2026-08-14T00:00:00Z"
GRACE_MINUTES: "25"
run: node ./scripts/check-prod-deploy.mjs --mode=freshness
- name: Production stuck on an older commit — must alarm
env:
GH_TOKEN: ${{ github.token }}
DRY_RUN: "1"
SITE_URL: http://127.0.0.1:8099
EXPECTED_SHA: a9ee55f45313ea9e4561bef11d7b9ebe9d6e9ab9
HEAD_COMMIT_ISO: "2026-08-14T00:00:00Z"
GRACE_MINUTES: "25"
run: |
if node ./scripts/check-prod-deploy.mjs --mode=freshness; then
echo "FAILED: a stale production did not alarm"
exit 1
fi
echo "OK: stale production alarmed (non-zero exit)"