Skip to content

Commit 8697508

Browse files
authored
Merge pull request #1356 from coleam00/dev
Release 0.3.8
2 parents 48c81d3 + e646cd4 commit 8697508

18 files changed

Lines changed: 310 additions & 54 deletions

File tree

.claude/skills/release/SKILL.md

Lines changed: 163 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,61 @@ git fetch origin main
4242

4343
If not on dev or working tree is dirty, abort with a clear message.
4444

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+
45100
### Step 2: Detect Stack and Current Version
46101

47102
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
211266

212267
```bash
213268
echo "Waiting for release workflow to finish uploading binaries..."
269+
WORKFLOW_FAILED=0
214270
for i in {1..30}; do
215271
ASSET_COUNT=$(gh release view "vx.y.z" --repo coleam00/Archon --json assets --jq '.assets | length')
216272
# Expect 7 assets: 5 binaries (darwin-arm64, darwin-x64, linux-arm64, linux-x64, windows-x64.exe) + archon-web.tar.gz + checksums.txt
217273
if [ "$ASSET_COUNT" -ge 7 ]; then
218274
echo "All $ASSET_COUNT assets uploaded"
219275
break
220276
fi
277+
278+
# Short-circuit: if the release workflow itself has failed, stop waiting.
279+
# Hanging for 15 min when CI already crashed just delays the recovery path.
280+
WORKFLOW_STATUS=$(gh run list --workflow release.yml --event push --limit 1 --json conclusion,status --jq '.[0] | "\(.status)|\(.conclusion)"')
281+
if [[ "$WORKFLOW_STATUS" == "completed|failure" ]]; then
282+
echo "Release workflow FAILED — no point waiting longer"
283+
WORKFLOW_FAILED=1
284+
break
285+
fi
286+
221287
echo " Assets so far: $ASSET_COUNT/7 — waiting 30s (attempt $i/30)..."
222288
sleep 30
223289
done
224290

225-
if [ "$ASSET_COUNT" -lt 7 ]; then
226-
echo "ERROR: Release workflow did not finish uploading assets after 15 minutes"
227-
echo "Check https://github.com/coleam00/Archon/actions for the release workflow run"
228-
exit 1
291+
if [ "$WORKFLOW_FAILED" -eq 1 ] || [ "$ASSET_COUNT" -lt 7 ]; then
292+
# Triage: rerun once in case it's transient, then check again.
293+
RUN_ID=$(gh run list --workflow release.yml --event push --limit 1 --json databaseId --jq '.[0].databaseId')
294+
echo "Release workflow failed on run $RUN_ID. Rerunning failed jobs once to confirm..."
295+
gh run rerun "$RUN_ID" --failed
296+
gh run watch "$RUN_ID" --exit-status --interval 30 || true
297+
298+
# Re-check asset count + run status after rerun.
299+
ASSET_COUNT=$(gh release view "vx.y.z" --repo coleam00/Archon --json assets --jq '.assets | length')
300+
if [ "$ASSET_COUNT" -ge 7 ]; then
301+
echo "Rerun succeeded — all assets now present"
302+
else
303+
echo ""
304+
echo "===== DETERMINISTIC CI FAILURE ====="
305+
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
229316
fi
230317
```
231318

@@ -376,9 +463,81 @@ Also run `/test-release curl-mac x.y.z` to cover the curl install path. The two
376463

377464
If you have a VPS available, also run `/test-release curl-vps x.y.z <vps-target>` to verify the Linux binary.
378465

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+
379536
## Important Rules
380537

381538
- 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.
382541
- NEVER skip the review step — always show the changelog before committing
383542
- NEVER include "Co-Authored-By: Claude" or any AI attribution in the commit
384543
- NEVER add emoji to changelog entries unless the user asks

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

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+
1019
## [0.3.7] - 2026-04-22
1120

1221
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.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "archon",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"private": true,
55
"workspaces": [
66
"packages/*"

packages/adapters/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/adapters",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/index.ts",
66
"types": "./src/index.ts",

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/cli",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/cli.ts",
66
"bin": {

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/core",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/index.ts",
66
"types": "./src/index.ts",

packages/docs-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/docs-web",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"private": true,
55
"scripts": {
66
"dev": "astro dev",

packages/git/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/git",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/index.ts",
66
"types": "./src/index.ts",

packages/isolation/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/isolation",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/index.ts",
66
"types": "./src/index.ts",

packages/paths/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@archon/paths",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"type": "module",
55
"main": "./src/index.ts",
66
"types": "./src/index.ts",

0 commit comments

Comments
 (0)