Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e96c15e
auto-claude: subtask-1-1 - Add optional title field to Subtask dataclass
AndyMik90 Feb 14, 2026
e361795
auto-claude: subtask-1-2 - Add title field to planner prompt subtask …
AndyMik90 Feb 14, 2026
69e32e8
auto-claude: subtask-2-1 - Add optional 'title' field to PlanSubtask …
AndyMik90 Feb 14, 2026
0edba9a
auto-claude: subtask-2-2 - Create extractSubtaskTitle utility function
AndyMik90 Feb 14, 2026
4b21e63
auto-claude: subtask-2-3 - Create unit tests for extractSubtaskTitle()
AndyMik90 Feb 14, 2026
41760db
auto-claude: subtask-3-1 - Use subtask.title with extractSubtaskTitle…
AndyMik90 Feb 14, 2026
0d3b2e7
auto-claude: subtask-3-2 - Use subtask.title with extractSubtaskTitle…
AndyMik90 Feb 14, 2026
c504c27
auto-claude: subtask-4-1 - Add line-clamp-2 CSS class to subtask titl…
AndyMik90 Feb 14, 2026
94fd2b6
Fix i18n bypass and relocate test file per PR review
AndyMik90 Feb 15, 2026
dce8437
fix: prevent duplicate display when subtask title equals description
AndyMik90 Feb 15, 2026
a5faf0e
fix: add title to validation schema and fix Tooltip accessibility
AndyMik90 Feb 15, 2026
a72de1c
fix: make subtask description tooltip keyboard-accessible
AndyMik90 Feb 15, 2026
0eaef26
fix: address remaining review findings for subtask title extraction
AndyMik90 Feb 16, 2026
20b5176
fix: address PR #1837 review findings for subtask title display
AndyMik90 Feb 17, 2026
5373658
fix: resolve OverflowDescription ref latch, period stripping with abb…
AndyMik90 Feb 18, 2026
37530a4
fix: re-attach ResizeObserver when overflow element switches DOM nodes
AndyMik90 Feb 18, 2026
87509b1
fix: prevent early sentence split for short descriptions and eliminat…
AndyMik90 Feb 18, 2026
738aa29
fix: address PR #1837 review findings in subtask title/overflow compo…
AndyMik90 Feb 18, 2026
dd72e95
fix: address 3 PR review findings in subtask title display
AndyMik90 Feb 18, 2026
e0f589e
fix: skip colon-space boundary when prefix is too short to be a title
AndyMik90 Feb 18, 2026
1227f7a
Merge branch 'develop' into auto-claude/231-investigate-and-fix-subta…
AndyMik90 Feb 18, 2026
37cb3db
Merge branch 'develop' into auto-claude/231-investigate-and-fix-subta…
AndyMik90 Feb 18, 2026
969917b
Merge remote-tracking branch 'origin/develop' into auto-claude/231-in…
AndyMik90 Feb 20, 2026
149a675
fix: add overflow tooltip to subtask titles and improve keyboard acce…
AndyMik90 Feb 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/backend/implementation_plan/subtask.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Subtask:

id: str
description: str
title: str | None = None
status: SubtaskStatus = SubtaskStatus.PENDING

# Scoping
Expand Down Expand Up @@ -53,6 +54,8 @@ def to_dict(self) -> dict:
"description": self.description,
"status": self.status.value,
}
if self.title:
result["title"] = self.title
Comment on lines +57 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Falsy check drops empty-string titles — use is not None for consistency.

if self.title: skips serialization when title is "". Other optional fields in this method (e.g., session_id on Line 79) correctly use is not None. Use the same pattern here to stay consistent and avoid silently losing data.

Proposed fix
-        if self.title:
+        if self.title is not None:
             result["title"] = self.title
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self.title:
result["title"] = self.title
if self.title is not None:
result["title"] = self.title
🤖 Prompt for AI Agents
In `@apps/backend/implementation_plan/subtask.py` around lines 57 - 58, The
current truthy check in the serialization (in the Subtask class's
to_dict/serialize method) uses "if self.title:" which drops empty-string titles;
change it to "if self.title is not None:" to match the pattern used for other
optional fields like session_id and ensure empty-string titles are preserved in
the resulting dict; update the conditional guarding result["title"] in the
method where self.title is handled.

