diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96a144d..549611d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true env: - BUN_VERSION: 1.3.10 + BUN_VERSION: 1.3.14 jobs: package-smoke: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e37907..57f8057 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ concurrency: cancel-in-progress: false env: - BUN_VERSION: 1.3.10 + BUN_VERSION: 1.3.14 jobs: preflight: diff --git a/CHANGELOG.md b/CHANGELOG.md index cc2416c..d19d033 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ See [RELEASING.md](./RELEASING.md) for the release process and version-bump poli ### Fixed +- Standalone release builds now disable Bun identifier minification to avoid runtime name-collision crashes ([#36](https://github.com/Noumena-Network/code/issues/36)). - Native `sharp` embedding build for macOS and other non-Linux targets ([#1](https://github.com/Noumena-Network/code/pull/1)) - Tool-call cancellation reason text on parallel tool cancellation ([#13](https://github.com/Noumena-Network/code/pull/13)) - NCode config and credentials are now isolated from Claude Code state on disk ([#11](https://github.com/Noumena-Network/code/pull/11)) diff --git a/build/build.mjs b/build/build.mjs index 93a051d..0e13d1f 100644 --- a/build/build.mjs +++ b/build/build.mjs @@ -19,9 +19,18 @@ const outFile = path.join(outDir, 'cli.js'); const bundledEntryFile = path.join(outDir, 'src', 'entrypoints', 'cli.js'); const bundledEntryMapFile = `${bundledEntryFile}.map`; const outMapFile = `${outFile}.map`; +// Identifier mangling (`identifiers: true`) is disabled because Bun's +// bundler renamer can produce runtime identifier collisions in large bundles. +// In issue #36, the standalone CLI contained `function Hg(H4, $) { const A = +// H4(H4); ... }`, where a parameter shadowed the function binding and crashed +// at startup with `H4 is not a function`. +// +// Upstream tracking: oven-sh/bun#28742 documents the same collision class, and +// oven-sh/bun#30272 is the open compiler fix. Re-enable identifier mangling only +// after that fix ships and packageSmoke's manifest guard is updated deliberately. export const SAFE_STANDALONE_MINIFY = { whitespace: true, - identifiers: true, + identifiers: false, }; const vendorSources = [ { diff --git a/build/package.mjs b/build/package.mjs index 057fbd4..bb8f7ef 100644 --- a/build/package.mjs +++ b/build/package.mjs @@ -195,13 +195,12 @@ export async function buildCompiledPackage(options = {}) { outfile: binaryPath, buildMode: parsed.buildMode, target: targetInfo.compileTarget, - // Bun 1.3.10 miscompiles this CLI when syntax minification is combined - // with whitespace minification. The emitted bundle fuses `return` with - // helper identifiers in the workflow-tool closure (for example - // `return __toCommonJS(...)` becomes `return__toCommonJS(...)`, and - // `return dA(...)` becomes `returndA(...)`), which breaks `--help` - // at runtime. whitespace+identifiers is currently the smallest - // known safe profile for the real single-executable CLI. + // SAFE_STANDALONE_MINIFY intentionally keeps syntax and identifier + // minification disabled. Syntax minification has fused `return` with + // helper identifiers in this CLI, and identifier minification has produced + // runtime name collisions (issue #36, upstream oven-sh/bun#28742). + // Whitespace-only minification is the current safe profile for the real + // single-executable CLI. minify: SINGLE_EXECUTABLE_MINIFY, }); diff --git a/build/packageSmoke.mjs b/build/packageSmoke.mjs index dea7a85..5e58db5 100644 --- a/build/packageSmoke.mjs +++ b/build/packageSmoke.mjs @@ -21,6 +21,12 @@ const FORBIDDEN_MANIFEST_KEYS = [ const EXPECTED_IMAGE_PROCESSOR_FALLBACK_WARNING = 'Native image processor not available, falling back to sharp'; +// These are smoke-test budgets, not product latency SLOs. They catch broken +// startup paths while allowing GitHub-hosted macOS x64 cold starts enough room +// to avoid millisecond-level flakes after safe whitespace-only packaging. +const VERSION_CHECK_BUDGET_MS = 3_000; +const HELP_CHECK_BUDGET_MS = 4_000; + function parseArgs(argv) { const args = { outDir: undefined, @@ -143,9 +149,9 @@ async function main() { `Compiled binary version output did not match expected contract: ${versionResult.stdout}`, ); } - if (versionResult.elapsedMs > 2_000) { + if (versionResult.elapsedMs > VERSION_CHECK_BUDGET_MS) { throw new Error( - `Compiled binary --version exceeded fast-path budget: ${versionResult.elapsedMs}ms`, + `Compiled binary --version exceeded fast-path budget (${VERSION_CHECK_BUDGET_MS}ms): ${versionResult.elapsedMs}ms`, ); } @@ -163,9 +169,9 @@ async function main() { .map(line => line.trimEnd()) .filter(line => line.trim().length > 0); expectLinesInOrder(helpLines, ['Usage: ncode', 'Options:'], 'compiled binary --help output'); - if (helpResult.elapsedMs > 4_000) { + if (helpResult.elapsedMs > HELP_CHECK_BUDGET_MS) { throw new Error( - `Compiled binary --help exceeded fast-path budget: ${helpResult.elapsedMs}ms`, + `Compiled binary --help exceeded fast-path budget (${HELP_CHECK_BUDGET_MS}ms): ${helpResult.elapsedMs}ms`, ); } } @@ -190,6 +196,12 @@ async function main() { ); } + if (manifest.compileOptions?.minify?.identifiers === true) { + throw new Error( + 'Standalone package manifest enabled identifier minification, which is unsafe for the mounted CLI runtime (issue #36).', + ); + } + if (!result.securityAudit?.ok) { throw new Error('Compiled package did not report a successful security audit.'); } diff --git a/package.json b/package.json index c5e4445..fc54a60 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "test:teleport": "bun test src/utils/teleport/api.e2e.test.ts src/utils/teleport/environments.test.ts src/utils/teleport/sessionResumeValidation.test.ts src/utils/teleport.test.ts src/utils/cliDisplayCommand.test.ts src/utils/remoteSessionCliOutput.test.ts src/utils/teleportProgressOutput.test.ts src/utils/background/remote/preconditions.test.ts src/utils/background/remote/remoteSession.test.ts src/constants/product.test.ts", "test:remote": "bun test src/remote/appServer/remoteAppServerBYOKSession.test.ts src/remote/appServer/remoteAppServerSession.test.ts src/remote/appServer/client.test.ts src/commands/teleport/index.test.ts src/commands/remote-env/index.test.ts src/session/remoteSubmitDispatch.test.ts src/session/remoteSubmitPolicy.test.ts src/components/RemoteCallout.test.tsx" }, - "packageManager": "bun@1.3.10", + "packageManager": "bun@1.3.14", "dependencies": { "@alcalzone/ansi-tokenize": "0.3.0", "@anthropic-ai/bedrock-sdk": "0.26.4",