Files
gstack/test/investigate-recipes.test.ts
Garry Tanandcapy-ai[bot] c1900088d5 v1.91.272.0 feat(skills): absorb pstack's blast radius, transcript recall, vet-the-number and design-twice as lazy passes (#3500)
* feat(skills): absorb pstack's blast radius, transcript recall, vet-the-number and design-twice as lazy sections

/investigate gains sections/blast-radius.md (the one safety fact, the five-rung
evidence ladder, look where grep stops, confirmed and cleared risks apart) with a
Phase 4 pointer, and the shared-premise red flag before the 3-strike rule.
/context-restore is carved for the first time: on NO_CHECKPOINTS, sections/transcripts.md
mines this workspace's own transcripts (host stores declared in hosts/*.ts and rendered
by TRANSCRIPT_SOURCES / TRANSCRIPT_DISCOVERY), a fixed 7-day window, never another
project's folder, subagents return only findings, redaction before anything leaves,
git and gh verify every artifact, a status-tagged brief. /benchmark is carved for the
first time: sections/vet-the-number.md asks the seven questions before Phase 5 reports
a comparison. /plan-eng-review gains sections/design-twice.md (a second structurally
distinct shape compared on interface depth, the eight design red flags), bound by the
autoplan methodology snapshot alongside review-sections.md.

Ideas from pstack (Lauren Tan, MIT), rewritten in gstack's words; NOTICE.md. Garry,
2026-10-26: "Do it, send". Guards: carve-guards entries and measured lines, eleven
plan-eng-review pins re-measured, periodic carve-section-loading cases for the two new
carves, free tests with a fixture transcript.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(pstack-c1): fold design-twice into Section 1 of the eng review section, per-host transcript table, fixture and slice-cap repairs

The remote free run reported nine failures in seven files, all from this
branch: four tests encode plan-eng-review's one companion section
(eng-scope-entry-ap, autoplan-methodology-names, plan-review-cases,
review-checklists), so the design-twice pass now lives inside Section 1 of
sections/review-sections.md with the skeleton, manifest and
bin/gstack-autoplan-snapshot.ts back at main; the Codex render of
context-restore named ~/.claude/ through the all-hosts transcript table, so
{{TRANSCRIPT_SOURCES}} renders this host's row only; the transcript fixture
is declared in FREE_FIXTURES; and the two new periodic cases took the census
to 39 slices, so evals-periodic.yml's eval-slices max-parallel goes 38 -> 40.
Pins: 11 plan-eng-review re-pins at +2,896; maxSizeRatio 1.2400 (measured
1.2389); Codex gpt-5.4 overlay 159,588 bytes.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes

