mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 22:45:02 +01:00
fix(ship-budget): Step 9's Red Team batch takes no start of its own; --finish settles it
gstack-evals #34 round 3: the pass program's start-once-per-role rule gave the Red Team batch an extra --start review token that no row ever finished. A later batch of the same Step 9 pass (the Red Team or an ADMIT remainder) now takes no start, and --finish REVIEW_START settles the open later batches of the step reserved after the started one, so none stays reserved at close. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
682def4cca
commit
2d3d90cd0c
7 files changed
+40
-7
No files matched your search
+6
-3
@@ -514,8 +514,10 @@ export function admitReviewStart(marker: string, reservation: string | undefined
|
||||
|
||||
/**
|
||||
* gstack-review-log --finish TOKEN: the pass is over, so settle the reservation
|
||||
* that start consumed and every other dispatch reservation started in the same
|
||||
* step (a pass logs one row even when its reviewers were admitted one by one).
|
||||
* that start consumed, every other dispatch reservation started in the same
|
||||
* step (a pass logs one row even when its reviewers were admitted one by one),
|
||||
* and every later batch of the pass reserved after it that took no start (Step
|
||||
* 9's Red Team or an ADMIT remainder).
|
||||
*/
|
||||
export function settleReviewFinish(marker: string, token: string, env: NodeJS.ProcessEnv = process.env): string | undefined {
|
||||
const active = activeLedger(marker);
|
||||
@@ -524,7 +526,8 @@ export function settleReviewFinish(marker: string, token: string, env: NodeJS.Pr
|
||||
return mutateLedger(active.file, env, l => {
|
||||
const r = l.reservations.find(started);
|
||||
if (!r) return undefined;
|
||||
const pass = l.reservations.filter(x => x.kind === 'dispatch' && x.state === 'consumed' && x.step === r.step);
|
||||
const at = l.reservations.indexOf(r);
|
||||
const pass = l.reservations.filter((x, i) => x.kind === 'dispatch' && x.step === r.step && (x.state === 'consumed' || (x.state === 'open' && i > at)));
|
||||
for (const x of pass) settleIn(l, x);
|
||||
return pass.map(x => x.id).join(',');
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ after they settle; `close` flags roles never re-checked. `--finish` settles the
|
||||
|
||||
**One program per review pass.** One execute program (one shell script off Capy) runs, in order:
|
||||
`gstack-review-packet build` (a later build in the pass is `build --like <first dir>`), `check --next "$BUDGET_NEXT"`,
|
||||
`gstack-review-log --start <skill> --reservation <id>` once per row the step logs, the reading `BEFORE`, the readers
|
||||
`gstack-review-log --start <skill> --reservation <id>` once per row the step logs (a later batch of the same pass (the Red Team or an ADMIT remainder) takes no start; `--finish` settles it), the reading `BEFORE`, the readers
|
||||
(`tools.subagent.create` on Capy), the poll until the last settles (`tools.subagent.get`), the reading `AFTER`,
|
||||
`gstack-ship-budget settle`, then `gstack-review-packet collect`, returning only collect's output.
|
||||
Settle each batch in the program that dispatched it: read the counter right before the readers
|
||||
|
||||
@@ -31,7 +31,7 @@ after they settle; `close` flags roles never re-checked. `--finish` settles the
|
||||
|
||||
**One program per review pass.** One execute program (one shell script off Capy) runs, in order:
|
||||
`gstack-review-packet build` (a later build in the pass is `build --like <first dir>`), `check --next "$BUDGET_NEXT"`,
|
||||
`gstack-review-log --start <skill> --reservation <id>` once per row the step logs, the reading `BEFORE`, the readers
|
||||
`gstack-review-log --start <skill> --reservation <id>` once per row the step logs (a later batch of the same pass (the Red Team or an ADMIT remainder) takes no start; `--finish` settles it), the reading `BEFORE`, the readers
|
||||
(`tools.subagent.create` on Capy), the poll until the last settles (`tools.subagent.get`), the reading `AFTER`,
|
||||
`gstack-ship-budget settle`, then `gstack-review-packet collect`, returning only collect's output.
|
||||
Settle each batch in the program that dispatched it: read the counter right before the readers
|
||||
|
||||
@@ -105,7 +105,7 @@ This pass is static; defer product probes to Step 9.2.1.
|
||||
|
||||
1. Read `~/.claude/skills/gstack/review/checklist.md`. If the file cannot be read, **STOP** and report the error.
|
||||
|
||||
2. Before reading the diff, run `~/.claude/skills/gstack/bin/gstack-review-log --start review` and save its token as REVIEW_START (under a token cap, run Step 9.1's `gstack-review-packet build` and `gstack-ship-budget check` first, then `gstack-review-log --start review --reservation <id>`; 9.1's program then dispatches that batch with no second check or start). Then run `git diff origin/<base>` (`git diff $RANGE_BASE` from pass 2 on). Read non-ignored untracked source files too (`git ls-files --others --exclude-standard`); the snapshot includes them.
|
||||
2. Before reading the diff, run `~/.claude/skills/gstack/bin/gstack-review-log --start review` and save its token as REVIEW_START (under a token cap, run Step 9.1's `gstack-review-packet build` and `gstack-ship-budget check` first, then `gstack-review-log --start review --reservation <id>`; 9.1's program then dispatches that batch with no second check or start; a later batch of the same Step 9 pass (the Red Team or an ADMIT remainder) takes no start, and `--finish REVIEW_START` settles it). Then run `git diff origin/<base>` (`git diff $RANGE_BASE` from pass 2 on). Read non-ignored untracked source files too (`git ls-files --others --exclude-standard`); the snapshot includes them.
|
||||
|
||||
Every pass captures a full token (no `--fix-range`), so the record binds the
|
||||
whole tree even when reviewers read only the range.
|
||||
|
||||
@@ -50,7 +50,7 @@ This pass is static; defer product probes to Step 9.2.1.
|
||||
|
||||
1. Read `~/.claude/skills/gstack/review/checklist.md`. If the file cannot be read, **STOP** and report the error.
|
||||
|
||||
2. Before reading the diff, run `~/.claude/skills/gstack/bin/gstack-review-log --start review` and save its token as REVIEW_START (under a token cap, run Step 9.1's `gstack-review-packet build` and `gstack-ship-budget check` first, then `gstack-review-log --start review --reservation <id>`; 9.1's program then dispatches that batch with no second check or start). Then run `git diff origin/<base>` (`git diff $RANGE_BASE` from pass 2 on). Read non-ignored untracked source files too (`git ls-files --others --exclude-standard`); the snapshot includes them.
|
||||
2. Before reading the diff, run `~/.claude/skills/gstack/bin/gstack-review-log --start review` and save its token as REVIEW_START (under a token cap, run Step 9.1's `gstack-review-packet build` and `gstack-ship-budget check` first, then `gstack-review-log --start review --reservation <id>`; 9.1's program then dispatches that batch with no second check or start; a later batch of the same Step 9 pass (the Red Team or an ADMIT remainder) takes no start, and `--finish REVIEW_START` settles it). Then run `git diff origin/<base>` (`git diff $RANGE_BASE` from pass 2 on). Read non-ignored untracked source files too (`git ls-files --others --exclude-standard`); the snapshot includes them.
|
||||
|
||||
Every pass captures a full token (no `--fix-range`), so the record binds the
|
||||
whole tree even when reviewers read only the range.
|
||||
|
||||
@@ -117,6 +117,28 @@ describe('gstack-review-log --start under a token cap', () => {
|
||||
expect(close.code, close.out).toBe(0);
|
||||
});
|
||||
|
||||
// Value: protects=a later batch of the same Step 9 pass (the Red Team, built after collect) takes no start of its own and still settles when the pass finishes, so no start token is left unfinished and no reservation stays open (gstack-evals #34 round 3: an extra red-team --start review token was never finished); fails_when=--finish settles only reservations a start consumed, leaving the Red Team's open and counted as reserved at close;
|
||||
// why_new=the --finish tests above start every batch; seam=none; no_claim=that the program's own settle ran
|
||||
test('--finish REVIEW_START settles the pass\'s later Red Team batch that took no start; close finds nothing reserved or unreserved', () => {
|
||||
const h = fresh();
|
||||
const run = capped(h);
|
||||
const first = reserve(h, run.run, 'dispatch:specialist');
|
||||
const start = h.reviewLog(['--start', 'review', '--reservation', first]);
|
||||
expect(start.code, start.err).toBe(0);
|
||||
const redTeam = reserve(h, run.run, 'dispatch:red-team');
|
||||
const earlier = h.ledger(run.ledger).reservations.find((r: any) => r.id === redTeam);
|
||||
expect(earlier.state).toBe('open');
|
||||
const row = { skill: 'review', timestamp: new Date().toISOString(), status: 'clean', issues_found: 0, critical: 0, informational: 0 };
|
||||
const fin = h.reviewLog([JSON.stringify(row), '--finish', start.out.trim()]);
|
||||
expect(fin.code, fin.err).toBe(0);
|
||||
expect(fin.err).toContain(`settled budget reservations ${first},${redTeam}`);
|
||||
expect(h.ledger(run.ledger).reservations.filter((r: any) => r.kind === 'dispatch').map((r: any) => r.state)).toEqual(['settled', 'settled']);
|
||||
const close = h.sb(['close', '--run', run.run, '--reading', '10']);
|
||||
expect(close.out).toContain('reserved=0');
|
||||
expect(close.out).not.toContain('BUDGET_CHECK_SKIPPED');
|
||||
expect(close.code, close.out).toBe(0);
|
||||
});
|
||||
|
||||
test('close prints one BUDGET_CHECK_SKIPPED line per unreserved dispatch and ends incomplete', () => {
|
||||
const h = fresh();
|
||||
const run = capped(h);
|
||||
|
||||
@@ -48,6 +48,14 @@ describe('ship budget skill structure', () => {
|
||||
expect(handoff).not.toContain('once per role on the printed reservation');
|
||||
});
|
||||
|
||||
// Value: protects=the pass program says a later batch of the same Step 9 pass takes no start, so the Red Team leaves no unfinished token; fails_when=the section tells every batch to start once per role;
|
||||
// why_new=the start test above pins the row rule only; seam=none; no_claim=the agent follows it
|
||||
test('a later batch of the same Step 9 pass (Red Team, an ADMIT remainder) takes no start of its own', () => {
|
||||
for (const rel of ['ship/sections/budget-handoff.md.tmpl', 'ship/sections/review-army.md.tmpl']) {
|
||||
expect(read(rel), rel).toMatch(/later batch of the same (?:Step 9 )?pass \(the Red Team or an ADMIT remainder\) takes no start/);
|
||||
}
|
||||
});
|
||||
|
||||
test('every review-log --start in the dispatch sections takes a reservation when a cap is set', () => {
|
||||
for (const rel of ['ship/sections/review-army.md.tmpl', 'ship/sections/adversarial.md.tmpl']) expect(read(rel)).toContain('--reservation');
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user