You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .claude/skills/release/SKILL.md
+163-4Lines changed: 163 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -42,6 +42,61 @@ git fetch origin main
42
42
43
43
If not on dev or working tree is dirty, abort with a clear message.
44
44
45
+
### Step 1.5: Pre-flight compiled-binary smoke test (MANDATORY before any other step)
46
+
47
+
> **Why this is first**: releases have ended up with zero working binaries because a module-init crash or bundler bug only surfaces in `bun build --compile` output, not in `bun run`. CI catches it — but only AFTER the tag is pushed and a GitHub Release is created. By then the damage (empty release, broken `releases/latest`, broken `install.sh`) is already live. Failing here, before any user-visible change, keeps the blast radius at "no release was cut."
48
+
49
+
Run locally on the native target. This takes ~15-30s and is cheaper than discovering the problem after tag+release.
50
+
51
+
```bash
52
+
# Guard: only run this for Node/Bun projects with a CLI entry point + build-binaries script.
53
+
if [ -f scripts/build-binaries.sh ] && [ -f packages/cli/src/cli.ts ];then
54
+
TMP_BINARY=$(mktemp)
55
+
trap"rm -f $TMP_BINARY" EXIT
56
+
57
+
# Compile for the native target only (not full cross-compile — that's CI's job).
58
+
# Match the real release flags so any bundler quirk reproduces locally.
59
+
bun build \
60
+
--compile \
61
+
--minify \
62
+
--target=bun \
63
+
--outfile="$TMP_BINARY" \
64
+
packages/cli/src/cli.ts
65
+
66
+
# Smoke test: the binary must start and exit 0 on a safe, non-interactive command.
67
+
# `version` or `--help` are both acceptable — pick one that does NOT touch the
68
+
# network, database, or require env vars.
69
+
if!"$TMP_BINARY" version > /tmp/archon-preflight.log 2>&1;then
70
+
echo"ERROR: compiled binary crashed at startup"
71
+
cat /tmp/archon-preflight.log
72
+
echo""
73
+
echo"This usually means a dependency has a module-init-time side effect that"
74
+
echo"fails in a compiled binary context (readFileSync of a path that only"
75
+
echo"exists in node_modules, etc.). Fix before cutting the release — do NOT"
76
+
echo"proceed to version bump."
77
+
exit 1
78
+
fi
79
+
80
+
# Also grep for known crash markers that exit 0 but print a fatal error
81
+
# (some module-init errors are caught by top-level try/catch but still log).
82
+
if grep -qE "Expected CommonJS module|TypeError:|ReferenceError:|SyntaxError:" /tmp/archon-preflight.log;then
83
+
echo"ERROR: compiled binary emitted a runtime error despite exit 0"
84
+
cat /tmp/archon-preflight.log
85
+
exit 1
86
+
fi
87
+
88
+
echo"Pre-flight binary smoke: PASSED"
89
+
fi
90
+
```
91
+
92
+
If this fails, **abort the release entirely** — do not bump version, do not modify CHANGELOG, do not create a PR. Surface the error to the user, point at the failing output, and stop. Recovery is: fix the bundler / dependency issue on a feature branch, merge to dev, re-run `/release`.
93
+
94
+
**Common failure modes this catches:**
95
+
- Bun `--bytecode` flag producing broken bytecode for the current module graph
96
+
- A dependency (e.g. an SDK) reading `package.json` or other files at module top level via paths that resolve fine in `node_modules/` but not next to a compiled binary
97
+
- Circular imports that break under minification but work under plain `bun run`
98
+
- A newly added package that ships CJS with an unusual wrapper shape
99
+
45
100
### Step 2: Detect Stack and Current Version
46
101
47
102
Detect the project's package manager and version file:
@@ -211,21 +266,53 @@ After the tag is pushed, `.github/workflows/release.yml` builds platform binarie
211
266
212
267
```bash
213
268
echo"Waiting for release workflow to finish uploading binaries..."
echo"The release workflow failed on two consecutive runs. This is NOT a flake."
306
+
echo"The tag and release exist but have no (or incomplete) assets."
307
+
echo""
308
+
echo"install.sh and similar 'releases/latest' paths are now 404-ing."
309
+
echo"Proceeding with Homebrew/tap sync would publish a formula pointing at"
310
+
echo"missing or inconsistent binaries."
311
+
echo""
312
+
echo"Jump to the 'Recovery: deterministic release CI failure' section at the"
313
+
echo"bottom of this skill and execute it. Do NOT continue past this point."
314
+
exit 1
315
+
fi
229
316
fi
230
317
```
231
318
@@ -376,9 +463,81 @@ Also run `/test-release curl-mac x.y.z` to cover the curl install path. The two
376
463
377
464
If you have a VPS available, also run `/test-release curl-vps x.y.z <vps-target>` to verify the Linux binary.
378
465
466
+
## Recovery: deterministic release CI failure
467
+
468
+
Reached here because Step 10 detected two consecutive workflow failures. The tag `vx.y.z` is pushed, the GitHub release exists, but assets are missing or incomplete. Every `install.sh` run currently resolves `releases/latest` to this broken release and 404s on download. Homebrew users are safe because Step 10's atomic formula update was blocked.
469
+
470
+
**Do not re-run the release workflow a third time hoping it succeeds.** If the failure was reproducible twice, it's a code bug — you need to ship code to fix it.
471
+
472
+
### Immediate mitigation (restore `install.sh`)
473
+
474
+
Delete the GitHub Release so `releases/latest` falls back to the previous version. Keep the git tag — tag immutability matters and there are no shipped artifacts pointing at it anyway.
475
+
476
+
```bash
477
+
gh release delete "vx.y.z" --yes
478
+
# Do NOT delete the tag:
479
+
# git push --delete origin vx.y.z ← do not run
480
+
# Tag stays so git history records the attempt; no release means no assets
481
+
# means releases/latest resolves to the prior working release.
482
+
```
483
+
484
+
Verify:
485
+
486
+
```bash
487
+
gh api repos/coleam00/Archon/releases/latest --jq '.tag_name'
488
+
# should now print the prior version (e.g. v0.3.6), not vx.y.z
489
+
```
490
+
491
+
### Diagnose
492
+
493
+
The release workflow logs tell you which target failed and at what stage (compile vs. smoke-test vs. upload):
494
+
495
+
```bash
496
+
gh run list --workflow release.yml --limit 2 --json databaseId,conclusion
497
+
gh run view <RUN_ID> --log-failed
498
+
```
499
+
500
+
Common causes:
501
+
-**Bundler/bytecode bug** — Bun `--bytecode` produces invalid output for the current module graph. Symptom: `TypeError: Expected CommonJS module to have a function wrapper` at binary startup. Historically caused by a new dependency's CJS/ESM shape interacting with `--bytecode` — dropping the flag or lazy-importing the offending module has been the fix.
502
+
-**Module-init crash** — a dependency does `readFileSync('package.json')` or similar at module top level via a path that exists in `node_modules/` but not next to a compiled binary. Symptom: every binary subcommand crashes immediately; error typically mentions a missing file adjacent to `process.execPath`. Fix by lazy-importing the dependency behind the code path that actually uses it.
503
+
-**Smoke-test timeout on Windows** — not actually a bug in the code; the Windows runner is slow. Rerun once; if it recurs, bump the test timeout.
504
+
505
+
Step 1.5 now runs a local compiled-binary smoke test before any user-visible step. If the failure mode above reproduces locally, you've found it. If it doesn't, the bug is platform-specific (Windows cross-compile, Linux glibc, etc.) and you need the CI logs.
506
+
507
+
### Fix and re-release as the NEXT patch
508
+
509
+
**Do not reuse `vx.y.z`.** Cut `vx.y.(z+1)` (or next-minor if warranted) with the fix. Rationale:
510
+
- Tag immutability: `vx.y.z` is already recorded in git history and release cache
511
+
- Semver clarity: users and tooling should see a new version number when the bits change
512
+
- Audit trail: "v0.3.7 was cut but had no shipped binaries; v0.3.8 is the first release with <fix>" is cleaner than rewriting v0.3.7
513
+
514
+
Steps:
515
+
516
+
1. Cut a fix branch off dev, implement the fix, PR to dev, merge.
517
+
2. Re-run `/release` (it will bump to the next patch — e.g. `0.3.8` — automatically).
518
+
3. Step 1.5's pre-flight smoke will catch the same bug locally if the fix didn't actually fix it. Iterate until it passes before tagging.
519
+
520
+
### CHANGELOG note for the hotfix release
521
+
522
+
Include a line in the new release's CHANGELOG that references the broken prior version so users understand why there's no binary artifact under that tag:
523
+
524
+
```markdown
525
+
### Fixed
526
+
527
+
-**First release with working compiled binaries after vx.y.z's <bug>.** vx.y.z was tagged but its binary smoke test failed deterministically (see RUN_ID in CI history). The tag is preserved for history; this release (vx.y.(z+1)) is the first with shipped binaries. `install.sh` and Homebrew were never updated to vx.y.z, so users were not exposed to the broken state.
528
+
```
529
+
530
+
### What NOT to do
531
+
532
+
-**Do not force-push or rewrite the tag.** Once a tag exists, it's a public promise of that SHA. Deleting and re-creating to a different SHA is tag-spoofing and breaks any downstream that cached the original.
533
+
-**Do not skip this recovery path to "just push more binaries to the broken release".** The release exists with a specific commit SHA; uploading binaries built from a newer SHA creates binary/source drift that is hard to diagnose later.
534
+
-**Do not update the Homebrew formula before v0.3.(z+1) is fully shipped.** The formula should always point at a version with all 7 assets uploaded and `/test-release brew` passing.
535
+
379
536
## Important Rules
380
537
381
538
- NEVER force push
539
+
-**NEVER skip Step 1.5 (pre-flight compiled-binary smoke).** If the stack is a Bun/Node project with a build-binaries script, the `bun build --compile` smoke test runs before version bump, PR, or tag. Skipping it means every bundler regression or module-init crash only surfaces after the tag is pushed — by which point `releases/latest` is already 404-ing for every user. The ~30s cost is paid to keep the failure mode local.
540
+
- If Step 1.5 fails, **abort the release** and fix the underlying issue on a feature branch. Do not "just skip it" and hope CI doesn't repro the problem.
382
541
- NEVER skip the review step — always show the changelog before committing
383
542
- NEVER include "Co-Authored-By: Claude" or any AI attribution in the commit
384
543
- NEVER add emoji to changelog entries unless the user asks
Copy file name to clipboardExpand all lines: CHANGELOG.md
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
## [0.3.8] - 2026-04-22
11
+
12
+
Hotfix for v0.3.7 — restore working compiled binaries. v0.3.7 was tagged but never shipped any working assets: two distinct bugs (Pi SDK's module-init `package.json` read, and Bun `--bytecode` producing broken output for this project's module graph) made every compiled archon binary crash at startup. The v0.3.7 GitHub Release was deleted immediately (the tag remains for history); v0.3.8 is the first release with working `archon-{darwin,linux}-{arm64,x64}` and `archon-windows-x64.exe` binaries since v0.3.6. Homebrew and `install.sh` were never updated to v0.3.7, so users were not exposed to the broken state.
13
+
14
+
### Fixed
15
+
16
+
- **Compiled archon binaries no longer crash at startup when the Pi provider is bundled.** `@mariozechner/pi-coding-agent/dist/config.js` runs `readFileSync(getPackageJsonPath(), 'utf-8')` at module top-level, which inside a compiled binary resolves to `dirname(process.execPath) + '/package.json'` — a path that doesn't exist next to `/usr/local/bin/archon`, making every archon command (including `archon version`) crash with ENOENT before it ran. The Pi SDK and all Pi-dependent helper modules are now dynamically imported inside `PiProvider.sendQuery()`; registering Pi and instantiating the provider no longer touches Pi's module-init side effects. A regression test (`provider-lazy-load.test.ts`) walks the same `registerCommunityProviders()` + `getAgentProvider('pi')` path the CLI and server take and asserts neither SDK package was resolved. Claude and Codex providers keep their static import style — their SDKs have no equivalent module-init side effect. Unblocks the v0.3.7 release binaries that could not ship because of this bug. (#1355)
17
+
-**Release binary compile no longer silently produces broken bytecode.**`scripts/build-binaries.sh` dropped the `--bytecode` flag: Bun 1.3.11's bytecode step failed with `Failed to generate bytecode for ./cli.js` against the 0.3.7 module graph and fell through to producing a binary that crashed at module instantiation with "Expected CommonJS module to have a function wrapper". Windows was already excluded; this removes the flag everywhere. Release parity preserved via `--minify`. (#1354)
18
+
10
19
## [0.3.7] - 2026-04-22
11
20
12
21
Pi community provider, home-scoped workflows/commands/scripts, worktree policy, Web UI approval-gate auto-resume, three-path env model, and a breaking change to Claude Code binary resolution for compiled binary users.
0 commit comments