mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-14 20:37:14 +01:00
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
commit
a01bb21430
133 files changed
+5672
-994
No files matched your search
@@ -111,7 +111,10 @@ determined leaker (a CHANGELOG line that does would fail a hostile screenshotter
|
||||
- **Engine + taxonomy:** `lib/redact-patterns.ts` (the single source of truth —
|
||||
3 tiers; HIGH = genuinely-secret credentials that block, MEDIUM = PII/legal/
|
||||
internal + high-FP credential shapes that confirm via AskUserQuestion, LOW =
|
||||
FYI) and `lib/redact-engine.ts` (pure `scan()` + `applyRedactions()`).
|
||||
FYI) and `lib/redact-engine.ts` (`scan()` + `applyRedactions()`; no I/O,
|
||||
and `scan()`'s one ambient input is the credential-named variables of
|
||||
`process.env`, read by value for the `env.value` HIGH finding unless
|
||||
`ScanOptions.processEnv` names another environment).
|
||||
Calibration matters: a gate that cries wolf gets ignored, so context-variable
|
||||
shapes (Stripe `pk_live_`, Google `AIza`, JWT, env `*_KEY=`) sit at MEDIUM.
|
||||
- **CLI:** `bin/gstack-redact` (exit 0 clean / 2 MEDIUM / 3 HIGH; `--json`,
|
||||
|
||||
+34
-5
@@ -269,8 +269,8 @@ through the `bun` shim beside it.
|
||||
- `GSTACK_BUN_SHIM=0` at install time writes no shim.
|
||||
- Removing it: delete `~/.local/bin/bun` and `~/.local/bin/bunx`. gstack
|
||||
replaces only a missing file or a file carrying the marker; a foreign file
|
||||
or a dangling link there is left untouched with a note, and the
|
||||
`GSTACK_LAUNCH` prefix stays the way to reach the certified Bun.
|
||||
or a dangling link there is left untouched with a note, and sourcing the
|
||||
launch file (`. <GSTACK_LAUNCH_FILE>`) stays the way to reach the certified Bun.
|
||||
- The doctor's `runtime` row notes the shim; the resolver never treats a
|
||||
shim-marked file as a Bun candidate.
|
||||
|
||||
@@ -345,8 +345,37 @@ Every successful run, `--check` included, prints before the trailer:
|
||||
```
|
||||
GSTACK_LAUNCH: PATH=<dir>:$PATH GSTACK_SESSION_KIND=unattended GSTACK_STATE_ROOT=<path> GSTACK_EPHEMERAL=1 GSTACK_CAPY=1 GSTACK_INSTALL_VERDICT=<file>
|
||||
GSTACK_LAUNCH_PATH: <dir>
|
||||
GSTACK_LAUNCH_FILE: <verdict file without .json>.env
|
||||
```
|
||||
|
||||
`GSTACK_LAUNCH_FILE` names the same assignments as a sourceable script (mode
|
||||
0600, beside the verdict under the state root; the PATH prepend is idempotent).
|
||||
The file is part of the ok verdict: a passing run that cannot write it exits 1
|
||||
with `gstack: fail launch-file — could not write the launch file <path>; fix:
|
||||
make <dir> writable, or set GSTACK_STATE_ROOT to a writable directory
|
||||
(LAUNCH_FILE_UNWRITABLE)`, and a failing run prints no `GSTACK_LAUNCH_FILE` line
|
||||
and removes the file an earlier passing run left beside the verdict it rewrites
|
||||
(a profile check that keeps a fuller ok verdict leaves that verdict's file).
|
||||
`. <that file>` at the start of a shell applies them without ever printing them,
|
||||
which is the only way to hand them to a child agent: pass the path, never the
|
||||
values. None of the assignments is a secret, but during GSTA-96's unattended
|
||||
/ship a docs-audit child re-typed them as `export $(...)`; the substitution came
|
||||
back empty, the bare `export` printed the whole environment, and three API key
|
||||
values landed in its tool output. Never `export $(...)`, `env`, `printenv`, a
|
||||
bare `set` or a bare `export` in a tool fence (`test/launch-env-echo-ratchet.test.ts`
|
||||
keeps them out of every skill fence and shell helper), and the redaction
|
||||
engine now refuses the VALUE of any `*_KEY`, `*_TOKEN`, `*_SECRET`,
|
||||
`*_PASSWORD`, `*_PASSWD`, `*_PASS`, `*_PAT`, `*_APIKEY` or `*_PWD` variable
|
||||
in the writer's environment (any case; names carrying a `PUBLIC` word are
|
||||
framework-public and exempt, gstack's own `GSTACK_BRAIN_LOCK_TOKEN` is not a
|
||||
secret) (`env.value`, HIGH), so a leaked value cannot reach a PR body, receipt,
|
||||
report or Drive file. There is no path exemption: a path-valued `SSL_KEY` that
|
||||
must appear in a text goes through `bin/gstack-redact --allowlist` or the
|
||||
pre-push hook's committed `.gstack-redact-allowlist`; the other writers
|
||||
(`gstack-post`, receipts, reports) take no allowlist, so for one of those
|
||||
commands set the variable empty for that command alone
|
||||
(`SSL_KEY= gstack-post …`): an empty value is below the length floor.
|
||||
|
||||
`PATH=<dir>:$PATH` and `GSTACK_LAUNCH_PATH` appear only when the certified Bun,
|
||||
or the project's resolved Node, is not first on PATH (older Capy images ship
|
||||
`/usr/local/bin/bun` 1.3.14; 57 gstack commands start with `#!/usr/bin/env bun`).
|
||||
@@ -358,8 +387,8 @@ directory holding only `bun`, `bunx` and, when needed, `node`, `npm`, `npx` and
|
||||
never puts that directory's other tools (the image's node) in front of PATH.
|
||||
The pregate's lanes launch through the same directory. A child shell's `export` cannot
|
||||
configure the parent's later commands, so the parent supplies these
|
||||
assignments to every gstack command it launches (prefix the command, or
|
||||
export them once in the shell that runs it). A fresh shell without them keeps
|
||||
assignments to every gstack command it launches by sourcing the launch file
|
||||
(`. <GSTACK_LAUNCH_FILE>`) at the start of the shell that runs it. A fresh shell without them keeps
|
||||
reporting the Bun it would really run: `FAIL runtime — bun 1.3.14 at
|
||||
/usr/local/bin/bun ...; verified bun 1.4.2 found at ~/.bun/bin (not on PATH);
|
||||
fix: export PATH="$HOME/.bun/bin:$PATH" or prefix commands with the
|
||||
@@ -434,6 +463,6 @@ Wrapper 2 only adds optional exports (the trailer's `latest:` note and the
|
||||
| Exit | Meaning |
|
||||
|-----:|---------|
|
||||
| 0 | `gstack: ok` — every required component passed |
|
||||
| 1 | `gstack: fail <ids>` — a required component failed; each row has a fix |
|
||||
| 1 | `gstack: fail <ids>` — a required component failed; each row has a fix. Also `gstack: fail launch-file …` when a passing run could not write its launch file ([LAUNCH_FILE_UNWRITABLE](troubleshooting.md#capy-install-launch-file-unwritable)) |
|
||||
| 2 | usage error |
|
||||
| 3 | preflight stopped before `./setup` (`INSTALL_PREFLIGHT_FAILED`, `RUNTIME_PIN_CONFLICT`); nothing was installed |
|
||||
@@ -178,8 +178,11 @@ draft are cut: the artifact path is tool-owned, a child's self-reported
|
||||
path list is both a pathspec-injection surface and a self-report, and the
|
||||
only history need is calibration, which `--commit <sha>` covers by scanning
|
||||
one commit's first-parent delta without the working tree.) Positional
|
||||
base defaults the way `gstack-diff-scope` does (`origin/HEAD` → `main` →
|
||||
`master`); an already-qualified ref is used as given. stdout: one
|
||||
base defaults the way `gstack-diff-scope` does (the remote's `HEAD` target,
|
||||
origin first, then the other remotes → `main` → `master`); an
|
||||
already-qualified ref is used as given, and a branch name with no local
|
||||
branch resolves through its remote-tracking twin (`origin/main` for `main`:
|
||||
the merge-base is the same commit), noted once on stderr. stdout: one
|
||||
`GATE_SUMMARY: listed=N read=N tagged={RH-1:2,RH-15:1} unmatched=N
|
||||
coverage=<patterns> languages_unlisted=<…> candidate=<sha+wtree>` line, then
|
||||
the read-level listing (bounded; the full listing goes to `--out`).
|
||||
@@ -207,9 +210,10 @@ hunks and 200 KB; `GATE_SUMMARY` reports `eligible`, `inspected`,
|
||||
like UNAVAILABLE, and never as `none detected`.
|
||||
|
||||
**Exit contract.** 0 with or without hunks; 2 only when the base is
|
||||
unresolvable (`GATE_ERROR=no_base ref=<ref>` with the concrete recovery:
|
||||
`git fetch <remote> <branch>` from the detected remote, or "deepen the
|
||||
shallow history") or the tool itself failed. Unmatched paths are reported
|
||||
unresolvable locally and as `<remote>/<ref>` (`GATE_ERROR=no_base ref=<ref>`
|
||||
with the concrete recovery: `git fetch <remote> +<branch>:refs/remotes/<remote>/<branch>`
|
||||
into the remote-tracking ref, `--unshallow` on a shallow clone, or a plain
|
||||
fetch/deepen for a sha or relative ref) or the tool itself failed. Unmatched paths are reported
|
||||
in `GATE_SUMMARY: unmatched=N` and listed, never an exit 2. The template
|
||||
block that calls it branches on the exit explicitly, never
|
||||
`2>/dev/null || true`:
|
||||
@@ -350,9 +354,21 @@ a test count natively.
|
||||
saved log is capped at 2 MB and would truncate the summary) and parses
|
||||
it into `tests_ran` for the supported runners only: bun (reusing
|
||||
`shard-engine`'s classifier; executed = pass + fail, so an all-skip run is
|
||||
ZERO-RUN), jest/vitest, pytest, rspec, and Go's zero-only case (every
|
||||
package `[no test files]`); the last summary per runner wins. Everything
|
||||
else is `unknown`. `check` reports ZERO-RUN as STALE-equivalent and
|
||||
ZERO-RUN), jest/vitest, pytest, rspec, Go's zero-only case (every
|
||||
package `[no test files]`), gstack's shard runner (the sum of its per-shard
|
||||
`[test:free] PASS — N tests` epilogues, or a files-from run's
|
||||
`FILES-FROM RESULT: … tests=N`), and a `gstack-pregate --remote` lane (its
|
||||
`PREGATE_REMOTE: tree=… tests=N failed=M` row or the obligation's
|
||||
`cleared by remote-gate tree=… tests=N` receipt line); when the shard runner
|
||||
spoke its verdict stands for the whole tail (the tail is stdout then stderr,
|
||||
so position cannot order bun's stderr counts against the runner's stdout
|
||||
epilogues), and any summary when it did not speak follows the
|
||||
last-summary-wins rule. Everything else is `unknown`. The shard runner and the
|
||||
remote gate print only bun's terminal totals, which count skipped tests, so
|
||||
for those two `tests_ran` is ran-with-skips rather than executed: the shard
|
||||
runner refuses a shard whose bun summary counts fewer files than planned, but
|
||||
a shard whose files all loaded and executed nothing still passes with that
|
||||
total. `check` reports ZERO-RUN as STALE-equivalent and
|
||||
unknown as non-blocking. The TRANSPARENCY INVARIANT holds: `run`'s exit is
|
||||
always the child's; detection lives in the receipt and in `check`.
|
||||
- After Step 7's test-generation child returns and after Step 9.4's parent
|
||||
|
||||
@@ -194,6 +194,36 @@ Two stages with separate clocks, both printed:
|
||||
issue templates, CODEOWNERS and dependabot config). Stylesheets (they import
|
||||
each other), any other dotfile (`.rspec`, `.husky/` hooks, `.env*`, version
|
||||
pins), CI workflows and actions, and `.gstack/` still need a lane.
|
||||
A release manifest the branch only re-stamped is waived as
|
||||
version-stamp-only (`version-stamp-only (no lane needed): …`): the file's
|
||||
diff against the merge base changes nothing but its version values. Three
|
||||
file kinds qualify, each by its own exact rule (lib/pregate/stamp-only.ts).
|
||||
A JSON file (`*.json`) whose two sides parse to objects of the same shape
|
||||
(the same own keys in the same order in every object, the same length in
|
||||
every array, the same type at every leaf), whose only differing leaf is the
|
||||
document's top-level `version` with a bare version on both sides (`1.2.3`,
|
||||
`v1.2.3.4`, `1.2.3-rc.1`; never a range), and whose text is line-for-line
|
||||
identical once that line has its old quoted version replaced by the new
|
||||
one. A plist whose differing lines are all `<string>` versions under
|
||||
`<key>CFBundleShortVersionString</key>` or `<key>CFBundleVersion</key>`. A
|
||||
`VERSION` or `VERSION.txt` file (any case) holding one bare version, with at
|
||||
most a trailing LF, on both sides. Texts are compared byte for byte outside
|
||||
the stamped value, so a line-ending change or a BOM is content. Nothing
|
||||
nested in JSON qualifies, so a dependency pin or range, a lockfile entry, a
|
||||
catalog or a package named `version` is never a stamp; nor is a changed
|
||||
description, a new, removed or reordered key, a floor such as
|
||||
`minHostVersion`, `schemaVersion` or `MinimumOSVersion`, a reflow, a change
|
||||
the parser would erase (a big integer, a duplicate key), a file that is not
|
||||
valid UTF-8 or is over 1 MiB, a mode change or a symlink on either side, an
|
||||
added, deleted or renamed manifest, or any other file kind: each keeps its
|
||||
`PREGATE_NO_LANE`. The rule runs only after the import graph, the
|
||||
declarations, the regen registry and both empty lanes found nothing, so a
|
||||
manifest a test reads still selects that test, and never under a test,
|
||||
fixture, golden, snapshot, `.gstack/` or CI-workflow tree, whose JSON a test
|
||||
may compare against by a path the graph cannot see.
|
||||
`--explain` prints the verdict per file (`version-stamp-only against
|
||||
<merge-base> (version 1.0.0 → 1.0.1)` or `not version-stamp-only
|
||||
(<reason>)`).
|
||||
In a Django project (a manage.py or code importing django, plus Python
|
||||
code, not a string or comment, that assigns `INSTALLED_APPS` or
|
||||
`ROOT_URLCONF`, or a config file names `DJANGO_SETTINGS_MODULE`), the files
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# Proactive skill suggestions
|
||||
|
||||
gstack notices what stage you are in (a new idea, a diff to review, "does this
|
||||
work?", time to land it) and can invoke the matching skill without you typing
|
||||
its slash command. The `proactive` config key says how far that goes.
|
||||
|
||||
| Value | What auto-invokes | Who it is for |
|
||||
|-------|-------------------|---------------|
|
||||
| `core` (default) | `/office-hours`, `/review`, `/qa` and `/ship` only. Every other skill waits for you to type it, unless a skill you ran hands off to it (`/office-hours` offering the next review). | Most people, and anyone running gstack beside another skill pack: the four core skills are the ones users praise, and the others no longer compete with another pack's niche skills. |
|
||||
| `all` | Any skill whose purpose matches your request. | You use gstack on its own and want the whole sprint suggested. |
|
||||
| `off` | Nothing. gstack never suggests or auto-invokes a skill, not even by asking "want me to run /X?". | You type every command yourself. |
|
||||
|
||||
`true` and `false` still work as the older spellings of `all` and `off`, so a
|
||||
config written before the `core` default keeps its behavior; `gstack-skill-start`
|
||||
reports them to the skill as `all` and `off`. A hand edit may be quoted, upper-case
|
||||
or commented (`proactive: "OFF" # mine`), and the YAML yes/no family (`yes`, `on`,
|
||||
`1`; `no`, `none`, `0`, `disabled`, `never`) reads as `all` or `off`, in
|
||||
`gstack-config set` as well, so a typed opt-out never fails open.
|
||||
|
||||
```bash
|
||||
gstack-config get proactive # core, all, off (or a legacy true/false)
|
||||
gstack-config set proactive all # suggest every matching skill
|
||||
gstack-config set proactive off # suggest nothing
|
||||
gstack-config set proactive core # back to the default
|
||||
```
|
||||
|
||||
A skill you name is always invoked, whatever the setting, and so is a skill that
|
||||
a running skill invokes itself (`/ship` dispatching `/document-release`). The
|
||||
setting governs unasked suggestions, and an in-skill *offer* to run another skill
|
||||
is one: under `off`, `/office-hours` recommends the next review in one line
|
||||
instead of offering to launch it, and the one-time first-run
|
||||
tips stay quiet. The change takes effect on the next skill run; no `./setup` is
|
||||
needed, because every skill reads `PROACTIVE` from `gstack-skill-start` at the
|
||||
start of each run and the router (`/gstack`) applies it to its routing rules. A
|
||||
value gstack does not know (a hand-edited `proactive: maybe`) reads as `core`.
|
||||
|
||||
Two surfaces follow the setting only in part. The first is the `## Skill routing`
|
||||
section gstack offers to append to your project's CLAUDE.md. Under `off` the offer is
|
||||
not made, because those rules are the auto-invocation you turned off (a section
|
||||
already in CLAUDE.md stays; delete it yourself). Under `core` the offered
|
||||
section still lists every routing rule and tells the host to invoke any
|
||||
matching skill. If you accepted it before the `core` default, trim that section
|
||||
to the four core rules (or delete it and let gstack offer it again); making the
|
||||
offered section follow `core` is tracked in #3446.
|
||||
|
||||
The second is the full skill descriptions, which a `--catalog-mode=full` build
|
||||
and the non-Claude hosts keep. Non-core skills still say "Proactively suggest
|
||||
when ..." there, and that text does not change with the setting, so a host
|
||||
that picks skills from those descriptions can still reach them under `core`.
|
||||
The default Claude build uses one-line descriptions without that sentence.
|
||||
#3446 tracks that too.
|
||||
|
||||
gstack asks once, after the telemetry question, which of the three you want;
|
||||
if you stored `off` before that, it takes the answer as given and does not ask.
|
||||
"Say stop suggesting" in a session runs `gstack-config set proactive off` for you.
|
||||
|
||||
If collisions with other skill packs keep coming up under `core`, the fallback
|
||||
Garry approved on 2026-10-25 is `off` by default.
|
||||
@@ -1236,6 +1236,10 @@ Outside-review routing follows the harness, independently of the configured mode
|
||||
|
||||
`/claude` was renamed to `/claude-code` without an alias. Run `./setup --host <name>` to migrate managed installations, including installations sharing that checkout. Setup retains a working old installation when replacement generation or installation fails.
|
||||
|
||||
## Proactive skill suggestions
|
||||
|
||||
gstack can invoke a skill for you when your request matches one. By default (`proactive: core`) only `/office-hours`, `/review`, `/qa` and `/ship` auto-invoke; `all` frees every skill and `off` suggests nothing. See [docs/proactive.md](proactive.md).
|
||||
|
||||
## Safety & Guardrails
|
||||
|
||||
Four skills that add safety rails to any Claude Code session. They work via Claude Code's PreToolUse hooks — transparent, session-scoped, no configuration required.
|
||||
|
||||
+7
-3
@@ -166,14 +166,18 @@ option and never writes there.
|
||||
A child shell's `export` cannot configure the parent's later commands
|
||||
(`lib/state-root.ts` reads each process's own environment), so the installer
|
||||
ends with one machine-readable line the parent supplies to every gstack
|
||||
command it launches:
|
||||
command it launches, and the same assignments as a sourceable file:
|
||||
|
||||
```
|
||||
GSTACK_LAUNCH: GSTACK_SESSION_KIND=unattended GSTACK_STATE_ROOT=<path> GSTACK_EPHEMERAL=1
|
||||
GSTACK_LAUNCH_FILE: <verdict file without .json>.env
|
||||
```
|
||||
|
||||
Prefix each command with those assignments (or export them once in the shell
|
||||
that runs them). See [docs/capy-install.md](capy-install.md).
|
||||
Apply them with `. <that file>` at the start of the shell that runs each
|
||||
command; never re-type them as `export $(...)`, and never run `env`, `printenv`,
|
||||
a bare `set` or a bare `export` in a tool fence (a bare `export` prints every
|
||||
variable, credentials included). See
|
||||
[docs/capy-install.md](capy-install.md#the-launch-line).
|
||||
|
||||
## Uninstall
|
||||
|
||||
|
||||
+48
-2
@@ -1470,6 +1470,20 @@ or nvm), then re-run `bin/gstack-capy-install`. A checksum mismatch means the
|
||||
upstream installer changed: update `GSTACK_BUN_INSTALL_SHA` only after
|
||||
reviewing the new script.
|
||||
|
||||
<a id="capy-install-launch-file-unwritable"></a>
|
||||
### `gstack: fail launch-file — could not write the launch file <path>; fix: ... (LAUNCH_FILE_UNWRITABLE)`
|
||||
|
||||
**Meaning.** The doctor passed, but `bin/gstack-capy-install` could not write
|
||||
the launch file (`GSTACK_LAUNCH_FILE`, the sourceable mode-0600 copy of the
|
||||
`GSTACK_LAUNCH` assignments beside the install verdict). The file is part of the
|
||||
ok verdict: without it a caller would be left with only the line and the
|
||||
re-typing path that leaked an environment in GSTA-96, so the run fails instead.
|
||||
The directory under the state root is unwritable, or a temp file could not be
|
||||
moved into place.
|
||||
|
||||
**Fix.** Make the verdict directory writable, or set `GSTACK_STATE_ROOT` to a
|
||||
writable directory, then re-run `bin/gstack-capy-install`.
|
||||
|
||||
<a id="upgrade-refused"></a>
|
||||
### `gstack-upgrade: <cause>; nothing was changed; fix: <command> (UPGRADE_REFUSED)`
|
||||
|
||||
@@ -2244,7 +2258,11 @@ entry that no longer applies; rotate a credential that is real.
|
||||
<a id="redact-version-as-ip"></a>
|
||||
### `pii.ip_public` MEDIUM on a four-part version number
|
||||
|
||||
**Meaning.** The line does not declare the value as a version.
|
||||
**Meaning.** The line does not declare the value as a version. When the
|
||||
text goes through `gstack-post` from inside the repository, four-part versions
|
||||
equal to the root `VERSION` or to a `## [x.y.z.w]` / `## vx.y.z.w` CHANGELOG.md heading are
|
||||
exempt wherever they appear, so there the finding means the quad is not a
|
||||
release of this repository (or the post ran outside the checkout).
|
||||
|
||||
**Fix.** Write it as `version: 1.2.3.4`, `"version": "..."` or `v1.2.3.4`.
|
||||
MEDIUM findings do not block pushes.
|
||||
@@ -2620,8 +2638,22 @@ read the receipt again.
|
||||
|
||||
**Meaning.** Two sections share one version heading, usually a stamp that
|
||||
re-headed `[Unreleased]` beside an entry that already carried the version.
|
||||
In `gstack-restamp` only duplicates the PR introduces count: the headings are
|
||||
compared against the base branch's CHANGELOG as read at `origin/<base>` and at
|
||||
the commit the branch's own lines are counted from (its fork point, or the
|
||||
predecessor's head in a gate-ahead stamp); the larger count per heading wins,
|
||||
so a version the base already heads twice (released history the PR inherited
|
||||
and must keep) passes as long as the PR adds no further copy of it. When a
|
||||
reading is unavailable (the PR adds the CHANGELOG itself, or the file moved),
|
||||
the stage detail and the refusal name it: with no reading left every duplicate
|
||||
counts, otherwise inherited headings are counted at the readable commit only. Two paths keep the strict rule: the section being re-headed itself
|
||||
(`[Unreleased]`, or the branch's earlier stamp) may exist only once, and
|
||||
`gstack-version-bump write --changelog` (the claim-at-open bump) compares the
|
||||
file with itself.
|
||||
|
||||
**Fix.** Merge them into one entry under the new version.
|
||||
**Fix.** Merge the duplicate sections the PR introduced into one entry under
|
||||
the new version. A duplicate that is on the base branch itself is not this
|
||||
PR's to fix in a restamp; leave it and the stamp proceeds.
|
||||
|
||||
<a id="changelog-top-mismatch"></a>
|
||||
### `CHANGELOG_TOP_MISMATCH`
|
||||
@@ -2999,6 +3031,20 @@ test. An unmapped source or code file still fails. A file under a test
|
||||
directory is not this empty lane. Images, fonts and media, and
|
||||
allowlisted tool config no test names, are the assets-and-tool-config empty lane,
|
||||
not this error.
|
||||
A release manifest the branch only re-stamped is not this error either: when a
|
||||
touched `*.json`, `*.plist`, `VERSION` or `VERSION.txt` file differs from the
|
||||
merge base in nothing but its version values, `lanes` waives it as
|
||||
version-stamp-only and prints `version-stamp-only (no lane needed): <files>`.
|
||||
The exact per-kind rule (the document's top-level `version` in JSON of the
|
||||
same shape and text, the two bundle version keys in a plist, one bare version
|
||||
in a VERSION file) is in [docs/pregate.md](pregate.md#gstack-pregate). A
|
||||
restamp PR on a base whose policy declares no lane for its manifests (gbrain's
|
||||
`release:restamp` and its plugin manifests) therefore passes without a
|
||||
policy change. A manifest whose description, keys, key order, nested
|
||||
versions, line endings or any non-version byte changed, one under a test,
|
||||
fixture, golden, `.gstack/` or workflow tree, or one added, deleted, renamed
|
||||
or re-moded, is still this error; `--explain` prints `not version-stamp-only (<reason>)`
|
||||
for it.
|
||||
|
||||
**Fix.** Add a test that names the file, or declare its lane (an explicit `[]`
|
||||
for files with no behaviour) under `dependencies` in `.gstack/pregate.json` (or the user-owned file the message names).
|
||||
|
||||
Reference in new issue
Block a user