fix(#2176): ground the Antigravity reviewer in the repo under review (#2184)

* fix(#2176): ground the Antigravity reviewer in the repo under review

- capability-probe --add-dir (mirrors the Codex bypass-flag probe) and pass
  the repo root on both invocation arms
- anchor _AGY_PROMPT to the absolute repo root; mandate a
  REVIEWED-WITHOUT-REPO-ACCESS self-report when the repo is unreadable
- stamp a [reviewed-without-repo-access] marker on self-reported or
  scratch-anchored output; Consensus Summary down-weights marked reviews
- apply the same absolute-root anchor to the cursor-agent prompt (AC5)

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* docs(#2176): changeset fragment for PR #2184

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* fix(#2176): review fixes — size baseline, cursor root anchor, anchored blind tells

- regenerate tests/workflow-size-baseline.json for review.md's growth
- cursor anchor uses git rev-parse --show-toplevel (bare pwd resolved the
  wrong root from a repo subdirectory)
- blind-review tells anchored: self-report to the first lines of output,
  scratch tell to a workspace-declaration phrasing — a grounded review
  quoting either string is no longer mis-stamped

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* fix(#2176): round-2 review fixes — scratch-tell bridge, behavioral test, changeset

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* test: regenerate golden-install-parity fixtures for the review.md change

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* test(#2176): pass the transcript path to bash with forward slashes

The behavioral detection test substitutes a mkdtemp path into the bash
compound; on Windows runners that path contains backslashes, which bash
strips, so the transcript is never found and the first assertion fails
(windows-latest/24 lane). Git Bash accepts D:/-style paths.

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* fix(#2176): use /gsd:review namespace syntax in workflow comment

The slash-command namespace invariant (#3443) bans retired /gsd-<cmd>
references in Claude-facing sources; a cursor-anchor comment used
/gsd-review. Size baseline + golden fixtures regenerated for the byte
change.

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* test(#2176): derive the POSIX path via path.sep, not a hardcoded separator

Review finding: out.replaceAll('\\', '/') hardcodes both separators;
use the separator-safe out.split(path.sep).join(path.posix.sep) idiom.

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg

* test(#2176): use the merged toPosixPath seam for the bash path

Per maintainer note: #2247's shell-command-projection now centralizes
running-OS → POSIX path conversion; import it instead of the inline
split/join idiom.

Claude-Session: https://claude.ai/code/session_01Hme55Pvq6BhpgwBcyC5HAg
This commit is contained in:
Cody Anderson
2026-07-13 13:54:19 -06:00
committed by GitHub
parent e0f969af6a
commit 98e4233ce9
22 changed files with 256 additions and 22 deletions

View File

@@ -0,0 +1,192 @@
// allow-test-rule: source-text-is-the-product (see #2073)
// gsd-core/workflows/review.md is a workflow document whose bash blocks ARE
// what /gsd-review loads and executes at runtime. Asserting the invocation
// shape asserts the deployed contract — this is behavioral coverage of the
// workflow, not a source-grep over application code.
/**
* Antigravity reviewer repo-grounding tests (#2176)
*
* The agy block invoked the CLI without granting it the repo under review:
* no --add-dir on either invocation arm, and no absolute repo-root anchor in
* _AGY_PROMPT. The agent frequently anchored on its own
* ~/.gemini/antigravity-cli/scratch dir, reviewed the plan text in isolation
* (the exact failure the block's Review Instructions forbid), and its
* ungrounded verdict flowed into the Consensus Summary at full weight,
* undetected.
*
* These tests pin the fix: capability-probed --add-dir (mirrors the Codex
* bypass-flag probe), absolute-root prompt anchor (agy AND the cursor-agent
* block, which shared the anchor gap), a mandated self-report line, a stamped
* blind-review marker, and consensus down-weighting of marked reviews.
* Each assertion fails against the pre-fix block.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '..');
const REVIEW_PATH = path.join(ROOT, 'gsd-core', 'workflows', 'review.md');
function reviewContent() {
return fs.readFileSync(REVIEW_PATH, 'utf-8');
}
function agyBashBlock() {
const fences = reviewContent().match(/```bash[\s\S]*?```/g) || [];
const agy = fences.find((f) => /\bagy\b/.test(f) && /gsd-review-antigravity/.test(f));
assert.ok(agy, 'review.md should contain the agy invocation bash block');
return agy;
}
function cursorBashBlock() {
const fences = reviewContent().match(/```bash[\s\S]*?```/g) || [];
const cursor = fences.find((f) => /cursor-agent -p/.test(f));
assert.ok(cursor, 'review.md should contain the cursor-agent invocation bash block');
return cursor;
}
describe('Antigravity reviewer repo grounding in /gsd-review (#2176)', () => {
test('probes agy for --add-dir support (capability-probe idiom, mirrors the Codex block)', () => {
const block = agyBashBlock();
assert.ok(
/agy --help 2>\/dev\/null \| grep -q -- '--add-dir'/.test(block),
'agy block must capability-probe --add-dir via `agy --help | grep -q` so older CLIs still run',
);
});
test('passes --add-dir with the repo root when supported', () => {
const block = agyBashBlock();
assert.ok(
/set -- "\$@" --add-dir "\$_AGY_WS"/.test(block),
'the probed arm must append --add-dir "$_AGY_WS" so both invocation arms (which expand "$@") receive it',
);
});
test('_AGY_PROMPT is anchored to the absolute repo root', () => {
const block = agyBashBlock();
const promptLine = block.split('\n').find((l) => l.startsWith('_AGY_PROMPT='));
assert.ok(promptLine, 'agy block must define _AGY_PROMPT');
assert.ok(
/\$_AGY_WS/.test(promptLine),
'_AGY_PROMPT must embed the absolute repo root ($_AGY_WS) so repo-relative references resolve on the no---add-dir fallback',
);
});
test('_AGY_PROMPT mandates a REVIEWED-WITHOUT-REPO-ACCESS self-report', () => {
const block = agyBashBlock();
const promptLine = block.split('\n').find((l) => l.startsWith('_AGY_PROMPT='));
assert.ok(
/REVIEWED-WITHOUT-REPO-ACCESS/.test(promptLine),
'_AGY_PROMPT must require the exact self-report line when the reviewer cannot read the repo',
);
});
test('stamps a blind-review marker on self-reported or scratch-anchored output', () => {
const block = agyBashBlock();
assert.ok(
/head -5 [^|]*\| grep -q 'REVIEWED-WITHOUT-REPO-ACCESS'/.test(block),
'the self-report tell must be anchored to the head of the output, not a whole-body substring',
);
assert.ok(
/grep -qiE '\(workspace\|working\) \(directory\|dir\)\.\{0,40\}antigravity-cli\/scratch'/.test(block),
'the scratch tell must be anchored to a workspace-declaration phrasing whose bridge (.{0,40}) can span the dotted ~/.gemini/ path prefix',
);
assert.ok(
/\[reviewed-without-repo-access\]/.test(block),
'detected blind reviews must be stamped with the [reviewed-without-repo-access] marker',
);
});
test('marker stamping avoids sed -i (BSD/GNU divergence) — uses temp file + mv', () => {
const block = agyBashBlock();
assert.ok(!/sed -i/.test(block), 'agy block must not use sed -i (BSD vs GNU incompatibility)');
assert.ok(
/\.tmp && \\?\s*\n?\s*mv /.test(block),
'marker stamping should rewrite via temp file + mv',
);
});
test('Consensus Summary down-weights marked blind reviews', () => {
const content = reviewContent();
const consensusIdx = content.indexOf('## Consensus Summary');
assert.ok(consensusIdx >= 0, 'review.md should contain the Consensus Summary section');
const consensus = content.slice(consensusIdx, consensusIdx + 2000);
assert.ok(
/\[reviewed-without-repo-access\]/.test(consensus),
'consensus instructions must reference the blind-review marker',
);
assert.ok(
/REVIEWED-WITHOUT-REPO-ACCESS/.test(consensus),
'consensus instructions must also honor the raw self-report line',
);
assert.ok(
/not count its verdict at full consensus weight/.test(consensus),
'marked reviews must be down-weighted, not counted at full weight',
);
});
test('blind-review detection behaves correctly on synthetic transcripts', () => {
// Behavioral, not string-on-string: extract the actual detection compound
// from the fence, point it at a temp file, and run it through bash for
// ungrounded and grounded transcript shapes.
const os = require('os');
const { execFileSync } = require('node:child_process');
const { toPosixPath } = require('../gsd-core/bin/lib/shell-command-projection.cjs');
const block = agyBashBlock();
const m = block.match(/\{ head -5[\s\S]*?\}; then/);
assert.ok(m, 'detection compound not found in the agy block');
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'agy-detect-'));
const out = path.join(tmp, 'review-out.md');
const detect = m[0]
.replace(/\}; then$/, '}')
// Convert the native path to POSIX form so it survives bash on Windows
// runners (Git Bash accepts D:/... but eats backslashes).
.replaceAll('/tmp/gsd-review-antigravity-{phase}.md', toPosixPath(out));
const runDetect = (content) => {
fs.writeFileSync(out, content);
try {
execFileSync('bash', ['-c', detect], { stdio: 'ignore' });
return true; // exit 0 → blind review detected
} catch {
return false;
}
};
try {
// Ungrounded tells — must be stamped
assert.equal(runDetect('REVIEWED-WITHOUT-REPO-ACCESS\n\n## Review\nPlan-only review.\n'), true,
'self-report line in the head must be detected');
assert.equal(runDetect('## Review\nMy working directory is ~/.gemini/antigravity-cli/scratch.\nPlan-only review.\n'), true,
'full dotted scratch path in a workspace declaration must be detected (#2184 re-review Major)');
assert.equal(runDetect('Workspace directory: /home/me/.gemini/antigravity-cli/scratch\n'), true,
'colon-style workspace declaration must be detected');
// Grounded shapes — must NOT be stamped
assert.equal(runDetect('## Review\nVerified hooks/gsd-statusline.js against the plan. Solid.\n'), false,
'ordinary grounded review must not be stamped');
assert.equal(runDetect('## Review\nThe workflow mentions antigravity-cli/scratch as the agy scratch dir; the guard there is correct.\n'), false,
'grounded review merely quoting the scratch path must not be stamped');
assert.equal(
runDetect('## Review\n\nGrounded findings below.\n\n### Details\nLine 20 of the workflow mentions REVIEWED-WITHOUT-REPO-ACCESS as the self-report marker; fine.\n'),
false,
'self-report string quoted beyond the first 5 lines must not be stamped');
} finally {
require('./helpers.cjs').cleanup(tmp);
}
});
test('cursor-agent prompt carries the same absolute-root anchor (identical gap, #2176 AC5)', () => {
const block = cursorBashBlock();
assert.ok(
/_CURSOR_ROOT="\$\(git rev-parse --show-toplevel 2>\/dev\/null \|\| pwd\)"/.test(block),
'cursor anchor must resolve the repo TOP-LEVEL (rev-parse, not bare pwd) so subdirectory invocations anchor correctly',
);
const promptLine = block.split('\n').find((l) => l.startsWith('CURSOR_PROMPT_ARG='));
assert.ok(promptLine, 'cursor block must define CURSOR_PROMPT_ARG');
assert.ok(
/repository under review is at \$_CURSOR_ROOT/.test(promptLine),
'CURSOR_PROMPT_ARG must anchor repo-relative references to the absolute repo root',
);
});
});

View File

@@ -285,7 +285,7 @@
"gsd-core/workflows/remove-phase.md": "23b9eb0858a2535e",
"gsd-core/workflows/remove-workspace.md": "d0bd7e0601138798",
"gsd-core/workflows/resume-project.md": "98e2cf8908e73a52",
"gsd-core/workflows/review.md": "43c052bba1cbd4ac",
"gsd-core/workflows/review.md": "c4622380e61615c8",
"gsd-core/workflows/scan.md": "a7fecd67e5cd655f",
"gsd-core/workflows/secure-phase.md": "52ddc46233e8fa66",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/review.md": "3ce57633978035ad",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -355,7 +355,7 @@
"gsd-core/workflows/remove-phase.md": "8effc8742d58a11a",
"gsd-core/workflows/remove-workspace.md": "10882656198d9075",
"gsd-core/workflows/resume-project.md": "af9761bcec0f6fe9",
"gsd-core/workflows/review.md": "eec3a15bebb7fcf0",
"gsd-core/workflows/review.md": "15b8defb2d791092",
"gsd-core/workflows/scan.md": "75c670d08cee8680",
"gsd-core/workflows/secure-phase.md": "8030d2b2a5bfdf07",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -284,7 +284,7 @@
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "f3ab3a88a7e9e1ed",
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "b0baf1dafebe3821",
"gsd-core/workflows/review.md": "f1dbb3769d461ec2",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "d6ac1f4db6a5da75",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -288,7 +288,7 @@
"gsd-core/workflows/remove-phase.md": "e336350f8113a328",
"gsd-core/workflows/remove-workspace.md": "e685dfbd736dfd90",
"gsd-core/workflows/resume-project.md": "e23981178fa37b3d",
"gsd-core/workflows/review.md": "6c689f4ff8146d28",
"gsd-core/workflows/review.md": "8c996dc10e923685",
"gsd-core/workflows/scan.md": "dfd92717caea0ce7",
"gsd-core/workflows/secure-phase.md": "00de56d6d993bb2c",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/review.md": "3ce57633978035ad",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -391,7 +391,7 @@
"gsd-core/workflows/remove-phase.md": "9ee0fddd11a0d9d4",
"gsd-core/workflows/remove-workspace.md": "19d7465aaa50cb62",
"gsd-core/workflows/resume-project.md": "9965f87eb278f7f8",
"gsd-core/workflows/review.md": "5faef3f4feb45c99",
"gsd-core/workflows/review.md": "a980007626185b70",
"gsd-core/workflows/scan.md": "1a3caa5d724d39e9",
"gsd-core/workflows/secure-phase.md": "ab387a4bca381c18",
"gsd-core/workflows/session-report.md": "dd8fa011c9394075",

View File

@@ -286,7 +286,7 @@
"gsd-core/workflows/remove-phase.md": "e262654e319d1bc4",
"gsd-core/workflows/remove-workspace.md": "ceddfeef5f2d6754",
"gsd-core/workflows/resume-project.md": "40db7f350f5866d8",
"gsd-core/workflows/review.md": "4b649f31865a1785",
"gsd-core/workflows/review.md": "1f44a33c50b1cd36",
"gsd-core/workflows/scan.md": "dcc2f76d0850e2fb",
"gsd-core/workflows/secure-phase.md": "9bec6635ee1cbaed",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "433affcd1a200826",
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "3ba8bb85c8ede5b8",
"gsd-core/workflows/review.md": "ad115fadce422eae",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "3063b0b6f7b56d46",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -285,7 +285,7 @@
"gsd-core/workflows/remove-phase.md": "fce799aae3ab2715",
"gsd-core/workflows/remove-workspace.md": "8facde381657dd71",
"gsd-core/workflows/resume-project.md": "a0443839f1f83c2d",
"gsd-core/workflows/review.md": "761dd1ae5be40613",
"gsd-core/workflows/review.md": "d8c53e495b067afe",
"gsd-core/workflows/scan.md": "b28f65d88c522767",
"gsd-core/workflows/secure-phase.md": "a503dc469fd7a252",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "ada8a0546c686483",
"gsd-core/workflows/remove-workspace.md": "fc83f362a2d0a1b7",
"gsd-core/workflows/resume-project.md": "7f8dc986f0f35d96",
"gsd-core/workflows/review.md": "f8109b9ec1f56962",
"gsd-core/workflows/review.md": "3d62fa3096f46ecb",
"gsd-core/workflows/scan.md": "47371c2073d6c0be",
"gsd-core/workflows/secure-phase.md": "4977cf9e0462745b",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -349,7 +349,7 @@
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/review.md": "3ce57633978035ad",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "dea4661e8f89596f",
"gsd-core/workflows/remove-workspace.md": "446847e71aa52504",
"gsd-core/workflows/resume-project.md": "ad9f06a10bab8cc0",
"gsd-core/workflows/review.md": "2d28a6683ff587de",
"gsd-core/workflows/review.md": "98e441bde98a9d92",
"gsd-core/workflows/scan.md": "ad8ebcad4626d4a8",
"gsd-core/workflows/secure-phase.md": "71e6e689e80288ec",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -252,7 +252,7 @@
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/review.md": "3ce57633978035ad",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -285,7 +285,7 @@
"gsd-core/workflows/remove-phase.md": "e8ae4fbbfac700f0",
"gsd-core/workflows/remove-workspace.md": "4ac64de862dc650e",
"gsd-core/workflows/resume-project.md": "7f20769f302e5427",
"gsd-core/workflows/review.md": "bcbc20cb8df021cc",
"gsd-core/workflows/review.md": "7e1d8901d796338f",
"gsd-core/workflows/scan.md": "949692db4834dd27",
"gsd-core/workflows/secure-phase.md": "ef7b5ad194b687bf",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -285,7 +285,7 @@
"gsd-core/workflows/remove-phase.md": "a46c2fe853bf4e86",
"gsd-core/workflows/remove-workspace.md": "ae0e1c6d4438d663",
"gsd-core/workflows/resume-project.md": "f242e4c8aba18ea2",
"gsd-core/workflows/review.md": "835cf8c9594f17c1",
"gsd-core/workflows/review.md": "5fd9c6a9d7777eee",
"gsd-core/workflows/scan.md": "63631467651d9ca8",
"gsd-core/workflows/secure-phase.md": "4a647aec1e4d2dfe",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -285,7 +285,7 @@
"gsd-core/workflows/remove-phase.md": "e7a6af429b36e77b",
"gsd-core/workflows/remove-workspace.md": "b5e60fbb33b3e33a",
"gsd-core/workflows/resume-project.md": "82cfe1b8cb17c085",
"gsd-core/workflows/review.md": "3e72508a5dccd45a",
"gsd-core/workflows/review.md": "a63e0b5bb3b73730",
"gsd-core/workflows/scan.md": "12c11b2edc165df9",
"gsd-core/workflows/secure-phase.md": "185a15d389951e6e",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -356,7 +356,7 @@
"gsd-core/workflows/remove-phase.md": "df9a45f0b1880999",
"gsd-core/workflows/remove-workspace.md": "a7ca66db6b7c132c",
"gsd-core/workflows/resume-project.md": "f28da1200e4545f4",
"gsd-core/workflows/review.md": "ad7c0f372ed986ae",
"gsd-core/workflows/review.md": "3ce57633978035ad",
"gsd-core/workflows/scan.md": "003883d71c37da7d",
"gsd-core/workflows/secure-phase.md": "22ab3bb5da494ea7",
"gsd-core/workflows/session-report.md": "2e5b1205324ddefa",

View File

@@ -64,7 +64,7 @@
"remove-phase.md": 8513,
"remove-workspace.md": 7551,
"resume-project.md": 17270,
"review.md": 47168,
"review.md": 50362,
"scan.md": 7732,
"secure-phase.md": 13622,
"session-report.md": 4044,