/** * PR-37 Delivery Planning — Entry Point * * Parts F, G, H, I: * - F: Sprint Planner * - G: Release Planner * - H: Batch Planner * - I: Capacity Planner * * Consumes SF-02 Requirement Package output and generates delivery * planning artifacts. Compatible with PR-36 Factory Registry. * * @module pr-37-delivery-planning * @since PR-37 */ import { generateSprintPlan, defaultSprintConfig } from "./sprint-planner.mjs"; import { generateReleasePlan } from "./release-planner.mjs"; import { generateBatchPlan } from "./batch-planner.mjs"; import { generateCapacityPlan, defaultCapacityConfig } from "./capacity-planner.mjs"; import { alignRoadmapWithPlans } from "./roadmap-aligner.mjs"; // ═══════════════════════════════════════════════════════ // PR-37 Artifact Constants // ═══════════════════════════════════════════════════════ export const PR37_ARTIFACTS = Object.freeze({ SPRINT_PLAN: "sprint-plan", RELEASE_PLAN: "release-plan", BATCH_PLAN: "batch-plan", CAPACITY_PLAN: "capacity-plan", ALIGNMENT_REPORT: "alignment-report", }); // ═══════════════════════════════════════════════════════ // Factory Integration // ═══════════════════════════════════════════════════════ /** * Create a delivery planning factory that consumes SF-02 upstream artifacts * and produces all 4 delivery plans. * * The factory accepts a PR-36 FactoryInput object (or a simplified upstream * artifacts object) and returns all plans bundled as a FactoryOutput. * * @param {object} upstreamArtifacts — SF-02 output containing: * - epics: object[] * - features: object[] * - userStories: object[] * - dependencies / graph: dependency graph * - priorities: priority matrix entries * - mvpScope: MVP scope definition * @param {object} [config] — Optional config overrides * @param {object} [config.sprint] — Sprint planner config * @param {object} [config.capacity] — Capacity planner config * @returns {object} FactoryOutput with all delivery plans * * @example * ```js * const factory = createDeliveryPlanningFactory(); * const output = factory(sf02Output, { * sprint: { teamVelocity: 20, maxStoryPointsPerSprint: 25 }, * capacity: { teamSize: 5, availabilityFactor: 0.8 }, * }); * // output.sprintPlan, output.releasePlan, output.batchPlan, output.capacityPlan * ``` */ export function createDeliveryPlanningFactory(upstreamArtifacts, config = {}) { if (!upstreamArtifacts) { return { factoryId: "PR-37", status: "failed", error: "No upstream artifacts provided. Provide SF-02 output.", }; } const warnings = []; // ── 1. Extract SF-02 artifacts ── const stories = upstreamArtifacts.userStories || upstreamArtifacts.stories || []; const epics = upstreamArtifacts.epics || []; const features = upstreamArtifacts.features || []; // Support both { graph: { nodes, edges } } and flat { nodes, edges } const dependencies = upstreamArtifacts.dependencies || upstreamArtifacts.graph || { nodes: [], edges: [] }; const priorities = upstreamArtifacts.priorities || []; const mvpScope = upstreamArtifacts.mvpScope || null; // ── 2. Validate data ── if (stories.length === 0) { warnings.push("No user stories found in upstream artifacts"); } // ── 3. Generate Sprint Plan (Part F) ── const sprintConfig = defaultSprintConfig(config.sprint || {}); const sprintPlan = generateSprintPlan({ stories, dependencies, priorities, config: sprintConfig, }); if (sprintPlan.warnings && sprintPlan.warnings.length > 0) { warnings.push(...sprintPlan.warnings.map(w => `[Sprint] ${w}`)); } // ── 4. Generate Release Plan (Part G) ── const releasePlan = generateReleasePlan({ mvpScope, priorities, dependencies, stories, epics, features, }); if (releasePlan.warnings && releasePlan.warnings.length > 0) { warnings.push(...releasePlan.warnings.map(w => `[Release] ${w}`)); } // ── 5. Generate Batch Plan (Part H) ── const batchPlan = generateBatchPlan({ stories, dependencies, priorities, sprints: sprintPlan, releases: releasePlan, }); if (batchPlan.warnings && batchPlan.warnings.length > 0) { warnings.push(...batchPlan.warnings.map(w => `[Batch] ${w}`)); } // ── 6. Generate Capacity Plan (Part I) ── const capacityConfig = defaultCapacityConfig(config.capacity || {}); const capacityPlan = generateCapacityPlan({ sprints: sprintPlan, releases: releasePlan, config: capacityConfig, }); if (capacityPlan.warnings && capacityPlan.warnings.length > 0) { warnings.push(...capacityPlan.warnings.map(w => `[Capacity] ${w}`)); } // ── 7. Align with Roadmap (Part J) ── const roadmap = upstreamArtifacts.roadmap || mvpScope?.phases; const alignmentReport = roadmap ? alignRoadmapWithPlans({ roadmap: { phases: Array.isArray(roadmap) ? roadmap : (roadmap.phases || []) }, sprintPlan, releasePlan, batchPlan, mvpScope, }) : null; // ── 8. Assemble output ── const output = { factoryId: "PR-37", status: warnings.length > 0 && stories.length === 0 ? "blocked" : "passed", sprintPlan, releasePlan, batchPlan, capacityPlan, alignmentReport, warnings: warnings.length > 0 ? warnings : undefined, metadata: { factoryId: "PR-37", upstreamArtifactId: upstreamArtifacts.id || "SF-02", generatedAt: new Date().toISOString(), totalStories: stories.length, totalEpics: epics.length, totalFeatures: features.length, totalSprints: sprintPlan.metrics.totalSprints, totalReleases: releasePlan.releases ? releasePlan.releases.length : 0, }, }; return output; } // ═══════════════════════════════════════════════════════ // Barrel Re-exports // ═══════════════════════════════════════════════════════ export { generateSprintPlan, defaultSprintConfig } from "./sprint-planner.mjs"; export { generateReleasePlan } from "./release-planner.mjs"; export { generateBatchPlan } from "./batch-planner.mjs"; export { generateCapacityPlan, defaultCapacityConfig } from "./capacity-planner.mjs"; export { alignRoadmapWithPlans } from "./roadmap-aligner.mjs";