Add an exact-SHA baseline receipt gate

d9ffc65f5cdd · Christopher David · · parent 3064e5237061

Add an exact-SHA baseline receipt gate

Require a clean commit, run precommit, merged coverage, and the disposable release smoke without retries, then atomically record only bounded results under .git. Track the remaining clean-run proof in Gate 0.

Deploy story

What this commit did to the running system — joined from the forge receipt chain, the part a commit page elsewhere cannot show.

Not deployed through the forge lane

No push, promotion, build, or deploy receipt references this commit (receipts are scanned over a bounded recent window). Changes shipped by full node replacement carry their proof in the release gate receipt instead.

Changed files

  • modified docs/2026-08-20-integration-hardening-and-staging-readiness-recommendations.md
  • added ops/ci/baseline.sh

Diff

2 files changed, +124 -2

docs/2026-08-20-integration-hardening-and-staging-readiness-recommendations.md modified +6 -2

@@ -111,9 +111,13 @@ Completed on 2026-08-20:

111 111
- Fixed `mix assets.deploy` to compile Phoenix's colocated assets before
112 112
  Tailwind resolves them. The release smoke exposed this production-only build
113 113
  failure and now passes against a fresh PostgreSQL 18 container.
114
- Added `ops/ci/baseline.sh` to require a clean worktree, run precommit, merged
115
  coverage, and the production release smoke without retries, recheck Git
116
  identity and cleanliness, and atomically write a content-free receipt under
117
  `.git/openagents/gate-receipts/`.
114 118
115
Gate 0 still requires an exact-SHA gate receipt that runs and records these
116
completed stages from one clean commit.
119
Gate 0 still requires one clean-commit execution of `ops/ci/baseline.sh` and
120
inspection of its exact-SHA receipt.
117 121
118 122
Do not use the current green suite as evidence for untested code. The updated
119 123
coverage audit records strong Issues and Projects coverage and the defects it
ops/ci/baseline.sh added +118

@@ -0,0 +1,118 @@

1
#!/bin/sh
2
set -eu
3
4
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
5
repo_root=$(CDPATH= cd -- "$script_dir/../.." && pwd)
6
run_root=$(mktemp -d /tmp/openagents-baseline.XXXXXX)
7
8
cleanup() {
9
  rm -rf -- "$run_root"
10
}
11
12
trap cleanup EXIT INT TERM
13
14
if [ ! -d "$repo_root/.git" ]; then
15
  echo "baseline gate must run from a Git worktree" >&2
16
  exit 1
17
fi
18
19
cd "$repo_root"
20
21
if [ -n "$(git status --porcelain --untracked-files=all)" ]; then
22
  echo "baseline gate requires a clean worktree" >&2
23
  exit 1
24
fi
25
26
git_sha=$(git rev-parse --verify HEAD)
27
started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
28
started_epoch=$(date +%s)
29
30
run_stage() {
31
  stage_name=$1
32
  shift
33
  stage_log="$run_root/$stage_name.log"
34
  stage_started=$(date +%s)
35
36
  echo "Running $stage_name"
37
38
  set +e
39
  "$@" >"$stage_log" 2>&1
40
  stage_status=$?
41
  set -e
42
43
  cat "$stage_log"
44
45
  if [ "$stage_status" -ne 0 ]; then
46
    echo "$stage_name failed" >&2
47
    exit "$stage_status"
48
  fi
49
50
  stage_finished=$(date +%s)
51
  eval "${stage_name}_duration_seconds=$((stage_finished - stage_started))"
52
}
53
54
run_stage precommit env MIX_ENV=test mix precommit
55
run_stage coverage "$repo_root/ops/ci/coverage.sh"
56
run_stage release_smoke "$repo_root/ops/ci/release-smoke.sh"
57
58
if [ "$(git rev-parse --verify HEAD)" != "$git_sha" ]; then
59
  echo "Git HEAD changed while the baseline gate was running" >&2
60
  exit 1
61
fi
62
63
if [ -n "$(git status --porcelain --untracked-files=all)" ]; then
64
  echo "baseline gate left the worktree dirty" >&2
65
  exit 1
66
fi
67
68
javascript_tests=$(awk '/^ℹ tests [0-9]+$/ {value=$3} END {print value}' "$run_root/precommit.log")
69
default_tests=$(awk '/^[0-9]+ tests, 0 failures/ {value=$1} END {print value}' "$run_root/precommit.log")
70
excluded_tests=$(awk '/^[0-9]+ tests, 0 failures \([0-9]+ excluded\)$/ {value=$5} END {gsub(/[()]/, "", value); print value}' "$run_root/precommit.log")
71
cluster_tests=$(awk '/^[0-9]+ tests, 0 failures \([0-9]+ excluded\)$/ {value=$1} END {print value}' "$run_root/coverage.log")
72
coverage_percent=$(awk -F '|' '/Total/ {value=$2} END {gsub(/[% ]/, "", value); print value}' "$run_root/coverage.log")
73
74
for parsed_value in "$javascript_tests" "$default_tests" "$excluded_tests" "$cluster_tests" "$coverage_percent"; do
75
  if [ -z "$parsed_value" ]; then
76
    echo "baseline gate could not parse a required content-free result" >&2
77
    exit 1
78
  fi
79
done
80
81
completed_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)
82
completed_epoch=$(date +%s)
83
total_duration_seconds=$((completed_epoch - started_epoch))
84
receipt_dir="$repo_root/.git/openagents/gate-receipts"
85
receipt_path="$receipt_dir/$git_sha.json"
86
receipt_temp="$receipt_path.tmp.$$"
87
88
mkdir -p "$receipt_dir"
89
umask 077
90
91
cat >"$receipt_temp" <<EOF
92
{
93
  "schema": "openagents.baseline-gate.v1",
94
  "git_sha": "$git_sha",
95
  "status": "passed",
96
  "started_at": "$started_at",
97
  "completed_at": "$completed_at",
98
  "total_duration_seconds": $total_duration_seconds,
99
  "automatic_retries": 0,
100
  "stages": {
101
    "precommit": {"status": "passed", "duration_seconds": $precommit_duration_seconds},
102
    "coverage": {"status": "passed", "duration_seconds": $coverage_duration_seconds},
103
    "release_smoke": {"status": "passed", "duration_seconds": $release_smoke_duration_seconds}
104
  },
105
  "tests": {
106
    "javascript": $javascript_tests,
107
    "default": $default_tests,
108
    "cluster": $cluster_tests,
109
    "excluded_from_default": $excluded_tests
110
  },
111
  "coverage_percent": $coverage_percent
112
}
113
EOF
114
115
mv "$receipt_temp" "$receipt_path"
116
117
echo "Baseline gate passed for $git_sha"
118
echo "Receipt: .git/openagents/gate-receipts/$git_sha.json"

This page updates live while a promote is in flight · changelog