Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gstack/ship-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"pr_count": { "default_pr_count": 1, "split_requires_owner": true },
"maintainers": ["garrytan"],
"train": { "gate_workflow": "evals.yml", "free_workflow": "free-tests.yml", "full_gate_check": "gstack/full-gate", "record_artifact": "gstack-train-record", "max_size": 3, "fill_hours": 1, "screen_selection": "gstack-paid", "trial_reservation_usd": 1.5 },
"min_gstack_version": "1.91.146.0"
"min_gstack_version": "1.91.299.0"
}
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## [1.91.307.0] - 2026-10-29

**/ship now reviews in proportion to what the diff can break: light, standard or full depth.** A diff with 50 or fewer runtime lines gets one batched specialist plus the native adversarial pass, a larger runtime diff gets the class's specialists scoped to their hunks, and gate code or any classifier error gets the full review as before. The receipt records the depth and the queue refuses a receipt below what the head needs. Before this, a 12-line fix paid for the same reviewer fan-out as a change to the merge gate. The backtest that would have let light depth drop the adversarial pass was inconclusive (9 light-eligible PRs, 5 with attributable findings, below the 15 the plan required), so light keeps it.

### Added
- **Review depth** (`lib/review-depth.ts`, `lib/review-collect.ts`, `gstack-review-plan`): `REVIEW_DEPTH:` (depth, runtime lines, the rules hash, reasons), `GATE_SCAN:` bound to HEAD, one `DEPTH_ERROR:` per input that fails closed to full (with the input, the cause and the repair), and per-reviewer `DIFF_CMD:` pathspecs. Runtime lines leave out docs, tests and outputs registered with a command in the base branch's `.gstack/generated.json`, read at the base commit. `--depth-floor standard|full` only raises the depth. A fixed or open blocker, or a CRITICAL, raises light to standard for the rest of the invocation.
- **Receipt fields**: `gstack_version` and `review_depth` (`{ depth, class, runtime_lines, rules, base, registry, gate_scan: { tagged, head }, reasons }`), computed at the checked-out head. `write --head` must be HEAD. `review_loop_exception` (`write --review-loop-exception`) records an owner-accepted stop after a fix-check edit with the fix-check's head and findings, and the PR body repeats its line word for word (Fixes #3559).
- **Queue checks** (`gstack-ship-queue ready`, `train build`): the depth the head needs is recomputed from git and a lower receipt is `QUEUE_RECEIPT_DEPTH_RAISED`, with the receipt's depth, the required depth, reasons and inputs on a `REVIEW_DEPTH:` line. A malformed owner exception, a body that dropped its line, or a fix-check head that is not the bound head or its ancestor is `QUEUE_RECEIPT_EXCEPTION_INVALID`. The queue reads the same committed range as the writer and trusts a recorded gate scan only when it names the full bound commit. A receipt with neither new field predates depth receipts and is accepted as legacy.

### Changed
- **Carry and depth**: a carry recomputes the depth at the new head, rebinds the gate scan, and refuses when the new head needs more (a test change that skips a test is gate class). It also refuses a `--head` other than the checked-out HEAD, a receipt with `gstack_version` but no `review_depth`, and an owner exception that no longer stands, since the queue would refuse each of them. Tracked edits in the checkout make the write-time gate scan unavailable, which records `full`.
- **`gstack-diff-scope <base> --head <sha>`**: the scope signals for the committed range only, with no working-tree or untracked files; an unknown sha is `SCOPE_ERROR=no_head` and a missing merge base `SCOPE_ERROR=no_merge_base`.
- **`.gstack/ship-policy.json`**: `min_gstack_version` rises from 1.91.146.0 to 1.91.299.0.

### Tests
- `test/ship-receipt-depth.test.ts`: write records the depth bound to HEAD and refuses another `--head`. The queue refuses light on a standard head, a current receipt with no depth and an unbound gate scan, and accepts a legacy receipt. A plain test change carries and rebinds the scan, while a skipped test refuses the carry. The review-loop exception is refused without a fix-check, with a foreign head or without its body line, and the queue refuses a body that dropped the line.
- `test/review-depth.test.ts`, `test/review-collect.test.ts`, `test/review-plan-cli.test.ts`: the depth matrix, the fixed light set, fail-closed errors and the registry read at the base commit. A `package.json` version line that also carries other keys is not stamp-only.
- `test/diff-scope.test.ts`: `--head` reads the committed range and ignores working-tree edits, and refuses an unknown sha.

## [1.91.306.0] - 2026-10-29

**A /ship receipt now survives a base merge that only regenerates outputs, and a rebase onto the tree it reviewed.** On gstack-evals, two full /ship reruns came from a merge of main that changed a generated file's diff (U1 paid about 28M tokens for its rerun), and one came from a rebase whose tree was byte-identical to the reviewed one (about 60M). Each re-reviewed code the receipt already covered. For crews the rule is now a single line. Merge main into a reviewed branch, and never rebase it.
Expand Down
5 changes: 5 additions & 0 deletions TODOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

## NEXT PRIORITY

### P1: review-depth receipt follow-ups (filed 2026-10-28, from the #3567 /ship plan audit)

- **Derive `carry.covers` from depth.** A carried receipt's `covers` is the fixed `CARRY_COVERS` list (lib/ship-receipt.ts); derive it from the recorded `review_depth` so a light review's carry does not claim the checks a full review ran (plan item 5, deferred at /ship). **Effort:** S. **Priority:** P1.
- **Bound the queue's recompute time.** `gstack-ship-queue` recomputes each receipt's needed depth at READY and at train build; add a test that a five-PR train recomputes within a fixed budget, so a large diff cannot stall the train. **Effort:** S. **Priority:** P1.

