mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 15:04:37 +01:00
feat(ship-receipt): carry across regenerated outputs and onto a reviewed tree after a rebase
A base merge whose only product-side change is a registered output the pregate regen check proved fresh now carries the receipt. A rebased head carries when its tree is one the receipt covers and its product patch-id against the current base matches (RECEIPT_CARRY_TREE_MISMATCH otherwise). Docs: merge main into a reviewed branch, never rebase it. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
4c745eec10
commit
9fe6c01835
12 files changed
+412
-97
No files matched your search
@@ -231,6 +231,14 @@ a push included. The sequence that works unattended on Capy:
|
||||
10. After the slot call, the stamp commit keeps the pin's receipt; re-READY with the same
|
||||
receipt, no second /ship.
|
||||
|
||||
When the base moves under a reviewed branch, merge it in; never rebase. A merge that
|
||||
leaves the product diff unchanged carries the receipt with `gstack-ship-receipt carry`
|
||||
(pregate at HEAD, no review rerun), and a merge whose resolution changed product code
|
||||
needs a fresh /ship. A rebase carries only onto a tree the receipt already covers with
|
||||
the same product diff; any other rebase is a full /ship
|
||||
([ship-policy.md](ship-policy.md#receipt-carry)). A controller never asks a lane to
|
||||
rebase a reviewed branch.
|
||||
|
||||
## Merge trains
|
||||
|
||||
gstack's queue tool drives trains when the repository's `.gstack/ship-policy.json` carries
|
||||
|
||||
+36
-6
@@ -891,6 +891,16 @@ line, and the codes a refusal ends with are
|
||||
<a id="receipt-carry"></a>
|
||||
## Carrying a receipt across a base merge or a test-only fix
|
||||
|
||||
**Merge main into a reviewed branch; never rebase it.** What happens to the
|
||||
receipt when the base moves:
|
||||
|
||||
| What changed since /ship | The receipt |
|
||||
|---|---|
|
||||
| a merge of the base, product diff unchanged (tests, release metadata, registered outputs the pregate regenerated fresh) | carries; no review reruns |
|
||||
| a merge of the base whose resolution changed product code | a fresh /ship (`RECEIPT_CARRY_PRODUCT_CHANGED`) |
|
||||
| a rebase or force-push onto a tree the receipt covers, product diff against the new base unchanged | carries (`carry.ancestry: rewritten`) |
|
||||
| any other rebase or force-push | a fresh /ship (`RECEIPT_CARRY_TREE_MISMATCH`) |
|
||||
|
||||
Since the release that added `review_scope`. A `/ship` receipt names the head it
|
||||
reviewed, so a conflict-only merge of the base or a test-only fix used to
|
||||
invalidate it and cost a full re-run on identical product code.
|
||||
@@ -921,8 +931,13 @@ newer tentative one the stamp moved (a `*_KEY_VERSION` written as the next
|
||||
minor, #3512; a version older than the old one or of another width is a hand
|
||||
edit). A logic change in a
|
||||
stamped source file is product. Lockfiles are product unless the policy names
|
||||
them. A base merge contributes no product
|
||||
change of its own. A receipt carried twice keeps the reviewed head as
|
||||
them. An output of an entry with a `command` in origin/<base>'s
|
||||
`.gstack/generated.json` is `generated`, not product, only when the
|
||||
`--pregate` result passed for HEAD shows the `regen` check passing with those
|
||||
entries fresh; without that evidence, or for an entry with no command or one
|
||||
only the branch registers, the output is product (U1 on gstack-evals paid a
|
||||
full rerun because a merge changed its generated index's diff). A base merge
|
||||
contributes no product change of its own. A receipt carried twice keeps the reviewed head as
|
||||
`carry.from` and adds up the merges and paths. The carried receipt records
|
||||
`carry: { from, reason, product_patch_id, changed_paths, merge_commits,
|
||||
range_diff, covers, reruns }` with the `git range-diff` evidence, prints
|
||||
@@ -933,10 +948,25 @@ skill=ship status=complete` line, and its summary line ends with
|
||||
|
||||
A changed product path, or a product patch-id that differs, is
|
||||
[`RECEIPT_CARRY_PRODUCT_CHANGED`](troubleshooting.md#receipt-carry-product-changed)
|
||||
(exit 3; the refusal names the paths), and a receipt whose head is not in HEAD's
|
||||
history (a rebase or force-push) or a working tree with uncommitted changes is
|
||||
[`RECEIPT_CARRY_REFUSED`](troubleshooting.md#receipt-carry-refused): run `/ship`
|
||||
on the new head. The queue's `ready`, `train build` and `verify --train` read the
|
||||
(exit 3; the refusal names the paths), and a working tree with uncommitted
|
||||
changes is [`RECEIPT_CARRY_REFUSED`](troubleshooting.md#receipt-carry-refused).
|
||||
|
||||
**Rewritten history.** `write` records `head_tree` (the reviewed head's tree)
|
||||
and `product_patch_id` (its product patch against the base), and every carry
|
||||
rebinds both to the new head and keeps the earlier trees in `carry.trees`.
|
||||
When the receipt's head is no longer in HEAD's history, the carry follows only
|
||||
when HEAD's tree is one of those trees and HEAD's product patch-id against the
|
||||
current base equals the recorded one; it records `carry.ancestry: rewritten`
|
||||
and `range_diff: tree-equal`, and needs neither the old commit nor its
|
||||
history, so a fresh clone carries too. The tree alone is not enough: a stacked
|
||||
branch rebased onto main after its base closed unmerged keeps its tree while
|
||||
its diff grows by the base's code, and that is `RECEIPT_CARRY_PRODUCT_CHANGED`.
|
||||
Any other tree is
|
||||
[`RECEIPT_CARRY_TREE_MISMATCH`](troubleshooting.md#receipt-carry-tree-mismatch),
|
||||
which names HEAD's tree and the covered trees. A receipt from before
|
||||
`head_tree` existed, whose head is not in this clone, is
|
||||
`RECEIPT_CARRY_REFUSED`: run `/ship` on the new head. A refusal never
|
||||
rewrites the body. The queue's `ready`, `train build` and `verify --train` read the
|
||||
carried receipt like any other.
|
||||
|
||||
**Delta /ship: eligibility, then checks, then publication.** `/ship` Step 1 runs
|
||||
|
||||
+30
-13
@@ -4694,26 +4694,43 @@ rebased; /ship's own rule is to merge the base in, never rebase a reviewed head.
|
||||
### `RECEIPT_CARRY_REFUSED`
|
||||
|
||||
**Meaning.** `gstack-ship-receipt carry` could not carry the PR's /ship receipt
|
||||
to HEAD: the PR body has no valid receipt, the receipt's head is not an ancestor
|
||||
of HEAD (a rebase or force-push dropped it), the working tree has uncommitted
|
||||
or untracked changes (a carry certifies a commit, never a tree that differs from
|
||||
it), or git could not compare the two product diffs or read the working tree's
|
||||
status. The receipt in the body is unchanged and still names the old head, so
|
||||
`gstack-ship-queue ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
to HEAD: the PR body has no valid receipt, the working tree has uncommitted or
|
||||
untracked changes (a carry certifies a commit, never a tree that differs from
|
||||
it), the receipt's head left HEAD's history and the receipt records no tree to
|
||||
compare (a receipt written before `head_tree`, whose head is not in this
|
||||
clone), or git could not compare the two product diffs or read the working
|
||||
tree's status. The receipt in the body is unchanged and still names the old
|
||||
head, so `gstack-ship-queue ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
|
||||
**Fix.** Keep the reviewed head in HEAD's history (merge the base in instead of
|
||||
rebasing), commit or stash every working-tree change, then rerun
|
||||
**Fix.** Commit or stash every working-tree change, then rerun
|
||||
`gstack-ship-receipt carry --pr <n>` at the new head. When the history is
|
||||
already rewritten, run /ship on the new head.
|
||||
already rewritten and the receipt predates `head_tree`, run /ship on the new
|
||||
head; next time merge the base in instead of rebasing.
|
||||
|
||||
<a id="receipt-carry-tree-mismatch"></a>
|
||||
### `RECEIPT_CARRY_TREE_MISMATCH`
|
||||
|
||||
**Meaning.** The branch was rebased or force-pushed after /ship, so the
|
||||
receipt's head is no longer in HEAD's history, and HEAD's tree is not a tree
|
||||
the receipt covers (its `head_tree` or a tree in `carry.trees`). No review saw
|
||||
this code. The refusal names HEAD's tree and the covered trees; the body is
|
||||
unchanged.
|
||||
|
||||
**Fix.** Run /ship on the new head. A rebase carries only onto a reviewed tree
|
||||
whose product diff against the new base is unchanged; merging the base in
|
||||
instead keeps every later carry available
|
||||
([ship-policy.md](ship-policy.md#receipt-carry)).
|
||||
|
||||
<a id="receipt-carry-product-changed"></a>
|
||||
### `RECEIPT_CARRY_PRODUCT_CHANGED`
|
||||
|
||||
**Meaning.** Between the receipt's head and HEAD the product diff against the
|
||||
base changed: at least one path that is neither a test, release metadata nor
|
||||
part of the base merge has a different patch than the reviewed head had. The
|
||||
reviewed verdict does not cover HEAD, so the receipt is not carried. The
|
||||
refusal names the changed product paths.
|
||||
base changed: at least one path that is neither a test, release metadata, a
|
||||
registered output the pregate regenerated fresh, nor part of the base merge has
|
||||
a different patch than the reviewed head had. After a rebase it means HEAD has
|
||||
the reviewed tree but the base moved under it, so the diff a reviewer would see
|
||||
is not the reviewed one. The reviewed verdict does not cover HEAD, so the
|
||||
receipt is not carried. The refusal names the changed product paths.
|
||||
|
||||
**Fix.** Run /ship on the new head. The receipt carries only across a
|
||||
conflict-only base merge, a test-only change or release metadata, which the
|
||||
|
||||
+3
-2
@@ -226,8 +226,9 @@ now names HEAD with a `carry` object holding the `git range-diff` evidence, and
|
||||
`ship-run.json` gains `carried_from` and `carry_reason`; the `review_loop`
|
||||
counters are kept, because the reviewed product diff is unchanged. `CARRY:
|
||||
current` means nothing moved. `CARRY: refused (RECEIPT_CARRY_PRODUCT_CHANGED)`
|
||||
or `(RECEIPT_CARRY_REFUSED)` with the refused result line means the product
|
||||
diff changed or the reviewed head left HEAD's history, so /ship runs again;
|
||||
, `(RECEIPT_CARRY_TREE_MISMATCH)` or `(RECEIPT_CARRY_REFUSED)` with the refused
|
||||
result line means the product diff changed, or history was rewritten onto a
|
||||
tree no review saw, so /ship runs again;
|
||||
`(RECEIPT_GIT_TOO_OLD)` means the machine's git predates 2.39 and cannot run
|
||||
`patch-id --verbatim` ([ship-policy.md](ship-policy.md#receipt-carry)).
|
||||
|
||||
|
||||
Reference in new issue
Block a user