fix(#3050): consolidate the spawn-timeout predicate and propagate the unresolved-root reason (#3060)

* chore(#3050): changeset and review artifacts for the follow-up

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(#3050): backfill changeset pr number

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-08-04 17:21:24 -04:00
committed by GitHub
parent 24066e536e
commit 4eb8e3648c
15 changed files with 530 additions and 253 deletions

View File

@@ -1,9 +1,10 @@
'use strict';
const { describe, test, beforeEach, afterEach } = require('node:test');
const { describe, test, beforeEach, afterEach, mock } = require('node:test');
const assert = require('node:assert/strict');
const path = require('node:path');
const fs = require('node:fs');
const childProcess = require('node:child_process');
const {
execGit,
@@ -113,7 +114,7 @@ describe('dispatchGsdCommand', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempDir(); });
afterEach(() => { cleanup(tmpDir); });
afterEach(() => { cleanup(tmpDir); mock.restoreAll(); });
test('resolveGsdToolsPath resolves to the real gsd-tools.cjs on disk', () => {
const toolsPath = resolveGsdToolsPath();
@@ -171,6 +172,26 @@ describe('dispatchGsdCommand', () => {
assert.equal(result.timedOut, true);
});
});
// #3050 item 4: this site (dispatchGsdCommand's `timedOut` derivation) now
// routes through the shared isSpawnTimeout predicate, which drops the
// `signal === 'SIGTERM'` requirement — a Windows-shaped timeout (no signal,
// only error.code === 'ETIMEDOUT') must still be detected. The real-timeout
// test above only exercises whatever shape THIS OS's spawnSync happens to
// produce (SIGTERM on POSIX); mocking spawnSync proves the Windows shape too.
test('Windows-shaped timeout (no signal, error.code ETIMEDOUT) is still reported as timedOut:true (#3050)', () => {
mock.method(childProcess, 'spawnSync', () => ({
status: null,
stdout: '',
stderr: '',
signal: null,
error: Object.assign(new Error('spawnSync ETIMEDOUT'), { code: 'ETIMEDOUT' }),
}));
const result = dispatchGsdCommand({ family: 'progress', subcommand: 'json', cwd: tmpDir });
assert.equal(result.ok, false);
assert.equal(result.timedOut, true);
});
});
// ─── probeTty ────────────────────────────────────────────────────────────────