mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 07:18:03 +01:00
v1.91.257.0 feat(pregate): clear --receipt accepts a remote_gate receipt from a stamp-only predecessor tree (gbrain gate-ahead) (#3486)
* feat(pregate): clear --receipt accepts a remote_gate receipt from a stamp-only predecessor tree (gbrain gate-ahead) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (hex object ids, stamp_sensitive_gates and base-branch refusals, one substitution type, docs) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(pregate): harden the stamp-only diff read (fixed presentation, diff-tree reconciliation), refuse an unreadable base policy, check the receipt before git work, bind verify to the receipt tree; docs audit corrections Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(pregate): the stamp-only suite expects the Linux-host lanes verdict; skip itself on win32 like test/pregate-remote.test.ts 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 commit is contained in:
1 parent
576839cb8c
commit
7f53ddb1f3
13 files changed
+612
-28
No files matched your search
+89
-1
@@ -357,7 +357,9 @@ the supervision above. Close the obligation one of two ways:
|
||||
|
||||
- after running it, `gstack-pregate clear --remote pregate.json/remote_gate
|
||||
--receipt <file>` with the gate's output, validated as `--remote` validates it
|
||||
(a run URL is refused, `PREGATE_RECEIPT_INVALID` for a bad receipt), or
|
||||
(a run URL is refused, `PREGATE_RECEIPT_INVALID` for a bad receipt; a receipt
|
||||
for a tree that differs from this one only by the repo's stamp rule is
|
||||
accepted and recorded, see [below](#remote-gate-stamp-only)), or
|
||||
`gstack-pregate clear --remote test_backend/<backend> --run-url <url>`, which
|
||||
has no machine-checkable receipt and is recorded as `attested:<url>`; or
|
||||
- `gstack-pregate --stage tests --remote --spend-cap <usd> --allow-repo-commands`,
|
||||
@@ -374,6 +376,92 @@ the supervision above. Close the obligation one of two ways:
|
||||
`spent, reserved, unknown`: a gate that prints no `spent=` is `unknown`,
|
||||
counted at the cap.
|
||||
|
||||
<a id="remote-gate-stamp-only"></a>
|
||||
### A receipt from a stamp-only predecessor tree (gate ahead of the stamp)
|
||||
|
||||
The receipt `clear --receipt` validates names the tree the gate tested, and
|
||||
pregate.json names the tree being published. A repo whose full gate runs before
|
||||
its turn in the queue ("gate ahead": the gate tests commit G, then /ship's
|
||||
restamp moves the version files, so the head H it publishes is G plus the
|
||||
stamp) could never clear the obligation with that run, and paid a second full
|
||||
gate on H for a diff that was nothing but stamps. `clear` now accepts that
|
||||
receipt for H when the two trees differ only as a stamp does:
|
||||
|
||||
- gstack diffs the gated tree against pregate.json's tree
|
||||
(`git diff --unified=0 <gated> <head>`, both trees as objects in this
|
||||
repository: the gated commit's tree once it is fetched, and the head tree
|
||||
`bin/gstack-wtree` wrote) and classifies every differing file with the tree
|
||||
receipt's own rule, `lib/tree-receipt.ts` under `.gstack/ship-policy.json` on
|
||||
`origin/<base>` ([ship-policy](ship-policy.md#tree-receipt)). That is the rule
|
||||
`gstack-tree-receipt`, `gstack-pregate regate` and the train's
|
||||
`verify --train` apply: with `stamp_files` set only those files can be
|
||||
stamps, each hunk in them must be the exact old → new rewrite under
|
||||
`stamp_patterns` (literal `{version}` templates) or the built-in forms
|
||||
(`(was X)`, `since: X`, `vX`), and the version file, `package.json`'s
|
||||
`version`, a lockfile's root version keys and a CHANGELOG re-heading have
|
||||
their own built-in rules. There is no second list and no looser rule here:
|
||||
the file verdicts `clear` reaches on a pair are the verdicts
|
||||
`gstack-tree-receipt --gated G --current H` reaches, and its gate rule holds
|
||||
too: a policy whose `stamp_sensitive_gates` names the test lane (`tests`, as
|
||||
gstack's own does because its tests read VERSION and the agents digest) is
|
||||
refused outright, since the tree receipt never marks such a lane reusable.
|
||||
The policy is the one on this repository's base branch (`origin/HEAD`, as
|
||||
`regate` reads it) at its current tip, never a branch pregate.json names: a
|
||||
pregate.json recorded against another base is refused. What the receipt does
|
||||
not compare, exactly as a same-tree receipt does not, is the gate's execution
|
||||
identity (Bun, OS, lockfiles); that stays the evidence bundle's job.
|
||||
- Accepted, the obligation's receipt reads `remote-gate tree=<gated>
|
||||
tests=<n> accepted-for=<head> stamp-only=<file count> receipt=<file>
|
||||
sha256=<digest>`, and pregate.json records the substitution beside it:
|
||||
`substitution: { gated_tree, accepted_for, policy, files: [{ path, rule }] }`,
|
||||
the policy label (`origin/<base>@<sha>:.gstack/ship-policy.json`) and every
|
||||
differing path with the rule that classed it (`version-file`,
|
||||
`version-manifest`, `manifest-version`, `lockfile-root-version`,
|
||||
`changelog-heading`, `stamp_files`, `stamp_paths`, `release_outputs`). `clear` prints the same
|
||||
as `PREGATE_RECEIPT_SUBSTITUTED: …`, `verify` repeats it on its ok line, and
|
||||
the ship receipt carries the receipt string. The receipt's own tests and
|
||||
failed checks apply as before (its exit status is checked only when
|
||||
`--remote` runs the gate itself), and `verify` refuses a recorded
|
||||
substitution whose `gated_tree` is not the receipt string's `tree=`.
|
||||
- Refused, with `PREGATE_RECEIPT_INVALID` and a fix naming the offending
|
||||
path: any differing path outside the rule (`lib/x.ts (stamp_files: not in the
|
||||
repo's stamp_files allowlist, line 1)`), a stamp path whose hunk is not the
|
||||
stamp rewrite (`plugin.json (stamp_files: "…description…" -> "…", line 4)`),
|
||||
a gated tree that is not a 40- or 64-hex object id or not an object here
|
||||
(fetch the gated commit first), a base branch whose policy cannot be read
|
||||
or is invalid, a pregate.json recorded with `--base` against another branch
|
||||
than this repository's base (`origin/HEAD`), a policy whose
|
||||
`stamp_sensitive_gates` names the test lane (`tests` or `remote_gate`;
|
||||
gstack's own policy does, so gstack itself never substitutes), or a patch
|
||||
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
|
||||
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
|
||||
the gate on the head, or make the extra change a stamp under the repo's
|
||||
rule.
|
||||
|
||||
A consuming repo declares its stamp paths once, in `.gstack/ship-policy.json`
|
||||
on its base branch (`stamp_files`, `stamp_patterns`, `changelog`), the same
|
||||
file the tree receipt and restamp already read; nothing is declared in
|
||||
`.gstack/pregate.json` for this. gbrain's is in
|
||||
[gbrain worked configs](#gbrain-worked-configs).
|
||||
|
||||
The gate must have run on a clean, committed checkout: `PREGATE_TREE` is the
|
||||
`bin/gstack-wtree` fingerprint, which equals the commit's tree only when nothing
|
||||
was uncommitted or untracked there, and `clear` can compare only trees that are
|
||||
objects in this repository. A receipt whose tree is not here is refused, never
|
||||
guessed. The sequence that reuses a gate-ahead run, on a machine under 8 cores:
|
||||
|
||||
```bash
|
||||
git fetch origin <gated sha> # the gated commit's tree must be here
|
||||
gstack-pregate --allow-repo-commands # on the stamped head: records pregate.json/remote_gate uncleared
|
||||
gstack-pregate clear --remote pregate.json/remote_gate --receipt <the gate's output for the gated tree>
|
||||
gstack-pregate verify # PREGATE_VERIFY: ok …; pregate.json/remote_gate: gated tree … accepted for tree …
|
||||
```
|
||||
|
||||
gstack's own example: `.gstack/pregate.json` declares
|
||||
`"remote_gate": "bash scripts/ubicloud/pregate-remote.sh"`. With
|
||||
`UBICLOUD_API_KEY` set the script runs the selected files on one Ubicloud VM
|
||||
|
||||
+35
-4
@@ -3122,9 +3122,32 @@ reports failures. The obligation stays uncleared. A run URL alone never clears
|
||||
this obligation; a `test_backend/<backend>` obligation takes a run URL, recorded
|
||||
as attested rather than validated.
|
||||
|
||||
Another tree is accepted in exactly one case: it differs from `pregate.json`'s
|
||||
tree only in the repo's stamp rule (`.gstack/ship-policy.json` on the base
|
||||
branch: `stamp_files`, `stamp_patterns`, the built-in version, manifest,
|
||||
lockfile and CHANGELOG forms), judged by the tree receipt's own classifier on a
|
||||
`git diff` of the two trees ([the gate ran ahead of the
|
||||
stamp](pregate.md#remote-gate-stamp-only)). The refusal line then says `and the
|
||||
trees differ beyond the stamp rule of <policy>: <path> (<rule>: <why>, line
|
||||
<n>)`, and the path it names is the one that is not a stamp: a file outside
|
||||
`stamp_files`, or a stamp file with a hunk that is not the old → new rewrite.
|
||||
`the gated tree <t> is not an object in this repository` means the gated
|
||||
commit is not fetched here, so the two trees cannot be compared. Three more
|
||||
refusals close the substitution without classifying anything: `lists the test
|
||||
lane as stamp-sensitive (stamp_sensitive_gates: tests)` (the base policy says
|
||||
the test tier reads the stamps, as gstack's own does, so no run on another tree
|
||||
covers this one; rerun the gate on the head), `pregate.json was recorded against
|
||||
origin/<a>, not this repository's base origin/<b>` (rerun `gstack-pregate --base
|
||||
<b>`, or `git remote set-head origin <a>` when `origin/HEAD` was unset), and
|
||||
`.gstack/ship-policy.json on origin/<base>@<sha> is invalid` or `could not be
|
||||
read` (fix the policy on the base, or fetch it).
|
||||
|
||||
**Fix.** Run the remote gate on the tree pregate tested and pass its output
|
||||
file with `--receipt`, or let `gstack-pregate --stage tests --remote --spend-cap
|
||||
<usd> --allow-repo-commands` run and validate it.
|
||||
<usd> --allow-repo-commands` run and validate it. For a gate-ahead receipt:
|
||||
fetch the gated commit (`git fetch origin <sha>`) and clear again, or make the
|
||||
named path a stamp under the repo's rule; a receipt is never accepted while any
|
||||
differing path is a change.
|
||||
|
||||
<a id="pregate-no-policy"></a>
|
||||
### `PREGATE_NO_POLICY`
|
||||
@@ -3190,12 +3213,20 @@ refuses to write the ship receipt otherwise.
|
||||
### `PREGATE_REMOTE_UNCLEARED`
|
||||
|
||||
**Meaning.** A `requires-remote` obligation (a platform lane this machine cannot
|
||||
run, named by `workflow.yml/job`) has no receipt.
|
||||
run, named by `workflow.yml/job`, or the small-machine `pregate.json/remote_gate`
|
||||
tier) has no receipt; or a `remote_gate` receipt accepted from a stamp-only
|
||||
predecessor tree records `accepted_for` as a tree other than `pregate.json`'s,
|
||||
or a `gated_tree` other than its receipt string's `tree=` (the file was edited
|
||||
after `clear`), so it clears nothing here.
|
||||
|
||||
**Fix.** Dispatch that job, then
|
||||
`gstack-pregate clear --remote <workflow.yml/job> --run-url <url>` (or
|
||||
`--bundle-lane <label>` from an evidence bundle). The ship receipt carries each
|
||||
obligation with its receipt.
|
||||
`--bundle-lane <label>` from an evidence bundle). For `pregate.json/remote_gate`,
|
||||
`clear --remote pregate.json/remote_gate --receipt <gate output>`: the gate's
|
||||
own run on this tree, or a run on a predecessor tree that differs only by the
|
||||
repo's stamp rule ([gate ahead of the stamp](pregate.md#remote-gate-stamp-only));
|
||||
`verify` then prints the accepted substitution on its ok line. The ship receipt
|
||||
carries each obligation with its receipt.
|
||||
|
||||
<a id="issue-unreadable"></a>
|
||||
### `ISSUE_UNREADABLE`
|
||||
|
||||
Reference in new issue
Block a user