fix(3259): non-mutating --help guard for native query handlers (#3272)

* fix(3259): non-mutating --help guard for native query handlers; reject --help as milestone version

Adds a dispatcher-level guard in query-dispatch.ts that short-circuits
to a non-mutating help stub whenever --help/-h appears in args destined
for a native mutating handler (fail-closed by default). Adds defense-
in-depth in milestoneComplete to reject --help/-h as a version value
before any disk write. Regression tests cover: per-handler --help guard,
registry-driven invariant across all mutating commands, handler-level
GSDError for both flags, and preservation of the #3019 CJS fallback
contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add changeset fragment for #3272

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-05-08 23:53:27 -04:00
committed by GitHub
parent ecd57e622c
commit 288b3b4170
5 changed files with 300 additions and 1 deletions

View File

@@ -213,6 +213,21 @@ export async function runQueryDispatch(deps: QueryDispatchDeps, queryArgv: strin
return toDispatchFailure(mapFallbackDispatchError(new Error('No native match in dispatch plan'), normCmd, normArgs));
}
// #3259: guard — if the invocation contains --help / -h AND the matched
// handler is a mutating command (mutation: true in the command manifest),
// short-circuit to a non-mutating stub. Mutating handlers are not help-aware
// by default (fail-closed). This prevents e.g. `milestone.complete --help`
// from writing milestone artifacts to disk.
const helpFlagPresent = matched.args.some((a) => a === '--help' || a === '-h');
if (helpFlagPresent && matched.mutation) {
return dispatchSuccess(
formatSuccess(
{ help: `Usage: gsd-sdk query ${matched.canonical} [args...]` },
undefined,
),
);
}
const dispatchNative = deps.nativeAdapter
? (cmd: string, args: string[]) => deps.nativeAdapter!.dispatch(cmd, args)
: deps.dispatchNative;