Merge commit '83ae96e0' into capy/gst-776-review-dispatch

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	capy/skill/gstack/SKILL.md
#	lib/capy-review-pass.js
This commit is contained in:
garrytan committed 2026-11-01 14:29:57 +00:00
commit d70f2e7b85
79 files changed
+7347 -5511

No files matched your search

+2 -2
View File
@@ -1031,7 +1031,7 @@ repository content: its setup script and pull destinations must resolve inside
`--src`, and a name it lists under `pass` is forwarded only when the command
line grants it (`--pass NAME`, or all of them with `--trust-config`); `--pass`
never forwards `UBICLOUD_*`. All of this is pinned offline in
`test/ubicloud-runner.test.ts` against a fake API and a fake `ssh`, including
`test/ubicloud-runner*.test.ts` against a fake API and a fake `ssh`, including
a fixture whose fake API creates, so `run` and `verify` execute end to end.
`run` and `verify` start their setup, command and tier phases detached on the
@@ -1047,7 +1047,7 @@ start/pass/fail per tier under `UBI_CHECKPOINT_DIR/<run key>/`, so a resume on
a fresh VM reruns setup and only the tiers without a pass; `pregate-remote.sh`
adds its tree and planned-set digest to the key, so a rerun of
`gstack-pregate --stage tests --remote` reattaches and bills from the original
`created_at`. The fake VM in `test/ubicloud-runner.test.ts` runs these phases
`created_at`. The fake VM in `test/helpers/ubi-runner-fake-vm.ts` runs these phases
for real in a directory.
Under 8 cores (`gstack-doctor`'s `cores` row says so), run the full unit and
+15 -2
View File
@@ -469,7 +469,7 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
3. `collect` hashes each reader's raw output and appends its findings to
`merged.jsonl` with the label as written (a JSON line's `severity`, or the most
severe of a prose line's Critical, High, Medium, Low, P0-P3, FIXABLE or INVESTIGATE
wherever they stand, headings and table rows included; a top-level list item under a
wherever they stand outside a negation (below), headings and table rows included; a top-level list item under a
label-only heading takes the stronger of the two). A `Severity:` or `Blocks:` line
continues the finding above it until a list item, heading, bold paragraph opener or a
second line for the same field starts another: `Severity` raises its label,
@@ -478,6 +478,15 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
adversarial voice's FIXABLE collects as CRITICAL (normalizer v2, GSTA-167). An unnumbered, unlabeled bullet with no file:line
under a paragraph finding whose Severity or Blocks line has not come yet is that finding's detail, so the field line after it
lands on the finding; a numbered item is its own finding.
A label a negation word governs ("found nothing CRITICAL", "no new **High** or Critical issues", "No P0/P1 issues":
no, nothing, not, none, zero, without or never, optionally followed by new, remaining, other, further, additional, more
or at, through Markdown, and carried to labels joined by a comma, slash, or, nor or and) is no finding unless it opens a
field (`High:`, GST-766). A line whose labels are all negated closes the open finding and heads the next, so a finding
worded around a negated label still arrives through its Severity or Blocks field. Each prose finding's line also
records the Severity and Blocks its own text states as `stated`, beside the collected label and never in its place;
a classified finding that carries `stated` shows it in its deferred-review issue next to the label ("CRITICAL (the
reviewer wrote Severity: Low, Blocks: no)"). /ship's Step 9.4 does not copy `stated` into its findings file yet: that
instruction lives in a reviewer brief that changes only after a no-loss replay. What blocks is unchanged.
A phantom finding costs a fold; a lost one costs the gate.
A reply with no finding counts as a review only with the clean marker its own brief asks for: `NO FINDINGS` alone on a
line for the specialists, the batched specialist, the targeted reader, the Red Team and fix-check, the `Recommendation:`
@@ -515,7 +524,10 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
create whose result was lost by its title and never starts a reader twice; a
missing or doubled title stops the pass. On Capy the pass runs as one call to the
checked-in program `lib/capy-review-pass.js`, served by `program` and loaded
unchanged by the gstack Capy skill.
unchanged by the gstack Capy skill. A full repeat pass takes `--diff-base` (RANGE_BASE; a scoped pass, none), a
fix-check plan renders its reader from `--fix-list`, `--hunks`, `--tests` and `--callers`,
`--audit-only` runs just the `--extras` readers (Step 14.5's docs audit), and the fork
point is found against `origin/<base>` first, as preflight does.
7. Every brief, audit readers' included, ends with a workspace paragraph naming the
checkout the bins run in (GST-482): a reader on a shared machine starts in the
machine's working directory, so it is told to `cd` there on every command, to give
@@ -538,6 +550,7 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
## Where this is enforced
- `lib/review-brief.ts`, `lib/review-ledger.ts`, `lib/review-pass.ts`, `lib/capy-review-pass.js`, `bin/gstack-review-dispatch`: [verbatim dispatch](#verbatim-dispatch) (`test/review-dispatch.test.ts`, `test/review-pass.test.ts`).
- `lib/ship-parts.ts`, `bin/gstack-ship-parts`: the section text each /ship phase uses, returned once per run inside the phase program before it; `next --resend` sends an unreceived batch again (`test/ship-parts.test.ts`).
- `scripts/resolvers/review-army.ts`: the floor and `NO FINDINGS` in every specialist and Red Team prompt (rendered into `review/sections/review-army.md` and `ship/sections/review-army.md`), and the lead judgment's "On record" line that turns a [ruled class](#rulings-below-the-floor) into a note; in `/ship` every specialist and Red Team prompt it renders carries its diff base as a literal sha the parent writes in (`DIFF_BASE=<diff-base>`: the merge base on pass 1, `RANGE_BASE` from pass 2 on) beside its plan's `DIFF_CMD` excludes, because a reviewer is its own process and never sees the parent's shell variables; the fan-out batches above.
- `ship/sections/review-army.md` Step 9: the plan's `REVIEW_DEPTH`, `GATE_SCAN` and `--depth-floor` on every run, the depth fields of the review-log row, passes, `RANGE_BASE`, classify, defer, render, the cap check before any edit, the Step 11 late-defer row, scratch files under `.gstack/tmp/`, the cap decisions; `ship/sections/adversarial.md` Step 11: the outside voice reads the same range and scopes queued fixes the same way.
+1 -1
View File
@@ -41,7 +41,7 @@ unless `--label` is given.
**Counting calls.** `gstack-ship-budget calls --events <file> [--since <iso>] [--until <iso>]` reads
the host's transcript items (a JSON array, or `{ "items": [...] }` as Capy's `tools.thread.get`
transcript returns) and prints
transcript returns; a leading `~` or `~/` in `<file>` means the home directory) and prints
`CALLS: total= decision= keepalive= idle_gaps_over_300s= max_gap_s= first= last= source=`. Every tool
item is one parent call; a `wait`, or a program that only reads status (plus at most one bash for its
note), is a keep-alive, and a gap over 300 s between calls outlives the prompt cache
+2 -1
View File
@@ -1341,7 +1341,8 @@ other `CARRY_TEST_GLOBS` in `lib/ship-receipt.ts`; an `openapi.spec.yaml` is
product) or release metadata (`CHANGELOG.md`, `agents-digest/**`, each
`<changelog_fragments.dir>/<slug>.md` fragment when the base policy sets
`changelog_fragments` (not the directory's README.md or a nested path; a README
change also makes the range's fragments product), and `VERSION`
change the branch itself makes also makes the range's fragments product, while one
a base merge brings in does not), and `VERSION`
or `package.json` only while the change is a version stamp and nothing else: a
dependency, a script or a new key makes them product). A path the policy's
`stamp_files`, `stamp_paths` or `release_outputs` name is metadata only while