diff --git a/docs/pregate.md b/docs/pregate.md index 58314a53d..859ba1407 100644 --- a/docs/pregate.md +++ b/docs/pregate.md @@ -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/` (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 @@ -89,7 +90,10 @@ so, or says `(absent)` when no file is there. 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 refuses the same way instead of falling back to defaults. +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` @@ -123,13 +127,15 @@ Two stages with separate clocks, both printed: `TIMING_ASSERTIONS: found= escaped= files=` 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:** @@ -150,7 +156,7 @@ Two stages with separate clocks, both printed: `set -o pipefail`, judged on the runner's summary line (zero tests ran is a failure, a `| tail`-hidden exit is still an exit); 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 @@ -163,7 +169,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). @@ -229,8 +235,8 @@ misses a descendant that was reparented or started detached. ## 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 diff --git a/docs/state-root.md b/docs/state-root.md index 5cd662ee5..45c246ec6 100644 --- a/docs/state-root.md +++ b/docs/state-root.md @@ -104,6 +104,9 @@ lives in `projects//`. `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. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index b47ead752..aa1652f35 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -2354,7 +2354,10 @@ 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/`, 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. **Fix.** `git fetch origin --depth=1`, then rerun. To run against the branch's own file deliberately, pass `--policy-from head --allow-repo-commands`; @@ -2812,7 +2815,7 @@ repository's lockfile), then rerun `gstack-regen check` or the pre-gate. that escapes the repository, a runner without `{files}`, or `secrets` in `warn`. The user-owned `/projects//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. +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 @@ -2859,7 +2862,7 @@ test. An unmapped source or code file still fails. A file under a test directory is not this empty lane. **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. `--explain` prints every mapping and why. For a file with no lane it also prints the import specifiers in test files that resolved to no repo file (for example a @@ -2892,7 +2895,7 @@ 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. @@ -3149,8 +3152,9 @@ next `./setup` prints no CRLF warning. ### `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.