Merge origin/main (v1.91.278.0) into eval-select-review-floor-followups; stamp v1.91.279.0

FILES: CHANGELOG.md VERSION agents-digest/gstack-AGENTS.md package.json
MERGE_CONDITION: sha=64684de144e5c5b8934408514b44434559487b18
NEW_VERSION=1.91.279.0

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-27 17:02:49 +00:00
commit c7f6daa7a4
30 files changed
+654 -85

No files matched your search

+6 -1
View File
@@ -1238,7 +1238,12 @@ GitHub-hosted restore never sees (until GSTA-48 every restore missed).
**Readiness (R1).** Paid slices for a PR head start only when the PR has been
quiet for the window (repository variable `EVALS_QUIET_MINUTES`, default 10,
measured from the run's creation) and Free Tests has concluded success on the
same head SHA. The `readiness` job (`scripts/eval-readiness.ts`) sleeps out the
same head SHA, as GitHub's `pull_request` run or, for a head with none, as a
`workflow_dispatch` run on the branch (the dispatch route of docs/ship-policy.md
for a head whose only conflict with the base is the CHANGELOG, which gets no
`pull_request` CI; the `pull_request` run built the merge ref, so it decides
whenever it exists, a dispatched run decides only without one, and a red
dispatched run blocks like any other, #3508). The `readiness` job (`scripts/eval-readiness.ts`) sleeps out the
window with no API calls, since a newer push cancels the run anyway, then polls
Free Tests every minute for up to 80 minutes. Free Tests red on the head fails
the run without paid work; re-run Free Tests, then re-run this workflow (a
+3 -1
View File
@@ -456,7 +456,9 @@ receipt for H when the two trees differ only as a stamp does:
that does not account for every path `git diff-tree` lists between the two
trees. The version rule is the tree
receipt's: a `stamp_files`, `stamp_paths` or `release_outputs` hunk is a stamp
only as the rewrite from the gated tree's version to the head's (so two trees
only as the rewrite to the head's version from the gated tree's version, or
from a newer tentative version of the repo's width that the stamp moved (a
`*_KEY_VERSION` written as the next minor, #3512) (so two trees
at the same version refuse such a hunk as `other`), while the built-in
version-file, manifest, lockfile and CHANGELOG rules compare content and do
not check the direction of the version move. The obligation stays uncleared; rerun
+32 -5
View File
@@ -425,7 +425,18 @@ conflicts with the base, CHANGELOG-only conflicts included) is replaced by a
scratch merge of the head onto the base, pushed as `screen/<pr>-<head12>`
(the `SCREEN_MERGE:` line says which); a screen dispatched on a stale ref would
plan no cases and report success. Only a real conflict with the base (or a
remote whose refs cannot be listed) is `QUEUE_SCREEN_STALE_REF`. A finished screen that planned no cases (no
remote whose refs cannot be listed) is `QUEUE_SCREEN_STALE_REF`. Under either
choice `ready` first reads what `gstack-pr-wait`'s base-overlap event reports,
from git: when the base moved since the head forked and its new commits touch
files the PR changes, `BASE_OVERLAP: <base> moved to <sha7> and touches files
in this PR: <files>; the train's merge onto <base> is clean` (or `conflicts
beyond the CHANGELOG and the stamp files (<files>)`), the train's own merge
rules run as a dry run in object storage; a git read that fails prints
`conflicts unknown` and refuses nothing. A conflict under `--no-screen` is
`QUEUE_TRAIN_CONFLICT` at READY, since the train would stop at this PR: the
lane reports it to the controller and, on its word (crew template rule 14),
merges the base into the pin, resolves, pushes and runs /ship before READY,
instead of learning it when the train is built. A finished screen that planned no cases (no
`paid-plan` artifact, which a superseded run never uploads, or a plan manifest
with nothing planned) is never followed as a screen: `ready --screen`
dispatches a fresh one and `train build` prints `SCREEN: #<n> run <id> planned
@@ -453,7 +464,14 @@ and waits (`--free-timeout <s>`, default 2700) for a green
through eval-bg's nonce. A train fills at `max_size` READY PRs or when its
oldest READY has waited `fill_hours`, whichever comes first; until then
`QUEUE_TRAIN_WAITING` names the time, and `--urgent` dispatches a one-PR
train at once. gstack's own block fills at 3 PRs or one hour (it was 5 PRs or
train at once. The scratch merges run before the fill rule: every `train
build` prints `#<pr> BASE_OVERLAP: <base> moved to <sha7> and touches files in
this PR: <files>` for a seated pin whose files the base moved onto since it
forked (the chain's merge onto the pin's own prefix is the verdict), refuses
`QUEUE_TRAIN_CONFLICT` for a pin that does not merge, and otherwise prints
`MERGE_CHECK: T1..T<k> merge clean onto <base>@<sha>`, so a
`QUEUE_TRAIN_WAITING` result is also a clean dry-run merge of the pool (the
crew template's sibling trial merge, done by the tool). gstack's own block fills at 3 PRs or one hour (it was 5 PRs or
three hours until 2026-10-26, which left READY PRs waiting most of a day). The same pins on the same base return the live train
instead of a second one; `train build --train <nonce>` resumes a train from
its record. Refusals, all before any spend: `QUEUE_TRAIN_UNTRUSTED` (a fork
@@ -708,7 +726,9 @@ integers under `review_loop`, `gate` and `ci`: `passes=2`, `passes: 2` and
several counters, `passed` under `gate` and `ci` or `pass` under every gate
lane, matches when the number equals any of them; an underscore in a key
matches `_`, a space or `-`; a name inside a longer word, `3 pass-through`, is
not a claim). A body that quotes a different number is
not a claim; a number that is the value of a `key=` or `key:` pair is that
key's claim alone, so `passes=2 blocking_found=1` is two claims, never a
`2 blocking_found` one, #3513). A body that quotes a different number is
`RECEIPT_PROSE_MISMATCH` (exit 1) and the `PROSE:` line names each field with
both values; prose that quotes no counter is not a claim. Data PRs (evals,
measurements, sweeps) run it before READY so the body's numbers are the run's.
@@ -798,7 +818,11 @@ 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
its change is a stamp, the rule the tree receipt applies to the same paths: a
manifest by its version key, any other file when its new text reads as the old
once the new release version is replaced by the old one. A logic change in a
once the new release version is replaced by the old one, or when every changed
line is a stamp-form rewrite to the new version of the old version or of a
newer tentative one the stamp moved (a `*_KEY_VERSION` written as the next
minor, #3512; a version older than the old one or of another width is a hand
edit). A logic change in a
stamped source file is product. Lockfiles are product unless the policy names
them. A base merge contributes no product
change of its own. A receipt carried twice keeps the reviewed head as
@@ -940,7 +964,10 @@ only, where `gstack-version-bump` writes; a dependency entry is `changed`),
the CHANGELOG (a heading move or re-heading, compared as an order-independent
multiset of entries), `stamp_paths` and `release_outputs` (every
removed/added pair must be an exact `(was X)` / `since: X` / `vX` rewrite (a quote around X in the first two forms included) with
X the gated version and the replacement the current one, so a dependency pin
X the gated version, or a newer version of the repo's width that the
stamp_paths stage moves (a tentative `*_KEY_VERSION` written as the next
minor, #3512; a version older than the gated one, or of another width, is a
hand edit), and the replacement the current one, so a dependency pin
that happens to equal the old version is `changed`; `stamp_patterns`
templates count as stamp forms too). With `stamp_files` set, a file outside it
is `changed (stamp_files: not in the repo's stamp_files allowlist)` whatever
+1 -1
View File
@@ -18,7 +18,7 @@ Beyond the slash-command skills, gstack ships standalone CLIs for workflows that
| `gstack-review-log` | **Review-pass receipts** — `--start <skill>` captures the working-tree fingerprint before a diff review; `'<JSON>' --finish <token>` consumes that single-use, repository/branch/skill-scoped receipt. Binding requires matching start/end content and reviewer-reported `completed:true` and `converged:true`; it is not independent proof that a model read the source. `--check-shared-libs <token>` reads a current finding as JSON on stdin and checks prior Skip decisions against the actual branch, capture and eligible raw source blobs. It returns `reusable:false` when proof is missing or unsafe, including older records without logger-versioned coverage. Final review logging computes shared-code fingerprints and coverage rather than trusting supplied proof. |
| `gstack-review-read` | **Review freshness** — emits review records with computed `review_freshness.status` and `reason`: CURRENT, STALE, or UNVERIFIED for diff reviews. `/ship` and `/land-and-deploy` use the same grade; a matching commit alone never certifies a diff review. [Dashboard rules](skills.md#review-readiness-dashboard). |
| `gstack-evidence` | **Verification-evidence ledger** — `run --label <lane> -- <cmd>` transparently wraps any test command (the child's exit code always passes through) and records what ran against which working-tree fingerprint; `check` grades each label FRESH/STALE/MISSING with `--expect-cmd`, `--max-age`, and `--allow-paths` binding. /ship and /land-and-deploy cite fresh evidence instead of re-running suites. Per-run logs are 0600, capped at 2MB, pruned after 30 days; the ledger and logs stay machine-local by design. |
| `gstack-pr-wait` | **PR event watcher** — `gstack-pr-wait <pr> [--repo owner/name] [--until green\|ready\|any-change] [--interval 20] [--timeout 60]` polls one pull request through `gh` and prints one stdout line per real event (a check failed with its link, all checks finished, a comment or review from someone other than the author, a review bot finished on the head, the base moved and its new commits touch this PR's files, BEHIND or DIRTY, merged or closed), with progress on stderr, so a monitor wakes the agent once per event instead of on a timer. `green` gates on every check on the head (an advisory bot's review is never waited for, its checks gate like any other), as `gstack-ci-gate` does; a check the base branch requires (rulesets and classic branch protection) is pending until it appears, never green by omission; two identical polls confirm a check set is complete. Exit 0 when the condition is met, 1 when it needs action or `--timeout` passes (`PR_WAIT_BLOCKED`), 2 on bad input, a second watcher for the same PR or a local error (`PR_WAIT_REFUSED`). State under `<state root>/pr-wait/`, baseline discarded after 6 hours. Review bots are configuration: `gstack-config set pr_wait_gating_bots greptile,macroscope`, `pr_wait_advisory_bots coderabbit` (advisory bots are reported, never waited on). `/land-and-deploy` Step 3 is one call of it. Design ported from tstack's pr-wait (MIT, Theo Browne; NOTICE.md). [Exit codes](troubleshooting.md#gstack-pr-wait). |
| `gstack-pr-wait` | **PR event watcher** — `gstack-pr-wait <pr> [--repo owner/name] [--until green\|ready\|any-change] [--interval 20] [--timeout 60] [--exclude CHECK,...]` polls one pull request through `gh` and prints one stdout line per real event (a check failed with its link, all checks finished, a comment or review from someone other than the author, a review bot finished on the head, the base moved and its new commits touch this PR's files, BEHIND or DIRTY, merged or closed), with progress on stderr, so a monitor wakes the agent once per event instead of on a timer. `green` gates on every check on the head (an advisory bot's review is never waited for, its checks gate like any other), as `gstack-ci-gate` does; a check the base branch requires (rulesets and classic branch protection) is pending until it appears, never green by omission; two identical polls confirm a check set is complete; `--exclude` leaves a named non-required check out of the verdict as `gstack-ci-gate --exclude` does (a required name is refused and gates). Exit 0 when the condition is met, 1 when it needs action or `--timeout` passes (`PR_WAIT_BLOCKED`), 2 on bad input, a second watcher for the same PR or a local error (`PR_WAIT_REFUSED`). State under `<state root>/pr-wait/`, baseline discarded after 6 hours. Review bots are configuration: `gstack-config set pr_wait_gating_bots greptile,macroscope`, `pr_wait_advisory_bots coderabbit` (advisory bots are reported, never waited on). `/land-and-deploy` Step 3 is one call of it. Design ported from tstack's pr-wait (MIT, Theo Browne; NOTICE.md). [Exit codes](troubleshooting.md#gstack-pr-wait). |
| `gstack-issue-guard` | **Tracker-text trust envelope** — fetches GitHub issue/PR text (`issue <n>`, `pr-body`, `pr-comments`, or `--stdin`) and wraps it in a labeled envelope so agents treat it as data: injection-shaped lines get labeled even through fullwidth and invisible-character evasion, and forged envelope banners are defused. Every tracker-text ingress in gstack routes through it, enforced by a CI scanner. |
| `gstack-ios-qa-daemon` | **iOS QA daemon** — Mac-side broker between an agent and a connected iPhone or iPad over USB CoreDevice. Loopback by default; `--tailnet` opens a Tailscale-facing listener with identity-gated capability tiers. Single-instance via flock on `~/.gstack/ios-qa-daemon.pid`. See [docs/howto-ios-testing-with-gstack.md](howto-ios-testing-with-gstack.md). |
| `gstack-ios-qa-mint` | **iOS allowlist manager** — owner-grant CLI for the tailnet allowlist. `grant`/`revoke`/`list` against `~/.gstack/ios-qa-allowlist.json` (mode 0600). Remote agents never auto-allowlist; this is the explicit-intent path. |
+38 -13
View File
@@ -2713,11 +2713,19 @@ code (you already do when you run its tests), or drop the release tool.
**Meaning.** `gstack-restamp` was told the base SHA (`--expect-base`) or the
merge order (`--expect-order`) the allocation assumed, and one of them moved:
the base advanced, a predecessor is not merged, or a successor merged first.
The JSON result carries `expected`, `actual` and `recompute`.
`--expect-order` lists the lane's own PR (`--pr`) first, then the remaining
order; a list that leaves it out reads every entry as an unmerged predecessor,
and the message says so. The JSON result carries `expected`, `actual` and
`recompute`.
**Fix.** Run the printed `recompute` command, which recomputes the version
from the current base, then merge with the server-side head condition
(`MERGE_CONDITION: sha=<head>`).
**Fix.** Run the printed `recompute` command: it carries the corrected order
(this PR first, merged PRs dropped, an unmerged predecessor kept ahead because
it must merge first) and recomputes the version from the current base; then
merge with the server-side head condition (`MERGE_CONDITION: sha=<head>`). A
predecessor GitHub calls merged that the local base does not carry yet
(`#<pr>=merged-not-in-base`) is kept ahead too, with `fix: git fetch origin
<base>`: fetch, then rerun, so the recompute, pinned to that local base, never
allocates the version the predecessor already took.
<a id="restamp-version-source"></a>
### `RESTAMP_VERSION_SOURCE`
@@ -4179,13 +4187,19 @@ with the new pin (`train build --prs <n>@<new sha>,…`).
### `QUEUE_TRAIN_CONFLICT`
**Meaning.** Merging a PR onto the prefix before it conflicts outside the
version files and the CHANGELOG. The `QUEUE_TRAIN_CONFLICT:` line names the
PR, the conflicting files and the PRs merged before it. Nothing was
dispatched and no branch was pushed.
version files and the CHANGELOG. From `train build`, the
`QUEUE_TRAIN_CONFLICT:` line names the PR, the conflicting files and the PRs
merged before it, and the check runs before the fill rule, so a filling train
finds it too. From `ready --no-screen`, the prefix is the base itself: the
`BASE_OVERLAP:` line names the files the base moved onto since the head
forked, and the train would stop at this PR (under `--screen` the same
conflict is `QUEUE_SCREEN_STALE_REF`). Nothing was dispatched and no branch
was pushed.
**Fix.** Run the printed `train build --prs <list without that PR>`. The PR's
lane merges main (with the earlier PRs once they land) and reports READY
again.
**Fix.** From `train build`, run the printed `train build --prs <list without
that PR>`. The PR's lane reports the conflict to the controller and, on its
word (crew template rule 14), merges main into the pin (with the earlier PRs
once they land), resolves, pushes, runs /ship and reports READY again.
<a id="queue-train-free-red"></a>
### `QUEUE_TRAIN_FREE_RED`
@@ -4214,7 +4228,11 @@ run carrying the nonce, or marks the dispatch absent; only then does
**Meaning.** The train has fewer PRs than `train.max_size` and its oldest
READY PR has waited less than `train.fill_hours`, so it does not dispatch
yet. The message prints when it will.
yet. The message prints when it will. The scratch merges already ran: the
`MERGE_CHECK: T1..T<k> merge clean onto <base>@<sha>` line above it says every
seated pin merges in slot order (a pin that does not is `QUEUE_TRAIN_CONFLICT`
instead), and a `#<pr> BASE_OVERLAP:` line names a pin whose files the base
moved onto since it forked.
**Fix.** Add READY PRs, rerun after the printed time, or pass `--urgent` for a
one-PR train.
@@ -4693,14 +4711,21 @@ reason }` and carry the `Documentation exception: accepted by <owner> <date> via
## gstack-pr-wait
`gstack-pr-wait <pr> [--repo owner/name] [--until green|ready|any-change]
[--interval SECONDS] [--timeout MINUTES]` watches one pull request and prints
[--interval SECONDS] [--timeout MINUTES] [--exclude CHECK,...]` watches one pull request and prints
one stdout line per real event, prefixed `#<pr>`; progress and recoverable
errors go to stderr. `/land-and-deploy` Step 3 runs it with `--until green`.
Exit 0 means the `--until` condition is met or the PR merged; exit 1 and 2
end with one of the two codes below. `green` gates on every check on the
head, as `gstack-ci-gate` does, so the two never disagree; a check the base
branch requires (rulesets and classic branch protection) counts as pending
until it appears. A 403 on the classic
until it appears. `--exclude <check,...>` leaves a named non-required check
out of the verdict, as `gstack-ci-gate --exclude` does: it neither holds
`green` while pending nor blocks when it fails, and the first poll logs
`excluded from the verdict: <names>`; a required check cannot be excluded
(`exclude refused for required check <name>`) and gates, and while a
required-check source cannot be read nothing is excluded (`exclude refused for
<names>: the required checks could not be read`), as `gstack-ci-gate` refuses
its overrides. A 403 on the classic
protection read (a token without admin scope) is logged and skipped, and the
rulesets read still names the required checks. The review-bot lists are
configuration: `gstack-config set pr_wait_gating_bots greptile,macroscope`