mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-08 09:48:20 +01:00
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:
commit
c7f6daa7a4
30 files changed
+654
-85
No files matched your search
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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`
|
||||
|
||||
Reference in new issue
Block a user