mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-09 02:08:19 +01:00
Merge origin/main into #3409 and stamp v1.91.215.0 (train slot 4)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
commit
7866caac97
40 files changed
+1582
-156
No files matched your search
@@ -109,7 +109,11 @@ and `/proc` start time; elsewhere `ps -o lstart`), because a pid is reused after
|
||||
a reboot or a machine wake. `bin/gstack-upgrade`, and so the session-start
|
||||
self-upgrade, refuses with [UPGRADE_BUSY](troubleshooting.md#upgrade-busy)
|
||||
while a lease held by a live process outside its own chain exists, naming each
|
||||
holder as `pid <p> (<age>m <command>)`. A lease whose pid now belongs to another
|
||||
holder as `pid <p> (<age>m <command>)`. The team-mode SessionStart hook
|
||||
(`bin/gstack-session-update`) honors the same leases: it logs
|
||||
`SKIP leased pid=<p>/<age>m` to `analytics/session-update.log` and tries again at
|
||||
the next session start, without writing its hourly throttle stamp. A lease whose
|
||||
pid now belongs to another
|
||||
process, or an identity-less lease from an older gstack past 120 minutes, is
|
||||
stale: `bin/gstack-lease live` reclaims it (`gstack-lease: reclaimed stale
|
||||
lease …`). Stale needs proof: two well-formed identities from the same backend
|
||||
|
||||
+41
-9
@@ -19,7 +19,8 @@ Three bins, all over `lib/`:
|
||||
|
||||
A pin file names commands. Its presence never authorizes execution: both tools
|
||||
read the committed copy on `origin/<base>` (a PR cannot smuggle a command into
|
||||
its own gate) and run commands only with `--allow-repo-commands`, which `/ship`
|
||||
its own gate; `gstack-pregate run` alone also reads a user-owned file outside
|
||||
the repository when the base has none, see [pregate v1](#pregate-v1)) and run commands only with `--allow-repo-commands`, which `/ship`
|
||||
passes. Standalone, `gstack-regen check` lists the commands and exits 3
|
||||
(`REPO_COMMANDS_NOT_ALLOWED`); `gstack-pregate` marks `regen` and `lanes`
|
||||
`incomplete`, never `pass`. `--policy-from head` (a PR that introduces or
|
||||
@@ -65,6 +66,7 @@ the byte-pinned `/ship` renders in `test/fixtures/golden/`).
|
||||
| `selection` | `workflow.yml/job` → a command printing the lane's file list, for matrices a planner computes (gstack: `scripts/test-free-shards.ts --list`) |
|
||||
| `warn` | check ids downgraded to warn; `secrets` and required lanes never |
|
||||
| `strays.allow` | intentional root files or new top-level directories |
|
||||
| `local` | `workflow.yml/job` lanes a local run satisfies: a platform-agnostic toolchain (an `ubuntu-latest` suite that runs the same on a Mac), or a repo whose CI is off. Their tests run here instead of becoming `requires-remote`, except that on a machine under 8 cores with a `remote_gate` or a remote `test_backend` the small-machine rule still records the test tier as a `requires-remote` obligation. `--explain` notes an id that matches no workflow lane |
|
||||
| `guards` | `[{ id, when: [globs], command, fix?, timeout_s? }]`: repo commands a lane must pass when a touched file matches `when` (manifest checks, subprocess goldens the import graph cannot see); see [guards](#guards) |
|
||||
| `serial_patterns` | test-file globs the `lanes` check runs one process per file, apart from the unit files (default `**/*.serial.test.*`) |
|
||||
| `sealed` | sealed or eval output that must never reach a public destination; read by the pre-push hook and `gstack-pregate sealed` (see [sealed output](#sealed)) |
|
||||
@@ -72,8 +74,36 @@ the byte-pinned `/ship` renders in `test/fixtures/golden/`).
|
||||
| `stall_timeout_s` | seconds a test command may print nothing before its process group is killed and the check is `incomplete` (`PREGATE_STALLED`); default 300, `0` disables; `--stall-timeout` overrides (see [supervision](#supervision)) |
|
||||
| `remote_gate` | the repo's remote test gate command; under 8 cores the local test tier becomes a `requires-remote` obligation naming it, and `--remote` runs it (see [remote gate](#remote-gate)) |
|
||||
|
||||
Only the `origin/<base>` copy is honoured, so a downgrade is reviewed before it
|
||||
takes effect. `gstack-pregate validate` / `init` cover the working-tree file.
|
||||
Only the `origin/<base>` copy or the user-owned file below is honoured, so a
|
||||
downgrade is reviewed (or is the user's own) before it takes effect. A repo that
|
||||
keeps `.gstack/` out of git (a shared repo where gstack is one contributor's
|
||||
tool) can hold the same file at `<state root>/projects/<slug>/pregate.json`. The
|
||||
state root is `GSTACK_STATE_ROOT`, else `GSTACK_HOME`, else `GSTACK_STATE_DIR`,
|
||||
else a gstack plugin's data directory, else `~/.gstack` ([docs/state-root.md](state-root.md)); `<slug>` is what
|
||||
`~/.claude/skills/gstack/bin/gstack-slug` prints as `SLUG=` in the repository
|
||||
(`GSTACK_PROJECT_SLUG` overrides it). It is read only when the base carries none
|
||||
(never under `--policy-from head`), it lives outside the repository so a PR still
|
||||
cannot reach it, and the record names it (`policy user <path>`) and fingerprints
|
||||
its bytes (`policy_digest`, and each check's `inputs.policy`); `verify` and the
|
||||
ship receipt treat an edited or deleted user file, or one that appears (or a
|
||||
link on its path that starts pointing nowhere) after a run under the defaults,
|
||||
as `PREGATE_STALE`. A path that is relative, resolves inside the repository, or
|
||||
reaches it through any symlink on the way (a state root linked into the
|
||||
checkout, or a link hop inside it, even when the last hop points back outside)
|
||||
is ignored, since a PR could commit it or choose the file; `--explain` says
|
||||
so, or says `(absent)` when no file is there. The slug and state root come from
|
||||
the caller's environment, never the checkout's `.env`: the bin's shebang passes
|
||||
`--no-env-file`, and a bare `bun gstack-pregate` launched beside any `.env` file
|
||||
refuses (`POLICY_SOURCE_UNAVAILABLE`). `verify` reads both paths from the
|
||||
record, not from its own environment.
|
||||
It covers `gstack-pregate run` only: `sealed` and `price_file` are read from the
|
||||
base branch's copy, so the user file refuses them (`PREGATE_CONFIG_INVALID`)
|
||||
rather than holding a policy nothing reads. A user file that exists but cannot
|
||||
be read, or a symlink on its path that points nowhere, refuses the same way
|
||||
instead of falling back to defaults. A base copy git cannot read (and
|
||||
`git ls-tree` cannot confirm is absent) refuses with `POLICY_SOURCE_UNAVAILABLE`
|
||||
instead of handing the run to the user file.
|
||||
`gstack-pregate validate` / `init` cover the working-tree file.
|
||||
|
||||
`gstack-pregate init` writes a draft built from the repo, not a fixed example.
|
||||
It prints the languages it found and the runner each language's tests would
|
||||
@@ -115,13 +145,15 @@ Two stages with separate clocks, both printed:
|
||||
`TIMING_ASSERTIONS: found=<n> escaped=<n> files=<n>` summary. It warns
|
||||
(`PREGATE_TIMING_ASSERTION`); it does not block. See [timing](#timing).
|
||||
|
||||
- `guards`: each guard in `.gstack/pregate.json` whose `when` globs match a
|
||||
- `guards`: each guard in the active policy (`.gstack/pregate.json`, or the
|
||||
user-owned file) whose `when` globs match a
|
||||
touched file (renames included) runs its `command` in its own process, under
|
||||
the verified launch Bun, with its own `timeout_s` (default `--timeout`). A
|
||||
non-zero exit fails pregate naming the guard and its `fix`
|
||||
(`PREGATE_GUARD_FAILED`); a timeout is `incomplete`; without
|
||||
`--allow-repo-commands` the selected guards are listed and the check is
|
||||
`incomplete`. Guards are read from the base branch's copy and cannot be
|
||||
`incomplete`. Guards are read from the base branch's copy (or the
|
||||
user-owned file when the base carries none) and cannot be
|
||||
downgraded to warn. See [guards](#guards).
|
||||
|
||||
**Stage 2, test execution:**
|
||||
@@ -181,7 +213,7 @@ Two stages with separate clocks, both printed:
|
||||
failure, a `| tail`-hidden exit is still an exit; cargo's `test result:` lines, one per
|
||||
test binary and doc-tests included, are summed); a platform lane this
|
||||
machine cannot run becomes a `requires-remote` obligation naming
|
||||
`workflow.yml/job`. `--explain` prints each test's provenance and every
|
||||
`workflow.yml/job`, unless `local` names that lane. `--explain` prints each test's provenance and every
|
||||
uncertain mapping (a planner-computed matrix without a `selection` manifest
|
||||
selects conservatively). Test files matching `serial_patterns` (default
|
||||
`**/*.serial.test.*`) run one process per file after the unit files. The
|
||||
@@ -194,7 +226,7 @@ Two stages with separate clocks, both printed:
|
||||
runner process (the unit-file group, each serial file, each guard without
|
||||
its own `timeout_s`), not their total; the stall timeout catches a hung or
|
||||
silent one sooner.
|
||||
- Under 8 cores, a base-branch `remote_gate` or a remote `test_backend`
|
||||
- Under 8 cores, a `remote_gate` in the active policy or a remote `test_backend`
|
||||
(`gstack-config set test_backend ubicloud`) records the local test tier as a
|
||||
`requires-remote` obligation instead of running it: see [remote gate](#remote-gate).
|
||||
|
||||
@@ -260,8 +292,8 @@ misses a descendant that was reparented or started detached.
|
||||
<a id="remote-gate"></a>
|
||||
## Small machines and the remote gate
|
||||
|
||||
Under 8 cores (`os.availableParallelism()`), when the base branch's
|
||||
`.gstack/pregate.json` declares `remote_gate` or `gstack-config get
|
||||
Under 8 cores (`os.availableParallelism()`), when the active policy (the
|
||||
base branch's `.gstack/pregate.json`, or the user-owned file) declares `remote_gate` or `gstack-config get
|
||||
test_backend` is not `local`, the lanes check runs nothing locally: it records
|
||||
the selected local files as a `requires-remote` obligation with the command to
|
||||
run, `pregate.json/remote_gate` (the declared command) or
|
||||
|
||||
@@ -104,6 +104,9 @@ lives in `projects/<slug>/`. `bin/gstack-slug` picks the slug from the git
|
||||
and the ssh, https and ssh-with-port spellings of one repository share one.
|
||||
- Without a remote, the project root's directory name is used.
|
||||
|
||||
A bucket can also hold a user-owned `pregate.json` policy, for a repo that keeps
|
||||
`.gstack/` out of git (see [pregate v1](pregate.md#pregate-v1)).
|
||||
|
||||
The first slug gstack resolves in a directory sticks (it is cached under
|
||||
`slug-cache/`), so a project keeps its bucket when it adds a remote later.
|
||||
|
||||
|
||||
+28
-8
@@ -2474,7 +2474,13 @@ file is described in [docs/ship-policy.md](ship-policy.md); the queue flow in
|
||||
**Meaning.** The policy is read from the committed copy on `origin/<base>`,
|
||||
and that ref does not resolve here (a shallow or detached gate checkout, or a
|
||||
fetch that never happened). The tool exits 3 and never falls back to the
|
||||
working-tree file silently.
|
||||
working-tree file silently. For `gstack-pregate` the same refusal covers a base
|
||||
`.gstack/pregate.json` git cannot read (a timeout, an oversized blob, a missing
|
||||
or corrupt object) that `git ls-tree` cannot confirm is absent; it never falls
|
||||
through to the user-owned file. It also refuses when Bun loaded a `.env` file
|
||||
from the working directory (a bare `bun gstack-pregate`), since a branch could
|
||||
then pick the user policy: run `gstack-pregate` directly or as
|
||||
`bun --no-env-file`.
|
||||
|
||||
**Fix.** `git fetch origin <base> --depth=1`, then rerun. To run against the
|
||||
branch's own file deliberately, pass `--policy-from head --allow-repo-commands`;
|
||||
@@ -2877,7 +2883,11 @@ or `REALITY: surface-check finding bun upgrade --version absent on 1.3.14 bin/gs
|
||||
|
||||
Both tools read their pin file (`.gstack/generated.json`, `.gstack/pregate.json`)
|
||||
from `origin/<base>`, never the working tree, and run repo-declared commands only
|
||||
under `--allow-repo-commands` or inside `/ship`. Details: docs/pregate.md.
|
||||
under `--allow-repo-commands` or inside `/ship`. When the base carries no
|
||||
`.gstack/pregate.json`, `gstack-pregate run` reads
|
||||
`<state root>/projects/<slug>/pregate.json` instead (outside the repository; see
|
||||
docs/pregate.md for the state root and slug).
|
||||
Details: docs/pregate.md.
|
||||
|
||||
<a id="regen-registry-invalid"></a>
|
||||
### `REGEN_REGISTRY_INVALID`
|
||||
@@ -2932,10 +2942,16 @@ repository's lockfile), then rerun `gstack-regen check` or the pre-gate.
|
||||
|
||||
**Meaning.** `.gstack/pregate.json` has an unknown key, a wrong type, a glob
|
||||
that escapes the repository, a runner without `{files}`, or `secrets` in `warn`.
|
||||
The user-owned `<state root>/projects/<slug>/pregate.json` also refuses when it
|
||||
sets `sealed` or `price_file` (both are read only from the base branch's copy)
|
||||
or exists but cannot be read (a dangling symlink on its path counts).
|
||||
|
||||
**Fix.** `gstack-pregate validate`. `secrets` and required-lane obligations
|
||||
cannot be downgraded; every other check id in `warn` is honoured only from the
|
||||
base branch's copy.
|
||||
base branch's copy or the user-owned file. For the user-owned file, edit it
|
||||
directly at the path the message names. A user file inside the repository, or
|
||||
reached through a symlink into it, is ignored rather than read (`--explain`
|
||||
names it).
|
||||
|
||||
<a id="pregate-secrets"></a>
|
||||
### `PREGATE_SECRETS`
|
||||
@@ -2980,7 +2996,7 @@ allowlisted tool config no test names, are the assets-and-tool-config empty lane
|
||||
not this error.
|
||||
|
||||
**Fix.** Add a test that names the file, or declare its lane (an explicit `[]`
|
||||
for files with no behaviour) under `dependencies` in `.gstack/pregate.json`.
|
||||
for files with no behaviour) under `dependencies` in `.gstack/pregate.json` (or the user-owned file the message names).
|
||||
Other non-Markdown documentation goes in `empty_lanes` on the base branch.
|
||||
Dart files under `integration_test/` or `test_driver/` need a device, so pregate
|
||||
never runs them: declare `"**/integration_test/**": []` (and
|
||||
@@ -3019,7 +3035,7 @@ declare `runner`), raise `--timeout`, or keep the tree still, then rerun.
|
||||
|
||||
**Meaning.** A test command the lanes check (or a guard, or the `--remote`
|
||||
gate) launched wrote nothing to its log for the stall timeout (default 300 s,
|
||||
`stall_timeout_s` in the base branch's `.gstack/pregate.json`, or
|
||||
`stall_timeout_s` in the base branch's `.gstack/pregate.json` or the user-owned file, or
|
||||
`--stall-timeout`). pregate killed its process group and names the last test
|
||||
file started. The check is incomplete, never pass.
|
||||
|
||||
@@ -3047,7 +3063,10 @@ file with `--receipt`, or let `gstack-pregate --stage tests --remote --spend-cap
|
||||
### `PREGATE_STALE`
|
||||
|
||||
**Meaning.** `pregate.json` was recorded against a different working-tree
|
||||
fingerprint than the one being published (the final stamp, a regen, any edit).
|
||||
fingerprint than the one being published (the final stamp, a regen, any edit),
|
||||
or the user-owned policy it ran under was edited or deleted (or one appeared,
|
||||
or a symlink on its path started pointing nowhere, after a run under the
|
||||
defaults).
|
||||
|
||||
**Fix.** Rerun `gstack-pregate` after the final generation and stamp; Step 18
|
||||
refuses to write the ship receipt otherwise.
|
||||
@@ -3276,8 +3295,9 @@ next `./setup` prints no CRLF warning.
|
||||
<a id="pregate-guard-failed"></a>
|
||||
### `PREGATE_GUARD_FAILED`
|
||||
|
||||
**Meaning.** A guard from the base branch's `.gstack/pregate.json` (a repo
|
||||
command such as a serial-test manifest check or a CLI golden test) matched a
|
||||
**Meaning.** A guard (a repo command such as a serial-test manifest check or a
|
||||
CLI golden test) from the base branch's `.gstack/pregate.json`, or from the
|
||||
user-owned file when the base carries none, matched a
|
||||
file the branch touched and exited non-zero. Guards cover what the import graph
|
||||
cannot see: manifests, subprocess goldens, registries. They cannot be
|
||||
downgraded to warn.
|
||||
|
||||
Reference in new issue
Block a user