mirror of
https://github.com/jparkerweb/plan2code.git
synced 2026-07-21 18:33:22 -07:00
metrics: adopt METRICS_JSON HTML comment as primary format
Plan, document, and finalize prompts now emit a single
<!-- METRICS_JSON {...} --> block with all structured fields. Collector
parses these directly and keeps the old prose/table parsing as fallback.
Also adds a per-task complexity check to the document prompt:
split tasks with 3+ complexity signals (5+ logic branches, 2+
integration points, shared interface mutation, etc.), combine
adjacent trivial tasks.
This commit is contained in:
@@ -36,6 +36,29 @@ function readFileSafe(filePath: string): string | null {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract METRICS_JSON HTML comment blocks from a file.
|
||||||
|
* Format: <!-- METRICS_JSON {"key": value, ...} -->
|
||||||
|
* A file may contain multiple blocks (e.g., overview.md has document + finalize).
|
||||||
|
* If `stepFilter` is provided, returns only the block with matching "step" field.
|
||||||
|
* Returns parsed object or null if not found / invalid.
|
||||||
|
*/
|
||||||
|
function extractMetricsJson(content: string, stepFilter?: string): Record<string, unknown> | null {
|
||||||
|
const re = /<!--\s*METRICS_JSON\s+(\{[\s\S]*?\})\s*-->/g;
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
while ((match = re.exec(content)) !== null) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(match[1]) as Record<string, unknown>;
|
||||||
|
if (!stepFilter || parsed['step'] === stepFilter) {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// try next match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Strip the "## Success Criteria" section so its checkboxes aren't counted as tasks. */
|
/** Strip the "## Success Criteria" section so its checkboxes aren't counted as tasks. */
|
||||||
function stripSuccessCriteriaSection(overview: string): string {
|
function stripSuccessCriteriaSection(overview: string): string {
|
||||||
return overview.replace(/^## Success Criteria\s*\n[\s\S]*?(?=\n## |\n*$)/m, '');
|
return overview.replace(/^## Success Criteria\s*\n[\s\S]*?(?=\n## |\n*$)/m, '');
|
||||||
@@ -53,8 +76,8 @@ function parseCompletionSummaryTaskCount(overview: string): { completed: number;
|
|||||||
if (!summaryMatch) return null;
|
if (!summaryMatch) return null;
|
||||||
const summary = summaryMatch[1];
|
const summary = summaryMatch[1];
|
||||||
|
|
||||||
// "Completion Rate: X% (Y/Z tasks)"
|
// "Completion Rate: X% (Y/Z tasks)" — handles bold markdown and various separators
|
||||||
const rateMatch = summary.match(/Completion Rate[:\s*]*\d{1,3}%\s*\((\d+)\/(\d+)\s*tasks?\)/i);
|
const rateMatch = summary.match(/\**Completion(?:\s+Rate)?\**[:\s*]+\d{1,3}%\s*\((\d+)\/(\d+)\s*tasks?\)/i);
|
||||||
if (rateMatch) return { completed: parseInt(rateMatch[1], 10), total: parseInt(rateMatch[2], 10) };
|
if (rateMatch) return { completed: parseInt(rateMatch[1], 10), total: parseInt(rateMatch[2], 10) };
|
||||||
|
|
||||||
// "Total Tasks | 28 (28/28 complete"
|
// "Total Tasks | 28 (28/28 complete"
|
||||||
@@ -119,16 +142,59 @@ export function collectStep1(specDir: string): Step1PlanMetrics {
|
|||||||
draftFiles.sort();
|
draftFiles.sort();
|
||||||
const latestDraft = readFileSafe(draftFiles[draftFiles.length - 1]) ?? '';
|
const latestDraft = readFileSafe(draftFiles[draftFiles.length - 1]) ?? '';
|
||||||
|
|
||||||
// Parse "Confidence: XX%" — look for overall or total confidence
|
// ── Primary source: METRICS_JSON HTML comment ──
|
||||||
let finalConfidence: number | null = null;
|
// Format: <!-- METRICS_JSON {"confidence": 95, "clarification_rounds": 0, ...} -->
|
||||||
const confMatch = latestDraft.match(/(?:Overall|Final|Total)?\s*[Cc]onfidence[:\s]+(\d{1,3})%/);
|
const metricsJson = extractMetricsJson(latestDraft);
|
||||||
|
if (metricsJson) {
|
||||||
|
const num = (key: string): number | null => {
|
||||||
|
const v = metricsJson[key];
|
||||||
|
return typeof v === 'number' ? v : null;
|
||||||
|
};
|
||||||
|
const bd = metricsJson['confidence_breakdown'] as Record<string, number> | undefined;
|
||||||
|
return {
|
||||||
|
present: true,
|
||||||
|
final_confidence: num('confidence'),
|
||||||
|
confidence_breakdown: bd ? {
|
||||||
|
requirements: bd.requirements ?? null,
|
||||||
|
feasibility: bd.feasibility ?? null,
|
||||||
|
integration: bd.integration ?? null,
|
||||||
|
risk: bd.risk ?? null,
|
||||||
|
} : null,
|
||||||
|
clarification_rounds: num('clarification_rounds'),
|
||||||
|
tech_stack_revision_rounds: num('tech_stack_revision_rounds'),
|
||||||
|
verification_gaps_found: num('verification_gaps_found'),
|
||||||
|
functional_requirements_count: num('functional_requirements_count'),
|
||||||
|
non_functional_requirements_count: num('non_functional_requirements_count'),
|
||||||
|
risk_count: num('risk_count'),
|
||||||
|
phase_count: num('phase_count'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fallback: structured ## Planning Metrics appendix ──
|
||||||
|
// Handles both plain (confidence: 95) and bold (**confidence:** 95) field formats
|
||||||
|
// Planning Metrics is typically the last section, so we capture greedily to end,
|
||||||
|
// but stop at the next ## heading or --- if present.
|
||||||
|
const metricsSection = latestDraft.match(/^##\s+Planning\s+Metrics\s*\n([\s\S]+?)(?:\n##\s|\n---)/m)
|
||||||
|
?? latestDraft.match(/^##\s+Planning\s+Metrics\s*\n([\s\S]+)/m);
|
||||||
|
const metricsBlock = metricsSection?.[1] ?? '';
|
||||||
|
const parseMetricField = (field: string): number | null => {
|
||||||
|
// Match plain "field: N" or bold "**field:** N"
|
||||||
|
const m = metricsBlock.match(new RegExp(`^\\**${field}\\**[:\\s*]+?(\\d+)`, 'm'));
|
||||||
|
return m ? parseInt(m[1], 10) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── Confidence ──
|
||||||
|
let finalConfidence = parseMetricField('confidence');
|
||||||
|
if (finalConfidence === null) {
|
||||||
|
// Fallback: prose scraping — handle bold markdown like **Confidence:** 95%
|
||||||
|
const confMatch = latestDraft.match(/(?:Overall|Final|Total)?\s*\**[Cc]onfidence\**[:\s*]+(\d{1,3})%/);
|
||||||
if (confMatch) {
|
if (confMatch) {
|
||||||
finalConfidence = parseInt(confMatch[1], 10);
|
finalConfidence = parseInt(confMatch[1], 10);
|
||||||
} else {
|
} else {
|
||||||
// Try table format: | Confidence | 92 |
|
|
||||||
const tableMatch = latestDraft.match(/[|]\s*[Cc]onfidence\s*[|]\s*(\d{1,3})/);
|
const tableMatch = latestDraft.match(/[|]\s*[Cc]onfidence\s*[|]\s*(\d{1,3})/);
|
||||||
if (tableMatch) finalConfidence = parseInt(tableMatch[1], 10);
|
if (tableMatch) finalConfidence = parseInt(tableMatch[1], 10);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Confidence breakdown (requirements, feasibility, integration, risk)
|
// Confidence breakdown (requirements, feasibility, integration, risk)
|
||||||
let confidenceBreakdown: Step1PlanMetrics['confidence_breakdown'] = null;
|
let confidenceBreakdown: Step1PlanMetrics['confidence_breakdown'] = null;
|
||||||
@@ -145,35 +211,53 @@ export function collectStep1(specDir: string): Step1PlanMetrics {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count ### FR- / ### NFR- headings
|
// ── Clarification rounds ──
|
||||||
const frCount = (latestDraft.match(/###\s+FR-/g) ?? []).length;
|
let clarificationRounds = parseMetricField('clarification_rounds');
|
||||||
const nfrCount = (latestDraft.match(/###\s+NFR-/g) ?? []).length;
|
|
||||||
|
|
||||||
// Count risk table rows (lines starting with | that contain risk-level keywords)
|
// ── Verification gaps ──
|
||||||
|
let verificationGaps = parseMetricField('verification_gaps_found');
|
||||||
|
|
||||||
|
// ── Functional / non-functional requirements ──
|
||||||
|
let frCount = parseMetricField('functional_requirements_count');
|
||||||
|
if (frCount === null) {
|
||||||
|
frCount = (latestDraft.match(/###\s+FR-/g) ?? []).length || null;
|
||||||
|
}
|
||||||
|
let nfrCount = parseMetricField('non_functional_requirements_count');
|
||||||
|
if (nfrCount === null) {
|
||||||
|
nfrCount = (latestDraft.match(/###\s+NFR-/g) ?? []).length || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Risk count ──
|
||||||
|
let riskCount = parseMetricField('risk_count');
|
||||||
|
if (riskCount === null) {
|
||||||
const riskRows = latestDraft.match(/^\s*[|][^|]*(?:High|Medium|Low|Critical)[^|]*[|]/gm) ?? [];
|
const riskRows = latestDraft.match(/^\s*[|][^|]*(?:High|Medium|Low|Critical)[^|]*[|]/gm) ?? [];
|
||||||
const riskCount = riskRows.length || null;
|
riskCount = riskRows.length || null;
|
||||||
|
}
|
||||||
|
|
||||||
// Count ## Phase headings
|
// ── Phase count ──
|
||||||
const phaseCount = (latestDraft.match(/^##\s+Phase\s+\d/gm) ?? []).length || null;
|
let phaseCount = parseMetricField('phase_count');
|
||||||
|
if (phaseCount === null) {
|
||||||
|
phaseCount = (latestDraft.match(/^##\s+Phase\s+\d/gm) ?? []).length || null;
|
||||||
|
}
|
||||||
|
|
||||||
// Clarification rounds from conversation file
|
// ── Tech stack revision rounds (conversation file only) ──
|
||||||
let clarificationRounds: number | null = null;
|
|
||||||
let techStackRevisions: number | null = null;
|
let techStackRevisions: number | null = null;
|
||||||
let verificationGaps: number | null = null;
|
|
||||||
|
|
||||||
|
// Fall back to conversation file scraping for fields not found in appendix
|
||||||
if (convFiles.length > 0) {
|
if (convFiles.length > 0) {
|
||||||
convFiles.sort();
|
convFiles.sort();
|
||||||
const latestConv = readFileSafe(convFiles[convFiles.length - 1]) ?? '';
|
const latestConv = readFileSafe(convFiles[convFiles.length - 1]) ?? '';
|
||||||
// Count heading repetitions as clarification rounds (## Clarification or ## Round)
|
if (clarificationRounds === null) {
|
||||||
const clarRounds = (latestConv.match(/^##\s+(?:Clarification|Round)\s+\d/gm) ?? []).length;
|
const clarRounds = (latestConv.match(/^##\s+(?:Clarification|Round)\s+\d/gm) ?? []).length;
|
||||||
clarificationRounds = clarRounds || null;
|
clarificationRounds = clarRounds || null;
|
||||||
// Tech stack revision rounds
|
}
|
||||||
const techRounds = (latestConv.match(/^##\s+(?:Tech\s+Stack|Technology)\s+Revision/gmi) ?? []).length;
|
const techRounds = (latestConv.match(/^##\s+(?:Tech\s+Stack|Technology)\s+Revision/gmi) ?? []).length;
|
||||||
techStackRevisions = techRounds || null;
|
techStackRevisions = techRounds || null;
|
||||||
// Verification gaps found
|
if (verificationGaps === null) {
|
||||||
const gapMatches = latestConv.match(/(?:verification\s+gap|gap\s+found|missing\s+requirement)/gi) ?? [];
|
const gapMatches = latestConv.match(/(?:verification\s+gap|gap\s+found|missing\s+requirement)/gi) ?? [];
|
||||||
verificationGaps = gapMatches.length || null;
|
verificationGaps = gapMatches.length || null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
present: true,
|
present: true,
|
||||||
@@ -182,8 +266,8 @@ export function collectStep1(specDir: string): Step1PlanMetrics {
|
|||||||
clarification_rounds: clarificationRounds,
|
clarification_rounds: clarificationRounds,
|
||||||
tech_stack_revision_rounds: techStackRevisions,
|
tech_stack_revision_rounds: techStackRevisions,
|
||||||
verification_gaps_found: verificationGaps,
|
verification_gaps_found: verificationGaps,
|
||||||
functional_requirements_count: frCount || null,
|
functional_requirements_count: frCount,
|
||||||
non_functional_requirements_count: nfrCount || null,
|
non_functional_requirements_count: nfrCount,
|
||||||
risk_count: riskCount,
|
risk_count: riskCount,
|
||||||
phase_count: phaseCount,
|
phase_count: phaseCount,
|
||||||
};
|
};
|
||||||
@@ -201,6 +285,27 @@ export function collectStep2(specDir: string): Step2DocumentMetrics {
|
|||||||
requirement_coverage_percent: null, verification_items_added: null };
|
requirement_coverage_percent: null, verification_items_added: null };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Primary source: METRICS_JSON in overview.md ──
|
||||||
|
const step2Json = extractMetricsJson(overview, 'document');
|
||||||
|
if (step2Json) {
|
||||||
|
const num = (key: string): number | null => {
|
||||||
|
const v = step2Json[key];
|
||||||
|
return typeof v === 'number' ? v : null;
|
||||||
|
};
|
||||||
|
const tpp = step2Json['tasks_per_phase'];
|
||||||
|
return {
|
||||||
|
present: true,
|
||||||
|
total_tasks: num('total_tasks'),
|
||||||
|
tasks_per_phase: Array.isArray(tpp) ? tpp : null,
|
||||||
|
phase_count: num('phase_count'),
|
||||||
|
parallel_groups_identified: num('parallel_groups_identified') ?? 0,
|
||||||
|
requirement_coverage_percent: num('requirement_coverage_percent'),
|
||||||
|
verification_items_added: num('verification_items_added'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fallback: regex scraping ──
|
||||||
|
|
||||||
// Detect Parallel Execution Groups table
|
// Detect Parallel Execution Groups table
|
||||||
const parallelGroups = (overview.match(/Parallel\s+Execution\s+Group/gi) ?? []).length;
|
const parallelGroups = (overview.match(/Parallel\s+Execution\s+Group/gi) ?? []).length;
|
||||||
|
|
||||||
@@ -278,6 +383,28 @@ export function collectStep3(specDir: string): Step3ImplementMetrics {
|
|||||||
const overview = readFileSafe(overviewPath);
|
const overview = readFileSafe(overviewPath);
|
||||||
const summaryCount = overview ? parseCompletionSummaryTaskCount(overview) : null;
|
const summaryCount = overview ? parseCompletionSummaryTaskCount(overview) : null;
|
||||||
|
|
||||||
|
// ── Primary source: METRICS_JSON in overview.md (from finalize step) ──
|
||||||
|
if (overview) {
|
||||||
|
const step3Json = extractMetricsJson(overview, 'finalize');
|
||||||
|
if (step3Json) {
|
||||||
|
const num = (key: string): number | null => {
|
||||||
|
const v = step3Json[key];
|
||||||
|
return typeof v === 'number' ? v : null;
|
||||||
|
};
|
||||||
|
const total = num('tasks_total');
|
||||||
|
const completed = num('tasks_completed');
|
||||||
|
return {
|
||||||
|
present: true,
|
||||||
|
task_completion_rate: total && total > 0 && completed != null ? completed / total : null,
|
||||||
|
tasks_completed: completed,
|
||||||
|
tasks_total: total,
|
||||||
|
blocker_count: num('blocker_count') ?? 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fallback: regex scraping ──
|
||||||
|
|
||||||
// present = true if phase files exist OR overview has completion data
|
// present = true if phase files exist OR overview has completion data
|
||||||
const present = phaseFiles.length > 0 || summaryCount != null;
|
const present = phaseFiles.length > 0 || summaryCount != null;
|
||||||
if (!present) {
|
if (!present) {
|
||||||
@@ -359,6 +486,24 @@ export function collectStep4(specDir: string): Step4FinalizeMetrics {
|
|||||||
archival_succeeded: archivalSucceeded };
|
archival_succeeded: archivalSucceeded };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Primary source: METRICS_JSON in overview.md ──
|
||||||
|
const step4Json = extractMetricsJson(overview, 'finalize');
|
||||||
|
if (step4Json) {
|
||||||
|
const num = (key: string): number | null => {
|
||||||
|
const v = step4Json[key];
|
||||||
|
return typeof v === 'number' ? v : null;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
present: true,
|
||||||
|
completion_rate_at_audit: num('completion_rate_at_audit'),
|
||||||
|
verification_failures_found: num('verification_failures_found') ?? 0,
|
||||||
|
documentation_updates_needed: num('documentation_updates_needed'),
|
||||||
|
archival_succeeded: archivalSucceeded,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Fallback: regex scraping ──
|
||||||
|
|
||||||
// Completion rate: prefer Completion Summary, fall back to stripped overview checkboxes
|
// Completion rate: prefer Completion Summary, fall back to stripped overview checkboxes
|
||||||
let completionRate: number | null = null;
|
let completionRate: number | null = null;
|
||||||
const summaryCount = parseCompletionSummaryTaskCount(overview);
|
const summaryCount = parseCompletionSummaryTaskCount(overview);
|
||||||
|
|||||||
+13
-15
@@ -221,7 +221,14 @@ State assessment and ask user to confirm.
|
|||||||
1. Save PLAN-CONVERSATION (see template below) — transcript + decision summary tables
|
1. Save PLAN-CONVERSATION (see template below) — transcript + decision summary tables
|
||||||
2. Create PLAN-DRAFT (see template below) using same date
|
2. Create PLAN-DRAFT (see template below) using same date
|
||||||
3. Verify: re-read conversation as source of truth, cross-reference against PLAN-DRAFT sections (Reqs→2.1/2.2, Tech→3, Architecture→4, Risks→6, Assumptions→9, Criteria→7). Add gaps with `<!-- VERIFICATION -->` comments. Output verification summary table.
|
3. Verify: re-read conversation as source of truth, cross-reference against PLAN-DRAFT sections (Reqs→2.1/2.2, Tech→3, Architecture→4, Risks→6, Assumptions→9, Criteria→7). Add gaps with `<!-- VERIFICATION -->` comments. Output verification summary table.
|
||||||
4. Append `## Planning Metrics` to PLAN-DRAFT with these exact fields: `confidence: [0-100]`, `clarification_rounds: [N]`, `functional_requirements_count: [N]`, `non_functional_requirements_count: [N]`, `risk_count: [N]`, `phase_count: [N]`, `verification_gaps_found: [N]`. Use exact field names — the metrics pipeline parses this section.
|
4. Append `## Planning Metrics` to PLAN-DRAFT with a `<!-- METRICS_JSON {...} -->` HTML comment. The metrics pipeline parses this — use exact format:
|
||||||
|
|
||||||
|
```
|
||||||
|
## Planning Metrics
|
||||||
|
<!-- METRICS_JSON {"confidence": 95, "clarification_rounds": 0, "functional_requirements_count": 8, "non_functional_requirements_count": 6, "risk_count": 7, "phase_count": 4, "verification_gaps_found": 0, "confidence_breakdown": {"requirements": 24, "feasibility": 23, "integration": 24, "risk": 22}} -->
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace values with actuals. Optionally add plain `key: value` lines below for readability (no bold/markdown).
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
@@ -275,20 +282,11 @@ When complete (PLAN-DRAFT created), tell user:
|
|||||||
> `/plan2code-2--document`
|
> `/plan2code-2--document`
|
||||||
> ```"
|
> ```"
|
||||||
|
|
||||||
## Abort Handling
|
## Abort / Recovery
|
||||||
|
|
||||||
If user says "abort", "cancel", "start over":
|
- **Abort:** Confirm with user, list files needing cleanup, stop workflow
|
||||||
1. Confirm: "Abort planning? Progress will not be saved."
|
- **Lost context:** Attach PLAN-DRAFT, state phase
|
||||||
2. If confirmed, state files created that may need cleanup
|
- **Confidence stuck <90%:** List blockers, ask targeted questions
|
||||||
3. Stop workflow
|
|
||||||
|
|
||||||
## Recovery
|
|
||||||
|
|
||||||
| Issue | Solution |
|
|
||||||
|-------|----------|
|
|
||||||
| Lost context | Attach PLAN-DRAFT, state phase |
|
|
||||||
| Unclear answers | One follow-up, max 3 rounds |
|
|
||||||
| Confidence stuck <90% | List blockers, ask targeted questions |
|
|
||||||
|
|
||||||
## Reminders
|
## Reminders
|
||||||
|
|
||||||
@@ -296,5 +294,5 @@ If user says "abort", "cancel", "start over":
|
|||||||
- Do NOT implement - design and present plan only
|
- Do NOT implement - design and present plan only
|
||||||
- Responses start with: `🤔 [PLANNING PHASE X: Name]`
|
- Responses start with: `🤔 [PLANNING PHASE X: Name]`
|
||||||
- **Workflow:** Plan -> Document -> Implement -> Finalize. After planning: `/plan2code-2--document`
|
- **Workflow:** Plan -> Document -> Implement -> Finalize. After planning: `/plan2code-2--document`
|
||||||
- Save conversation log (7A) before PLAN-DRAFT (7B)
|
- Save conversation log (7A) before PLAN-DRAFT (7B), run verification (7C) after
|
||||||
- Run verification (7C) after PLAN-DRAFT - conversation log is source of truth
|
- Run verification (7C) after PLAN-DRAFT - conversation log is source of truth
|
||||||
|
|||||||
@@ -54,12 +54,14 @@ If no plan exists and user wants to skip:
|
|||||||
|
|
||||||
| Criterion | Description |
|
| Criterion | Description |
|
||||||
|-----------|-------------|
|
|-----------|-------------|
|
||||||
| Time-boxed | 15-60 minutes |
|
| Time-boxed | 15-60 min. Split if 5+ logic branches, 2+ integration points, or shared interface mutation. Combine adjacent trivial tasks that form a cohesive unit. |
|
||||||
| Self-contained | No deps on incomplete same-phase tasks |
|
| Self-contained | No deps on incomplete same-phase tasks |
|
||||||
| Measurable | Objectively verifiable |
|
| Measurable | Objectively verifiable |
|
||||||
| Action-oriented | Imperative: "Create...", "Implement..." |
|
| Action-oriented | Imperative: "Create...", "Implement..." |
|
||||||
| Specific | File paths, function names, exact requirements |
|
| Specific | File paths, function names, exact requirements |
|
||||||
|
|
||||||
|
**Complexity check** — before finalizing each task, consider: logic branches, distinct behaviors, integration points, shared interface impact, and error/edge cases. Tasks that are complex on 3+ of these signals should be split.
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
| Bad | Good |
|
| Bad | Good |
|
||||||
@@ -83,7 +85,7 @@ If no plan exists and user wants to skip:
|
|||||||
- Success Criteria (plain bullet list, no checkboxes) (section 7)
|
- Success Criteria (plain bullet list, no checkboxes) (section 7)
|
||||||
- Phase Checklist (Implementation Phases)
|
- Phase Checklist (Implementation Phases)
|
||||||
- Quick Reference (Key Files, Environment Variables, External Dependencies)
|
- Quick Reference (Key Files, Environment Variables, External Dependencies)
|
||||||
7. **Write** each `phase-X.md` with detailed tasks
|
7. **Write** each `phase-X.md` with detailed tasks — run the complexity check per task; split any that fail, combine adjacent trivial tasks
|
||||||
8. **Analyze** parallel execution eligibility
|
8. **Analyze** parallel execution eligibility
|
||||||
9. **Verify** all PLAN-DRAFT requirements covered:
|
9. **Verify** all PLAN-DRAFT requirements covered:
|
||||||
- 9A: Re-read PLAN-DRAFT as source of truth
|
- 9A: Re-read PLAN-DRAFT as source of truth
|
||||||
@@ -193,6 +195,14 @@ Total tasks: Y
|
|||||||
Parallel Execution: [Groups or "None - sequential only"]
|
Parallel Execution: [Groups or "None - sequential only"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Also append a machine-parseable metrics comment to the END of `overview.md` for the metrics pipeline:
|
||||||
|
|
||||||
|
```
|
||||||
|
<!-- METRICS_JSON {"step": "document", "total_tasks": 28, "tasks_per_phase": [7, 7, 7, 7], "phase_count": 4, "parallel_groups_identified": 2, "verification_items_added": 3} -->
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace values with actuals. `verification_items_added` = total Added column from the Verification Summary table.
|
||||||
|
|
||||||
**Tell user:**
|
**Tell user:**
|
||||||
1. What was created (spec files list)
|
1. What was created (spec files list)
|
||||||
2. Path for next session: `specs/<feature-name>/overview.md`
|
2. Path for next session: `specs/<feature-name>/overview.md`
|
||||||
|
|||||||
@@ -268,6 +268,10 @@ specs--completed/
|
|||||||
- **Completion Rate:** [X]% ([Y]/[Z] tasks)
|
- **Completion Rate:** [X]% ([Y]/[Z] tasks)
|
||||||
- **Archived To:** `specs--completed/<feature-name>/`
|
- **Archived To:** `specs--completed/<feature-name>/`
|
||||||
|
|
||||||
|
<!-- METRICS_JSON {"step": "finalize", "completion_rate_at_audit": 0.95, "tasks_completed": 19, "tasks_total": 20, "verification_failures_found": 1, "documentation_updates_needed": 2} -->
|
||||||
|
|
||||||
|
Replace METRICS_JSON values with actuals. `completion_rate_at_audit` = Y/Z as decimal (e.g., 19/20 = 0.95).
|
||||||
|
|
||||||
### Finalization Steps Completed
|
### Finalization Steps Completed
|
||||||
- [x] Step 1: Task Completion Audit
|
- [x] Step 1: Task Completion Audit
|
||||||
- [x] Step 2: Implementation Verification
|
- [x] Step 2: Implementation Verification
|
||||||
|
|||||||
Reference in New Issue
Block a user