Dashboard › craft › Session 1BlOrjKSHFnf
1BlOrjKSHFnf58tXjWe have a failure when publishing to GCS: https://github.com/getsentry/publish/actions/runs/28084819405/job/83148110750
I suspect it is due to https://github.com/nodejs/node/issues/63989
Can you form a plan to fix this?
Make sure to follow all the references. My preferences is to upgrade to a patched Node version rather than workarounds.
No plan file exists yet. You should create your plan at /home/byk/Code/getsentry/craft/.opencode/plans/1782290100699-sunny-mountain.md using the write tool. You should build your plan incrementally by writing to or editing this file. NOTE that this is the only file you are allowed to edit - other than this you are only allowed to take READ-ONLY actions.
Goal: Gain a comprehensive understanding of the user's request by reading through code and asking them questions. Critical: In this phase you should only use the explore subagent type.
Focus on understanding the user's request and the code associated with their request
Launch up to 3 explore agents IN PARALLEL (single message, multiple tool calls) to efficiently explore the codebase.
I'm looking at the GitHub Actions failure related to GCS publishing and the suspected Node.js issue. The user wants to fix this by upgrading to a patched Node version rather than implementing workarounds. I need to examine the GitHub Actions logs and the Node.js issue #63989 to understand what's happening.
I'll investigate the references and the codebase to form a plan. Let me start by fetching the linked resources.
http: node-fetch throws ERR\_STREAM\_PREMATURE\_CLOSE on keep-alive socket closures after latest security releases · Issue #63989 · nodejs/node · GitHub
[Skip to content](#start-of-content)
## Navigation Menu
Toggle navigation
[](/)
[Sign in](/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fissues%2F63989)
Appearance settings
- Platform
- AI CODE CREATION
- [
GitHub CopilotWrite better code with AI
](https://github.com/features/copilot)
- [
GitHub Copilot appDirect agents from issue to merge
](https://github.com/features/ai/github-app)
- [
MCP RegistryNewIntegrate external tools
](https://github.com/mcp)
- DEVELOPER WORKFLOWS
- [
...
I'm tracing through the issue references to understand the bug timeline. The problem was introduced in Node 24.17.0 and fixed by PR #64004, so I need to find which version contains that fix. I'm also checking the GitHub Actions logs and the craft codebase to see what Node version is currently pinned.
Let me follow the references — the fix PR #64004, the related issues, and check the failure log and the craft codebase's Node version.
http: avoid stream listeners on idle agent sockets by mcollina · Pull Request #64004 · nodejs/node · GitHub
[Skip to content](#start-of-content)
## Navigation Menu
Toggle navigation
[](/)
[Sign in](/login?return_to=https%3A%2F%2Fgithub.com%2Fnodejs%2Fnode%2Fpull%2F64004)
Appearance settings
- Platform
- AI CODE CREATION
- [
GitHub CopilotWrite better code with AI
](https://github.com/features/copilot)
- [
GitHub Copilot appDirect agents from issue to merge
](https://github.com/features/ai/github-app)
- [
MCP RegistryNewIntegrate external tools
](https://github.com/mcp)
- DEVELOPER WORKFLOWS
- [
ActionsAutomate any workflow
...
Publish a new version Publish using Craft 2026-06-24T08:15:05.2156311Z ##[group]Run docker://getsentry/craft:latest Publish a new version Publish using Craft 2026-06-24T08:15:05.2157249Z with: Publish a new version Publish using Craft 2026-06-24T08:15:05.2157742Z entrypoint: /bin/bash Publish a new version Publish using Craft 2026-06-24T08:15:05.2158444Z args: -e -c " export HOME=/root && cd __repo__/. && exec craft publish 0.15.2 " Publish a new version Publish using Craft 2026-06-24T08:15:05.2159160Z env: Publish a new version Publish using Craft 2026-06-24T08:15:05.2159822Z SENTRY_DSN: https://303a687befb64dc2b40ce4c96de507c5@o1.ingest.sentry.io/6183838 Publish a new version Publish using Craft 2026-06-24T08:15:05.2160617Z XDG_STATE_HOME: /github/workspace/.craft-state Publish a new version Publish using Craft 2026-06-24T08:15:05.2161225Z CRAFT_MERGE_TARGET: Publish a new version Publish using Craft 2026-06-24T08:15:05.2161742Z CRAFT_LOG_LEVEL: Info Publish a new ...
So the root cause is clear: the craft Docker image is running Node 22.23.0, which has the buggy security patch for CVE-2026-48931 that triggers ERR_STREAM_PREMATURE_CLOSE — the exact issue from nodejs/node#63989. The fix exists in PR #64004, which has been backported to both v22.x and v24.x and released in v22.23.1. I need to track down where the Node version is pinned in the Docker image and update it to 22.23.1, since the Dockerfile appears to be using a floating base image rather than the v22.12.0 specified in Volta.
Now I have the key data point: the publish run is using gl-node/22.23.0 — the exact buggy Node release. Let me examine how craft pins its Node version (Dockerfile, package.json/Volta, etc.).
<path>/home/byk/Code/getsentry/craft/Dockerfile</path>
<type>file</type>
<content>
1: FROM node:22-bookworm-slim AS builder
2:
3: WORKDIR /usr/local/lib
4:
5: # Install pnpm (version from Volta config in package.json)
6: COPY package.json ./
7: RUN npm install -g pnpm@$(node -p "require('./package.json').volta.pnpm")
8:
9: COPY package.json pnpm-lock.yaml ./
10: RUN pnpm install --frozen-lockfile --quiet
11:
12: COPY . .
13:
14: RUN \
15: NODE_ENV=production \
16: NODE_PATH=/usr/local/lib/node_modules \
17: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin" \
18: pnpm build
19:
20: FROM node:22-bookworm
21:
22: ENV DEBIAN_FRONTEND=noninteractive \
23: DOTNET_CLI_TELEMETRY_OPTOUT=1 \
24: # See https://github.com/CocoaPods/CocoaPods/issues/6795
25: COCOAPODS_ALLOW_ROOT=1 \
26: CARGO_HOME=/root/.cargo \
27: RUSTUP_HOME=/root/.rustup \
28: PATH=${PATH}:/root/.cargo/bin:/opt/flutter/bin:/venv/bin
29:
30: RUN ...
<path>/home/byk/Code/getsentry/craft/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "@sentry/craft",
3: "version": "2.26.8",
4: "description": "The universal sentry workflow CLI",
5: "main": "dist/craft",
6: "repository": "https://github.com/getsentry/craft",
7: "author": "Sentry",
8: "license": "MIT",
9: "bin": {
10: "craft": "dist/craft",
11: "sentry-craft": "dist/craft"
12: },
13: "devDependencies": {
14: "@aws-sdk/client-lambda": "^3.984.0",
15: "@eslint/js": "^9.17.0",
16: "@google-cloud/storage": "^7.14.0",
17: "@octokit/plugin-retry": "^7.1.2",
18: "@octokit/request-error": "^7.0.0",
19: "@octokit/rest": "^21.0.2",
20: "@sentry/esbuild-plugin": "^2.23.0",
21: "@sentry/node": "^10.27.0",
22: "@types/async": "^3.0.1",
23: "@types/aws4": "^1.5.1",
24: "@types/cli-table": "^0.3.0",
25: "@types/git-url-parse": "^9.0.0",
26: "@types/is-ci": "^2.0.0",
27: "@types/js-yaml": ...
So the issue is clear now: the Docker image uses floating Node.js tags that automatically pull the latest 22.x version, and the publish logs confirm it's running Node 22.23.0, which is what's causing the incompatibility.
The fix is to upgrade to Node 22.23.1 or later, where the regression was patched. The challenge is that the craft :latest image was built when the floating tag still pointed to 22.23.0, so even though the node:22-bookworm tag will eventually float to 22.23.1, the craft image itself needs to be rebuilt to pick up that newer base image.
Given the user's preference for upgrading to a patched version, the most reliable approach is to explicitly pin the Dockerfile to node:22.23.1-bookworm-slim rather than relying on floating tags, which ensures deterministic builds and guarantees the patched version is used. be slim for a smaller image. The key is pinning to an exact patch version like 22.23.1 rather than relying on floating tags, since that's what caused the ...
I now have a complete, confirmed picture. Let me verify the patched Node version exists and check what the floating Docker tag currently resolves to, plus check whether craft has Docker dependency automation.
=== Node 22.x releases (looking for 22.23.0 and 22.23.1) === v22.23.1 2026-06-22 v22.23.0 2026-06-17
=== docker dependabot/renovate config === total 0 drwxrwxr-x 1 byk byk 18 Jan 13 14:03 . drwxrwxr-x 1 byk byk 794 Jun 9 19:29 .. drwxrwxr-x 1 byk byk 208 May 22 10:11 workflows ---dependabot--- no dependabot.yml ---renovate--- no renovate
So v22.23.0 from June 17th is the buggy release that matches the failed publish log, while v22.23.1 from June 22nd has the fix backported. Since there's no Dependabot or Renovate configuration, the floating node:22-bookworm tag is the only way the image gets updated, which means it's unpredictable. Now I need to check if those Docker Hub tags actually exist for version 22.23.
Confirmed: v22.23.0 (2026-06-17, buggy — matches the failed publish log's gl-node/22.23.0) and v22.23.1 (2026-06-22, patched with the #64004 backport). Now let me confirm the Docker Hub tags for the patched version exist.
=== Check node:22.23.1-bookworm-slim and -bookworm tags exist on Docker Hub === 22.23.1-bookworm-slim -> HTTP 200 22.23.1-bookworm -> HTTP 200 22.23.0-bookworm-slim -> HTTP 200
Found 11 matches
/home/byk/Code/getsentry/craft/.github/workflows/docs-preview.yml:
Line 20: - uses: actions/setup-node@v6
Line 22: node-version: '22'
/home/byk/Code/getsentry/craft/.github/workflows/lint.yml:
Line 16: - uses: actions/setup-node@v6
Line 18: node-version-file: package.json
/home/byk/Code/getsentry/craft/.github/workflows/build.yml:
Line 21: node: ['22', '24']
Line 25: - uses: actions/setup-node@v6
Line 27: node-version: '${{ matrix.node }}'
Line 54: - uses: actions/setup-node@v6
Line 56: node-version-file: package.json
Line 96: - uses: actions/setup-node@v6
Line 98: node-version-file: package.json
Both Node image variants are confirmed available on Docker Hub, and the CI workflows use either floating version tags or read from the package.json file via Volta, which specifies 22.12.0—the CI doesn't perform actual GCS uploads so it's not impacted by the image availability. Looking at the build.yml matrix on line 21 to understand the CI configuration better.
The CI test job runs on floating Node 22 (currently 22.23.1), but since tests are mocked, the http.Agent regression doesn't affect them. For the actual fix, I need to pin the Dockerfile base images to node:22.23.1-bookworm-slim and node:22.23.1-bookworm to resolve the GCS publish failure caused by the 22.23.0 regression. Pinning exactly is more deterministic than relying on floating tags, which is what led to this incident in the first place—the floating tag picked up 22.23.0 the same day it was released. ...
All facts confirmed. Both patched Docker tags (node:22.23.1-bookworm and -slim) exist (HTTP 200). Let me do a final scan for any other references to the Node base image to make sure I capture every spot the version is pinned.
Found 15 matches /home/byk/Code/getsentry/craft/Dockerfile: Line 1: FROM node:22-bookworm-slim AS builder Line 20: FROM node:22-bookworm Line 66: && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa1.rabbitmq.com/rabbitmq/rabbitmq-erlang/deb/debian bookworm main" >> /etc/apt/sources.list.d/erlang.list \ Line 67: && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/rabbitmq.E495BB49CC4BBE5B.gpg] https://ppa2.rabbitmq.com/rabbitmq/rabbitmq-erlang/deb/debian bookworm main" >> /etc/apt/sources.list.d/erlang.list \ /home/byk/Code/getsentry/craft/CHANGELOG.md: Line 836: - ref: upgrade docker to debian bookworm (#526) by @asottile-sentry Line 873: - Revert "build: Bump Dockerfile base image to node:20-bookworm (#511)" (#513) by @loewenheim Line 885: - build: Bump Dockerfile base image to node:20-bookworm (#511) by @loewenheim Line 977: - feat(build): Bump Dockerfile base image to node:20-bookworm (#472) by ...
The fix is straightforward—I need to update the Node.js base image versions in the Dockerfile from the generic 22 tag to the specific 22.23.1 version on both the builder stage and the final stage. Now I'm drafting the plan. Plan outline... I should verify the fix locally by building the image and checking that Node 22.23.1 is running inside it. There's a test coverage gap here worth noting: this is a runtime/infrastructure bug from the floating base image, not a code issue, so craft's unit tests wouldn't catch it since they mock network calls. The real safeguard is pinning the exact version plus setting up Dependabot to automate future base image updates. I'm confident pinning to 22.23.1 is the right call since the floating tag is what caused the regression in the first place.
I have everything I need. The only place the Node base image is pinned is the Dockerfile (lines 1 and 20); the temp_work/ and temp_update_sha/ hits are unrelated scratch dirs. Here's the plan.
ERR_STREAM_PREMATURE_CLOSE)The failed craft publish 0.15.2 job dies during the GCS upload with:
Invalid response body while trying to fetch https://www.googleapis.com/oauth2/v4/token: Premature close
code: ERR_STREAM_PREMATURE_CLOSE
The request headers in that same error show the smoking gun: x-goog-api-client: gl-node/22.23.0.
Chain of causation:
http.Agent"). That fix attached a public 'data' listener to idle keep-alive Agent sockets.socket.listenerCount('data') > 0 while closing a response and, seeing the new listener, throws a false ERR_STREAM_PREMATURE_CLOSE on reused pooled sockets (nodejs/node#63989).@google-cloud/storage → google-auth-library → gaxios@6 → node-fetch@2, exactly during the GCS OAuth token fetch.Dockerfile uses the floating node:22-bookworm / node:22-bookworm-slim base images. The float silently advanced to the bad 22.23.0 — which is why this broke with no change to craft's code or deps.Node fixed the regression in PR #64004, backported to the 22 line and released as Node 22.23.1 (2026-06-22). I verified both patched Docker tags exist on Docker Hub (HTTP 200):
node:22.23.1-bookworm-slimnode:22.23.1-bookwormChange Dockerfile to pin the exact patched version:
FROM node:22-bookworm-slim AS builder → FROM node:22.23.1-bookworm-slim AS builderFROM node:22-bookworm → `FROM ...The plan file at .opencode/plans/1782290100699-sunny-mountain.md is empty. Please write the plan first before calling plan_exit.
I need to save the plan to a file before calling plan_exit.
Wrote file successfully.
User approved switching to build agent. Wait for further instructions.
The plan at .opencode/plans/1782290100699-sunny-mountain.md has been approved, you can now edit files. Execute the plan
A plan file exists at /home/byk/Code/getsentry/craft/.opencode/plans/1782290100699-sunny-mountain.md. You should execute on the plan defined within it
I'll execute the plan. The core fix is the two-line Dockerfile change.
Edit applied successfully.
Edit applied successfully.
<path>/home/byk/Code/getsentry/craft/Dockerfile</path>
<type>file</type>
<content>
1: FROM node:22.23.1-bookworm-slim AS builder
2:
3: WORKDIR /usr/local/lib
4:
5: # Install pnpm (version from Volta config in package.json)
6: COPY package.json ./
7: RUN npm install -g pnpm@$(node -p "require('./package.json').volta.pnpm")
8:
9: COPY package.json pnpm-lock.yaml ./
10: RUN pnpm install --frozen-lockfile --quiet
11:
12: COPY . .
13:
14: RUN \
15: NODE_ENV=production \
16: NODE_PATH=/usr/local/lib/node_modules \
17: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin" \
18: pnpm build
19:
20: FROM node:22.23.1-bookworm
21:
(Showing lines 1-21 of 110. Use offset=22 to continue.)
</content>
Both edits are applied and the fix is complete. I'm verifying there are no other places that need changes—the plan only requires modifying the Dockerfile itself, with the optional follow-ups like Dependabot and Volta not being necessary. Let me check the git status to confirm only the Dockerfile changed, since the user asked me to execute the plan and I shouldn't commit unnecessary churn to other files.
Both base image references are now pinned to the patched 22.23.1. Let me verify the working tree reflects only the intended change.
diff --git a/Dockerfile b/Dockerfile index 8639d77..e9a2db7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22-bookworm-slim AS builder +FROM node:22.23.1-bookworm-slim AS builder WORKDIR /usr/local/lib @@ -17,7 +17,7 @@ RUN \ PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin" \ pnpm build -FROM node:22-bookworm +FROM node:22.23.1-bookworm ENV DEBIAN_FRONTEND=noninteractive \ DOTNET_CLI_TELEMETRY_OPTOUT=1 \ === status === M Dockerfile ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/
Please address this message and continue with your tasks. </system-reminder>
I need to update Volta by bumping the Node version in package.json from 22.12.0 to 22.23.1, then create a branch and get a PR up. Once CI passes, I'll merge it, cut a new release for craft, and retry the publish job. Let me first check whether this is a jj repo or a git repo by looking at the directory structure.
Let me align Volta, then check the VCS setup before creating the branch.
Edit applied successfully.
GIT_REPO === branch === fix/replace-extract-zip === remote === origin https://github.com/getsentry/craft.git (fetch) origin https://github.com/getsentry/craft.git (push)
I'm on the fix/replace-extract-zip branch with uncommitted changes in the Dockerfile and package.json. Following the Craft workflow, I need to fetch master, create a new branch from it, stash my changes, and then pop them onto the new branch before committing and pushing.
For the branch name, I'll follow the convention {user}/{type}/{description} — something like byk/fix/node-2223-premature-close. Before committing, I need to be careful about the lore directives: restore AGENTS.md if it's been modified (to avoid lore-daemon churn in feature PRs), and make sure I'm only staging the actual changes (Dockerfile and package.json) while leaving untracked temp directories and .opencode/ alone.
I'll stash just the two modified files, create the branch from origin/master, then pop the stash to apply them cleanly. After that, I'll verify the diff only contains those two files, restore AGENTS.md if needed, and commit.
Git repo, but I'm on fix/replace-extract-zip. Per the Craft workflow, this fix belongs on its own branch off master. Let me fetch master, check for any lore churn, and stash my two changes.
fetched === full status === M Dockerfile M package.json ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/ === lore/agents files present? === -rw-r--r-- 1 byk byk 4637 Jun 9 17:20 AGENTS.md -rw-r--r-- 1 byk byk 59975 Jun 9 19:39 .lore.md
Only the Dockerfile and package.json have been modified, so there's no risk of lore churn. I'll stash these changes, create a new branch from origin/master following the naming convention byk/fix/node-2223-premature-close, and then pop the stashed files back.
Only Dockerfile and package.json are modified — no lore churn. Let me create the fix branch off master.
Saved working directory and index state On fix/replace-extract-zip: node-2223-fix
branch 'byk/fix/node-2223-premature-close' set up to track 'origin/master'.
Auto-merging package.json
On branch byk/fix/node-2223-premature-close
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: Dockerfile
modified: package.json
Untracked files:
(use "git add <file>..." to include in what will be committed)
.opencode/
temp_rust/
temp_single_update/
temp_update_sha/
temp_work/
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (0448e90bda63c92de1708cedf52b9b1b24e7d277)
=== diff vs master ===
M Dockerfile
M package.json
?? .opencode/
?? temp_rust/
?? temp_single_update/
?? temp_update_sha/
?? temp_work/
Switched to a new branch 'byk/fix/node-2223-premature-close'
The stash pop auto-merged package.json, which means master had a different version than what I stashed. I need to verify the resulting diff includes both the Dockerfile change to 22.23.1 and the Volta version bump, since the auto-merge could have introduced conflicts or lost changes if master's version field diverged from what I expected earlier.
The stash auto-merged package.json against master's version. Let me verify the diff against master contains exactly my two intended changes and nothing unexpected.
diff --git a/Dockerfile b/Dockerfile
index 8639d77..e9a2db7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:22-bookworm-slim AS builder
+FROM node:22.23.1-bookworm-slim AS builder
WORKDIR /usr/local/lib
@@ -17,7 +17,7 @@ RUN \
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin" \
pnpm build
-FROM node:22-bookworm
+FROM node:22.23.1-bookworm
ENV DEBIAN_FRONTEND=noninteractive \
DOTNET_CLI_TELEMETRY_OPTOUT=1 \
diff --git a/package.json b/package.json
index ceca0ed..ab60d3b 100644
--- a/package.json
+++ b/package.json
@@ -90,7 +90,7 @@
"docs:build": "cd docs && pnpm build"
},
"volta": {
- "node": "22.12.0",
+ "node": "22.23.1",
"pnpm": "10.27.0"
},
"dependencies": {
The diff shows the three expected changes across the Dockerfile and package.json for the Node version update. Now I'm thinking through the commit process—there's a convention about adding .lore.md before committing, but it hasn't been modified, so I need to figure out whether that directive applies here.
Actually, the directive is clear: always run git add .lore.md before committing, even if it's unmodified. I'll stage the Dockerfile, package.json, and .lore.md together, then write a conventional commit message explaining the Node version pin and the node-fetch regression it addresses.
Diff is exactly the three intended changes. Now let me commit (staging .lore.md per repo policy, even though it's clean) and push.
M Dockerfile M package.json ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/
=== committed === adc20c8 fix: pin Node to 22.23.1 to avoid node-fetch premature-close regression
remote: https://github.com/getsentry/craft/security/dependabot remote: To https://github.com/getsentry/craft.git * [new branch] byk/fix/node-2223-premature-close -> byk/fix/node-2223-premature-close branch 'byk/fix/node-2223-premature-close' set up to track 'origin/byk/fix/node-2223-premature-close'.
Pushed. Now let me open the PR.
https://github.com/getsentry/craft/pull/837
PR #837 is created and I'm waiting for the CI checks to pass before merging. I'm considering whether the adversarial subagent review directive applies here—it was originally for the loreai project, and this is just a minor three-line config update to the Docker base image and Volta version, so the mutation testing review might not be necessary for such a straightforward change.
PR created: https://github.com/getsentry/craft/pull/837
Now let me watch CI. I'll wait for the checks to complete.
Analyze (actions) pass 41s https://github.com/getsentry/craft/actions/runs/28086960454/job/83155172489 Changelog Preview / Semver Impact pass 0 https://github.com/getsentry/craft/actions/runs/28086962512 Patch CodeQL pass 2s https://github.com/getsentry/craft/runs/83155271973 Cursor Bugbot pass 38s https://cursor.com/docs/bugbot Lint fixes pass 31s https://github.com/getsentry/craft/actions/runs/28086962491/job/83155175415 Node 22 pass 36s https://github.com/getsentry/craft/actions/runs/28086962464/job/83155175683 Node 24 pass 37s https://github.com/getsentry/craft/actions/runs/28086962464/job/83155175755 Secret ...
Most of the CI checks are passing, but the Docker image build is still pending—that's the critical one since it actually constructs the image with the new base. The build process with all the dependencies like apt packages, Rust, and Flutter could take several minutes, so I'll wait for it to complete.
Most checks pass; the Docker image build and a few others are still pending. The image job (which actually builds with the new base) is the key one. Let me wait for it.
semgrep-cloud-platform/scan pass 1m39s https://semgrep.dev/orgs/sentry/projects/2706060/scans/185096856 warden pass 20s https://github.com/getsentry/craft/actions/runs/28086962501/job/83155191473 warden pass 0 https://warden.sentry.dev warden: security-review pass 0 https://warden.sentry.dev Analyze (actions) pass 41s https://github.com/getsentry/craft/actions/runs/28086960454/job/83155172489 Analyze (javascript) pass 55s https://github.com/getsentry/craft/actions/runs/28086960454/job/83155172439 Build pass 22s https://github.com/getsentry/craft/actions/runs/28086962464/job/83155294407 Build Docs pass 30s https://github.com/getsentry/craft/actions/runs/28086962464/job/83155294361 Changelog Preview / Semver Impact pass 0 https://github.com/getsentry/craft/actions/runs/28086962512 Patch CodeQL pass 2s https://github.com/getsentry/craft/runs/83155271973 Cursor Bugbot pass 38s https://cursor.com/docs/bugbot Lint ...
All the CI checks have passed, including the Docker image build with the pinned Node base image. Since the user wanted me to merge when CI passes, I'll go ahead and squash merge this to keep a clean history with a conventional commit title.
All checks pass, including the image Docker build (5m24s) which successfully built with the pinned node:22.23.1-bookworm. Merging now.
X Pull request getsentry/craft#837 is not mergeable: the base branch policy prohibits the merge. To have the pull request merged after all the requirements have been met, add the `--auto` flag. To use administrator privileges to immediately merge the pull request, add the `--admin` flag. === state === OPEN
The branch protection policy requires an approving review before merging, even though CI has passed and the user explicitly asked me to merge when CI passes. I'm weighing whether to use admin privileges to override the protection and merge immediately, since the user has admin access and gave clear instructions, or enable auto-merge which would wait for a review that might not come automatically.
The merge is blocked by base branch protection. Let me check exactly what requirement is missing.
{"mergeStateStatus":"BLOCKED","mergeable":"MERGEABLE","reviewDecision":"REVIEW_REQUIRED"}
The PR is mergeable and CI passed, but branch protection requires an approving review that hasn't been given yet. The user asked me to merge when CI passes, so I'm weighing whether to use the --admin flag to override the review requirement since they have admin access, or ask them first.
Using admin override would bypass a required human review—that's a meaningful policy decision. Even though the user's instruction was clear about merging when CI passes, they may not have anticipated this review blocker. I should ask them whether to proceed with the admin merge rather than unilaterally bypassing branch protection.
CI is fully green (mergeable: MERGEABLE), but branch protection requires an approving review (reviewDecision: REVIEW_REQUIRED) — that's the only thing blocking. You said "merge when CI passes," but the policy needs a review that isn't there yet. How should I proceed?
User has answered your questions: "PR #837 has all CI green but is BLOCKED by a required-review branch protection rule. How do you want to merge?"="Admin override now (Recommended)". You can now continue with the user's answers in mind.
failed to run git: fatal: 'master' is already used by worktree at '/home/byk/.local/share/opencode/worktree/55fa901aca64a01fa4b5c2abce1c85c1c27475e7/stellar-falcon' === state === MERGED 77012afa53f4b5e52b138eba436363f278eb319a
The PR is already merged with commit 77012af, so the main work is done. The local branch cleanup failed due to a worktree conflict when trying to switch to master, but that's just a local issue—the actual merge and remote operations succeeded. I should verify the remote branch was deleted and confirm everything went through on the server side.
Now I'm looking at the remaining steps: I need to cut a new release for craft. Since craft dogfoods itself, the release process involves triggering a workflow that runs craft prepare <version> to create a release branch and bump the version, then merging the resulting PR triggers the publish workflow. I should check the workflows directory to see how releases are typically initiated, and I need to figure out what the next version should be based on the current version in master and the changelog.
PR #837 is MERGED (commit 77012af on master). The local --delete-branch cleanup hit a worktree conflict, which is harmless — the merge succeeded. Now let me figure out how craft cuts a release (it dogfoods itself).
build.yml changelog-preview.yml docs-preview.yml enforce-license-compliance.yml image.yml lint.yml release.yml === current version on master === "version": "2.27.0-dev.0", === latest tags === refs/tags/2.26.7 refs/tags/2.26.8 refs/tags/2.26.9 refs/tags/2.26.10 refs/tags/2.26.11
The current dev version is ahead of the latest release, so I need to bump to the next patch version. Let me check the release workflow to see how to trigger it.
<path>/home/byk/Code/getsentry/craft/.github/workflows/release.yml</path>
<type>file</type>
<content>
1: name: Release
2: concurrency: ${{ github.workflow }}-${{ github.ref }}
3:
4: on:
5: # For Craft's own releases (dogfooding)
6: workflow_dispatch:
7: inputs:
8: version:
9: description: Version to release
10: required: true
11: default: 'auto'
12: force:
13: description: Force a release even when there are release-blockers (optional)
14: required: false
15:
16: # For external repos to call this workflow
17: workflow_call:
18: inputs:
19: version:
20: description: Version to release (semver, bump type, or "auto")
21: type: string
22: required: false
23: force:
24: description: Force a release even when there are release-blockers
25: type: string
26: required: false
27: default: 'false'
28: merge_target:
29: description: Target branch ...
The release workflow is triggered via gh workflow run release.yml and accepts version and force inputs. Since the user wants to cut a new release, I'll dispatch it with the default auto version setting, which will automatically compute the next patch version as 2.26.12 and kick off the prepare step that creates the release branch and PR.
Once triggered, craft prepare will bump the version, update the changelog, and create a release PR that needs to be merged. That merge to master will then trigger the publish workflow through the getsentry/publish app. But here's the critical piece — the publish step relies on the getsentry/craft:latest Docker image, which needs to include my fix. I need to check the image.yml workflow to see when that image gets rebuilt and pushed, since the sentry-native retry won't work without the updated image.
The Release workflow is a workflow_dispatch. Before triggering it, I need to confirm when getsentry/craft:latest (the image the publish pipeline actually uses) gets rebuilt — that's the image that must carry the fix for the retry to work.
<path>/home/byk/Code/getsentry/craft/.github/workflows/image.yml</path>
<type>file</type>
<content>
1: name: image
2:
3: on:
4: pull_request:
5: push:
6: branches: [master, release/**, test-me-*]
7:
8: jobs:
9: image:
10: runs-on: ubuntu-latest
11: permissions:
12: contents: read
13: packages: write
14: steps:
15: - uses: actions/checkout@v6
16: - name: builder-image
17: run: |
18: set -euxo pipefail
19:
20: img=ghcr.io/getsentry/craft-builder:latest
21: args=()
22: if docker pull -q "$img"; then
23: args+=(--cache-from "$img")
24: fi
25: docker buildx build \
26: "${args[@]}" \
27: --build-arg BUILDKIT_INLINE_CACHE=1 \
28: --target builder \
29: --tag "$img" \
30: .
31: - name: image
32: run: |
33: set -euxo pipefail
34:
35: img=ghcr.io/getsentry/craft:latest
36: ...
The image.yml workflow triggers on pushes to master and rebuilds the ghcr.io/getsentry/craft:latest image, so PR #837's merge should have updated it with the fix. However, the failing publish job was referencing a different image tag that wasn't updated.
The publish pipeline uses docker://getsentry/craft:latest from Docker Hub, not the ghcr.io version. So the real question is where Docker Hub's getsentry/craft:latest gets updated — likely through craft's own release process via its .craft.yml configuration, which probably has a docker target that publishes to Docker Hub. I need to check that file to confirm whether the Docker Hub image gets updated when craft publishes a new release.
Key insight: image.yml pushes to ghcr.io, but the failed publish ran docker://getsentry/craft:latest — that's Docker Hub. I need to confirm how Docker Hub's getsentry/craft:latest gets updated (almost certainly craft's own publish docker target) and whether craft's self-publish touches GCS (the broken path). Let me read .craft.yml.
<path>/home/byk/Code/getsentry/craft/.craft.yml</path>
<type>file</type>
<content>
1: minVersion: '2.14.0'
2: changelog:
3: policy: auto
4: artifactProvider:
5: name: github
6: config:
7: artifacts:
8: Build & Test:
9: - craft-binary
10: - craft-docs
11: preReleaseCommand: >-
12: node -p "
13: const {execSync} = require('child_process');
14: execSync('npm --no-git-tag-version version ' + process.env.CRAFT_NEW_VERSION).toString();
15: "
16: postReleaseCommand: >-
17: node -p "
18: const {execSync} = require('child_process');
19: execSync('npm --no-git-tag-version version preminor --preid=dev');
20: execSync('git diff --quiet || git commit -anm \'meta: Bump new development version\\n\\n#skip-changelog\' && git pull --rebase && git push').toString();"
21: requireNames:
22: - /^sentry-craft.*\.tgz$/
23: - /^craft$/
24: targets:
25: - name: npm
26: - name: gcs
27: includeNames: /^.*craft.*$/
28: bucket: ...
I'm seeing the core issue now: craft's own .craft.yml configuration includes a GCS target for uploading artifacts to the sentry-sdk-assets bucket, which means when craft publishes itself, it will use the same broken GCS code path that's currently failing. This creates a bootstrapping problem—to fix and release the updated craft, it needs to publish itself using the broken Docker image, but that publish will fail on the GCS target.
The publish targets execute in a specific order (npm, gcs, registry, docker targets, github, gh-pages), so GCS runs before the docker targets that would update Docker Hub with the fixed image. If GCS fails, the entire publish aborts and never reaches the docker targets, creating a deadlock where craft can't complete a publish to fix Docker Hub's latest image.
To break this, I'd need to manually rebuild and push getsentry/craft:latest to Docker Hub outside the normal release flow, which requires Docker Hub credentials. ...
Critical finding: craft's own .craft.yml includes a gcs target (lines 26-35) that runs before the docker targets (which update Docker Hub getsentry/craft:latest). Since craft publishes itself using the currently-broken Docker Hub getsentry/craft:latest image, its own release could fail at gcs before ever reaching the docker target that would ship the fix — a potential bootstrap deadlock. I need to understand craft's target-failure behavior before dispatching a release.
artifacts_cmds artifacts.ts changelog.ts config.ts prepare.ts publish.ts targets.ts __tests__ === publish command file === src/commands/__tests__/publish.test.ts src/commands/publish.ts
Found 81 matches
/home/byk/Code/getsentry/craft/src/commands/artifacts_cmds/list.ts:
Line 59: } catch (e) {
/home/byk/Code/getsentry/craft/src/commands/publish.ts:
Line 33: catchKeyboardInterrupt,
Line 168: throw Error(`Invalid version provided: "${version}"`);
Line 179: async function publishToTarget(
Line 267: for (const targetConfig of targetConfigList) {
Line 272: continue;
Line 283: } catch (err) {
Line 285: throw err;
Line 295: * This helps to catch cases when there are several independent providers (e.g. Travis,
Line 367: } catch (e) {
Line 378: * Error thrown when the release branch merge fails due to conflicts.
Line 396: * Error thrown when the post-merge push fails (e.g., expired token). ...
I need to examine the abort-versus-continue logic in the publish.ts file, specifically looking at the target loop around lines 257-290 and the per-target publishing section around lines 745-770.
<path>/home/byk/Code/getsentry/craft/src/commands/publish.ts</path>
<type>file</type>
<content>
255: logger.warn('No artifacts found for the revision.');
256: }
257: }
258: }
259:
260: async function getTargetList(
261: targetConfigList: TargetConfig[],
262: artifactProvider: BaseArtifactProvider,
263: ): Promise<BaseTarget[]> {
264: logger.trace('Initializing targets');
265: const githubRepo = await getGlobalGitHubConfig();
266: const targetList: BaseTarget[] = [];
267: for (const targetConfig of targetConfigList) {
268: const targetClass = getTargetByName(targetConfig.name);
269: const targetDescriptor = BaseTarget.getId(targetConfig);
270: if (!targetClass) {
271: logger.warn(`Target implementation for "${targetDescriptor}" not found.`);
272: continue;
273: }
274: try {
275: logger.debug(`Creating target ${targetDescriptor}`);
276: logger.trace(targetConfig);
277: const target = new targetClass(
278: ...
<path>/home/byk/Code/getsentry/craft/src/commands/publish.ts</path>
<type>file</type>
<content>
740: const targetList = await getTargetList(targetConfigList, artifactProvider);
741: if (targetList.length > 0) {
742: logger.info('Publishing to targets:');
743:
744: logger.info(targetList.map(target => ` - ${target.id}`).join('\n'));
745: logger.info(' ');
746: await promptConfirmation();
747:
748: await withTempDir(async (downloadDirectory: string) => {
749: artifactProvider.setDownloadDirectory(downloadDirectory);
750:
751: // Ensure the state directory exists. `mkdirSync` with
752: // `recursive: true` is idempotent, so this is safe on resumed
753: // runs where the directory was already created. ...
I see the publish loop iterates through targets sequentially in the order they're configured, calling publishToTarget for each one and updating the publish state file after each successful publication. If publishToTarget throws an error, the loop stops there.
For craft's own .craft.yml, the target order is npm, gcs, registry, docker (release), docker (latest), github, and gh-pages. So when craft publishes itself, it would succeed at npm, then fail at gcs and abort — never reaching the docker targets that would update Docker Hub's getsentry/craft:latest image with the fix. This creates a bootstrap deadlock: dispatching the Release workflow and merging the PR would leave craft half-published (npm done, everything else incomplete), and retrying would just fail at gcs again. The only way out is to fix Docker Hub latest out-of-band, but the fixed image already exists at ghcr.io/getsentry/craft:latest from the master branch build. ...
Now the full picture is clear, and there's a bootstrap deadlock I need to flag before cutting a release. Let me read the post-loop merge handling and verify the ghcr image rebuilt on the master merge.
<path>/home/byk/Code/getsentry/craft/src/commands/publish.ts</path>
<type>file</type>
<content>
179: async function publishToTarget(
180: target: BaseTarget,
181: version: string,
182: revision: string,
183: ): Promise<void> {
184: const publishMessage = `=== Publishing to target: ${chalk.bold.cyan(
185: target.id,
186: )} ===`;
187: const delim = Array(stringLength(publishMessage) + 1).join('=');
188: logger.info(' ');
189: logger.info(delim);
190: logger.info(publishMessage);
191: logger.info(delim);
192: await withTracing(
193: async () => {
194: await target.publish(version, revision);
195: },
196: {
197: name: `craft.target.${target.id}`,
198: op: 'craft.target.publish',
199: attributes: {
200: 'target.id': target.id,
201: 'target.name': target.config.name,
202: version,
203: revision,
204: },
205: },
206: )();
207: }
208:
209: /**
210: * Prints summary for the revision, ...
So publishToTarget awaits the target's publish method, which throws on failure and stops the loop before marking that target as successful or moving to the next one—it's a sequential, fail-fast approach that respects config order. Now I need to check whether the ghcr image was actually rebuilt and pushed during the master merge of #837, so I can confirm the fixed image is available to mirror from.
Confirmed: targets publish sequentially in config order, fail-fast — a gcs throw aborts before the docker targets run. Now let me verify the fixed image actually got built+pushed to ghcr on the master merge.
=== recent 'image' workflow runs (post-merge on master) === in_progress fix: pin Node to 22.23.1 to avoid node-fetch premature-close regressi… image master push 28087538845 7m59s 2026-06-24T09:04:18Z completed success meta: Bump new development version image master push 28024678175 7m27s 2026-06-23T12:01:46Z completed success Merge branch 'release/2.26.11' image master push 28024673836 1m37s 2026-06-23T12:01:41Z completed success fix: resolve 10 Dependabot security alerts (#836) image master push 28019752324 7m18s 2026-06-23T10:29:24Z completed success build(deps-dev): bump esbuild from 0.25.12 to 0.28.1 (#834) image master push 27451491905 11m10s 2026-06-13T00:50:31Z === merge commit 77012af on master? === 1d955b5 meta: Bump new development version a33b173 Merge branch 'release/2.26.11' f245d27 release: 2.26.11
The image workflow for that merge commit is still building and pushing the fixed Docker image, running for about 8 minutes now. Once it completes, I'll have the full picture of what's happening and can walk through the situation and next steps with you.
The image workflow for my merge commit is currently in progress — it's building and pushing the fixed ghcr.io/getsentry/craft:latest. Let me wait for it to finish to confirm the fixed image lands on ghcr.
Now let's see if we can upgrade to Node 24 with a patched version
The user wants to explore upgrading craft from Node 22.23.1 to Node 24 with a patched version (24.18.0) that includes a necessary fix. I'm starting to map out what needs to change, beginning with the Dockerfile which currently specifies the older Node version.
I need to check the current state of the Dockerfile, package.json (for Volta and engines field), CI workflows, and verify that Node 24.18.0 images are available on Docker Hub. This is a separate PR from the hotfix since it's a major version bump of the runtime, even though it's related to the same Node issue.
Good follow-up — the hotfix kept craft on the 22.x line, but the fix is also in Node 24.18.0 'Krypton' (LTS). Let me investigate the current state and feasibility before making changes.
<path>/home/byk/Code/getsentry/craft/Dockerfile</path>
<type>file</type>
<content>
1: FROM node:22.23.1-bookworm-slim AS builder
2:
3: WORKDIR /usr/local/lib
4:
5: # Install pnpm (version from Volta config in package.json)
6: COPY package.json ./
7: RUN npm install -g pnpm@$(node -p "require('./package.json').volta.pnpm")
8:
9: COPY package.json pnpm-lock.yaml ./
10: RUN pnpm install --frozen-lockfile --quiet
11:
12: COPY . .
13:
14: RUN \
15: NODE_ENV=production \
16: NODE_PATH=/usr/local/lib/node_modules \
17: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/node_modules/.bin" \
18: pnpm build
19:
20: FROM node:22.23.1-bookworm
21:
22: ENV DEBIAN_FRONTEND=noninteractive \
23: DOTNET_CLI_TELEMETRY_OPTOUT=1 \
24: # See https://github.com/CocoaPods/CocoaPods/issues/6795
25: COCOAPODS_ALLOW_ROOT=1 \
26: CARGO_HOME=/root/.cargo \
27: RUSTUP_HOME=/root/.rustup \
28: PATH=${PATH}:/root/.cargo/bin:/opt/flutter/bin:/venv/bin
29: ...
I need to check the package.json for engine specifications and volta configuration, then review the CI workflows to verify the Node 24.18.0 image is available and confirm what the latest 24.x patch version is.
Let me check package.json (engines/volta), the CI workflows referencing Node, and verify the Node 24 patched image exists.
<path>/home/byk/Code/getsentry/craft/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "@sentry/craft",
3: "version": "2.27.0-dev.0",
4: "description": "The universal sentry workflow CLI",
5: "main": "dist/craft",
6: "repository": "https://github.com/getsentry/craft",
7: "author": "Sentry",
8: "license": "MIT",
9: "bin": {
10: "craft": "dist/craft",
11: "sentry-craft": "dist/craft"
12: },
13: "devDependencies": {
14: "@aws-sdk/client-lambda": "^3.984.0",
15: "@eslint/js": "^9.17.0",
16: "@google-cloud/storage": "^7.14.0",
17: "@octokit/plugin-retry": "^7.1.2",
18: "@octokit/request-error": "^7.0.0",
19: "@octokit/rest": "^21.0.2",
20: "@sentry/esbuild-plugin": "^2.23.0",
21: "@sentry/node": "^10.27.0",
22: "@types/async": "^3.0.1",
23: "@types/aws4": "^1.5.1",
24: "@types/cli-table": "^0.3.0",
25: "@types/git-url-parse": "^9.0.0",
26: "@types/is-ci": "^2.0.0",
27: "@types/js-yaml": ...
Looking at the package.json, there's no engines field defined, and @types/node is pinned to ^22.10.1 which matches the Volta Node version of 22.23.1. If we upgrade the runtime to Node 24, I should consider bumping the types package to ^24 for consistency and to get accurate type definitions for the newer Node APIs, though the current ^22 types would still function. Next I need to check the CI workflows to see what Node versions they're using and verify Node 24 compatibility.
Now let me check the CI workflows that reference Node and verify the patched Node 24 images exist + find the latest 24.x patch.
Found 13 matches
/home/byk/Code/getsentry/craft/.github/workflows/lint.yml:
Line 16: - uses: actions/setup-node@v6
Line 18: node-version-file: package.json
/home/byk/Code/getsentry/craft/.github/workflows/docs-preview.yml:
Line 20: - uses: actions/setup-node@v6
Line 22: node-version: '22'
/home/byk/Code/getsentry/craft/.github/workflows/build.yml:
Line 20: matrix:
Line 21: node: ['22', '24']
Line 22: name: Node ${{ matrix.node }}
Line 25: - uses: actions/setup-node@v6
Line 27: node-version: '${{ matrix.node }}'
Line 54: - uses: actions/setup-node@v6
Line 56: node-version-file: package.json
Line 96: - uses: actions/setup-node@v6
Line 98: node-version-file: package.json
=== latest Node 24.x (from nodejs dist index) === v
I'm running into that fullwidth pipe character issue again with jq, so I'll switch to a cleaner Python script instead of trying to fix the one-liner.
Latest 24.x releases: v24.18.0 2026-06-23 lts=Krypton v24.17.0 2026-06-17 lts=Krypton v24.16.0 2026-05-21 lts=Krypton v24.15.0 2026-04-15 lts=Krypton v24.14.1 2026-03-24 lts=Krypton v24.14.0 2026-02-24 lts=Krypton
The latest Node 24.x version is v24.18.0 (released June 23, 2026 as LTS 'Krypton'), which includes the fix I need. Now I'm checking whether the Docker Hub tags for the bookworm-slim and bookworm variants of this version are available.
Latest Node 24.x is v24.18.0 (2026-06-23, LTS 'Krypton') — the exact release containing the fix. Let me confirm the Docker Hub tags exist.
node:24.18.0-bookworm-slim -> HTTP 404 node:24.18.0-bookworm -> HTTP 404
Both endpoints are returning 404s, which is odd since Node 24.18.0 was released on nodejs.org back in June 2026, though the Docker images might not be published yet or the API endpoint format could be different. Let me try the same verification method that worked earlier for 22.23.1 — checking the manifest directly with a curl request to see if I get an HTTP 200 response.
404 — but that hub API endpoint can be unreliable. Let me verify with a definitive method and compare against the known-good 22.23.1 tag.
=== compare via Docker Hub tag API (page through 24.18) === node:24.18.0-bookworm-slim -> 404 node:24.18.0-bookworm -> 404 node:22.23.1-bookworm-slim -> 200 node:24.17.0-bookworm-slim -> 200 === authoritative: docker registry manifest (token + HEAD) === node:24.18.0-bookworm-slim -> 404 node:24.17.0-bookworm-slim -> 200 node:22.23.1-bookworm-slim -> 200
I'm checking Docker Hub availability for different Node.js versions and finding that 24.18.0 hasn't been published yet despite the release on nodejs.org, while 24.17.0 exists but has a known regression, so our current pin to 22.23.1 remains the stable choice. We can't upgrade to Node 24 right now without reintroducing the bug, so the best approach is to wait for the 24.18.0 Docker image to be published (likely within a day or two) and then do the upgrade. I should check the docker-node repository to get a more precise timeline on when that image will be available.
Authoritative result: the patched node:24.18.0 Docker images are NOT published yet (404). The only available Node 24 image is 24.17.0 — which is the broken version with this exact regression. nodejs.org shipped 24.18.0 yesterday (Jun 23), but the official docker-node images lag. Let me check the docker-node repo for an ETA.
=== docker-node: is 24.18.0 in the official library yet? === 26: None 24: None 22: None === recent docker-node commits mentioning 24.18 === nodejs/docker-node de1c8c994e1bf8a5843ff7d4d987eee0cad69243 feat: Node.js 22.18.0, 24.5.0 nodejs-github-bot 2025-08-01T10:06:16Z === recent docker-node commits === Merge pull request #2545 from nodejs/update-branch feat: Node.js 22.23.1 Merge pull request #2537 from MikeMcC399/de-duplicate-security-issue-choice Merge pull request #2543 from nodejs/dependabot/github_actions/actions/checkout-7.0.0 docs: describe strategy for Alpine new release (#2536)
The latest docker-node release is 22.23.1, and they haven't published Node 24.18.0 yet—only up to 24.17.0. This means I can't upgrade to 24.18.0 through Docker at the moment since the official image doesn't exist.
So the situation is clear: the patched version exists on nodejs.org but isn't dockerized yet, so we're stuck with 24.17.0 (the broken version) if we try to upgrade now. I should present this finding to the user and offer options—either wait for docker-node to publish 24.18.0, prepare the upgrade on a branch ready to merge once it lands, or set up monitoring for when the image becomes available.
Now I have a clear answer. docker-node's latest published version is 22.23.1 — 24.18.0 has not been added to the official Docker library yet. Let me check if there's an in-flight PR so I can give you an ETA.
=== open docker-node PRs (looking for 24.18.0) === 2546 feat: Node.js 24.18.0 update-branch OPEN 2026-06-24T07:46:11Z 2538 chore: consolidate StackOverflow docker tags MikeMcC399:consolidate-stackoverflow-tags OPEN 2026-06-15T09:13:50Z 2533 docs: document Node.js 27 introduction plans MikeMcC399:node-27-introduction-plans OPEN 2026-06-08T12:46:26Z 2511 chore: port Bash scripts to JS nschonni:port-update-2026 DRAFT 2026-05-20T03:51:55Z 2494 feat: only include the architectures supported in version.json nschonni:image-conditions DRAFT 2026-05-09T22:40:49Z 2485 Fix: Pre-create node_modules with correct ownership for non-root users mulder999:main OPEN 2026-05-06T14:08:52Z 2476 feat: Node.js Alpine arm64 Unofficial Builds nschonni:arm64-pre-built DRAFT 2026-04-21T16:17:00Z 2459 chore: use release-keys keyring for gpg fingerprints nschonni:parse-keyring-on-update OPEN 2026-04-14T17:57:56Z 2415 chore: switch to official `release-keys` repo to verify Node.js ...
There's an open PR in docker-node to add Node.js 24.18.0 images, which was just submitted this morning. Once it merges and the official-images build pipeline runs, the node:24.18.0-bookworm image will be available on Docker Hub—typically within a few hours after the maintainers review it.
So the situation is clear: the patched Node 24.18.0 exists on nodejs.org, but the Docker image isn't ready yet. PR #2546 is open and imminent, so we can't upgrade right now, but it's coming very soon. I'm thinking the best approach would be to prepare an upgrade branch now with the Dockerfile update, Volta config, and @types/node bump to ^24, then open it as a draft PR. The image build check will fail until the base image is published, but once node:24.18.0-bookworm lands, we can verify the tag and merge immediately. I'm weighing whether to prep a draft PR now with the Volta and types/node bumps, or wait until the Docker image lands. ...
Here's the situation:
The patched Node 24 exists, but its Docker image doesn't yet.
node:24.18.0-bookworm Docker images are not published yet (verified 404 on the registry). docker-node's latest is still 22.23.1.24.17.0 — which is the broken release with this exact regression. So moving the Dockerfile to Node 24 right now would reintroduce the bug.node:24.18.0-bookworm becomes pullable — typically within hours to a day.So we can't flip the Docker base image today, but everything else (Volta, CI matrix already includes '24', @types/node) isn't gated on Docker Hub.
How do you want to proceed?
User has answered your questions: "The patched node:24.18.0 Docker image isn't published yet (PR #2546 pending). How do you want to handle the Node 24 upgrade?"="Prep draft PR now + remind to merge". You can now continue with the user's answers in mind.
I'm going to create a new branch and update the Node version from 22.23.1 to 24.18.0 across the Dockerfile and package.json, then bump the @types/node dependency to match the new major version, which will require regenerating the lockfile.
I should verify that the typecheck, build, and tests all pass locally before pushing the changes, since bumping @types/node could surface stricter type errors. Let me plan out the full sequence: create the branch, make the version updates, run pnpm install to update the lockfile, then validate everything compiles and tests pass before committing and pushing.
First I need to check my current git state and branch off master to start the work.
Let me set up the work and create the branch off master.
[
{
"content": "Create branch off master for Node 24 upgrade",
"status": "in_progress",
"priority": "high"
},
{
"content": "Update Dockerfile base images to node:24.18.0",
"status": "pending",
"priority": "high"
},
{
"content": "Update Volta node pin to 24.18.0 in package.json",
"status": "pending",
"priority": "high"
},
{
"content": "Bump @types/node to ^24 + pnpm install",
"status": "pending",
"priority": "medium"
},
{
"content": "Verify locally: typecheck, build, test",
"status": "pending",
"priority": "high"
},
{
"content": "Commit, push, open DRAFT PR",
"status": "pending",
"priority": "high"
},
{
"content": "Set reminder to verify image + flip PR to ready",
"status": "pending",
"priority": "medium"
}
]
?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/ === current branch === byk/fix/node-2223-premature-close === fetch + create branch === Switched to a new branch 'byk/feat/node-24' branch 'byk/feat/node-24' set up to track 'origin/master'. 9f13505 meta: Bump new development version
I'm checking out the byk/feat/node-24 branch which is tracking origin/master, and the latest commit is the post-release version bump after 2.26.12. The Dockerfile and package.json should now have Node 22.23.1 from my merged fix, so I'm verifying the current state on this fresh branch.
On byk/feat/node-24 tracking master (which includes the merged 22.23.1 fix). Let me confirm current values then apply the edits.
=== Dockerfile FROM lines === 1:FROM node:22.23.1-bookworm-slim AS builder 20:FROM node:22.23.1-bookworm === package.json volta + types/node === 29: "@types/node": "^22.10.1", 30: "@types/node-fetch": "^2.5.10", 93: "node": "22.23.1",
Confirmed at 22.23.1. Now applying the edits.
Edit applied successfully.
Edit applied successfully.
Edit applied successfully.
I'm updating the @types/node dependency to version 24 in package.json, then running pnpm install to refresh the lockfile before running typecheck, build, and tests to verify everything still works.
Now bump @types/node to ^24 to match the runtime major, then install + verify.
Edit applied successfully.
=== local node/pnpm === v24.18.0 10.28.0 === pnpm install (updates lockfile for @types/node ^24) === + esbuild 0.28.1 - shell-quote 1.7.3 + shell-quote 1.8.4 - tar 7.5.11 + tar 7.5.16 + vite 7.3.5 ╭ Warning ─────────────────────────────────────────────────────────────────────╮ │ │ │ Ignored build scripts: esbuild@0.28.1. │ │ Run "pnpm approve-builds" to pick which dependencies should be allowed │ │ to run scripts. │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯ Done in 15.6s using pnpm v10.28.0
My local environment is already on Node v24.18.0 and pnpm 10.28.0, but the install output is showing some unexpected dependency updates like shell-quote, tar, vite, and esbuild that seem disconnected from what I'm working on — these might be pre-existing mismatches between the lockfile and what's actually installed.
Actually, I think what's happening is my node_modules was in a stale state from a different branch, and pnpm is reconciling it to match the current lockfile. The "- / +" notation shows changes to the installed packages, not the lockfile itself. But here's what concerns me: the lockfile should already have tar 7.5.16 from the Dependabot PR, so why is it showing that update? I need to check the actual git diff on pnpm-lock.yaml to see what changed and make sure it's only @types/node-related updates, not a bunch of unrelated churn. Also, there's a version mismatch — Volta pins pnpm 10.27.0 but my local is 10.28.0, which could introduce lockfile format differences.
Note: my local Volta already resolved node v24.18.0 from the new pin. But pnpm ran as 10.28.0 while Volta pins 10.27.0 — I need to make sure the lockfile didn't get unrelated churn. Let me inspect the diff carefully.
=== changed files === M Dockerfile M package.json M pnpm-lock.yaml === lockfile diff stat === pnpm-lock.yaml | 60 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) === lockfileVersion line (ensure unchanged) === 1:lockfileVersion: '9.0' === lockfile diff (only @types/node expected) === - version: 7.3.5(@types/node@22.19.1)(tsx@4.21.0) + version: 7.3.5(@types/node@24.13.2)(tsx@4.21.0) - version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(vite@7.3.5(@types/node@22.19.1)(tsx@4.21.0)) + version: 4.1.8(@opentelemetry/api@1.9.0)(@types/node@24.13.2)(vite@7.3.5(@types/node@24.13.2)(tsx@4.21.0)) - '@types/node@22.19.1': + '@types/node@24.13.2': - '@types/node': 22.19.1 + '@types/node': 24.13.2 - '@types/node': 22.19.1 + '@types/node': 24.13.2 - '@types/node': 22.19.1 + '@types/node': 24.13.2 - '@types/node': 22.19.1 + '@types/node': 24.13.2 - ...
The lockfile changes look good—just a @types/node version bump from 22.19.1 to 24.13.2 with no lockfileVersion changes or unrelated churn. Now I'm running the typecheck, build, and test suite to make sure everything still works.
Clean lockfile diff — only @types/node 22.19.1 → 24.13.2, no version/format churn. Now verify typecheck, build, and tests.
=== typecheck === > @sentry/craft@2.27.0-dev.0 typecheck /home/byk/Code/getsentry/craft > tsc --noEmit EXIT typecheck: 0
Typecheck is passing with the latest @types/node, so I'll move on to running the build and test suite.
Typecheck passes. Now build and tests.
=== build === > @sentry/craft@2.27.0-dev.0 build /home/byk/Code/getsentry/craft > node build.mjs [build] SENTRY_AUTH_TOKEN not found, skipping source map upload EXIT build: 0 === smoke: run built binary === 2.27.0-dev.0
Build succeeds and the binary runs. Now the test suite.
=== test ===
[warn] [[target/github]] Release body exceeds GitHub limit (125001 > 125000 chars). Truncating and linking to full changelog.
[info] [[target/github]] Git tag: "1.0.0"
[warn] [[target/github]] Refusing to delete release "v1.0.0" because it is not a draft
[info] [dry-run] Would execute: github.repos.deleteRelease(...)
10:28:30 AM [vite] [33m[2m(ssr)[22m[39m [33mwarning: [33mThis assignment will throw because "checkExecutableIsPresent" is an import[33m
56 |
57 | test('config missing', () => {
58 | (checkExecutableIsPresent as MockedFunction<
| ^
59 | typeof checkExecutableIsPresent
60 | >) = vi.fn();
[39m
Plugin: [35mvite:esbuild[39m
File: [36m/home/byk/Code/getsentry/craft/src/targets/__tests__/symbolCollector.test.ts[39m
10:28:30 AM [vite] [33m[2m(ssr)[22m[39m [33mwarning: [33mThis assignment will throw because "checkExecutableIsPresent" is an import[33m
66 |
67 | test('symbol collector present and config ok', () ...
All the tests are passing across the board—1025 passed with just one skipped—and the warnings showing up are pre-existing issues unrelated to the Node version update. Everything checks out locally on Node 24.18.0 with typecheck, build, and test all passing. Now I'm moving on to update the todos.
I need to stage the right files for commit: Dockerfile, package.json, pnpm-lock.yaml, and .lore.md per repo policy. I'll be careful to exclude the untracked temp directories and .opencode folder. Let me verify AGENTS.md hasn't been modified before proceeding.
For the commit message, I'm going with a conventional commit format—something like "feat: upgrade Docker/Volta to Node 24.18.0"—and I'll include context in the body about moving from 22.23.1 to 24.18.0 LTS Krypton and the @types/node bump to ^24. Though I'm second-guessing whether this should be labeled as a "feat" or something more like a "chore" since it's really a build/infrastructure change rather than a user-facing feature. ...
All green on Node 24.18.0: typecheck pass, build pass, 1025 tests passed (1 skipped, pre-existing). Let me check the changelog prefix categories so I pick the right commit type, then verify no stray files before committing.
=== recognized changelog prefixes === 866: * - (?<type>...) - The prefix to strip from changelog entries 876: title: 'Breaking Changes 🛠', 888: title: 'New Features ✨', 889: commit_patterns: ['^(?<type>feat(?:\\((?<scope>[^)]+)\\))?!?:\\s*)'], 893: title: 'Bug Fixes 🐛', 895: '^(?<type>fix(?:\\((?<scope>[^)]+)\\))?!?:\\s*)', 904: commit_patterns: ['^(?<type>docs?(?:\\((?<scope>[^)]+)\\))?!?:\\s*)'], 910: '^(?<type>(?:build|refactor|meta|chore|ci|ref|perf|tests?|style)(?:\\((?<scope>[^)]+)\\))?!?:\\s*)', 1318: * - `(?<type>...)` - The type prefix to strip (e.g., `feat(scope):`)
I'm mapping out the commit message categories for versioning: new features get a minor bump, bug fixes get a patch bump, and documentation changes are separate. Build-related changes like Node base image upgrades fall into the grouped category with refactoring and other internal improvements, which typically trigger a patch bump or no version change at all.
Using build: as the prefix makes sense here since it's updating the Docker base image and Volta toolchain. The commit message will note the upgrade from Node 22.23.1 to 24.18.0 (LTS Krypton), explaining that both versions contain the node-fetch keep-alive fix and that this moves the project onto the current LTS major while bumping @types/node accordingly.
Before committing, I need to verify the working tree is clean—checking for any temporary directories or unwanted changes to AGENTS.md—then stage only the relevant files along with .lore.md.
build: is the right prefix (covers build/runtime changes). Let me verify no stray files and that .lore.md/AGENTS.md are clean before staging.
=== full status === M Dockerfile M package.json M pnpm-lock.yaml ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/ === diff stat (tracked) === Dockerfile | 4 ++-- package.json | 4 ++-- pnpm-lock.yaml | 60 +++++++++++++++++++++++++++++----------------------------- 3 files changed, 34 insertions(+), 34 deletions(-)
I've checked the status and only the Dockerfile, package.json, and pnpm-lock.yaml have changes—the temp directories and .opencode/ folder are untracked. Even though .lore.md hasn't been modified, the policy requires always staging it, so I'll include it along with the three modified files. Now I'm staging everything and preparing a commit message for the Node 24.18.0 LTS upgrade.
Clean — only the 3 intended files. Staging those plus .lore.md per repo policy, then committing.
M Dockerfile M package.json M pnpm-lock.yaml ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/ === committed === 3e2c7b6 build: upgrade to Node 24.18.0 (LTS Krypton)
I'll commit this change and push it up as a draft PR, with a note in the description that it's waiting on the Docker image from PR #2546 to be published before it can be merged.
Now push and open the draft PR with a clear note about the image dependency.
remote: https://github.com/getsentry/craft/security/dependabot remote: To https://github.com/getsentry/craft.git * [new branch] byk/feat/node-24 -> byk/feat/node-24 branch 'byk/feat/node-24' set up to track 'origin/byk/feat/node-24'.
https://github.com/getsentry/craft/pull/838
I've created the PR draft, so now I need to handle the follow-up tasks. Setting a reminder through the chat tool isn't quite right for tracking a PR merge—that's more for personal notifications within Beeper. The user mentioned they'd set a reminder to verify the Docker image landed and flip the PR to ready, but I'm realizing the reminder mechanism might not be the best fit for this workflow.
Let me check if the Docker image is actually available now instead of waiting. The PR was 404 just minutes ago, so it's unlikely to have changed, but it's worth verifying before deciding on next steps. I need to find the right chat to attach this reminder to. A self-chat or notes channel would be ideal so the reminder surfaces in a clean context, but I'm not certain what's available. The core issue is that the user explicitly chose the reminder option, so I should honor that rather than second-guessing whether to use the tool at all. ...
Draft PR #838 created. Now let me set up the reminder. Let me find an appropriate chat to attach it to (ideally a self/notes chat).
re-checking image availability one more time before setting reminder node:24.18.0-bookworm -> 404
The 404 error persists, so I'm looking for a self-chat or notes conversation in Beeper where the user might have saved the reminder.
Still 404 — confirms the hold. Let me find a good chat to anchor the reminder.
# Chats No chats found. # Using this information - Pass the "chatID" to get_chat or search_messages for details about a chat, or send_message to send a message to a chat. - Link the "open" link to the user to allow them to view the chat in Beeper Desktop.
I'm reconsidering how to set up the reminder mechanism. The beeper_set_chat_reminder needs a chatID, and without a dedicated notes chat, anchoring it to the Joshua DM where the Node issue is being discussed makes the most sense—it would resurface the relevant context when the reminder triggers. Though I need to be careful that setting a reminder there doesn't come across as actually messaging Joshua.
Actually, a Beeper chat reminder is private to the user, just a nudge that floats the chat to the top of the inbox at the set time without sending any message to the other party. So using the Joshua thread is reasonable and contextually relevant. When it fires, the user sees the Node saga thread and remembers to merge PR #838.
But I'm realizing the reminder tool doesn't have a text field—it only takes remindAtMs and dismissOnIncomingMessage—so it just resurfaces the chat without any custom note attached. ...
No dedicated craft/self chat exists. The Beeper reminder primitive just resurfaces a chat at a set time (no custom text), so I'll anchor it to the Joshua DM — the contextual home of this whole Node saga — for later today, and document clearly what it's for. Let me compute the time and set it.
now (UTC): Wed Jun 24 10:31:59 AM UTC 2026 now_ms=1782297119308 remind_ms=1782315119308 remind at (local): Wed Jun 24 03:31:59 PM UTC 2026
The system clock confirms everything is in UTC, so there's no timezone offset to worry about — the reminder should trigger at 15:31 UTC today, which is about five hours from now. Now I'm setting up the reminder for Joshua DM with the timestamp 1782315119308 and making sure it won't dismiss when new messages arrive.
Machine is on UTC. Setting the reminder for ~15:30 UTC today on the Joshua thread (no incoming-message dismissal so a stray message won't cancel it).
""
[
{
"content": "Create branch off master for Node 24 upgrade",
"status": "completed",
"priority": "high"
},
{
"content": "Update Dockerfile base images to node:24.18.0",
"status": "completed",
"priority": "high"
},
{
"content": "Update Volta node pin to 24.18.0 in package.json",
"status": "completed",
"priority": "high"
},
{
"content": "Bump @types/node to ^24 + pnpm install",
"status": "completed",
"priority": "medium"
},
{
"content": "Verify locally: typecheck, build, test",
"status": "completed",
"priority": "high"
},
{
"content": "Commit, push, open DRAFT PR",
"status": "completed",
"priority": "high"
},
{
"content": "Set reminder to verify image + flip PR to ready",
"status": "completed",
"priority": "medium"
}
]
All prepped. Summary:
Finding: The patched Node 24 (24.18.0, LTS Krypton, with the same #64004 fix) is out on nodejs.org, but its official Docker image isn't published yet — node:24.18.0-bookworm returns 404; the only Node 24 image on Docker Hub is the broken 24.17.0. docker-node PR #2546 (opened this morning) will publish it.
What I did — draft PR #838 (byk/feat/node-24 off master):
22.23.1 → 24.18.022.23.1 → 24.18.0@types/node ^22 → ^24 (lockfile diff clean: only @types/node 22.19.1 → 24.13.2, no lockfileVersion/format churn)Verified locally on Node 24.18.0: typecheck ✅, build ✅ (binary runs), tests ✅ 1025 passed / 1 skipped.
Reminder set: Beeper nudge at ~15:30 UTC today on the Joshua thread (won't auto-dismiss on incoming messages). When it fires — or whenever you get to it — the merge gate is:
node:24.18.0-bookworm is pullable (docker-node #2546 merged + built)image CI check on #838 (it'll stay red until the base image lands)The PR body documents all of this with the ⚠️ do-not-merge-yet note, so it's safe if anyone else stumbles on it.
One judgment call to flag: I anchored the reminder to the Joshua DM since there's no self/notes chat — it just resurfaces that thread (doesn't message him). Say the word if you'd rather I move it elsewhere or change the timing.
I think it's time
The user is signaling it's time to verify the Node 24.18.0 Docker image has been published and then move PR #838 to ready for merging. I need to check the image availability, the status of the docker-node PR #2546, and confirm the CI state for PR #838.
Let me check whether the image landed and where things stand.
=== node:24.18.0-bookworm availability === node:24.18.0-bookworm -> 200 node:24.18.0-bookworm-slim -> 200
The images are live (both → 200). Let me sync the branch with master and check the PR state before flipping it.
=== current branch ===
byk/feat/node-24
=== PR #838 state ===
{"base":"master","head":"byk/feat/node-24","isDraft":true,"mergeStateStatus":"BLOCKED","mergeable":"MERGEABLE","state":"OPEN"}
=== fetch master, check if behind ===
9f13505 meta: Bump new development version
--- commits on master not in branch ---
The branch is current with master and the PR is in draft status with checks blocked. I'll mark it ready for review to trigger the image CI check against the base image, then monitor the results.
Branch is current with master, mergeable. Images are live. Flipping PR #838 to ready, which triggers the CI checks (including image) against the now-available base image.
✓ Pull request getsentry/craft#838 is marked as "ready for review" === marked ready; current checks === image fail 20s https://github.com/getsentry/craft/actions/runs/28092249572/job/83172851267 Analyze (actions) pass 42s https://github.com/getsentry/craft/actions/runs/28092247818/job/83172849202 Analyze (javascript) pass 59s https://github.com/getsentry/craft/actions/runs/28092247818/job/83172849274 Build pass 18s https://github.com/getsentry/craft/actions/runs/28092249579/job/83172971591 Build Docs pass 24s https://github.com/getsentry/craft/actions/runs/28092249579/job/83172971577 Changelog Preview / Semver Impact pass 0 https://github.com/getsentry/craft/actions/runs/28092249584 Patch CodeQL pass 1s https://github.com/getsentry/craft/runs/83172953843 Lint fixes pass 28s https://github.com/getsentry/craft/actions/runs/28092249658/job/83172851580 Node 22 pass 38s https://github.com/getsentry/craft/actions/runs/28092249579/job/83172851410 Node ...
The image build job completed in 20 seconds, which is unusually fast — likely not a base image pull issue since those take longer. Something must have failed quickly, either a transient error or a problem with the Docker build context. Let me check the job logs to see what went wrong.
The image check failed in just 20s — too fast to be a real build failure. Let me inspect the log.
image UNKNOWN STEP 2026-06-24T10:31:04.5731774Z -------------------- image UNKNOWN STEP 2026-06-24T10:31:04.5745119Z 1 | >>> FROM node:24.18.0-bookworm-slim AS builder image UNKNOWN STEP 2026-06-24T10:31:04.5746266Z 2 | image UNKNOWN STEP 2026-06-24T10:31:04.5746685Z 3 | WORKDIR /usr/local/lib image UNKNOWN STEP 2026-06-24T10:31:04.5747169Z -------------------- image UNKNOWN STEP 2026-06-24T10:31:04.5748833Z ERROR: failed to build: failed to solve: node:24.18.0-bookworm-slim: failed to resolve source metadata for docker.io/library/node:24.18.0-bookworm-slim: docker.io/library/node:24.18.0-bookworm-slim: not found image UNKNOWN STEP 2026-06-24T10:31:04.5782491Z ##[error]Process completed with exit code 1. image UNKNOWN STEP 2026-06-24T10:31:04.6033456Z Post job cleanup. image UNKNOWN STEP 2026-06-24T10:31:04.6845692Z [command]/usr/bin/git version image UNKNOWN STEP 2026-06-24T10:31:04.6881265Z git version 2.54.0 image UNKNOWN STEP 2026-06-24T10:31:04.6921478Z ...
The image build failed because the Docker image wasn't fully propagated to all CDN edges when the GitHub Actions runner tried to pull it, even though it had just become available moments before.
The image build hit node:24.18.0-bookworm-slim: not found at 10:31:04Z — that's Docker Hub CDN propagation lag (the manifest is live on the edge I queried but the GH runner's pull raced it). This is transient. Re-running the failed image job.
=== rerun triggered; waiting ===
{"conclusion":"","status":"in_progress"}
The build is running now but hasn't confirmed completion yet, so I'll need to poll periodically to check when it finishes—these multi-architecture image builds typically take a while with all the dependencies involved.
Rerun is in progress. The craft image build is heavy (installs lots of tooling), so it'll take several minutes. Let me poll.
[10:34:15] in_progress / [10:34:45] in_progress / [10:35:16] in_progress / [10:35:47] in_progress / [10:36:18] in_progress / [10:36:49] in_progress / [10:37:20] in_progress / [10:37:51] in_progress / [10:38:21] in_progress / [10:38:52] completed / success
The image build succeeded on rerun (propagation lag confirmed). Let me confirm all checks are green and the PR is mergeable.
=== mergeability ===
{"isDraft":false,"mergeStateStatus":"BLOCKED","mergeable":"MERGEABLE","state":"OPEN"}
=== any non-pass checks? ===
(all checks passing)
The PR is blocked despite all checks passing, which usually means it's waiting for required review approval or a status check that hasn't reported yet. I'm checking the review decision and required status checks to understand the block — the user mentioned wanting adversarial subagent review before merging, so that might be what's holding it up.
All checks pass but mergeStateStatus is BLOCKED — likely needs an approving review. Let me check what's gating it.
{"reviewDecision":"REVIEW_REQUIRED","reviews":[]}
=== required status checks / protection ===
{"message":"Branch not protected","documentation_url":"https://docs.github.com/rest/branches/branch-protection#get-branch-protection","status":"404"}gh: Branch not protected (HTTP 404)
The PR has a ruleset requiring review approval, not classic branch protection. Since the user (BYK) is the author, they can't approve their own PR—someone else needs to review it, or an admin could bypass the requirement.
The user said "it's time" to merge, which suggests they want to proceed. There's a tension here: the lore directive says to request adversarial review before merging, but this is just a Node version bump with all CI passing and no logic changes to mutate. I'm weighing whether to ask for confirmation before using admin override to bypass the review gate, or to respect the user's decisive intent and merge directly. I think the clearest path forward is to ask the user directly whether they want me to admin-merge now (bypassing the review gate since CI is green and the change is trivial) or wait for a colleague's review per their repo policy. ...
All checks are green, branch is current with master, and the image build passed on rerun. The only thing gating merge is the repo's REVIEW_REQUIRED ruleset — there are no reviews yet, and GitHub won't let you approve your own PR.
This is a merge (effectively irreversible into master that auto-releases), and your repo policy requires a review, so I want your call on how to proceed.
User has answered your questions: "PR #838 is green on all checks but blocked by the REVIEW_REQUIRED ruleset. How do you want to merge?"="Admin-merge (squash) now". You can now continue with the user's answers in mind.
Admin-merging with squash.
=== post-merge state ===
{"mergeCommit":"175021eb484ecc22819b843483734d57d375f0e6","mergedAt":"2026-06-26T10:40:04Z","state":"MERGED"}
The merge is in, so now I need to clear that Beeper reminder and clean up my local branches since the work is done. I should also verify that the image build workflow triggers on master to publish the latest Node 24 version to the registry.
Merged — squash commit 175021eb. Now clearing the reminder (task done) and checking that the master merge kicked off the image build that publishes the Node-24 :latest.
""
fatal: 'master' is already used by worktree at '/home/byk/.local/share/opencode/worktree/55fa901aca64a01fa4b5c2abce1c85c1c27475e7/stellar-falcon' hint: invocation. fatal: Need to specify how to reconcile divergent branches. === master HEAD === 3e2c7b6 build: upgrade to Node 24.18.0 (LTS Krypton) 9f13505 meta: Bump new development version === latest image workflow run on master === 28232937002 in_progress/ sha=175021eb 2026-06-26T10:40:07Z 28088854224 completed/success sha=9f13505b 2026-06-24T09:28:04Z 28088851613 completed/success sha=4f7a76f0 2026-06-24T09:28:01Z
The git checkout master command failed because master is already checked out in another worktree, but I'm still on the byk/feat/node-24 branch so that's not a blocker. What matters is that the image workflow run 28232937002 for merge commit 175021eb is currently in progress on master — it's building and pushing the Node-24 image to ghcr.io/getsentry/craft:latest.
The Node 24 PR was a follow-up upgrade after the 22.23.1 fix was already released. The user said "I think it's time" to merge it, but they haven't asked for a craft release — they only asked to merge. The image workflow will publish to ghcr.io automatically, but pushing to Docker Hub's getsentry/craft:latest requires an actual craft release via the .craft.yml docker target. Since this is more of a "keep current" improvement rather than an urgent fix, I should report the status and offer to cut a release if they want the Node-24 image on Docker Hub.
Done. PR #838 is merged and the follow-up is rolling.
node:24.18.0-bookworm + -slim both 200 (docker-node #2546 landed).image build first hit a transient not found (Docker Hub CDN propagation lag racing the just-published manifest) — re-ran it and it passed.175021eb ("build: upgrade to Node 24.18.0 (LTS Krypton)").28232937002 is in_progress on master for 175021eb — this builds & pushes the Node-24 ghcr.io/getsentry/craft:latest.Master now runs Node 24.18.0 (LTS Krypton), which carries the same #64004 idle-socket fix as 22.23.1, so it stays clear of the ERR_STREAM_PREMATURE_CLOSE regression while moving onto the current LTS major.
One thing to decide: the image workflow only updates ghcr.io/...:latest. If you want the Node-24 image pushed to Docker Hub getsentry/craft:latest (what external consumers pull), that needs a craft release (the docker target in .craft.yml). The 22.23.1 fix already shipped and unblocked GCS publishing, so this isn't urgent — want me to cut a release for the Node-24 bump, or leave it for the next routine release?
Let's cut a new release
The user needs to cut a new release for the Node 24 bump, and craft handles this through its own release tooling. I'm trying to remember the exact workflow—there's a Release workflow that gets triggered, probably through GitHub Actions, which runs craft prepare and then creates a publish request. Let me look at the available workflows to see how to dispatch the release process.
Let me look at how craft cuts its own releases (the release workflow + current version) before triggering anything.
=== workflows ===
build.yml
changelog-preview.yml
docs-preview.yml
enforce-license-compliance.yml
image.yml
lint.yml
release.yml
=== current version on master ===
"version": "2.27.0-dev.0",
=== latest released tag ===
v2
2.26.10
2.26.9
2.26.8
2.26.7
=== latest GH release ===
{"createdAt":"2026-06-24T09:16:03Z","tagName":"2.26.12"}
The latest release is 2.26.12 from June 24th, though my local git tags are outdated and only show up to 2.26.10. The dev version is currently 2.27.0-dev.0. I need to check the release.yml workflow to understand how releases are triggered and what version I should use.
Latest release is 2.26.12 (the 22.23.1 fix); master is at 2.27.0-dev.0. Let me read the release workflow to trigger it correctly.
<path>/home/byk/Code/getsentry/craft/.github/workflows/release.yml</path>
<type>file</type>
<content>
1: name: Release
2: concurrency: ${{ github.workflow }}-${{ github.ref }}
3:
4: on:
5: # For Craft's own releases (dogfooding)
6: workflow_dispatch:
7: inputs:
8: version:
9: description: Version to release
10: required: true
11: default: 'auto'
12: force:
13: description: Force a release even when there are release-blockers (optional)
14: required: false
15:
16: # For external repos to call this workflow
17: workflow_call:
18: inputs:
19: version:
20: description: Version to release (semver, bump type, or "auto")
21: type: string
22: required: false
23: force:
24: description: Force a release even when there are release-blockers
25: type: string
26: required: false
27: default: 'false'
28: merge_target:
29: description: Target branch ...
The release workflow is triggered through workflow_dispatch on release.yml with a version input, which kicks off a build and then runs craft prepare to handle version bumping, branch creation, changelog commits, and publishing setup. This creates a publish request issue in the getsentry/publish repository, and the actual release publishes once that issue receives the appropriate label.
For this specific release, dispatching with 'auto' should resolve to version 2.26.13 since the only change since 2.26.12 is the Node 24 build bump, which counts as a patch-level change. The in-progress master image build doesn't block the release workflow since the image for the new version tag will be built fresh when the release branch is created.
The release process is two-stage: first I dispatch release.yml to run craft prepare (which bumps the version and creates a publish issue), then someone approves that issue with the 'accepted' label to trigger the actual publish step. ...
The release is the dogfooding workflow_dispatch on release.yml — it runs craft prepare (auto-computes the version, creates the release branch/tag, and opens a publish request). Since 2.26.12, the only change is the Node-24 build: bump (patch), so auto → 2.26.13.
Since 2.26.12 already updated Docker Hub getsentry/craft:latest to the fixed Node, the GCS publish path is no longer deadlocked. Dispatching the release with version=auto:
https://github.com/getsentry/craft/actions/runs/28233017090 === dispatched; waiting for run to register === 28233017090 in_progress/ workflow_dispatch 2026-06-26T10:41:57Z Release 28088119938 completed/success workflow_dispatch 2026-06-24T09:14:44Z Release 28021800167 completed/success workflow_dispatch 2026-06-23T11:08:02Z Release
Watching release run 28233017090 progress through its build and prepare stages, which will involve running craft once the build completes in a few minutes.
Release run 28233017090 is in progress (build → prepare). Let me watch it through completion.
[10:42:17] in_progress / [10:42:48] in_progress / [10:43:19] in_progress / [10:43:50] completed / success
The release run finished successfully, likely using a cached build. Now I'm checking the job outputs to see the resolved version and whether a publish issue was created in the getsentry/publish repo.
Release prepare succeeded. Let me get the resolved version and find the publish request it created.
=== resolved version (from prepare step log) ===
Release a new version
=== latest tags on remote ===
refs/tags/2.26.9
refs/tags/2.26.10
refs/tags/2.26.11
refs/tags/2.26.12
=== publish request issues (getsentry/publish) ===
failed to parse jq expression (line 1, column 87)
.[] | "#\(.number) [\(.state)] \(.createdAt) \(.title) labels=\([.labels[].name]|join(\",\"))"
^ unexpected token "\\"
The latest remote tags only go up to 2.26.12, so there's no new tag yet. The jq command for issues failed due to escaping issues, so I need to check the release run's prepare step log to see what version was resolved and what actually happened. I'm also wondering if the prepare step created a release branch and publish issue but the tag gets created during or after the publish phase, since that's how craft's workflow typically operates.
Let me look at the prepare step output to confirm the resolved version, check if a release branch was pushed, and examine the publish issue in getsentry/publish. I'll also fix the jq query and re-run it.
Let me read the prepare step log for the resolved version and find the publish issue (fixing the jq).
=== release branches on remote === 1e1e6a0e271e4fe960e8eaf20978eb067eb9efca refs/heads/itay/pre_release_target e724996ca4ee2ce84dc035eb407d4d6a4ecf8395 refs/heads/release/2.26.13 === publish issues === #8717 [OPEN] publish: getsentry/craft@2.26.13 | labels= #8671 [CLOSED] publish: getsentry/craft@2.26.12 | labels=accepted,ci-ready #8655 [CLOSED] publish: getsentry/craft@2.26.11 | labels=accepted,ci-ready #8622 [OPEN] publish: getsentry/arroyo@2.40.2 | labels=ci-failed #8525 [CLOSED] publish: getsentry/craft@2.26.10 | labels=accepted,ci-ready #8503 [CLOSED] publish: getsentry/craft@2.26.9 | labels=accepted,ci-ready
The release branch and publish request are ready, but I need to add the accepted label to issue #8717 to trigger the actual publish workflow, just like the previous releases had.
The user said "cut a new release" pretty clearly, which means they want it shipped. Adding accepted is the approval gate that publishes to npm, Docker Hub, and other registries — it's irreversible, but given their explicit directive and that the release prep is already done, I think proceeding is the right call. I'll add the label and then monitor the publish workflow.
Release prepare is done:
release/2.26.13 pushedThe publish is gated by the accepted label (same as prior releases 2.26.12/2.26.11). Since 2.26.12 already fixed Docker Hub getsentry/craft:latest, the GCS path won't deadlock. Adding accepted to trigger the actual publish:
https://github.com/getsentry/publish/issues/8717
=== label added; checking issue ===
{"labels":["accepted"],"state":"OPEN"}
I've added the accepted label, so the getsentry/publish workflow should now pick it up and run craft publish. Now I need to check on the publish workflow to see if it's started running.
accepted added — this triggers the publish workflow in getsentry/publish. Let me find and watch that run.
=== recent publish workflow runs (getsentry/publish) === 28233173204 in_progress/ CI Status Poller | CI Status Poller 28233163407 completed/success CI Status Poller | CI Status Poller 28233162723 completed/skipped Publish | publish: getsentry/craft@2.26.13 28233153098 completed/success Publish | publish: getsentry/craft@2.26.13 28233081805 completed/success auto-approve non-sdks | publish: getsentry/craft@2.26.13 28232965043 completed/success CI Status Poller | CI Status Poller
I see there's an "auto-approve non-sdks" workflow that already ran successfully on the 2.26.13 publish issue, which means the accepted label was likely added automatically before I manually added it—so my action was probably redundant. Now I'm looking at two separate "Publish" runs in the workflow history.
The first publish run (28233153098) shows as completed/success, while the second one (28233162723) was skipped—likely because my manual label addition triggered a re-run that got skipped due to concurrency controls. I need to verify that the actual publish succeeded by checking the issue state and confirming all targets were released (npm, GitHub release, etc.).
Note: an auto-approve non-sdks workflow already auto-accepted it (craft is a non-SDK), so my manual label was redundant — and a Publish run already completed. Let me verify the publish actually shipped all targets end-to-end.
=== publish issue #8717 state ===
{"labels":["accepted","ci-pending"],"state":"OPEN"}
=== npm latest ===
2.26.12
=== npm 2.26.13 published? ===
npm error code E404
=== GH release 2.26.13 ===
release not found
=== Publish run 28233153098 conclusion + timing ===
{"conclusion":"success","createdAt":"2026-06-26T10:45:00Z","status":"completed","updatedAt":"2026-06-26T10:45:15Z"}
The publish workflow completed too quickly—it looks like it only ran a gating check that detected the ci-pending status and stopped before actually publishing to npm. The issue is still marked as open with the accepted, ci-pending labels, and npm hasn't updated to version 2.26.13 yet, so we're waiting for CI to finish.
The CI Status Poller workflow is currently running and monitoring the release branch's build and image jobs. Once those complete successfully, the label will flip to ci-ready and the actual publish targets will execute—pushing to npm, GCS, the registry, Docker, GitHub, and gh-pages. The Docker image build with Node 24.18.0 takes about 8 minutes, so I need to wait for that to finish and then monitor the issue label until it transitions to ci-ready.
It's ci-pending — the publish waits for the release/2.26.13 branch CI (build + image) to go green before it actually ships targets. The 15s "Publish" run was just the gate check. Let me look at the release-branch CI and poll until it flips to ci-ready and publishes.
=== CI runs on release/2.26.13 (sha e724996) === 28233078982 image in_progress/ 28233079095 Build & Test completed/success
The release branch CI is progressing through its build and test phase successfully, with the Docker image currently building for version 2.26.13. Once that completes, the CI Status Poller will mark the issue as ready and trigger the Publish workflow. I'm going to monitor both the image build on the release branch and the issue label status until publishing finishes.
Release-branch Build & Test passed; the image build (Node-24 Docker image for 2.26.13) is still running (~8 min). Once it's green, the poller flips the issue to ci-ready and the real publish runs. Polling both.
[10:46:52] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:47:23] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:47:54] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:48:25] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:48:57] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:49:28] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:49:59] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:50:30] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:51:02] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:51:33] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:52:04] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:52:36] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:53:07] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:53:38] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:54:10] image=in_progress/ | issue=OPEN [accepted,ci-pending] [10:54:41] ...
The image build for release/2.26.13 succeeded and marked the issue as ci-ready, so the Publish workflow should now kick off to handle the actual publishing steps across npm, GCS, the registry, Docker, GitHub, and gh-pages. I'm going to track down the new Publish run and monitor it until the issue closes.
Image is green and the issue flipped to ci-ready — the real publish is now running craft publish across all targets. Let me watch it to completion.
cannot add: number (28233585122) and string (" ")
[10:55:23] publishRun= | issue=OPEN [accepted,ci-ready]
cannot add: number (28233585122) and string (" ")
[10:55:55] publishRun= | issue=OPEN [accepted,ci-ready]
cannot add: number (28233585122) and string (" ")
[10:56:26] publishRun= | issue=OPEN [accepted,ci-ready]
cannot add: number (28233585122) and string (" ")
[10:56:58] publishRun= | issue=CLOSED [accepted,ci-ready]
The issue #8717 is now closed with the same labels as previous successful releases, which confirms the publish went through. I should verify that the npm package 2.26.13, the GitHub release, and tag are all properly published and that the workflow completed successfully.
Issue #8717 is CLOSED with accepted,ci-ready — the same terminal state as prior successful releases. Let me verify the published artifacts to be sure.
=== npm ===
2.26.13
2.26.13 -> 2.26.13
=== GH release/tag ===
{"createdAt":"2026-06-26T10:43:16Z","isDraft":false,"tagName":"2.26.13"}
=== publish run result ===
28233704721 queued/ publish: getsentry/sentry-godot@2.0.0
28233585122 completed/success publish: getsentry/craft@2.26.13
The release 2.26.13 is fully deployed across all channels—npm package is marked latest, the GitHub release is published, the publish workflow succeeded, and the tracking issue is closed. Docker Hub now has the updated image with Node 24.18.0.
Release 2.26.13 is fully shipped. ✅
| Step | Result |
|------|--------|
| PR #838 (Node 24.18.0) | Merged to master (175021eb) |
| Release dispatch (auto) | → resolved 2.26.13 (patch, build: bump) |
| Release branch release/2.26.13 | Build & Test ✅, image ✅ (Node-24 Docker image) |
| Publish #8717 | Auto-approved → ci-ready → CLOSED success |
| npm @sentry/craft | 2.26.13 is now latest |
| GitHub release/tag | 2.26.13 published (not draft) |
| Publish run 28233585122 | completed/success — all targets (npm, gcs, registry, docker×2, github, gh-pages) |
Docker Hub getsentry/craft:latest is now Node 24.18.0 (LTS Krypton), which carries the #64004 idle-socket fix — so the ERR_STREAM_PREMATURE_CLOSE regression that started this whole thread is fully behind us, now on the current LTS major rather than the 22.x line.
No deadlock this time: 2.26.12 had already restored a working :latest, so the GCS target published cleanly.
One housekeeping note: your local checkout is still on the now-merged byk/feat/node-24 branch. Want me to delete the local + remote branch, or leave it?
let's clean up
I need to clean up after the merge — I'm currently on the feature branch and can't switch to master since it's already checked out in another worktree. GitHub might have already auto-deleted the remote branch if that setting is enabled, so I should verify that first before deciding whether to delete it manually or just switch to a detached HEAD state.
=== current branch === byk/feat/node-24 === local branches === byk/chore/prettier-reformat * byk/feat/node-24 byk/fix/actions-node24-deprecations byk/fix/changelog-code-blocks byk/fix/changelog-commit-without-prerelease byk/fix/node-2223-premature-close byk/fix/smol-toml-security ci/fix-node20-deprecation-warnings dependabot/npm_and_yarn/docs/astro-6.1.6 dependabot/npm_and_yarn/tar-7.5.3 docs/improve-registry-target-docs docs/improve-registry-target-docs-v2 feat/actions-publish-repo-self + feat/ci-ready-signal feat/improve-onboarding feat/internal-changes-commit-types feat/npm-oidc-trusted-publishing feat/security-commit-prefix fix/action-artifact-name fix/bump-brace-expansion fix/calver-disable-publish-issue-mentions fix/changelog-preview-fork-prs fix/dependabot-security-alerts fix/dependabot-tootallnate-once fix/dependency-vulnerabilities + fix/deps-security-advisories fix/existing-publish-issue-detection ...
The remote branches have already been cleaned up on merge, so I just need to handle the local cleanup. I'm currently on byk/feat/node-24 but can't switch to master since it's already checked out in another worktree, so I'll need to find an alternative branch to switch to before deleting this one.
Looking at the branch list, I can see that byk/feat/node-24 and byk/fix/node-2223-premature-close are both squash-merged PRs that are fully shipped and safe to delete. Since the remote branches are already gone, I'll detach HEAD and force-delete both branches locally with -D since squash merges aren't recognized as merged by the standard -d flag.
Both remote branches were already auto-deleted on merge. Locally I just need to drop the two node branches from this thread (byk/feat/node-24 = PR #838, byk/fix/node-2223-premature-close = PR #837). I'll detach HEAD first since master is held by another worktree, then delete them (force, since squash-merges aren't recognized as merged by -d).
detached at 175021e Deleted branch byk/feat/node-24 (was 3e2c7b6). Deleted branch byk/fix/node-2223-premature-close (was adc20c8). === verify gone === (both deleted)
Both branches are deleted and I'm detached at the commit now—that's fine. The cleanup worked. I'm noticing there are some untracked scratch directories lying around like .opencode/, temp_rust/, and a few other temp folders from earlier work. The .opencode/ directory has the plan file so I should probably keep that, but those temp directories are just clutter. Since the user mentioned cleaning up, I should check what's actually in git to decide whether to remove them.
Both merged branches deleted; HEAD is detached at the merge commit 175021e. Let me check the working tree for leftover scratch before calling it done.
=== git status === M .lore.md ?? .opencode/ ?? temp_rust/ ?? temp_single_update/ ?? temp_update_sha/ ?? temp_work/ === untracked dirs sizes === temp_rust -> 276K temp_single_update -> 499M temp_update_sha -> 52M temp_work -> 537M .opencode -> 57M
I'm noticing several temporary directories consuming a lot of space—temp_single_update and temp_work are particularly large at nearly 500MB each—though I should preserve the .opencode directory since it stores important plans. I also see that .lore.md has uncommitted changes, but since I'm not making a commit right now, I don't need to stage it yet.
The temp_* directories are clearly scratch work from earlier sessions, and while deleting ~1GB of untracked files is irreversible, the user's request to clean up suggests removing them is the right call. I should probably ask for confirmation before removing them, though the naming convention makes it pretty clear they're disposable.
Branch cleanup is done. While checking, I found ~1.1 GB of untracked scratch dirs left over (not created in this thread):
| Dir | Size | |
|-----|------|--|
| temp_work/ | 537M | scratch |
| temp_single_update/ | 499M | scratch |
| temp_update_sha/ | 52M | scratch |
| temp_rust/ | 276K | scratch |
| .opencode/ | 57M | opencode's working dir (plans, etc.) — keep |
Also: .lore.md shows as modified — that's the background lore daemon, I'll leave it untouched (not committing).
The four temp_* dirs look like disposable scratch, but since I didn't create them and rm is irreversible, I want your OK before deleting.
User has answered your questions: "Delete the ~1.1 GB of untracked temp_* scratch dirs?"="Delete all four temp_* dirs". You can now continue with the user's answers in mind.
=== git status === M .lore.md ?? .opencode/ === disk freed; remaining untracked === ?? .opencode/
Cleanup complete.
byk/feat/node-24 and byk/fix/node-2223-premature-close (both remotes were already auto-deleted on merge). HEAD detached at merge commit 175021e.temp_work, temp_single_update, temp_update_sha, temp_rust → ~1.1 GB freed..opencode/ (intentionally kept) and .lore.md (background daemon — left untouched, not committing).That wraps the whole thread: Node 24.18.0 merged → craft 2.26.13 released across all targets → Docker Hub getsentry/craft:latest now on the patched LTS → branches and scratch cleaned up.
Date: June 26, 2026 * 🟡 (11:05) Cleanup: remote branch `byk/feat/node-24` was already auto-deleted on merge; no manual remote deletion needed * 🟡 (11:05) HEAD detached at merge commit `175021e` (master held by another worktree, so couldn't switch) * 🟡 (11:05) Local branches deleted: `byk/feat/node-24` (was 3e2c7b6) and `byk/fix/node-2223-premature-close` (was adc20c8); both force-deleted with …
<observations> Date: June 26, 2026 * 🟡 (10:32) User signaled it was time to proceed with Node 24 upgrade merge; assistant checked node:24.18.0-bookworm and node:24.18.0-bookworm-slim availability — both returned 200 (docker-node PR #2546 had landed) * 🟡 (10:33) PR #838 state at flip: isDraft=true, mergeStateStatus=BLOCKED, mergeable=MERGEABLE, state=OPEN; branch byk/feat/node-24 current with ma…
<observations> Date: June 24, 2026 * 🟡 (09:12) image workflow run ID 28087538845 was in_progress (started 09:04:18Z, ~8min) for commit "fix: pin Node to 22.23.1 to avoid node-fetch premature-close regressi…" on master — building/pushing ghcr.io/getsentry/craft:latest * 🟡 (10:21) User decided to explore upgrading craft from Node 22.23.1 to Node 24 with patched version * 🟡 (10:21) Dockerfile con…
Date: June 24, 2026 * 🟡 (09:12) Agent confirmed `publishToTarget` (src/commands/publish.ts lines 179–207) awaits `target.publish(version, revision)` via `withTracing`; throws on failure, causing fail-fast sequential abort — no state written for failed or subsequent targets * 🟡 (09:12) Agent confirmed publish loop is sequential + fail-fast in config order: gcs failure aborts before docker target…
<observations> Date: June 24, 2026 * 🟡 (09:11) Agent examined `src/commands/publish.ts` lines 740–809: publish loop at line 757 iterates `targetList` sequentially; calls `publishToTarget(target, newVersion, revision)` per target (line 758); updates `publishState.published[BaseTarget.getId(target.config)] = true` after each success (line 759); writes state to `publishStateFile` via `safeFs.writeF…
Date: June 24, 2026 * 🟡 (09:09) Agent examined `src/commands/publish.ts` lines 255–299: `getTargetList()` defined at line 260; target loop at line 267 iterates `targetConfigList`; missing target implementation logs warning and `continue`s (lines 270–272); target instantiation errors at line 283 log error and re-throw (lines 284–285); function returns `targetList` at line 289
<observations> Date: June 24, 2026 * 🟡 (09:09) Agent ran grep search returning 81 matches across files for error/catch/throw patterns in `src/commands/`; key files: `publish.ts`, `prepare.ts`, `changelog.ts`, `artifacts_cmds/list.ts`, `artifacts_cmds/download.ts`, `__tests__/publish.test.ts`, `__tests__/prepare.test.ts`, `__tests__/changelog-versioning-policy.test.ts` * 🟡 (09:09) Agent identifi…
Date: June 24, 2026 * 🟡 (09:07) Agent retrieved directory listing of `src/commands/` — contains: `artifacts.ts`, `changelog.ts`, `config.ts`, `prepare.ts`, `publish.ts`, `targets.ts`, `__tests__/`; publish command files: `src/commands/publish.ts` and `src/commands/__tests__/publish.test.ts`
<observations> * 🟡 (09:06) Agent read `.craft.yml` (55 lines) — craft publishes itself via: npm → gcs (bucket: `sentry-sdk-assets`, paths `/craft/{{version}}/` and `/craft/latest/`) → registry (`app:craft`, sha256 hex checksums, urlTemplate `https://downloads.sentry-cdn.com/craft/{{version}}/{{file}}`) → docker×2 (source: `ghcr.io/getsentry/craft`, target: `getsentry/craft` + `getsentry/craft:la…
<observations> Date: June 24, 2026 * 🔴 (08:41) User approved plan at `.opencode/plans/1782290100699-sunny-mountain.md` and switched to build agent mode * 🟡 (08:42) Dockerfile edits applied: line 1 `node:22-bookworm-slim` → `node:22.23.1-bookworm-slim`; line 20 `node:22-bookworm` → `node:22.23.1-bookworm` * 🟡 (08:44) Dockerfile verified post-edit: line 1 `FROM node:22.23.1-bookworm-slim AS buil…
<observations> Date: June 24, 2026 * 🟡 (08:39) Dockerfile confirmed: uses floating base images `node:22-bookworm-slim` (line 1, builder stage) and `node:22-bookworm` (line 20, final stage) — no pinned patch version; this is the root cause of the incident * 🟡 (08:39) craft package.json: name `@sentry/craft`, version `2.26.8`; Volta config pins `node: 22.12.0`, `pnpm: 10.27.0`; Volta pin predates…
<observations> Date: June 24, 2026 * 🟡 (08:37) Assistant identified root cause of craft CI failure: Docker image running Node `gl-node/22.23.0` (buggy release with CVE-2026-48931 security patch) triggering `ERR_STREAM_PREMATURE_CLOSE`; fix available in v22.23.1 (PR #64004 backport) * 🟡 (08:37) Assistant noted discrepancy: craft's Dockerfile appears to use a floating base image rather than pinne…
Date: June 24, 2026 * 🟡 (08:37) PR #64004 ("http: avoid stream listeners on idle agent sockets") confirmed MERGED into nodejs:main on Jun 20, 2026; authored by @mcollina; 1 commit (hash `0d62f61c827062070b88e5ffa50f441026b0d1a0`, force-pushed from `8d8919c`); signed-off by Matteo Collina <hello@matteocollina.com> * 🟡 (08:37) PR #64004 root-cause description (from @mcollina): "The response-queue…
Date: June 24, 2026 * 🟡 (08:35) Node.js issue #63989 details confirmed: `ERR_STREAM_PREMATURE_CLOSE` thrown by `node-fetch@2.7.0` via `gaxios@6.7.1` on keep-alive socket reuse after Node.js 24.17.0 security release * 🟡 (08:35) Issue #63989 root cause: Node 24.17.0 changelog entry "http: fix response queue poisoning in `http.Agent`" (plus TLS/SNI changes) altered keep-alive socket reuse timing, …
Date: June 24, 2026 * 🔴 (08:35) User reported a failure when publishing to GCS in the getsentry/publish GitHub Actions workflow: https://github.com/getsentry/publish/actions/runs/28084819405/job/83148110750 * 🔴 (08:35) User suspects the GCS publish failure is caused by Node.js issue https://github.com/nodejs/node/issues/63989 * 🔴 (08:35) User preference: upgrade to a patched Node version rathe…