Adds scripts/audit-leaf-package-publish.mjs, wired into v3-ci.yml's existing
audit job (semver is already installed there).
Only four @claude-flow/* components are bundled into the release tarballs —
INTERNAL_RUNTIME_PACKAGES in stage-internal-runtime-bundles.mjs: security,
codex, mcp, plugin-agent-federation. cli-core, neural, shared and memory
resolve from the REGISTRY at whatever v3/@claude-flow/cli/package.json pins, so
a merged source change in one of those does NOT ship with the three-package
train: the release silently carries the last published copy.
This has bitten twice. #3334 fixed RUFLO_INTELLIGENCE_MODE in
@claude-flow/memory; v3.42.1 shipped the train only and a fresh consumer
install had zero references to the fix (#3335). #3390's reasoningBank
activation spanned cli/src/memory/memory-bridge.ts AND
memory/src/controller-registry.ts — publishing only the train would have
shipped the bridge half and left the embedder half unreachable, leaving
reasoningBank exactly as inert as before. It was caught by hand and
memory@3.0.0-alpha.25 went out alongside v3.42.5.
What makes it easy to miss is that CI goes GREEN: the leaf's own package tests
run against workspace source, so the change looks verified while being
unshippable.
The audit asserts:
1. every non-bundled leaf's workspace version is covered by the CLI's
declared range (a miss means the release resolves to OLDER code than
this repo's source);
2. diff mode — if a leaf's publishable source changed vs the base ref but
its version did not, fail with remediation.
It imports INTERNAL_RUNTIME_PACKAGES rather than restating it, so the bundled
and non-bundled sets cannot drift apart; exits non-zero if it inspects zero
leaves, so a broken classifier can't report clean; and reports a skipped diff
check as NOT EXERCISED rather than as a pass.
@claude-flow/shared is entered as a time-boxed accepted finding (expires
2026-10-21): the CLI pins 3.0.0-alpha.7 while the workspace is at
3.0.0-alpha.8, which IS published — stale, not unshippable. Raising the pin
edits v3/@claude-flow/cli deps and so forces a pnpm-lock.yaml regen in the same
change (#2540 -> #2552), which wants its own PR. An EXPIRED entry fails the
guard rather than muting it.
Negative-controlled, all four paths:
- memory source changed, no bump -> FAIL (replays #3390)
- same change with a version bump -> pass
- test-only change in a leaf -> pass (not publishable)
- waiver expiry backdated -> FAIL, "no longer honoured"
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(release): spawn npm.cmd with shell:true on Windows
spawnSync('npm.cmd', ['run','build'], {stdio:'inherit'}) threw EINVAL on
Windows, blocking every @claude-flow/cli publish on this platform since
CreateProcess can't launch a .cmd directly and Node has refused to shell
out implicitly since CVE-2024-27980.
Found live while publishing 3.42.3: prepublishOnly -> prepare-publish.mjs
-> stageInternalRuntimeBundles() rebuilds the four internal runtime
packages (security/codex/mcp/plugin-agent-federation) and hit this on
the very first Windows publish attempt after upgrading past the Node
version where the implicit shell fallback still worked.
command/args here are constant literals ('npm.cmd', ['run','build']),
never hook-derived or user-controlled, so shell:true carries no
injection risk.
Co-Authored-By: RuFlo <ruv@ruv.net>
* fix(release): avoid DEP0190 warning in the npm.cmd shell:true fix
Single-string command form instead of args array — same shell:true
safety (still constant literals, nothing interpolated), no warning.
Co-Authored-By: RuFlo <ruv@ruv.net>