mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-10 10:40:32 +01:00
fix(pregate): sealed labels the checked commit, not "pushed" (#3565)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
4c745eec10
commit
af5165ae50
5 files changed
+32
-3
No files matched your search
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
**`gstack-pregate sealed` no longer prints a line that reads like a push confirmation.** On gstack-evals#29 the lane read `pushed aeb2413` in the `SEALED:` line as the push landing, while the remote stayed at 9036ab1 (#3565). `sealed` never pushes: it checks the commits a push would publish. `pushed <sha>` was its label for the commit whose `sealed` policy it read beside the base branch's.
|
||||
|
||||
### Fixed
|
||||
- **`lib/sealed-guard.ts`**: that label now reads `commit <sha12>` (`policy=origin/main + commit aeb2413f0ca1`), in the `SEALED:` line, in the managed pre-push hook's messages and in the unreadable-policy error.
|
||||
- **`gstack-pregate --help`** and **docs/pregate.md#sealed** say the command never pushes and that `git ls-remote <remote> <ref>` confirms a push.
|
||||
|
||||
### Tests
|
||||
- `test/sealed-guard.test.ts`: `gstack-pregate sealed` names the checked commit, prints no `pushed`, and leaves the remote's refs exactly as they were. It failed before the fix on `policy=origin/main + pushed <sha>`.
|
||||
|
||||
## [1.91.299.0] - 2026-10-28
|
||||
|
||||
**The Captain brief now says what `rec` does when a gate carries earlier decisions.** At a gate after the owner already answered some items, such as a round-cap gate, `gstack-owner-brief render --captain` listed each item's recommendation ("keep the plan as written", `rec resolves to d2a`) while `gstack-gate render` and `gstack-autoplan answer --reply rec` keep the carried answer (`d2b`). A Captain relaying the brief told the owner the opposite of what their one-word reply would record (#3546). The brief now takes its choices from the same resolver `gstack-gate render` uses.
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ const HELP = `gstack-pregate — cheap checks before anything paid or remote (do
|
||||
validate | init the working-tree ${PREGATE_CONFIG_PATH}
|
||||
sealed [--range <a..b>] [--remote <name>] [--base <branch>]
|
||||
the sealed-output guard (docs/pregate.md#sealed) over the commits <remote> lacks, or the range:
|
||||
run it before any push or PR creation in a repo with a \`sealed\` config; exit 1 refuses (SEALED_OUTPUT_REFUSED)
|
||||
run it before any push or PR creation in a repo with a \`sealed\` config; exit 1 refuses (SEALED_OUTPUT_REFUSED);
|
||||
it never pushes: policy=<base> + commit <sha> names where the policy was read
|
||||
regate --against <gated-head> --bundle <file> [--require <lanes>] [--pr <n>] [--predecessor-pr <n> | --predecessor-ref <ref>]
|
||||
a fresh tree receipt, each lane's reuse verdict, migration collisions (base branch and open PRs), then
|
||||
REGATE: needed|not-needed (docs/pregate.md#regate); \`gstack-pregate --against <head>\` is the same verb; exit 1 = needed
|
||||
|
||||
+4
-1
@@ -788,7 +788,10 @@ gstack-pregate sealed [--range <a..b>] [--remote origin] [--base <branch>] [--js
|
||||
(`/home/<user>/`, `/Users/<user>/`, `C:\Users\<user>\`) check each commit's added
|
||||
lines. Findings print rule, `file[:line]` and commit, never the text.
|
||||
- **Policy.** The union of the destination base branch's `sealed` and the
|
||||
pushed commit's: a push can tighten it, never relax it. A malformed or
|
||||
pushed commit's: a push can tighten it, never relax it. The `SEALED:` line
|
||||
names both as `policy=<remote>/<base> + commit <sha12>`. `gstack-pregate sealed`
|
||||
only checks and never pushes, so confirm a push with
|
||||
`git ls-remote <remote> <ref>`. A malformed or
|
||||
unreadable policy refuses. Waivers come only from the base branch's
|
||||
`.gstack/sealed-allow.tsv` (`rule<TAB>path<TAB>sha<TAB>reason`) and are
|
||||
listed on every push.
|
||||
|
||||
+1
-1
@@ -233,7 +233,7 @@ export function evaluateSealed(o: EvaluateOptions): SealedReport {
|
||||
let policy: SealedPolicy | null = base.policy;
|
||||
const labels = base.policy ? [base.label] : [];
|
||||
for (const t of o.targets) {
|
||||
const pushed = sealedPolicyAt(o.repoRoot, t.pushedSha, `pushed ${t.pushedSha.slice(0, 12)}`);
|
||||
const pushed = sealedPolicyAt(o.repoRoot, t.pushedSha, `commit ${t.pushedSha.slice(0, 12)}`);
|
||||
if ('error' in pushed) return { ...blank, status: 'error', error: `the sealed policy in ${pushed.label} is unreadable: ${pushed.error}` };
|
||||
if (pushed.policy) labels.push(pushed.label);
|
||||
policy = unionPolicies(policy, pushed.policy);
|
||||
|
||||
@@ -221,3 +221,17 @@ describe('visibility (cache under the state root; unknown counts as public)', ()
|
||||
} finally { fs.rmSync(state, { recursive: true, force: true }); }
|
||||
});
|
||||
});
|
||||
|
||||
describe('gstack-pregate sealed is a check, not a push (#3565)', () => {
|
||||
test('the SEALED line names the checked commit, never says pushed, and the remote ref does not move', () => {
|
||||
const s = scratch({ '.gstack/pregate.json': policy(SEALED) });
|
||||
const head = commit(s, { 'notes.md': 'ok\n' });
|
||||
const before = sh(s.work, s.env, ['ls-remote', 'origin']);
|
||||
const r = spawnSync('bun', [PREGATE, 'sealed'], { cwd: s.work, encoding: 'utf8', timeout: 60_000, env: { ...process.env, ...s.env } });
|
||||
expect(r.status).toBe(0);
|
||||
expect(r.stdout).toContain(`policy=origin/main + commit ${head.slice(0, 12)}`);
|
||||
expect(`${r.stdout}${r.stderr}`).not.toMatch(/\bpushed\b/);
|
||||
expect(sh(s.work, s.env, ['ls-remote', 'origin'])).toBe(before);
|
||||
expect(before).not.toContain('refs/heads/results');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user