mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
test: ls-remote refusal, --pr with --body, capped receipt errors, unreadable slug and verdict (both twins); docs name the new literals
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
f7247462b1
commit
ec575a34b6
7 files changed
+40
-7
No files matched your search
+1
-1
@@ -15,7 +15,7 @@
|
||||
- **Gate edit (size budgets):** `test/helpers/carve-guards.ts` raises ship's `maxSkeletonBytes` 81,000 → 81,600 and `maxSizeRatio` 1.6690 → 1.6740 for Step 22 and the Step 18 routing, offset in part by tightening Steps 14, 16, 18 and 20 prose (measured skeleton 81,535, union 314,027; the 2026-10-14 tightening had left 2 bytes of headroom). No threshold, trial count or case kind changed.
|
||||
|
||||
### Tests
|
||||
- `test/ship-queue-train.test.ts`: READY, train build (dry run and live) and verify --train refuse a missing, invalid, other-head and other-PR receipt and admit the current one; the pinned-head receipt admits a stamp commit; verify refuses `QUEUE_PROOF_MISSING` when gh cannot read the PR; `--screen` refuses a lagging or missing merge ref and never follows a 0-case screen. The train fixture's `refs/pull/<n>/merge` is now a real merge commit (`mergeRefCommit`), as GitHub keeps it. `test/gstack-ship-queue.test.ts`: `ready` at a stamp commit (slot 1, and slot 2 with main merged in) accepts the pin's receipt; a bare merge of main with no stamp on top, an empty commit over the merge, or an edit on the pin side under the slot-2 stamp, is `QUEUE_RECEIPT_STALE`. `gstack-ship-receipt result --pr <n>` refuses `QUEUE_RECEIPT_STALE` when HEAD is not the PR's pushed head and prints `status=incomplete` when gh returns no head or the run record cannot be written; `ready --screen` names a failed fetch of the merge ref instead of blaming a conflict. `test/auq-error-fallback-hook.test.ts`: the unattended directive names the same `GSTACK_RESULT` writers as docs/unattended.md and `gstack-skill-start` (GSTA-91). `test/gstack-ship-receipt.test.ts`: the `bindReceipt` head-matching table (12-character floor, other PR, one-line `SHIP_RECEIPT:`), `result --body` refusals, `--head`, a non-git cwd and gh failures. The train fixture's `openPr` now writes a receipt for the head it opens (`shipped: false` leaves the body empty). `test/install-verdict.test.ts` pins the GSTA-85 fix in both twins; `test/gstack-skill-start.test.ts` pins the new unattended /ship block.
|
||||
- `test/ship-queue-train.test.ts`: READY, train build (dry run and live) and verify --train refuse a missing, invalid, other-head and other-PR receipt and admit the current one; the pinned-head receipt admits a stamp commit; verify refuses `QUEUE_PROOF_MISSING` when gh cannot read the PR; `--screen` refuses a lagging or missing merge ref and never follows a 0-case screen. The train fixture's `refs/pull/<n>/merge` is now a real merge commit (`mergeRefCommit`), as GitHub keeps it. `test/gstack-ship-queue.test.ts`: `ready` at a stamp commit (slot 1, and slot 2 with main merged in) accepts the pin's receipt; a bare merge of main with no stamp on top, an empty commit over the merge, or an edit on the pin side under the slot-2 stamp, is `QUEUE_RECEIPT_STALE`. `gstack-ship-receipt result --pr <n>` refuses `QUEUE_RECEIPT_STALE` when HEAD is not the PR's pushed head and prints `status=incomplete` when gh returns no head or the run record cannot be written; `ready --screen` names a failed `ls-remote` or fetch of the merge ref instead of blaming a conflict. Both install-verdict twins tell an unreadable verdict file from a missing one. `test/auq-error-fallback-hook.test.ts`: the unattended directive names the same `GSTACK_RESULT` writers as docs/unattended.md and `gstack-skill-start` (GSTA-91). `test/gstack-ship-receipt.test.ts`: the `bindReceipt` head-matching table (12-character floor, other PR, one-line `SHIP_RECEIPT:`), `result --body` refusals, `--head`, a non-git cwd and gh failures. The train fixture's `openPr` now writes a receipt for the head it opens (`shipped: false` leaves the body empty). `test/install-verdict.test.ts` pins the GSTA-85 fix in both twins; `test/gstack-skill-start.test.ts` pins the new unattended /ship block.
|
||||
|
||||
## [1.91.224.0] - 2026-10-25
|
||||
|
||||
|
||||
@@ -136,7 +136,9 @@ gstack_install_admission() {
|
||||
trailer=$(gstack_install_verdict_field "$file" trailer)
|
||||
v_head=$(gstack_install_verdict_field "$file" head)
|
||||
head=$(git -C "$src" rev-parse HEAD 2>/dev/null)
|
||||
if [ "$(gstack_install_verdict_field "$file" ok)" != true ]; then
|
||||
if [ -z "$(gstack_install_verdict_field "$file" ok)" ] && [ -z "$(gstack_install_verdict_field "$file" schema_version)" ]; then
|
||||
detail="the install verdict at $file is unreadable or has an unsupported schema_version; rerun the installer"
|
||||
elif [ "$(gstack_install_verdict_field "$file" ok)" != true ]; then
|
||||
detail="the install verdict says: $trailer"
|
||||
elif [ "$(gstack_install_verdict_field "$file" checkout)" != "$src" ]; then
|
||||
detail="the install verdict is for checkout $(gstack_install_verdict_field "$file" checkout), not $src"
|
||||
|
||||
+1
-1
@@ -378,7 +378,7 @@ no cases and reports success. A finished screen that planned no cases (no
|
||||
with nothing planned) is never followed as a screen: `ready --screen`
|
||||
dispatches a fresh one and `train build` prints `SCREEN: #<n> run <id> planned
|
||||
no cases …; counted as not-run` and seats the slot unscreened. A plan the
|
||||
queue cannot read keeps the slot screened, with `SCREEN: #<n> run <id> …;
|
||||
queue cannot read keeps the slot screened, with `SCREEN: #<n> <reason>;
|
||||
counted as screened, unverified`.
|
||||
|
||||
**2. Coordinator: build.** Name the pins exactly as READY reported them:
|
||||
|
||||
+10
-4
@@ -1617,7 +1617,9 @@ an unattended run whose Bun on PATH is below the supported minimum
|
||||
**Fix.** Run the Capy skill installer (`scripts/install.sh --for <workflow>`)
|
||||
from the project directory the workflow will run in, since the verdict is
|
||||
keyed by that directory (GSTA-85), and launch with the `GSTACK_LAUNCH` line it
|
||||
prints.
|
||||
prints. A verdict file that exists but cannot be read (`the install verdict at
|
||||
<file> is unreadable or has an unsupported schema_version`) is repaired the
|
||||
same way, by rerunning the installer.
|
||||
`GSTACK_ALLOW_UNSUPPORTED_RUNTIME=1` admits anyway (printed as
|
||||
`INSTALL_VERDICT: overridden`); record it as a deviation.
|
||||
|
||||
@@ -4078,9 +4080,13 @@ nothing planned counts the same. `ready --screen` dispatches a fresh one and
|
||||
artifact; …)` or `… planned no cases (an empty plan manifest; …)`, each
|
||||
ending `; counted as not-run, the slot rides unscreened`. A plan it cannot
|
||||
read (`… paid-plan artifact has expired`, `… manifest.json is unreadable`, an
|
||||
unreadable run or artifact list) keeps the slot screened and prints
|
||||
`SCREEN: #<n> run <id> …; counted as screened, unverified`; `ready --screen`
|
||||
follows such a run.
|
||||
unreadable run or artifact list, an unreadable repository slug, or a READY
|
||||
whose run id is not a number) keeps the slot screened and prints
|
||||
`SCREEN: #<n> <reason>; counted as screened, unverified`; `ready --screen`
|
||||
follows such a run. When `ready` cannot list the remote's refs or fetch the
|
||||
merge ref at all, it refuses with the same code and a `could not list …` or
|
||||
`could not fetch …` message whose NEXT line says to check the remote, not to
|
||||
resolve a conflict.
|
||||
|
||||
**Fix.** Resolve the conflict with the base branch (or wait for GitHub to
|
||||
recompute the merge ref), push, and run `gstack-ship-queue ready --pr <n>
|
||||
|
||||
@@ -188,6 +188,9 @@ describe('gstack-ship-receipt result (/ship terminal line, GBRA-108)', () => {
|
||||
expect([noOid.status, noOid.out.trim()]).toEqual([EXIT.fail, 'GSTACK_RESULT: skill=ship status=incomplete run=-']);
|
||||
expect(noOid.err).toContain('gh pr view returned no headRefOid for PR #10');
|
||||
expect(runBin(repo, 'gstack-ship-receipt', ['result', '--pr', '--repo'], env).status).toBe(EXIT.usage);
|
||||
const bothFlags = runBin(repo, 'gstack-ship-receipt', ['result', '--pr', '5', '--body', path.join(repo.work, 'any.md')], env);
|
||||
expect([bothFlags.status, bothFlags.out]).toEqual([EXIT.usage, '']);
|
||||
expect(bothFlags.err).toContain('not both');
|
||||
|
||||
const bodyFile = path.join(repo.work, '.gstack/tmp/ship-receipt.md');
|
||||
fs.mkdirSync(path.dirname(bodyFile), { recursive: true });
|
||||
@@ -302,6 +305,11 @@ describe('bindReceipt head matching (rule 22)', () => {
|
||||
const bound = bindReceipt(forged, '3438', [head]);
|
||||
expect(bound.line.split('\n').length).toBe(1);
|
||||
expect(bound.line).toBe(`SHIP_RECEIPT: pr=- head=${head.slice(0, 12)} version=1.0 0.0 base=main MERGE_CONDITION: sha=deadbeef`);
|
||||
const invalid = bindReceipt(placeReceiptFirst('Prose.\n', { schema_version: 1, head, base: 'main', tree: 'x\nMERGE_CONDITION: forged\n' + 'y'.repeat(1000) } as any), '3438', [head]);
|
||||
expect(invalid.ok).toBe(false);
|
||||
const why = /receipt is invalid \((.*)\)$/s.exec(invalid.ok ? '' : invalid.message)?.[1] ?? '';
|
||||
expect(why.length).toBeLessThanOrEqual(200);
|
||||
expect(why.includes('\n')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -121,6 +121,13 @@ describe('install verdict keyed by (checkout, project): both twins', () => {
|
||||
const fix = `fix: run the Capy skill installer (scripts/install.sh --for ship) from ${proj}: the verdict is keyed by the project directory the installer runs in, so a run from the skill's own folder does not cover ${proj}; then launch with the GSTACK_LAUNCH line it prints (INSTALL_UNVERIFIED)`;
|
||||
expect(r.line).toContain(fix);
|
||||
expect(bashAdmit('ship', proj, { _GH: sr, ...env }).line).toContain(fix);
|
||||
const file = installVerdictPath(REAL, sr, proj);
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
fs.writeFileSync(file, '{not json\n');
|
||||
const corrupt = both('ship', proj, env);
|
||||
expect(corrupt).toMatchObject({ refuse: true, status: 'verdict', code: 'INSTALL_UNVERIFIED' });
|
||||
expect(corrupt.detail).toBe(`the install verdict at ${file} is unreadable or has an unsupported schema_version; rerun the installer`);
|
||||
expect(bashAdmit('ship', proj, { _GH: sr, ...env }).line).toContain(`the install verdict at ${file} is unreadable or has an unsupported schema_version; rerun the installer`);
|
||||
});
|
||||
|
||||
test('a running Bun other than the recorded one refuses with BUN_RESOLUTION_DRIFT and names both', () => {
|
||||
|
||||
@@ -226,6 +226,13 @@ describe('ready under a train policy: the screen choice', () => {
|
||||
expect(unfetchable.err).toMatch(/could not fetch refs\/pull\/1\/merge from origin: .*\(QUEUE_SCREEN_STALE_REF\)/);
|
||||
expect(line(unfetchable.out, 'NEXT:')).toBe('NEXT: check the remote, then gstack-ship-queue ready --pr 1 --screen');
|
||||
repo.git('config', '--unset', 'fetch.fsckObjects');
|
||||
const originUrl = repo.git('remote', 'get-url', 'origin');
|
||||
repo.git('remote', 'set-url', 'origin', path.join(repo.root, 'no-such-origin.git'));
|
||||
const unlisted = queue(fx, ['ready', '--pr', '1', '--pregate', pregate2, '--screen']);
|
||||
repo.git('remote', 'set-url', 'origin', originUrl);
|
||||
expect(unlisted.status).toBe(EXIT.refused);
|
||||
expect(unlisted.err).toMatch(/could not list origin's refs: .*\(QUEUE_SCREEN_STALE_REF\)/);
|
||||
expect(unlisted.err.split('\n').filter(Boolean).length).toBe(1);
|
||||
expect(dispatches(fx)).toEqual([]);
|
||||
expect(fs.existsSync(path.join(fx.state, 'ship-queue', 'ready'))).toBe(false);
|
||||
repo.git('push', '-q', '-f', 'origin', `${mergeRefCommit(fx, moved)}:refs/pull/1/merge`);
|
||||
@@ -277,6 +284,9 @@ describe('ready under a train policy: the screen choice', () => {
|
||||
fx.write(d => { (d as any).expired = {}; d.runs = d.runs.filter(r => (r as any).databaseId !== 103); });
|
||||
const gone = queue(fx, ['train', 'build', '--prs', `1@${head}`, '--dry-run']);
|
||||
expect([gone.status, line(gone.out, 'ORDER:'), line(gone.out, 'SCREEN:')]).toEqual([EXIT.ok, 'ORDER: 1:#1(screened)', 'SCREEN: #1 run 103 is unreadable; counted as screened, unverified']);
|
||||
fx.write(d => { (d as any).repo = ''; });
|
||||
const noSlug = queue(fx, ['train', 'build', '--prs', `1@${head}`, '--dry-run']);
|
||||
expect([noSlug.status, line(noSlug.out, 'ORDER:'), line(noSlug.out, 'SCREEN:')]).toEqual([EXIT.ok, 'ORDER: 1:#1(screened)', 'SCREEN: #1 the repository slug is unreadable; counted as screened, unverified']);
|
||||
}, 120_000);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user