fix: pre-landing review fixes (depth scan binding, queue scope, carry checks)

- carry refuses a --head other than the checked-out HEAD, a receipt with
  gstack_version but no review_depth, and an owner exception that no longer
  stands; a dirty or foreign checkout makes the write-time gate scan
  unavailable (full)
- the queue binds a gate scan only to the full bound commit and recomputes
  the scope signals with gstack-diff-scope <base> --head <sha>, the same
  range the writer uses
- the queue checks the review-loop exception's fix-check ancestry
- a package.json version line counts as stamp-only only when it is a bare
  version key
- docs and help name what review_depth is and which registry the queue reads

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-29 00:53:12 +00:00
1 parent 2b42ec597e
commit 4ad8a6bfd9
10 files changed
+172 -38

No files matched your search

+21
View File
@@ -367,6 +367,27 @@ describe('uncommitted work is visible (#2299)', () => {
expect(vars.SCOPE_BACKEND).toBe('true');
});
// Value: protects=--head scopes one committed range from any checkout, the input the receipt writer and the merge queue
// share; fails_when=--head still reads the working tree or untracked files, or an unknown head reads as all-false; seam=none
test('--head <sha> scopes only the committed range: working-tree and untracked files are ignored, an unknown head is an error', () => {
const dir = createRepo(['lib/auth.ts']);
const head = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: dir, stdio: 'pipe' }).stdout.toString().trim();
mkdirSync(join(dir, 'db', 'migrate'), { recursive: true });
writeFileSync(join(dir, 'db', 'migrate', '001.rb'), '# untracked\n');
writeFileSync(join(dir, 'styles.css'), 'a{}\n');
const scope = (args: string[]) => spawnSync('bash', [SCRIPT, 'main', ...args], { cwd: dir, stdio: 'pipe', timeout: 5000 });
const ranged = scope(['--head', head]);
expect(ranged.status).toBe(0);
const out = ranged.stdout.toString();
expect(out).toContain('SCOPE_AUTH=true');
expect(out).toContain('SCOPE_MIGRATIONS=false');
expect(out).toContain('SCOPE_FRONTEND=false');
expect(runScopeFull(dir).vars.SCOPE_MIGRATIONS).toBe('true');
const unknown = scope(['--head', 'f'.repeat(40)]);
expect(unknown.status).toBe(2);
expect(unknown.stdout.toString()).toContain('SCOPE_ERROR=no_head');
});
test('non-ASCII path still matches extension globs (NUL-safe file listing, #2526)', () => {
const dir = createRepo(['docs/M2 — Notes.md']);
const { vars, status } = runScopeFull(dir);
+9
View File
@@ -25,6 +25,15 @@ function repo(baseFiles: Record<string, string | Buffer>) {
}
describe('collectCommit: git objects only', () => {
// Value: protects=only a bare version bump in package.json is release metadata; fails_when=a version line that also
// carries scripts counts as stamp-only (docs class, light); seam=none
test('a package.json version line that also carries scripts is not stamp-only; a bare version bump is', () => {
const r = repo({ 'package.json': '{\n "name": "x",\n "version": "1.0.0"\n}\n' });
const bare = r.commit({ 'package.json': '{\n "name": "x",\n "version": "1.0.1"\n}\n' });
expect(collectCommit(r.dir, r.baseSha, bare).stampOnly).toEqual(['package.json']);
const riding = r.commit({ 'package.json': '{\n "name": "x",\n "version": "1.0.2", "scripts": { "postinstall": "node x.js" }\n}\n' });
expect(collectCommit(r.dir, r.baseSha, riding).stampOnly).toEqual([]);
});
test('a registered glob output is excluded, a command:null entry is not, and a .tmpl edit counts once', () => {
const r = repo({ '.gstack/generated.json': REGISTRY, 'ship/SKILL.md.tmpl': 'a\n', 'ship/SKILL.md': 'a\n' });
const head = r.commit({ 'ship/SKILL.md.tmpl': 'a\nb\nc\n', 'ship/SKILL.md': lines(120), 'src/hand.ts': lines(5) });
+53
View File
@@ -91,6 +91,33 @@ describe('the queue refuses a receipt below the depth its head needs', () => {
const verdict = receiptDepthVerdict(repo.work, 'origin/main', extractReceipt(legacy).receipt!, head);
expect(verdict).toEqual({ ok: true, line: 'REVIEW_DEPTH: legacy receipt (written before receipts recorded review_depth); accepted' });
}, 60_000);
// Value: protects=the queue's required depth keeps the scope signals the writer used, and a gate scan binds only the full
// bound commit; fails_when=the queue recomputes without diff-scope (auth reads light) or binds an empty or short scan head
// by prefix; seam=bindShippedReceipt
test('an auth path needs full at the queue though the receipt says light; an empty or short gate scan head binds nothing', () => {
const repo = makeFixtureRepo(); repos.push(repo);
const head = commit(repo, { 'lib/auth.js': lines(3) });
const body = path.join(repo.root, 'body.md');
expect(writeReceipt(repo, body).status).toBe(EXIT.ok);
expect(receiptIn(body).review_depth).toMatchObject({ depth: 'full', class: 'gate' });
const bind = (text: string) => bindShippedReceipt(repo.work, 'origin/main', text, '7', [head]);
const light = bind(rewrite(body, r => { r.review_depth.depth = 'light'; }));
expect(!light.ok && light.code).toBe('QUEUE_RECEIPT_DEPTH_RAISED');
expect(!light.ok && light.depthLine).toContain('receipt=light required=full');
expect(!light.ok && light.depthLine).toContain('class=gate');
const plain = makeFixtureRepo(); repos.push(plain);
const plainHead = commit(plain, { 'lib/util.js': lines(3) });
const plainBody = path.join(plain.root, 'body.md');
expect(writeReceipt(plain, plainBody).status).toBe(EXIT.ok);
for (const scanHead of ['', plainHead.slice(0, 12)]) {
const r = bindShippedReceipt(plain.work, 'origin/main', rewrite(plainBody, x => { x.review_depth.gate_scan.head = scanHead; }), '7', [plainHead]);
expect(r.ok).toBe(false);
expect(!r.ok && r.depthLine).toContain('required=full');
expect(!r.ok && r.depthLine).toContain('gate_scan@missing');
}
}, 90_000);
});
describe('carry and the review depth', () => {
@@ -114,6 +141,27 @@ describe('carry and the review depth', () => {
expect(refused.err).toContain("above the receipt's light");
expect(receiptIn(body).head).toBe(plain);
}, 90_000);
// Value: protects=a carry never binds a scan of one checkout to another commit, and never rebinds a receipt the queue would
// refuse; fails_when=carry --head from another checkout carries a skipped test at light, or a current receipt without
// review_depth carries; seam=none
test('carry --head from another checkout refuses; a receipt with gstack_version but no review_depth refuses', () => {
const repo = makeFixtureRepo(); repos.push(repo);
const reviewed = commit(repo, { 'lib/util.js': lines(3), 'test/util.test.js': "test('a', () => {});\n" });
const body = path.join(repo.root, 'body.md');
expect(writeReceipt(repo, body).status).toBe(EXIT.ok);
const skipped = commit(repo, { 'test/util.test.js': "test.skip('a', () => {});\n" });
repo.git('checkout', '-q', reviewed);
const r = runBin(repo, 'gstack-ship-receipt', ['carry', '--body', body, '--base', 'main', '--head', skipped]);
expect(r.status).toBe(EXIT.refused);
expect(r.err).toContain(`--head ${skipped.slice(0, 12)} is not the checked-out HEAD ${reviewed.slice(0, 12)}`);
expect(receiptIn(body).head).toBe(reviewed);
repo.git('checkout', '-q', skipped);
rewrite(body, x => { delete x.review_depth; });
const noDepth = runBin(repo, 'gstack-ship-receipt', ['carry', '--body', body, '--base', 'main']);
expect(noDepth.status).toBe(EXIT.refused);
expect(noDepth.err).toContain('records gstack_version but no review_depth');
}, 90_000);
});
describe('the review-loop exception (#3559)', () => {
@@ -150,5 +198,10 @@ describe('the review-loop exception (#3559)', () => {
const dropped = bindShippedReceipt(repo.work, 'origin/main', fs.readFileSync(body, 'utf8').replace(line, ''), '7', [head]);
expect(!dropped.ok && dropped.code).toBe('QUEUE_RECEIPT_EXCEPTION_INVALID');
expect(!dropped.ok && dropped.message).toContain('does not carry the review-loop exception line verbatim');
const foreign = { ...exception, fix_check: { head: 'f'.repeat(40), findings: 1 } };
const forged = rewrite(body, x => { x.review_loop_exception = foreign; }).replace(line, reviewLoopExceptionLine(foreign));
const offBranch = bindShippedReceipt(repo.work, 'origin/main', forged, '7', [head]);
expect(!offBranch.ok && offBranch.code).toBe('QUEUE_RECEIPT_EXCEPTION_INVALID');
expect(!offBranch.ok && offBranch.message).toContain('is not the receipt head or an ancestor of it');
}, 90_000);
});