mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-13 03:48:06 +01:00
Merge main 3d99c224 (v1.92.76.0) into the browse wave (rule 19 grant, GSTA-36)
Resolution: test/fixtures/context-budget.json keeps main's capy/skill/gstack ceiling (5315). To fit it, #3700's GSTACK_MODEL/SKILL_ROOT sentence leaves capy/skill/gstack/SKILL.md (approved cut; GST-982 moves model pickup into the Capy installer). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
commit
c2bb6e5c28
64 files changed
+2552
-208
No files matched your search
@@ -180,9 +180,12 @@ 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 (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
|
||||
already-qualified ref is used as given, and a branch name resolves through
|
||||
its remote-tracking twin (`origin/main` for `main`), so a stale local `main`
|
||||
never adds commits the branch only inherited (GST-883). Only when no remote
|
||||
has the branch does the local ref stand, noted once on stderr (a repository
|
||||
with no remote at all uses it quietly). Branches are read by full ref name,
|
||||
so a tag named `main` or `origin/main` cannot stand in for them. 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`).
|
||||
|
||||
+77
-15
@@ -383,7 +383,7 @@ The record is `.gstack/tmp/pregate.json` (`gstack-artifact validate --as pregate
|
||||
(`PREGATE_STALE`: rerun after the final stamp), green (`PREGATE_LANE_FAILED`,
|
||||
`PREGATE_INCOMPLETE`, or `PREGATE_ENVIRONMENT` when every incomplete check is
|
||||
the machine's) and every `requires-remote` obligation carries a receipt
|
||||
(`PREGATE_REMOTE_UNCLEARED`: dispatch the job, then
|
||||
(`PREGATE_REMOTE_UNCLEARED`: dispatch the job, wait for it to finish green, then
|
||||
`gstack-pregate clear --remote <workflow.yml/job> --run-url <url>`). The ship
|
||||
receipt then carries `pregate` and `requires_remote`. `gstack-pregate verify`
|
||||
runs the same check standalone.
|
||||
@@ -754,17 +754,24 @@ reads `remote-gate tree=<t12> tests=<n> runs=<id>/<attempt>,… profiles=<p>,…
|
||||
heads=<h12> verified=<time>` (with `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 keeps today's `--run-url`
|
||||
recording, with one check (GSTA-203): when the base config and the GitHub
|
||||
`--bundle-lane` is refused. A lane with no profile still clears by
|
||||
`--run-url`, with run checks (GSTA-203): when the base config and the GitHub
|
||||
origin can be read, `clear` reads the run once and refuses
|
||||
`PREGATE_RECEIPT_INVALID` unless it is a run of this repository whose
|
||||
workflow file is the lane's own `<workflow.yml>`. A run of another workflow
|
||||
refuses with `REUSE: refused reason=workflow_mismatch — run <id> attempt <n>
|
||||
ran .github/workflows/<other>.yml, not .github/workflows/<lane's>.yml`; a
|
||||
run gh cannot read refuses with `api_error`, and a URL that is not an Actions
|
||||
run URL or id on this repository with `run_ref_invalid`. Only the workflow
|
||||
file is checked there, not the job, conclusion or tree; that is what a run
|
||||
profile adds. `test_backend/<backend>` lanes and `--bundle-lane` are
|
||||
run URL or id on this repository with `run_ref_invalid`. The run must also
|
||||
have finished green (GSTA-203 PR 8): a run that is still queued or running
|
||||
refuses with `run_in_progress`, and one that concluded anything but `success`
|
||||
(`failure`, `cancelled`, `timed_out`, …) with `run_not_success`; each fix line
|
||||
says to rerun the workflow and clear with the green run. The check reads the
|
||||
run's newest attempt, so a `/job/<id>` or `/attempts/<n>` link is judged by
|
||||
the whole run, not that job or attempt, and the lane records
|
||||
`https://github.com/<repo>/actions/runs/<id>/attempts/<n>` for the attempt it
|
||||
judged rather than the link you passed. The job, tree and age are not
|
||||
checked there; that is what a run profile adds. `test_backend/<backend>` lanes and `--bundle-lane` are
|
||||
unchanged. When the base config or the GitHub origin cannot be read, `--run`
|
||||
fails, and so do `--run-url` and `--bundle-lane` unless the base was read and
|
||||
shows the lane has no profile; then the lane is recorded as before, with a
|
||||
@@ -781,10 +788,15 @@ 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
|
||||
still clears by `--run-url`, checked only for its workflow file. On main the
|
||||
Windows obligation is `windows-smoke.yml/windows-smoke` (the six-shard
|
||||
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.
|
||||
Two narrower Windows lanes are owed only when a branch selects their own test
|
||||
files: `free-tests.yml/cso-windows-launcher` (cleared by a `free-tests.yml`
|
||||
run) and `windows-setup-e2e.yml/windows-setup` (cleared by a
|
||||
`windows-setup-e2e.yml` run). The six-shard
|
||||
`windows-free-tests.yml/windows-free-shard` job is `manual`, so it is never an
|
||||
obligation), and a `windows-smoke.yml` run clears it. A branch cut before
|
||||
obligation. A branch cut before
|
||||
`windows-smoke.yml` landed still owes `windows-free-tests.yml/windows-free-shard`
|
||||
from its own workflows, and only a `windows-free-tests.yml` run clears that. A
|
||||
pull request cut before these
|
||||
@@ -1093,7 +1105,8 @@ with a timing flake: never raise the bound.
|
||||
"answer_markers": ["SEALED ANSWER:"],
|
||||
"seed_patterns": ["shuffle_seed=\\d+"],
|
||||
"local_paths": true,
|
||||
"case_insensitive": true } }
|
||||
"case_insensitive": true,
|
||||
"label_keys": ["stratum", "decoy_set"] } }
|
||||
```
|
||||
|
||||
A repo with a `sealed` key gets a configured local check against accidental
|
||||
@@ -1139,11 +1152,60 @@ gstack-pregate sealed [--range <a..b>] [--remote origin] [--base <branch>] [--js
|
||||
sealed data. Each exempt path is listed on every push (`exempt <rule>
|
||||
<file> commit <sha> (sealed.allow_paths <glob>)`) and counted as
|
||||
`exempt=<n>` on the `SEALED:` line.
|
||||
- **Version skew.** A gstack older than the release whose CHANGELOG adds
|
||||
`sealed.allow_paths` refuses `allow_paths` and `case_insensitive` as
|
||||
unknown keys, so every push and `gstack-pregate run` in the repository
|
||||
fail on that machine. Raise `min_gstack_version` in
|
||||
`.gstack/ship-policy.json` to that release before landing either key.
|
||||
- **Label joins.** Off unless `label_keys` names at least one field. Then
|
||||
`sealed.label_join` refuses a published record that puts a label field next
|
||||
to an identifier, the way a panel row with `stratum` beside `head` and
|
||||
`plan_bytes` tells a model under test which unit it is. A record is a JSON
|
||||
object at any depth (its own fields plus one level of nested objects, named
|
||||
like `meta.head`), a JSONL or NDJSON line, a CSV or TSV row, or a row of a
|
||||
Markdown pipe table (CSV and TSV cells may be quoted). Field names and table headers are compared after
|
||||
trimming, lowercasing and turning spaces and hyphens into underscores
|
||||
(`Tree SHA-256` is `tree_sha_256`); this is the rule's own normalization,
|
||||
separate from `case_insensitive`. An identifier is a field whose name
|
||||
matches an `identifier_keys` glob (`*` is the only wildcard; the default
|
||||
is `head`, `head_sha`, `sha`, `commit`, `tree`, `tree_sha*`, `*_sha`,
|
||||
`*_sha256`, `sha256`, `digest`, `*_digest`, `hash`, `*_hash`,
|
||||
`*_bytes`, `bytes`, `size`, `files`, `lines`), or any string value (or a
|
||||
list holding one) of 7 to 64 hex characters with at least one digit and
|
||||
one letter a to f. An
|
||||
object key of that shape counts as an identifier of the object it holds
|
||||
(shown as `(key)`), so a map keyed by sha joins too. Numbers count only
|
||||
through the field name. A field can be both a label and an identifier, and
|
||||
a record joins when it has a label field and a different identifier field,
|
||||
so adding identifier names to `label_keys` never hides a join. Both lists take bare field names: a nested field
|
||||
matches by its own name (`stratum`, never `meta.stratum`). For each supported file a commit adds,
|
||||
modifies, renames or copies, the guard fingerprints every joined record
|
||||
(sha256 over its label and identifier fields and their values, kept in
|
||||
memory) and compares the set with the parent's version of the file (the
|
||||
rename or copy source; every parent for a merge). A fingerprint no parent
|
||||
has is a finding, so unchanged records and formatting-only edits stay clear,
|
||||
and a replaced, added or widened record refuses even when the count stays
|
||||
the same. A file whose text never names a label key, ignoring double
|
||||
quotes (so `"Strat"um` still names `stratum`), cannot hold a join, so
|
||||
it is skipped without parsing (a commented `tsconfig.json` passes). A file
|
||||
with a `\u` escape, which could spell a label key, is always parsed, so a
|
||||
commented one is `sealed.label_join_unchecked`. A
|
||||
supported file over 4 MiB, one that names a label key and does not parse (one bad
|
||||
JSONL line is enough), one nested deeper than 64 levels, or a blob git
|
||||
cannot read is `sealed.label_join_unchecked`. It refuses too, and the
|
||||
`SEALED:` line counts it as `unchecked=<n>`. Findings print field names
|
||||
(`keys=`, label fields first), never values; `--json` carries them as
|
||||
`keys`. `allow_paths` never exempt either rule; a base-branch waiver does.
|
||||
Both lists are the union of the base branch's and the pushed policy's, each
|
||||
resolved to its default first, so a push can turn the rule on or widen it
|
||||
but never turn it off or narrow it. Without `label_keys` the guard reads no
|
||||
file contents and its output is unchanged. Known limits: joins across files,
|
||||
joins by reference inside a file (a `decoy_set` list naming units described
|
||||
elsewhere), a map from an identifier straight to a label value with no
|
||||
label field name, columnar JSON (`columns` plus `rows`), labels encoded in
|
||||
file names, and prose.
|
||||
- **Version skew.** A gstack older than the release that adds a `sealed` key
|
||||
refuses it as unknown, so every push and `gstack-pregate run` in the
|
||||
repository fail on that machine. `allow_paths` and `case_insensitive` came
|
||||
with the release whose CHANGELOG adds `sealed.allow_paths`; `label_keys`
|
||||
and `identifier_keys` came with the release whose CHANGELOG adds
|
||||
`sealed.label_join`. Raise `min_gstack_version` in
|
||||
`.gstack/ship-policy.json` to that release before landing the key.
|
||||
- **Policy.** The union of the destination base branch's `sealed` and the
|
||||
pushed commit's: a push can tighten it, never relax it (`allow_paths` come
|
||||
from the base branch alone, and a pushed deny glob cannot override one). The
|
||||
|
||||
+35
-16
@@ -181,7 +181,14 @@ follows that file, four files deep. Precedence: agent prose (`.gstack/**`), a te
|
||||
fixture path, a gate path, a config file (`.github/**`, anything under a `config/`
|
||||
directory, dotfiles, `*.config.*`, `package.json`, lockfiles, `tsconfig*.json`,
|
||||
`manifest.json` and the other tool manifests `lib/review-depth.ts` lists) and a named
|
||||
consumer each keep the path code, and YAML, TOML and SVG are always code. More than
|
||||
consumer each keep the path code, and YAML and TOML are always code. An SVG counts
|
||||
as data only when its whole text at the head is a drawing (GST-911): SVG drawing
|
||||
elements and RDF metadata, CSS made of declarations only (no `@` rule and no
|
||||
function but a same-file `url(#id)`, which closes `@import`, `image-set()`,
|
||||
`image()`, `element()` and every other CSS load), no backslash escape, every
|
||||
`href` a same-file `#` reference or an inline raster image, no event handler and no
|
||||
entity declaration, and no DOCTYPE but a bare `<!DOCTYPE svg>` or W3C's own SVG DTD, read after character references are decoded; anything else, a
|
||||
script or `foreignObject` included, keeps it code. More than
|
||||
2,000 data paths keep every one code; a `git grep` failure is `consumer_scan_failed`
|
||||
and fails the depth closed. Only `classifyDiff` applies the rule (`lib/review-collect.ts`
|
||||
finds the consumers, `gstack-ship-preflight`'s `RUNTIME_LINES` reads the same
|
||||
@@ -215,23 +222,25 @@ input, redirect toggles, proxy settings and host allowlists, archive extraction
|
||||
and path traversal (`tar.x`, adm-zip, yauzl, extract-zip, `extractall`, `tar -x`,
|
||||
`unzip`, `path.join` or `resolve` on request or entry input, a `'..'` check added
|
||||
or removed), and any changed line that names the `child_process` or
|
||||
`node:child_process` module, in any import or require spelling. The GST-850
|
||||
`node:child_process` module, in any import or require spelling. A changed SVG
|
||||
line that can run code (a script or `foreignObject` element, an event handler, a
|
||||
`javascript:` URL) is security too (GST-911). The GST-850
|
||||
rules are coarse on purpose and parse no import clause or call arguments;
|
||||
`test/review-risk-linear.test.ts` holds each of them under 100 ms on
|
||||
200,000-character hostile lines, its anchor followed by a long run of the
|
||||
characters it repeats included. That claim covers only the rules GST-850 adds,
|
||||
not R51's older ones. **data** is user data: a path naming pii, gdpr, ccpa, privacy,
|
||||
characters it repeats included. `test/review-risk-redos.test.ts` (GST-898) holds every risk pattern, R51's `rm` and `rmSync` rules included, under 100 ms on 100,000-character hostile lines. An `rmSync(` call with more than 500 characters before the first `)` after it counts as security on its own. **data** is user data: a path naming pii, gdpr, ccpa, privacy,
|
||||
personal, account, customer, cookie, persistence, storage or database, or a line
|
||||
that drops a table or column, deletes rows or bulk-deletes records. Auth scope,
|
||||
migrations and deleted code paths were already gate class. `RISK_RULES` hashes
|
||||
the patterns and `DEPTH_RULES` includes it.
|
||||
|
||||
**UI text and SVGs** stay code (light unless a risk class applies). A docs path
|
||||
for UI copy, locale values and chart SVGs was built as an allowlist in GST-850 and
|
||||
dropped for review cost: its first review round found a new bypass class in each
|
||||
version, the same pattern as three rounds of R51. When a diff publishes numbers,
|
||||
the batched specialist's diff keeps the write-up's text files instead of leaving
|
||||
docs out.
|
||||
**UI text** stays code (light unless a risk class applies). A docs path for UI
|
||||
copy and locale values was built as an allowlist in GST-850 and dropped for
|
||||
review cost: its first review round found a new bypass class in each version,
|
||||
the same pattern as three rounds of R51. Chart SVGs use the drawing allowlist
|
||||
above, which reads the whole file rather than changed lines. When a diff
|
||||
publishes numbers, the batched specialist's diff keeps the write-up's text files
|
||||
instead of leaving docs out.
|
||||
|
||||
**Batched checklists (the lean ensemble, GSTA-167 part 2).** One specialist
|
||||
subagent reads Testing and Security on every code or gate diff, plus
|
||||
@@ -350,7 +359,7 @@ and matching is token-bounded: `costume` never reads as cost.
|
||||
|---|---|
|
||||
| money | a path segment naming cost, spend, budget, ledger, billing, price, fee or invoice; or a changed line with an identifier ending in `_usd` or `Usd`, any `spend_cap` spelling, or a call to a paid provider `lib/pricing.ts` prices |
|
||||
| gate | a hunk the gate scan tagged: a relaxed threshold, a skipped test, a loosened check |
|
||||
| auth | `SCOPE_AUTH` from `gstack-diff-scope`, on a code path with an auth, session, jwt, permission or role token in its name |
|
||||
| auth | `SCOPE_AUTH` from `gstack-diff-scope`, on a code path with an auth, session, jwt, permission or role token in its name; a data file named for sessions (`recovered-sessions.jsonl`) outside a config location does not set `SCOPE_AUTH` |
|
||||
| secret | a secrets, credentials, keychain or token path, or a changed line that reads a key-named environment variable (`OPENAI_API_KEY`, `GH_TOKEN`) or a credential store |
|
||||
|
||||
An input the classifier cannot read (the diff, `gstack-diff-scope`, the gate
|
||||
@@ -387,14 +396,17 @@ narrowed to the gate scan's tagged hunks.
|
||||
|
||||
## What the receipt records
|
||||
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE>` reads this invocation's
|
||||
review-log rows and writes the counters `gstack-ship-receipt write --review-loop`
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE> --run <run dir>` reads this invocation's
|
||||
review-log rows and the dispatch ledgers under the run directory (`--ledger <f>` names one;
|
||||
`--no-ledger` when no reader ran through `gstack-review-dispatch`) and writes the counters `gstack-ship-receipt write --review-loop`
|
||||
puts in the receipt:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `review_loop.passes` | full review passes this ship ran (1 or 2; up to 3 under `review_scope: all`) |
|
||||
| `review_loop.blocking_found` | distinct blocking findings across the passes |
|
||||
| `review_loop.blocking_found` | every blocker found (GST-951): the rows' blocking findings (classified, or an unclassified CRITICAL), plus each ledger CRITICAL (as collected or judged) that no row finding from the same reviewer at the same path:line accounts for (each row finding pairs with one per ledger), so an adversarial, outside-voice or not-yet-classified blocker counts and one whose newest classification is below the floor does not. An unreadable ledger, merged file or judged set, or a judged set changed since `judge`, is `REVIEW_SCOPE_LEDGER`, never a count |
|
||||
| `review_loop.unrecorded` | blockers found that no row gives a disposition (fixed, deferred, noted, skipped, a gate answer); absent when 0 |
|
||||
| `review_loop.ledgers_read` | dispatch ledgers the count read; absent under `--no-ledger` |
|
||||
| `review_loop.deferred` | blocking findings in unchanged code filed as issues (a Step 11 late defer, logged as a `review-late-defer` row so it never displaces the newest `review` pass, counts here and is not a pass) |
|
||||
| `review_loop.noted` | below-floor findings listed in the PR body |
|
||||
| `review_loop.fixed` | findings fixed in this PR |
|
||||
@@ -593,7 +605,14 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
|
||||
`COLLECT_WRONG_TREE`, a later commit in the same checkout `COLLECT_TREE_MOVED`; nothing
|
||||
is merged, the reader becomes `failed`, and `verify` reports `NOT_COLLECTED`. An
|
||||
outside voice's tree failure is recorded `unavailable` only when its reply holds no finding. The line catches a reader that
|
||||
worked elsewhere by accident; it is not proof of reading, since diff text can predict it.
|
||||
worked elsewhere by accident; it is not proof of reading, since diff text can predict it. A `printed:` label
|
||||
before the line (plain, bold or inside its backticks) is still read, and its tree and toplevel are still compared
|
||||
exactly (GST-776). Capy cannot start a subagent in a chosen directory, so before a stage takes a start token or sends
|
||||
its first reader, the Capy pass program compares the checkout with the one shared readers start in (the thread's
|
||||
working directory) and stops when that is another checkout or cannot be read: run /ship in the thread's own checkout.
|
||||
A start directory outside any checkout, such as a multi-repo workspace root, holds no tree to review by mistake and
|
||||
dispatches. Step 11's voices take the `adversarial-review` start
|
||||
token by default, and a `logStart` list without it stops before anything runs.
|
||||
|
||||
## Where this is enforced
|
||||
|
||||
@@ -604,7 +623,7 @@ ones, and the arm lost blockers. `bin/gstack-review-dispatch` closes both ends:
|
||||
- `ship/sections/review-army.md` Step 9: the plan's `REVIEW_DEPTH`, `GATE_SCAN` and `--depth-floor` on every run, the depth fields of the review-log row, passes, `RANGE_BASE`, classify, defer, render, the cap check before any edit, the Step 11 late-defer row, scratch files under `.gstack/tmp/`, the cap decisions; `ship/sections/adversarial.md` Step 11: the outside voice reads the same range and scopes queued fixes the same way.
|
||||
- `lib/review-depth.ts`, `bin/gstack-review-plan`: the diff class, the depth (`depthFor`, `DEPTH_RULES`, `depthOfCommitInput`), each dispatch's `exclude` pathspecs and `DIFF_CMD`, the transition table, the numeric-claim scan and each role's effort tier (`test/review-depth.test.ts`, `test/review-plan-cli.test.ts`).
|
||||
- `lib/review-collect.ts`: `collectCommit` (git objects only, from any checkout) and `collectLive` (the working tree), per-path lines and the base commit's registry (`test/review-collect.test.ts`).
|
||||
- `lib/review-scope.ts`, `bin/gstack-review-scope`: `classify`, `defer`, `render`, `summary` (with `fix_checks`, `dispatches`, `est_input_tokens`); refusals are `REVIEW_SCOPE_INPUT` and `REVIEW_SCOPE_RANGE` ([troubleshooting.md](troubleshooting.md#review-scope-input)).
|
||||
- `lib/review-scope.ts`, `bin/gstack-review-scope`: `classify`, `defer`, `render`, `summary` (with `fix_checks`, `dispatches`, `est_input_tokens`, `unrecorded`, `ledgers_read`); refusals are `REVIEW_SCOPE_INPUT`, `REVIEW_SCOPE_RANGE` and `REVIEW_SCOPE_LEDGER` ([troubleshooting.md](troubleshooting.md#review-scope-input)).
|
||||
- `lib/ship-policy.ts`: the `review_scope` key and its `ship: review_scope …` consequence line.
|
||||
- `review/checklist.md`: `prereg-with-results` under Gate Integrity and `test-keyboard-count-loop` under Pass 2; `bin/gstack-coauthor-trailers` for the contributor-credit trailers of a port.
|
||||
- `lib/ship-receipt-prose.ts`, `gstack-ship-receipt read --check-prose`: the body's quoted counters against the receipt (`RECEIPT_PROSE_MISMATCH`, [ship-policy.md](ship-policy.md#ship-receipt)).
|
||||
|
||||
+7
-6
@@ -1268,11 +1268,11 @@ construction:
|
||||
the run and names the finding (path, severity, problem) for the owner instead
|
||||
of a third round; the owner may grant one scoped fix-check.
|
||||
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE>` reads the branch's review-log
|
||||
rows for this invocation and writes the counters the ship receipt carries
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE> --run <run dir>` reads the branch's review-log
|
||||
rows for this invocation and its dispatch ledgers, and writes the counters the ship receipt carries
|
||||
(`gstack-ship-receipt write … --review-loop @<file>`): `review_loop: { passes,
|
||||
blocking_found, deferred, noted, fixed, scope }`, plus `fix_checks`, `dispatches`
|
||||
and `est_input_tokens` when the run recorded them, printed on the receipt summary
|
||||
blocking_found, deferred, noted, fixed, scope }`, plus `fix_checks`, `dispatches`,
|
||||
`est_input_tokens`, `unrecorded` and `ledgers_read` when the run recorded them, printed on the receipt summary
|
||||
line as `passes=<n> blocking_found=<k> deferred=<m>`, so a scorecard can track
|
||||
passes per PR.
|
||||
|
||||
@@ -1286,8 +1286,9 @@ instead of `defer`. `review_scope: all` is the opt-back to the earlier loop:
|
||||
every finding is `scope: fix` on every pass, reviewers read the whole diff each
|
||||
time, and only the three-cycle fix cap stops the loop. `show-effective` prints the choice as a `ship: review_scope …`
|
||||
line, and the codes a refusal ends with are
|
||||
[`REVIEW_SCOPE_INPUT`](troubleshooting.md#review-scope-input) and
|
||||
[`REVIEW_SCOPE_RANGE`](troubleshooting.md#review-scope-range).
|
||||
[`REVIEW_SCOPE_INPUT`](troubleshooting.md#review-scope-input),
|
||||
[`REVIEW_SCOPE_RANGE`](troubleshooting.md#review-scope-range) and
|
||||
[`REVIEW_SCOPE_LEDGER`](troubleshooting.md#review-scope-ledger).
|
||||
|
||||
<a id="base-red"></a>
|
||||
## `base_red`: pausing /ship on a red base
|
||||
|
||||
+1
-1
@@ -1119,7 +1119,7 @@ Claude: Week of Mar 1: 47 commits (3 contributors), 3.2k LOC, 38% tests, 12 PRs,
|
||||
[Top 3 team wins, 3 things to improve, 3 habits for next week]
|
||||
```
|
||||
|
||||
It saves a JSON snapshot to `.context/retros/` so the next run can show trends.
|
||||
It saves a JSON snapshot under `~/.gstack/projects/<slug>/retros/`, shared by every worktree of the repository, so the next run can show trends from any checkout. An older `.context/retros/` is moved there on the first run.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+36
-4
@@ -3651,7 +3651,7 @@ base branch's copy or the user-owned file. For the user-owned file, edit it
|
||||
directly at the path the message names. A user file inside the repository, or
|
||||
reached through a symlink into it, is ignored rather than read (`--explain`
|
||||
names it). An unknown `sealed` key that a newer gstack defines (such as
|
||||
`allow_paths` or `case_insensitive`) means this machine's gstack is older than
|
||||
`allow_paths`, `case_insensitive`, `label_keys` or `identifier_keys`) means this machine's gstack is older than
|
||||
the policy: upgrade gstack here, and raise `min_gstack_version` in the
|
||||
repository's `.gstack/ship-policy.json` before landing such a key.
|
||||
|
||||
@@ -3848,6 +3848,15 @@ reads `gstack-pregate clear: receipt refused — REUSE: refused reason=<code>
|
||||
branch's `run_profiles`. Each reason code and
|
||||
what it checks is listed in [run evidence](pregate.md#run-evidence).
|
||||
|
||||
`gstack-pregate clear --remote <workflow.yml/job> --run-url <url>` on a lane
|
||||
with no run profile refuses with this code too, reading `REUSE: refused
|
||||
reason=<code> — …`: `run_ref_invalid` (not an Actions run URL or id on this
|
||||
repository), `api_error` (gh could not read the run), `workflow_mismatch` (a
|
||||
run of another workflow), `run_in_progress` (still queued or running) or
|
||||
`run_not_success` (it failed, was cancelled or timed out). Each prints a fix
|
||||
line; after the last three, clear again with a finished green run of the
|
||||
lane's own workflow ([run evidence](pregate.md#run-evidence)).
|
||||
|
||||
Another tree is accepted in exactly one case: it differs from `pregate.json`'s
|
||||
tree only in the repo's stamp rule (`.gstack/ship-policy.json` on the base
|
||||
branch: `stamp_files`, `stamp_patterns`, the built-in version, manifest,
|
||||
@@ -3949,7 +3958,7 @@ after `clear`), so it clears nothing here; or a `clear --run` receipt names
|
||||
another tree than `pregate.json`'s with no substitution recorded (`run
|
||||
receipt(s) verified for another tree`).
|
||||
|
||||
**Fix.** Dispatch that job, then
|
||||
**Fix.** Dispatch that job, wait for it to finish green, then
|
||||
`gstack-pregate clear --remote <workflow.yml/job> --run-url <url>` (or
|
||||
`--bundle-lane <label>` from an evidence bundle). For `pregate.json/remote_gate`,
|
||||
`clear --remote pregate.json/remote_gate --receipt <gate output>`: the gate's
|
||||
@@ -4260,7 +4269,10 @@ migration is a code change, so the gate reruns.
|
||||
sealed path or transcript (binary, empty and later-deleted files included), or
|
||||
adds a line with a sealed-answer marker, a seed or a local home path, and the
|
||||
destination is public or its visibility is unknown (a new or unreadable
|
||||
repository counts as public). An unreadable or malformed sealed policy refuses
|
||||
repository counts as public). With `sealed.label_keys` set, a new record that
|
||||
puts a label field next to an identifier (`sealed.label_join`), or a supported
|
||||
file the guard could not read (`sealed.label_join_unchecked`), refuses the
|
||||
same way. An unreadable or malformed sealed policy refuses
|
||||
too. The managed pre-push hook and `gstack-pregate sealed` print one line per
|
||||
finding: rule, `file[:line]`, commit. They never print the matched text.
|
||||
|
||||
@@ -4269,7 +4281,9 @@ reaches the public repository (a later commit that deletes it still publishes
|
||||
it). A publishable file kept next to sealed data (a `MANIFEST.json` of hashes
|
||||
and counts) can be exempted from the path rules with a `sealed.allow_paths`
|
||||
glob in the base branch's `.gstack/pregate.json`; the text rules still read it
|
||||
([docs/pregate.md#sealed](pregate.md#sealed)). A reviewed false positive gets
|
||||
([docs/pregate.md#sealed](pregate.md#sealed)). For `sealed.label_join`, drop
|
||||
the identifier fields (or the labels) from the published file, or keep the file
|
||||
out of the public repository; `allow_paths` never exempt it. A reviewed false positive gets
|
||||
one line in the base branch's
|
||||
`.gstack/sealed-allow.tsv`: `rule<TAB>path<TAB>sha<TAB>reason`, where `sha` is
|
||||
the full commit sha (a shortened one is ignored and named on stderr). A private
|
||||
@@ -5731,6 +5745,24 @@ pass).
|
||||
the invocation record, and start a full pass (`--pass 1`) when the branch was
|
||||
rebased; /ship's own rule is to merge the base in, never rebase a reviewed head.
|
||||
|
||||
<a id="review-scope-ledger"></a>
|
||||
### `REVIEW_SCOPE_LEDGER`
|
||||
|
||||
**Meaning.** `gstack-review-scope summary` could not count the review's
|
||||
dispatch ledgers, so it wrote no `REVIEW_LOOP:` line rather than a count that
|
||||
might be low (GST-951). Either no ledger was named (`--run`, `--ledger` or
|
||||
`--no-ledger`), `--run` holds no `review-ledger.jsonl`, or a ledger, the
|
||||
`merged.jsonl` it names or its judged set is unreadable, or the judged set
|
||||
changed after `judge` checked it. The ledgers hold every reader's findings
|
||||
(specialists, Red Team, both adversarial voices, targeted, fix-check) before any
|
||||
review-log row classifies them, which is why the count reads them.
|
||||
|
||||
**Fix.** Pass `--run <run dir>`, the directory the review passes wrote (on
|
||||
Capy, `ship/DRIVER.md`'s run directory), or each `--ledger <f>`. Pass
|
||||
`--no-ledger` only when no reader ran through `gstack-review-dispatch`. When a
|
||||
file moved, restore it; when a judged set changed, rerun `gstack-review-dispatch
|
||||
judge` on it.
|
||||
|
||||
<a id="receipt-carry-refused"></a>
|
||||
### `RECEIPT_CARRY_REFUSED`
|
||||
|
||||
|
||||
+2
-1
@@ -172,7 +172,8 @@ unpushed head is `QUEUE_RECEIPT_STALE` to the queue.
|
||||
The receipt's `review_loop` object, written from `gstack-review-scope summary
|
||||
--since <REVIEW_SINCE>`, is the loop's machine-readable outcome: `passes`
|
||||
(Step 9 full passes this ship ran, 1 or 2; up to 3 under `review_scope: all`),
|
||||
`blocking_found` (distinct blocking findings across them), `deferred`
|
||||
`blocking_found` (every blocker the readers found, rows and dispatch ledgers
|
||||
both, [review-policy.md](review-policy.md#what-the-receipt-records)), `deferred`
|
||||
(blocking findings in unchanged code filed as issues, Step 11's late defers
|
||||
included), `noted`, `fixed` and `scope`. The
|
||||
summary line a parent reads prints it as `passes=<n> blocking_found=<k>
|
||||
|
||||
Reference in new issue
Block a user