mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-08 09:48:20 +01:00
fix(ship-release): a completed fix-check's range counts as reviewed at finish; fix-check rows carry completed/converged (GST-1130)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
14d7414af3
commit
7a71d27d66
6 files changed
+117
-4
No files matched your search
@@ -191,6 +191,23 @@ describe('lib: deriveFromFindings, bindReview order and reviewFreshness', () =>
|
||||
expect(reviewFreshness(bound, 'd'.repeat(64))?.status).toBe('STALE');
|
||||
});
|
||||
|
||||
// Value: protects=a fix-check row is written with the fields review-read verifies (GEVA-218: the row lacked them, so it read
|
||||
// UNVERIFIED and finish could not count it); fails_when=the logger accepts a review-fix-check row without completed and
|
||||
// converged, or consumes the start token while refusing it; seam=bindReview
|
||||
test('a review-fix-check row without completed and converged is refused and keeps its start token (GST-1130)', () => {
|
||||
const dir = path.join(home, 'starts', '.review-starts');
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
const token = '77777777-2222-3333-4444-555555555555';
|
||||
const w = 'e'.repeat(64);
|
||||
fs.writeFileSync(path.join(dir, `${token}.json`), JSON.stringify({ skill: 'review-fix-check', repo: '/r', branch: 'b', wtree: w, started_at: 't' }));
|
||||
const env = { GSTACK_REVIEW_DIR: path.join(home, 'starts'), GSTACK_REVIEW_REPO: '/r', GSTACK_REVIEW_BRANCH: 'b', GSTACK_STAMP_WTREE: w };
|
||||
for (const row of [{}, { completed: true }, { converged: true }, { completed: 'yes', converged: true }]) {
|
||||
expect(() => bindReview({ skill: 'review-fix-check', status: 'clean', ...row }, token, env)).toThrow('review-log: a review-fix-check row must carry completed and converged as true or false');
|
||||
}
|
||||
expect(fs.existsSync(path.join(dir, `${token}.json`))).toBe(true);
|
||||
expect(bindReview({ skill: 'review-fix-check', status: 'clean', completed: true, converged: true }, token, env).review_binding.state).toBe('verified');
|
||||
});
|
||||
|
||||
test('reviewFreshness reads findings_open: resolved criticals are CURRENT, claimed issues_found stays conservative', () => {
|
||||
const w = 'b'.repeat(64);
|
||||
const base = { skill: 'review', completed: true, converged: true, wtree: w, review_binding: { state: 'verified', start_wtree: w, end_wtree: w } };
|
||||
|
||||
@@ -739,3 +739,51 @@ describe('finish with an owner-accepted edit after a fix-check (GST-965, GBRA-27
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Value: protects=a granted fix-check's clean read clears the paths it reviewed at Step 16, a gate-edited test included, so
|
||||
// finish does not send them back through Steps 5–11.5 (GEVA-218, gstack-evals#168); fails_when=finish routes a path inside
|
||||
// the fix-check's range as behavior, counts a path edited after the fix-check, or counts a row that is not a completed clean
|
||||
// fix-check with a range base; seam=finish's route reading the review log; no_claim=Step 11.5's binding
|
||||
describe('finish after a completed fix-check (GST-1130, GEVA-218)', () => {
|
||||
const TEST_V1 = "import { expect, test } from 'bun:test';\ntest('a', () => { expect(2).toBe(2); });\n";
|
||||
const TEST_V2 = "import { expect, test } from 'bun:test';\ntest('a', () => { expect(2).toBe(2); }, 1_000);\ntest('b finishes fast', () => { expect(1 + 1).toBe(2); }, 1_000);\n";
|
||||
const logFixCheck = (f: ShipFx, row: Record<string, unknown>) => {
|
||||
const start = f.run(path.join(BIN, 'gstack-review-log'), ['--start', 'review-fix-check']);
|
||||
expect(start.status).toBe(0);
|
||||
return f.run(path.join(BIN, 'gstack-review-log'), [JSON.stringify({ skill: 'review-fix-check', timestamp: '2026-11-02T00:00:00Z', via: 'ship', review_scope: 'frozen', status: 'clean', issues_found: 0, critical: 0, ...row }), '--finish', start.stdout.trim()]);
|
||||
};
|
||||
const afterFixCheck = () => {
|
||||
const f = fx({ 'src/x.ts': 'export const x = 1;\n' }, { 'src/x.ts': 'export const x = 2;\n', 'test/x.test.ts': TEST_V1 });
|
||||
const passHead = f.git('rev-parse', 'HEAD').stdout.trim();
|
||||
const reviewed = wtree(f);
|
||||
f.write({ 'src/x.ts': 'export const x = 3;\n', 'test/x.test.ts': TEST_V2 }); f.commit('fix the round-2 blocker and bound its case');
|
||||
return { f, passHead, fixHead: f.git('rev-parse', 'HEAD').stdout.trim(), reviewed, post: snapshot(f) };
|
||||
};
|
||||
|
||||
test('paths inside a completed clean fix-check range count as reviewed, a gate-edited test included; a later edit still stops', () => {
|
||||
const { f, passHead, fixHead, reviewed, post } = afterFixCheck();
|
||||
const plain = finish(f, reviewed, post);
|
||||
expect([plain.status, plain.stdout.includes('STOP: behavior, tests or build inputs changed since review (src/x.ts,test/x.test.ts)')]).toEqual([3, true]);
|
||||
expect(logFixCheck(f, { completed: true, converged: true, range_base: passHead }).status).toBe(0);
|
||||
const r = finish(f, reviewed, post);
|
||||
expect(lineOf(r.stdout, 'FIX_CHECK')).toBe(`FIX_CHECK: reviewed src/x.ts,test/x.test.ts (fix-check at ${fixHead.slice(0, 12)}, range from ${passHead.slice(0, 12)})`);
|
||||
expect(lineOf(r.stdout, 'ROUTE')).toMatch(/ behavior=0 docs=0 metadata=0 tests=0 base=unknown context=0 fix_checked=2$/);
|
||||
expect([r.status, lineOf(r.stdout, 'EVIDENCE')]).toEqual([0, expect.stringMatching(/^EVIDENCE: FRESH label=tests /)]);
|
||||
f.write({ 'src/x.ts': 'export const x = 4;\n' }); f.commit('an edit after the fix-check');
|
||||
const late = finish(f, reviewed, post);
|
||||
expect(lineOf(late.stdout, 'FIX_CHECK')).toStartWith('FIX_CHECK: reviewed test/x.test.ts (');
|
||||
expect([late.status, late.stdout.includes('STOP: behavior, tests or build inputs changed since review (src/x.ts)')]).toEqual([3, true]);
|
||||
});
|
||||
|
||||
test('a fix-check row that did not finish clean, or names no range base, is not counted', () => {
|
||||
const { f, passHead, reviewed, post } = afterFixCheck();
|
||||
expect(logFixCheck(f, { completed: false, converged: false, range_base: passHead }).status).toBe(0);
|
||||
const incomplete = finish(f, reviewed, post);
|
||||
expect(lineOf(incomplete.stdout, 'FIX_CHECK')).toBe('FIX_CHECK: not counted (missing start capture or incomplete/nonconverged pass)');
|
||||
expect([incomplete.status, incomplete.stdout.includes('STOP: behavior, tests or build inputs changed since review (src/x.ts,test/x.test.ts)')]).toEqual([3, true]);
|
||||
expect(logFixCheck(f, { completed: true, converged: true }).status).toBe(0);
|
||||
const unranged = finish(f, reviewed, post);
|
||||
expect(lineOf(unranged.stdout, 'FIX_CHECK')).toBe('FIX_CHECK: not counted (the row names no range_base commit on this branch)');
|
||||
expect(unranged.status).toBe(3);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user