From 669e59d65b48081547de9a3ae202122af5bbe065 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 25 Oct 2026 03:50:18 -0700 Subject: [PATCH 1/9] v1.91.206.0 feat(pregate): user-owned policy file and local lanes (port of #3286) (#3398) * feat(pregate): user-owned policy file and local lanes A repo that keeps .gstack/ out of git can hold its pregate policy at /projects//pregate.json. gstack-pregate run reads it only under --policy-from base when the base carries no .gstack/pregate.json, so a reviewed base copy always wins and a PR still cannot reach the policy. A missing file falls back; any other read error, and a non-null sealed or price_file (read only from the git copies), refuse with PREGATE_CONFIG_INVALID and a fix that names the file. A new `local` key lists / lanes a local run satisfies: a platform lane named there runs here (tier unit) instead of becoming a requires-remote obligation. Lane ids share one LANE_ID check with selection. Co-Authored-By: Claude Opus 5.5 * docs(pregate): the user-owned policy file and local lanes Co-Authored-By: Claude Opus 5.5 * fix(pregate): policy fixes name the user-owned file when it is the active policy With no .gstack/pregate.json on the base, an edit to that path is never read, so the no-lane, runner, stall_timeout_s, remote_gate and strays fixes now name ctx.configFile: the user-owned path when it is the active policy. Co-Authored-By: Claude Opus 5.5 * test(pregate): the remote_gate, strays and stall fixes name the active policy file Co-Authored-By: Claude Opus 5.5 * fix(pregate): pre-landing review fixes for the user-owned policy Ignore a user policy path that is relative or inside the checkout, fingerprint the active user file into check inputs, name the user file in its JSON errors and the refusal lead, add --explain lines for an absent user file and an unknown local id, and document the state-root location and slug lookup. Co-Authored-By: Claude Opus 5.5 * fix(pregate): containment edges and explain wording for the user policy A state root under an in-repo directory named ..something passed the inside-the-repo check, and realpath kept the caller's casing on macOS. The unknown-local explain line now says workflow lane, and the state-root chain in explain and docs names every source. Co-Authored-By: Claude Opus 5.5 * fix(pregate): a base copy git cannot read, or a dangling user symlink, refuses Any git show failure read as no base policy, which now hands the run to the unreviewed user file; it is absent only when cat-file says so, and a spawn error (stdout cut at the buffer limit) counts as a failure. A user pregate.json that is a dangling symlink refuses instead of reading as absent. Co-Authored-By: Claude Opus 5.5 * fix(pregate): decide a missing base policy from the tree, and catch dangling parent links cat-file -e checks the blob, so a missing or corrupt object still read as no base copy; ls-tree reads the tree entry, and any failure refuses. The dangling-symlink check now covers every path component, not just the file. Co-Authored-By: Claude Opus 5.5 * fix(result-codes): POLICY_SOURCE_UNAVAILABLE names an unreadable committed pin file Co-Authored-By: Claude Opus 5.5 * docs(pregate): the active policy may be the user-owned file; new refusals Guards, remote_gate, stall timeouts and lane fixes name the user-owned file when it is the active policy; the trust boundary says run alone reads it; the dangling-symlink and unreadable-base-copy refusals are documented, and state-root lists the bucket's pregate.json. Co-Authored-By: Claude Opus 5.5 * fix(pregate): a checkout's .env cannot pick the user policy; verify re-checks it Bun loads .env from the working directory, so a branch could set GSTACK_PROJECT_SLUG or a state root and choose which pregate.json governs it. The shebang now passes --no-env-file, scriptInvocation carries that flag over when it launches the bin as bun