fix(#1140): implement state add-roadmap-evolution CJS handler (#1148)

* fix(#1140): implement state add-roadmap-evolution CJS handler

`query state.add-roadmap-evolution` was unreachable: the CJS state router
listed it in the `unsupported` map with a circular message ("...is SDK-only.
Use: gsd-tools query state.add-roadmap-evolution ...") and no CJS handler
existed after the SDK retirement (ADR-0174). Every `/gsd:phase insert` and
`/gsd:phase --edit` run hit a dead end recording Roadmap Evolution.

Re-implement `cmdStateAddRoadmapEvolution` in CJS (src/state.cts) and wire it
into the state router; remove the now-stale `unsupported` entry. The handler
appends a single-line bullet under `## Accumulated Context` → `### Roadmap
Evolution` (creating the subsection/section if missing, deduping identical
entries), scoping every lookup to the Accumulated Context body so a decoy
heading in an unrelated section is never targeted, and flattening multiline
notes to a single bullet. Section-boundary regexes mirror the sibling
add-decision/add-blocker handlers and preserve following sections on CRLF input.

Regression cases live in tests/state.test.cjs (per the no-new-bug-NNNN-files
policy) and cover the literal issue repro plus the CLI/parser QA matrix
(missing/empty/whitespace note, flag-shaped value, duplicate flags, hostile
shell metacharacters, Unicode, decoy section, CRLF, missing STATE.md).

Closes #1140

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#1140): backfill changeset PR number (1148)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-06-12 22:27:02 -04:00
committed by GitHub
parent 1b880fefd7
commit b431b1fab4
4 changed files with 481 additions and 3 deletions

View File

@@ -40,6 +40,7 @@ interface StateModule {
cmdStateUpdateProgress(cwd: string, raw: boolean): void;
cmdStateAddDecision(cwd: string, opts: Record<string, string | null | undefined>, raw: boolean): void;
cmdStateAddBlocker(cwd: string, opts: Record<string, string | null | undefined>, raw: boolean): void;
cmdStateAddRoadmapEvolution(cwd: string, opts: Record<string, string | boolean | null | undefined>, raw: boolean): void;
cmdStateResolveBlocker(cwd: string, text: string | null | undefined, raw: boolean): void;
cmdStateRecordSession(cwd: string, opts: Record<string, string | null | undefined>, raw: boolean): void;
cmdStateBeginPhase(cwd: string, phase: string | null | undefined, name: string | null | undefined, plans: number | null, raw: boolean): void;
@@ -78,9 +79,10 @@ function routeStateCommand({ state, args, cwd, raw, error }: RouteStateCommandOp
args,
subcommands: ['load', 'complete-phase', ...STATE_SUBCOMMANDS.filter((s) => s !== 'load')],
defaultSubcommand: 'load',
unsupported: {
'add-roadmap-evolution': 'state add-roadmap-evolution is SDK-only. Use: gsd-tools query state.add-roadmap-evolution ...',
},
// No SDK-only state subcommands remain: add-roadmap-evolution was the last
// holdout after the SDK retirement (ADR-0174) and is now implemented in CJS
// (handler below). See #1140.
unsupported: {},
error,
cwd,
raw,
@@ -147,6 +149,17 @@ function routeStateCommand({ state, args, cwd, raw, error }: RouteStateCommandOp
const a = parseNamedArgs(args, ['text', 'text-file']);
state.cmdStateAddBlocker(cwd, { text: strArg(a, 'text'), text_file: strArg(a, 'text-file') }, raw);
},
'add-roadmap-evolution': () => {
const a = parseNamedArgs(args, ['phase', 'action', 'after', 'note', 'note-file'], ['urgent']);
state.cmdStateAddRoadmapEvolution(cwd, {
phase: strArg(a, 'phase'),
action: strArg(a, 'action'),
after: strArg(a, 'after'),
note: strArg(a, 'note'),
note_file: strArg(a, 'note-file'),
urgent: a['urgent'] === true,
}, raw);
},
'resolve-blocker': () => state.cmdStateResolveBlocker(cwd, strArg(parseNamedArgs(args, ['text']), 'text'), raw),
'record-session': () => {
const a = parseNamedArgs(args, ['stopped-at', 'resume-file']);