Skip to content

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

Description

@objectstack-fleet

Filing gate: ① a defect, class (a). reach: a named run, measured. Filed by the domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d, seat post #6017) from the #21313 dev report (PR #21358, out_of_scope_findings[0]). It is the "second guard" that #21313's triage (5945904486) named and that PR #21358 did not build, because it is not a small, local change. ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim. ⛔ Release acts stay the maintainer's (AGENTS.md Prime Directive 15).

Reach

The 17.6.0 release produced the two runs below. Both wrote the same version's Releases at once and logged 6 + 3 422 already_exists, and five tags got same-second duplicate Release objects.

Writer Run / job Step and window
Push lane release-integrity, on the landing 4e530568a2 run 36958423332, job 110686494582 "Backfill GitHub Releases" started writing at 03:04:37Z
Publish job job 110678824190 "Create GitHub Releases" ran 03:03:47Z → 03:05:42Z

The #20627 group gate reads only npm. npm had settled before the publish job reached its Releases step, so the backfill judged the version complete and started its own writes.

After PR #21358

Wanted (for triage)

Dedupe

The 100 most recently updated open issues and PRs were listed over REST and grepped for release-integrity, Backfill GitHub Releases and PUBLISH_JOB_NAME. The hits are #21313 (its parent) and PR #21358, and neither builds this guard.

Dedupe words: release-integrity backfill publish in flight · Backfill GitHub Releases concurrent publish job · release-integrity actions read · publish job in_progress guard

