mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 06:24:41 +01:00
v1.92.7.0 fix: /ship's review pass works on large diffs again (GST-507) (#3736)
* fix(review): gstack-review-plan and review-dispatch let stdout drain before exit, so a large plan JSON reaches pass whole (GST-507) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (gstack-review-dispatch: a BriefError no longer falls through to the rethrow, GST-507) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
ed107918db
commit
5b7b0950b8
8 files changed
+37
-7
No files matched your search
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## [1.92.7.0] - 2026-11-01
|
||||
|
||||
- `gstack-review-dispatch pass` no longer dies on a large diff. `gstack-review-plan` exited before its stdout pipe drained, so a plan JSON past about 128 KB (1.39 MB on a 4,000-file results PR) reached `pass` cut short and failed to parse. Both bins now set the exit code and let their output drain, and a 4,000-file test keeps it that way (GST-507).
|
||||
|
||||
## [1.92.6.0] - 2026-11-01
|
||||
|
||||
**Choosing "core" now means core everywhere gstack routes, and the four core skills carry their plain-English trigger phrases where hosts read them.** Under the default `proactive: core`, the CLAUDE.md routing section gstack offered still listed all 13 routing rules, and 16 non-core skills still told Codex and the other hosts to "proactively suggest" them (#3446). Separately, phrases like "test the site" or "ship it" lived only in each skill's `triggers:` metadata, which no host reads, so they never reached the list the agent picks skills from (#2118). The always-loaded Claude catalog does not grow. It measures 5,682 bytes before and 5,678 after (1,421 → 1,420 token-equivalents against the unchanged 1,423 ceiling).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.92.6.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.92.7.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
@@ -345,7 +345,6 @@ function main(): number {
|
||||
return EXIT.usage;
|
||||
}
|
||||
|
||||
try { process.exit(main()); } catch (e) {
|
||||
if (e instanceof BriefError) { process.stderr.write(`BRIEF_RENDER_FAILED: ${e.message}\n`); process.exit(EXIT.fail); }
|
||||
throw e;
|
||||
try { process.exitCode = main(); } catch (e) {
|
||||
if (e instanceof BriefError) { process.stderr.write(`BRIEF_RENDER_FAILED: ${e.message}\n`); process.exitCode = EXIT.fail; } else throw e;
|
||||
}
|
||||
@@ -167,4 +167,4 @@ function main(): number {
|
||||
return EXIT.ok;
|
||||
}
|
||||
|
||||
process.exit(main());
|
||||
process.exitCode = main();
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.92.6",
|
||||
"version": "1.92.7",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -86,6 +86,14 @@ describe('gstack-review-dispatch render: briefs from the sections, slots only',
|
||||
expect(run(['render', '--role', 'specialist', '--name', 'no-such-specialist', '--out', out, '--diff-base', SHA]).stderr).toContain('BRIEF_RENDER_FAILED: checklist not found');
|
||||
});
|
||||
|
||||
// Value: protects=a brief-render refusal reads as one clean line, not a crash (GST-507 moved the bin to process.exitCode);
|
||||
// fails_when=the BriefError handler falls through to its rethrow and Bun dumps source and a stack after the line; seam=gstack-review-dispatch render
|
||||
test('a BriefError prints only its BRIEF_RENDER_FAILED line and exits 1', () => {
|
||||
const r = run(['render', '--role', 'specialist', '--name', 'no-such-specialist', '--out', tmp(), '--diff-base', SHA]);
|
||||
expect(r.status).toBe(1);
|
||||
expect(r.stderr.trim().split('\n')).toEqual([expect.stringMatching(/^BRIEF_RENDER_FAILED: checklist not found/)]);
|
||||
});
|
||||
|
||||
// Value: protects=the lean ensemble's one batched reader (GSTA-167 part 2) gets checklist paths when the parent passes the plan's
|
||||
// checklists= names as printed; fails_when=the brief names bare checklist words a reader cannot open, or an unknown name
|
||||
// renders; seam=gstack-review-dispatch render
|
||||
|
||||
@@ -226,6 +226,25 @@ describe('the targeted reader in a pass (GST-257 on main)', () => {
|
||||
}, 180_000);
|
||||
});
|
||||
|
||||
describe('a large diff (GST-507)', () => {
|
||||
// Value: protects=a pass on a results PR with thousands of files reads gstack-review-plan's whole JSON plan through the pipe and stages its readers; fails_when=either bin exits before its stdout drains, so the plan JSON arrives cut short and pass dies on JSON.parse;
|
||||
// why_new=HARN-1 hit it on a 4,000-file receipt tree (a 1.39 MB plan); seam=none; no_claim=what reviewers read on a large diff (GST-508)
|
||||
test('a 4,000-file diff round-trips a plan JSON over 1 MB and pass stages its readers', () => {
|
||||
const { dir, env } = repo();
|
||||
const files = 4000;
|
||||
const made = spawnSync('bash', ['-c', `for d in $(seq 1 40); do p=receipts/harness-evals-lane-b-results-run-$d/per-case-receipts/skill-behavior-panel; mkdir -p $p; for i in $(seq 1 100); do echo "{\\"case\\":$i}" > $p/receipt-for-case-number-$i-trial-a.json; done; done && git add -A && git commit -qm receipts`], { cwd: dir, env, encoding: 'utf8', timeout: 120_000 });
|
||||
expect(made.status, made.stderr).toBe(0);
|
||||
const plan = spawnSync(path.join(BIN, 'gstack-review-plan'), ['--base', 'main', '--json'], { cwd: dir, env, encoding: 'utf8', timeout: 120_000, maxBuffer: 64 * 1024 * 1024 });
|
||||
expect(plan.status, plan.stderr).toBe(0);
|
||||
expect(plan.stdout.length).toBeGreaterThan(1_000_000);
|
||||
expect((JSON.parse(plan.stdout) as { paths: string[] }).paths.filter(p => p.startsWith('receipts/'))).toHaveLength(files);
|
||||
const out = path.join(dir, '.state/run/p1');
|
||||
const p = spawnSync(path.join(BIN, 'gstack-review-dispatch'), ['pass', '--base', 'main', '--out', out, '--pass', 'big-p1'], { cwd: dir, env, encoding: 'utf8', timeout: 180_000 });
|
||||
expect(p.status, p.stdout + p.stderr).toBe(0);
|
||||
expect(p.stdout).toMatch(/^MANIFEST: file=.* pass=big-p1 readers=[1-9]/m);
|
||||
}, 300_000);
|
||||
});
|
||||
|
||||
describe('Capy pass program (fake host, real bins)', () => {
|
||||
test('gstack-review-dispatch program serves the checked-in file verbatim with its sha', () => {
|
||||
const r = spawnSync(path.join(BIN, 'gstack-review-dispatch'), ['program'], { encoding: 'utf8', timeout: 30_000 });
|
||||
|
||||
Reference in new issue
Block a user