ci: keep the route and backstop policy reads inline (git and jq only); docs follow

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-31 18:40:33 +00:00
1 parent ff14f14c63
commit 48a9d92a2f
6 files changed
+29 -11

No files matched your search

+11 -2
View File
@@ -45,11 +45,20 @@ jobs:
with:
fetch-depth: 0
# Read with git and jq only (history-read.sh is git alone): without a train block nothing below runs.
# Read with git and jq only: without a train block nothing below runs.
- name: Read the train policy (main)
id: policy
run: |
policy=$(bash .github/scripts/history-read.sh "$GITHUB_SHA" .gstack/ship-policy.json --absent null)
ref="$GITHUB_SHA" file=.gstack/ship-policy.json policy=null
read_failed() { echo "::error::cannot read ${file} at ${ref}. $1 ($(git --version)). Fix: see docs/troubleshooting.md#history-read-failed (HISTORY_READ_FAILED)" >&2; exit 1; }
listed=$(git ls-tree --full-tree "$ref" -- "$file") || read_failed "git ls-tree failed"
if [ -n "$listed" ]; then
for try in 1 2 3; do
policy=$(git show "${ref}:${file}") && break
if [ "$try" = 3 ]; then read_failed "git show failed 3 times"; fi
sleep $((try * 2))
done
fi
train=$(jq -c 'if (.train | type) == "object" then .train else null end' <<<"$policy") || { echo "::error::${GITHUB_SHA}:.gstack/ship-policy.json is not valid JSON; fix the policy file" >&2; exit 1; }
if [ -z "$train" ] || [ "$train" = null ]; then
echo "train=false" >> "$GITHUB_OUTPUT"
+11 -2
View File
@@ -186,7 +186,7 @@ jobs:
with:
bun-version: 1.4.2
# Read with git and jq only (history-read.sh is git alone): no repository code decides whether PR pushes start paid work.
# Read with git and jq only: no repository code decides whether PR pushes start paid work.
- name: Read the train policy (base revision)
id: policy
env:
@@ -194,7 +194,16 @@ jobs:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
base="${BASE_SHA:-origin/${DEFAULT_BRANCH}}"
policy=$(bash .github/scripts/history-read.sh "$base" .gstack/ship-policy.json --absent null)
ref="$base" file=.gstack/ship-policy.json policy=null
read_failed() { echo "::error::cannot read ${file} at ${ref}. $1 ($(git --version)). Fix: see docs/troubleshooting.md#history-read-failed (HISTORY_READ_FAILED)" >&2; exit 1; }
listed=$(git ls-tree --full-tree "$ref" -- "$file") || read_failed "git ls-tree failed"
if [ -n "$listed" ]; then
for try in 1 2 3; do
policy=$(git show "${ref}:${file}") && break
if [ "$try" = 3 ]; then read_failed "git show failed 3 times"; fi
sleep $((try * 2))
done
fi
train=$(jq -c 'if (.train | type) == "object" then .train else null end' <<<"$policy") || { echo "::error::${base}:.gstack/ship-policy.json is not valid JSON; fix the policy file" >&2; exit 1; }
artifact=$(jq -r '.record_artifact // ""' <<<"${train:-null}")
check=$(jq -r '.full_gate_check // ""' <<<"${train:-null}")
+1 -1
View File
@@ -2,5 +2,5 @@
### Changed
- **Blobless checkouts** (`filter: blob:none`) in `free-tests.yml` (free-plan), `version-gate.yml` and `evals.yml` (route, screen-summary, plan-slices, eval-slices, eval-rerun, train-bisect, slices-report). The release, CSO, periodic, marathon, sweep and recorder workflows and the main backstop keep full clones. `test/ci-history-read.test.ts` names each with its reason and fails on any other full-history checkout without the filter, printing the file and line to fix.
- **History reads fail loudly.** The train-policy reads in the route and backstop jobs and the Version Gate's base `VERSION` read go through `.github/scripts/history-read.sh`. The Version Gate's train classification goes through its TypeScript twin, `lib/history-read.ts`. Each one checks existence with `git ls-tree`, reads with `git show` (3 tries in a partial clone) and fails with `HISTORY_READ_FAILED` naming the ref, the path and the git version (docs/troubleshooting.md#history-read-failed). A file that is really absent keeps its old meaning. A ship policy that is not valid JSON now fails the route and backstop jobs instead of reading as "no train". The Version Gate goes red when `git fetch origin <base>` fails 3 times instead of carrying on.
- **History reads fail loudly.** The train-policy reads in the route and backstop jobs stay inline (git and jq only, so no repository code decides whether a PR push starts paid work). The Version Gate's base `VERSION` read goes through `.github/scripts/history-read.sh` from the gate's merge-commit checkout, and its train classification through the TypeScript twin, `lib/history-read.ts`, which also lists the merge base's changed files without rename detection so that diff needs no file contents. Each one checks existence with `git ls-tree`, reads with `git show` (3 tries in a partial clone) and fails with `HISTORY_READ_FAILED` naming the ref, the path and the git version (docs/troubleshooting.md#history-read-failed). A file that is really absent keeps its old meaning. A ship policy that is not valid JSON now fails the route and backstop jobs instead of reading as "no train". The Version Gate goes red when `git fetch origin <base>` fails 3 times instead of carrying on.
- **The eval image asserts partial clone support.** `.github/docker/Dockerfile.ci` makes a blobless clone and fetches a missing blob at build time, so an image whose git cannot do that fails to build.
+5 -4
View File
@@ -2829,10 +2829,11 @@ tree is there, but an older file's contents are fetched from GitHub the first
time something reads them. When that fetch fails three times (a GitHub outage,
a network drop), or the ref does not resolve at all, the read fails here
instead of counting as "the file is absent". A file the commit really does not
have still reads as absent: no ship policy at the base means no train, no
VERSION means `0.0.0.0`, as before. `.github/scripts/history-read.sh` (the
workflow steps) and `lib/history-read.ts` (the Version Gate's train
classification) print this line. The Version Gate prints the same code when
have still reads as absent, as before. No ship policy at the base means no
train, and no VERSION means `0.0.0.0`. The route and backstop policy reads (inline
in their workflows), `.github/scripts/history-read.sh` (the Version Gate's base
`VERSION`) and `lib/history-read.ts` (the Version Gate's train classification)
print this line. The Version Gate prints the same code when
`git fetch origin <base>` fails three times.
**Fix.** Rerun the failed job once GitHub is reachable. If it fails the same
+1
View File
@@ -186,6 +186,7 @@ describe.skipIf(!Bun.which('jq') || !Bun.which('bash'))('the workflow steps that
for (const { file, job, name, env } of policySteps) {
test(`${file} ${job}: an unfetchable policy fails, a malformed one fails, an absent one routes as before`, () => {
const script = stepOf(file, job, name).run!;
expect(script, 'the policy read runs no repository code: git and jq only').not.toMatch(/\.github\/scripts|\bbun\b/);
expect(script).not.toContain('|| true');
expect(script).not.toContain('2>/dev/null');
-2
View File
@@ -379,8 +379,6 @@ describe.skipIf(!Bun.which('jq') || !Bun.which('bash'))('the route job reads the
fs.writeFileSync(path.join(dir, 'README.md'), 'x\n');
git('add', '-A');
git('commit', '-q', '-m', 'base');
fs.mkdirSync(path.join(dir, '.github/scripts'), { recursive: true });
fs.copyFileSync(path.join(ROOT, '.github/scripts/history-read.sh'), path.join(dir, '.github/scripts/history-read.sh'));
return { dir, base: git('rev-parse', 'HEAD').stdout.trim() };
}
function run(script: string, dir: string, env: Record<string, string>): Record<string, string> {