(stack)
| 309 | } |
| 310 | |
| 311 | function renderStackPlan(stack) { |
| 312 | const lines = [ |
| 313 | 'Citadel Stack Landing Plan', |
| 314 | '='.repeat(40), |
| 315 | `Generated: ${stack.generatedAt}`, |
| 316 | `Status: ${stack.status}`, |
| 317 | `Project: ${stack.projectRoot}`, |
| 318 | '', |
| 319 | 'Next Action', |
| 320 | ` Label: ${stack.nextAction.label}`, |
| 321 | ` Command: ${stack.nextAction.command}`, |
| 322 | ` Can run now: ${stack.nextAction.canRunNow ? 'yes' : 'no'}`, |
| 323 | ` Why: ${stack.nextAction.why}`, |
| 324 | '', |
| 325 | 'Landing Order', |
| 326 | ]; |
| 327 | |
| 328 | if (stack.reports.length === 0) { |
| 329 | lines.push(' (no PR readiness reports found)'); |
| 330 | } else { |
| 331 | for (const [index, report] of stack.reports.entries()) { |
| 332 | lines.push(` ${index + 1}. ${report.branch}`); |
| 333 | lines.push(` PR: ${report.pr || '(missing)'}`); |
| 334 | lines.push(` Head: ${report.head || '(unknown)'}`); |
| 335 | if (report.currentHead) lines.push(` Current: ${report.currentHead}`); |
| 336 | lines.push(` Readiness: ${report.status}`); |
| 337 | lines.push(` Verification: ${report.gates.verification.pass ? 'pass' : 'fail'} (${report.gates.verification.detail})`); |
| 338 | lines.push(` Report: ${report.path}`); |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | lines.push(''); |
| 343 | lines.push('Blocked Items'); |
| 344 | if (stack.blocked.length === 0) { |
| 345 | lines.push(' (none)'); |
| 346 | } else { |
| 347 | for (const entry of stack.blocked) { |
| 348 | lines.push(` - ${entry.report.branch}: ${entry.reasons.join('; ')}`); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | lines.push(''); |
| 353 | lines.push('Approval Boundary'); |
| 354 | if (stack.status === 'approval-needed') { |
| 355 | lines.push(' Human approval is required before marking draft PRs ready or merging the stack.'); |
| 356 | if (stack.approvalCapsule?.path) lines.push(` Approval capsule: ${stack.approvalCapsule.path}`); |
| 357 | } else if (stack.status === 'blocked') { |
| 358 | lines.push(' Resolve blocked readiness gates before requesting merge approval.'); |
| 359 | } else { |
| 360 | lines.push(' Generate PR readiness reports before requesting stack approval.'); |
| 361 | } |
| 362 | |
| 363 | lines.push(''); |
| 364 | lines.push('Post-Approval Landing Runbook'); |
| 365 | for (const [index, item] of stack.postApprovalRunbook.entries()) { |
| 366 | lines.push(` ${index + 1}. ${item.step}`); |
| 367 | lines.push(` Gate: ${item.gate}`); |
| 368 | lines.push(` Action: ${item.action}`); |
no outgoing calls
no test coverage detected