fix(3584): address coderabbit findings on PR #3606

CodeRabbit surfaced one outstanding inline finding plus an outside-diff
finding and a finer point on two already-remediated sites; all addressed:

1. (inline, Minor) runtime-slash.cjs:31 — a degenerate input like `/gsd:`,
   `gsd:`, or `gsd-` normalizes to empty and the previous fallback returned
   the original colon-form input, reintroducing the deprecated shape the
   module exists to suppress. Now returns `''` (empty string) so callers
   see "no command" instead of an unroutable string. Also catches
   whitespace-only inputs the same way. New unit tests pin the contract.

2. (inline, Major) drift.cjs library purity — the earlier remediation
   passed `projectDir` into `detectDrift` and re-resolved runtime inside the
   library. CodeRabbit (correctly) flagged this as breaking the module's
   pure-library contract. detectDrift now accepts `input.runtime` directly;
   verify.cmdVerifyCodebaseDrift resolves the runtime once and passes the
   literal name in. drift.cjs no longer reads env or config at all.

3. (duplicate inline, Minor) gsd2-import.cjs:475 — when
   `gsd2-import --path <dir>` targets a project that isn't the process
   cwd, the preview command was formatted for the wrong runtime.
   buildPreview now receives the resolved `projectDir` (the same one
   used to find the .gsd/ root) instead of the raw `cwd`.

4. (outside-diff, Minor) tests/copilot-install.test.cjs:1-5 — the
   `allow-test-rule: integration-test-input` rationale block specifically
   named verify.cjs as the fixture, but #3584 changed that test to use a
   synthetic input. Comment now describes the real shape of the file's
   readFileSync usage (commands/, agents/, install.js source inputs to
   the installer/converter functions under test) and notes the synthetic
   substitution for the bin/lib path.

Full suite: 9366/9366 pass (+1 new test). Lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-15 19:52:17 -04:00
parent bdf06c04b0
commit 77b16993f8
6 changed files with 47 additions and 19 deletions

View File

@@ -125,14 +125,23 @@ describe('formatGsdSlash — runtime-aware slash command formatter', () => {
assert.strictEqual(formatGsdSlash('', 'claude'), '');
});
test('whitespace-only string is treated as empty after prefix strip', () => {
// Whitespace is preserved as-is in the output rather than mangled,
// so callers can detect "no real command" instead of receiving "/gsd- ".
const result = formatGsdSlash(' ', 'claude');
assert.ok(
result === ' ' || result === '/gsd- ',
`whitespace input should round-trip or be predictably wrapped, got ${JSON.stringify(result)}`,
);
test('whitespace-only string returns empty string (no spurious /gsd- emission)', () => {
assert.strictEqual(formatGsdSlash(' ', 'claude'), '');
assert.strictEqual(formatGsdSlash('\t\n', 'codex'), '');
});
test('degenerate prefix-only input returns empty (does NOT re-emit colon form)', () => {
// Regression guard for the CodeRabbit finding on the original PR:
// a previous fallback returned `commandName` unchanged when the bare
// tail was empty, which re-introduced the deprecated `/gsd:` shape for
// inputs like `/gsd:`, `gsd:`, or `gsd-`. The formatter must never
// emit the colon form — return empty so callers detect "no command"
// instead of receiving an unroutable string.
assert.strictEqual(formatGsdSlash('/gsd:', 'claude'), '');
assert.strictEqual(formatGsdSlash('gsd:', 'claude'), '');
assert.strictEqual(formatGsdSlash('gsd-', 'claude'), '');
assert.strictEqual(formatGsdSlash('/gsd-', 'codex'), '');
assert.strictEqual(formatGsdSlash('$gsd-', 'codex'), '');
});
test('commands with arguments preserve the argument tail', () => {

View File

@@ -1,8 +1,12 @@
// allow-test-rule: integration-test-input
// Reads verify.cjs as real test fixture input to the convertClaudeToCopilotContent()
// function under test. The file is not inspected for string presence; it is the
// input whose *transformation* is being asserted. This is the correct level of testing
// for format-conversion functions where a real source file is the canonical test case.
// Reads shipped source files (commands/gsd/*.md, agents/*.md, bin/install.js) as
// real test fixture input for installer/converter functions like
// convertClaudeToCopilotContent() and the install.js plumbing. Those files are
// not inspected for string presence; they are inputs whose *transformation* or
// installation behavior is being asserted. The converter-purity test on
// bin/lib/*.cjs uses a synthetic input string instead (per #3584:
// runtime-slash.cjs eliminated literal /gsd: refs from runtime CJS, so reading
// verify.cjs is no longer a meaningful fixture for testing the converter).
/**
* GSD Tools Tests - Copilot Install Plumbing