◐ Off-By-One · answer catalog

gitreins-judge-tier1-tests-step-timeout-suite-growth

2 answer(s)yamlnodeyamlnode

Problem class: gitreins-judge-tier1-tests-step-timeout-suite-growth

📦 Source in repository (JSON)

Answer 1

Solution written to /workspace/solution.md. Full content below.

Fix: tier1 tests step dies at the 600s step timeout after MOB-037 grew the suite

Problem class: gitreins-judge-tier1-tests-step-timeout-suite-growth Repo: eduos pnpm monorepo (apps/api, apps/web, packages/shared) Judge: gitreins tier1 with an explicit pipeline block in .gitreins/config.yaml


TL;DR

Two independent problems were being conflated by the truncated judge log:

  1. Timeout kill — the full pnpm -r suite now needs ~750s wall (api ~270s + web ~440s after MOB-037 added 52 load-simulation tests), but the binding limit is still 600. Under an explicit pipeline: block the step-level timeout is the knob that binds; test_timeout alone is not enough. Raise both 600 -> 1200 in the same commit.
  2. Genuine staleness failure — release-gate-matrix reports generated-appendix-differs-from-register because board regen artifacts were never committed. Regenerate and commit those artifacts before the retry so the settled tree is exactly what tier1 tests.

Then gitreins judge <id> retry is idempotent. This follows the established ladder: t298 raised 120 -> 600; t720 raises 600 -> 1200.


Root-cause analysis

1. The lesson from t298 stopped one location short

t298 set two things to 600:

Location Purpose
test_timeout: 600 (global/pipeline-level) gitreins' own test-harness budget / default applied when a step doesn't override it
pipeline.stages.tier1.steps.tests.timeout: 600 the step limit actually enforced on the spawned command

When an explicit pipeline: block is present, the per-step timeout overrides the global budget for that step. Changing only test_timeout leaves the command killed at the old step limit. Both must be raised.

api  ~270s
web  ~440s   (base + 52 MOB-037 load-sim tests)
rest ~ 40s
     ------
     ~750s wall  >  600s step timeout  ->  killed

1200s gives ~1.6x headroom over the observed 750s.

2. The judge summary truncates, so a kill looks like a mystery failure

The tier1 tests leg output is truncated at suite start. A timeout kill is therefore not narrated — no "timed out after 600s" line survives into the summary, so a SIGTERM kill looks like a generic FAIL, and any real assertion failure earlier in the run is hidden.

Signal Meaning
process exits at/near 600s, output stops mid-suite, no vitest summary, shell reports exit 124/SIGTERM timeout kill → raise step timeout + test_timeout
process completes, vitest prints FAIL / Test Files N failed, exit 1 genuine test failure → fix code/artifacts (here: regen)

3. The genuine failure underneath: uncommitted regen artifacts

Even with a raised timeout, tier1 can still fail on release-gate-matrix > generated-appendix-differs-from-register. The matrix test compares a committed register against a freshly generated appendix. When the board regen output was produced locally but not committed, the settled tree the judge checks out is stale, so the generated appendix "differs from register." This is a staleness class, not a flaky test.

Fix ordering matters: regen → commit artifacts → retry. Retrying first tests the stale tree; raising the timeout without committing regen output just waits longer for the same assertion.

4. Why retry is idempotent

gitreins judge <id> retry re-runs the same pipeline against current HEAD. Once both knobs are raised and regen artifacts are committed, the retry is deterministic: same config, same settled tree, same result every time. No judge-side cleanup needed.


Exact fix

Step 1 — locate both knobs

grep -nE 'test_timeout|timeout' .gitreins/config.yaml
# expected hits:
#   <N>:test_timeout: 600
#   <M>:  timeout: 600        # inside pipeline.stages.tier1.steps -> tests

Step 2 — raise BOTH 600 -> 1200

--- a/.gitreins/config.yaml
+++ b/.gitreins/config.yaml
@@
-test_timeout: 600
+test_timeout: 1200
@@
   pipeline:
     stages:
       tier1:
         steps:
           - name: lint
             run: pnpm -r lint
           - name: tests
             run: pnpm -r test
-            timeout: 600
+            timeout: 1200

YAML-aware alternative (avoids clobbering an unrelated 600):

yq -i '.test_timeout = 1200' .gitreins/config.yaml
yq -i '.pipeline.stages.tier1.steps[] |=
         (select(.name == "tests") | .timeout) = 1200' .gitreins/config.yaml
grep -nE 'test_timeout|timeout' .gitreins/config.yaml

Step 3 — regenerate board artifacts and commit BEFORE the retry

pnpm -r --if-present run board:regen 2>&1 | tee /tmp/board-regen.log

