export const meta = { name: 'release', description: 'Relicario release lifecycle: develop features (single/multi-agent), iterate on debug, cut releases', phases: [ { title: 'Discover' }, { title: 'Plan' }, { title: 'Execute' }, { title: 'Verify' }, { title: 'Generate' }, { title: 'Finalize' }, ], } // ── Schemas ─────────────────────────────────────────────────────────────────── const MANIFEST_SCHEMA = { type: 'object', properties: { plans: { type: 'array', items: { type: 'string' } }, taskCount: { type: 'number' }, domains: { type: 'array', items: { type: 'string' } }, }, required: ['plans', 'taskCount', 'domains'], } const TASK_LIST_SCHEMA = { type: 'object', properties: { tasks: { type: 'array', items: { type: 'object', properties: { id: { type: 'string' }, description: { type: 'string' }, planPath: { type: 'string' }, techDomain: { type: 'string' }, }, required: ['id', 'description', 'planPath', 'techDomain'], }, }, }, required: ['tasks'], } const ASSIGNMENT_SCHEMA = { type: 'object', properties: { devCount: { type: 'number' }, devs: { type: 'array', items: { type: 'object', properties: { letter: { type: 'string' }, scope: { type: 'string' }, tasks: { type: 'array', items: { type: 'string' } }, outOfScope: { type: 'array', items: { type: 'string' } }, techDomain: { type: 'string' }, planFiles: { type: 'array', items: { type: 'string' } }, }, required: ['letter', 'scope', 'tasks', 'outOfScope', 'techDomain', 'planFiles'], }, }, pmScope: { type: 'string' }, }, required: ['devCount', 'devs', 'pmScope'], } const DEBUG_RESULT_SCHEMA = { type: 'object', properties: { fixed: { type: 'boolean' }, summary: { type: 'string' }, remainingFailures: { type: 'string' }, }, required: ['fixed', 'summary'], } const VERIFY_RESULT_SCHEMA = { type: 'object', properties: { allPass: { type: 'boolean' }, failures: { type: 'array', items: { type: 'string' } }, summary: { type: 'string' }, }, required: ['allPass', 'failures', 'summary'], } // ── Helpers ─────────────────────────────────────────────────────────────────── const REPO = '/home/alee/Sources/relicario' const COORD_DIR = 'docs/superpowers/coordination' function devRole(letter) { return 'dev-' + letter.toLowerCase() } // ── Routing ─────────────────────────────────────────────────────────────────── const action = (args && args.action) || 'develop' const mode = (args && args.mode) || 'single' const release = args && args.release const context = args && args.context // ── ACTION: develop ─────────────────────────────────────────────────────────── if (action === 'develop') { if (!release) throw new Error('args.release is required for action=develop') phase('Discover') const manifest = await agent( `Scan docs/superpowers/plans/ in ${REPO} for plan files belonging to release "${release}". ` + `A plan file belongs if its filename contains the release label, or its opening lines reference it as its target release. ` + `Read each matching file, count checkbox tasks (lines starting with - [ ]), and identify tech domains (rust, extension, docs, etc.). ` + `Return: plans (relative paths from repo root), taskCount, domains.`, { schema: MANIFEST_SCHEMA, label: 'discover-plans', phase: 'Discover' } ) log(`Found ${manifest.plans.length} plan(s), ${manifest.taskCount} tasks — domains: ${manifest.domains.join(', ')}`) // ── SINGLE MODE ───────────────────────────────────────────────────────────── if (mode === 'single') { phase('Plan') const taskList = await agent( `You are the PM for the ${release} release of Relicario at ${REPO}.\n` + `Read these plan files:\n${manifest.plans.map(p => ' ' + p).join('\n')}\n\n` + `Extract every checkbox task (- [ ] items) and order them to respect dependencies ` + `(e.g. core Rust changes before WASM/CLI consumers, schema changes before UI). ` + `For each task return: id (short slug like S1-step2), description (full step text), ` + `planPath (which file it came from), techDomain (rust/extension/docs/cli/wasm).`, { schema: TASK_LIST_SCHEMA, label: 'pm-plan', phase: 'Plan' } ) log(`PM ordered ${taskList.tasks.length} tasks for sequential execution`) phase('Execute') await pipeline( taskList.tasks, (task) => agent( `You are a senior developer on the ${release} release of Relicario.\n` + `Repo: ${REPO}\n\n` + `IMPORTANT: cd into ${REPO} before any git or cargo commands.\n\n` + `Your task (${task.id}): ${task.description}\n` + `Plan file for full context: ${task.planPath}\n` + `Tech domain: ${task.techDomain}\n\n` + `Instructions:\n` + `1. Read the plan file for context on this specific step.\n` + `2. Implement ONLY this step — do not run ahead to the next one.\n` + `3. Run the relevant tests after your change (cargo test -p for Rust; pnpm build for extension).\n` + `4. Commit with a conventional commit message scoped to the change.\n` + `5. Report: what you did, test result (pass/fail), any blockers.`, { label: task.id, phase: 'Execute' } ) ) phase('Verify') const verifyResult = await agent( `Run the full Relicario test suite from ${REPO}. IMPORTANT: cd ${REPO} first.\n` + `Commands:\n` + ` cargo test\n` + ` cargo build --all-targets\n` + ` cargo clippy -- -D warnings\n` + `Report pass/fail for each command. List every failure with its error message.`, { schema: VERIFY_RESULT_SCHEMA, label: 'full-verify', phase: 'Verify' } ) if (!verifyResult.allPass) { log(`Verify FAILED — ${verifyResult.failures.length} failure(s): ${verifyResult.failures.join(' | ')}`) log(`Fix with: Workflow({name:"release", args:{action:"debug", context:""}})`) return { status: 'verify-failed', failures: verifyResult.failures, summary: verifyResult.summary } } phase('Finalize') await agent( `Update ${REPO}/STATUS.md to reflect the ${release} work that just completed.\n` + `Mark any in-flight items as landed. Set what is now in flight next.\n` + `Commit the STATUS.md update with message "docs: update STATUS for ${release} develop pass".`, { label: 'update-status', phase: 'Finalize' } ) log(`Single-mode develop complete. Run action=release when ready to tag.`) return { status: 'complete', mode: 'single', release } } // ── MULTI MODE ────────────────────────────────────────────────────────────── phase('Plan') const assignment = await agent( `You are the PM for the ${release} release of Relicario at ${REPO}.\n` + `Read these plan files:\n${manifest.plans.map(p => ' ' + p).join('\n')}\n\n` + `Decide how many dev streams are needed (one per major domain or plan, max 3). ` + `Minimize cross-dev dependencies. For each dev assign: ` + `letter (A/B/C), scope summary (2 sentences), task IDs they own, ` + `out-of-scope task IDs (owned by other devs), primary techDomain, and which planFiles they need to read. ` + `Also write a 2-sentence pmScope describing your oversight and review duties.`, { schema: ASSIGNMENT_SCHEMA, label: 'pm-assign', phase: 'Plan' } ) log(`PM assigned ${assignment.devCount} dev stream(s)`) phase('Generate') const allRoles = ['pm', ...assignment.devs.map(d => devRole(d.letter))].join(', ') await parallel([ () => agent( `Write a self-contained PM kickoff prompt to ${REPO}/${COORD_DIR}/${release}-pm-prompt.md.\n\n` + `Release: ${release}\n` + `PM scope: ${assignment.pmScope}\n` + `Plans: ${manifest.plans.join(', ')}\n` + `Dev roster:\n${assignment.devs.map(d => ` Dev-${d.letter}: ${d.scope}`).join('\n')}\n\n` + `The file must include these sections in order:\n` + `1. Role header ("You are the PM for the ${release} release of Relicario.")\n` + `2. Working directory: ${REPO}\n` + `3. Required reading: CLAUDE.md, all plan files listed above\n` + `4. Authority: approve scope changes, review dev PRs, write CHANGELOG entry, drive doc updates, tag release (with user approval only)\n` + `5. Boundaries: write NO feature code; NO destructive ops without user confirmation\n` + `6. Relay server section: localhost:7331, your from="pm", tools: post_message/read_messages/list_pending, recipients: ${allRoles}. Include Python shim fallback.\n` + `7. Dev roster with each dev letter, branch name (feature/${release}-dev-X), worktree path (${REPO}.dev-x), and scope\n` + `8. Coordination protocol: DIRECTIVE block format, RELEASE STATUS rollup format\n` + `9. PR review procedure (gh pr view / gh pr diff)\n` + `10. Pre-tag checklist (all tests pass, CHANGELOG written, STATUS.md updated, all dev PRs merged)\n` + `11. First action: read all required files, emit a RELEASE STATUS block confirming context absorbed, then check all dev inboxes\n` + `Make every section concrete — the receiving Claude has zero prior context.`, { label: 'gen-pm', phase: 'Generate' } ), ...assignment.devs.map((dev) => () => agent( `Write a self-contained Dev-${dev.letter} kickoff prompt to ${REPO}/${COORD_DIR}/${release}-dev-${dev.letter.toLowerCase()}-prompt.md.\n\n` + `Release: ${release}\n` + `Dev-${dev.letter} scope: ${dev.scope}\n` + `Tasks owned: ${dev.tasks.join(', ')}\n` + `Out of scope: ${dev.outOfScope.join(', ')}\n` + `Tech domain: ${dev.techDomain}\n` + `Plan files: ${dev.planFiles.join(', ')}\n\n` + `The file must include these sections in order:\n` + `1. Role header ("You are Dev-${dev.letter} for the ${release} release of Relicario.")\n` + `2. Worktree setup commands (run these FIRST before anything else):\n` + ` git -C ${REPO} worktree add ${REPO}.dev-${dev.letter.toLowerCase()} -b feature/${release}-dev-${dev.letter.toLowerCase()}\n` + ` cd ${REPO}.dev-${dev.letter.toLowerCase()}\n` + `3. Working directory after setup: ${REPO}.dev-${dev.letter.toLowerCase()}\n` + `4. CRITICAL subagent rule: every subagent prompt MUST start with "cd ${REPO}.dev-${dev.letter.toLowerCase()} &&" — never rely on working-directory headers alone\n` + `5. Required reading: CLAUDE.md, ${dev.planFiles.join(', ')}\n` + `6. Execution mode: use superpowers:subagent-driven-development\n` + `7. Scope: in-scope tasks (${dev.tasks.join(', ')}), out-of-scope (${dev.outOfScope.join(', ')})\n` + `8. Hard rules from the plan (copy any HIGH-severity or acceptance-test constraints verbatim)\n` + `9. Relay: localhost:7331, your from="${devRole(dev.letter)}", call read_messages before each task, post status/questions to "pm". Recipients: ${allRoles}. Include Python shim fallback.\n` + `10. STATUS UPDATE format: Task / Status (COMPLETE|IN-PROGRESS|BLOCKED) / Notes (what + why) / Next — print locally AND post to pm via relay\n` + `11. Final test commands for ${dev.techDomain}\n` + `12. PR procedure: gh pr create targeting main, title "feat(${release}): Dev-${dev.letter} — "\n` + `13. First action: run worktree setup, emit STATUS UPDATE "setup complete", start Task 1`, { label: `gen-dev-${dev.letter.toLowerCase()}`, phase: 'Generate' } )), ]) log(`Generated ${assignment.devCount + 1} prompt files in ${COORD_DIR}/`) log(``) log(`Next steps:`) log(` 1. cd ${REPO}/tools/relay && ./start.sh`) log(` 2. Open ${assignment.devCount + 1} terminal windows`) log(` 3. PM terminal → paste ${COORD_DIR}/${release}-pm-prompt.md`) assignment.devs.forEach(dev => { log(` Dev-${dev.letter} terminal → paste ${COORD_DIR}/${release}-dev-${dev.letter.toLowerCase()}-prompt.md`) }) return { status: 'prompts-ready', devCount: assignment.devCount, coordDir: COORD_DIR } } // ── ACTION: debug ───────────────────────────────────────────────────────────── if (action === 'debug') { if (!context) throw new Error('args.context required for action=debug — describe the failure or paste test output') let currentContext = context const MAX_ITERATIONS = 5 for (let i = 1; i <= MAX_ITERATIONS; i++) { phase(`Debug iteration ${i}`) const result = await agent( `You are debugging a failure in Relicario at ${REPO}. IMPORTANT: cd ${REPO} first.\n\n` + `Failure context:\n${currentContext}\n\n` + `Use systematic debugging:\n` + `1. Form a specific hypothesis about the root cause.\n` + `2. Read the relevant source files and tests.\n` + `3. Implement the minimal fix — no unrelated changes.\n` + `4. Run the failing test(s) to confirm they now pass.\n` + `5. Run cargo test to confirm no regressions.\n` + `6. Commit the fix if clean.\n\n` + `Return fixed=true if all tests pass, fixed=false with remainingFailures if not.`, { schema: DEBUG_RESULT_SCHEMA, label: `debug-iter-${i}` } ) log(`Iteration ${i}: ${result.summary}`) if (result.fixed) { log(`Fixed after ${i} iteration(s).`) return { status: 'fixed', iterations: i, summary: result.summary } } currentContext = result.remainingFailures || currentContext log(`Still failing — next iteration with updated context`) } log(`Reached max iterations (${MAX_ITERATIONS}). Manual intervention needed.`) return { status: 'max-iterations', lastContext: currentContext } } // ── ACTION: verify ──────────────────────────────────────────────────────────── if (action === 'verify') { phase('Verify') const result = await agent( `Run the full Relicario test suite from ${REPO}. IMPORTANT: cd ${REPO} first.\n` + ` cargo test\n` + ` cargo build --all-targets\n` + ` cargo clippy -- -D warnings\n` + `Report pass/fail for each. List every failure with its error text.`, { schema: VERIFY_RESULT_SCHEMA, label: 'verify' } ) if (result.allPass) { log(`All checks pass.`) } else { log(`FAILED: ${result.failures.join(' | ')}`) log(`Fix with: Workflow({name:"release", args:{action:"debug", context:""}})`) } return result } // ── ACTION: release ─────────────────────────────────────────────────────────── if (action === 'release') { if (!release) throw new Error('args.release is required for action=release') phase('Verify') const verifyResult = await agent( `Run the full Relicario test suite from ${REPO}. IMPORTANT: cd ${REPO} first.\n` + ` cargo test\n` + ` cargo build --all-targets\n` + ` cargo clippy -- -D warnings\n` + `Report pass/fail. List failures.`, { schema: VERIFY_RESULT_SCHEMA, label: 'pre-release-verify' } ) if (!verifyResult.allPass) { log(`Tests failing — cannot cut release. Fix first with action=debug.`) return { status: 'blocked', failures: verifyResult.failures } } phase('Finalize') await agent( `Cut the ${release} release for Relicario at ${REPO}. IMPORTANT: cd ${REPO} first.\n\n` + `Steps (in order):\n` + `1. Run: git log $(git describe --tags --abbrev=0)..HEAD --oneline\n` + ` Use that output to write a ${release} section in CHANGELOG.md — user-facing language, grouped by type.\n` + `2. Update STATUS.md: mark ${release} as released, set what is next.\n` + `3. Update ROADMAP.md: check off the ${release} milestone.\n` + `4. Commit those doc updates: git commit -m "release: ${release}"\n` + `5. Create annotated tag: git tag -a ${release} -m "Release ${release}"\n` + `6. STOP. Print the tag SHA and the push command, then ask the user to confirm before pushing.\n` + ` Do NOT run git push or git push --tags without explicit user confirmation.`, { label: 'cut-release', phase: 'Finalize' } ) return { status: 'tagged', release, note: 'Confirm and push manually.' } } log(`Unknown action: "${action}". Valid: develop, debug, verify, release`) return { status: 'error', action }