Repository navigation
gates: check-issue-citations retries a transient 5xx or connection reset on a board read before refusing - #23438
Conversation
…re refusing A 5xx, an ECONNRESET or an UND_ERR_SOCKET from a board read now gets a bounded ladder (3 attempts per read, 500/1000 ms backoff, 4 retries per run) before the gate concludes PREREQUISITE NOT MET. A 4xx and every other thrown error are answers and are not retried; a read still failing after its ladder keeps its exit-3 path, and the refusal names the attempt count. Claude-Session: https://claude.ai/code/session_01B7mrh59UAGffd7s9asUfoY Co-authored-by: Claude <noreply@anthropic.com>
A new `retry` battery (42 cases, floor raised to 10 batteries): one transient 500 then a 200 resolves on the head probe and on a per-number probe; persistent 500s are refused after exactly the ladder and exit 3 end to end; a 404 is asked once and stays a finding; every other 4xx and every unnamed thrown error is not retried; the per-run budget caps a flapping board at 3000 ms of backoff; no scripted failure at any read yields a board. Claude-Session: https://claude.ai/code/session_01B7mrh59UAGffd7s9asUfoY Co-authored-by: Claude <noreply@anthropic.com>
…en it refuses Every resolve case settles its promise, so a regression reds that case by name rather than escaping as an uncaught throw; the per-read refusal reads "every attempt one read is allowed, so not one blip". Claude-Session: https://claude.ai/code/session_01B7mrh59UAGffd7s9asUfoY Co-authored-by: Claude <noreply@anthropic.com>
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 38538817058 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Fixes #23361
Clause-②: no
What this changes
scripts/check-issue-citations.mjsread ONE HTTP 500 from the board as PREREQUISITE NOT MET (exit 3) with no retry, so a GitHub blip turned the lint lane red and ejected a PR from the merge queue (run 38496246256). Every board read now goes through one helper,readBoard, shared by both strategies (probeBoard's head probe and per-number probes,enumerateBoard's pages), with ONE retry budget per run created inbuildBoard.ECONNRESETorUND_ERR_SOCKET(RETRYABLE_NETWORK_CODES).ECONNREFUSED,ENOTFOUND, a proxy refusing the tunnel, a body that is not JSON). Those are rethrown unchanged, so they keep the exit path they had.run()'s existing catch turns it intoprerequisiteRefusal, exit 3, never 0. The refusal now names the read, the answer and the attempt count, for exampleGET issues?per_page=1 -> HTTP 500 after 3 attempts (every attempt one read is allowed, so not one blip).res.json()then throwsTypeError: terminatedwith the same cause codes.run()andbuildBoardtake optionalfetchImpl/sleep(defaults: the globalfetch, a realsetTimeoutsleep). Only the self-test passes them.makeTransferProbedeliberately does not share the retry. Its 3xx is a verdict and stays one. A failed read there never exits 3: it degrades to anullverdict, which classifies asallocated-but-absent, still a finding. It is only reached under--probe-cause, which no workflow runs.dispatch-gates: local-env, pinned by line number indispatch-gates.self-test.mjs) are untouched. All edits sit below it.Retry policy and worst-case added wall time
BOARD_READ_RETRY: 3 attempts per read (one read plus two retries), backoff 500 ms then 1000 ms, and 4 retries per run shared by every read the run makes.--enumerate) spends its ladder and the board build ends, so the cost does not depend on how many numbers the change cites. The self-test pins it with 400 cited numbers (the mostprobeBoardtakes before switching to enumeration) and with the enumerating strategy: 3 reads and 1500 ms in both.500, 1000, 500, 1000(3000 ms total), then... after 1 attempt (this run's budget of 4 retries is spent).Which connection-failure shapes are retried, as measured
Measured on node v22.22.0 (CI runs node 22) against a local socket server, both directly and through a local CONNECT proxy under
--use-env-proxy(the gate's own re-exec route):fetchthrowsTypeError: fetch failed, cause codeECONNRESETfetch failed, cause codeUND_ERR_SOCKET("other side closed")TypeError: terminatedfrom the body read, causeECONNRESETterminated, causeUND_ERR_SOCKETfetch failed, causeECONNRESET/UND_ERR_SOCKETfetch failed, cause chain ending inUND_ERR_ABORTEDECONNREFUSEDENOTFOUNDVerification (head
3becc14a)node scripts/check-issue-citations.mjs --self-test, exit 0,(215 cases, 10 batteries). It was 173 cases and 9 batteries. The newretrybattery has 42 cases (floor 42), andSELF_TEST_BATTERY_FLOORgoes from 9 to 10.fetchImplis a stub, and it judges a real temp-repo diff. A 404 is asked once, never retried, and is still a finding.c732ce2e, read from a checkout at4f81f315): median 5418 ms. After: median 5722 ms. The +0.3 s is the offline child process and its temp repo. Backoff sleeps are injected, so the new cases add no sleep.scripts/ablation-replace.mjsin WRAP mode, at HEAD3becc14a. Each restore was proven by blob == HEAD (d2a21576) and an emptygit diff HEAD. Every leg went red by name:attemptsPerRead: 1(no retry): red at "one transient 500 on the HEAD probe, then a 200, RESOLVES ... it refused instead: GET issues?per_page=1 -> HTTP 500 after 1 attempt".run()'s board-read catch returns 0 instead of refusing: red at "persistent 500s EXIT 3 end to end, never 0 — got exit 0".500, 1000six times.--base 4f81f315~15: 60 citations across 33 files,probed (16 citations), frontier #23434, exit 0. This isreadBoardover real responses.GET issues?per_page=1 -> ECONNRESET after 3 attempts (...).fetch failedas before.node scripts/check-issue-citations.mjs, exit 0:no issue citations added against 4f81f3152 (0 file(s) read). The diff is all underscripts/**, a deferred surface.3becc14a:dispatch-gates --commands --repo objectstack-ai/objectstackderived 32 commands. All 32 ran and all exited 0.--ranwith exit codes reconciles: "32 derived famil(ies) accounted for — 32 run, 0 NOT-MEASURED (a DERIVED zero)".pnpm check:pm-dispatch-gates(fast tier, 1841 cases), whose census pinscripts/check-issue-citations.mjs:204 local-envand live local-env rows for this script both pass.eslint --no-inline-config --format json scripts/check-issue-citations.mjsgave 1 file, 0 errors, 0 warnings.eslint.config.mjs's**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}object covers the file.parserOptionsareecmaVersionandsourceTypeonly, with noprojectand noprojectService. Type-aware linting is off, so this diff cannot move another file's verdict.pnpm lintis CI's run.--censusamong them), 11 wide-population families, 55 artifact-roster families, the path-scheduledTest Corejob and the type-check lanes. No package is touched.skip-changeset, because rootscripts/publishes nothing.Acceptance notes
--probe-cause, a transfer probe that reads a 5xx yields anullverdict, whose detail text says "re-run with --probe-cause" even though the run already passed it. That is report wording, in a mode no workflow runs; it never passes a board. Noted, not filed. Carrier: none.fetch failed, which does not name the read. It is kept byte-identical so those exit paths are unchanged.scripts/check-shard-attestation.mjscarries its owngithubFetchJson(one retry on 5xx, 429 or any network error). It was not reused: this ruling excludes 429 and unnamed network errors.Generated by Claude Code