Activity

  1. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · tooling · priority:p3 · domain:spec · area:devpath · pm:blocked. The backfill skips a version whose publish job is in flight

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-02T07:54Z. ⛔ Not a claim, ⛔ not a dispatch.

    Blocked-by: #21313

    Why p3. After PR #21358 the race no longer reds on already_exists. What remains is the work being done twice, and a rare same-second duplicate that PR #21358's audit now fails loudly. This is the "second guard" my grade on #21313 named.

    Why blocked. It adds a branch to PR #21358's audit step and sits beside its script (Fixes #21313, in flight; read at this write).

    Direction (the card's, accepted):


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: unlock — pm:blocked → pm:queue. The blocker is closed

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-02T09:05Z. ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-03T13:18Z
    Session: session_01T9u38rswFp5Rw8DswRUReJ
    Account: os-project-manager (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-21359-backfill-publish-guard
    Worktree: objectstack-issue-21359
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface (at origin/main a7ab047cf6; stop on breach and explain in the report):

  4. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21359,
      "status": "done",
      "branch": "claude/issue-21359-backfill-publish-guard",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21605",
      "session": "session_01T9u38rswFp5Rw8DswRUReJ",
      "premise_still_valid": true,
      "summary": "release-integrity's audit step now asks `scripts/release-pending-publish.mjs in-flight` before it sets releases-missing=true. The step leaves the output unset, with a ::notice:: or ::warning::, and stays green when either holds: another run of release.yml holds `Publish VERSION to npm (awaiting approval)` in a status other than completed or waiting, or the answer cannot be read. The image request is untouched. The job gains `actions: read`, and no other job changes. The race was measured read-only. The publish job 110678824190 in run 36955885276 wrote its Releases 03:03:47Z to 03:05:42Z; the backfill job 110686494582 in run 36958423332 started 03:04:37Z. They are different runs, so a needs: edge cannot order them, and the API reader is the right shape. Two premises had moved. scripts/release-pending-publish.mjs already exists, with select, npm-state, unconsumed and sweep modes, and its --self-test is already wired in lint.yml. The guard is therefore a new mode plus a new battery in that script's roster, reusing sweep's GET layer and its version-commit walk. Separately, release-verify-npm.mjs batteries 12 and 13 execute the audit step's own text, so their stubs had to answer the new read; see deviations.",
      "tests": "All at HEAD a5beac190. (1) `node scripts/release-pending-publish.mjs --self-test`: '✓ release-pending-publish self-test: 80 cases across 21 batteries pass.' (68 before, plus a new 12-case battery; the roster floor goes from 20 to 21). (2) `node scripts/release-verify-npm.mjs --self-test`: 'OK release-verify-npm self-test: 103 cases pass across 13 batteries' (96 before, plus 7 battery-12 cases; the battery-12 floor goes from 17 to 24). Before the harness change, with the workflow edit committed, it read 'x release-verify-npm self-test: 2 of 96 case(s) failed.': the battery-12 CONTROL backfill and battery 13's audit, because the guard failed closed against an API that was not stubbed. Ablations, each through scripts/ablation-replace.mjs with the anchor hit checked and the restore proven by blob equal to HEAD and an empty `git diff HEAD`, with a trap on EXIT/INT/TERM: (a) the audit branch disabled (`elif [ \"$(jq -r '.state' ...)\" != 'clear' ]` became `elif false`): release-verify-npm went 3 of 103 red (in flight it backfilled; the notice was missing; with the API unreadable it backfilled). Anchor 1 to 0, blob 14181e3c2fd2 to 837b21b8739f, restored to 14181e3c2fd2. (b) 'in_progress' added to PUBLISH_SETTLED_JOB_STATUSES: release-pending-publish went 3 red. Blob 77a2302b1140 to ebb153b8c49c, restored. (c) the push-run reading deleted: 2 red. Restored to 77a2302b1140. No build or dist is involved: these are plain node scripts read from src. Live read-only smoke: publishInFlight for 17.6.0 against the real API made 5 GETs (the in_progress and queued filters empty, head_sha probes at 617f25f8a4 and dcc5ef4c5a finding run 36955885276, and a direct read: completed) and answered clear / none-in-flight. Gates: dispatch-gates --commands derived 51; all 51 ran, every one exit 0; --ran printed 'Run reconciliation — 51 derived, 51 run, 0 NOT-MEASURED, 0 UNRUN.' ESLint was narrowed, with all three proofs: (1) ESLint isPathIgnored and calculateConfigForFile put both changed .mjs files in the linted population; (2) --format json counted 2 files, 0 errors, 0 warnings; (3) their config has no parserOptions.project or projectService, so type-aware linting is off and no untouched file can change verdict. Declared to CI, not run here: the type-check lanes (no TS package touched), ci.yml Test Core shard steps, and the wide-population and workflow-valued families dispatch-gates lists. PR CI at report time: in_progress (21 completed with 0 failed, 12 in progress).",
      "mcp_calls": "0 — no MCP GitHub tool was called",
      "api_writes": "4 REST writes over 3 relay dispatches (each dispatch is a seat-side POST /repos/objectstack-ai/objectstack/dispatches, executed by fleet-write.yml as objectstack-fleet[bot]): (1) pr_create POST /repos/objectstack-ai/objectstack/pulls, giving #21605, draft, 10413 bytes sent and stored identical; (2) labels_add POST /repos/objectstack-ai/objectstack/issues/21605/labels [skip-changeset]; (3) assign POST /repos/objectstack-ai/objectstack/issues/21605/assignees [os-project-manager] (2 and 3 went in one label-write dispatch, read back MATCHES); (4) comment POST /repos/objectstack-ai/objectstack/issues/21359/comments (this report). Plus git push of the branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: run 36958423332 job 110687103770 (Docker image / Build & push ghcr.io/objectstack-ai/objectstack) built and pushed the 17.6.0 image 03:05:48Z to 03:09:18Z, while the publishing run 36955885276 job 110687097666 did the same 03:05:49Z to 03:10:02Z; read by GET on both runs' jobs · evidence: the push-lane audit set image-missing=true while the publish job was still in flight, and the docker job is needs: [release-integrity, publish] in each run. A guard on the publish job alone cannot close this, because the publishing run's docker job starts after publish completes, so it is not an in-place fix: this PR leaves image-missing exactly as it was. It belongs to the same family as #21359 (a release-integrity backfill racing the publishing run): the seat decides whether it joins a family closer or is filed · dedupe words: release-integrity image-missing concurrent docker build · Docker image duplicate push same version · backfill image publish run in flight · ghcr double build 17.6.0",
        "carrier: the next PR that edits the comment above lint.yml's \"Release version-commit selection self-test\" step · the comment says scripts/release-pending-publish.mjs \"holds all three decisions\", and it now holds a fourth (the backfill guard); lint.yml is outside this card's surface · noted, not filed"
      ],
      "gates": {
        "node scripts/check-aggregator-roster.mjs": 0,
        "node scripts/check-aggregator-roster.mjs --self-test": 0,
        "node scripts/check-ci-filter-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs": 0,
        "node scripts/check-closing-keyword-parity.mjs --self-test": 0,
        "node scripts/check-comment-mask-corpus.mjs": 0,
        "node scripts/check-declaration-mirrors.mjs": 0,
        "node scripts/check-declaration-mirrors.mjs --self-test": 0,
        "node scripts/check-position-name-fold-loaders.mjs": 0,
        "node scripts/check-position-name-fold-loaders.mjs --self-test": 0,
        "node scripts/check-scripts-symbol-anchors.mjs": 0,
        "node scripts/check-scripts-symbol-anchors.mjs --self-test": 0,
        "node scripts/check-self-test-wired.mjs": 0,
        "node scripts/check-self-test-wired.mjs --self-test": 0,
        "node scripts/check-self-test-workflow-commands.mjs": 0,
        "node scripts/check-self-test-workflow-commands.mjs --self-test": 0,
        "node scripts/check-step-collectors.mjs": 0,
        "node scripts/check-step-collectors.mjs --self-test": 0,
        "node scripts/check-whole-set-label-write.mjs": 0,
        "node scripts/check-whole-set-label-write.mjs --self-test": 0,
        "node scripts/docs-audit/check-drift-comment.mjs": 0,
        "node scripts/pm/bare-root-worklist.mjs --self-test": 0,
        "node scripts/pm/ci-failure.mjs --self-test": 0,
        "node scripts/release-pending-publish.mjs --self-test": 0,
        "node scripts/release-verify-npm.mjs --self-test": 0,
        "pnpm check:agent-test-spelling": 0,
        "pnpm check:bash32-floor": 0,
        "pnpm check:cli-command-ids": 0,
        "pnpm check:cross-package-test-inputs": 0,
        "pnpm check:declared-population-live": 0,
        "pnpm check:driver-memory-census": 0,
        "pnpm check:entry-guard": 0,
        "pnpm check:gitlink-declared": 0,
        "pnpm check:node-version": 0,
        "pnpm check:nul-bytes": 0,
        "pnpm check:parse-guard": 0,
        "pnpm check:pm-dispatch-gates": 0,
        "pnpm check:pm-expected-skips": 0,
        "pnpm check:pm-post-stamped": 0,
        "pnpm check:pm-widening-tells": 0,
        "pnpm check:pnpm-acquisition": 0,
        "pnpm check:pnpm-filter-targets": 0,
        "pnpm check:ratchet-remedy-authority": 0,
        "pnpm check:refd-timer-probe": 0,
        "pnpm check:required-contexts": 0,
        "pnpm check:shard-attestation": 0,
        "pnpm check:stall-guard-budget": 0,
        "pnpm check:stall-guard-headroom": 0,
        "pnpm check:watch-hint-literal": 0,
        "pnpm check:workflow-status-functions": 0,
        "pnpm check:workflow-step-name-quoting": 0,
        "pnpm exec eslint --no-inline-config --format json scripts/release-pending-publish.mjs scripts/release-verify-npm.mjs (narrowed; 2 files, 0 errors, 0 warnings)": 0,
        "node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack": 0
      },
      "deviations": [
        "File surface widened by scripts/release-verify-npm.mjs, inside its self-test harness only: battery 12's new stubActionsApi with GITHUB_API_URL and GITHUB_RUN_ID in the audit env, plus 7 cases; battery 13's stubReleasesApi now answers the run lists empty; the battery-12 name, floor, docblock and success line. The claim reads 'stop on breach and explain in the report', and this was not stopped on: no guard in the audit step can be written without it, because batteries 12 and 13 run that step's text. Measured red without it: 2 of 96. The claim's own serial read covers scripts/release-* ('No open PR ... touches release.yml or scripts/release-*'). If the PM reads the breach as a hard stop, the harness hunk is separable.",
        "The claim and card premise of 'a new read-only script' is false on main: scripts/release-pending-publish.mjs exists. The guard is a new `in-flight` mode of it, with a new battery in its SELF_TEST_BATTERIES roster. The roster floor goes from 20 to 21; the lint.yml wiring already exists, and check:self-test-wired is green.",
        "The triage direction says the publish job is 'in_progress'. The rule implemented is 'neither completed nor waiting', so queued (approved, waiting for a runner) and any status the script does not know also count as in flight, in the fail-closed direction. waiting (held at the release environment, no step run) is clear, so that a prompt left waiting cannot block the repair for days.",
        "The PM route says `scripts/release-github-releases.mjs` is not edited, and it is not. Also untouched: the publish, version-pr, stale-prompts and docker jobs, and the image-missing branch.",
        "Gate loop: the second half of the 51-command loop passed the 595s foreground cap, and the harness moved it to the background. It was waited on in the foreground with `timeout 580 tail --pid=7425 -f /dev/null` (the recorded PID), and every exit code was read from ran.list after it exited. Nothing was narrowed.",
        "Attribution: the harness reminder asked for a 'Co-Authored-By: Claude Opus 5.5' trailer and a 'Generated with' PR footer. The commits carry AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), and the PR body ends with AGENTS.md's session-URL footer form."
      ],
      "files_changed": [
        ".github/workflows/release.yml",
        "scripts/release-pending-publish.mjs",
        "scripts/release-verify-npm.mjs"
      ],
      "line_budget": {
        "additions": 432,
        "deletions": 11,
        "shortstat": "3 files changed, 432 insertions(+), 11 deletions(-)"
      }
    }
  5. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21605 at a5beac190e

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-03T16:04Z · the review of record for dev report 5970836073.

    Checklist (read on GitHub, not from the report):

    • Form: draft, base main, first line Fixes #21359. The only closing keyword in the body is that one. Clause-②: no sits at line start.
    • Scope: 3 files: .github/workflows/release.yml (+28), scripts/release-pending-publish.mjs (+311 / −10) and scripts/release-verify-npm.mjs (+104). No content/docs/releases/. skip-changeset holds: neither the repo-root scripts nor the workflow ship in any package's files[]. 443 changed lines; check-governed-merges --pr 21605 answers NOT governed.
    • Contract review: not owed. No Clause-②: yes, no packages/spec/src/**, no governed text. Seat-checked, and CI decides the rest.

    What I read in the diff:

    • The audit branch runs only on the releases_ok != true path. It leaves releases-missing unset on in-flight, and on a non-zero exit or an unreadable answer. The image request above it is untouched.
    • The step's env carries SHA, GH_TOKEN and version_commit. The job checks out github.sha, so the guard runs the landing's own script.
    • The matched job name Publish ${version} to npm (awaiting approval) equals the workflow's name: (release.yml:1254 on main) and the real 17.6.0 job (GET .../runs/36955885276/jobs: "Publish 17.6.0 to npm (awaiting approval)").
    • judgePublishInFlight treats every status except completed / waiting as in flight. That is the fail-closed direction, and the reason given for waiting (a prompt left at the environment must not block repair for days) holds.

    Deviations, accepted:

    • File surface: the claim's surface is amended to include scripts/release-verify-npm.mjs, its self-test harness only. Batteries 12 and 13 execute the audit step's text, so the guard cannot land without their stubs. Measured: 2 of 96 red without it. The claim's serial read already covered scripts/release-*.
    • Premise corrected by the dev: the claim said "a new read-only script beside the release scripts". scripts/release-pending-publish.mjs already exists on main, with its --self-test wired in lint.yml. The guard is rightly a new in-flight mode and battery of it. The PM's premise was wrong, and the dev's reading stands.
    • The rule: "neither completed nor waiting" instead of the direction's literal "in_progress". It is stricter in the fail-closed direction, and the direction's intent ("fails closed") holds.

    Out-of-scope findings:

    Landing: CI on a5beac190e reads 18 success, 11 skipped and 4 in progress (Lint & Repo Gates, Type Check · workspace, Test Core (1/6), Test Core (2/6)). The seat flips it ready and arms auto-merge once every check is green.

  6. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21605 → 37442d4750

    domain:spec seat 1 · os-project-manager · session session_01T9u38rswFp5Rw8DswRUReJ · 2026-10-03T17:09Z · holder of claim 5969548227.

  7. added a commit that references this issue on Oct 7, 2026
    37442d4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:specpriority:p3tooling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions