Skip to content

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

Closed

Description

@objectstack-fleet

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.

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… through readRuns.
  • readRuns throws on any non-OK answer: if (!res.ok) throw new Error(\GET ${url} answered HTTP ${res.status}`) (:904`).
  • releaseGate catches it and refuses: ::error::release gate NOT MEASURED -- … Refusing: an unread full battery is not a green one. Then it exits EXIT_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-battery job refused with release gate NOT MEASURED -- GET …/actions/workflows/lint.yml/runs?…event=schedule… answered HTTP 502.
  • publish and Docker image were 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 release environment. next.5 then published.
  • So one GitHub blip costs a publish its run and the maintainer one more re-run and approval. tooling enters pm:queue through the gate "measured to slow the fleet: repeated re-runs" (triage-duties.md:37), with this run as the named instance.

Wanted

⛔ 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

  1. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 17 · a p1 domain:devx card from triage (filed on the maintainer's 「立卡」); runs beside #24579 round 9 under the maintainer's 「并发2」
    Session: session_01FM29ZqLTrnPNXxijioZwMp
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-24741-release-gate-read-retry
    Worktree: objectstack-issue-24741
    Domain: domain:devx
    Seat: domain:devx#1
    File surface: scripts/ci/scheduled-full-run.mjs: readRuns, releaseGate's NOT MEASURED message, and the file's --self-test pins. Also scripts/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, under scripts/. Stop on breach and explain in the report. ⛔ No .github/workflows/** (Tier H governed; release.yml unchanged, 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 it
    Thread-read: none
    Serial constraints cleared: none of the 11 open PRs touches scripts/ci/scheduled-full-run.mjs or scripts/check-issue-citations.mjs (read at 2026-10-20T03:38Z). No open card claims either file. #24579 round 9 (in flight) edits only docs/qa/platform-checklist/areas/*.json.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:devx seat 1 · session_01FM29ZqLTrnPNXxijioZwMp · 2026-10-20T05:16Z

    reading result
    merge queue (gh-readonly-queue/main/pr-24748-* heads) none left: the entry has drained
    PR object PR #24748 merged at 2026-10-20T05:15:37Z as 7a7a43516c (not governed). This seat landed it on its ACCEPT 6283551291, after one fix round (45a06c8a5b, a dropped space restored)
    origin/main, by content scripts/ci/scheduled-full-run.mjs is byte-identical to the PR head 45a06c8a5b (blob de4b65dc66). scripts/check-issue-citations.mjs is unchanged by this PR. readRuns reads through readBoard, and readFullBattery and runReleaseGate are exported on main

    State change: PR #24748 closed this card through Fixes #24741 (completed). pm:dispatched comes 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.yml is unchanged.

    Noted, not filed (the dev's out-of-scope findings):

    • The constraint that the gate's load closure is builtins-only, with nothing run at load, is a comment at the import and is not enforced. It holds today: every module in the closure imports only node:*. release.yml's full-battery job installs nothing, so a violation would first fail on a release run. A new gate for it is 「新增门禁默认否」, and no consumer pulls for it.
    • A later lift of the retry policy into its own module must put scripts/pm/dispatch-gates.self-test.mjs in its claim. That file's census row scripts/check-issue-citations.mjs:204 local-env is line-anchored.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:devxpriority:p1High: required for production / M2target:v18tooling

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions