Skip to content

fix(awards): audit findings — multi-pick, empty ballots, stale prefil… #2597

fix(awards): audit findings — multi-pick, empty ballots, stale prefil…

fix(awards): audit findings — multi-pick, empty ballots, stale prefil… #2597

Workflow file for this run

name: Contract gate
# PR-time contract-as-code enforcement (the 2026-07-08 schema-drift lesson):
# 1. specs/openapi.json (committed snapshot) must match the spec the route
# serves — a spec edit without the regenerated snapshot fails.
# 2. api-client/src/schema.ts is GENERATED from the snapshot — mirror-drift
# is impossible; stale types fail.
# 3. A snapshot change without a changelog entry fails — contract changes
# must be announced (downstream drift detectors classify from
# /api/changelog).
# The daily live checks (api-drift.yml field coverage) remain the backstop
# for data/deploy drift PR-time can't see.
#
# The lessons step exits 1 BY DESIGN while any lesson still says `Guard: none`
# — that red is the signal (a lesson that never became a check), not the lane
# breaking (read by scripts/check-workflow-health.ts):
# workflow-health: signal-steps: ^Every lesson names its guard$
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
contract:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
# Full history: the changelog-coupling step diffs against the PR
# base's merge-base, which a shallow clone can't resolve (exit 128).
fetch-depth: 0
- uses: pnpm/action-setup@v4
with: { version: 10 }
- uses: actions/setup-node@v4
with: { node-version: "24", cache: "pnpm" }
- run: pnpm install --frozen-lockfile
- name: Contract artifacts fresh (snapshot + generated client types)
run: pnpm contract:check
- name: Writer conformance (read-back, pipefail, exit-stomps, probe coverage)
run: pnpm exec tsx scripts/check-writer-conformance.ts
# A lesson only counts when it became a check. Every improvements/lessons
# file names its guard (test / guard script / workflow) or says `none`.
# Reported, not blocking, while `none` lines remain — declared as a
# signal step in the header so the red reads as "unguarded lessons",
# never as a broken lane. Flip continue-on-error to false the day every
# lesson is guarded; from then on a new unguarded lesson fails the PR.
- name: Every lesson names its guard
continue-on-error: true
run: pnpm exec tsx scripts/check-lessons-guarded.ts
- name: Routing surface stays terse (sls-051 — descriptions ≤600 chars, no docs-question capture)
run: pnpm exec tsx scripts/eval/routing-surface-check.ts
# Every served value must be datable, or say it is not. Raven #1134: a
# DeepWiki answer sat beside three dates that all described the code scan
# and none dated the answer. Ratcheted at 40 — new ones fail.
- name: Served values are datable
run: pnpm exec tsx scripts/check-answer-dating.ts
- name: Route files export only handlers + config (2026-07-21 — non-handler exports break next build, tsc/vitest miss it)
run: pnpm exec tsx scripts/eval/route-exports-check.ts
- name: Changelog⇄contract assertion (sls-054)
# The newest changelog entry may not name response fields absent from
# the published schema — the changelog is a consumption contract.
run: pnpm exec tsx scripts/eval/changelog-contract-check.ts
- name: Spec change requires a changelog entry
if: github.event_name == 'pull_request'
run: |
git fetch origin "${{ github.base_ref }}" --depth=50
CHANGED=$(git diff --name-only "origin/${{ github.base_ref }}...HEAD")
if echo "$CHANGED" | grep -qx "specs/openapi.json"; then
if ! echo "$CHANGED" | grep -qx "src/lib/changelog.ts"; then
echo "::error::specs/openapi.json changed without a changelog entry. Contract changes must be announced — add an entry to src/lib/changelog.ts in this PR."
exit 1
fi
echo "spec changed + changelog updated — OK"
else
echo "no contract change in this PR"
fi