Repository navigation
Conversation
…impossible fetch (garrytan#3269) gstack-pregate --base <sha> refused with "fix: git fetch origin <sha> --depth=1". That fetch can never create origin/<sha>: a short SHA fails with "couldn't find remote ref", and a full SHA only reaches FETCH_HEAD. A fork clone hits the same wall with --base upstream/main. When origin/<base> does not resolve but <base> names a commit here and no local branch (a SHA, a tag, another remote's ref), the refusal now says --base takes a branch name on origin and suggests origin/HEAD's branch. A local branch, or a name that resolves nowhere, keeps the fetch fix: that is the unfetched or shallow checkout it repairs. Same code, same exit 3, and pin files are still read only from the reviewed origin/<base>. Fixes garrytan#3269 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
Thanks @szsunyuan — clean, well-argued fix, and the "why this shape" section made the trust-boundary call easy to agree with. Your commit is cherry-picked into #3306 with your authorship kept (it conflicted with #3286 in |
|
Thanks @szsunyuan. Your commit was cherry-picked into #3306 with your authorship kept, alongside the other non-JS pregate fixes (#3266-#3269). Closing this in favour of that port; it's stamped v1.91.161.0 and through its gate. |
Why (in your own words)
gstack-pregate --base <sha>refuses withPOLICY_SOURCE_UNAVAILABLEand the fixgit fetch origin <sha> --depth=1. That fetch can never make the rerun pass:FETCH_HEADonly, soorigin/<sha>still doesn't exist. In a throwaway full clone, that--depth=1fetch also wrote.git/shallow, which hides the history behind that commit.--base upstream/mainfails the same way: pregate buildsorigin/upstream/mainand suggests fetching it from the fork.An agent that follows the fix gets the same refusal again, and a person learns nothing about what
--baseaccepts.Now, when
<base>names a commit here but is not a local branch (a SHA, a tag, another remote's ref), the refusal says--basetakes a branch name on origin and suggests one: origin/HEAD's branch, when it exists. The result code (POLICY_SOURCE_UNAVAILABLE) and the exit code (3) stay the same.Fixes #3269.
Why this shape
--baseas a commit when it is one, or say that--basetakes a branch name onoriginand drop the fetch hint." This takes the second, for exactly the inputs the fetch can't help.origin/<base>, so a PR can't smuggle a command into its own gate (docs/pregate.md#trust-boundary).--policy-from head --allow-repo-commandsis the path that labels a policy unreviewed. Accepting any local commit as the base,HEADincluded, would read pin files from a commit nobody reviewed, without that label. Accepting only commits that an origin branch contains might be safe, but that's a trust-boundary call for you. Happy to follow up.origin/copy was never fetched, or a name that resolves nowhere (the shallow gate checkout the hint was written for, F12), still getsgit fetch origin <base> --depth=1, byte for byte. The existing pins for that are untouched and pass:test/pregate-wtree-refusal.test.ts:112,test/gstack-ship-policy.test.ts:115andtest/restamp-command-boundary.test.ts:84.ContextRefusal.fixis already "a cause-specific fix that replaces the code's own" forWTREE_UNAVAILABLE(wtreeRefusal).describeRefusalreturned before reading it forPOLICY_SOURCE_UNAVAILABLE; now it reads it there too, and the line still says "pin files".--base main). That's what the agent-ready error contract asks for (docs/agent-ready-errors.md): "an exact command, flag or edit … A fix counts only when it is safe and authorized."main(2d42e4ece), and fine to absorb into a wave.main, fix(scope,review,ship): recognise Dart and Flutter in diff scope and test detection #3284 and fix(pregate): Dart import graph and test lanes #3285. With feat(pregate): user-owned policy file and local lanes #3286 (user-owned policy file), the only conflict is two adjacent import lines at the top oflib/pregate/context.ts; keep both. The new helper sits next towtreeRefusalrather than abovebuildContext, where feat(pregate): user-owned policy file and local lanes #3286 adds its own.What it does
lib/pregate/context.ts: on the refusal path only,buildContextasks two things:<base>a local branch (refs/heads/<base>)?<base>^{commit})?If it names a commit but is not a branch, the refusal gets the new detail and fix. Otherwise it's today's refusal, unchanged.
bin/gstack-pregate:--base <ref>becomes--base <branch>in the usage line and in the flag help. That matches thesealedandregatelines in the same file.docs/troubleshooting.md#policy-source-unavailable: one paragraph.--basenames a branch onorigin; a SHA or another remote's ref never resolves asorigin/<base>; pregate now says so instead of suggesting the fetch.test/pregate-wtree-refusal.test.ts: one CLI test, next to the existing--base no-such-baseenvelope case.upstream/mainget the branch fix. The--jsonfix and the stderr line agree, and neither saysgit fetch.What it deliberately does not do
HEAD) accepted as the base. See "Why this shape".RESULT_CODES.POLICY_SOURCE_UNAVAILABLErow.gstack-ship-policy,gstack-restamp,gstack-ship-queue,gstack-pregate regateand the other tools that buildorigin/<base>still print the fetch hint for a SHA. They're a follow-up if you want them; the check is one function.--base <ref>help.VERSION/CHANGELOG/ README / ETHOS untouched, and no generated files./shipnot run (the wave stamps the version).AI was used for assistance.
Live evidence
Setup. A throwaway fork-style clone in a temp dir:
originandupstreamare local bare repos holding the samemain.feature, andunpushedis a local branch that was never pushed.GSTACK_HOMEand the git config point into the temp dir.bin/gstack-pregatefrom a detachedupstream/mainworktree; After runs this branch's.Before (
upstream/mainat2d42e4ece): every case gets the fetch hint.After (this branch,
18c53ffa2): a SHA and another remote's ref get a branch to pass. A local branch and an unknown name keep the fetch fix, byte for byte.The old hint, followed literally:
Scope
lib/pregate/context.ts(+26/−6),bin/gstack-pregate(+2/−2),docs/troubleshooting.md(+6),test/pregate-wtree-refusal.test.ts(+27). No VERSION, no CHANGELOG, no generated files.The new test is red on
upstream/main, green here.2d42e4eceit fails attoContain('--base takes a branch name on origin'), receivingorigin/789930c3 is not a resolvable commit.Focused files, each in its own process with
LC_ALL=C, all passing:pregate-wtree-refusalpregate-selectiongstack-ship-policyrestamp-command-boundarygstack-regentroubleshooting-anchorsEvery file pregate selects for this diff.
originis my fork, so I pointed a temporaryorigin/pregate-baseref atupstream/main(2d42e4ece):pregate-supervisefails 2 of 14 here and the same 2 onupstream/main. Both readbun test's per-file progress lines, most likely in Bun 1.4.2's format; this box has Bun 1.4.0, and CI pins 1.4.2.Typecheck:
bun run typecheckexits 0.bun run typecheck:testreports 12 diagnostics, all in files this PR doesn't touch. The list is identical onupstream/main(Bun 1.4.0's types).Full
bun run test: not green on this Linux box, onupstream/maineither.This run on
18c53ffa2failed 367 tests in 71 files.The main causes:
bun run build:csoand Docker.pregate's import graph links one of the 71 files to the changed files:
pregate-supervise, above.Per-file failure counts match my earlier full run of this change (on
e9dfcf64f), except four files that failed in only one of the two runs:setup-gbrain-remote-callerandtest-free-shards-settlement-lifecycleqa-browser-deadline-evidenceandtest-free-shards-captureAll four pass alone, with and without this change.
The serial shard 17 (
bootstrap-retention) passed its 36 tests but was marked failed by the home-containment check.~/.claude/sessions/<id>.jsonchanged while it ran, and that file belongs to a live Claude Code session on this machine. The other real-home warnings are~/.claude/pluginsfiles from the same sessions. No new~/.gstack/projectsentries.CI's Linux free suite is the gate.
Redaction:
bin/gstack-redact --repo-visibility publicon the diff reports no findings.git diff --checkis clean.slop-scan: 696 findings on both trees, none in the changed files.
npxrefuses to run inside the repo right now (EOVERRIDE:sharpis^0.35.4in dependencies and0.35.5in overrides, onupstream/maintoo), so I ran the cached slop-scan binary directly.Paid evals:
bun run eval:select --base upstream/mainpicks the whole PR gate (108 behaviors, 24 judges), because every gate case reachesbin/gstack-pregateandlib/pregate/context.ts. Fork PRs get no eval secrets, so I couldn't run it. Every changed line runs only whenorigin/<base>doesn't resolve, and a refusal without its own fix prints the same bytes as before (theunpushedandno-such-baserows above).Liveness proof (required for external contributors)
Checklist
GSTACK PRtyped live into a real surface) or PR author is @garrytan (owner exemption)Test plan
upstream/mainand green herebun run test: the same environment failures asupstream/main; none comes from the changed filesFork PRs do not get eval secrets; the free files above are the gate.
Fixes #3269