Repository navigation
v1.91.254.0 pregate: starter policy, benign-finding allowlist, environment vs diff failures, per-run scratch - #3480
Merged
Merged
Conversation
…es, per-run scratch A repository with no .gstack/pregate.json now gets a warn (PREGATE_NO_POLICY) with a starter policy printed, instead of PREGATE_NO_LANE on every touched file; gstack-pregate init remains the explicit write. The policy gains an allowlist for benign secrets/pii findings (env.value and secret never allowlistable). Checks that cannot run because of the machine report PREGATE_ENVIRONMENT (incomplete, with kind and evidence) apart from diff failures. Every run writes its scratch under the state root, per run id. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
😎 This pull request was merged. |
…o its test Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…tant name for a one-character value Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
guards: an exited guard is the machine's only on exit 127, never from its output; secrets: clear an interrupted run's added-line copies before scanning; environment: one fix table per kind, one ANSI strip, hoisted not-found vocabulary; starter returns its lines; allowlistDigest reuses sha256; stale headers and the PREGATE_INCOMPLETE fix clause updated; tests for the mixed verify branch, disk precedence, allowlist boundaries, a positionless finding, head-policy strictness, run-id edges and the unwritable state root. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…sumer
consumersOf anchored both the stem and the full basename on a path
separator, so run('install.sh') or bash install.sh without a directory
stopped counting for a multi-character old value. Only the bare stem
needs the separator; the full file name is specific on its own.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…n --run id gets its own directory Adversarial review of the environment classifier and the scratch layout: - exit 127 and 'command not found' are missing-binary only when the command's own binary is off PATH (runner, guard and remote gate alike); a script the diff controls that exits 127 is the diff's failure - a local lane with no test summary is classified from its output only when the runner was not seen starting a test file; a remote gate only from its first 40 lines, before any test output could stream - environment.proof records launch (certain) or output (inferred); an output-inferred cause never says 'the diff is not at fault', its line says '(inferred from output)', and verify names such checks - a runner killed by a signal is PREGATE_INCOMPLETE, not missing-binary - a --run id whose directory exists takes <id>-2 .. -9 instead of sharing (and truncating) a live run's logs - engine.* findings are never allowlistable; a pattern entry the scanner gave no position for is reported as not tested instead of silently inert - docs: synopsis --run, no-policy lanes wording, verify's code list, guard exit 127 carve-out, PREGATE_SECRETS allowlist pointer, scratch Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ELOG lists every doc edit Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
6 of 7 tasks
7 tasks done
…nment vs diff failures, per-run scratch Merge origin/main (4d48d39, v1.91.253.0) and restamp at slot 1. FILES: CHANGELOG.md VERSION agents-digest/gstack-AGENTS.md package.json MERGE_CONDITION: sha=c3c96b3ad0af6a486958872020ace795e83a8f2d NEW_VERSION=1.91.254.0 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
garrytan
added a commit
that referenced
this pull request
Oct 26, 2026
…tamp-only Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
garrytan
added a commit
that referenced
this pull request
Oct 26, 2026
Rule 19 grant (train 15 QUEUE_TRAIN_CONFLICT). CHANGELOG additive. test/fixtures/context-budget.json by hand: canary keeps the hoisted ceiling (main did not change it; the conflict was main's adjacent new captain row); captain (new on main, now hoisted) and five loaded-skill ceilings lowered to a fresh capture (captain 12011 -> 6551, autoplan, office-hours, plan-ceo-review, plan-devex-review, plan-eng-review by 2-22). No ceiling raised. Generated files and ship goldens regenerated. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
garrytan
added a commit
that referenced
this pull request
Oct 26, 2026
…or stands in for it Merging origin/main b004c06 (#3480) into this branch created a semantic conflict between two individually correct parents: #3480 made verifyPregate return PREGATE_ENVIRONMENT (instead of PREGATE_INCOMPLETE) when every incomplete check was caused by the environment, and this branch's by-tree mirror reader in ready() treated only PREGATE_LANE_FAILED and PREGATE_INCOMPLETE as final. Merged, a local record whose secrets scan never finished fell through to a same-tree tests-only mirror and ready passed without a secrets scan, against the rule this branch documents ("a record whose own checks failed or stayed incomplete is never overridden"). ready() now keeps a PREGATE_ENVIRONMENT record final as well. Regression in test/gstack-ship-queue.test.ts ("one READY block ..."): an environment-incomplete secrets record beside a passing same-tree mirror is refused with (PREGATE_ENVIRONMENT) and no "from the state root" suffix. Red on the bare merge 28b64d7 (expected exit 3, received 0), green with this fix. Found by the pass-4 delta review of the merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This was referenced Oct 26, 2026
v1.91.276.0 feat(preamble): tier 2+ skills load the shared preamble once per session (GSTA-96)
#3434
Merged
capy-ai Bot
added a commit
that referenced
this pull request
Oct 27, 2026
… train-gated commit; cross-repo refs, run ids, pr-body section merge, pregate by tree (#3483) * fix(release-cut,ship-queue): the weekly release accepts a train-gated commit, and five lane frictions seen in operation scripts/release-cut.ts gates a main commit either by a successful backstop check run or by a green/merging/merged train record in refs/notes/gstack-trains whose merged[] names its sha or tree (CANDIDATE: ... gated by train <nonce>); a red check, a running run, the required check and the freeze are judged as before. --required-check, --backstop-check and --notes-ref make the names configurable. lib/issue-links.ts reads (name #N[, #M]), (name PR #N) and (name issue #N) as cross-repo references. lib/gstack-post.ts allowlists an 11-digit run id after a run word or chained after one, and pr-body merges by ## section into the PR's current body (--replace overwrites). gstack-pregate mirrors each record under <state root>/pregate/by-tree/<tree>.json and ready reads the mirror for its tree when the checkout's record is missing or for another tree. docs/troubleshooting.md gains #redact-run-id-as-phone, #gate-error-no-base and #gstack-post-pr-body-merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (pass 1) Fence-length-aware section splitting in gstack-post pr-body (an unclosed receipt fence or a four-backtick block no longer exposes a heading to the merge), the run-id allowlist drops generic words (id, job, attempt, workflow), ready accepts a by-tree pregate mirror only from a complete run against origin/<base> under a reviewed policy and prints no home path, release-cut skips non-object notes and never substitutes train evidence for the required check when both names are equal, issue-links treats closing/cf./e.g./v1.2 as words rather than repositories. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (pass 2) An unclosed receipt fence makes the body from that line on one section the merge never matches, so posted leading prose cannot replace it and a closed receipt before it stays a receipt; the by-tree pregate mirror must hold the tests stage (what --stage tests --remote writes) rather than every check; empty sha/tree strings in a train note are not evidence; (v1.2.3-rc1 #301) is a version word, not a repository. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CANDIDATE line names its gate; issue-links reference documents the (name #N) form Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship-queue): a PREGATE_ENVIRONMENT local record is final; no mirror stands in for it Merging origin/main b004c06 (#3480) into this branch created a semantic conflict between two individually correct parents: #3480 made verifyPregate return PREGATE_ENVIRONMENT (instead of PREGATE_INCOMPLETE) when every incomplete check was caused by the environment, and this branch's by-tree mirror reader in ready() treated only PREGATE_LANE_FAILED and PREGATE_INCOMPLETE as final. Merged, a local record whose secrets scan never finished fell through to a same-tree tests-only mirror and ready passed without a secrets scan, against the rule this branch documents ("a record whose own checks failed or stayed incomplete is never overridden"). ready() now keeps a PREGATE_ENVIRONMENT record final as well. Regression in test/gstack-ship-queue.test.ts ("one READY block ..."): an environment-incomplete secrets record beside a passing same-tree mirror is refused with (PREGATE_ENVIRONMENT) and no "from the state root" suffix. Red on the bare merge 28b64d7 (expected exit 3, received 0), green with this fix. Found by the pass-4 delta review of the merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * v1.91.262.0 stamp Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
A touched file with no test lane in a repository that has no
.gstack/pregate.json: before, a hardPREGATE_NO_LANEfail (exit 1) with no way forward; after, awarnunderPREGATE_NO_POLICYwith a starter policy printed, the selected tests still run, and the lane log lives under the state root's per-run scratch instead of/tmp.Before/after as text: A touched file with no test lane in a repository that has no .gstack/pregate.json: before, a hard PREGATE_NO_LANE fail (exit 1) with no way forward; after, a warn under PREGATE_NO_POLICY with a starter policy printed, the selected tests still run, and the lane log lives under the state root's per-run scratch instead of /tmp.
Why
Three Captains lost their first
/shippass toPREGATE_NO_LANEin a repository with no.gstack/pregate.jsonyet, the non-downgradablesecretscheck refused 1,800 dummy per-trial tokens and run ids read as phone numbers, a lane whose runner could not start read as the diff's failure, and pregate's/tmplogs were shared between parallel runs and wiped between Capy wakes (harness-codify Lane D; Garry, 2026-10-26: "Do it and also improve it: fix the issues all the captains have seen in this Capy project"). GSTA-114'sGSTACK_CAPY_WRAPPER=2 → 3edit then flagged 575 test files underPREGATE_LITERALS, so theconsumersOffix rides here. A user now gets a warn with a starter instead of a dead end, can name a benign finding with a scoped, reasoned allowlist entry (never a real credential value), sees whether a failure was the machine's or the diff's and how certain that is, and finds the lane log where thefix:line says.Approvals
Source: gstack-approvals record for gsta-112/pregate-ergonomics (display only; not an approval citation).
Summary
Pregate ergonomics for a repository that is not gstack (harness-codify Lane D; plan rows E4–E6). Three Captains lost their first
/shippass toPREGATE_NO_LANEin a repo with no.gstack/pregate.jsonyet; the non-downgradablesecretscheck refused 1,800 dummy per-trial tokens, 11-digit Actions run ids and a vendor's public web key; a lane whose runner could not start read as the diff's failure and got waived by test name; and pregate's logs underos.tmpdir()were shared between parallel runs and wiped by a Capy wake.No policy → warn plus a printed starter, never a hard
PREGATE_NO_LANEWhen the base branch carries no
.gstack/pregate.jsonand no user-owned policy exists, a touched file with no lane is awarnrow under the new codePREGATE_NO_POLICY. The selected tests still run (a red lane is stillfail; an unknown platform matrix is still arequires-remoteobligation) and the table prints a starter drafted from the repository betweenPREGATE_STARTER: beginandend: detected runner, test directories, CI lanes, adependenciesentry per unmapped touched file (nearest tests or same stem, never[]), and aTODOline per file nothing covers.runwrites nothing;gstack-pregate initis the explicit write. Once any policy exists, an unmapped file isPREGATE_NO_LANEas before.allowlistin the policy, withenv.valueand thesecretcategory un-allowlistableallowlist: [{ finding, paths?, pattern?, reason }](lib/pregate/allowlist.ts).findingis a gstack-redact id or one of the categoriespii,internal,hygiene,legal; one scope (pathsorpattern) and areasonare required;patternis matched at the finding's column and must end at a token boundary. Thesecretsrow saysN allowlisted (<ids>), names each allowlisted finding with its entry and reason, and records the entries' digest asinputs.allowlist. Namingenv.value,secretor anengine.*scanner refusal is aPREGATE_CONFIG_INVALIDatallowlist[i].finding, and the matcher never consults the list forenv.value, so a hand-edited policy cannot silence a real credential value either (test: apattern: ".*"entry injected past validation still fails). Apatternentry that named a finding the scanner gave no position for is reported as not tested instead of silently inert.Environment failures apart from diff failures
lib/pregate/environment.tsclassifies a check that could not run because of the machine (missing binary / exit 127 / ENOENT, network, quota, disk, runtime, gstack's own scanner not finishing) asincompletewith the new codePREGATE_ENVIRONMENT,cause: "environment"andenvironment: { kind, evidence, proof }inpregate.json, and anenvironment:table line.proofislaunch(the command could not start, its own binary is off PATH, the runtime or a gstack tool refused: "the diff is not at fault") oroutput(a rule matched the command's text before any test was counted, which the diff's own output could have written: the line says(inferred from output)and the fix asks for confirmation on another machine or with--remote).PREGATE_LANE_FAILED,PREGATE_GUARD_FAILEDandPREGATE_SECRETScarrycause: "diff". The classifier is conservative: a counted test failure is the diff's whatever its log says; a local lane with no summary is read from its output only when the runner was not seen starting a test file; exit 127 andcommand not foundaremissing-binaryonly when the command's own binary is off PATH, so a runner, guard or gate script the diff controls that exits 127 is the diff's; a remote gate is environment only when it printed noPREGATE_REMOTE:line and the cause is in its first 40 lines, before any test output could stream; a runner killed by a signal, stalls and timeouts keepPREGATE_INCOMPLETE/PREGATE_STALLED.verifyprintsPREGATE_ENVIRONMENT: <n> check(s) blocked by the environment (<kinds>), not by the diffwhen every incomplete check is environment-caused, naming any check inferred from output alone.One scratch directory per run under the state root
<state root>/projects/<slug>/pregate/runs/<run id>/(0700;--run <id>names it) holds the secrets scan's added-line copies (deleted after the scan),lanes/lanes.log,guards/<id>.logand the remote gate's files.pregate.jsonrecords it asscratch, the table printsscratch: <dir>; a--run <id>whose directory already exists takes<id>-2…-9instead of sharing (and truncating) a live run's logs; runs older than 7 days are pruned; an unwritable state root falls back to a private per-run directory underos.tmpdir()and says so.Outside
lib/pregate/**, additivelib/result-codes-release.ts: the two new rows (lib/result-codes.tssits at the 800-line ratchet, and the doctor/cathedral/stop-hook tests copy a fixed list of lib files, so a new module there broke them).lib/headless-artifacts.ts: the pregate schema acceptscause,environment,scratch.scripts/lib/windows-curation.ts: the four new tests excluded on Windows (POSIX binaries and modes).bin/gstack-doctor-components.sh: regenerated (gen:skill-docs) for the two anchors.test/pregate-invalidation.test.ts: 30 s per-test timeouts; it ran 4.2 s on main alone and timed out at Bun's 5 s default under a loaded 28-file run.literalsno longer floods on a common module stem (GSTA-114)consumersOfcounted a test as a consumer of a changed constant when it merely contained the source file's stem as a bare word (install,retry), soGSTACK_CAPY_WRAPPER=2 → 3incapy/skill/gstack/scripts/install.shflagged 575 test files. The module now counts as its full file name (install.sh) or as a path fragment (/retry.js), and a one-character old value needs the constant's name on the line. Probe on the real tree: 575 → 5 consumers, all namingGSTACK_CAPY_WRAPPER.Tests (red before, green after)
test/pregate-starter.test.ts(4),test/pregate-allowlist.test.ts(6),test/pregate-environment.test.ts(9, incl. the spoof: a bun test that printsfetch failedand exits before the summary is at most an output-inferred cause whose fix never clears the diff),test/pregate-scratch.test.ts(3), theconsumersOfregression intest/pregate-selection.test.ts. Red outputs before the change:exit 1 (PREGATE_NO_LANE)on a no-policy fixture;exit 1 (PREGATE_SECRETS) | 1 credential finding(s) in added lines (env.kv)on a dummysk-trial-…token;PREGATE_LANE_FAILED cause= undefined | local lane exited 127on a missing runner;log: /tmp/gstack-pregate-lanes-*/lanes.log, noscratchfield. Existing no-policy fixtures inpregate-selection,pregate-docs-lane-review,pregate-non-js,pregate-rust,pregate-dartnow assert the warn contract /PREGATE_ENVIRONMENT.Checks run:
bun run typecheck; everytest/pregate-*.test.tsexcept the pre-existingpregate-regatered (identical onorigin/main); gstack-doctor, module-size ratchet, troubleshooting anchors, changelog, headless-artifacts schema, parity suite, bun-entry isolation. Pregate stage 1 clean on the final tree; remote free tier on a standard-4 VM: 21503 tests, 0 failed on tree a5e9fd18dd36 ($0.04); CI green at c3c96b3.Test Coverage
All new code paths have test coverage (Step 7 coverage audit at 213ffe9; no weak gaps named). Tests ran: pregate.json/remote_gate 21503 (0 failed); local pregate/doctor/ratchet/parity suite 324 (0 failed).
Pre-Landing Review
Pass 1 (whole diff, 6 specialists): 27 INFORMATIONAL, 0 CRITICAL; 21 auto-fixed in 213ffe9, 6 advisory skipped (performance notes in starter.ts/allowlist.ts/secrets.ts/run-scratch.ts, two test-shape notes). Pass 2 (range e9f9c21..213ffe9): 1 INFORMATIONAL, fixed in e890d0d (
consumersOfalso demanded a separator before the full file name). Pass 3 (range 213ffe9..f2a3246, the cap): 5 INFORMATIONAL, none at the blocking floor, deferred and named here:lib/pregate/environment.tsclassifyCommandEnvironment: a relative-path command (./scripts/gate.sh) is resolved against the process cwd, not the command's cwd; a diff that deletes a base-policy script reads as missing-binary/launch. Incomplete never passes; follow-up: resolve against the command's cwd and treat a slash-bearing path as the diff's.lib/pregate/checks/lanes.tslocal-lane comment overstatesobservedStart(set only when a progress adapter is active; single-file and serial runs never set it, so a single-file spoof is still an output-inferred cause with its caveat). Follow-up: detect headers for single-file runs too.lib/headless-artifacts.tspregate schema does not declareenvironment.proof(permissive; validates).PREGATE_INCOMPLETE"ended without an exit code").MAX_RUN_ID_SIBLINGScounts directories tried, not siblings (naming).Gate edits: none detected (no gate finding in any pass; nothing kept, restored or open).
Outside review: Codex unavailable on this machine (not installed); native Claude adversarial passes only.
Adversarial Review
Pass 1 at 213ffe9: 9 findings. Fixed in f2a3246: a test in the diff could relabel its failure as environment by printing a network line and exiting before the summary (now vetoed when the runner was seen starting a file, and every output-inferred cause carries
proof: outputwith a fix that never clears the diff); the remote gate was classified from its whole output (now its first 40 lines); guard exit 127 was unconditionally environment (now only when the guard's own binary is off PATH); signal death read as missing-binary (nowPREGATE_INCOMPLETE); a reused--run <id>shared a scratch directory between concurrent runs (now a numbered sibling);engine.*was allowlistable; pattern entries were silently inert without a column. Accepted as-is: secret-category ids allowlistable by path scope alone (the policy is base-branch or user-owned, which a PR author cannot write), a cosmetic label, the absolute scratch path inpregate.json. Pass 2 at f2a3246 verified each fix; residuals, all advisory: a single-file or serial lane can still label itselfPREGATE_ENVIRONMENTwithproof: output(verify stays not-ok and the caveat prints); the relative-path veto edge above; the remote preamble is read from the last 4 MiB of a very large log; the tenth duplicate--runid falls back to a tmpdir under a "state root not writable" explain line; a guard killed by a signal reads as the diff's (pre-existing, safe direction). Recommendation: ship.Exploratory QA
Not run (CLI library change; the pregate fixture suites and the remote free tier are the behavioural evidence).
Design Review
No frontend files changed — design review skipped.
Eval Results
No prompt-related files changed — evals skipped.
Scope Drift
Scope Check: CLEAN against the Lane D spec; one addition requested by GStack Minder (the
literalsconsumersOffix for GSTA-114).Plan Completion
Lane D spec: 21 items done, 3 changed (the starter is written by the existing
gstack-pregate initverb rather than a new--writeflag; the two new result-code rows live inlib/result-codes-release.tsbecauselib/result-codes.tssits at the 800-line ratchet; the scratch module isrun-scratch.tsbecause the strays rule rejectsscratch*basenames), 1 unverifiable (GBrain practice 10, below).Deferred (not in this diff)
GBrain practice 10 (a
known-infra-flakesentry granting one named infra rerun) is a /ship + ready-validator concern, not a pregate.json one; noted for the ship lane.docs/state-root.md's project-bucket inventory should mentionpregate/runs/<run id>/(outside this lane; routed to GStack Minder).Verification Results
Pregate verify: ok, requires-remote cleared, current for tree a5e9fd18dd36 (remote gate 21503/0 at $0.04; the three CI lanes cleared with run 40356546176 (Free Tests) and 40356546444 (Windows Free Tests)). CI at f2a3246 saw one flaky red (
browse/test/terminal-agent-lifecycle.test.ts5 s timeout on the macOS runner, a file this branch does not touch; green ongh run rerun --failed, logged in the crew flaky ledger); CI at c3c96b3 green first time.TODOS
No TODO items completed in this PR.
Documentation
Status: current for c3c96b3 after two read-only audits (gsta112-docs-1 at 213ffe9, gsta112-docs-2 at f2a3246); every correction they named inside this lane is applied, the last one (the
PREGATE_INCOMPLETEanchor naming a signal-killed runner) in c3c96b3, a docs-only commit after audit 2, read back againstlib/pregate/checks/lanes.ts.Scope. 33 committed files against
main@5755601dac5d. Docs in the diff:docs/pregate.md(synopsis, stage-1guardsand stage-2lanesbullets, Publication list, #no-policy, #allowlist, #environment, #scratch),docs/troubleshooting.md(PREGATE_NO_POLICY,PREGATE_ENVIRONMENT,PREGATE_GUARD_FAILED,PREGATE_SECRETS,PREGATE_INCOMPLETE,PREGATE_NO_LANE),CHANGELOG.md[Unreleased],gstack-pregate --help. Verified againstlib/pregate/**: 64-entry allowlist cap;env.value,secretandengine.*refused at validation and kept by the matcher; anchored pattern with token boundary;inputs.allowlistdigest;environment.prooflaunch|output with its two fix tails and the(inferred from output)suffix; the binary-on-PATH veto; the remote gate's first-40-lines rule; signal death asPREGATE_INCOMPLETE; 0700 per-run scratch, 7-day prune, tmpdir fallback, numbered sibling for a taken--runid;initrefusing without--force; anchors registered in the result-code table and the doctor's map.Debt.
docs/state-root.mdproject-bucket inventory omitspregate/runs/<run id>/(outside this lane; routed to GStack Minder).docs/pregate.mdstage-1secretsbullet could cross-reference#allowlist;PREGATE_CONFIG_INVALIDdoes not list the new allowlist refusals (its messages are self-describing). No tutorial for the new surfaces, consistent with the rest of the pregate docs. No ARCHITECTURE diagram names pregate: no diagram drift.