mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-13 03:48:06 +01:00
feat(restamp): the repo's stamp rule (stamp_files, literal stamp_patterns) and restamp_command in the staging worktree
stamp_files is an allowlist applied before every stamp rule; stamp_patterns
are bounded literal {version} templates shared by the restamp writer and the
tree receipt. restamp_command runs first in the staging worktree with the
target version (argument and GSTACK_RESTAMP_VERSION), needs a clean caller
tree, skips gstack's version writer and keeps output confinement; a failure
or a wrong version writes nothing. migrations { dir, id } is the policy key
gstack-pregate regate reads.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
05e6f2e828
commit
c47fd042d9
6 files changed
+298
-26
No files matched your search
+31
-3
@@ -43,6 +43,10 @@ prints this table. JSON carries no comments, so examples live in
|
||||
| `stamp_sensitive_gates` | evidence labels | `[]` | Gates that read VERSION or generated bytes (gstack's own `tests` lane does, through the agents-digest freshness test). The tree receipt never marks them reusable. |
|
||||
| `pr_count` | `{ default_pr_count, split_requires_owner }` | `{1, true}` | The PR-count row in plan reviews. |
|
||||
| `maintainers` | GitHub logins | `[]` | Maintainer set for `/review` contributor mode; empty means repository push permission. |
|
||||
| `stamp_files` | globs | `[]` | The repo's stamp rule: when set, only these files may differ by a stamp (list the version file, manifests, plugin trees and the files holding the PR's own `since:` rows). It applies before every other stamp rule, so a doc `vX` mention outside it is a difference. |
|
||||
| `stamp_patterns` | literal templates | `[]` | At most 16 templates of at most 128 characters, each with exactly one `{version}` slot (`"version": "{version}"`, `since: '{version}'`, `<!-- x-stamp: {version} -->`); matched literally at a version boundary, never as a regex. The restamp writer and the tree receipt use them beside the built-in forms. |
|
||||
| `restamp_command` | command or `null` | `null` | The repo's own restamp, run first in the staging worktree; see [restamp_command](#restamp-command). |
|
||||
| `migrations` | `{ dir, id }` or `null` | `null` | The migration directory and a regex with exactly one capture group for the number in a file name (`"^v(\\d+)-"`); `gstack-pregate regate` reports `MIGRATION_COLLISION`. |
|
||||
|
||||
Containment, checked by `validate` and again at write time: paths and globs are
|
||||
repo-relative with no `..` segment, under 256 characters, at most 64 per list,
|
||||
@@ -161,10 +165,11 @@ or the order moved, the result is `QUEUE_STALE` (exit 3) with the exact
|
||||
|
||||
Stages, each journaled in `.gstack/tmp/restamp-journal.json`:
|
||||
|
||||
1. **version**: `gstack-version-bump write --version <X>` in the staging worktree (VERSION, the manifest, npm lockfiles).
|
||||
0. **restamp_command** (only when the policy sets it): the repo's own restamp, see [restamp_command](#restamp-command).
|
||||
1. **version**: `gstack-version-bump write --version <X>` in the staging worktree (VERSION, the manifest, npm lockfiles); skipped when `restamp_command` owns the version.
|
||||
2. **release_tool**: the policy command, under the contract below.
|
||||
3. **changelog**: the PR's `[Unreleased]` or old-version section moves to the top under `<X>`, above its predecessor's entry. Duplicate headings (`CHANGELOG_DUPLICATE_HEADING`) and a top entry that is not `<X>` (`CHANGELOG_TOP_MISMATCH`) are errors.
|
||||
4. **stamp_paths**: exact `(was X)`, `since: X` and `vX` rewrites within the declared globs. `X` is the branch's own tentative stamp (its VERSION when it moved past base), else `--was <X>`, else the version an unstamped branch would have taken next (`base + bump`); mentions of a version the base never released (not a CHANGELOG heading at base, not a tag) are rewritten too, so a queue reorder that moves the base under a branch still finds its `since:` fields. Only the repo's own version width counts, so a 3-digit dependency pin in a 4-digit repo is never a candidate.
|
||||
4. **stamp_paths**: exact `(was X)`, `since: X` and `vX` rewrites, plus every `stamp_patterns` template, within `stamp_paths` and `stamp_files`. `X` is the branch's own tentative stamp (its VERSION when it moved past base), else `--was <X>`, else the version an unstamped branch would have taken next (`base + bump`); mentions of a version the base never released (not a CHANGELOG heading at base, not a tag) are rewritten too, so a queue reorder that moves the base under a branch still finds its `since:` fields. Only the repo's own version width counts, so a 3-digit dependency pin in a 4-digit repo is never a candidate.
|
||||
5. **mirror**: tier 2; declared and validated, reported as not run.
|
||||
6. **apply**: every changed file is written to the branch only after all stages validated, each against the fingerprinted preimage; a file edited meanwhile is `RESTAMP_CONFLICT` and the applied writes roll back (rollback refuses to overwrite a file whose bytes differ from the recorded postimage).
|
||||
|
||||
@@ -199,6 +204,26 @@ implementation (`bun run release:restamp` still runs the PR's package script),
|
||||
so the receipt names the policy revision, the command, its working directory
|
||||
and the trust scope (`policy: base origin/main@<sha>` or `head (unreviewed)`).
|
||||
|
||||
<a id="restamp-command"></a>
|
||||
### `restamp_command`: the repo owns the stamp
|
||||
|
||||
A repo whose own tool knows its stamp locations sets `restamp_command`.
|
||||
`gstack-restamp` runs it as the first stage inside its staging worktree, a
|
||||
clean checkout of HEAD, with the allocated version as the last argument and in
|
||||
`GSTACK_RESTAMP_VERSION`. The caller's HEAD, index and worktree must be clean
|
||||
first (`RESTAMP_CONFLICT`, nothing written); the generic path keeps its
|
||||
working-tree fingerprint seed. Anything the command stages or commits in the
|
||||
staging worktree is folded back into plain edits; then output confinement
|
||||
applies as for the release tool (`RESTAMP_OUTPUT_OUTSIDE_ALLOWED`), the
|
||||
version file must hold the target version (`RESTAMP_VERSION_SOURCE`), and a
|
||||
non-zero exit is `RESTAMP_RELEASE_TOOL_FAILED`. In every failure nothing
|
||||
reaches the branch. gstack's version writer is skipped; the CHANGELOG and
|
||||
`stamp_paths` stages still run (a no-op when the tool already did the work).
|
||||
Read from the base branch and run only with `--allow-repo-commands`, like
|
||||
`release_tool`. A tool that fetches, merges the base branch or needs a version
|
||||
of its own choosing cannot run here; give it a mode that takes the version and
|
||||
writes only stamps.
|
||||
|
||||
<a id="tree-receipt"></a>
|
||||
## The tree receipt
|
||||
|
||||
@@ -225,7 +250,10 @@ the CHANGELOG (a heading move or re-heading, compared as an order-independent
|
||||
multiset of entries), `stamp_paths` and `release_outputs` (every
|
||||
removed/added pair must be an exact `(was X)` / `since: X` / `vX` rewrite with
|
||||
X the gated version and the replacement the current one, so a dependency pin
|
||||
that happens to equal the old version is `changed`). Any other byte is a change.
|
||||
that happens to equal the old version is `changed`; `stamp_patterns`
|
||||
templates count as stamp forms too). With `stamp_files` set, a file outside it
|
||||
is `changed (stamp_files: not in the repo's stamp_files allowlist)` whatever
|
||||
its diff. Any other byte is a change.
|
||||
|
||||
Equivalence is decided per gate. A lane in `stamp_sensitive_gates` always
|
||||
`rerun`s; a verified lane is `reusable` only when the tree is same-modulo-stamps
|
||||
|
||||
Reference in new issue
Block a user