mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
* wip(GSTA-131): review depth, fix-check transitions, dispatch counters, carry pregate, autoplan round policy Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * wip(GSTA-131): reviewers stay at full effort; audits light Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship): Step 9 dispatches what gstack-review-plan plans; fix-check after below-floor fixes; cap two full passes plus one fix-check Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: review-dispatch-compare has a free lane; review-depth fixtures registered free; resumed max_rounds case off the old default Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(GSTA-131): ship review pass-1 repairs: fix-check binds under its own skill, Step 11.5 accepts a fix-check or a planned adversarial skip, delta carry with a pregate, carry --pregate pinned to the checkout, quiet-after-cap revise regates, skill-dir markdown and rule docs classed code, gate signals from tests class up, plan fails closed on git errors, per-class compare ratios Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(GSTA-131): ship review pass-2 repairs: Step 11 follows the last full-pass plan, Important Rules restored, ship goldens regenerated, true per-class medians, review-policy transitions and once-per-invocation voices, docs/** assets stay docs-class, CLI and fix-check binding tests Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): GSTA-131 review depth, fix-check, delta /ship and autoplan rounds Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: ship-policy cap and counters, review-policy /review cycle cap, unattended passes (docs audit) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(review): 400-word findings cap fits the /review size pins (five host pins lowered by 2); CHANGELOG max_rounds wording Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): gate integrity names the size-ledger entry and the lowered /review pins Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: the round-budget case asserts the default through ROUNDS output only (pregate literals heuristic) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: curate ship-receipt-carry-pregate out of the Windows lane like its --pregate siblings Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): end the Windows curation sentence instead of a mid-sentence colon Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship): one decision each for the review-loop exits, the planned adversarial skip and Step 16's TODOS rerun (screen judge repair) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship): fix-check repairs: pin the renamed Step 11 heading, keep the below-floor exit at the cap, name the plan run a repeat reuses, print the native skip on outside-only plans 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>
49 lines
3.9 KiB
TypeScript
49 lines
3.9 KiB
TypeScript
import { describe, test, expect } from 'bun:test';
|
|
import { summarizeReviewLoop, reviewLoopLine, FIX_CHECK_SKILL } from '../lib/review-scope';
|
|
import { parseReviewLoop, receiptSummaryLine } from '../lib/ship-receipt';
|
|
|
|
const since = '2026-10-27T10:00:00Z';
|
|
const row = (o: Record<string, unknown>) => ({ via: 'ship', review_scope: 'frozen', findings: [], ...o });
|
|
|
|
describe('review loop dispatch counters', () => {
|
|
test('fix-check rows count as fix_checks, never passes, and dispatch events add up by role', () => {
|
|
const rows = [
|
|
row({ skill: 'review', timestamp: '2026-10-27T10:01:00Z', findings: [{ fingerprint: 'a:1:x', action: 'auto-fixed' }],
|
|
dispatches: [{ role: 'specialist', prompt_bytes: 4000, diff_bytes: 8000 }, { role: 'specialist', prompt_bytes: 4000, diff_bytes: 8000 }, { role: 'red-team', prompt_bytes: 2000 }] }),
|
|
row({ skill: 'adversarial-review', timestamp: '2026-10-27T10:05:00Z', dispatches: [{ role: 'adversarial-native', prompt_bytes: 1000 }, { role: 'outside-voice', status: 'unknown' }] }),
|
|
row({ skill: FIX_CHECK_SKILL, timestamp: '2026-10-27T10:10:00Z', dispatches: [{ role: 'fix-check', prompt_bytes: 400, diff_bytes: 600 }] }),
|
|
row({ skill: 'review', timestamp: '2026-10-27T09:00:00Z', dispatches: [{ role: 'specialist', prompt_bytes: 99999 }] }),
|
|
];
|
|
const s = summarizeReviewLoop(rows, since);
|
|
expect(s).toMatchObject({ passes: 1, fixed: 1, fix_checks: 1, dispatches: { specialist: 2, 'red-team': 1, 'adversarial-native': 1, 'outside-voice': 1, 'fix-check': 1 }, est_input_tokens: Math.round(28000 / 4) });
|
|
expect(reviewLoopLine(s)).toBe('REVIEW_LOOP: passes=1 blocking_found=0 deferred=0 noted=0 fixed=1 scope=frozen fix_checks=1 dispatches=6 est_input_tokens=7000');
|
|
});
|
|
|
|
test('older rows without dispatch events fall back to the specialist stats and print the old line plus a count', () => {
|
|
const rows = [row({ skill: 'review', timestamp: '2026-10-27T10:01:00Z', specialists: { testing: { dispatched: true }, security: { dispatched: false, reason: 'scope' }, 'red-team': { dispatched: true } } })];
|
|
const s = summarizeReviewLoop(rows, since);
|
|
expect(s.dispatches).toEqual({ specialist: 1, 'red-team': 1 });
|
|
expect(s.est_input_tokens).toBeUndefined();
|
|
expect(reviewLoopLine(s)).toBe('REVIEW_LOOP: passes=1 blocking_found=0 deferred=0 noted=0 fixed=0 scope=frozen dispatches=2');
|
|
});
|
|
|
|
test('rows with neither keep the historical line byte for byte', () => {
|
|
const s = summarizeReviewLoop([row({ skill: 'review', timestamp: '2026-10-27T10:01:00Z' })], since);
|
|
expect(reviewLoopLine(s)).toBe('REVIEW_LOOP: passes=1 blocking_found=0 deferred=0 noted=0 fixed=0 scope=frozen');
|
|
});
|
|
});
|
|
|
|
describe('receipt round trip', () => {
|
|
test('parseReviewLoop keeps the new counters and drops malformed ones; an old receipt parses unchanged', () => {
|
|
expect(parseReviewLoop({ passes: 1, blocking_found: 0, deferred: 0 })).toEqual({ passes: 1, blocking_found: 0, deferred: 0 });
|
|
expect(parseReviewLoop({ passes: 1, blocking_found: 0, deferred: 0, fix_checks: 1, est_input_tokens: 7000, dispatches: { specialist: 2, 'Bad Key': 3, 'red-team': -1, 'fix-check': 1 } }))
|
|
.toEqual({ passes: 1, blocking_found: 0, deferred: 0, fix_checks: 1, est_input_tokens: 7000, dispatches: { specialist: 2, 'fix-check': 1 } });
|
|
});
|
|
test('the SHIP_RECEIPT line carries fix_checks and the dispatch total only when recorded', () => {
|
|
const r = { schema: 1, skill: 'ship', head: 'a'.repeat(40), base: 'main', pr: 7 } as any;
|
|
expect(receiptSummaryLine({ ...r, review_loop: { passes: 1, blocking_found: 0, deferred: 0 } })).toContain('passes=1 blocking_found=0 deferred=0');
|
|
expect(receiptSummaryLine({ ...r, review_loop: { passes: 1, blocking_found: 0, deferred: 0 } })).not.toContain('dispatches=');
|
|
expect(receiptSummaryLine({ ...r, review_loop: { passes: 1, blocking_found: 0, deferred: 0, fix_checks: 1, dispatches: { specialist: 2, 'fix-check': 1 } } })).toContain('deferred=0 fix_checks=1 dispatches=3');
|
|
});
|
|
});
|