code-modernization: pilot-first uplift migration, brief-driven execution, deeper preflight

/modernize-uplift migrates one representative project end-to-end and
writes its lessons to analysis/<system>/PLAYBOOK.md before touching the
rest. The remaining projects then fan out through a new uplift-migrate
workflow, one uplift-migrator agent per project, in dependency-aware
escalating batches behind a per-batch circuit breaker. A recorded
per-test baseline (analysis/<system>/BASELINE.md) gates the migration,
and the delta catalog reports a test framework whose runner does not
support the target as its own highest-blast-radius dependency.

The three execution commands (uplift, transform, reimagine) read
MODERNIZATION_BRIEF.md and treat their phase's scope and entry and exit
criteria as gates, so editing the brief steers execution. For a
same-stack uplift the brief requires the delta catalog and applies the
same ordering overrides the execution command does.

/modernize-preflight opens with a short interview (scope, local build
and test, bespoke build infrastructure, prior attempts, what is off
limits) without blocking on the answers, reads the CI/build definition
for how the system builds, escalates the smoke test to a whole-project
restore and build, and adds a scope-boundary check that enumerates
inbound and outbound dependencies when the system directory is a slice
of a larger repository.

Workflow scripts accept args delivered as either a JSON string or an
object.
This commit is contained in:
Morgan Lunt
2026-07-08 18:42:18 -07:00
parent 8d9e142e4e
commit 96e3e8c6af
15 changed files with 994 additions and 52 deletions

View File

@@ -12,9 +12,15 @@ export const meta = {
],
}
// `args` may arrive as the caller's raw JSON string rather than the parsed
// object, depending on the invoking runtime; normalize so both work. A string
// that is not valid JSON falls through and the requires-args check reports it.
const ARGS = typeof args === 'string' ? (() => { try { return JSON.parse(args) } catch (e) { return args } })() : args
// ---- args -----------------------------------------------------------------
// The slash command passes these; the script never touches the filesystem.
const system = args && args.system
const system = ARGS && ARGS.system
if (!system) {
throw new Error(
'modernize-extract-rules workflow requires args: {system: "<system-dir>", modulePattern?: "<glob>", maxRounds?: number}',
@@ -23,8 +29,8 @@ if (!system) {
if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(system)) {
throw new Error(`Unsafe system name ${JSON.stringify(system)} — must be a plain directory name under legacy/`)
}
const modulePattern = (args && args.modulePattern) || ''
const maxRounds = Math.max(1, Math.min((args && args.maxRounds) || 4, 8))
const modulePattern = (ARGS && ARGS.modulePattern) || ''
const maxRounds = Math.max(1, Math.min((ARGS && ARGS.maxRounds) || 4, 8))
const legacyDir = `legacy/${system}`
// ---- shared prompt fragments ----------------------------------------------