mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-09 18:28:15 +01:00
Merge main e85005482 (v1.93.27.0, train 94) into wave/review-53cfb5bfe011 (rule 19 grant)
Conflicts resolved keeping both sides: bin/gstack-review-dispatch (main's targetedRead record plus the wave's exclude digest on the rendered event); bin/gstack-ship-receipt (main's receiptTargetedAtHead and targetedClassesSince plus the wave's receiptReviewAtHead and receiptExclude; both help paragraphs); docs/troubleshooting.md (main's targeted-refusal fix plus the wave's review_exclude, REVIEW_DIFF_TOO_LARGE, REVIEW_DIFF_UNREADABLE and REVIEW_EXCLUDE_REFERENCE entries); ship/DRIVER.md.tmpl (main's line, DRIVER.md 12,241 B); test/review-packets-removed.test.ts (both replay lists, the wave's placeholder brief-body hashes under main's GST-986 test). test/review-targeted.test.ts follows main's DRIVER wording ([--review-targeted @<f>]), which main's own copy of that check in test/review-targeted-record.test.ts pins.
This commit is contained in:
commit
feeec2e760
74 files changed
+1618
-316
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.
|
||||
|
||||
|
||||
+41
-4
@@ -89,7 +89,7 @@ the byte-pinned `/ship` renders in `test/fixtures/golden/`).
|
||||
| `timeout_s` | seconds each runner process may take (the unit-file group, each serial file, each guard without its own `timeout_s`, and the remote gate's deadline) when `--timeout` is not passed; 1 to 3600, default 600. `gstack-ship-preflight` and the `finish` fallback run pregate without `--timeout`, so this is how a repo with long lanes sets theirs |
|
||||
| `remote_gate` | the repo's remote test gate command; under 8 cores the local test tier becomes a `requires-remote` obligation naming it, and `--remote` runs it (see [remote gate](#remote-gate)) |
|
||||
| `run_profiles` | name → `{ workflow, events, jobs, artifact, max_age_hours?, os? }`: the GitHub Actions runs `clear --run` verifies as evidence on the identical tree; a profile named after a lane (`free-tests.yml/macos-named-regressions`) clears that lane (see [run evidence](#run-evidence)). `--from-run` and the main-push reuse job verify runs against them too ([from-run](#from-run), [CI reuse](#ci-reuse)) |
|
||||
| `remote_gate_runs` | `{ profiles, inventory }`: the run profiles that together clear `pregate.json/remote_gate`, and the command that prints the remote gate's test files, one per line, which their runs must cover (see [run evidence](#run-evidence)) |
|
||||
| `remote_gate_runs` | `{ profiles, inventory, skipped? }`: the run profiles that together clear `pregate.json/remote_gate`, the command that prints the remote gate's test files, one per line, which their runs must cover, and an optional command that prints which selected files the gate itself never runs (see [run evidence](#run-evidence)) |
|
||||
| `allowlist` | `[{ finding, paths?, pattern?, reason }]`: benign findings the `secrets` check accepts and names as allowlisted, by gstack-redact finding id or non-secret category, path globs and a pattern anchored at the finding; never `env.value` (see [allowlist](#allowlist)) |
|
||||
|
||||
Only the `origin/<base>` copy or the user-owned file below is honoured, so a
|
||||
@@ -228,6 +228,18 @@ Two stages with separate clocks, both printed:
|
||||
elsewhere in the file is not a consumer. A call-site name such as `attempts`
|
||||
or `limit` is an ordinary word, so for it the file must also name the changed
|
||||
module.
|
||||
A test that keeps the old value on purpose, such as the refused input after a
|
||||
minimum version goes up, marks that one line with a comment on it or on the
|
||||
comment line directly above it:
|
||||
`// pregate-literal: MIN_RUNTIME='1.4.0' refused input below the minimum`
|
||||
(`#` and `/*` comments work too). The marker waives only that line, and only
|
||||
when it names the changed constant and its old value and gives a reason (a
|
||||
reason cannot start with `*`, so a bare `*/`, `**/` or `* */` closer is not
|
||||
one); text inside a string is not a marker. Every other line in the file is still
|
||||
checked. The check lists each waived line as
|
||||
`waived: <file>:<line>: <NAME> <old> (<reason>)`, adding `marker added on
|
||||
this branch` when the merge-base file lacks the marker. The policy has no
|
||||
waiver key, so neither a base nor a pushed `pregate.json` can widen it.
|
||||
|
||||
- `timing`: added or changed test lines that compare an elapsed wall-clock or
|
||||
CPU time against a bound, or sleep and then assert, print
|
||||
@@ -742,6 +754,16 @@ Then, for the lane:
|
||||
while the working tree is pregate.json's tree (checked before and after it
|
||||
runs); an empty or failing inventory refuses, and it runs under `pipefail`,
|
||||
so a pipeline whose first command fails refuses too.
|
||||
- When `remote_gate_runs` declares `skipped`, that command runs the same way,
|
||||
with `PREGATE_FILES_FILE` naming a file of the obligation's selected test
|
||||
files, one per line, and prints the ones the remote gate itself never runs
|
||||
(gstack's gate leaves paid test files to the paid PR gate). Those files need
|
||||
no run, and the receipt counts them as `skipped=<n>`. A file the inventory
|
||||
prints stays required even when `skipped` names it, and a failing `skipped`
|
||||
command refuses (`skipped_failed`). When `skipped` names every selected file,
|
||||
no selected test ran, so the clear refuses (`paid_only`), just as the gate's
|
||||
own `tests=0` report never clears. Without `skipped`, every selected file
|
||||
must appear in a run's `files`.
|
||||
- Every run is read once more after the inventory: a run that moved to a new
|
||||
attempt or stopped being a success meanwhile refuses (`run_changed`).
|
||||
|
||||
@@ -753,13 +775,14 @@ verification is named with its own reason (`fork_run`, `event_mismatch`,
|
||||
`os_mismatch`, `api_error`, …); the clear-level codes are `base_mismatch`,
|
||||
`no_verified_run`, `tree_mismatch` (runs on different trees),
|
||||
`tree_not_stamp_only`, `no_tests`, `inventory_failed`, `inventory_empty`,
|
||||
`inventory_uncovered` (the remote gate) and `tests_uncovered` (a lane's own
|
||||
`skipped_failed`, `paid_only`, `inventory_uncovered` (the remote gate) and `tests_uncovered` (a lane's own
|
||||
selected tests). A wrong run count, a repeated run or a lane with no
|
||||
profile is a usage error (exit 2), not a REUSE refusal.
|
||||
|
||||
Accepted, `clear` prints `REUSE: accepted <lane> <receipt>`, and the receipt
|
||||
reads `remote-gate tree=<t12> tests=<n> runs=<id>/<attempt>,… profiles=<p>,…
|
||||
heads=<h12> verified=<time>` (with `accepted-for=` and the substitution for a
|
||||
heads=<h12> verified=<time>` (with `skipped=<n>` after `tests=` when the
|
||||
`skipped` command excused selected files, and `accepted-for=` and the substitution for a
|
||||
stamp-only predecessor). A lane with its own profile gets `run tree=… runs=…`
|
||||
the same way; for such a lane `--run-url` is verified as `--run` is, and
|
||||
`--bundle-lane` is refused. A lane with no profile still clears by
|
||||
@@ -804,7 +827,21 @@ every non-matrix job and the `free-tests` aggregate, `os` Linux) and
|
||||
aggregate, `os` Windows). Each accepts `pull_request` and `workflow_dispatch`
|
||||
runs and reads the `tested-tree` artifact, which both aggregates upload only
|
||||
after their strict verification passes. The main-push reuse job and
|
||||
`--from-run` read them. No lane has a profile, so every macOS and Windows lane
|
||||
`--from-run` read them, and `remote_gate_runs` names `ci-reuse/free-tests`,
|
||||
so a green Free Tests run at pregate.json's tree clears
|
||||
`pregate.json/remote_gate` without a Ubicloud VM:
|
||||
|
||||
```bash
|
||||
gstack-pregate clear --remote pregate.json/remote_gate --run <free-tests run id or url> --allow-repo-commands
|
||||
```
|
||||
|
||||
Its `inventory` is `scripts/test-free-shards.ts --list` (the complete free
|
||||
suite, the same set the Free Tests plan shards), and its `skipped` command
|
||||
prints the selected files `test/helpers/paid-test-set.ts` marks paid, the
|
||||
same split `scripts/ubicloud/pregate-remote.sh` makes before it runs. A
|
||||
`pull_request` run counts only when the branch already contains its base
|
||||
(the merge commit's tree is the head's), so a branch behind main still needs
|
||||
the gate or a fresh run. No lane has a profile, so every macOS and Windows lane
|
||||
still clears by `--run-url`, checked for its workflow file and a green
|
||||
conclusion. On main the free tier's Windows smoke lane is
|
||||
`windows-smoke.yml/windows-smoke`, and a `windows-smoke.yml` run clears it.
|
||||
|
||||
@@ -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) |
|
||||
|
||||
+22
-6
@@ -333,7 +333,8 @@ fix-checks run at `full` effort (the lane's reasoning); specialists run at
|
||||
batched specialist, which carries the Red Team prompt and stays `full`. The
|
||||
outside voice and the native adversarial pass carry the floor sentence and end
|
||||
each finding with `Severity:` and `Blocks: yes|no`; a finding is CRITICAL when
|
||||
its label rule says so (native FIXABLE, outside Critical or High) or it says
|
||||
its label rule says so (native FIXABLE, except as the collect step below
|
||||
relaxes it on a runtime-only code or docs plan; outside Critical or High) or it says
|
||||
`Blocks: yes`, and `Blocks: no` never lowers one (`gstack-review-dispatch collect`). Work that cannot block (the Step 7 coverage audit, the Step 8
|
||||
plan-completion audit, the Step 14.5 docs audit, decision-trail reviewers,
|
||||
watchers) runs at `light`: the same model at a lower reasoning effort, never a
|
||||
@@ -406,8 +407,19 @@ The brief states the blocking floor in one sentence, and its money block cites
|
||||
the checklist's Forced-CRITICAL rule on uncapped spend, so a money finding can
|
||||
block without new floor words. The reader is read-only, and `NO FINDINGS` is a
|
||||
result. `gstack-review-dispatch render --role targeted --classes <classes>`
|
||||
renders its brief; `verify --targeted-out <file>` writes the record that
|
||||
`gstack-ship-receipt write --review-targeted @<file>` puts in the receipt.
|
||||
renders its brief; `verify --targeted-out .gstack/tmp/targeted.json` writes the record that
|
||||
`gstack-ship-receipt write --review-targeted @.gstack/tmp/targeted.json` puts in the receipt
|
||||
(Step 19, the handoff and the driver pass it when the file exists). The record is `done` when
|
||||
the reader got its brief verbatim and its reply was collected on a committed diff, whatever
|
||||
happened to HEAD after, and it names the commit it read (`head`). `write` binds it only while
|
||||
that commit is an ancestor of HEAD, its product patch is the receipt's, and nothing changed
|
||||
after it carries a targeted class, with the gate scan run over that range: any product change
|
||||
after the read refuses with `RECEIPT_INVALID`, docs and TODOS.md commits included, and so
|
||||
does a test edit the gate scan tags (a skipped case). Every verify
|
||||
removes `--targeted-out` before it writes a record, so an earlier run's record never reaches
|
||||
Step 19. `write` and `carry` (which rescans the gate at the new head) also
|
||||
refuse a receipt whose head needs a class no `done` record covers (`RECEIPT_INVALID`,
|
||||
`RECEIPT_CARRY_REFUSED`). The queue refuses the same receipts at `ready` (#3859).
|
||||
`gstack-ship-queue ready`, `train build` and `land` print
|
||||
`REVIEW_TARGETED: required=<classes|none> receipt=<classes|none|legacy>
|
||||
verdict=ok|missing|failed` and refuse
|
||||
@@ -454,11 +466,11 @@ puts in the receipt:
|
||||
| `review_loop.noted` | below-floor findings listed in the PR body |
|
||||
| `review_loop.fixed` | findings fixed in this PR |
|
||||
| `review_loop.scope` | the `review_scope` the loop ran under |
|
||||
| `review_loop.fix_checks` | fix-checks this ship ran (never passes) |
|
||||
| `review_loop.fix_checks` | fix-checks this ship ran (never passes): the larger of the `review-fix-check` rows and the ledgers holding a fix-check reader's review. When fix-checks ran without their rows, a blocker no row disposes counts as fixed once a later fix-check ledger followed it (decision 3.5 runs one only after the fixes) |
|
||||
| `review_loop.rounds` | passes plus fix-checks (R51 caps it at 2; the queue's `REVIEW_DEPTH:` line prints it) |
|
||||
| `review_loop.dispatches` | reviewer dispatches by role: `specialist`, `red-team`, `adversarial-native`, `outside-voice`, `fix-check`, `targeted` |
|
||||
| `review_loop.est_input_tokens` | prompt plus diff bytes the dispatches read, divided by 4: a relative estimate for comparing runs, not a bill |
|
||||
| `review_targeted` | the [targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code): the `classes` it covered, `rules`, `brief_sha`, the receipt's own `product_patch_id`, `status` (`done` only with its output collected and the committed diff it was rendered on still the one at verify, else `failed`; absent when write got no record, which the queue reports as `legacy`), `findings` and `raised_by`; a carry keeps it while the product patch is unchanged |
|
||||
| `review_targeted` | the [targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code): the `classes` it covered, `rules`, `brief_sha`, the receipt's own `product_patch_id`, `status` (`done` only with its brief sent verbatim and its output collected on a committed diff, else `failed`; `write` refuses a record when a change after its read commit carries a targeted class or the read commit is not an ancestor, and refuses to write without a record when the head needs a class), `findings` and `raised_by`; a carry keeps it while the product patch is unchanged |
|
||||
|
||||
Each review row may carry `dispatches: [{role, effort, prompt_bytes,
|
||||
diff_bytes, status}]`, and each finding `raised_by` (the reviewer that raised it,
|
||||
@@ -589,7 +601,11 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
|
||||
second line for the same field starts another: `Severity` sets its label (a label leading the finding stays its floor),
|
||||
`Blocks: yes` makes it CRITICAL and `Blocks: no` never lowers it; an inline
|
||||
`Blocks: yes` is CRITICAL too, and with no finding above it is one. The native
|
||||
adversarial voice's FIXABLE collects as CRITICAL (normalizer v2, GSTA-167). An unnumbered, unlabeled bullet with no file:line
|
||||
adversarial voice's FIXABLE collects as CRITICAL (normalizer v2, GSTA-167) unless collect `--manifest` names this
|
||||
ledger's pass and its plan is the code or docs class with only `runtime=<n>` depth reasons (no `risk:`, `error:`,
|
||||
`history:` or `floor:` reason). On such a plan a native finding's own `Severity` field decides and FIXABLE beside it
|
||||
raises nothing once it also states `Blocks: no`; a native FIXABLE finding without `Blocks: no`, with no `Severity` field,
|
||||
or whose field names only FIXABLE or INVESTIGATE still collects as CRITICAL (GST-987). An unnumbered, unlabeled bullet with no file:line
|
||||
under a paragraph finding whose Severity or Blocks line has not come yet is that finding's detail, so the field line after it
|
||||
lands on the finding; a numbered item is its own finding.
|
||||
A negated severity word still collects at its label ("found nothing CRITICAL", "No P0/P1 issues"), so a reader's
|
||||
|
||||
+20
-10
@@ -113,10 +113,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`)
|
||||
@@ -1234,13 +1240,17 @@ both values; prose that quotes no counter is not a claim. The `## Approvals`
|
||||
block `gstack-approvals` renders (its heading, the Date | Asked | Exact words
|
||||
table and the `Source: gstack-approvals record` line) quotes the owner's
|
||||
words, not this PR's numbers, so it is skipped: "one Step 11 pass" in that table
|
||||
is not a `pass` claim. Text you write under the heading after that block, and
|
||||
a `## Approvals` example inside a code fence, are still checked. The
|
||||
`Review-loop exception: …` line the receipt's own exception requires is
|
||||
skipped the same way (GST-977): it quotes the owner's reason, so "outside 1 and
|
||||
2 skip detector" is not a `2 skip` claim. Only a line that matches the rendered
|
||||
line exactly is skipped, inside a code fence too, as the receipt check accepts
|
||||
a fenced copy; the same words inside a longer line are still checked. Data PRs (evals,
|
||||
is not a `pass` claim. Text you write under the heading after that block is
|
||||
still checked. The `Review-loop exception: …` line (GST-977) and the
|
||||
`Documentation exception: …` line (GST-983) the receipt's own exceptions
|
||||
require are skipped the same way: they quote the owner's reason, so "outside 1
|
||||
and 2 skip detector" is not a `2 skip` claim. Only a line that matches the
|
||||
rendered line exactly is skipped; the same words inside a longer line are still
|
||||
checked. A closed code fence is skipped too (GST-984): pasted test output
|
||||
("113 pass, 0 fail") and quoted code are evidence, not this PR's counters, so
|
||||
put a count you mean as a claim outside the fence. A line like ```` ```npm test``` ran ````
|
||||
opens no fence, and a fence that never closes stays prose, along with the rest
|
||||
of the body. Data PRs (evals,
|
||||
measurements, sweeps) run it before READY so the body's numbers are the run's.
|
||||
|
||||
<a id="review-exclude-paths"></a>
|
||||
|
||||
+25
-7
@@ -3320,10 +3320,19 @@ for an older PR, write it once by hand with the same command.
|
||||
**Meaning.** The block exists but does not validate against the `ship-receipt`
|
||||
schema (`gstack-artifact schema ship-receipt`). `gstack-ship-receipt write`
|
||||
also refuses with this code, and writes nothing, when `--head` is not the
|
||||
checked-out HEAD: the receipt binds the tree the review read.
|
||||
checked-out HEAD: the receipt binds the tree the review read. It refuses the
|
||||
same way when the head touches money, gate, auth or secret code and no done
|
||||
targeted record covers those classes (`REVIEW_TARGETED: … verdict=missing|failed`),
|
||||
or when `--review-targeted` names a read commit whose product patch is not
|
||||
the receipt's and a change after it carries a targeted class (money, gate,
|
||||
auth or secret code changed after the targeted reader read it, a test edit the
|
||||
gate scan tags included), or that is not an ancestor of HEAD.
|
||||
|
||||
**Fix.** Regenerate it with `gstack-ship-receipt write`; never hand-edit the
|
||||
block. For the `--head` refusal, check out that commit and write there.
|
||||
block. For the `--head` refusal, check out that commit and write there. For a
|
||||
targeted refusal, run the targeted reader on HEAD, `gstack-review-dispatch verify
|
||||
--targeted-out .gstack/tmp/targeted.json`, and write again with
|
||||
`--review-targeted @.gstack/tmp/targeted.json`.
|
||||
|
||||
**`field=review_exclude`.** `gstack-ship-receipt write` and the queue's receipt check also
|
||||
use this code when the receipt's review_exclude digest (or the
|
||||
@@ -3767,7 +3776,12 @@ directory, or under a scratch name (`zz-*`, `tmp*`, `scratch*`).
|
||||
appears with its old value in a test file the branch did not update — nightly-
|
||||
and Heavy-only files included.
|
||||
|
||||
**Fix.** Update every listed `file:line`, or keep the old value.
|
||||
**Fix.** Update every listed `file:line`, or keep the old value. When a test
|
||||
keeps the old value on purpose (the refused input after a minimum version goes
|
||||
up), mark that line: `// pregate-literal: NAME=<old value> <reason>` on the line
|
||||
or on the comment line directly above it. The marker waives only that line, and
|
||||
the check lists it as `waived:`. Don't use `warn: ["literals"]` for this, since
|
||||
that turns the check off for every constant.
|
||||
|
||||
<a id="pregate-no-lane"></a>
|
||||
### `PREGATE_NO_LANE`
|
||||
@@ -5910,7 +5924,10 @@ working tree's status. It also refuses when an owner exception the receipt recor
|
||||
no longer stands, and, for a receipt from a gstack that records review
|
||||
depth, when HEAD needs a deeper review than the receipt records, `--head` is
|
||||
not the checked-out HEAD, or the receipt records `gstack_version` without
|
||||
`review_depth` ([ship-policy.md](ship-policy.md#ship-receipt)). The receipt in
|
||||
`review_depth` ([ship-policy.md](ship-policy.md#ship-receipt)). It refuses, too, when
|
||||
HEAD needs a targeted class (money, gate, auth, secret) that the receipt's
|
||||
`review_targeted` does not cover with a done pass, since the queue would refuse
|
||||
that receipt. The receipt in
|
||||
the body is unchanged and still names the old head, so `gstack-ship-queue
|
||||
ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
|
||||
@@ -5918,7 +5935,7 @@ ready` refuses `QUEUE_RECEIPT_STALE`.
|
||||
`gstack-ship-receipt carry --pr <n>` at the new head. When the history is
|
||||
already rewritten and the receipt predates `head_tree`, run /ship on the new
|
||||
head; next time merge the base in instead of rebasing. For a retargeted stacked
|
||||
PR whose older receipt lost its parent branch, or a depth or exception
|
||||
PR whose older receipt lost its parent branch, or a depth, targeted or exception
|
||||
refusal, run /ship on the new head. When `--head` is not the checked-out HEAD,
|
||||
check it out and carry there.
|
||||
|
||||
@@ -6013,8 +6030,9 @@ body that names a counter the receipt records (`passes=3` against
|
||||
against `gate.passed: 41`) with a different value. The receipt is written from
|
||||
the run; the prose was typed, so a reader of the body would learn a number the
|
||||
run did not produce. The `PROSE:` line names each field with both values. The
|
||||
`## Approvals` table `gstack-approvals` renders, and the `Review-loop exception: …`
|
||||
line the receipt's exception requires, are never read as claims.
|
||||
`## Approvals` table `gstack-approvals` renders, the `Review-loop exception: …`
|
||||
and `Documentation exception: …` lines the receipt's exceptions require, and
|
||||
the inside of a closed code fence are never read as claims.
|
||||
|
||||
**Fix.** Correct the body to the receipt's number. When the receipt is the
|
||||
stale one (the run was repeated), rerun `/ship` so the receipt is rewritten
|
||||
|
||||
Reference in new issue
Block a user