Repository navigation
v1.91.262.0 fix(release-cut,ship-queue): the weekly release accepts a train-gated commit; cross-repo refs, run ids, pr-body section merge, pregate by tree - #3483
Merged
Conversation
… commit, and five lane frictions seen in operation scripts/release-cut.ts gates a main commit either by a successful backstop check run or by a green/merging/merged train record in refs/notes/gstack-trains whose merged[] names its sha or tree (CANDIDATE: ... gated by train <nonce>); a red check, a running run, the required check and the freeze are judged as before. --required-check, --backstop-check and --notes-ref make the names configurable. lib/issue-links.ts reads (name #N[, #M]), (name PR #N) and (name issue #N) as cross-repo references. lib/gstack-post.ts allowlists an 11-digit run id after a run word or chained after one, and pr-body merges by ## section into the PR's current body (--replace overwrites). gstack-pregate mirrors each record under <state root>/pregate/by-tree/<tree>.json and ready reads the mirror for its tree when the checkout's record is missing or for another tree. docs/troubleshooting.md gains #redact-run-id-as-phone, #gate-error-no-base and #gstack-post-pr-body-merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
😎 This pull request was merged. |
6 of 7 tasks
Fence-length-aware section splitting in gstack-post pr-body (an unclosed receipt fence or a four-backtick block no longer exposes a heading to the merge), the run-id allowlist drops generic words (id, job, attempt, workflow), ready accepts a by-tree pregate mirror only from a complete run against origin/<base> under a reviewed policy and prints no home path, release-cut skips non-object notes and never substitutes train evidence for the required check when both names are equal, issue-links treats closing/cf./e.g./v1.2 as words rather than repositories. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
An unclosed receipt fence makes the body from that line on one section the merge never matches, so posted leading prose cannot replace it and a closed receipt before it stays a receipt; the by-tree pregate mirror must hold the tests stage (what --stage tests --remote writes) rather than every check; empty sha/tree strings in a train note are not evidence; (v1.2.3-rc1 #301) is a version word, not a repository. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…the (name #N) form Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
4 tasks done
…-friction Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> # Conflicts: # CHANGELOG.md # test/pregate-invalidation.test.ts
…or stands in for it Merging origin/main b004c06 (#3480) into this branch created a semantic conflict between two individually correct parents: #3480 made verifyPregate return PREGATE_ENVIRONMENT (instead of PREGATE_INCOMPLETE) when every incomplete check was caused by the environment, and this branch's by-tree mirror reader in ready() treated only PREGATE_LANE_FAILED and PREGATE_INCOMPLETE as final. Merged, a local record whose secrets scan never finished fell through to a same-tree tests-only mirror and ready passed without a secrets scan, against the rule this branch documents ("a record whose own checks failed or stayed incomplete is never overridden"). ready() now keeps a PREGATE_ENVIRONMENT record final as well. Regression in test/gstack-ship-queue.test.ts ("one READY block ..."): an environment-incomplete secrets record beside a passing same-tree mirror is refused with (PREGATE_ENVIRONMENT) and no "from the state root" suffix. Red on the bare merge 28b64d7 (expected exit 3, received 0), green with this fix. Found by the pass-4 delta review of the merge. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…-friction Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> # Conflicts: # CHANGELOG.md
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
garrytan
added a commit
that referenced
this pull request
Oct 27, 2026
GSTA-36 rule-19 grant 2026-10-27. CHANGELOG via mergeChangelogs; lib/gstack-post.ts and its test keep both --replace (#3483) and --attribute; catalog ratchet re-measured on top of design-motion: 1,314 -> 1,359 (5,427 bytes = 1,357; modernize +178); context-budget.json modernize 9466 and alwaysOnTotal 7880 recaptured; bin/gstack-doctor-components.sh regenerated. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
7 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The weekly release gate and five lane frictions the merge-train coordinator and its lanes hit in operation, each red before green.
The weekly release accepts a commit a green train merged (
scripts/release-cut.ts). Trains merge faster than the main backstop settles, so most main commits end with a neutral (superseded)main-backstopcheck run, and three dry runs of weekly-release.yml returnedNO_GREEN_COMMITfor a week of commits every one of which a green train had gated. A first-parent commit is now gated by either a successful backstop check run or a train record in the notes ref (kindtrain, stategreen,mergingormerged) whosemerged[]lists its sha or its tree. Everything else is unchanged: the required check (free-tests) must have succeeded, a red check run (a red backstop included) is red, a running run is skipped, and an active freeze refuses the cut. TheCANDIDATEline saysgated by main-backstoporgated by train <nonce>.--required-check,--backstop-checkand--notes-refmake the names configurable; weekly-release.yml is untouched (defaults).readTrainNotesreplacesactiveFreeze(one fetch of the notes ref returns the freeze and the train evidence; nothing else imported it).Lane frictions.
lib/issue-links.ts: a parenthesised named reference,(name #N[, #M]…),(name PR #N)or(name issue #N), is a cross-repo reference toname(Refs name#N), never a same-repo Refs line or a promotion candidate.(see #12),(fixes #12),(PR #12)and the other reference words stay bare refs.lib/gstack-post.ts: an 11-digit GitHub Actions run id right after a run word on the same line (run, screen, gate, backstop, bisect, rerun, dispatched, failed, passed, workflow, job, attempt, id, …, optional:#=), and every id chained after one through,/and/&/or//(optionally with(#1234)between), is allowlisted againstpii.phone.e164; a bare 11-digit number, a 10-digit number and+14155551234still report.gstack-post pr-bodymerges by##section into the PR's current body (read through the same runner): a section in the file replaces the same-titled one in place, the ship-receipt block is a section of its own, sections the file does not rewrite are kept byte for byte, new sections are appended.--replacekeeps the old overwrite. An unreadable current body posts nothing (exit 3, naming--replace). The scan runs on the merged bytes.lib/pregate/index.ts,lib/ship-queue.ts:gstack-pregatemirrors every record it writes to<state root>/pregate/by-tree/<tree>.json(best effort, never fails the record);readyreads the mirror for the current tree when the checkout's.gstack/tmp/pregate.jsonis missing or was recorded for another tree, uses it only if it verifies, and says(from <mirror>, recorded by another checkout)on the PREGATE line.#redact-run-id-as-phone,#gate-error-no-base,#gstack-post-pr-body-merge; docs/releases.md describes both kinds of release evidence; docs/pregate.md the by-tree mirror.Why
A release that cannot see train evidence never cuts; the lane frictions each cost a coordinator a hand step (a stripped cross-repo ref, a run id confirmation prompt, a PR body overwritten by /ship's section write, a worktree's pregate lost to another checkout).
Screenshots
The weekly release CANDIDATE line now says which evidence gated the commit: a successful main-backstop run or a green train record in refs/notes/gstack-trains (before: NO_GREEN_COMMIT for a week of train-merged commits).
Before/after as text: The weekly release CANDIDATE line now says which evidence gated the commit: a successful main-backstop run or a green train record in refs/notes/gstack-trains
Tests
Focused lanes at b8373e2 (the pushed head), run on this 4-core machine with the suite's 30s per-test timeout (
bun test --timeout=30000, the valuescripts/test-free-shards.tspasses; a barebun testat the 5s default times out three pre-existingpregate-invalidationtests here, a launcher artefact, not a product defect):bun test --timeout=30000 test/release-cut.test.ts test/ship-issue-intent.test.ts test/gstack-post.test.ts test/gstack-post-cli.test.ts test/gstack-ship-queue.test.ts test/pregate-invalidation.test.ts test/troubleshooting-anchors.test.ts test/parity-suite.test.ts test/bun-entry-isolation.test.ts test/module-size-ratchet.test.ts test/spawnsync-timeout-tripwire.test.ts test/weekly-release-workflow.test.ts test/changelog*.test.ts test/harness-version.test.ts: 361 pass, 0 fail.HOME=<private> bun test --timeout=30000 test/test-free-shards.test.ts: 77 pass, 0 fail (a private HOME keeps the evidence wrapper's own~/.gstacklog out of the home-write guard).bun run typecheck: exit 0.bun run typecheck:test: 819 known diagnostics, none new.gstack-pregate --stage tests --remote --base main --spend-cap 2 --timeout 2700→PREGATE_REMOTE: tree=60f60b770c72052e09a70f12737d6248222bb119 tests=10167 failed=0 spent=0.06(us-east-a2; two earlier attempts in eu-central-h1 failed on my own tree edit during the sync and on vCPU capacity, $0.16, no tests ran). The macOS and Windows platform lanes are cleared from this head's CI runs (Free Tests 40363111696, Windows Free Tests 40363111743); the receipt'srequires_remotecarries all three.At the merged head 020bdef (main b004c06 merged, plus the pass-4 fix), the same lanes again: the focused set 363 pass, 0 fail (two tests #3480 added to these files);
test/test-free-shards.test.ts77 pass, 0 fail; the Windows-curation check (test-free-shards,setup-hook-syntax-gate,ubicloud-pregate-remoteunder a private state root) 112 pass, 0 fail;bun run typecheckexit 0,typecheck:test819 known, none new. Full free tier on tree 309f4babb16d:PREGATE_REMOTE: tree=309f4babb16d09effd19b105f63dcab66a46ff5c tests=10199 failed=0 spent=0.04(us-east-a2). Main stamped v1.91.256.0 after the merge, so GitHub schedules no pull_request CI on the CHANGELOG-conflicted head (rule 19); the macOS and Windows lanes are cleared from the dispatched runs at this head (Free Tests 40400236429, Windows Free Tests 40400240927). No flaky case in any lane.Step 7 coverage audit added five tests, each red at HEAD before its fix and green after:
test/gstack-post.test.ts(GitLab merge path; CRLF, duplicate titles, receipt first, no trailing newline; composed-body idempotence),test/pregate-invalidation.test.ts(run and clear mirror byte for byte; an unwritable state root never fails the record),test/release-cut.test.ts(readTrainNotesgating: fetch error, missing ref, junk notes, freeze beside evidence). Review passes added: fence-length closers, info-string closers and the unclosed receipt fence (three merge cases); run-word negative controls; mirror negative rows (--only timing, another base, head policy) and the tests-stage mirror; equal--required-check/--backstop-checknames; non-object and malformed notes; closure, filler and version words in parentheses. No existing test was weakened, skipped or deleted; one pass-1 test row (workflow/job/attempt/id/idsrun words) moved from the allowlisted set to the controls, which is the behaviour change.Evals: not run. The diff changes no skill prompt and no paid case reads it; the thread owner forbids paid eval dispatch (recorded as not run).
Pre-Landing Review
Four passes: three to the cap on the branch, plus one delta-only pass on the main merge below. Blocking floor applied by hand: a finding blocks only if it would fail a test, break production, or lose data or secrets.
Pass 1 (whole diff vs
origin/main5755601; six specialists, native adversarial, GPT outside voice, Step 7 coverage audit, QA smoke) found and fixed, all inside the diff (commit 90f08a3):gstack-post pr-body: the merge was not idempotent on the body /ship composes, and text after a receipt could replace the intro (coverage audit, security, testing) → text sections are keyed by role.gstack-post pr-body: a```line inside a four-backtick block or a```jsline closed a fence early and an unclosed receipt fence swallowed## Approvals(native High; GPT Medium) → CommonMark closers (same character, at least the opener's length, nothing after).gstack-post: thepii.phone.e164allowlist had widened to the generic words id, ids, job, attempt, workflow (native High; security) → dropped;failed/passedstay (receipt vocabulary:failed <run id> | rerun passed <run id>).gstack-ship-queue ready: a same-tree record waiting only on a remote receipt never read the mirror (state-machines CRITICAL); mirror reuse was keyed by tree alone, so a--only, other-base or--policy-from headrecord from any clone counted (GPT Medium; native) → the mirror stands in when the local record is missing, unreadable, another tree, or uncleared, and must hold the tests stage, the sameorigin/<base>and a reviewed policy; the write is a temp file renamed into place; the PREGATE line names the mirror relative to the state root instead of printing the home path.scripts/release-cut.ts: a notes blob that parsed tonullcrashed the planner (TypeErroronr.kind; GPT Low, native Low) → shape-validated; equal--required-check/--backstop-checknames let train evidence stand in for the required check (GPT Medium) → no substitution when the names are equal.lib/issue-links.ts:(closing #12),(cf. #12),(v1.2 #3)became phantom cross-repo references and vanished from Refs and thefix #Noffer (native Medium) → closure, filler and version words added toNOT_A_NAME.Pass 2 (delta 1543806..90f08a3 only; structured, native adversarial, GPT outside voice) found and fixed (commit b8373e2):
## Approvals(GPT CRITICAL, reproduced) and a closed receipt before a later unclosed opener was demoted too → the unclosed tail is oneunclosed-receiptsection the merge never matches;gstack-pregate --stage tests --remoterecordschecks=[lanes](structured and native CRITICAL) → the mirror must hold the tests-stage checks;tree: ""/sha: ""note entries were stored as evidence andbyTree.get('')could hit them → skipped;(v1.2.3-rc1 #301)read as a repository →v\d[\w.-]*.Pass 3 (delta 90f08a3..b8373e2 only): no blocking finding, which is the exit. GPT outside voice (openai/gpt-6.1-sol): NO FINDINGS, both pass-2 fixes verified closed. Native reviewer: four INFORMATIONAL notes, listed under advisory below: a tests-only mirror certifies READY when the checkout has no record of its own, so preflight (secrets/regen/strays) may never have run for that tree (same as the local path today, where
--stage tests --remoteoverwrites pregate.json with a one-check record);v\d[\w.-]*swallows repository names such as v2ray or v0; an incoming file that itself carries an unclosed receipt fence is added rather than refused (bytes intact, renders as one code block); a mirror that fails verify leaves no trace on the PREGATE line. Then one docs-only commit (d08c42c, two one-line corrections from the documentation audit) with no code change.Main merge and pass 4 (rule-19 grant from GStack Minder after
train buildfound a real conflict):origin/mainb004c06 (v1.91.255.0) merged into the branch as 28b64d7 (merge, not rebase). Two conflicts:CHANGELOG.md, resolved bymergeChangelogs(our entry stays under## [Unreleased], byte-identical; main's entries intact), andtest/pregate-invalidation.test.ts, where #3480 gave the preceding test a30_000timeout and this branch appended the mirror test after it (both kept). Pass 4 (delta only, d08c42c..28b64d7; native reviewer plus the GPT outside voice) found one semantic conflict the merge created between two individually correct parents: #3480 makesverifyPregatereturnPREGATE_ENVIRONMENTfor environment-caused incomplete checks, andready's mirror reader treated onlyPREGATE_LANE_FAILEDandPREGATE_INCOMPLETEas final, so a local record whose secrets scan never finished fell through to a same-tree tests-only mirror andreadypassed without a secrets scan (reproduced: expected exit 3, got 0). A gate bypass is at the floor, so it was fixed in 020bdef:PREGATE_ENVIRONMENTis final too, with a regression case intest/gstack-ship-queue.test.tsthat is red on the bare merge and green with the fix. No other finding; the scratch-directory fallback from #3480 does not touchpregate/by-tree/.Gate edits: none detected (no threshold, rubric, trial count, case kind, judge prompt or timeout changed; the
--timeout=30000above is the suite's existing value passed to a focused run, not a change).Outside review: completed in all three passes (openai/gpt-6.1-sol via a Capy subagent, not the Codex CLI).
Advisory, named and not fixed here (below the blocking floor):
scripts/release-cut.ts:--required-check,--backstop-checkand--notes-refhave no caller besides the tests; weekly-release.yml runs the defaults (simplification).refs/notes/gstack-trainscan write an unsigned train record that gates a backstop-less commit (same trust level as pushing to main; native 5). Evidence is not subtracted by a later freeze or red-on-main record for the same commits; an active freeze still refuses the cut (native 6).gstack-post pr-body: HTML comments are not tracked by the section splitter (native 3); the read-merge-write has no conflict check against an edit made during a--confirmround trip (native 8); a MEDIUM finding already in a kept section blocks a repost of a clean file (native 9); the stderr summary echoes section titles unsanitised (native 10);## X ##, CRLF/LF mixing and Unicode heading forms are not normalised (native 11);BLOCK_REin ship-receipt.ts and the splitter disagree about a receipt inside another fence (native 12).lib/issue-links.ts:NOT_A_NAMEis a blocklist and cannot be closed ((ticket #12),(reverts #12),(ref. #12)still read as repositories; a repository literally named upstream, tracking or bug is now a same-repo ref). The real fix is a positive rule (require/or a PR/issue keyword), out of this PR's scope.gstack-post:failed/passedremain run words and the allowlist is by exact token value across the whole post, sorun 40269992077 … 40269992077exempts the second occurrence too (native).gstack-ship-queue readynow fingerprints the tree on every run (60s cap) even without a record (native 14); a tests-only mirror stands in for a missing local record without preflight evidence; a mirror that fails verify is not named on the PREGATE line (pass 3).lib/issue-links.ts:v\d[\w.-]*also treats v2ray/v0/v8-engine as version words (pass 3).gstack-post pr-body: an incoming file with an unclosed receipt fence is added as a section rather than refused (pass 3).Deferred (pre-existing or outside the diff): the header comment of
.github/workflows/weekly-release.ymlstill describes the gate as two check runs (the file is not in this diff);gstack-postcannot detect the host on a Capy checkout whose origin is git.capy.ai (--host githubneeded; QA ISSUE-001).Exploratory QA
Step 9.2.1 smoke (report-only, stub
gh, fresh state root;.gstack/qa-reports/ship-20261026/functional/report.md): 12 probes, 8 contracts pass (run-id allowlist posts; bare 11-digit number exits 2 withRULE: pii.phone.e164; pr-body merges by section with receipt first and untouched sections byte-identical; unreadable body exits 3 naming--replace; by-tree mirror byte-identical to the record;(gbrain #12, #13)cross-repo; release-cut usage names the three flags). Captures 001-004 superseded (my setup error: no--host github). Materialize verdictinconclusiveon that open item only; no product defect found.Design Review
No frontend files changed — design review skipped.
Eval Results
No prompt-related files changed — evals skipped (and no paid dispatch allowed in this thread).
Greptile Review
No Greptile comments (
GREPTILE_SIGNAL: none, early PR not applicable: the PR already existed).Plan Completion
Step 8 not run: no plan or design file is bound to this branch (
PLAN_BINDING: []).Issues
gstack-issue-links --base mainreads(v1.2.3-rc1 #301)in the pass-2 commit message asRefs #301; that token is a test example quoted in the message, not a reference to issue 301, so no Refs line is posted and the receipt'sissuescarries none.Verification Results
Step 8.1 obligations: none (no plan). Review-army probes: fence closers, run-word controls, mirror rows, equal check names, junk notes, parenthesised words, each proven red at the prior code and green after (details under Tests).
Documentation
Status: current (two attempts: the first read-only audit returned blocked on two one-line factual corrections, applied in d08c42c; the re-audit at d08c42c is current with no blockers). Scope: CHANGELOG.md, docs/releases.md, docs/pregate.md, docs/troubleshooting.md, docs/ship-policy.md, docs/unattended.md, README.md, ARCHITECTURE.md, ship/sections/pr-body.md.tmpl, checked against scripts/release-cut.ts, lib/issue-links.ts, lib/gstack-post.ts, lib/pregate/index.ts, lib/ship-queue.ts, bin/gstack-gate-diff, bin/gstack-diff-scope and lib/gate-diff/git.ts. Banned terms: none. No diagram drift.
Documentation health: README.md current; ARCHITECTURE.md current; CONTRIBUTING.md current; CHANGELOG.md current (Unreleased entry consistent with the code); docs/releases.md current (both gate kinds, the CANDIDATE row, the three flags); docs/pregate.md current (by-tree mirror and the named-reference paragraph); docs/troubleshooting.md current (
#redact-run-id-as-phone,#gstack-post-pr-body-merge,#gate-error-no-base); docs/ship-policy.md, docs/unattended.md, ship/sections/pr-body.md.tmpl current; TODOS.md skipped (out of ship-owned scope).Debt (not blockers):
#gate-error-no-basesits at the end of the Windows section of docs/troubleshooting.md though the failure is platform-agnostic;#pregate-staleand#queue-not-readydo not yet point at the by-tree mirror fallback; docs/pregate.md's By tree paragraph omits the unreadable-record case the CHANGELOG andreadycover; the header comment of .github/workflows/weekly-release.yml still describes the gate as two check runs (YAML comment, outside doc scope).Test plan
bun test --timeout=30000 <14 focused files>: 361 pass, 0 failHOME=<private> bun test --timeout=30000 test/test-free-shards.test.ts: 77 pass, 0 failbun run typecheck: clean;bun run typecheck:test: none newgstack-pregate run --base main --allow-repo-commands:PREGATE: requires-remote (3 uncleared) tree=60f60b770c72 head=d08c42c16e94 checks=7(secrets: warn, 70 MEDIUM pii.phone.e164 run-id tokens in CHANGELOG/docs/tests, non-credential)gstack-pregate --stage tests --remote --base main --spend-cap 2 --timeout 2700 --allow-repo-commands:PREGATE_REMOTE: tree=60f60b770c72052e09a70f12737d6248222bb119 tests=10167 failed=0 spent=0.06; macOS and Windows lanes cleared with their CI run URLsApprovals