mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 07:18:03 +01:00
v1.93.20.0 docs: version flow under changelog fragments; scorecard pass cap of 2 (GST-1003) (#3882)
* docs: version flow under changelog fragments; scorecard pass cap matches the two-round review cap (GST-1003) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG_STYLE.md describes the fragment flow; pin the train land link to #train-land (GST-1003, #3885) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: GST-1003 fragment 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
cb72cc13b1
commit
16e4d2f4f0
11 files changed
+91
-65
No files matched your search
+46
-42
@@ -1,7 +1,9 @@
|
||||
# CHANGELOG + VERSION style
|
||||
|
||||
Moved verbatim from CLAUDE.md (token-load reduction). Read this BEFORE
|
||||
writing any `## [X.Y.Z]` CHANGELOG entry.
|
||||
writing a CHANGELOG entry. In gstack the entry is a fragment,
|
||||
`changelog.d/<branch-slug>.md`, and the merge step gives it its `## [X.Y.Z.W]`
|
||||
heading ([changelog fragments](ship-policy.md#changelog-fragments)).
|
||||
|
||||
## Versioning and branch-scoped entries
|
||||
|
||||
@@ -9,15 +11,15 @@ Moved verbatim from CLAUDE.md (#2096 size limit). The autonomous version
|
||||
policy (choose versions without asking; default to PATCH) stays in CLAUDE.md
|
||||
as the single source.
|
||||
|
||||
**Versioning invariant (workspace-aware ship).** VERSION is a monotonic ordered
|
||||
release identifier, not a strict semver commitment. The bump level
|
||||
(major/minor/patch/micro) expresses intent at ship time. Queue-advancing past a
|
||||
claimed version within the same bump level is explicitly permitted — if branch A
|
||||
claims v1.7.0.0 as a MINOR and branch B is also a MINOR, B lands at v1.8.0.0
|
||||
(still a MINOR relative to main). Downstream consumers must NOT rely on
|
||||
"MINOR = feature-only, PATCH = fix-only" as a strict contract. This is why
|
||||
`bin/gstack-next-version` advances within the chosen bump level rather than
|
||||
repicking the level when collisions happen.
|
||||
**Versioning invariant.** VERSION is a monotonic ordered release identifier,
|
||||
not a strict semver commitment. The bump level (major/minor/patch/micro)
|
||||
expresses intent. Versions are assigned at merge, in merge order: each stamp
|
||||
takes the version before it plus the effective bump, which is the policy's
|
||||
PATCH raised by any folded fragment's `bump:` front matter. If PR A and PR B
|
||||
both ask for MINOR and land back to back on v1.6.0.0, A lands at v1.7.0.0 and
|
||||
B at v1.8.0.0 (still a MINOR relative to the version before it). Downstream
|
||||
consumers must NOT rely on "MINOR = feature-only, PATCH = fix-only" as a
|
||||
strict contract.
|
||||
|
||||
**package.json carries the npm-valid translation, not VERSION verbatim.**
|
||||
VERSION stays the 4-digit source of truth (e.g. `1.67.0.0`); package.json and
|
||||
@@ -26,18 +28,25 @@ translation (`1.67.0`), and lockfile `version` fields sync only when the
|
||||
lockfile already exists. `bin/gstack-version-bump` (via `lib/version-source.ts`)
|
||||
owns the translation and judges drift on translated forms — do NOT "fix" the
|
||||
apparent mismatch by hand, and do not write a 4-digit version into
|
||||
package.json (npm rejects it). Rationale and translation rules live in the
|
||||
package.json (npm rejects it). A branch never writes either one: the merge
|
||||
stamp does, through `bin/gstack-version-bump`. Rationale and translation rules live in the
|
||||
`lib/version-source.ts` header; `test/gstack-version-bump.test.ts` pins the
|
||||
contract.
|
||||
|
||||
**VERSION and CHANGELOG are branch-scoped.** Every feature branch that ships gets its
|
||||
own version bump and CHANGELOG entry. The entry describes what THIS branch adds —
|
||||
not what was already on main.
|
||||
**The CHANGELOG entry is branch-scoped; the version is not.** Every feature
|
||||
branch that ships writes one entry as `changelog.d/<branch-slug>.md`: the entry
|
||||
text only, with no `## [x]` heading, and `bump: minor` or `bump: major` front
|
||||
matter only when the change warrants more than a PATCH. A branch never edits
|
||||
VERSION, package.json's version or a released CHANGELOG.md section. At merge,
|
||||
`train land` (or `gstack-ship-queue land`) assigns the version, folds the
|
||||
fragment into one `## [<version>] - <date>` section at the top of CHANGELOG.md
|
||||
and deletes the file. The entry describes what THIS branch adds — not what was
|
||||
already on main.
|
||||
|
||||
**The CHANGELOG entry is the diff between main and the shipping branch — what users
|
||||
get when they upgrade. NOT how the branch got there.** A reader landing on the entry
|
||||
should learn what they can do now that they couldn't before; they should not learn
|
||||
about the branch's internal version bumps, the bugs we caught and fixed mid-branch,
|
||||
about versions the branch carried during development, the bugs we caught and fixed mid-branch,
|
||||
the plan reviews we ran, or the commits we squashed. That is branch development
|
||||
narrative. It belongs in PR descriptions and commit messages, not CHANGELOG.
|
||||
|
||||
@@ -62,29 +71,28 @@ the user gets; the path to that system is invisible to them.
|
||||
**When to write the CHANGELOG entry:**
|
||||
- At `/ship` time (Step 13), not during development or mid-branch.
|
||||
- The entry covers ALL commits on this branch vs the base branch.
|
||||
- `/ship` lints the lines the branch added with `bin/gstack-prose-lint CHANGELOG.md --base origin/<base> --gate`,
|
||||
and the Quality gate workflow gates the `## [Unreleased]` block the same way. Rewrite each `PROSE:` line
|
||||
it names ([docs/troubleshooting.md](troubleshooting.md#prose-lint-found)).
|
||||
- Never fold new work into an existing CHANGELOG entry from a prior version that
|
||||
already landed on main. If main has v0.10.0.0 and your branch adds features,
|
||||
bump to v0.10.1.0 with a new entry — don't edit the v0.10.0.0 entry.
|
||||
- `/ship` lints the fragment with `bin/gstack-prose-lint changelog.d/<branch-slug>.md --gate`.
|
||||
Rewrite each `PROSE:` line it names ([docs/troubleshooting.md](troubleshooting.md#prose-lint-found)).
|
||||
- Never fold new work into a CHANGELOG.md section that already landed on main.
|
||||
New work goes in this branch's fragment, and the merge stamp gives it its own
|
||||
section at the next version.
|
||||
|
||||
**Key questions before writing:**
|
||||
1. What branch am I on? What did THIS branch change?
|
||||
2. Is the base branch version already released? (If yes, bump and create new entry.)
|
||||
3. Does an existing entry on this branch already cover earlier work? (If yes, replace
|
||||
it with one unified entry for the final version.)
|
||||
2. Does this change warrant more than a PATCH? (If yes, open the fragment with
|
||||
`bump: minor` or `bump: major` front matter; otherwise add none.)
|
||||
3. Does this branch's fragment already cover earlier work? (If yes, rewrite it as
|
||||
one unified entry.)
|
||||
|
||||
**Merging main does NOT mean adopting main's version.** When you merge origin/main into
|
||||
a feature branch, main may bring new CHANGELOG entries and a higher VERSION. Your branch
|
||||
still needs its OWN version bump on top. If main is at v0.13.8.0 and your branch adds
|
||||
features, bump to v0.13.9.0 with a new entry. Never jam your changes into an entry that
|
||||
already landed on main. Your entry goes on top because your branch lands next.
|
||||
**Merging main never gives your branch a version.** When you merge origin/main into
|
||||
a feature branch, main may bring new CHANGELOG sections and a higher VERSION. Take them
|
||||
exactly as main has them. Your branch still carries only its fragment, and never jams its
|
||||
changes into a section that already landed on main; the merge stamp puts its section on top.
|
||||
|
||||
**After merging main, always check:**
|
||||
- Does CHANGELOG have your branch's own entry separate from main's entries?
|
||||
- Is VERSION higher than main's VERSION?
|
||||
- Is your entry the topmost entry in CHANGELOG (above main's latest)?
|
||||
- Is your fragment still in `changelog.d/`, with its own text, separate from main's sections?
|
||||
- Are VERSION, package.json's version and every released CHANGELOG.md section exactly
|
||||
main's? (The Version Gate check requires this of a head the merge step did not stamp.)
|
||||
If any answer is no, fix it before continuing.
|
||||
|
||||
**After any CHANGELOG edit that moves, adds, or removes entries,** immediately run
|
||||
@@ -93,13 +101,9 @@ order. Gaps between version numbers are fine. A branch that ships at v1.6.4.0 wi
|
||||
a prior v1.5.2.0 or v1.5.3.0 entry on main is correct — those were branch-internal
|
||||
version numbers that never landed. Do not back-fill gaps with placeholder entries.
|
||||
|
||||
**Never orphan branch-internal versions.** If your branch bumped VERSION several times
|
||||
during development (v1.5.1.0 → v1.5.2.0 → v1.6.4.0, say) and those earlier entries were
|
||||
never released to main, the final ship consolidates ALL of them into a single entry at
|
||||
the final version (v1.6.4.0). Collapse them — delete the old entries and move their
|
||||
content into the final entry, re-version table columns accordingly. Readers see one
|
||||
release, not a branch diary. Gaps are fine (v1.6.3.0 → v1.6.4.0 with no v1.5.x
|
||||
in between on main is correct).
|
||||
**Keep one entry per branch.** When the branch's work grows after its fragment was
|
||||
written, rewrite that fragment as one unified entry instead of adding a second file
|
||||
beside it. Readers see one release, not a branch diary.
|
||||
|
||||
CHANGELOG.md is **for users**, not contributors. Write it like product release notes:
|
||||
|
||||
@@ -122,9 +126,9 @@ we got here. Keep out of the CHANGELOG, always:
|
||||
documents what DID ship, not what MIGHT ship.
|
||||
- Version-bump housekeeping when no user-facing work actually landed.
|
||||
|
||||
If the diff between the base branch version and this version has no user-facing change
|
||||
(only merges, only CHANGELOG edits, only placeholder work), the honest entry is one
|
||||
sentence: "Version bump for branch-ahead discipline. No user-facing changes yet." Stop
|
||||
If the diff between the base branch and this branch has no user-facing change
|
||||
(only merges, only CHANGELOG edits, only placeholder work), the honest fragment is one
|
||||
sentence: "No user-facing changes yet." Stop
|
||||
there. Do not pad. Do not explain the plan that will ship eventually. Do not narrate
|
||||
the branch's history. When real work lands, the entry will replace this at /ship time.
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ line whose input could not be read says `unavailable (<reason>)` rather than 0.
|
||||
gstack-factory-scorecard --repo owner/name --since-days 7 \
|
||||
[--crew login,login] [--gate-workflow evals.yml] [--free-workflow free-tests.yml] \
|
||||
[--train-prefix train/] [--ready-marker 'READY #\d+|SHIP_RECEIPT:'] [--pass-marker '"passes": (\d+)|passes=(\d+)'] \
|
||||
[--pass-cap 3] [--out-dir .context/factory-scorecard] [--now <iso>]
|
||||
[--pass-cap 2] [--out-dir .context/factory-scorecard] [--now <iso>]
|
||||
```
|
||||
|
||||
`--repo` defaults to the origin remote's owner/name on github.com (or
|
||||
@@ -280,7 +280,7 @@ measured later does not count anything that happened after that week.
|
||||
| Merged PRs | `merged_at` in the window, base = default branch; crew by `--crew` logins, else author association OWNER/MEMBER/COLLABORATOR | several merges a day, every day; outside contributions land in the same week |
|
||||
| Lead time, created → merged | PR `created_at` to `merged_at`, median and p90 | median under 24h, p90 under 3 days (DORA "elite" change lead time is under a day) |
|
||||
| Lead time, READY → merged | first PR comment matching `--ready-marker` at or before `merged_at`; PRs with no such comment are left out and counted | under 2h; every merged PR carries a READY comment |
|
||||
| Review passes per PR | the largest count `--pass-marker` captures in the PR body (the ship receipt's `"passes": N` once /ship writes it) or a PR comment (`passes=N`, `REVIEW_PASSES: N`), median and p90, plus how many PRs sat at `--pass-cap` (3: pass 1 reviews the whole diff, later passes only the delta, a pass that finds nothing new is the exit) | median 2 passes; none at the cap |
|
||||
| Review passes per PR | the largest count `--pass-marker` captures in the PR body (the ship receipt's `"passes": N` once /ship writes it) or a PR comment (`passes=N`, `REVIEW_PASSES: N`), median and p90, plus how many PRs reached `--pass-cap` (2, the [review policy](review-policy.md)'s cap of two rounds, a fix-check included: pass 1 reviews the whole diff, pass 2 only the range since, and a pass that finds nothing blocking is the exit) | median 1 pass; few at the cap |
|
||||
| Gate runs green / red / cancelled | completed runs of the gate workflow (`workflow_dispatch` by default; `--gate-event any`); cost from the run's `report-verdict-a*` artifact (`collector-outcomes.json` `.verdict.costUsd`), else the `**$N** rule/judge cost` or `cost=$N` line of the PR's verdict comment | green on 95%+ of runs, cancelled near 0, cost known for every run |
|
||||
| Known gate cost per merged PR | known cost / merged PRs; unknown-cost runs make it a floor | known, stable or falling week over week |
|
||||
| Red-train rate | runs whose head branch starts with `--train-prefix`, cancelled excluded; red = conclusion other than success; red backstops and screens shown beside it | under 5% (each red train is a bisect plus a rebuild, about two gates) |
|
||||
|
||||
+9
-3
@@ -112,10 +112,16 @@ refuses the file by name (`POLICY_GSTACK_TOO_OLD`).
|
||||
`.gstack/ship-policy.example.json` shows every key set the way a serial-merge
|
||||
repo with a release script, docs mentions and a mirror would set them.
|
||||
|
||||
### Queue-side procedure for gstack
|
||||
<a id="queue-side-procedure-for-gstack"></a>
|
||||
### Queue-side procedure under `stamp-at-merge` without fragments
|
||||
|
||||
Who does what under `stamp-at-merge`, for the PR author and for the thread
|
||||
running the merge queue:
|
||||
gstack itself no longer follows these steps. Since #3654 (v1.91.342.0) its
|
||||
policy sets [`changelog_fragments`](#changelog-fragments), so a gstack PR adds
|
||||
a fragment and never a version, and trains merge through
|
||||
[`train land`](#train-land). The steps
|
||||
below are for a repo on `stamp-at-merge` whose policy has no
|
||||
`changelog_fragments`. Who does what there, for the PR author and for the
|
||||
thread running the merge queue:
|
||||
|
||||
1. **Open.** The branch carries a tentative stamp of the base's version plus
|
||||
PATCH (`gstack-version-bump write --version <base+patch> --regen-digest`)
|
||||
|
||||
Reference in new issue
Block a user