mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 07:18:03 +01:00
1. A gate with a scope change (uc1) and a packaging change (uc2) answered `rec` deadlocked: after `revise --plan v2 --incorporates uc1` reopened the phases, `revise --settle uc2` compared against the first phase snapshot (now undefined) and was refused forever. Settle evidence is now the plan hash recorded when the owner accepted the change (Amendment.plan_sha256) or a later plan_revised hash, plus "the plan file has no unrevised edits". `--settle` may ride the same revise as `--plan/--incorporates` and binds to the revision's hash. Settling the last amendment after a reopen leaves the run `running` (the reopened phases rerun) instead of approving it. NEXT and PLAN_AMEND_PENDING list the settle step before the edit step. 7. A title association alone no longer confirms: the same remedy confirms, opposite remedies (one keeps, drops or negates what the other changes) disagree, different or missing remedies stay `unverified`. The pairing fixture gains same-title negative controls (opposite and different remedies) and a same-remedy positive, for ordinary findings and user challenges. 8. A gate.json without rec_rev (written by 1.91.101.0, whose render said `all` leaves b items pending) refuses rec/all/approve all/`all except` with GATE_REC_STALE until a re-render binds --rec-hash; explicit tokens still answer. New gates carry rec_rev: 0. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
64 lines
4.6 KiB
TypeScript
64 lines
4.6 KiB
TypeScript
/**
|
|
* Pairing negative control (next-wave B2/B-a): finding pairs copied verbatim
|
|
* from a real autoplan run. File:line proximity associates findings but never
|
|
* confirms them, and neither does a title match alone: a title match with
|
|
* the same remedy confirms, opposite remedies disagree, different remedies stay
|
|
* unverified; the parent's override with one shared proposal also confirms.
|
|
*/
|
|
import { describe, expect, test } from 'bun:test';
|
|
import fixture from './fixtures/autoplan-pairing/run-20261014-pairs.json';
|
|
import { reconcile } from '../lib/autoplan-reconcile';
|
|
import { challengeDefault } from '../lib/autoplan-gate';
|
|
|
|
const pair = (name: string) => fixture.pairs.find(p => p.name.startsWith(name))! as any;
|
|
const challenge = (rows: any[], changes = 'direction') => rows.map(r => ({ ...r, user_challenge: true, changes }));
|
|
|
|
describe('pairing negative control from a real run', () => {
|
|
test('two findings three lines apart on one file are associated but never confirmed, and never recommend b', () => {
|
|
const p = pair('ceo r1 outside-1');
|
|
const [row] = reconcile(p.native, p.outside).rows;
|
|
expect(row).toMatchObject({ disposition: 'unverified', basis: 'file_line', native_id: p.native[0].id });
|
|
expect(row!.resolution).toStartWith('same location, agreement unverified (bin/gstack-capy-install:197)');
|
|
const [n] = challenge(p.native), [o] = challenge(p.outside);
|
|
expect(challengeDefault(row!, n, o)).toMatchObject({ recommended: 'a', basis: 'plan direction; same location, agreement unverified' });
|
|
});
|
|
|
|
test('a location match with different titles stays unverified until the parent confirms it with one proposal', () => {
|
|
const p = pair('dx r2 outside-2');
|
|
const [row] = reconcile(p.native, p.outside).rows;
|
|
expect(row!.disposition).toBe('unverified');
|
|
const [confirmed] = reconcile(p.native, p.outside, [{ outside_id: p.outside[0].id, native_id: p.native[0].id, disposition: 'confirmed', resolution: 'same trailer grammar break', proposal: 'keep `gstack: fail <components>` and specify the pins exit' }]).rows;
|
|
expect(challengeDefault(confirmed!, challenge(p.native, 'scope')[0], challenge(p.outside, 'scope')[0])).toMatchObject({ recommended: 'b', basis: 'both voices; shared proposal' });
|
|
});
|
|
|
|
test('the same problem on different files is new until the parent associates it; different remedies keep a', () => {
|
|
const p = pair('dx r2 native-2');
|
|
expect(reconcile(p.native, p.outside).rows[0]).toMatchObject({ disposition: 'new', native_id: null });
|
|
const [row] = reconcile(p.native, p.outside, [{ outside_id: p.outside[0].id, native_id: p.native[0].id, disposition: 'confirmed', resolution: 'both: a recommendation edit changes what a seen reply resolves to' }]).rows;
|
|
expect(challengeDefault(row!, challenge(p.native, 'scope')[0], challenge(p.outside, 'scope')[0])).toMatchObject({ recommended: 'a', basis: 'plan direction; remedies differ; no shared proposal' });
|
|
});
|
|
|
|
test('the same title with opposite remedies disagrees, as an ordinary finding and as a user challenge; never b', () => {
|
|
const p = pair('constructed same title, opposite');
|
|
const [row] = reconcile(p.native, p.outside).rows;
|
|
expect(row).toMatchObject({ disposition: 'disagree', basis: 'title' });
|
|
expect(row!.resolution).toStartWith('remedies conflict (same title): native "move attempts into a new retry_attempts table"');
|
|
const [n] = challenge(p.native, 'scope'), [o] = challenge(p.outside, 'scope');
|
|
const [crow] = reconcile([n], [o]).rows;
|
|
expect(crow!.disposition).toBe('disagree');
|
|
expect(challengeDefault(crow!, n, o)).toMatchObject({ recommended: 'a', basis: 'plan direction; voices disagree' });
|
|
});
|
|
|
|
test('the same title with different remedies is only associated; the same remedy confirms and recommends b', () => {
|
|
const differ = pair('constructed same title, different');
|
|
const [row] = reconcile(differ.native, differ.outside).rows;
|
|
expect(row).toMatchObject({ disposition: 'unverified' });
|
|
expect(row!.resolution).toStartWith('same title, remedies differ');
|
|
expect(challengeDefault(row!, challenge(differ.native, 'scope')[0], challenge(differ.outside, 'scope')[0])).toMatchObject({ recommended: 'a', basis: 'plan direction; remedies differ; no shared proposal' });
|
|
const same = pair('constructed same title, same remedy');
|
|
const [ok] = reconcile(same.native, same.outside).rows;
|
|
expect(ok).toMatchObject({ disposition: 'confirmed', basis: 'title' });
|
|
expect(challengeDefault(ok!, challenge(same.native, 'scope')[0], challenge(same.outside, 'scope')[0])).toMatchObject({ recommended: 'b', basis: 'both voices; same remedy' });
|
|
});
|
|
});
|