Repository navigation
v1.91.306.0 ship-receipt: carry across regenerated outputs and onto a reviewed tree after a rebase - #3564
Merged
Conversation
…wed 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 pull request was merged. |
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
The regen check deletes each touched output in its scratch worktree before the command runs and records the ones recreated byte-identically as proven. gstack-ship-receipt carry exempts only those paths, and only when the check ran origin/<base>'s registry at the commit the carry reads. A file under an output glob that the generator never writes stays product (pre-landing review, #3564 pass 1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
4 tasks done
The regen verdict comes from the unmodified run again, so a generator that reads its own registered output (gen-skill-docs and qa/templates/functional-report-template.md) passes as before; the proof is a second run with the touched outputs deleted, and its failure only means not proven. The carry ignores a proven path with glob syntax, since it uses the paths as pathspec globs (pre-landing review, #3564 pass 2). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…x line Documentation audit for #3564 (/document-release, audit ship3564-docs-1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
5 tasks done
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
capy-ai Bot
added a commit
that referenced
this pull request
Oct 29, 2026
…ith depth-checked receipts (#3567) * 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> * fix: regenerate doctor anchors; type the head_tree assert Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(review): proportional /ship review depth (light, standard, full) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: carry sets aside only outputs the regen check proved it recreates The regen check deletes each touched output in its scratch worktree before the command runs and records the ones recreated byte-identically as proven. gstack-ship-receipt carry exempts only those paths, and only when the check ran origin/<base>'s registry at the commit the carry reads. A file under an output glob that the generator never writes stays product (pre-landing review, #3564 pass 1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship-receipt): record review depth, check it in the queue, structured review-loop exception (#3559) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: prove generated outputs in a second regen run; literal names only The regen verdict comes from the unmodified run again, so a generator that reads its own registered output (gen-skill-docs and qa/templates/functional-report-template.md) passes as before; the proof is a second run with the touched outputs deleted, and its failure only means not proven. The carry ignores a proven path with glob syntax, since it uses the paths as pathspec globs (pre-landing review, #3564 pass 2). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: receipt evidence keys and carry record fields; carry refusal fix line Documentation audit for #3564 (/document-release, audit ship3564-docs-1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (depth scan binding, queue scope, carry checks) - carry refuses a --head other than the checked-out HEAD, a receipt with gstack_version but no review_depth, and an owner exception that no longer stands; a dirty or foreign checkout makes the write-time gate scan unavailable (full) - the queue binds a gate scan only to the full bound commit and recomputes the scope signals with gstack-diff-scope <base> --head <sha>, the same range the writer uses - the queue checks the review-loop exception's fix-check ancestry - a package.json version line counts as stamp-only only when it is a bare version key - docs and help name what review_depth is and which registry the queue reads Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG for the review fixes; defer two plan items to TODOS Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: queue and carry refusal lists name the depth and exception codes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: approval-classes summary-line regexes allow the receipt's depth field Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship-receipt): summary line prints depth before approval Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Revert "test: approval-classes summary-line regexes allow the receipt's depth field" This reverts commit e9ec264. --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
capy-ai Bot
added a commit
that referenced
this pull request
Oct 29, 2026
#3572) * 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> * fix: regenerate doctor anchors; type the head_tree assert Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(review): proportional /ship review depth (light, standard, full) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: carry sets aside only outputs the regen check proved it recreates The regen check deletes each touched output in its scratch worktree before the command runs and records the ones recreated byte-identically as proven. gstack-ship-receipt carry exempts only those paths, and only when the check ran origin/<base>'s registry at the commit the carry reads. A file under an output glob that the generator never writes stays product (pre-landing review, #3564 pass 1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship-receipt): record review depth, check it in the queue, structured review-loop exception (#3559) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: prove generated outputs in a second regen run; literal names only The regen verdict comes from the unmodified run again, so a generator that reads its own registered output (gen-skill-docs and qa/templates/functional-report-template.md) passes as before; the proof is a second run with the touched outputs deleted, and its failure only means not proven. The carry ignores a proven path with glob syntax, since it uses the paths as pathspec globs (pre-landing review, #3564 pass 2). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: receipt evidence keys and carry record fields; carry refusal fix line Documentation audit for #3564 (/document-release, audit ship3564-docs-1). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (depth scan binding, queue scope, carry checks) - carry refuses a --head other than the checked-out HEAD, a receipt with gstack_version but no review_depth, and an owner exception that no longer stands; a dirty or foreign checkout makes the write-time gate scan unavailable (full) - the queue binds a gate scan only to the full bound commit and recomputes the scope signals with gstack-diff-scope <base> --head <sha>, the same range the writer uses - the queue checks the review-loop exception's fix-check ancestry - a package.json version line counts as stamp-only only when it is a bare version key - docs and help name what review_depth is and which registry the queue reads Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG for the review fixes; defer two plan items to TODOS Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(ship-receipt): carry a stacked child after its parent lands (#3570) When the carry base differs from the receipt's (a stack parent landed and the PR was retargeted), the receipt carries only when HEAD's product diff against the base equals the range reviewed against the parent branch. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: queue and carry refusal lists name the depth and exception codes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes A retargeted carry compared the reviewed range with a patch-id whose pathspec came from the reviewed head..HEAD range, so a parent's package.json change that never reached the base dropped out and the receipt carried. Compare with HEAD's whole product patch-id against the base (the one write records), rebind the receipt's base on a retarget carry, and cover the parent-metadata, landed- with-changes and test-only-child cases. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: approval-classes summary-line regexes allow the receipt's depth field Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: troubleshooting names the stacked-retarget carry refusals Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ship-receipt): summary line prints depth before approval Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * Revert "test: approval-classes summary-line regexes allow the receipt's depth field" This reverts commit e9ec264. * test: assert a retarget carry rebinds review_depth to the new base and binds at the queue; docs: split the --head carry advice 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
docs/ship-policy.md's receipt-carry section now opens with "merge main, never rebase" and a four-row table of what happens to a receipt when the base moves, because the carry rules below it changed.
Before/after as text: Receipt carry: merge main and keep the receipt when only proven regenerated outputs changed; a rebase carries only onto a reviewed tree with the reviewed product diff
gstack-ship-receipt carrytreats an output of an entry with acommandin origin/'s.gstack/generated.jsonas generated, not product, only when the--pregateresult for HEAD lists that path under theregencheck'sprovenpaths and the check ran the same registry as origin/. To prove a path, the regen check deletes it in a scratch worktree, reruns the command in a second run apart from the verdict, and requires a byte-identical rewrite. A path matching an output glob that the command never writes stays product, as do head-only registry entries,command: nullentries, vendored code and lockfiles.writerecordshead_treeandproduct_patch_id. A head that is no longer a descendant of the receipt's head carries only when its tree is one the receipt covers and its product patch-id against the current base matches. It recordscarry.ancestry: rewrittenand needs no old commit, so a fresh clone can carry. A different tree refuses with the newRECEIPT_CARRY_TREE_MISMATCH; the reviewed tree on a base that moved under it refuses withRECEIPT_CARRY_PRODUCT_CHANGED. A dirty tree refuses before any of this.Why
On gstack-evals, two full /ship reruns came from a merge of main that only changed a generated file's diff, and one came from a rebase whose tree was byte-identical to the reviewed one. Each rerun re-reviewed code the receipt already covered, so a lane paid for a second review loop on identical product code. Now a merge that only touches proven regenerated outputs, or a rebase back onto a reviewed tree, keeps the receipt. This is PR 1 of the GSTA-147 plan (approved by Garry, gate_rev 2, reply "rec"); PR 2 stacks proportional review depth on it.
Blast radius
The change touches receipt carry and the regen pregate check.
writeadds two keys to new receipts, and old receipts without them carry through the existing patch-id path but refuse a rewritten head because they record no tree. The regen check's verdict is unchanged. Its newprovenlist comes from a second run in the scratch worktree, and a failed proof proves nothing (test/gstack-regen.test.ts covers the self-reading generator at scripts/gen-skill-docs.ts:1038, script ran). Review, queue and pregate behaviour are otherwise unchanged.Approvals
Source: gstack-approvals record for gsta147-carry-rules (display only; not an approval citation).
Summary
Lanes kept paying for a full /ship after a base merge or a rebase that left the reviewed code alone. The receipt now carries across both when the evidence shows the product diff is unchanged. A regenerated output counts as generated only when the pregate proved the generator writes it, and a rebase carries only onto a tree the receipt already covers. Review found a gate bypass in the first version (any output glob was exempt once regen said fresh), so the proof step and the registry check were added before shipping.
Test Coverage
New tests: test/ship-receipt-carry-rewrite.test.ts (rebase onto a covered tree from a fresh clone, rebase onto a different tree, the reviewed tree on a moved base, and a base merge that changes a registered output with and without a proving pregate, including a tampered pregate), and a test/gstack-regen.test.ts case for a self-reading generator. The rebase and registered-output cases fail on e97b857 and pass after the fixes. Known gaps are listed under Review notes (legacy receipts on a rebase, the "no registered input or output touched" negative case, eight dry-run state cells).
Tests ran: focused 496
Pre-Landing Review
Three blocking findings, all fixed:
bin/gstack-ship-receipt:396gate bypass:verifiedOutputsexempted every base-registry output glob once regen said fresh. Found by the red team, the native adversarial pass and the GPT outside voice, and confirmed by state-machines. Fixed in f47aba6: regen proves each touched output, and the carry checks the registry label.lib/regen.ts:248the prove step deleted a source that scripts/gen-skill-docs.ts:1038 reads (its own output). Fixed in bf9a417: the proof is a second run apart from the verdict, and a failed proof proves nothing.bin/gstack-ship-receipt:401proven names were used as pathspec globs. Fixed in bf9a417: names containing* ? [ ]or a backslash are dropped.REVIEW_LOOP: passes=2 blocking_found=3 deferred=0 noted=10 fixed=6 scope=frozen fix_checks=1 dispatches=10
Gate edits: 2 listed, 1 read, 0 findings — 0 kept, 0 restored, 0 open
Outside review: openai/gpt-6.1-sol on pass 1, verdict: one blocking finding (the gate bypass above), fixed.
Review notes
Below the blocking floor (advisory, informational, test-gap or wording items); recorded here and not fixed in this PR.
lib/ship-receipt.ts:201classifyCarryPath docstring omits extra.outputs / 'generated'bin/gstack-ship-receipt:522no test for legacy receipts without head_tree/product_patch_id on a rebasebin/gstack-ship-receipt:396no negative test for regen pass 'no registered input or output touched'test/ship-receipt-carry-rewrite.test.ts:115registered-output test ignores the merge exit statusbin/gstack-ship-receipt:394/ship Step 1.4 dry-run without --pregate cannot reach the generated carry (fails closed)bin/gstack-ship-receipt8 untested state-matrix cells (dry-run variants)bin/gstack-ship-receipt:146write and a rebase carry compute product_patch_id under different regen evidence or stamp demotion, so an exact tree can be refused RECEIPT_CARRY_PRODUCT_CHANGED (fails closed)bin/gstack-ship-receipt:504covered trees trust receipt.head_tree from the PR body without cross-checking from^{tree}bin/gstack-ship-receipt:507carry --base override lets the caller pick the base for both patch-ids (predates this PR)lib/regen.ts:254a verdict-run command failure skips the scratch reset, so later entries in the same check can print misleading lines (the verdict is already REGEN_COMMAND_FAILED; predates this PR)Exploratory QA
Not run as a separate charter: the surface is a CLI exercised end to end by the carry tests above (real git repos, a fresh clone, real merges and rebases).
Design Review
No frontend files changed — design review skipped.
Eval Results
No prompt-related files changed — evals skipped.
Greptile Review
No Greptile comments.
Plan Completion
GSTA-147 PR 1 (receipt carry): both items delivered, carry across proven regenerated outputs and carry across rewritten history. Proportional review depth is PR 2.
Issues
Refs #3564
Verification Results
3 PASS, 0 FAIL, 0 BLOCKED, 0 NOT RUN: a rebase onto a covered tree carries from a fresh clone; any other tree refuses; a registered output carries only with a proving pregate from the same registry.
TODOS
No TODO items completed in this PR.
Documentation
Status: updated. Four factual corrections in three docs about receipt carry; no blockers.
Scope: the committed diff of gsta147-carry-rules against 4c745ee, head bf9a417 (17 files; nothing staged, unstaged or untracked). Docs audited: docs/ship-policy.md (receipt and receipt-carry sections), docs/troubleshooting.md (carry entries), docs/unattended.md, docs/captain.md, docs/pregate.md, captain/templates/CREW.md, ship/SKILL.md.tmpl (Step 1 carry check). CHANGELOG.md was read and not edited.
Documentation health
head_treeandproduct_patch_id; thecarryrecord listsancestryandtreesprovenbullet matches lib/regen.tsCoverage debt: the carry's
generatedclass, the regenprovenfield,head_tree/product_patch_idand RECEIPT_CARRY_TREE_MISMATCH have reference and explanation coverage (ship-policy.md, pregate.md, troubleshooting.md), and the delta /ship sequence covers the how-to. No tutorial walks through recovering a receipt after a rebase (common gap). ship/SKILL.md.tmpl Step 1.4 and two section headings still describe only the merge and test-only cases.Diagram drift: none; no diagrams reference the changed modules.
Test plan
gstack-evidence --label focused3, 14 files: receipt, carry-rewrite, regen, pregate, result codes, docs anchors): 496 pass, 0 failbun run typecheck: 0 errors;bun run typecheck:test: no new diagnosticsgstack-pregate --base main --stage tests --remote --spend-cap 2) at 3b90122, tree 038eb3e4c86e: 23351 tests, 0 failed, $0.08🤖 Generated with Claude Code