Files
Claude-Code-Workflow/.codex/skills/spec-generator/phases/07-issue-export.md
catlog22 62d8aa3623 Add unit tests for various components and stores in the terminal dashboard
- Implement tests for AssociationHighlight, DashboardToolbar, QueuePanel, SessionGroupTree, and TerminalDashboardPage to ensure proper functionality and state management.
- Create tests for cliSessionStore, issueQueueIntegrationStore, queueExecutionStore, queueSchedulerStore, sessionManagerStore, and terminalGridStore to validate state resets and workspace scoping.
- Mock necessary dependencies and state management hooks to isolate tests and ensure accurate behavior.
2026-03-08 21:38:20 +08:00

10 KiB

Phase 7: Issue Export

Map specification Epics to issues, create them via ccw issue create, and generate an export report with spec document links.

Execution Mode: Inline This phase runs in the main orchestrator context (not delegated to agent) for direct access to ccw issue create CLI and interactive handoff options.

Objective

  • Read all EPIC-*.md files from Phase 5 output
  • Assign waves: MVP epics → wave-1, non-MVP → wave-2
  • Create one issue per Epic via ccw issue create
  • Map Epic dependencies to issue dependencies
  • Generate issue-export-report.md with mapping table and spec links
  • Present handoff options for execution

Input

  • Dependency: {workDir}/epics/_index.md (and individual EPIC-*.md files)
  • Reference: {workDir}/readiness-report.md, {workDir}/spec-config.json
  • Reference: {workDir}/product-brief.md, {workDir}/requirements/_index.md, {workDir}/architecture/_index.md

Execution Steps

Step 1: Load Epic Files

const specConfig = JSON.parse(Read(`${workDir}/spec-config.json`));
const epicFiles = Glob(`${workDir}/epics/EPIC-*.md`);
const epicsIndex = Read(`${workDir}/epics/_index.md`);

// Parse each Epic file
const epics = epicFiles.map(epicFile => {
  const content = Read(epicFile);
  const fm = parseFrontmatter(content);
  const title = extractTitle(content);
  const description = extractSection(content, "Description");
  const stories = extractSection(content, "Stories");
  const reqRefs = extractSection(content, "Requirements");
  const adrRefs = extractSection(content, "Architecture");
  const deps = fm.dependencies || [];

  return {
    file: epicFile,
    id: fm.id,           // e.g., EPIC-001
    title,
    description,
    stories,
    reqRefs,
    adrRefs,
    priority: fm.priority,
    mvp: fm.mvp || false,
    dependencies: deps,   // other EPIC IDs this depends on
    size: fm.size
  };
});

Step 2: Wave Assignment

const epicWaves = epics.map(epic => ({
  ...epic,
  wave: epic.mvp ? 1 : 2
}));

// Log wave assignment
const wave1 = epicWaves.filter(e => e.wave === 1);
const wave2 = epicWaves.filter(e => e.wave === 2);
// wave-1: MVP epics (must-have, core functionality)
// wave-2: Post-MVP epics (should-have, enhancements)

Step 3: Issue Creation Loop

const createdIssues = [];
const epicToIssue = {};  // EPIC-ID -> Issue ID mapping

for (const epic of epicWaves) {
  // Build issue JSON matching roadmap-with-file schema
  const issueData = {
    title: `[${specConfig.session_id}] ${epic.title}`,
    status: "pending",
    priority: epic.wave === 1 ? 2 : 3,  // wave-1 = higher priority
    context: `## ${epic.title}

${epic.description}

## Stories
${epic.stories}

## Spec References
- Epic: ${epic.file}
- Requirements: ${epic.reqRefs}
- Architecture: ${epic.adrRefs}
- Product Brief: ${workDir}/product-brief.md
- Full Spec: ${workDir}/`,
    source: "text",
    tags: [
      "spec-generated",
      `spec:${specConfig.session_id}`,
      `wave-${epic.wave}`,
      epic.mvp ? "mvp" : "post-mvp",
      `epic:${epic.id}`
    ],
    extended_context: {
      notes: {
        session: specConfig.session_id,
        spec_dir: workDir,
        source_epic: epic.id,
        wave: epic.wave,
        depends_on_issues: [],  // Filled in Step 4
        spec_documents: {
          product_brief: `${workDir}/product-brief.md`,
          requirements: `${workDir}/requirements/_index.md`,
          architecture: `${workDir}/architecture/_index.md`,
          epic: epic.file
        }
      }
    },
    lifecycle_requirements: {
      test_strategy: "acceptance",
      regression_scope: "affected",
      acceptance_type: "manual",
      commit_strategy: "per-epic"
    }
  };

  // Create issue via ccw issue create (pipe JSON to avoid shell escaping)
  const result = Bash(`echo '${JSON.stringify(issueData)}' | ccw issue create`);

  // Parse returned issue ID
  const issueId = JSON.parse(result).id;  // e.g., ISS-20260308-001
  epicToIssue[epic.id] = issueId;

  createdIssues.push({
    epic_id: epic.id,
    epic_title: epic.title,
    issue_id: issueId,
    wave: epic.wave,
    priority: issueData.priority,
    mvp: epic.mvp
  });
}

Step 4: Epic Dependency → Issue Dependency Mapping

// Map EPIC dependencies to Issue dependencies
for (const epic of epicWaves) {
  if (epic.dependencies.length === 0) continue;

  const issueId = epicToIssue[epic.id];
  const depIssueIds = epic.dependencies
    .map(depEpicId => epicToIssue[depEpicId])
    .filter(Boolean);

  if (depIssueIds.length > 0) {
    // Update issue's extended_context.notes.depends_on_issues
    // This is informational — actual dependency enforcement is in execution phase
    // Note: ccw issue create already created the issue; dependency info is in the context
  }
}