if self.service:
result["service"] = self.service
if self.all_services:
Expand Down Expand Up @@ -89,6 +92,7 @@ def from_dict(cls, data: dict) -> "Subtask":
return cls(
id=data["id"],
description=data["description"],
title=data.get("title"),
status=SubtaskStatus(data.get("status", "pending")),
service=data.get("service"),
all_services=data.get("all_services", False),
Expand Down
8 changes: 8 additions & 0 deletions apps/backend/prompts/planner.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Based on the workflow type and services involved, create the implementation plan
"subtasks": [
{
"id": "subtask-1-1",
"title": "Create data models for analytics",
"description": "Create data models for [feature]",
"service": "backend",
"files_to_modify": ["src/models/user.py"],
Comment on lines 232 to 238

This comment was marked as outdated.

Expand All @@ -246,6 +247,7 @@ Based on the workflow type and services involved, create the implementation plan
},
{
"id": "subtask-1-2",
"title": "Create API endpoints for analytics events",
"description": "Create API endpoints for [feature]",
"service": "backend",
"files_to_modify": ["src/routes/api.py"],
Expand All @@ -272,6 +274,7 @@ Based on the workflow type and services involved, create the implementation plan
"subtasks": [
{
"id": "subtask-2-1",
"title": "Create aggregation Celery task",
"description": "Create aggregation Celery task",
Comment on lines +277 to 278
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The title and description are identical. According to the new guideline on line 370, the description should contain "full implementation details". To better guide the AI, consider making the description more detailed.

For example: "description": "Create a Celery task that aggregates analytics events from the last hour into a summary table."

Comment on lines 276 to 278
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Example subtask has identical title and description — demonstrates the wrong pattern.

Several examples (e.g., subtask-2-1) have title and description set to the same string. Since the guideline on Line 370 says "Description contains full implementation details," the examples should show descriptions that are more detailed than the title. Otherwise, the LLM will learn to duplicate them.

🤖 Prompt for AI Agents
In `@apps/backend/prompts/planner.md` around lines 276 - 278, The example subtask
"subtask-2-1" currently has identical "title" and "description", which teaches
the wrong pattern; update the example entries (e.g., subtask-2-1) so the "title"
remains a short action phrase and the "description" provides full implementation
details, acceptance criteria, and any constraints or inputs/outputs expected
(distinct from the title) to follow the guideline that descriptions contain full
implementation details.

"service": "worker",
"files_to_modify": ["worker/tasks.py"],
Expand All @@ -296,6 +299,7 @@ Based on the workflow type and services involved, create the implementation plan
"subtasks": [
{
"id": "subtask-3-1",
"title": "Create real-time dashboard component",
"description": "Create dashboard component",
"service": "frontend",
"files_to_modify": [],
Expand All @@ -320,6 +324,7 @@ Based on the workflow type and services involved, create the implementation plan
"subtasks": [
{
"id": "subtask-4-1",
"title": "Verify end-to-end analytics flow",
"description": "End-to-end verification of analytics flow",
"all_services": true,
"files_to_modify": [],
Expand Down Expand Up @@ -362,6 +367,7 @@ Use ONLY these values for the `type` field in phases:
2. **Small scope** - Each subtask should take 1-3 files max
3. **Clear verification** - Every subtask must have a way to verify it works
4. **Explicit dependencies** - Phases block until dependencies complete
5. **Title must be a short imperative label** (max 60 chars, e.g. "Create data models for analytics"). Description contains full implementation details.

### Verification Types

Expand All @@ -385,6 +391,7 @@ Use ONLY these values for the `type` field in phases:
```json
{
"id": "subtask-investigate-1",
"title": "Identify root cause of memory leak",
"description": "Identify root cause of memory leak",
Comment on lines +394 to 395
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The title and description are identical. To align with the guideline on line 370, the description should be more detailed than the title.

For example: "description": "Use memory profiling tools to trace the source of the memory leak reported in issue #123. The investigation should focus on the data processing pipeline."

"expected_output": "Document with: (1) Root cause, (2) Evidence, (3) Proposed fix",
"files_to_modify": [],
Expand All @@ -400,6 +407,7 @@ Use ONLY these values for the `type` field in phases:
```json
{
"id": "subtask-refactor-1",
"title": "Add new auth system alongside old",
"description": "Add new auth system alongside old",
Comment on lines +410 to 411
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The title and description are identical. The new guideline on line 370 states that the description should provide full implementation details. A more detailed description here would be a better example for the AI.

For instance: "description": "Implement the new JWT-based authentication system in 'src/auth/new_auth.ts' and integrate it into the main auth flow in 'src/auth/index.ts' behind a feature flag."

"files_to_modify": ["src/auth/index.ts"],
"files_to_create": ["src/auth/new_auth.ts"],
Expand Down
1 change: 1 addition & 0 deletions apps/backend/spec/validate_pkg/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"subtask_schema": {
"required_fields": ["id", "description", "status"],
"optional_fields": [
"title",
"service",
"all_services",
"files_to_modify",
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/main/project-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { findAllSpecPaths } from './utils/spec-path-helpers';
import { ensureAbsolutePath } from './utils/path-helpers';
import { writeFileAtomicSync } from './utils/atomic-file';
import { updateRoadmapFeatureOutcome, revertRoadmapFeatureOutcome } from './utils/roadmap-utils';
import { extractSubtaskTitle } from '../shared/utils/subtask-title';

interface TabState {
openProjectIds: string[];
Expand Down Expand Up @@ -502,7 +503,7 @@ export class ProjectStore {
const items = phase.subtasks || (phase as { chunks?: PlanSubtask[] }).chunks || [];
return items.map((subtask) => ({
id: subtask.id,
title: subtask.description,
title: subtask.title || extractSubtaskTitle(subtask.description),
description: subtask.description,
status: subtask.status,
files: []
Expand Down
69 changes: 54 additions & 15 deletions apps/frontend/src/renderer/components/task-detail/TaskSubtasks.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CheckCircle2, Clock, XCircle, AlertCircle, ListChecks, FileCode } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Badge } from '../ui/badge';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
Expand All @@ -22,6 +23,48 @@ function getSubtaskStatusIcon(status: string) {
}
}

/**
* Generic overflow-aware text component.
* Renders children in a line-clamped container and shows a tooltip with the
* full text when the content is visually truncated.
*/
function OverflowText({
text,
as: Tag = 'p',
className,
}: {
text: string;
as?: 'p' | 'span';
className?: string;
}) {
const [el, setEl] = useState<HTMLElement | null>(null);
const [isOverflowing, setIsOverflowing] = useState(false);

useEffect(() => {
if (!el) return;
const check = () => setIsOverflowing(el.scrollHeight > el.clientHeight);
check();
const observer = new ResizeObserver(check);
observer.observe(el);
return () => observer.disconnect();
}, [el, text]);

return (
<Tooltip open={isOverflowing ? undefined : false}>
<TooltipTrigger asChild>
<Tag ref={(node: HTMLElement | null) => setEl(node)} className={className} tabIndex={isOverflowing ? 0 : undefined}>
{text}
</Tag>
</TooltipTrigger>
{isOverflowing && (
<TooltipContent side="bottom" className="max-w-sm">
<p className="text-xs">{text}</p>
</TooltipContent>
)}
</Tooltip>
);
}

export function TaskSubtasks({ task }: TaskSubtasksProps) {
const { t } = useTranslation(['tasks']);
const progress = calculateProgress(task.subtasks);
Expand Down Expand Up @@ -68,22 +111,18 @@ export function TaskSubtasks({ task }: TaskSubtasksProps) {
)}>
#{index + 1}
</span>
<span className="text-sm font-medium text-foreground break-words flex-1 min-w-0">
{subtask.title || t('tasks:subtasks.untitled')}
</span>
<OverflowText
text={subtask.title || t('tasks:subtasks.untitled')}
as="span"
className="text-sm font-medium text-foreground break-words flex-1 min-w-0 line-clamp-2"
/>
</div>
<Tooltip>
<TooltipTrigger asChild>
<p className="mt-1 text-xs text-muted-foreground line-clamp-2 cursor-default break-words">
{subtask.description}
</p>
</TooltipTrigger>
{subtask.description && subtask.description.length > 80 && (
<TooltipContent side="bottom" className="max-w-sm">
<p className="text-xs">{subtask.description}</p>
</TooltipContent>
)}
</Tooltip>
{subtask.description && subtask.description !== subtask.title && (
<OverflowText
text={subtask.description}
className="mt-1 text-xs text-muted-foreground line-clamp-2 break-words"
/>
)}
{subtask.files && subtask.files.length > 0 && (
<div className="mt-2 flex flex-wrap gap-1">
{subtask.files.map((file) => (
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/src/renderer/stores/task-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { create } from 'zustand';
import { arrayMove } from '@dnd-kit/sortable';
import type { Task, TaskStatus, SubtaskStatus, ImplementationPlan, Subtask, TaskMetadata, ExecutionProgress, ExecutionPhase, ReviewReason, TaskDraft, ImageAttachment, TaskOrderState } from '../../shared/types';
import { debugLog, debugWarn } from '../../shared/utils/debug-logger';
import { extractSubtaskTitle } from '../../shared/utils/subtask-title';
import { useProjectStore } from './project-store';

/** Default max parallel tasks when no project setting is configured */
Expand Down Expand Up @@ -373,7 +374,7 @@ export const useTaskStore = create<TaskState>((set, get) => ({
: `subtask-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`);
// Defensive fallback: validatePlanData() ensures description exists, but kept for safety
const description = subtask.description || 'No description available';
const title = description; // Title and description are the same for subtasks
const title = subtask.title || extractSubtaskTitle(description);
const status = (subtask.status as SubtaskStatus) || 'pending';

return {
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/src/shared/types/task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export interface Phase {

export interface PlanSubtask {
id: string;
title?: string;
description: string;
status: SubtaskStatus;
verification?: {
Expand Down
Loading
Loading