mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
ship: judge treats a missing severity as invalid; Capy passes rendered briefs via cat (replay C1-34 breaks)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
aa6cd2694d
commit
6f193a6710
4 files changed
+7
-2
No files matched your search
@@ -46,7 +46,7 @@ Capy checklist for `/autoplan`: drive `gstack-autoplan`, never a hand adaptation
|
||||
|
||||
Token caps on /ship: pass a brief's cap as `--token-cap <n>`. /ship reads `cost.tokens` and `cost.credits` from `tools.thread.get({ thread_id: "<your own jam id>" })` (its own jam, subagents included; `"this"` names the root thread, so only a root run may pass it) inside the same execute program as the `gstack-ship-budget` command that takes them as `--reading <n> --credits <c>`, never in a call of its own. `ship/DRIVER.md` starts `--mode observe`: each program ends with `note`, and only a note past the cap stops (`docs/ship-budget.md`).
|
||||
|
||||
/ship on Capy is about a dozen calls, each one execute program (`ship/DRIVER.md`). A review pass, Step 11 and the Step 14.5 audit each run inside one program, in order: (1) `gstack-review-plan --base <base>` with the pass's flags (its `DISPATCH:` lines); (2) `gstack-review-log --start <skill>` once per row the step logs (admission mode: `--reservation` from `RESERVATION_ID:`); (3) `BEFORE` (tokens and credits); (4) `tools.subagent.create`, one reader each, `machine: "shared"`, its brief copied verbatim from the section that dispatches it; audit readers (Steps 7, 8, 9.2.1, 14.5) cannot block, so they add `model: { model_id: "anthropic/claude-sonnet-5-5", reasoning_mode: "low" }` (`anthropic/claude-haiku-5-5` also fits); (5) a poll: one `tools.subagent.list` per ~20 s tick (a `tools.subagent.get` with `transcript: "none"` only on a status change), returning 200 s after the program's first line, where a reader is settled only at status `done` or `failed` or with a `summary` (a just-created `idle` reader has not run); start readers listed under `rejected` with `tools.subagent.start` as slots free; report, never drop, one stuck 20 minutes; (6) `gstack-ship-budget note --record <rec> --step <n> --reading <AFTER> --credits <c> --dispatched-at <BEFORE> --dispatched-credits <c> --readers <n>`; (7) return only each reader's final output, or what still runs. The parent never walks the dispatch steps call by call. Waits: never let 5 minutes pass between calls (a later call costs about 40x). While readers run, never end the turn on `wait` (each finished subagent wakes a turn); rerun the poll program. Otherwise re-issue `wait` with `duration_seconds: 200` and the operation ids. At Step 22 run `gstack-ship-budget calls --events <file>` on your jam's `tools.thread.get` transcript since /ship started; report its `CALLS:` line. A small waiting child saves nothing.
|
||||
/ship on Capy is about a dozen calls, each one execute program (`ship/DRIVER.md`). A review pass, Step 11 and the Step 14.5 audit each run inside one program, in order: (1) `gstack-review-plan --base <base>` with the pass's flags (its `DISPATCH:` lines); (2) `gstack-review-log --start <skill>` once per row the step logs (admission mode: `--reservation` from `RESERVATION_ID:`); (3) `BEFORE`; (4) `tools.subagent.create`, one reader each, `machine: "shared"`, its brief copied verbatim from the section that dispatches it (a rendered file as bash `cat` prints it: a file read cuts long lines); audit readers (Steps 7, 8, 9.2.1, 14.5) cannot block, so they add `model: { model_id: "anthropic/claude-sonnet-5-5", reasoning_mode: "low" }` (`anthropic/claude-haiku-5-5` also fits); (5) a poll: one `tools.subagent.list` per ~20 s tick (a `tools.subagent.get` with `transcript: "none"` only on a status change), returning 200 s after the program's first line, where a reader is settled only at status `done` or `failed` or with a `summary` (a just-created `idle` reader has not run); start readers listed under `rejected` with `tools.subagent.start` as slots free; report, never drop, one stuck 20 minutes; (6) `gstack-ship-budget note --record <rec> --step <n> --reading <AFTER> --credits <c> --dispatched-at <BEFORE> --dispatched-credits <c> --readers <n>`; (7) return only each reader's final output, or what still runs. The parent never walks the dispatch steps call by call. Waits: never let 5 minutes pass between calls. While readers run, never end the turn on `wait`; rerun the poll program. Otherwise re-issue `wait` with `duration_seconds: 200` and the operation ids. At Step 22 run `gstack-ship-budget calls --events <file>` on your jam's `tools.thread.get` transcript since /ship started; report its `CALLS:` line. A small waiting child saves nothing.
|
||||
|
||||
## Plan, approve, ship
|
||||
|
||||
|
||||
@@ -12,3 +12,5 @@
|
||||
### Fixed
|
||||
- **`gstack-ship-budget note --step 9.1`** records under step 9 with the sub-step as its label. Before, it was refused, and replays lost two notes to that error.
|
||||
- **`gstack-review-scope render`** accepts `{"deferred": [...]}` as well as the bare array.
|
||||
- **`gstack-review-dispatch judge`** treats a judged line with no severity, or `"severity": null`, as invalid and prints `JUDGMENT_REJECTED`. Before, it crashed with a TypeError, and a replay spent three calls recovering.
|
||||
- **The Capy skill passes a rendered brief as bash `cat` prints it.** A file read cut the long lines of a 231,589-byte outside-voice brief, so `sent` reported `BRIEF_MISMATCH` and the reader had to be sent again.
|
||||
@@ -41,7 +41,8 @@ export function recordSent(ledger: string, reader: string, sentText: string): {
|
||||
|
||||
/** Severity ranks across the voices' vocabularies; a label with no severity (FIXABLE, INVESTIGATE) ranks 0. */
|
||||
const RANKS: Record<string, number> = { critical: 4, p0: 4, high: 3, p1: 3, medium: 2, p2: 2, informational: 2, low: 1, p3: 1, fixable: 0, investigate: 0 };
|
||||
export function rank(label: string): number | null {
|
||||
export function rank(label: unknown): number | null {
|
||||
if (typeof label !== 'string') return null;
|
||||
const r = RANKS[label.trim().replace(/^\[|\]$/g, '').toLowerCase()];
|
||||
return r === undefined ? null : r;
|
||||
}
|
||||
|
||||
@@ -211,6 +211,8 @@ describe('gstack-review-dispatch sent, collect, judge and verify: nothing change
|
||||
expect(parseFindings('r', 'FIXABLE: race in a.ts:3\nINVESTIGATE: odd lock\nRecommendation: Fix the race because x\n').map(f => f.label)).toEqual(['FIXABLE', 'INVESTIGATE']);
|
||||
const m: MergedLine[] = [{ id: 'r#1', reader: 'r', label: 'FIXABLE', raw: 'x', output_sha256: 'h' }];
|
||||
expect(judge(m, [{ id: 'r#1', severity: 'severe' }]).invalid).toEqual(['r#1']);
|
||||
// Replay C1-34 (GSTA-193): a judged line with "severity": null crashed judge with a TypeError; it is an invalid line.
|
||||
expect(judge(m, [{ id: 'r#1', severity: null as unknown as string }, { id: 'lead#1' } as unknown as { id: string; severity: string }]).invalid).toEqual(['r#1', 'lead#1']);
|
||||
expect(judge(m, [{ id: 'r#1', severity: 'INFORMATIONAL', reason: 'a real defect' }]).ok).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user