Step 5: Generate issue-export-report.md

const timestamp = new Date().toISOString();

const reportContent = `---
session_id: ${specConfig.session_id}
phase: 7
document_type: issue-export-report
status: complete
generated_at: ${timestamp}
stepsCompleted: ["load-epics", "wave-assignment", "issue-creation", "dependency-mapping", "report-generation"]
version: 1
dependencies:
  - epics/_index.md
  - readiness-report.md
---

# Issue Export Report

## Summary

- **Session**: ${specConfig.session_id}
- **Issues Created**: ${createdIssues.length}
- **Wave 1 (MVP)**: ${wave1.length} issues
- **Wave 2 (Post-MVP)**: ${wave2.length} issues
- **Export Date**: ${timestamp}

## Issue Mapping

| Epic ID | Epic Title | Issue ID | Wave | Priority | MVP |
|---------|-----------|----------|------|----------|-----|
${createdIssues.map(i =>
  `| ${i.epic_id} | ${i.epic_title} | ${i.issue_id} | ${i.wave} | ${i.priority} | ${i.mvp ? 'Yes' : 'No'} |`
).join('\n')}

## Spec Document Links

| Document | Path | Description |
|----------|------|-------------|
| Product Brief | ${workDir}/product-brief.md | Vision, goals, scope |
| Requirements | ${workDir}/requirements/_index.md | Functional + non-functional requirements |
| Architecture | ${workDir}/architecture/_index.md | Components, ADRs, tech stack |
| Epics | ${workDir}/epics/_index.md | Epic/Story breakdown |
| Readiness Report | ${workDir}/readiness-report.md | Quality validation |
| Spec Summary | ${workDir}/spec-summary.md | Executive summary |

## Dependency Map

| Issue ID | Depends On |
|----------|-----------|
${createdIssues.map(i => {
  const epic = epicWaves.find(e => e.id === i.epic_id);
  const deps = (epic.dependencies || []).map(d => epicToIssue[d]).filter(Boolean);
  return `| ${i.issue_id} | ${deps.length > 0 ? deps.join(', ') : 'None'} |`;
}).join('\n')}

## Next Steps

1. **team-planex**: Execute all issues via coordinated team workflow
2. **Wave 1 only**: Execute MVP issues first (${wave1.length} issues)
3. **View issues**: Browse created issues via \`ccw issue list --tag spec:${specConfig.session_id}\`
4. **Manual review**: Review individual issues before execution
`;

Write(`${workDir}/issue-export-report.md`, reportContent);

Step 6: Update spec-config.json

specConfig.issue_ids = createdIssues.map(i => i.issue_id);
specConfig.issues_created = createdIssues.length;
specConfig.phasesCompleted.push({
  phase: 7,
  name: "issue-export",
  output_file: "issue-export-report.md",
  issues_created: createdIssues.length,
  wave_1_count: wave1.length,
  wave_2_count: wave2.length,
  completed_at: timestamp
});
Write(`${workDir}/spec-config.json`, JSON.stringify(specConfig, null, 2));

Step 7: Handoff Options

AskUserQuestion({
  questions: [
    {
      question: `${createdIssues.length} issues created from ${epicWaves.length} Epics. What would you like to do next?`,
      header: "Next Step",
      multiSelect: false,
      options: [
        {
          label: "Execute via team-planex",
          description: `Execute all ${createdIssues.length} issues with coordinated team workflow`
        },
        {
          label: "Wave 1 only",
          description: `Execute ${wave1.length} MVP issues first`
        },
        {
          label: "View issues",
          description: "Browse created issues before deciding"
        },
        {
          label: "Done",
          description: "Export complete, handle manually"
        }
      ]
    }
  ]
});

// Based on user selection:
if (selection === "Execute via team-planex") {
  const issueIds = createdIssues.map(i => i.issue_id).join(',');
  Skill({ skill: "team-planex", args: `--issues ${issueIds}` });
}

if (selection === "Wave 1 only") {
  const wave1Ids = createdIssues.filter(i => i.wave === 1).map(i => i.issue_id).join(',');
  Skill({ skill: "team-planex", args: `--issues ${wave1Ids}` });
}

if (selection === "View issues") {
  Bash(`ccw issue list --tag spec:${specConfig.session_id}`);
}

Output

  • File: issue-export-report.md — Issue mapping table + spec links + next steps
  • Updated: .workflow/issues/issues.jsonl — New issue entries appended
  • Updated: spec-config.json — Phase 7 completion + issue IDs

Quality Checklist

  • All MVP Epics have corresponding issues created
  • All non-MVP Epics have corresponding issues created
  • Issue tags include spec-generated and spec:{session_id}
  • Issue extended_context.notes.spec_documents paths are correct
  • Wave assignment matches MVP status (MVP → wave-1, non-MVP → wave-2)
  • Epic dependencies mapped to issue dependency references
  • issue-export-report.md generated with mapping table
  • spec-config.json updated with issue_ids and issues_created
  • Handoff options presented

Error Handling

Error Blocking? Action
ccw issue create fails for one Epic No Log error, continue with remaining Epics, report partial creation
No EPIC files found Yes Error and return to Phase 5
All issue creations fail Yes Error with CLI diagnostic, suggest manual creation
Dependency EPIC not found in mapping No Skip dependency link, log warning

Completion

Phase 7 is the final phase. The specification package has been fully converted to executable issues ready for team-planex or manual execution.