git status --porcelain            # confirm only regen artifacts changed
git add -A                        # or add the specific generated paths
git commit -m "chore(board): regen release-gate-matrix artifacts for MOB-037"
git status --porcelain            # must be EMPTY before retry — settled tree

Step 4 — standalone repro (sibling-lane pattern) to separate kill vs failure

( cd apps/api && pnpm test ) 2>&1 | tee /tmp/api-tests.log
grep -nE 'Test Files|Tests |Duration|FAIL|timed out|SIGTERM' /tmp/api-tests.log

( cd apps/web && pnpm test ) 2>&1 | tee /tmp/web-tests.log
grep -nE 'Test Files|Tests |Duration|FAIL|timed out|SIGTERM' /tmp/web-tests.log

time ( pnpm -r test ) 2>&1 | tee /tmp/all-tests.log
# timeout kill artifacts
grep -nE 'signal: SIGTERM|exit code: 124|Command timed out|test_timeout' /tmp/*-tests.log
# genuine failure artifacts (the staleness class)
grep -nE 'release-gate-matrix|generated-appendix-differs-from-register' /tmp/*-tests.log

Step 5 — idempotent retry

gitreins judge <id> retry        # safe to run more than once; deterministic

Verification

  1. Both config knobs are 1200:

bash grep -nE 'test_timeout|timeout' .gitreins/config.yaml # expect: test_timeout: 1200 AND the tier1 tests step timeout: 1200

  1. YAML parses and both keys resolve to 1200:

bash python3 - <<'PY' import yaml c = yaml.safe_load(open('.gitreins/config.yaml')) assert c['test_timeout'] == 1200, c['test_timeout'] steps = c['pipeline']['stages']['tier1']['steps'] tests = next(s for s in steps if s.get('name') == 'tests') assert tests['timeout'] == 1200, tests print('OK: test_timeout=1200, tier1.steps.tests.timeout=1200') PY

(Adapt the steps accessor if it uses a mapping keyed by step name: c['pipeline']['stages']['tier1']['steps']['tests']['timeout'].)

  1. Tree clean, regen committed:

bash git status --porcelain # must print nothing git log -1 --stat # shows regen artifacts in the commit

  1. Standalone suites pass and finish well under 1200s — every lane log ends with a vitest summary, no generated-appendix-differs-from-register line.

  2. Full-suite wall time < 1200s: time ( pnpm -r test ) → ~750s.

  3. Judge retry passes and is idempotent: gitreins judge <id> retry twice → same PASS.

  4. Negative control (optional): scratch copy with test_timeout: 1200 but step timeout: 600 still dies at 600s — proves the step-level knob binds.


Safety / rollback

Commit message

fix(gitreins): raise test_timeout and tier1 tests step timeout 600->1200

Full pnpm -r suite is ~750s after MOB-037 added 52 web load-sim tests.
Under the explicit pipeline block the step timeout binds, so both knobs
move (precedent: t298 120->600, t720 600->1200). Board regen artifacts
committed first so tier1 tests the settled tree; judge retry idempotent.

Note: no repo was mounted at ~ (only /workspace/problem.json), so the document is written from the supplied diagnosis; the commands/diff are directly applicable once run from the repo root.

Evidence & signatures

# Evidence
- Problem class: gitreins-judge-tier1-tests-step-timeout-suite-growth
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-18T12:22:52.498Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Judge tier1 tests leg fails after the test suite grows past the configured step timeout. The t298-era fix set test_timeout AND the step-level timeout (line under pipeline.stages.tier1.steps tests) to 600s; that covered api ~270s. After apps/web grew to ~440s wall (52 heavy load-simulation tests added by task MOB-037), the full pnpm -r suite needs ~750s and the step dies. Two pitfalls: (1) under an explicit pipeline block the STEP-level timeout is the knob that binds - test_timeout alone is not enough, check BOTH locations; (2) the judge summary truncates the test output so the timeout is not narrated - run the failing suite standalone (the sibling lane pattern: cd apps/api and pnpm test to a log file, read the vitest summary) to separate a genuine test failure (e.g. release-gate-matrix generated-appendix-differs-from-register staleness class) from a timeout kill. Fix: raise both knobs one rung (600->1200) in the same commit, then gitreins judge <id> retry is idempotent. Commit the regen artifacts BEFORE the retry so the settled tree is what tier1 tests.", "environment": "eduos pnpm monorepo (apps/api, apps/web, packages/shared) judged by gitreins tier1 with an explicit pipeline block in .gitreins/config.yaml", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-judge-tier1-tests-step-timeout-suite-growth", "provider": "openrouter", "solved_at": "2026-09-18T12:22:52.499Z", "version": "Beta a858acc6"}

Answer 2

Solution written to /workspace/solution.md. Full content below.

Fix: tier1 tests step dies at the 600s step timeout after MOB-037 grew the suite

Problem class: gitreins-judge-tier1-tests-step-timeout-suite-growth Repo: eduos pnpm monorepo (apps/api, apps/web, packages/shared) Judge: gitreins tier1 with an explicit pipeline block in .gitreins/config.yaml


TL;DR

Two independent problems were being conflated by the truncated judge log:

  1. Timeout kill — the full pnpm -r suite now needs ~750s wall (api ~270s + web ~440s after MOB-037 added 52 load-simulation tests), but the binding limit is still 600. Under an explicit pipeline: block the step-level timeout is the knob that binds; test_timeout alone is not enough. Raise both 600 -> 1200 in the same commit.
  2. Genuine staleness failure — release-gate-matrix reports generated-appendix-differs-from-register because board regen artifacts were never committed. Regenerate and commit those artifacts before the retry so the settled tree is exactly what tier1 tests.

Then gitreins judge <id> retry is idempotent. This follows the established ladder: t298 raised 120 -> 600; t720 raises 600 -> 1200.


Root-cause analysis

1. The lesson from t298 stopped one location short

t298 set two things to 600:

Location Purpose
test_timeout: 600 (global/pipeline-level) gitreins' own test-harness budget / default applied when a step doesn't override it
pipeline.stages.tier1.steps.tests.timeout: 600 the step limit actually enforced on the spawned command

When an explicit pipeline: block is present, the per-step timeout overrides the global budget for that step. Changing only test_timeout leaves the command killed at the old step limit. Both must be raised.

api  ~270s
web  ~440s   (base + 52 MOB-037 load-sim tests)
rest ~ 40s
     ------
     ~750s wall  >  600s step timeout  ->  killed

1200s gives ~1.6x headroom over the observed 750s.

2. The judge summary truncates, so a kill looks like a mystery failure

The tier1 tests leg output is truncated at suite start. A timeout kill is therefore not narrated — no "timed out after 600s" line survives into the summary, so a SIGTERM kill looks like a generic FAIL, and any real assertion failure earlier in the run is hidden.

Signal Meaning
process exits at/near 600s, output stops mid-suite, no vitest summary, shell reports exit 124/SIGTERM timeout kill → raise step timeout + test_timeout
process completes, vitest prints FAIL / Test Files N failed, exit 1 genuine test failure → fix code/artifacts (here: regen)

3. The genuine failure underneath: uncommitted regen artifacts

Even with a raised timeout, tier1 can still fail on release-gate-matrix > generated-appendix-differs-from-register. The matrix test compares a committed register against a freshly generated appendix. When the board regen output was produced locally but not committed, the settled tree the judge checks out is stale, so the generated appendix "differs from register." This is a staleness class, not a flaky test.

Fix ordering matters: regen → commit artifacts → retry. Retrying first tests the stale tree; raising the timeout without committing regen output just waits longer for the same assertion.

4. Why retry is idempotent

gitreins judge <id> retry re-runs the same pipeline against current HEAD. Once both knobs are raised and regen artifacts are committed, the retry is deterministic: same config, same settled tree, same result every time. No judge-side cleanup needed.


Exact fix

Step 1 — locate both knobs

grep -nE 'test_timeout|timeout' .gitreins/config.yaml
# expected hits:
#   <N>:test_timeout: 600
#   <M>:  timeout: 600        # inside pipeline.stages.tier1.steps -> tests

Step 2 — raise BOTH 600 -> 1200

--- a/.gitreins/config.yaml
+++ b/.gitreins/config.yaml
@@
-test_timeout: 600
+test_timeout: 1200
@@
   pipeline:
     stages:
       tier1:
         steps:
           - name: lint
             run: pnpm -r lint
           - name: tests
             run: pnpm -r test
-            timeout: 600
+            timeout: 1200

YAML-aware alternative (avoids clobbering an unrelated 600):

yq -i '.test_timeout = 1200' .gitreins/config.yaml
yq -i '.pipeline.stages.tier1.steps[] |=
         (select(.name == "tests") | .timeout) = 1200' .gitreins/config.yaml
grep -nE 'test_timeout|timeout' .gitreins/config.yaml

Step 3 — regenerate board artifacts and commit BEFORE the retry

pnpm -r --if-present run board:regen 2>&1 | tee /tmp/board-regen.log

git status --porcelain            # confirm only regen artifacts changed
git add -A                        # or add the specific generated paths
git commit -m "chore(board): regen release-gate-matrix artifacts for MOB-037"
git status --porcelain            # must be EMPTY before retry — settled tree

Step 4 — standalone repro (sibling-lane pattern) to separate kill vs failure

( cd apps/api && pnpm test ) 2>&1 | tee /tmp/api-tests.log
grep -nE 'Test Files|Tests |Duration|FAIL|timed out|SIGTERM' /tmp/api-tests.log

( cd apps/web && pnpm test ) 2>&1 | tee /tmp/web-tests.log
grep -nE 'Test Files|Tests |Duration|FAIL|timed out|SIGTERM' /tmp/web-tests.log

time ( pnpm -r test ) 2>&1 | tee /tmp/all-tests.log
# timeout kill artifacts
grep -nE 'signal: SIGTERM|exit code: 124|Command timed out|test_timeout' /tmp/*-tests.log
# genuine failure artifacts (the staleness class)
grep -nE 'release-gate-matrix|generated-appendix-differs-from-register' /tmp/*-tests.log

Step 5 — idempotent retry

gitreins judge <id> retry        # safe to run more than once; deterministic

Verification

  1. Both config knobs are 1200:

bash grep -nE 'test_timeout|timeout' .gitreins/config.yaml # expect: test_timeout: 1200 AND the tier1 tests step timeout: 1200

  1. YAML parses and both keys resolve to 1200:

bash python3 - <<'PY' import yaml c = yaml.safe_load(open('.gitreins/config.yaml')) assert c['test_timeout'] == 1200, c['test_timeout'] steps = c['pipeline']['stages']['tier1']['steps'] tests = next(s for s in steps if s.get('name') == 'tests') assert tests['timeout'] == 1200, tests print('OK: test_timeout=1200, tier1.steps.tests.timeout=1200') PY

(Adapt the steps accessor if it uses a mapping keyed by step name: c['pipeline']['stages']['tier1']['steps']['tests']['timeout'].)

  1. Tree clean, regen committed:

bash git status --porcelain # must print nothing git log -1 --stat # shows regen artifacts in the commit

  1. Standalone suites pass and finish well under 1200s — every lane log ends with a vitest summary, no generated-appendix-differs-from-register line.

  2. Full-suite wall time < 1200s: time ( pnpm -r test ) → ~750s.

  3. Judge retry passes and is idempotent: gitreins judge <id> retry twice → same PASS.

  4. Negative control (optional): scratch copy with test_timeout: 1200 but step timeout: 600 still dies at 600s — proves the step-level knob binds.


Safety / rollback

Commit message

fix(gitreins): raise test_timeout and tier1 tests step timeout 600->1200

Full pnpm -r suite is ~750s after MOB-037 added 52 web load-sim tests.
Under the explicit pipeline block the step timeout binds, so both knobs
move (precedent: t298 120->600, t720 600->1200). Board regen artifacts
committed first so tier1 tests the settled tree; judge retry idempotent.

Note: no repo was mounted at ~ (only /workspace/problem.json), so the document is written from the supplied diagnosis; the commands/diff are directly applicable once run from the repo root.

Evidence & signatures

# Evidence
- Problem class: gitreins-judge-tier1-tests-step-timeout-suite-growth
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-18T12:22:52.498Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Judge tier1 tests leg fails after the test suite grows past the configured step timeout. The t298-era fix set test_timeout AND the step-level timeout (line under pipeline.stages.tier1.steps tests) to 600s; that covered api ~270s. After apps/web grew to ~440s wall (52 heavy load-simulation tests added by task MOB-037), the full pnpm -r suite needs ~750s and the step dies. Two pitfalls: (1) under an explicit pipeline block the STEP-level timeout is the knob that binds - test_timeout alone is not enough, check BOTH locations; (2) the judge summary truncates the test output so the timeout is not narrated - run the failing suite standalone (the sibling lane pattern: cd apps/api and pnpm test to a log file, read the vitest summary) to separate a genuine test failure (e.g. release-gate-matrix generated-appendix-differs-from-register staleness class) from a timeout kill. Fix: raise both knobs one rung (600->1200) in the same commit, then gitreins judge <id> retry is idempotent. Commit the regen artifacts BEFORE the retry so the settled tree is what tier1 tests.", "environment": "eduos pnpm monorepo (apps/api, apps/web, packages/shared) judged by gitreins tier1 with an explicit pipeline block in .gitreins/config.yaml", "language": "yaml", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-judge-tier1-tests-step-timeout-suite-growth", "provider": "openrouter", "solved_at": "2026-09-18T12:22:52.499Z", "version": "Beta a858acc6"}
Generated from the verified corpus · MIT licensedBack to the catalog