Spike outcome for #3625: evaluate cross-spawn / nano-spawn / execa against
the hand-rolled Windows binary resolution and cmd.exe mediation that epic
#3411 Phase 1 (PR #3621) is landing in the platform seam.
Verdict: stay hand-rolled, with revisit-if conditions recorded so the call
is not re-litigated in a future PR.
Evidence, per the issue's "Done when" list:
- Sync/async verdict per candidate. nano-spawn is async-only — settled by
its own README, which lists "synchronous execution" among the features
execa has and it does not. cross-spawn exposes `.sync`. execa exposes
execaSync, which its own docs discourage.
- CVE-2024-27980 escaping verdict per candidate. None uses shell:true.
cross-spawn independently arrives at the SAME mechanism the seam uses:
cmd.exe /d /s /c with a pre-escaped line and windowsVerbatimArguments.
That validates the seam's approach rather than superseding it.
- Maturity axes scored with measured data (registry metadata 2026-08-18,
transitive footprint measured by install).
Two premises in the issue did not survive measurement, both recorded:
- The CRITICAL 167-symbol/53-file blast radius is a `direction:both`
measurement, inflated by downstream callees. A call-shape change ripples
to CALLERS: upstream at depth 15 is 14 symbols / 7 files, MEDIUM, and it
terminates at depth 4. The decision does not rest on the CRITICAL figure.
- The cited vendoring precedent path does not exist; the real one is
gsd-core/bin/lib/vendor/re2js.cjs.
Decisive against the only structurally-eligible candidate (cross-spawn):
it resolves process.cwd() first on Windows even when an explicit PATH is
supplied, calls process.chdir() during resolution, and keys escape depth on
a node_modules/.bin/*.cmd path regex of the same shape #3411 was filed to
delete. Adoption would also break execTool's observable not-found contract
across 53 dependent files.
Doc-only: docs/adr/** plus a root-level CONTEXT.md pointer. The CONTEXT.md
addition is a new line rather than an edit to the seam's glossary paragraph,
which PR #3621 rewrites wholesale — same-line edits would conflict on merge.
ADR index regenerated via scripts/gen-adr-index.cjs --write.
lint:ci exit 0; lint-docs-required and changeset/lint both run with
GITHUB_BASE_REF=next and report ok_no_user_facing_changes.
Closes#3625
Co-authored-by: sim <sim@local>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>