fix(#1538): roadmap upgrade must not process.exit inside the no-throw hub (#1539)

* fix(core): roadmap upgrade must not process.exit inside the no-throw hub (#1538)

The upgrade handler called process.stderr.write + process.exit(1) on an
unsupported --convention, structurally bypassing the command-routing-hub's
no-throw contract (ADR-0012). It also parsed only the space-separated
--convention <value> form, so --convention=<value> was silently dropped and
defaulted to milestone-prefixed, running the migration the user did not request.

Throw instead of exit (the hub converts to HandlerFailure and the adapter routes
it through error()); parse both --convention forms and fail closed on any
missing/unsupported value.

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

* chore(changeset): Fixed fragment for #1539 (roadmap upgrade hub contract)

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

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Rezolv
2026-06-21 23:04:34 -04:00
committed by GitHub
parent 41bd333b30
commit 22e38f0b4a
3 changed files with 105 additions and 4 deletions

View File

@@ -181,10 +181,25 @@ function routeRoadmapCommand({ roadmap, args, cwd, raw, error }: RouteRoadmapCom
},
'upgrade': () => {
const dryRun = !args.includes('--apply');
const convention = args.find((_a, i) => args[i - 1] === '--convention') || 'milestone-prefixed';
// Parse `--convention <value>` and `--convention=<value>`. When the flag is
// absent entirely, default to the only supported convention; when present
// with a missing/unsupported value, fall through to the rejection below
// (fail-closed — never silently run a migration the user did not request).
let convention = 'milestone-prefixed';
const conventionFlagIdx = args.findIndex(
(a) => a === '--convention' || a.startsWith('--convention='),
);
if (conventionFlagIdx !== -1) {
const token = args[conventionFlagIdx];
convention = token.includes('=')
? token.slice(token.indexOf('=') + 1)
: (args[conventionFlagIdx + 1] ?? '');
}
if (convention !== 'milestone-prefixed') {
process.stderr.write('Only --convention milestone-prefixed is supported\n');
process.exit(1);
// No-throw hub contract (ADR-0012): a hub-dispatched handler must not call
// process.exit. Throw instead — the hub converts this to HandlerFailure and
// the adapter routes it through the injected error() boundary.
throw new Error('Only --convention milestone-prefixed is supported');
}
const plan = roadmapUpgrade.computeMigrationPlan(cwd);
roadmapUpgrade.applyMigration(cwd, plan, { dryRun });