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:
Garry Tanandcapy-ai[bot] authored and GitHub committed 2026-11-02 21:28:30 +00:00
1 parent cb72cc13b1
commit 16e4d2f4f0
11 files changed
+91 -65

No files matched your search

+12
View File
@@ -1,5 +1,17 @@
# Changelog # Changelog
## [1.93.20.0] - 2026-11-02
### Changed
- `gstack-factory-scorecard` now counts a PR as "at the cap" once it reaches 2 review passes, the two-round cap the review policy sets, instead of 3. The default `--pass-cap` is 2, and the "Review passes per PR" benchmark reads "median 1 pass, few at the cap". Pass `--pass-cap 3` to get the old count. Rows from earlier weeks in `trend.csv` were counted at the old cap, and each row names the cap it used.
### Docs
- CLAUDE.md now describes how gstack versions its own PRs today: a branch writes its entry as `changelog.d/<branch-slug>.md` and never edits VERSION, package.json or CHANGELOG.md, and the merge train's stamp assigns the version (`bump: minor` or `bump: major` front matter asks for more than a patch). The old text said to claim a version slot with `bin/gstack-next-version`.
- docs/CHANGELOG_STYLE.md, which CLAUDE.md says to read before writing an entry, now describes the same fragment flow: what goes in the fragment, when to add `bump:` front matter, and what to check after merging main. It no longer tells a branch to bump VERSION or claim a slot with `bin/gstack-next-version`.
- docs/ship-policy.md marks the queue-side procedure as being for repos on `stamp-at-merge` without changelog fragments, and points gstack's own PRs to the fragment flow and `train land`. The old link to that section still works.
## [1.93.19.0] - 2026-11-02 ## [1.93.19.0] - 2026-11-02
**`gstack-ship-receipt read --check-prose` no longer reads the Documentation exception line or code fences as counter claims (GST-983, GST-984).** `write --docs` with an owner exception requires the PR body to carry the whole `Documentation exception: …` line word for word, but the prose check read the owner's reason in it as prose, so a reason like "sections 1 and 2 skip the audit" was a `2 skip` claim and no body could pass both checks. That is the deadlock GST-977 fixed for the Review-loop exception line. Separately, on gstack-evals #142, test output pasted in a fenced block ("113 passed, 0 failed") and a quoted code comment ("the first 35 fail") were read as receipt counts and refused. **`gstack-ship-receipt read --check-prose` no longer reads the Documentation exception line or code fences as counter claims (GST-983, GST-984).** `write --docs` with an owner exception requires the PR body to carry the whole `Documentation exception: …` line word for word, but the prose check read the owner's reason in it as prose, so a reason like "sections 1 and 2 skip the audit" was a `2 skip` claim and no body could pass both checks. That is the deadlock GST-977 fixed for the Review-loop exception line. Separately, on gstack-evals #142, test output pasted in a fenced block ("113 passed, 0 failed") and a quoted code comment ("the first 35 fail") were read as receipt counts and refused.
+15 -11
View File
@@ -374,18 +374,22 @@ is not a reason to choose MINOR. Choose **MINOR (X.Y+1.0.0)** or **MAJOR
("ridiculous"), such as an incompatible public-interface change or a genuinely ("ridiculous"), such as an incompatible public-interface change or a genuinely
new product-scale release. Make that judgment without another approval question. new product-scale release. Make that judgment without another approval question.
Use `bin/gstack-next-version` to check the live release queue before publishing. gstack's policy sets `changelog_fragments` (#3654), so a branch never writes a
If a slot is claimed, advance to the next available version at the chosen bump version and never checks `bin/gstack-next-version` for a slot. The merge
level and use `bin/gstack-version-bump` to synchronize release metadata. A higher train's stamp (`train land`) assigns base + PATCH at merge. To ask for more,
base version does not itself require a MINOR bump. Keep the PR ready for Garry to open the branch's fragment with `bump: minor` (or `major`) front matter. A
merge; autonomous version decisions do not authorize merging, deploying or higher base version does not itself require a MINOR bump. Keep the PR READY for
skipping required validation. the merge train; autonomous version decisions do not authorize merging,
deploying or skipping required validation.
**VERSION and CHANGELOG are branch-scoped.** Each shipping branch gets its own **The CHANGELOG entry is a branch fragment.** Each shipping branch writes one
bump and one entry, written at `/ship` time, that describes what THIS branch entry at `/ship` time as `changelog.d/<branch-slug>.md` (entry text only, no
adds versus main, for users, with no branch-internal versions or development `## [x]` heading) that describes what THIS branch adds versus main, for users,
narrative. VERSION stays 4-digit; package.json carries the 3-digit npm with no branch-internal versions or development narrative. A branch never edits
translation owned by `bin/gstack-version-bump`, so never hand-edit it. Read VERSION, package.json or a released CHANGELOG.md section: the merge stamp folds
the fragment into CHANGELOG.md under its version, keeps VERSION 4-digit and
writes package.json's 3-digit npm translation through `bin/gstack-version-bump`
([changelog fragments](docs/ship-policy.md#changelog-fragments)). Read
[docs/CHANGELOG_STYLE.md](docs/CHANGELOG_STYLE.md) BEFORE writing an entry: it [docs/CHANGELOG_STYLE.md](docs/CHANGELOG_STYLE.md) BEFORE writing an entry: it
holds the versioning invariant, the merge-main checks, what stays out and the holds the versioning invariant, the merge-main checks, what stays out and the
entry format. Always credit community contributions with entry format. Always credit community contributions with
+1 -1
View File
@@ -1 +1 @@
1.93.19.0 1.93.20.0
+1 -1
View File
@@ -1,4 +1,4 @@
# gstack digest v1.93.19.0 — regenerate/re-copy after upgrading gstack # gstack digest v1.93.20.0 — regenerate/re-copy after upgrading gstack
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
for agent hosts without a full skill install. The full skills add workflows, for agent hosts without a full skill install. The full skills add workflows,
+1 -1
View File
@@ -11,7 +11,7 @@
* gstack-factory-scorecard [--repo owner/name] [--base main] [--since-days 7] * gstack-factory-scorecard [--repo owner/name] [--base main] [--since-days 7]
* [--crew login,login] [--gate-workflow evals.yml] [--gate-event workflow_dispatch|any] * [--crew login,login] [--gate-workflow evals.yml] [--gate-event workflow_dispatch|any]
* [--free-workflow free-tests.yml] [--train-prefix train/] [--ready-marker <regex>] * [--free-workflow free-tests.yml] [--train-prefix train/] [--ready-marker <regex>]
* [--pass-marker <regex>] [--pass-cap 3] [--max-artifact-runs 60] * [--pass-marker <regex>] [--pass-cap 2] [--max-artifact-runs 60]
* [--out-dir <dir>] [--csv <file>] [--json] [--cache-dir <dir>] * [--out-dir <dir>] [--csv <file>] [--json] [--cache-dir <dir>]
* [--fixtures <recorded gh responses.json>] [--now <iso>] * [--fixtures <recorded gh responses.json>] [--now <iso>]
* *
+46 -42
View File
@@ -1,7 +1,9 @@
# CHANGELOG + VERSION style # CHANGELOG + VERSION style
Moved verbatim from CLAUDE.md (token-load reduction). Read this BEFORE 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 ## 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 policy (choose versions without asking; default to PATCH) stays in CLAUDE.md
as the single source. as the single source.
**Versioning invariant (workspace-aware ship).** VERSION is a monotonic ordered **Versioning invariant.** VERSION is a monotonic ordered release identifier,
release identifier, not a strict semver commitment. The bump level not a strict semver commitment. The bump level (major/minor/patch/micro)
(major/minor/patch/micro) expresses intent at ship time. Queue-advancing past a expresses intent. Versions are assigned at merge, in merge order: each stamp
claimed version within the same bump level is explicitly permitted — if branch A takes the version before it plus the effective bump, which is the policy's
claims v1.7.0.0 as a MINOR and branch B is also a MINOR, B lands at v1.8.0.0 PATCH raised by any folded fragment's `bump:` front matter. If PR A and PR B
(still a MINOR relative to main). Downstream consumers must NOT rely on both ask for MINOR and land back to back on v1.6.0.0, A lands at v1.7.0.0 and
"MINOR = feature-only, PATCH = fix-only" as a strict contract. This is why B at v1.8.0.0 (still a MINOR relative to the version before it). Downstream
`bin/gstack-next-version` advances within the chosen bump level rather than consumers must NOT rely on "MINOR = feature-only, PATCH = fix-only" as a
repicking the level when collisions happen. strict contract.
**package.json carries the npm-valid translation, not VERSION verbatim.** **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 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`) 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 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 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 `lib/version-source.ts` header; `test/gstack-version-bump.test.ts` pins the
contract. contract.
**VERSION and CHANGELOG are branch-scoped.** Every feature branch that ships gets its **The CHANGELOG entry is branch-scoped; the version is not.** Every feature
own version bump and CHANGELOG entry. The entry describes what THIS branch adds — branch that ships writes one entry as `changelog.d/<branch-slug>.md`: the entry
not what was already on main. 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 **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 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 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 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. 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:** **When to write the CHANGELOG entry:**
- At `/ship` time (Step 13), not during development or mid-branch. - At `/ship` time (Step 13), not during development or mid-branch.
- The entry covers ALL commits on this branch vs the base 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`, - `/ship` lints the fragment with `bin/gstack-prose-lint changelog.d/<branch-slug>.md --gate`.
and the Quality gate workflow gates the `## [Unreleased]` block the same way. Rewrite each `PROSE:` line Rewrite each `PROSE:` line it names ([docs/troubleshooting.md](troubleshooting.md#prose-lint-found)).
it names ([docs/troubleshooting.md](troubleshooting.md#prose-lint-found)). - Never fold new work into a CHANGELOG.md section that already landed on main.
- Never fold new work into an existing CHANGELOG entry from a prior version that New work goes in this branch's fragment, and the merge stamp gives it its own
already landed on main. If main has v0.10.0.0 and your branch adds features, section at the next version.
bump to v0.10.1.0 with a new entry — don't edit the v0.10.0.0 entry.
**Key questions before writing:** **Key questions before writing:**
1. What branch am I on? What did THIS branch change? 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.) 2. Does this change warrant more than a PATCH? (If yes, open the fragment with
3. Does an existing entry on this branch already cover earlier work? (If yes, replace `bump: minor` or `bump: major` front matter; otherwise add none.)
it with one unified entry for the final version.) 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 **Merging main never gives your branch a version.** When you merge origin/main into
a feature branch, main may bring new CHANGELOG entries and a higher VERSION. Your branch a feature branch, main may bring new CHANGELOG sections and a higher VERSION. Take them
still needs its OWN version bump on top. If main is at v0.13.8.0 and your branch adds exactly as main has them. Your branch still carries only its fragment, and never jams its
features, bump to v0.13.9.0 with a new entry. Never jam your changes into an entry that changes into a section that already landed on main; the merge stamp puts its section on top.
already landed on main. Your entry goes on top because your branch lands next.
**After merging main, always check:** **After merging main, always check:**
- Does CHANGELOG have your branch's own entry separate from main's entries? - Is your fragment still in `changelog.d/`, with its own text, separate from main's sections?
- Is VERSION higher than main's VERSION? - Are VERSION, package.json's version and every released CHANGELOG.md section exactly
- Is your entry the topmost entry in CHANGELOG (above main's latest)? 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. If any answer is no, fix it before continuing.
**After any CHANGELOG edit that moves, adds, or removes entries,** immediately run **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 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. 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 **Keep one entry per branch.** When the branch's work grows after its fragment was
during development (v1.5.1.0 → v1.5.2.0 → v1.6.4.0, say) and those earlier entries were written, rewrite that fragment as one unified entry instead of adding a second file
never released to main, the final ship consolidates ALL of them into a single entry at beside it. Readers see one release, not a branch diary.
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).
CHANGELOG.md is **for users**, not contributors. Write it like product release notes: 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. documents what DID ship, not what MIGHT ship.
- Version-bump housekeeping when no user-facing work actually landed. - 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 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 entry is one (only merges, only CHANGELOG edits, only placeholder work), the honest fragment is one
sentence: "Version bump for branch-ahead discipline. No user-facing changes yet." Stop sentence: "No user-facing changes yet." Stop
there. Do not pad. Do not explain the plan that will ship eventually. Do not narrate 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. the branch's history. When real work lands, the entry will replace this at /ship time.
+2 -2
View File
@@ -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 \ gstack-factory-scorecard --repo owner/name --since-days 7 \
[--crew login,login] [--gate-workflow evals.yml] [--free-workflow free-tests.yml] \ [--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+)'] \ [--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 `--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 | | 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, 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 | | 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 | | 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 | | 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) | | 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
View File
@@ -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 `.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. 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 gstack itself no longer follows these steps. Since #3654 (v1.91.342.0) its
running the merge queue: 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 1. **Open.** The branch carries a tentative stamp of the base's version plus
PATCH (`gstack-version-bump write --version <base+patch> --regen-digest`) PATCH (`gstack-version-bump write --version <base+patch> --regen-digest`)
+2 -2
View File
@@ -70,7 +70,7 @@ export interface ScorecardOptions {
export const DEFAULTS = { export const DEFAULTS = {
base: 'main', sinceDays: 7, gateWorkflow: 'evals.yml', gateEvent: 'workflow_dispatch', freeWorkflow: 'free-tests.yml', base: 'main', sinceDays: 7, gateWorkflow: 'evals.yml', gateEvent: 'workflow_dispatch', freeWorkflow: 'free-tests.yml',
trainPrefix: 'train/', readyMarker: 'READY #\\d+|SHIP_RECEIPT:|gstack-ship-receipt', trainPrefix: 'train/', readyMarker: 'READY #\\d+|SHIP_RECEIPT:|gstack-ship-receipt',
passMarker: '"passes"\\s*:\\s*(\\d+)|\\bpasses=(\\d+)|\\bREVIEW_PASS(?:ES)?[:= ]\\s*(\\d+)', passCap: 3, maxArtifactRuns: 60, passMarker: '"passes"\\s*:\\s*(\\d+)|\\bpasses=(\\d+)|\\bREVIEW_PASS(?:ES)?[:= ]\\s*(\\d+)', passCap: 2, maxArtifactRuns: 60,
} as const; } as const;
export const CREW_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']); export const CREW_ASSOCIATIONS = new Set(['OWNER', 'MEMBER', 'COLLABORATOR']);
@@ -399,7 +399,7 @@ export const BENCHMARKS: Record<string, string> = {
merged: 'several merges a day, every day; outside contributions land in the same week', merged: 'several merges a day, every day; outside contributions land in the same week',
lead_time: 'median under 24h, p90 under 3 days (DORA elite: change lead time under a day)', lead_time: 'median under 24h, p90 under 3 days (DORA elite: change lead time under a day)',
ready_to_merge: 'under 2h from READY to merge; every merged PR carries a READY comment', ready_to_merge: 'under 2h from READY to merge; every merged PR carries a READY comment',
review_passes: 'median 2 passes, none at the cap (a pass that finds nothing new is the exit; what is left at the cap is named in the PR body)', review_passes: 'median 1 pass, few at the cap (a pass that finds nothing blocking is the exit; a PR at the cap ships with what is left named in the PR body)',
gates: 'green on 95%+ of runs, cancelled near 0, cost known for every run', gates: 'green on 95%+ of runs, cancelled near 0, cost known for every run',
cost_per_merge: 'known, stable or falling week over week', cost_per_merge: 'known, stable or falling week over week',
red_train: 'under 5% of trains red (each red train costs a bisect and a rebuild)', red_train: 'under 5% of trains red (each red train costs a bisect and a rebuild)',
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "gstack", "name": "gstack",
"version": "1.93.19", "version": "1.93.20",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.", "description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
+1 -1
View File
@@ -59,7 +59,7 @@ describe('factory-scorecard: recorded window', () => {
expect(values.merged).toBe('3 (crew 2, outside 1)'); expect(values.merged).toBe('3 (crew 2, outside 1)');
expect(values.lead_time).toBe('median 8.0h, p90 5.0d'); expect(values.lead_time).toBe('median 8.0h, p90 5.0d');
expect(values.ready_to_merge).toBe('median 1.0h, p90 2.0h (2 of 3 PRs with a READY comment)'); expect(values.ready_to_merge).toBe('median 1.0h, p90 2.0h (2 of 3 PRs with a READY comment)');
expect(values.review_passes).toBe('median 2, p90 3 (2 of 3 PRs report a pass count; 1 at the cap of 3)'); expect(values.review_passes).toBe('median 2, p90 3 (2 of 3 PRs report a pass count; 2 at the cap of 2)');
expect(values.gates).toBe('2 / 1 / 1; known cost $43.00 on 2 of 4 runs'); expect(values.gates).toBe('2 / 1 / 1; known cost $43.00 on 2 of 4 runs');
expect(values.cost_per_merge).toBe('$14.33 (2 runs without a known cost)'); expect(values.cost_per_merge).toBe('$14.33 (2 runs without a known cost)');
expect(values.red_train).toBe('50% (1 red of 2 trains; 0 red backstop, 0 red screens)'); expect(values.red_train).toBe('50% (1 red of 2 trains; 0 red backstop, 0 red screens)');