The discovery block takes the none branch from hosts/*.ts's transcripts
declaration instead of a host-name fallthrough, and the resolver header says
where each half lives; the T2 subagent brief frames the transcript as data,
not instructions; the test asserts table/block agreement per host, imports
CAPY_NO_TRANSCRIPTS, and drops the fixture-shape case nothing reads.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: ADDING_A_HOST.md lists the optional transcripts HostConfig field

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>
2026-10-27 09:59:09 +00:00

123 lines
8.1 KiB
TypeScript

/**
* Plan D3: investigate recipes and backlog mode. Pins the carve shape (manifest
* ids, STOP pointers, the Arguments table routing `--backlog`), the tokens each
* recipe must carry (commands, the `## Flake evidence` section, the four backlog
* classes, the never-retry rule), the Phase 1 additions, the benchmark
* `git update-index --refresh` line, and that an external host gets the recipes
* inlined. Prose is not pinned.
*/
import { describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { CARVE_GUARDS } from './helpers/carve-guards';
const ROOT = path.resolve(import.meta.dir, '..');
const read = (rel: string) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
const RECIPES = ['flake', 'polluter', 'race', 'eval-bisect', 'backlog', 'blast-radius'] as const;
describe('investigate carve', () => {
const manifest = JSON.parse(read('investigate/sections/manifest.json')) as { skill: string; sections: Array<{ id: string; file: string; trigger: string }> };
const skeleton = read('investigate/SKILL.md');
test('manifest lists the six recipes and the registry guards them', () => {
expect(manifest.skill).toBe('investigate');
expect(manifest.sections.map(s => s.id)).toEqual([...RECIPES]);
expect(CARVE_GUARDS.investigate!.expectedSections).toEqual(manifest.sections.map(s => s.file));
for (const s of manifest.sections) expect(fs.existsSync(path.join(ROOT, 'investigate', 'sections', `${s.file}.tmpl`))).toBe(true);
});
test('the skeleton routes: Arguments table with --backlog before the first STOP, one STOP per recipe, recipes after the Phase 2 pattern table', () => {
const args = skeleton.indexOf('## Arguments');
const firstStop = skeleton.indexOf('> **STOP.**');
expect(args).toBeGreaterThan(0);
expect(args).toBeLessThan(firstStop);
expect(skeleton).toMatch(/\| `\/investigate --backlog \[<issue numbers or gh query>\]` \| Read-only triage/);
for (const id of RECIPES) expect(skeleton).toContain(`sections/${id}.md`);
expect(skeleton.match(/> \*\*STOP\.\*\*/g)!.length).toBe(RECIPES.length);
const recipes = skeleton.indexOf('### Recipes');
expect(recipes).toBeGreaterThan(skeleton.indexOf('## Phase 2: Pattern Analysis'));
expect(recipes).toBeLessThan(skeleton.indexOf('## Phase 3: Hypothesis Testing'));
expect(skeleton).not.toContain('## Flake recipe');
});
test('Phase 1 additions: call sites before calling two queries duplicates; update-index before timing git in a worktree (investigate and benchmark)', () => {
const phase1 = skeleton.slice(skeleton.indexOf('## Phase 1: Root Cause Investigation'), skeleton.indexOf('## Scope Lock'));
expect(phase1).toMatch(/Before calling two queries, helpers or code paths duplicates, read their call sites/);
expect(phase1).toContain('`git update-index --refresh`');
const benchmark = read('benchmark/SKILL.md');
expect(benchmark).toContain('git update-index --refresh >/dev/null 2>&1 || true');
expect(benchmark).toMatch(/before timing anything that spawns git/);
});
});
describe('recipe tokens', () => {
test('flake: reproduce on main, N runs, gh run list, forced probe, never loosen, rerun rule, Flake evidence section, paid evals never retried', () => {
const flake = read('investigate/sections/flake.md');
expect(flake).toContain('## Flake recipe');
expect(flake).toContain('git worktree add <dir> origin/<base>');
expect(flake).toContain('ship-measure.ts free --files <file> --reruns N');
expect(flake).toContain('gh run list --branch <base> --workflow free-tests.yml --status success --limit 20');
expect(flake).toContain('**Forced probe.**');
expect(flake).toContain('**Never loosen.**');
expect(flake).toContain('P(fail | regression)');
expect(flake).toContain('RH-15');
expect(flake).toContain('**A rerun only when the cause is known and outside the PR**');
expect(flake).toContain('`## Flake evidence` in the PR body (required for any flake fix)');
expect(flake).toMatch(/Paid evals\s+are never retried \(`EVAL_POLICY`/);
expect(flake).toContain('routing note');
});
test('polluter: CI file order, ship-measure free --files bisect, the leak list, the POLLUTER report line', () => {
const polluter = read('investigate/sections/polluter.md');
expect(polluter).toContain('bun --no-env-file --config=/dev/null run scripts/ship-measure.ts free --shard <I>');
expect(polluter).toContain('--files <affected-files> --reruns 3');
expect(polluter).toContain('**Bisect the earlier files.**');
expect(polluter).toContain('POLLUTER: <file> leaked <what> into <victim>; fixed by <change>');
expect(polluter).toContain('**Fix the polluter, not the victim.**');
});
test('race: timing probes, forced interleaving, the concurrent-writer template', () => {
const race = read('investigate/sections/race.md');
expect(race).toContain('**Timing probes.**');
expect(race).toContain('**Forced interleaving.**');
expect(race).toContain('Promise.allSettled(Array.from({ length: N }, (_, i) => writer(i)))');
expect(race).toMatch(/N ≥ 8/);
});
test('eval-bisect: distinct revisions, cached cheap scorer, detached runs with the sentinel, never retry a paid case', () => {
const bisect = read('investigate/sections/eval-bisect.md');
expect(bisect).toContain('**Distinct revisions only.**');
expect(bisect).toContain('.gstack/tmp/eval-bisect/');
expect(bisect).toContain('### gstack-detach EXIT=<code> ###');
expect(bisect).toContain('Never retry a paid case');
});
test('backlog: the four classes, repro required for still_open, contributor PRs evidence only, read-only table, no closing', () => {
const backlog = read('investigate/sections/backlog.md');
for (const cls of ['`still_open`', '`already_fixed <commit>`', '`partially_fixed`', '`owned_elsewhere`']) expect(backlog).toContain(cls);
expect(backlog).toMatch(/A reproduction is\s+required/);
expect(backlog).toContain('`repro needed`');
expect(backlog).toContain('**Contributor PRs are evidence only.**');
expect(backlog).toContain('| issue | class | receipt (commit / repro / link) | contributor PR (evidence) | next |');
expect(backlog).toMatch(/no `gh issue close`/);
expect(backlog).not.toMatch(/gh issue close <n>/);
});
test('blast-radius: the one safety fact, the five-rung ladder, look where grep stops, confirmed and cleared risks apart, the Phase 4 pointer and the shared-premise red flag', () => {
const blast = read('investigate/sections/blast-radius.md');
expect(blast).toContain('## Blast radius');
expect(blast).toContain('**Name the one fact it is safe because of.**');
expect(blast).toContain('**Look where grep stops.**');
expect(blast).toMatch(/1\. You said so\.[\s\S]*5\. You reproduced it in the running app\./);
expect(blast).toContain('**Keep confirmed risks and cleared risks apart.**');
expect(blast).toContain('the word "unproven"');
const skeleton = read('investigate/SKILL.md');
const phase4 = skeleton.slice(skeleton.indexOf('## Phase 4: Implementation'), skeleton.indexOf('## Phase 5: Verification & Report'));
expect(phase4).toContain('sections/blast-radius.md');
expect(phase4.indexOf('sections/blast-radius.md')).toBeLessThan(phase4.indexOf('**Fix the root cause, not the symptom.**'));
const phase3 = skeleton.slice(skeleton.indexOf('## Phase 3: Hypothesis Testing'), skeleton.indexOf('## Phase 4: Implementation'));
expect(phase3).toMatch(/Two fixes that shared one premise failed the same gate — write the premise down in one sentence and test it before a third fix/);
});
});
describe('external hosts inline the recipes', () => {
const codex = path.join(ROOT, '.agents', 'skills', 'gstack-investigate', 'SKILL.md');
test.skipIf(!fs.existsSync(codex))('the codex render carries every recipe heading and no section pointer', () => {
const text = fs.readFileSync(codex, 'utf8');
for (const heading of ['## Flake recipe', '## Polluter recipe', '## Race recipe', '## Eval bisect recipe', '## Backlog mode', '## Blast radius']) expect(text).toContain(heading);
expect(text).not.toContain('sections/flake.md');
});
});