diff --git a/tests/fixtures/golden-install-parity/antigravity.json b/tests/fixtures/golden-install-parity/antigravity.json index 4615ba8ec..cc1c9ee97 100644 --- a/tests/fixtures/golden-install-parity/antigravity.json +++ b/tests/fixtures/golden-install-parity/antigravity.json @@ -284,7 +284,7 @@ "gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e", "gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798", "gsd-core/workflows/resume-project.md": "98e2cf8908e73a52", - "gsd-core/workflows/review.md": "9bc686206c96748c", + "gsd-core/workflows/review.md": "3597a1ad15eb20a3", "gsd-core/workflows/scan.md": "a7fecd67e5cd655f", "gsd-core/workflows/secure-phase.md": "96b199dfac00e60f", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/augment.json b/tests/fixtures/golden-install-parity/augment.json index a39d82e99..133da25d2 100644 --- a/tests/fixtures/golden-install-parity/augment.json +++ b/tests/fixtures/golden-install-parity/augment.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "df9a45f0b1880999", "gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c", "gsd-core/workflows/resume-project.md": "f28da1200e4545f4", - "gsd-core/workflows/review.md": "f3c2e894941c9943", + "gsd-core/workflows/review.md": "fa3d682b94afcdcd", "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/claude.json b/tests/fixtures/golden-install-parity/claude.json index b96c3ccec..209dbac3c 100644 --- a/tests/fixtures/golden-install-parity/claude.json +++ b/tests/fixtures/golden-install-parity/claude.json @@ -283,7 +283,7 @@ "gsd-core/workflows/remove-phase.md": "ada8a0546c686483", "gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed", "gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96", - "gsd-core/workflows/review.md": "5cd71e81c4ace401", + "gsd-core/workflows/review.md": "23de3fcd9e5ba43b", "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "59d3c50aba8c9a6c", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/cline.json b/tests/fixtures/golden-install-parity/cline.json index cc82e4925..b827b085e 100644 --- a/tests/fixtures/golden-install-parity/cline.json +++ b/tests/fixtures/golden-install-parity/cline.json @@ -287,7 +287,7 @@ "gsd-core/workflows/remove-phase.md": "e336350f8113a328", "gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90", "gsd-core/workflows/resume-project.md": "e23981178fa37b3d", - "gsd-core/workflows/review.md": "c2b3fcc5bfc80038", + "gsd-core/workflows/review.md": "21e1b701b454ea9e", "gsd-core/workflows/scan.md": "dfd92717caea0ce7", "gsd-core/workflows/secure-phase.md": "cf78183f06a02582", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/codebuddy.json b/tests/fixtures/golden-install-parity/codebuddy.json index b47dbf1de..b1a767fd8 100644 --- a/tests/fixtures/golden-install-parity/codebuddy.json +++ b/tests/fixtures/golden-install-parity/codebuddy.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "df9a45f0b1880999", "gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c", "gsd-core/workflows/resume-project.md": "f28da1200e4545f4", - "gsd-core/workflows/review.md": "f3c2e894941c9943", + "gsd-core/workflows/review.md": "fa3d682b94afcdcd", "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/codex.json b/tests/fixtures/golden-install-parity/codex.json index a1a0bb73c..633fa8703 100644 --- a/tests/fixtures/golden-install-parity/codex.json +++ b/tests/fixtures/golden-install-parity/codex.json @@ -319,7 +319,7 @@ "gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4", "gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62", "gsd-core/workflows/resume-project.md": "9965f87eb278f7f8", - "gsd-core/workflows/review.md": "3a086e38f13eab6f", + "gsd-core/workflows/review.md": "e1fdf297509bebb0", "gsd-core/workflows/scan.md": "1a3caa5d724d39e9", "gsd-core/workflows/secure-phase.md": "db91810d16964b1e", "gsd-core/workflows/session-report.md": "dd8fa011c9394075", diff --git a/tests/fixtures/golden-install-parity/copilot.json b/tests/fixtures/golden-install-parity/copilot.json index 23cd30d93..53c2201a9 100644 --- a/tests/fixtures/golden-install-parity/copilot.json +++ b/tests/fixtures/golden-install-parity/copilot.json @@ -285,7 +285,7 @@ "gsd-core/workflows/remove-phase.md": "e262654e319d1bc4", "gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754", "gsd-core/workflows/resume-project.md": "40db7f350f5866d8", - "gsd-core/workflows/review.md": "01b40d7ddd69dc8b", + "gsd-core/workflows/review.md": "3a6786b46734397e", "gsd-core/workflows/scan.md": "dcc2f76d0850e2fb", "gsd-core/workflows/secure-phase.md": "d87bd706f85bcad6", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/cursor.json b/tests/fixtures/golden-install-parity/cursor.json index 62441d144..2b9bf4dad 100644 --- a/tests/fixtures/golden-install-parity/cursor.json +++ b/tests/fixtures/golden-install-parity/cursor.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "ada8a0546c686483", "gsd-core/workflows/remove-workspace.md": "433affcd1a200826", "gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96", - "gsd-core/workflows/review.md": "338005f1da182bdc", + "gsd-core/workflows/review.md": "c2ce42a245fcc972", "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "c55975672c4e1895", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/hermes.json b/tests/fixtures/golden-install-parity/hermes.json index 6757b3c2c..816be6a81 100644 --- a/tests/fixtures/golden-install-parity/hermes.json +++ b/tests/fixtures/golden-install-parity/hermes.json @@ -284,7 +284,7 @@ "gsd-core/workflows/remove-phase.md": "fce799aae3ab2715", "gsd-core/workflows/remove-workspace.md": "8facde381657dd71", "gsd-core/workflows/resume-project.md": "a0443839f1f83c2d", - "gsd-core/workflows/review.md": "f11ffe5b46b50565", + "gsd-core/workflows/review.md": "41dc23cb171b336e", "gsd-core/workflows/scan.md": "b28f65d88c522767", "gsd-core/workflows/secure-phase.md": "f2957d4b88fb3746", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/kilo.json b/tests/fixtures/golden-install-parity/kilo.json index 21a1560ca..b6c10bd8e 100644 --- a/tests/fixtures/golden-install-parity/kilo.json +++ b/tests/fixtures/golden-install-parity/kilo.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "ada8a0546c686483", "gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7", "gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96", - "gsd-core/workflows/review.md": "3af2d510da581502", + "gsd-core/workflows/review.md": "bf7defbff23639ab", "gsd-core/workflows/scan.md": "47371c2073d6c0be", "gsd-core/workflows/secure-phase.md": "e8855104c1e0417c", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/kimi.json b/tests/fixtures/golden-install-parity/kimi.json index 81d6a4c32..0af4bc694 100644 --- a/tests/fixtures/golden-install-parity/kimi.json +++ b/tests/fixtures/golden-install-parity/kimi.json @@ -320,7 +320,7 @@ "gsd-core/workflows/remove-phase.md": "df9a45f0b1880999", "gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c", "gsd-core/workflows/resume-project.md": "f28da1200e4545f4", - "gsd-core/workflows/review.md": "f3c2e894941c9943", + "gsd-core/workflows/review.md": "fa3d682b94afcdcd", "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/opencode.json b/tests/fixtures/golden-install-parity/opencode.json index 87e595d16..7adc67017 100644 --- a/tests/fixtures/golden-install-parity/opencode.json +++ b/tests/fixtures/golden-install-parity/opencode.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "dea4661e8f89596f", "gsd-core/workflows/remove-workspace.md": "446847e71aa52504", "gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0", - "gsd-core/workflows/review.md": "94ff56aecbbe2753", + "gsd-core/workflows/review.md": "8e4d4a67f4b16419", "gsd-core/workflows/scan.md": "ad8ebcad4626d4a8", "gsd-core/workflows/secure-phase.md": "e9a488cec3b4efdc", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/qwen.json b/tests/fixtures/golden-install-parity/qwen.json index 40444eb7b..942069296 100644 --- a/tests/fixtures/golden-install-parity/qwen.json +++ b/tests/fixtures/golden-install-parity/qwen.json @@ -284,7 +284,7 @@ "gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0", "gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e", "gsd-core/workflows/resume-project.md": "7f20769f302e5427", - "gsd-core/workflows/review.md": "bb5c36262d5ff951", + "gsd-core/workflows/review.md": "6c639036a133a00a", "gsd-core/workflows/scan.md": "949692db4834dd27", "gsd-core/workflows/secure-phase.md": "6758f1acf4113e9e", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/trae.json b/tests/fixtures/golden-install-parity/trae.json index 00087a04b..46464ac7f 100644 --- a/tests/fixtures/golden-install-parity/trae.json +++ b/tests/fixtures/golden-install-parity/trae.json @@ -284,7 +284,7 @@ "gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86", "gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663", "gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2", - "gsd-core/workflows/review.md": "34a23b9b65fb55f6", + "gsd-core/workflows/review.md": "388c7e1a884767bb", "gsd-core/workflows/scan.md": "63631467651d9ca8", "gsd-core/workflows/secure-phase.md": "6cc236e53c2e7d56", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/windsurf.json b/tests/fixtures/golden-install-parity/windsurf.json index a4a2eba24..417874a02 100644 --- a/tests/fixtures/golden-install-parity/windsurf.json +++ b/tests/fixtures/golden-install-parity/windsurf.json @@ -284,7 +284,7 @@ "gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b", "gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a", "gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085", - "gsd-core/workflows/review.md": "0873ea94ca23383c", + "gsd-core/workflows/review.md": "846e5693ea0cba84", "gsd-core/workflows/scan.md": "12c11b2edc165df9", "gsd-core/workflows/secure-phase.md": "7bf923689bf58288", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/fixtures/golden-install-parity/zcode.json b/tests/fixtures/golden-install-parity/zcode.json index 3d05f6e97..8ece548c5 100644 --- a/tests/fixtures/golden-install-parity/zcode.json +++ b/tests/fixtures/golden-install-parity/zcode.json @@ -355,7 +355,7 @@ "gsd-core/workflows/remove-phase.md": "df9a45f0b1880999", "gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c", "gsd-core/workflows/resume-project.md": "f28da1200e4545f4", - "gsd-core/workflows/review.md": "f3c2e894941c9943", + "gsd-core/workflows/review.md": "fa3d682b94afcdcd", "gsd-core/workflows/scan.md": "003883d71c37da7d", "gsd-core/workflows/secure-phase.md": "29fc6b62c5c5dc62", "gsd-core/workflows/session-report.md": "2e5b1205324ddefa", diff --git a/tests/review-default-reviewers-workflow.test.cjs b/tests/review-default-reviewers-workflow.test.cjs index 4e29db3ac..3c9245551 100644 --- a/tests/review-default-reviewers-workflow.test.cjs +++ b/tests/review-default-reviewers-workflow.test.cjs @@ -171,36 +171,66 @@ const path = require('node:path'); const reviewPath = path.resolve(__dirname, '..', 'gsd-core', 'workflows', 'review.md'); const read = () => fs.readFileSync(reviewPath, 'utf-8'); -describe('bug #687: agy print mode must be bounded via its native --print-timeout', () => { - test('invokes agy with its own --print-timeout flag (not an external killer)', () => { - assert.match(read(), /agy --print-timeout \d+s? -p "\$\(cat/, - 'review.md must cap agy through `agy --print-timeout -p …` (the tool\'s own mechanism)'); +describe('bug #687 → #2073: agy print mode bounded by --print-timeout PAIRED with an external timeout', () => { + // #687 established that agy print mode must be bounded (its native + // --print-timeout, default 5m). #2073 superseded the "no external killer" + // half of that contract with documentation: + // - agy's own print-mode guidance says to PAIR --print-timeout with an + // external terminal `timeout` ("Pair with the terminal timeout= so the + // outer call doesn't cut the run short"), because --print-timeout cannot + // fire before agy creates a session (a pre-session stall otherwise hangs + // unbounded). The external cap is set HIGHER than --print-timeout so it + // only backstops a stall, never cuts a healthy run. + // - agy gained `--model` in ~1.0.3 (issue #3782's "no --model flag" note + // was correct at the time, stale now); review.models.agy is passed as + // --model so a pinned model that 404s has an escape hatch. + // - the prompt is now a file reference: inline `-p "$(cat …)"` overflows + // the exec arg list on a large review prompt (Linux MAX_ARG_STRLEN + // 128 KB/single-arg → rc 126). + + test('invokes agy with --print-timeout AND a paired external timeout', () => { + const c = read(); + assert.match(c, /--print-timeout \d+s?/, 'review.md must pass agy its native --print-timeout'); + // External `timeout agy …` — the paired backstop per agy guidance. + assert.match(c, /(^|\s)timeout\s+\d+\s+agy\b/, + 'review.md must wrap agy in an external `timeout agy` (agy guidance pairs --print-timeout with a terminal timeout)'); }); - test('discards partial output on non-zero exit so the fallback fires', () => { + test('external timeout is >= --print-timeout so it never cuts a healthy run', () => { + const c = read(); + const external = c.match(/timeout\s+(\d+)\s+agy\b/); + const native = c.match(/--print-timeout\s+(\d+)s/); + assert.ok(external && native, 'both the external timeout and --print-timeout must be present'); + assert.ok( + Number(external[1]) >= Number(native[1]), + 'external `timeout` (seconds) must be >= --print-timeout (seconds) so it only backstops a stall', + ); + }); + + test('uses a file-reference prompt, not inline "$(cat …)" (arg-list overflow, #2073)', () => { + const c = read(); + assert.doesNotMatch(c, /agy[^\n]*-p "\$\(cat/, + 'review.md must not feed agy the prompt inline via "$(cat …)" — a large review prompt overflows the exec arg list (rc 126)'); + assert.match(c, /agy[^\n]*-p "Read the file at \/tmp\/gsd-review-prompt-/, + 'review.md should pass agy a file-reference prompt (mirrors the Cursor block)'); + }); + + test('wires --model from review.models.agy (#2073 mode 2; agy gained --model in ~1.0.3)', () => { + assert.match(read(), /--model "\$AGY_MODEL"/, + 'review.md must pass --model "$AGY_MODEL" when review.models.agy is set'); + }); + + test('discards partial output on non-zero exit so the fallback fires (#687)', () => { const c = read(); assert.match(c, /_AGY_RC.*-ne 0/, 'review.md must check the agy exit code'); assert.match(c, /: > \/tmp\/gsd-review-antigravity-/, 'review.md must truncate the output file when agy timed out / failed'); }); - test('agy is bounded only by its own --print-timeout, not an external process killer', () => { - const c = read(); - // Print-mode reviewers invoke the tool directly; agy must self-terminate via - // --print-timeout, never via an external SIGKILL/timeout binary wrapped around it. - assert.doesNotMatch(c, /-s KILL/, 'must not SIGKILL agy from the outside'); - // Any external timeout binary wrapping agy — `timeout 300s agy …`, - // `gtimeout 300 agy …`, `timeout -s KILL 300 agy …`. The lookbehind keeps - // agy's own `--print-timeout` flag from tripping it. - assert.doesNotMatch(c, /(? { - // A bare `agy -p "$(cat …)"` with no cap was the original hang. - assert.doesNotMatch(read(), /^agy -p "\$\(cat/m, - 'review.md must not invoke agy -p without --print-timeout'); + // A bare `agy -p …` with no cap was the original #687 hang. + assert.doesNotMatch(read(), /^agy -p/m, + 'review.md must not invoke a bare `agy -p` unbounded at line start'); }); }); });