fix(ui): preserve original task description after spec creation (#536)

* fix(ui): preserve original task description after spec creation

Task descriptions were being replaced with AI-generated content from
spec.md after spec creation completed. The description extraction in
project-store.ts prioritized spec.md Overview section over the user's
original description stored in implementation_plan.json.

Reordered priority: plan.json → requirements.json → spec.md (fallback)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(pr-review): add input validation and timeouts to subprocess calls

Address CodeRabbit findings:

1. Import and use _validate_git_ref for head_sha validation before
   passing to subprocess (security)

2. Add timeout=60 to git worktree add subprocess call to prevent
   indefinite hangs on slow/corrupted repos

3. Add timeout=30 to git worktree remove, list, and prune calls
   for consistent timeout handling

4. Remove head_branch fallback - only use head_sha for worktree
   creation to ensure consistent semantics

5. Fix potential IndexError in worktree list parsing by checking
   split result length before accessing index

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andy
2026-01-02 11:17:51 +01:00
committed by GitHub
parent f58c257824
commit 7990dcb41d
+19 -19
View File
@@ -346,29 +346,13 @@ export class ProjectStore {
}
}
// Try to read spec file for description
// PRIORITY 1: Read description from implementation_plan.json (user's original)
let description = '';
if (existsSync(specFilePath)) {
try {
const content = readFileSync(specFilePath, 'utf-8');
// Extract full Overview section until next heading or end of file
// Use \n#{1,6}\s to match valid markdown headings (# to ######) with required space
// This avoids truncating at # in code blocks (e.g., Python comments)
const overviewMatch = content.match(/## Overview\s*\n+([\s\S]*?)(?=\n#{1,6}\s|$)/);
if (overviewMatch) {
description = overviewMatch[1].trim();
}
} catch {
// Ignore read errors
}
}
// Fallback: read description from implementation_plan.json if not found in spec.md
if (!description && plan?.description) {
if (plan?.description) {
description = plan.description;
}
// Fallback: read description from requirements.json if still not found
// PRIORITY 2: Fallback to requirements.json
if (!description) {
const requirementsPath = path.join(specPath, AUTO_BUILD_PATHS.REQUIREMENTS);
if (existsSync(requirementsPath)) {
@@ -404,6 +388,22 @@ export class ProjectStore {
}
}
// PRIORITY 3: Final fallback to spec.md Overview (AI-synthesized content)
if (!description && existsSync(specFilePath)) {
try {
const content = readFileSync(specFilePath, 'utf-8');
// Extract full Overview section until next heading or end of file
// Use \n#{1,6}\s to match valid markdown headings (# to ######) with required space
// This avoids truncating at # in code blocks (e.g., Python comments)
const overviewMatch = content.match(/## Overview\s*\n+([\s\S]*?)(?=\n#{1,6}\s|$)/);
if (overviewMatch) {
description = overviewMatch[1].trim();
}
} catch {
// Ignore read errors
}
}
// Try to read task metadata
const metadataPath = path.join(specPath, 'task_metadata.json');
let metadata: TaskMetadata | undefined;