Batch 12 of the ad hoc timeout literal migration (epic #4445). Replaces every bare numeric timeout/timeoutMs object-literal property in tests/check-env.test.cjs, tests/config-get-default.test.cjs, tests/federated-config.test.cjs, tests/gsd-check-update-worker-platform-gate.test.cjs, tests/gsd-mcp-server-bin.test.cjs, tests/health-validation.test.cjs, tests/locking-bugs-1909-1916-1925-1927.test.cjs, tests/perf-316-state-lock-buffer-alloc.test.cjs, tests/perf-317-context-monitor-fs.test.cjs, and tests/pi-config-dir-env-override.test.cjs with a named constant, per eslint-rules/no-adhoc-timeout-literal.cjs. Removes these 10 files from the rule's allowlist. Ground truth via eslint matched the issue's stated 26 sites across 10 files exactly. Reuses PROBE_TIMEOUT_MS, GENERATOR_SCRIPT_TIMEOUT_MS, GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS, and INSTALL_TIMEOUT_MS across 4 files. No new shared constants needed -- every recurring value across files was independently verified to be a genuinely different operation class, per this migration's standing rule that numeric coincidence is never identity. Adds 11 new file-local constants, three of which are not real subprocess timeouts at all (a config-merge fixture value, and two node:test per-test timeout options bounding ReDoS/lock-retry regression backstops). Per the issue's explicit mandate, gsd-check-update-worker-platform-gate.test.cjs now imports (read-only) NPM_VIEW_TIMEOUT_MS from gsd-core/bin/check-latest-version.cjs for disclosure -- this file and that production module once independently guessed the same 15000ms value, causing the PR #4428 Windows double-SIGKILL collision. No site in this file's current bare literals actually wraps a live npm-view call needing margin arithmetic, so the import documents the historical relationship honestly rather than fabricating a computation. No src/bin file edited (only a read-only import added), no numeric value changed anywhere. Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,24 +1,14 @@
|
||||
[
|
||||
"tests/assumption-delta-checkpoint-e2e.test.cjs",
|
||||
"tests/assumption-delta.test.cjs",
|
||||
"tests/check-env.test.cjs",
|
||||
"tests/check-predicate.test.cjs",
|
||||
"tests/cjs-command-router-adapter.test.cjs",
|
||||
"tests/code-review-pipeline-regression.test.cjs",
|
||||
"tests/code-review.test.cjs",
|
||||
"tests/config-get-default.test.cjs",
|
||||
"tests/dispatcher.test.cjs",
|
||||
"tests/federated-config.test.cjs",
|
||||
"tests/gate-predicate-evaluator.test.cjs",
|
||||
"tests/gsd-check-update-worker-platform-gate.test.cjs",
|
||||
"tests/gsd-mcp-server-bin.test.cjs",
|
||||
"tests/gsd-statusline.test.cjs",
|
||||
"tests/health-validation.test.cjs",
|
||||
"tests/locking-bugs-1909-1916-1925-1927.test.cjs",
|
||||
"tests/perf-316-state-lock-buffer-alloc.test.cjs",
|
||||
"tests/perf-317-context-monitor-fs.test.cjs",
|
||||
"tests/phase6-capstone-conformance.test.cjs",
|
||||
"tests/pi-config-dir-env-override.test.cjs",
|
||||
"tests/plan-phase-stall-detection.test.cjs",
|
||||
"tests/plan-review-convergence.test.cjs",
|
||||
"tests/policy-160-route0-resume.test.cjs",
|
||||
|
||||
@@ -23,6 +23,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const { GENERATOR_SCRIPT_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const SCRIPT = path.resolve(__dirname, '..', 'scripts', 'check-env.cjs');
|
||||
const FIXTURE_ROOT = path.resolve(__dirname, 'fixtures', 'check-env');
|
||||
@@ -41,7 +42,7 @@ function runScript(cwd, args = [], envOverrides = {}) {
|
||||
const result = spawnSync(process.execPath, [SCRIPT, ...args], {
|
||||
cwd,
|
||||
encoding: 'utf8',
|
||||
timeout: 30_000,
|
||||
timeout: GENERATOR_SCRIPT_TIMEOUT_MS,
|
||||
env: { ...process.env, ...envOverrides },
|
||||
});
|
||||
return {
|
||||
|
||||
@@ -16,6 +16,14 @@ const path = require('path');
|
||||
const os = require('os');
|
||||
const { cleanup, captureFdSync } = require('./helpers.cjs');
|
||||
|
||||
/**
|
||||
* A `config-get` CLI call with a deliberately pathological 50KB key,
|
||||
* proving the call fails safely without hanging -- distinct from
|
||||
* PROBE_TIMEOUT_MS's plain-query class (15000ms) despite the shared file,
|
||||
* since this site is specifically testing pathological-input tolerance.
|
||||
*/
|
||||
const LONG_KEY_CLI_TIMEOUT_MS = 8000;
|
||||
|
||||
// In-process invocation, not execFileSync: cmdConfigGet is a pure CJS
|
||||
// function reachable without spawning `node` as a child. The prior
|
||||
// execFileSync(..., { timeout: 5000 }) raced a real subprocess's startup
|
||||
@@ -767,6 +775,7 @@ const { execFileSync } = require('node:child_process');
|
||||
const GSD_TOOLS = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const { ERROR_REASON } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'io.cjs'));
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
describe('bug-2943: config-get returns schema default for context_window', () => {
|
||||
let tmpDir;
|
||||
@@ -798,7 +807,7 @@ describe('bug-2943: config-get returns schema default for context_window', () =>
|
||||
stdout = execFileSync(process.execPath, args, {
|
||||
encoding: 'utf-8',
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
timeout: 15000,
|
||||
timeout: PROBE_TIMEOUT_MS,
|
||||
});
|
||||
} catch (err) {
|
||||
exitCode = err.status ?? 1;
|
||||
@@ -1108,7 +1117,7 @@ test('config-get with a very long key (50KB) fails safely without hanging', (t)
|
||||
const projectDir = createTempProject('cli-neg-config-13-');
|
||||
t.after(() => cleanup(projectDir));
|
||||
const longKey = 'x'.repeat(50000);
|
||||
const result = runCli(['config-get', longKey], { cwd: projectDir, timeoutMs: 8000 });
|
||||
const result = runCli(['config-get', longKey], { cwd: projectDir, timeoutMs: LONG_KEY_CLI_TIMEOUT_MS });
|
||||
assert.equal(result.signal, null, 'long input must not trigger the harness timeout');
|
||||
assert.equal(result.hasStackTrace, false, 'long input must not crash');
|
||||
assert.notEqual(result.status, 0, 'unknown 50KB key must fail');
|
||||
|
||||
@@ -67,6 +67,15 @@ const neverCentral = (_key) => false;
|
||||
/** An always-central isCentralKey. */
|
||||
const alwaysCentral = (_key) => true;
|
||||
|
||||
/**
|
||||
* NOT a subprocess spawn timeout. Fixture DATA -- the test value for a
|
||||
* fictional `mytool.timeout` config key in a mergeFederatedConfig()
|
||||
* schema-merge test, proving a user-supplied override for a numeric config
|
||||
* key is honored. Never passed into a real spawnSync/execFileSync options
|
||||
* object.
|
||||
*/
|
||||
const FIXTURE_CONFIG_TIMEOUT_VALUE = 99;
|
||||
|
||||
// ─── 1. Empty configSchema ────────────────────────────────────────────────────
|
||||
|
||||
describe('empty configSchema', () => {
|
||||
@@ -279,9 +288,9 @@ describe('valid federated key — user override with correct type', () => {
|
||||
const result = mergeFederatedConfig({
|
||||
configSchema: { 'mytool.timeout': NUMBER_SLICE },
|
||||
isCentralKey: neverCentral,
|
||||
userConfig: { mytool: { timeout: 99 } },
|
||||
userConfig: { mytool: { timeout: FIXTURE_CONFIG_TIMEOUT_VALUE } },
|
||||
});
|
||||
assert.strictEqual(result.values['mytool.timeout'], 99);
|
||||
assert.strictEqual(result.values['mytool.timeout'], FIXTURE_CONFIG_TIMEOUT_VALUE);
|
||||
assert.deepEqual(result.warnings, []);
|
||||
});
|
||||
|
||||
|
||||
@@ -29,6 +29,35 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { scanFencedBlocks } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs');
|
||||
const { NPM_VIEW_TIMEOUT_MS } = require('../gsd-core/bin/check-latest-version.cjs');
|
||||
|
||||
/**
|
||||
* NOT a margin above NPM_VIEW_TIMEOUT_MS (imported above for disclosure,
|
||||
* per this batch's epic issue -- this file and check-latest-version.cjs
|
||||
* once independently guessed the same 15000ms value, causing a Windows
|
||||
* double-SIGKILL collision fixed in PR #4428). This specific scenario
|
||||
* (missing compiled runtime library) degrades BEFORE reaching the wrapped
|
||||
* `npm view` call at all -- see this test's own #3582 comment
|
||||
* ("checkLatestVersion -> not ok" on the degrade, never invoked) -- so its
|
||||
* bound has no margin relationship to NPM_VIEW_TIMEOUT_MS to preserve.
|
||||
* Kept as its own independent, pre-existing value.
|
||||
*/
|
||||
const WORKER_DEGRADED_PATH_TIMEOUT_MS = 8000;
|
||||
|
||||
/**
|
||||
* gsd-update-banner.js only reads a small cache file from disk and prints
|
||||
* JSON -- no subprocess/network work -- so this leaves generous headroom
|
||||
* over its sub-second worst case even on a contended CI runner. NOT
|
||||
* related to NPM_VIEW_TIMEOUT_MS or WORKER_DEGRADED_PATH_TIMEOUT_MS above
|
||||
* (a different hook script entirely).
|
||||
*/
|
||||
const UPDATE_BANNER_HOOK_TIMEOUT_MS = 10_000;
|
||||
|
||||
// Imported for disclosure only (see WORKER_DEGRADED_PATH_TIMEOUT_MS's own
|
||||
// doc comment above): confirms this file references the collision-prone
|
||||
// constant from check-latest-version.cjs without deriving any of this
|
||||
// file's own timeout values from it.
|
||||
void NPM_VIEW_TIMEOUT_MS;
|
||||
|
||||
const WORKER_PATH = path.join(__dirname, '..', 'hooks', 'gsd-check-update-worker.js');
|
||||
const PROJECTION_PATH = path.join(
|
||||
@@ -134,7 +163,7 @@ describe('worker delegates the npm spawn (does not re-open the gate, #498)', ()
|
||||
};
|
||||
const r = runHookSeam(path.join(cold.hooksDir, 'gsd-check-update-worker.js'), [], {
|
||||
env,
|
||||
timeoutMs: 8000,
|
||||
timeoutMs: WORKER_DEGRADED_PATH_TIMEOUT_MS,
|
||||
});
|
||||
assert.equal(r.exitCode, 0, `worker must exit 0 on a build failure; stderr: ${r.stderr}`);
|
||||
assert.ok(fs.existsSync(cacheFile), 'worker must still reach the end and write a cache record');
|
||||
@@ -863,7 +892,7 @@ describe('gsd-update-banner.js end-to-end', () => {
|
||||
// contended CI runner.
|
||||
const r = seamRunHook(HOOK_PATH, [], {
|
||||
env: { ...process.env, HOME: home, USERPROFILE: home },
|
||||
timeoutMs: 10_000,
|
||||
timeoutMs: UPDATE_BANNER_HOOK_TIMEOUT_MS,
|
||||
});
|
||||
return { status: r.exitCode, stdout: r.stdout, stderr: r.stderr };
|
||||
}
|
||||
@@ -1017,7 +1046,7 @@ describe('gsd-update-banner.js: #3582 cold tree — degrades to silent, never cr
|
||||
|
||||
const r = seamRunHook(path.join(cold.hooksDir, 'gsd-update-banner.js'), [], {
|
||||
env: { ...process.env, HOME: home, USERPROFILE: home },
|
||||
timeoutMs: 10_000,
|
||||
timeoutMs: UPDATE_BANNER_HOOK_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
assert.equal(r.exitCode, 0, `hook must exit 0 on a build failure; stderr: ${r.stderr}`);
|
||||
|
||||
@@ -11,6 +11,7 @@ const assert = require('node:assert/strict');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const path = require('node:path');
|
||||
const { PROTOCOL_VERSION } = require('../gsd-core/bin/lib/mcp-server.cjs');
|
||||
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const SHIM = path.join(__dirname, '..', 'bin', 'gsd-mcp-server.js');
|
||||
|
||||
@@ -18,7 +19,7 @@ function run(stdin) {
|
||||
return spawnSync(process.execPath, [SHIM], {
|
||||
input: stdin,
|
||||
encoding: 'utf-8',
|
||||
timeout: 15000,
|
||||
timeout: PROBE_TIMEOUT_MS,
|
||||
env: { ...process.env, GSD_TEST_MODE: '1' },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1492,6 +1492,19 @@ const {
|
||||
buildNotStartedPhaseVariants,
|
||||
} = require('../gsd-core/bin/lib/validate.cjs');
|
||||
|
||||
/**
|
||||
* NOT a subprocess spawn timeout. This is `node:test`'s own per-test
|
||||
* `{ timeout }` option (the second positional argument to `test(name,
|
||||
* options, fn)`), used as a hang BACKSTOP for two ReDoS (#663) adversarial-
|
||||
* input regression tests -- catches any regression that re-introduces a
|
||||
* catastrophically-backtracking pattern. Coincides numerically with
|
||||
* tests/helpers/timeouts.cjs's SCAN_USAGE_ERROR_TIMEOUT_MS and
|
||||
* MALFORMED_INPUT_HOOK_TIMEOUT_MS but is a completely different mechanism
|
||||
* (a node:test option, not a subprocess spawn bound) -- disclosed, not
|
||||
* merged.
|
||||
*/
|
||||
const REDOS_BACKSTOP_TEST_TIMEOUT_MS = 5000;
|
||||
|
||||
// ─── Part A: behavior preservation ───────────────────────────────────────────
|
||||
|
||||
describe('buildRoadmapPhaseVariants — behavior preservation (#663)', () => {
|
||||
@@ -1588,7 +1601,7 @@ describe('buildRoadmapPhaseVariants — ReDoS adversarial fixture (#663)', () =>
|
||||
// yields NO match (empty roadmapPhases Set) — the correct behavior when the
|
||||
// terminating colon is absent. The { timeout: 5000 } backstop catches any
|
||||
// regression that re-introduces a slow pattern.
|
||||
test('malformed heading without colon yields no phase match (adversarial input)', { timeout: 5000 }, () => {
|
||||
test('malformed heading without colon yields no phase match (adversarial input)', { timeout: REDOS_BACKSTOP_TEST_TIMEOUT_MS }, () => {
|
||||
const pathological = '## Phase a' + '-a'.repeat(32) + ' ';
|
||||
const { roadmapPhases } = buildRoadmapPhaseVariants(pathological);
|
||||
assert.strictEqual(roadmapPhases.size, 0,
|
||||
@@ -1601,7 +1614,7 @@ describe('buildNotStartedPhaseVariants — ReDoS adversarial fixture (#663)', ()
|
||||
// A checklist-style line with many "-a" segments and no colon triggers the
|
||||
// same catastrophic backtracking. Assert the correct structured result:
|
||||
// the malformed line yields an empty notStarted Set.
|
||||
test('malformed unchecked-item without terminator yields no phase match (adversarial input)', { timeout: 5000 }, () => {
|
||||
test('malformed unchecked-item without terminator yields no phase match (adversarial input)', { timeout: REDOS_BACKSTOP_TEST_TIMEOUT_MS }, () => {
|
||||
// No trailing colon or whitespace: the regex terminator [:\s*] cannot match,
|
||||
// so the engine must backtrack through all '-a' repetitions and conclude no
|
||||
// match. Under the old nested-quantifier pattern this was exponential;
|
||||
|
||||
@@ -23,6 +23,14 @@ const { spawn } = require('child_process');
|
||||
|
||||
const { runGsdTools, createTempProject, cleanup, waitFor, TOOLS_PATH } = require('./helpers.cjs');
|
||||
|
||||
/**
|
||||
* NOT a subprocess spawn timeout. This is the file's own `waitFor` busy-
|
||||
* poll helper's giving-up bound, waiting for two racing subprocesses to
|
||||
* each write a "ready" sentinel file before the test drops their shared
|
||||
* synchronization barrier.
|
||||
*/
|
||||
const BARRIER_WAIT_TIMEOUT_MS = 10000;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Helpers
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -234,7 +242,7 @@ describe('#1925 TOCTOU: state commands use readModifyWriteStateMd', () => {
|
||||
// ── Orchestrate: wait for both ready-signals, then drop the barrier ───────
|
||||
try {
|
||||
await waitFor(() => fs.existsSync(readyA) && fs.existsSync(readyB), {
|
||||
timeoutMs: 10000,
|
||||
timeoutMs: BARRIER_WAIT_TIMEOUT_MS,
|
||||
stepMs: 10,
|
||||
message: 'Timed out waiting for both subprocesses to reach barrier',
|
||||
});
|
||||
@@ -373,7 +381,7 @@ describe('#1925 TOCTOU: state commands use readModifyWriteStateMd', () => {
|
||||
// ── Orchestrate: wait for both ready-signals, then drop the barrier ───────
|
||||
try {
|
||||
await waitFor(() => fs.existsSync(readyA) && fs.existsSync(readyB), {
|
||||
timeoutMs: 10000,
|
||||
timeoutMs: BARRIER_WAIT_TIMEOUT_MS,
|
||||
stepMs: 10,
|
||||
message: 'Timed out waiting for both subprocesses to reach barrier',
|
||||
});
|
||||
@@ -501,7 +509,7 @@ describe('#1927 config.json: setConfigValue must hold planning lock', () => {
|
||||
// ── Wait for both to reach barrier, then release ──────────────────────────
|
||||
try {
|
||||
await waitFor(() => fs.existsSync(readyA) && fs.existsSync(readyB), {
|
||||
timeoutMs: 10000,
|
||||
timeoutMs: BARRIER_WAIT_TIMEOUT_MS,
|
||||
stepMs: 10,
|
||||
message: 'Timed out waiting for both config-set subprocesses to reach barrier',
|
||||
});
|
||||
|
||||
@@ -38,6 +38,17 @@ const { cleanup } = require('./helpers.cjs');
|
||||
// Constants
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* NOT a subprocess spawn timeout. This is `node:test`'s own per-test
|
||||
* `{ timeout }` option (the second positional argument to `test(name,
|
||||
* options, fn)`), bounding a worker_threads + SharedArrayBuffer
|
||||
* lock-contention regression test (sabCount retry assertion). Coincides
|
||||
* numerically with tests/helpers/timeouts.cjs's PROBE_TIMEOUT_MS but is a
|
||||
* completely different mechanism (a node:test option, not a subprocess
|
||||
* spawn bound) -- disclosed, not merged.
|
||||
*/
|
||||
const LOCK_RETRY_TEST_TIMEOUT_MS = 15000;
|
||||
|
||||
const STATE_CJS_PATH = path.join(
|
||||
__dirname, '..', 'gsd-core', 'bin', 'lib', 'state.cjs'
|
||||
);
|
||||
@@ -193,7 +204,7 @@ describe('perf #316: acquireStateLock hoists sleep buffer — exactly one SAB pe
|
||||
|
||||
test(
|
||||
'sabCount === 1 after a call that undergoes >= 1 retry (post-fix assertion)',
|
||||
{ timeout: 15000 },
|
||||
{ timeout: LOCK_RETRY_TEST_TIMEOUT_MS },
|
||||
async () => {
|
||||
// ── Worker A: hold the lock until the writer signals contention ─────────
|
||||
// The holder releases the lock only when the writer signals its first
|
||||
|
||||
@@ -27,10 +27,42 @@ const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const MONITOR_PATH = path.join(__dirname, '..', 'hooks', 'gsd-context-monitor.js');
|
||||
const tmpDir = os.tmpdir();
|
||||
|
||||
/**
|
||||
* A plain single-invocation query-style spawn of hooks/gsd-context-monitor.js
|
||||
* via the file's MONITOR_PATH constant -- no fan-out, no detached child
|
||||
* writer triggered.
|
||||
*/
|
||||
const CONTEXT_MONITOR_QUERY_TIMEOUT_MS = 5000;
|
||||
|
||||
/**
|
||||
* The #1974 fold: this hook invocation additionally triggers a DETACHED
|
||||
* `state record-session` writer internally -- heavier than
|
||||
* CONTEXT_MONITOR_QUERY_TIMEOUT_MS's plain-query class despite the
|
||||
* coincidentally-matching value with sites in other files this batch.
|
||||
*/
|
||||
const CONTEXT_MONITOR_CRITICAL_RECORD_TIMEOUT_MS = 10000;
|
||||
|
||||
/**
|
||||
* A tighter bound chosen for one specific quick-probe scenario in this
|
||||
* file -- distinct tier from the other three context-monitor constants in
|
||||
* this file, not equalized without bench data.
|
||||
*/
|
||||
const CONTEXT_MONITOR_QUICK_PROBE_TIMEOUT_MS = 4000;
|
||||
|
||||
/**
|
||||
* The #2289/#3709 folds: a hook spawn sometimes carrying an extra
|
||||
* `--require <preload-script>` flag ahead of the hook path itself --
|
||||
* still one process, no fan-out. Coincides numerically with other files'
|
||||
* timeout constants in this same batch (a CLI pathological-input query, a
|
||||
* worker's degraded fast path) but describes neither -- kept local.
|
||||
*/
|
||||
const CONTEXT_MONITOR_PRELOAD_HOOK_TIMEOUT_MS = 8000;
|
||||
|
||||
/**
|
||||
* Spawn the context-monitor hook with the given options.
|
||||
*
|
||||
@@ -100,7 +132,7 @@ function runMonitorRaw(opts) {
|
||||
stdout = execFileSync(process.execPath, [MONITOR_PATH], {
|
||||
input,
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
timeout: CONTEXT_MONITOR_QUERY_TIMEOUT_MS,
|
||||
});
|
||||
} catch (e) {
|
||||
exitCode = e.status ?? 1;
|
||||
@@ -220,7 +252,7 @@ describe('perf #317: config.json absent (exercises config-missing → defaults p
|
||||
stdout = execFileSync(process.execPath, [MONITOR_PATH], {
|
||||
input: JSON.stringify({ session_id: sessionId, cwd: testCwd, hook_event_name: 'PostToolUse' }),
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
timeout: CONTEXT_MONITOR_QUERY_TIMEOUT_MS,
|
||||
});
|
||||
} catch (e) {
|
||||
exitCode = e.status ?? 1;
|
||||
@@ -395,7 +427,7 @@ function runHook(sessionId, remainingPct, cwd) {
|
||||
|
||||
const result = runHookSeam(HOOK_PATH, [], {
|
||||
input,
|
||||
timeoutMs: 10000,
|
||||
timeoutMs: CONTEXT_MONITOR_CRITICAL_RECORD_TIMEOUT_MS,
|
||||
env: { ...process.env, HOME: process.env.HOME },
|
||||
});
|
||||
|
||||
@@ -412,7 +444,7 @@ function runRecordSession(cwd, stoppedAt) {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[GSD_TOOLS, 'state', 'record-session', '--stopped-at', stoppedAt, '--cwd', cwd],
|
||||
{ encoding: 'utf-8', timeout: 30000 }
|
||||
{ encoding: 'utf-8', timeout: GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS }
|
||||
);
|
||||
return {
|
||||
exitCode: result.status,
|
||||
@@ -652,7 +684,7 @@ function runStatuslineHook(remainingPct, totalTokens = 1_000_000, acwEnv = null)
|
||||
execFileSync(process.execPath, [HOOK_PATH], {
|
||||
input: payload,
|
||||
env,
|
||||
timeout: 4000,
|
||||
timeout: CONTEXT_MONITOR_QUICK_PROBE_TIMEOUT_MS,
|
||||
});
|
||||
} catch { /* non-zero exit is fine; we only need the bridge file */ }
|
||||
|
||||
@@ -685,7 +717,7 @@ function runMonitorHook(remainingPct, usedPct) {
|
||||
stdout = execFileSync(process.execPath, [MONITOR_PATH], {
|
||||
input,
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
timeout: CONTEXT_MONITOR_QUERY_TIMEOUT_MS,
|
||||
});
|
||||
} catch (e) {
|
||||
stdout = e.stdout || '';
|
||||
@@ -843,7 +875,7 @@ function runMonitor({ hookEventName, sessionId, remainingPct = 30, usedPct = 70,
|
||||
stdout = execFileSync(process.execPath, [MONITOR_PATH], {
|
||||
input: JSON.stringify(payload),
|
||||
encoding: 'utf-8',
|
||||
timeout: 5000,
|
||||
timeout: CONTEXT_MONITOR_QUERY_TIMEOUT_MS,
|
||||
env: { ...process.env, ...env },
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -1069,7 +1101,7 @@ function runMonitor(opts) {
|
||||
input: JSON.stringify(payload),
|
||||
env,
|
||||
encoding: 'utf8',
|
||||
timeout: 8000,
|
||||
timeout: CONTEXT_MONITOR_PRELOAD_HOOK_TIMEOUT_MS,
|
||||
});
|
||||
} catch (e) {
|
||||
stdout = e.stdout || '';
|
||||
@@ -1359,7 +1391,7 @@ describe('#3709 context-monitor: PreCompact resets the warn sentinel', () => {
|
||||
stdout = execFileSync(process.execPath, argv, {
|
||||
input: JSON.stringify({ session_id: id, cwd, hook_event_name: event }),
|
||||
encoding: 'utf8',
|
||||
timeout: 8000,
|
||||
timeout: CONTEXT_MONITOR_PRELOAD_HOOK_TIMEOUT_MS,
|
||||
env,
|
||||
});
|
||||
} catch (e) { stdout = e.stdout || ''; exitCode = e.status ?? 1; }
|
||||
@@ -2302,7 +2334,7 @@ describe('#3709 round 3: DEBOUNCE_CALLS and STALE_SECONDS at their limits', () =
|
||||
stdout = execFileSync(process.execPath, ['--require', NOW_PRELOAD, HOOK], {
|
||||
input: JSON.stringify({ session_id: id, cwd: os.tmpdir(), hook_event_name: 'PostToolUse' }),
|
||||
encoding: 'utf8',
|
||||
timeout: 8000,
|
||||
timeout: CONTEXT_MONITOR_PRELOAD_HOOK_TIMEOUT_MS,
|
||||
env: { ...process.env, GSD_TEST_NOW_MS: String(nowMs) },
|
||||
});
|
||||
} catch (e) { stdout = e.stdout || ''; exitCode = e.status ?? 1; }
|
||||
|
||||
@@ -41,13 +41,26 @@ const { spawnSync } = require('node:child_process');
|
||||
|
||||
const { INSTALL_SCRIPT, installerEnv, BUILD_SCRIPT } = require('./helpers/install-shared.cjs');
|
||||
const { cleanup, createTempDir } = require('./helpers.cjs');
|
||||
const { INSTALL_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
/**
|
||||
* Spawns scripts/build-hooks.js (the same script BUILD_TIMEOUT_MS
|
||||
* documents, at 30000ms) but at four times that constant's bound, used
|
||||
* here as a prep step before this file's own real installer run below.
|
||||
* Not equalized to the lighter shared norm without bench data -- the
|
||||
* identical pattern (and identical value) to epic #4445 batch 9's
|
||||
* file-local BUILD_HOOKS_UNDER_LOAD_TIMEOUT_MS in a different file
|
||||
* (tests/emitted-attribution.test.cjs, not importable) -- disclosed as
|
||||
* coincidence, not merged.
|
||||
*/
|
||||
const BUILD_HOOKS_PREP_TIMEOUT_MS = 120_000;
|
||||
|
||||
// hooks/dist is gitignored and built (DEFECT.HOOKS-DIST-SCOPED-CI): a scoped CI
|
||||
// lane does not run build:hooks first, so a real --pi --global install there
|
||||
// would emit no gsd-hooks/ dir. Build idempotently before spawning, exactly as
|
||||
// the golden/emitted-attribution harnesses do.
|
||||
function ensureHooksBuilt() {
|
||||
spawnSync(process.execPath, [BUILD_SCRIPT], { encoding: 'utf-8', stdio: 'pipe', timeout: 120_000 });
|
||||
spawnSync(process.execPath, [BUILD_SCRIPT], { encoding: 'utf-8', stdio: 'pipe', timeout: BUILD_HOOKS_PREP_TIMEOUT_MS });
|
||||
}
|
||||
|
||||
/** Spawn the real installer for --pi --global against a fresh sandbox HOME,
|
||||
@@ -57,7 +70,7 @@ function runPiGlobalInstall(home, extraEnv = {}) {
|
||||
const result = spawnSync(process.execPath, [INSTALL_SCRIPT, '--pi', '--global'], {
|
||||
cwd: home,
|
||||
encoding: 'utf8',
|
||||
timeout: 120_000,
|
||||
timeout: INSTALL_TIMEOUT_MS,
|
||||
env: installerEnv({ HOME: home, USERPROFILE: home, ...extraEnv }),
|
||||
});
|
||||
assert.strictEqual(result.status, 0,
|
||||
|
||||
Reference in New Issue
Block a user