mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-11 17:21:03 +08:00
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.
This commit is contained in:
329
.codex/skills/spec-generator/phases/07-issue-export.md
Normal file
329
.codex/skills/spec-generator/phases/07-issue-export.md
Normal file
@@ -0,0 +1,329 @@
|
||||
# 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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```javascript
|
||||
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.
|
||||
Reference in New Issue
Block a user