### P3: `/codex --effort` follow-ups (filed 2026-10-11, from the #3146 autoplan review)

- **Show the effort that ran.** Step 0.5 prints `CODEX_EFFORT: <level> (source: /codex --effort)` once, but the result footers do not repeat the effort, so it scrolls away. Add an `Effort:` line to the review, challenge and consult result footers. Blocked on skeleton bytes in `codex/SKILL.md` (cap 60,200). **Effort:** S. **Priority:** P3.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.91.306.0
1.91.307.0
2 changes: 1 addition & 1 deletion agents-digest/gstack-AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# gstack digest v1.91.306.0 — regenerate/re-copy after upgrading gstack
# gstack digest v1.91.307.0 — regenerate/re-copy after upgrading gstack

Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
for agent hosts without a full skill install. The full skills add workflows,
Expand Down
21 changes: 21 additions & 0 deletions bin/gstack-diff-scope
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
# gstack-diff-scope — categorize what changed in the diff against a base branch
# Usage: source <(gstack-diff-scope main) → sets SCOPE_FRONTEND=true SCOPE_BACKEND=false ...
# Or: gstack-diff-scope main → prints SCOPE_*=... lines
# Or: gstack-diff-scope <base> --head <sha> → the committed range <base>...<sha> only
# (no working tree, no untracked files): what a receipt writer and the
# merge queue compute for one commit, from any checkout (GSTA-147).
#
# Output contract (#2526 — all-false must be distinguishable from "we could
# not look" and from "nothing matched"):
Expand Down Expand Up @@ -31,6 +34,8 @@ _default_base() {
echo "main"
}
BASE="${1:-$(_default_base)}"
HEAD_REF=""
if [ "${2:-}" = "--head" ]; then HEAD_REF="${3:-}"; fi

FRONTEND=false
BACKEND=false
Expand Down Expand Up @@ -79,6 +84,21 @@ _collect() {
[ -n "$f" ] && FILES_LIST+=("$f")
done
}
if [ -n "$HEAD_REF" ] || [ "${2:-}" = "--head" ]; then
if ! git rev-parse --verify -q "${HEAD_REF}^{commit}" >/dev/null 2>&1; then
_print_flags
echo "SCOPE_ERROR=no_head"
echo "# --head '${HEAD_REF}' is not a commit in this repository"
exit 2
fi
if ! git merge-base "${BASE}" "${HEAD_REF}" >/dev/null 2>&1; then
_print_flags
echo "SCOPE_ERROR=no_merge_base"
echo "# '${BASE}' and '${HEAD_REF}' share no merge base"
exit 2
fi
_collect < <(git diff -z "${BASE}...${HEAD_REF}" --name-only 2>/dev/null)
else
# Committed diff vs base (merge-base form; two-dot fallback when no merge base).
_collect < <(git diff -z "${BASE}...HEAD" --name-only 2>/dev/null || git diff -z "${BASE}" --name-only 2>/dev/null || true)
# Working-tree changes (staged + unstaged). `git diff HEAD` fails on a repo
Expand All @@ -87,6 +107,7 @@ _collect < <(git diff -z HEAD --name-only 2>/dev/null || true)
# Untracked files: a brand-new component/migration/test is exactly what a
# reviewer should see, and /ship commits it in Step 15 regardless.
_collect < <(git ls-files -z --others --exclude-standard 2>/dev/null || true)
fi

if [ "${#FILES_LIST[@]}" -eq 0 ]; then
_print_flags
Expand Down
3 changes: 3 additions & 0 deletions bin/gstack-doctor-components.sh
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ gstack_result_anchor() {
QUEUE_TRAIN_FROZEN) printf '%s' 'queue-train-frozen' ;;
QUEUE_RECEIPT_MISSING) printf '%s' 'queue-receipt-missing' ;;
QUEUE_RECEIPT_STALE) printf '%s' 'queue-receipt-stale' ;;
QUEUE_RECEIPT_DEPTH_RAISED) printf '%s' 'queue-receipt-depth-raised' ;;
QUEUE_RECEIPT_EXCEPTION_INVALID) printf '%s' 'queue-receipt-exception-invalid' ;;
QUEUE_SCREEN_STALE_REF) printf '%s' 'queue-screen-stale-ref' ;;
QUEUE_SCREEN_NOT_RUN) printf '%s' 'queue-screen-not-run' ;;
QUEUE_TRAIN_STACK_RED) printf '%s' 'queue-train-stack-red' ;;
Expand All @@ -306,6 +308,7 @@ gstack_result_anchor() {
REVIEW_SCOPE_RANGE) printf '%s' 'review-scope-range' ;;
RECEIPT_CARRY_REFUSED) printf '%s' 'receipt-carry-refused' ;;
PROSE_LINT_FOUND) printf '%s' 'prose-lint-found' ;;
REVIEW_LOOP_EXCEPTION_INVALID) printf '%s' 'review-loop-exception-invalid' ;;
RECEIPT_CARRY_TREE_MISMATCH) printf '%s' 'receipt-carry-tree-mismatch' ;;
RECEIPT_CARRY_PRODUCT_CHANGED) printf '%s' 'receipt-carry-product-changed' ;;
DOCS_AUDIT_BLOCKED) printf '%s' 'docs-audit-blocked' ;;
Expand Down
Loading
Loading