Repository navigation
release gate: one transient GitHub API 5xx while reading the full-battery runs refuses the publish as NOT MEASURED, with no retry (next.5's release run 39230668843 needed a manual re-run and a second release approval) #24741
Description
Filing gate ③: a task the maintainer directed. In the triage seat's session on 2026-10-20, the maintainer was given the options (A: file a card for a bounded retry; B: keep today's behaviour) and answered verbatim 「立卡」. Filed by the triage seat (seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U. ⛔ Not a claim.
Reader who acts: the domain:devx seat, in one PR on scripts/ci/scheduled-full-run.mjs and its self-test. This lane landed the file's recent cards: #24278 (PR #24305) and #23880 (PR #23975, PR #24211).
Dedupe: MCP search_issues, objectstack, closed included: "release gate NOT MEASURED transient GitHub API 502 5xx retry scheduled-full-run readRuns refuses publish". 22 hits.
- The nearest is gates:
check-issue-citationsreads one transient HTTP 500 from the board as PREREQUISITE NOT MET with no retry, so a GitHub blip turns the lint lane red and drops a PR from the merge queue #23361 (closed): the same class oncheck-issue-citations. It was fixed by PR gates: check-issue-citations retries a transient 5xx or connection reset on a board read before refusing #23438 (863213a90e) with a bounded retry ladder. - fix(release): the post-publish npm check races the registry and only ever looks at 1 of 69 packages — it red-flagged a release that had fully succeeded #15321, release.yml: once a version's fixed group is on npm, the push-lane Releases backfill races the publish job's own Releases step — 17.6.0 got 9 × 422 already_exists, 5 duplicate GitHub Releases and two red runs #21313 and release.yml: the push-lane "Backfill GitHub Releases" step runs while the same version's publish job is still creating its Releases, because the #20627 gate reads only npm #21359 (closed) are other
release.ymlraces. - None covers the release gate's run reads.
What happens (origin/main 9b2953d62c)
-
releaseGate(scripts/ci/scheduled-full-run.mjs:914) reads, for each release-gate workflow and each full-battery event,GET /repos/…/actions/workflows/<file>/runs?branch=main&event=<event>&status=completed…throughreadRuns. -
readRunsthrows on any non-OK answer:if (!res.ok) throw new Error(\GET ${url} answered HTTP ${res.status}`)(:904`). -
releaseGatecatches it and refuses:::error::release gate NOT MEASURED -- … Refusing: an unread full battery is not a green one.Then it exitsEXIT_PREREQUISITE_NOT_MET(:929). There is no retry anywhere on this path.
Measured once, on the publish lane. Release run 39230668843 (the next.5 version PR #24477, push on 6b42b6bc8a, 2026-10-19).
- Its
full-batteryjob refused withrelease gate NOT MEASURED -- GET …/actions/workflows/lint.yml/runs?…event=schedule… answered HTTP 502. publishandDocker imagewere skipped.- Every full-battery verdict it would have read was green, so it was one blip, not a red battery.
- The remedy was the maintainer's (Prime Directive 15): "Re-run failed jobs", then a second approval of the
releaseenvironment. next.5 then published. - So one GitHub blip costs a publish its run and the maintainer one more re-run and approval.
toolingenterspm:queuethrough the gate "measured to slow the fleet: repeated re-runs" (triage-duties.md:37), with this run as the named instance.
Wanted
readRunsretries a transient failure with a bounded ladder, and only a transient failure:- an HTTP 5xx, or a named connection failure (
ECONNRESET,UND_ERR_SOCKET); - a 4xx, a body without
workflow_runs, or any other error stays final.
- an HTTP 5xx, or a named connection failure (
- Reuse gates:
check-issue-citationsreads one transient HTTP 500 from the board as PREREQUISITE NOT MET with no retry, so a GitHub blip turns the lint lane red and drops a PR from the merge queue #23361's policy, not a second copy of it. PR gates: check-issue-citations retries a transient 5xx or connection reset on a board read before refusing #23438'sBOARD_READ_RETRY(3 attempts per read, backoff 500 / 1000 ms, a 4-retry budget per run),retryableNetworkCode, and its read wrapper live inscripts/check-issue-citations.mjs(about:749–:800). Import them, or lift them into one shared module that both files import. - Still refuse when the ladder or budget is spent. The message names the last status and how many attempts were made. ⛔ An unread battery is never read as green, so the refusal and its exit code do not change in kind.
- Pins in the file's self-test, with an injectable
fetchandsleep:- a 502 then 200 is measured and the verdict is computed;
- three 5xx answers refuse NOT MEASURED, naming the attempts;
- a 404 refuses at once, with no retry;
- the run budget caps the total retries across workflows and events.
⛔ Not a new gate (「新增门禁默认否」): this makes the existing release gate robust to the network, and changes no verdict. ⛔ No change to release.yml, its environment or its dispatch. No seat re-runs or dispatches release workflows (Prime Directive 15).
18.0 note. This does not hold GA. If GA's release run hits a 5xx before this lands, the remedy stays the maintainer's re-run of the failed job, as it was for next.5.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 20, 2026
Claim: PM loop round 17 · a p1
domain:devxcard from triage (filed on the maintainer's 「立卡」); runs beside #24579 round 9 under the maintainer's 「并发2」Session:
session_01FM29ZqLTrnPNXxijioZwMpAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24741-release-gate-read-retryWorktree:
objectstack-issue-24741Domain:
domain:devxSeat:
domain:devx#1File surface:
scripts/ci/scheduled-full-run.mjs:readRuns,releaseGate's NOT MEASURED message, and the file's--self-testpins. Alsoscripts/check-issue-citations.mjs, but only if #23361's retry policy (BOARD_READ_RETRY,retryableNetworkCode,readBoard,readFailure) is lifted into one shared module both files import. That module may be new, underscripts/. Stop on breach and explain in the report. ⛔ No.github/workflows/**(Tier H governed;release.ymlunchanged, as the card says). ⛔ No new gate, and no verdict changes.Container & model:
S (one read path's bounded retry plus its pins),mode:subagent,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate)Clause-②: no
Responsibility:
one transient GitHub 5xx while the release gate reads the full-battery runs refuses the publish as NOT MEASURED, with no retry|the maintainer re-runs the failed job and approves the release environment a second time, as for next.5's run 39230668843|every release, GA 18.0.0 included, and the maintainer who approves itThread-read: none
Serial constraints cleared: none of the 11 open PRs touches
scripts/ci/scheduled-full-run.mjsorscripts/check-issue-citations.mjs(read at 2026-10-20T03:38Z). No open card claims either file. #24579 round 9 (in flight) edits onlydocs/qa/platform-checklist/areas/*.json.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
os-dev-report
{ "issue": 24741, "status": "done", "branch": "claude/issue-24741-release-gate-read-retry", "pr": "https://github.com/objectstack-ai/objectstack/pull/24748", "session": "session_01FM29ZqLTrnPNXxijioZwMp (subagent: the parent's id, as the claim names it)", "premise_still_valid": true, "summary": "Premise verified on BASE 7e7e605de3. readRuns threw on any non-OK answer and releaseGate refused NOT MEASURED, exit 3, with no retry: BASE's own CLI, fed one 502 by a stub, refused at the first request. Implemented in scripts/ci/scheduled-full-run.mjs only: readRuns reads each page through check-issue-citations.mjs's readBoard. It retries a 5xx, ECONNRESET or UND_ERR_SOCKET (3 attempts, 500/1000 ms). A 4xx, an OK body without workflow_runs, and any other error are final at once. The new readFullBattery shares ONE boardReadBudget (4 retries) across every workflow x event x page of a gate run. The new runReleaseGate returns lines and exit with readHistory/fetchImpl/sleep injectable. The NOT MEASURED line keeps its prefix, its suffix and exit 3, and its text now reads 'GET URL -> HTTP 503 after 3 attempts (...)'. A new self-test battery holds 16 cases; the battery floor goes 6 -> 7. REUSE DECISION (four axes, in the PR body): IMPORT from check-issue-citations.mjs, not lift. The lift was implemented first (42643e8ca9) and measured to shift the line-anchored census row 'scripts/check-issue-citations.mjs:204 local-env' in scripts/pm/dispatch-gates.self-test.mjs:7490 to :205: pnpm check:pm-dispatch-gates went 1 of 1913 red. That file is outside the claim's surface, so per 'stop on breach' I reverted to import (5cad717ed4), and check-issue-citations.mjs is byte-identical to BASE (blob d2a21576f8). Measured import closure: scheduled-full-run.mjs -> check-issue-citations, import-prerequisite, cli-build-prerequisite, invoked-as, git-env, glob-match, js-comment-mask, symbol-anchors, pm/check-half-states. Every one imports node:* only, and the import made 0 child_process and 0 fs calls at load (122 ms). It loads in all three jobs. release.yml full-battery is checkout fetch-depth 0 + setup-node 22, no install: I loaded the module and ran --self-test from a git archive of scripts/ and .github/ with no node_modules above it, exit 0. scheduled-full-run-card.yml 'file' is a bare checkout plus a github-script import(): same closure, same proof. lint.yml lint-ci-engine is checkout + setup-node + pnpm install. Files changed: 1 (scripts/ci/scheduled-full-run.mjs, +305/-26, 2001 -> 2280 lines). Commits: 42643e8ca9 (lift), 6dfe6a73b1 (retry + pins), 5cad717ed4 (import instead of lift). DEVIATIONS: (1) the lift-then-revert above, net diff one file; (2) origin/main moved 4 commits past BASE (to 39e93b9ef9) and was not merged: 0 of them touch this file or its import closure; (3) commit trailers are AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By, and the PR footer is AGENTS.md's session-URL form; (4) the derivation was run both without paths (role file) and with the final path (dispatch): identical 32-command lists.", "tests": "All on HEAD 5cad717ed4 unless marked BASE. (a) node scripts/ci/scheduled-full-run.mjs --self-test: exit 0, 'scheduled-full-run: self-test OK'. The new battery 'release gate: the read path retries a transient answer, inside one budget per run' registers 16 cases, measured by a temp copy pinned to 99 that reported 'registered 16 case(s)'. BASE: exit 0. (b) THE FOUR PINS, RED ON BASE: an offline CLI probe ran 'node scripts/ci/scheduled-full-run.mjs --release-gate --release-commit 7e7e605de3b046999e5714d37c281c5c420cf0a6' in each tree, with a preloaded stub fetch and the real git history. Results 'BASE 0 GREEN, 4 RED' and 'HEAD 4 GREEN, 0 RED'. Pin 1, 502 then 200 on lint.yml/schedule: BASE exit 3, '... page=1 answered HTTP 502. Refusing: ...'; HEAD exit 0, PASS, 7 requests. Pin 2, persistent 503: BASE 1 request to that listing, the text names no attempts; HEAD exit 3, 3 requests to it, '-> HTTP 503 after 3 attempts (every attempt one read is allowed, so not one blip)'. Pin 3, 404 on ci.yml/schedule: BASE exit 3 and 1 request (its 'at once' half holds on BASE), RED on the missing attempt count; HEAD exit 3, 1 request, '-> HTTP 404 after 1 attempt (not retried: only a 5xx or a connection reset is)'. Pin 4, every listing 502 once: BASE refused at request 1; HEAD exit 3, 9 requests, spec-shape-gate.yml/schedule '-> HTTP 502 after 1 attempt (this run's budget of 4 retries is spent)'. The offline end-to-end probe the dispatch asked for is pin 1: HEAD measured and passed (exit 0); BASE refused NOT MEASURED (exit 3). (c) ABLATIONS on committed HEAD 5cad717ed4, via scripts/ablation-replace.mjs (WRAP mode, trap-restored, no build because nothing reads dist/). A1 widens retryable to status 400 and up in check-issue-citations.mjs (anchor x1->x0, blob d2a21576f893 -> b57e86cbb704): --self-test exit 1 at pin 3, 'got exit 3 after 3 request(s), slept 500,1000'. A2 gives readFullBattery a fresh boardReadBudget per readRuns call (blob a5707a8b6bf9 -> a34296145061): --self-test exit 1 at pin 4, 'got exit 0 after 12 requests and 6 backoff(s)'. Both restored with 'blob == HEAD' and an empty git diff HEAD; git status --porcelain empty afterwards. A first round of the same two ablations on the lift tree (6dfe6a73b1) read the same. (d) node scripts/check-issue-citations.mjs --self-test: exit 0, 215 cases, 10 batteries, on HEAD and on BASE. (e) pnpm exec eslint --no-inline-config scripts/ci/scheduled-full-run.mjs: exit 0. (f) grep -naP control-byte scan of the changed file: no hits.", "gates": "LOCAL, on HEAD 5cad717ed4, the final commit; the union ran after it. 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands' with no paths gives 32 commands; with the final path, the identical list. All 32 exit 0, each code captured before any pipe: check-ci-filter-parity; check-closing-keyword-parity and its --self-test; check-comment-mask-corpus; check-declaration-mirrors and its --self-test; check-position-name-fold-loaders and its --self-test; check-scripts-symbol-anchors and its --self-test; check-self-test-wired and its --self-test; check-self-test-workflow-commands and its --self-test; check-whole-set-label-write and its --self-test; scheduled-full-run.mjs --self-test; pm/bare-root-worklist.mjs --self-test; pnpm check:agent-test-spelling, bash32-floor, cli-command-ids, cross-package-test-inputs, driver-memory-census, entry-guard, gitlink-declared, nul-bytes, parse-guard, pm-dispatch-gates (1913 cases pass, run detached and waited on with tail --pid), pnpm-filter-targets, pnpm-flag-spellings, refd-timer-probe, watch-hint-literal. Reconciled with 'dispatch-gates --ran': 32 derived, 32 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. The dispatch's direct commands: scheduled-full-run.mjs --self-test exit 0; --check-concurrency exit 0; check-issue-citations.mjs --self-test exit 0. BASE 7e7e605de3: the same three, each exit 0. The 4 workflow-valued families the derivation lists as NOT MEASURED locally (check-issue-citations --census, shard-attestation --emit, test-completeness, check-half-states --format=markdown) are CI's. check-platform-checklist --changed-from was not run: no exported symbol was renamed or deleted in the net diff. On the lift tree a first local round of 36 derived commands read 35 exit 0 and pm-dispatch-gates exit 1 (the census row), which is what changed the reuse shape. CI, on PR #24748 head 5cad717ed4, read once after creation: 40 check-runs; success 8 (Check Changeset, Check PR Size, Check Documentation Links, filter, and the four claim/closing checks); skipped 11 (Build Core, Console Pin Gate, Temporal Conformance, Dogfood Verify CLI, Build Docs, a second Check Changeset run on the labeled event, Auto Label, Check PR Size, two packed-tarball smokes, the dogfood shard matrix); in_progress 21, among them Lint · CI, release & engine gates (this file's --self-test step), Lint · repo & tooling gates, Lint · PM dispatch-gates, Lint · ESLint, Test Core 1-6/6, Type Check lanes, Governed Surface Queue Guard. Not polled further, per the role file.", "mcp_calls": "0", "api_writes": "2 relay strokes, each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]. Stroke 1, pr_create (run 39368815810, success): POST /repos/objectstack-ai/objectstack/pulls (draft) -> #24748; POST /repos/objectstack-ai/objectstack/issues/24748/assignees [os-warren]; POST /repos/objectstack-ai/objectstack/issues/24748/labels [skip-changeset]. Read back: body 9827 bytes sent, 9827 stored, identical (relay, and a full REST re-read); labels [skip-changeset]; assignees [os-warren]; draft true. Stroke 2: POST /repos/objectstack-ai/objectstack/issues/24741/comments (this report). Plus 4 git pushes (the empty branch probe, then 42643e8ca9, 6dfe6a73b1 and 5cad717ed4), which are not REST.", "open_questions": [], "out_of_scope_findings": [ "carrier: none (承接者:无) · noted in PR #24748 Acceptance notes, not filed. The builtins-only, nothing-at-load constraint on scheduled-full-run.mjs's load closure, which now includes check-issue-citations.mjs and its four helpers, is a comment at the import and is not enforced. lint.yml's lint-ci-engine job installs dependencies, so a non-builtin import added anywhere in that closure would first fail on a release run's install-free full-battery job. Not a defect today (measured: node:* only, 0 load-time side effects). No reach.", "carrier: the PM (a decision, not a finding) · noted, not filed. If the retry policy should later get its own module (the lift), the claim's surface must include scripts/pm/dispatch-gates.self-test.mjs, whose census row 'scripts/check-issue-citations.mjs:204 local-env' is line-anchored on purpose and moves to :205 with any added import line." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Landed ·
domain:devxseat 1 ·session_01FM29ZqLTrnPNXxijioZwMp· 2026-10-20T05:16Zgh-readonly-queue/main/pr-24748-*heads)7a7a43516c(not governed). This seat landed it on its ACCEPT6283551291, after one fix round (45a06c8a5b, a dropped space restored)origin/main, by contentscripts/ci/scheduled-full-run.mjsis byte-identical to the PR head45a06c8a5b(blobde4b65dc66).scripts/check-issue-citations.mjsis unchanged by this PR.readRunsreads throughreadBoard, andreadFullBatteryandrunReleaseGateare exported onmainState change: PR #24748 closed this card through
Fixes #24741(completed).pm:dispatchedcomes off in this act.What the next release run shows. One transient 5xx or connection reset on a full-battery run listing is retried inside the gate: 3 attempts per read, 4 retries per gate run. It no longer refuses the publish. A spent ladder or budget still refuses NOT MEASURED with exit 3, and the line now names the last answer and the attempts.
release.ymlis unchanged.Noted, not filed (the dev's out-of-scope findings):
node:*.release.yml'sfull-batteryjob installs nothing, so a violation would first fail on a release run. A new gate for it is 「新增门禁默认否」, and no consumer pulls for it.scripts/pm/dispatch-gates.self-test.mjsin its claim. That file's census rowscripts/check-issue-citations.mjs:204 local-envis line-anchored.Generated by Claude Code