carlos / platform Public

e2e: one top-level test per boundary so the gate's shard deal can spread the recovery matrix; the e2e default is four shards

TestEveryBoundaryRecovers ran the whole boundary × fault × second-crash matrix as one test, 60% of the e2e suite's time (1492 s under load on 2026-10-23), and the gate deals tests across shards by name, so that shard alone could outlast CI's 30-minute job whatever E2E_SHARDS was. Each boundary now has its own TestBoundaryRecovers<Name>; TestEveryBoundaryRecovers keeps the plan's name, checks every boundary has its test (and no test names a boundary that is gone), and keeps the ordinary-progression check. The gate's E2E_SHARDS default rises from 2 to 4: the witnesses mostly wait on ticks.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 7fbeecbcf45b55d539f0d7881d4e36dd33fa3b82 parent 6d90689
2 files changed, +93 −19
  • .amadan/test-gate.sh +6 −3
  • internal/e2e/selfserve/job_boundary_test.go +87 −16

CI failed — run details

CI log
=== gate ===
.amadan/test-gate.sh

amadan: step gate failed: signal: terminated

amadan: job failed at step gate — its output is above, not at the tail of this log
diff --git a/.amadan/test-gate.sh b/.amadan/test-gate.sh
index 9e408df39..328a2ee84 100755
--- a/.amadan/test-gate.sh
+++ b/.amadan/test-gate.sh
@@ -235,7 +235,10 @@ wait
# each witness runs two boxes and a console in one process, and when
# they ran beside the console shards the runner killed the gate (five
# runs on 2026-10-23: three bare 30-minute timeouts, two terminations).
-# Alone they take under ten minutes across two shards. Discovery first,
+# They are dealt across four shards (E2E_SHARDS): the witnesses mostly wait
+# on ticks, so the shards cost little CPU, and no single test may dominate a
+# shard (the boundary matrix is one top-level test per boundary for that
+# reason). Discovery first,
# as for the split packages: a witness package that lists no test fails
# the gate rather than passing with nothing run.
if ! discover go test -count=1 -tags e2e -list '.*' ./internal/e2e/selfserve >"$out/list-e2e.raw" 2>&1; then
@@ -252,10 +255,10 @@ else
# whose pattern matches nothing fails the gate.
grep -E '^Test' "$out/list-e2e.raw" | sort -u >"$out/list-e2e" || true
listed=$(wc -l <"$out/list-e2e")
- awk -v n="${E2E_SHARDS:-2}" '{ print (NR - 1) % n "\t" $0 }' "$out/list-e2e" >"$out/deal-e2e"
+ awk -v n="${E2E_SHARDS:-4}" '{ print (NR - 1) % n "\t" $0 }' "$out/list-e2e" >"$out/deal-e2e"
i=0
launched=0
- while [ "$i" -lt "${E2E_SHARDS:-2}" ]; do
+ while [ "$i" -lt "${E2E_SHARDS:-4}" ]; do
pattern=$(awk -v i="$i" -F '\t' '$1 == i { printf "%s%s", sep, $2; sep = "|" }' "$out/deal-e2e")
if [ -n "$pattern" ]; then
start "e2e@$i" go test -count=1 -timeout 30m -tags e2e -run "^($pattern)\$" ./internal/e2e/selfserve
diff --git a/internal/e2e/selfserve/job_boundary_test.go b/internal/e2e/selfserve/job_boundary_test.go
index 5e328fe43..0b80114af 100644
--- a/internal/e2e/selfserve/job_boundary_test.go
+++ b/internal/e2e/selfserve/job_boundary_test.go
@@ -117,32 +117,103 @@ var jobFaults = []jobFault{
{name: "commit after the context was cancelled", point: "written", put: putHeld},
}
-// TestEveryBoundaryRecovers is Review Focus 1 for the job's writes before
-// the barrier (queue, staging, restage, precheck, estimate, the maintenance
-// round, the queued abort, the abort, its finish, the cut's restage): at
-// every boundary a crash before the request, with it persisted, after the
-// write, before the confirmation, before the bump and before notified_seq,
-// a lost reply, and a store that commits the write after the client's
-// context was cancelled; each crash recovered by a console that reopens the
-// same SQLite file, with and without a second crash during the recovery.
-// The row reconciles forward and never regresses, the request in flight
-// keeps its persisted identity (no second request of its kind is issued
-// while it is unconfirmed), the late commit is confirmed without a second
-// write, and the serial is bumped for the landed write.
-func TestEveryBoundaryRecovers(t *testing.T) {
+// The witnesses below are Review Focus 1 for the job's writes before the
+// barrier (queue, staging, restage, precheck, estimate, the maintenance
+// round, the queued abort, the abort, its finish, the cut's restage, and the
+// cut's own writes): at every boundary a crash before the request, with it
+// persisted, after the write, before the confirmation, before the bump and
+// before notified_seq, a lost reply, and a store that commits the write
+// after the client's context was cancelled; each crash recovered by a
+// console that reopens the same SQLite file, with and without a second
+// crash during the recovery. The row reconciles forward and never
+// regresses, the request in flight keeps its persisted identity (no second
+// request of its kind is issued while it is unconfirmed), the late commit
+// is confirmed without a second write, and the serial is bumped for the
+// landed write.
+//
+// One top-level test per boundary, not one test with a boundary loop: the
+// gate deals the package's tests across shards by name, and the whole
+// matrix in one test was 60% of the suite's time in one shard (2026-10-23,
+// 1492 s under load), past CI's 30-minute job. TestEveryBoundaryRecovers
+// keeps the plan's name and pins the matrix complete: every boundary has
+// its test, and the ordinary progression bumps the serial once per landed
+// write.
+func boundaryRecovers(t *testing.T, name string) {
for _, b := range jobBoundaries {
+ if b.name != name {
+ continue
+ }
for _, f := range jobFaults {
for _, second := range []bool{false, true} {
if second && f.point == "" {
continue
}
- name := b.name + "/" + f.name
+ n := f.name
if second {
- name += "/second crash"
+ n += "/second crash"
}
- t.Run(name, func(t *testing.T) { everyBoundary(t, b, f, second) })
+ t.Run(n, func(t *testing.T) { everyBoundary(t, b, f, second) })
}
}
+ return
+ }
+ t.Fatalf("no boundary named %q in jobBoundaries", name)
+}
+
+func TestBoundaryRecoversQueue(t *testing.T) { boundaryRecovers(t, "queue") }
+func TestBoundaryRecoversStaging(t *testing.T) { boundaryRecovers(t, "staging") }
+func TestBoundaryRecoversRestage(t *testing.T) { boundaryRecovers(t, "restage") }
+func TestBoundaryRecoversPrecheck(t *testing.T) { boundaryRecovers(t, "precheck") }
+func TestBoundaryRecoversEstimate(t *testing.T) { boundaryRecovers(t, "estimate") }
+func TestBoundaryRecoversMaintain(t *testing.T) { boundaryRecovers(t, "maintain") }
+func TestBoundaryRecoversAbortQueued(t *testing.T) { boundaryRecovers(t, "abort-queued") }
+func TestBoundaryRecoversAbort(t *testing.T) { boundaryRecovers(t, "abort") }
+func TestBoundaryRecoversFinish(t *testing.T) { boundaryRecovers(t, "finish") }
+func TestBoundaryRecoversCutRestage(t *testing.T) { boundaryRecovers(t, "cut-restage") }
+func TestBoundaryRecoversStart(t *testing.T) { boundaryRecovers(t, "start") }
+func TestBoundaryRecoversSeal(t *testing.T) { boundaryRecovers(t, "seal") }
+func TestBoundaryRecoversFlip(t *testing.T) { boundaryRecovers(t, "flip") }
+func TestBoundaryRecoversOpen(t *testing.T) { boundaryRecovers(t, "open") }
+func TestBoundaryRecoversFinishGate1(t *testing.T) { boundaryRecovers(t, "finish-gate1") }
+func TestBoundaryRecoversFinishDone(t *testing.T) { boundaryRecovers(t, "finish-done") }
+func TestBoundaryRecoversAbortAfterStart(t *testing.T) { boundaryRecovers(t, "abort-after-start") }
+
+// boundaryTests names the top-level test of every boundary; a boundary
+// added to jobBoundaries without one fails TestEveryBoundaryRecovers.
+var boundaryTests = map[string]func(*testing.T){
+ "queue": TestBoundaryRecoversQueue,
+ "staging": TestBoundaryRecoversStaging,
+ "restage": TestBoundaryRecoversRestage,
+ "precheck": TestBoundaryRecoversPrecheck,
+ "estimate": TestBoundaryRecoversEstimate,
+ "maintain": TestBoundaryRecoversMaintain,
+ "abort-queued": TestBoundaryRecoversAbortQueued,
+ "abort": TestBoundaryRecoversAbort,
+ "finish": TestBoundaryRecoversFinish,
+ "cut-restage": TestBoundaryRecoversCutRestage,
+ "start": TestBoundaryRecoversStart,
+ "seal": TestBoundaryRecoversSeal,
+ "flip": TestBoundaryRecoversFlip,
+ "open": TestBoundaryRecoversOpen,
+ "finish-gate1": TestBoundaryRecoversFinishGate1,
+ "finish-done": TestBoundaryRecoversFinishDone,
+ "abort-after-start": TestBoundaryRecoversAbortAfterStart,
+}
+
+func TestEveryBoundaryRecovers(t *testing.T) {
+ for _, b := range jobBoundaries {
+ if boundaryTests[b.name] == nil {
+ t.Errorf("boundary %q has no top-level TestBoundaryRecovers test", b.name)
+ }
+ }
+ for name := range boundaryTests {
+ found := false
+ for _, b := range jobBoundaries {
+ found = found || b.name == name
+ }
+ if !found {
+ t.Errorf("boundaryTests names %q, which is not in jobBoundaries", name)
+ }
}
t.Run("ordinary progression bumps once per landed write", func(t *testing.T) {
w := newJobWorld(t)