Add deliverables-to-todos sync feature
Dashboard now automatically syncs milestone deliverables to todos: - On load, checks each milestone's deliverables table - If a deliverable doesn't exist as a todo (matched by text + milestone), adds it - Maps deliverable status to todo priority (Done=completed, In Progress=high, Not Started=medium) - Saves updated todos.md if any items were added Also updated CLAUDE.md to document the sync behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e885ccc409
commit
a6937463d0
|
|
@ -86,6 +86,15 @@ src/development_hub/
|
|||
| `Ctrl+]` | Expand all sections |
|
||||
| `Ctrl+[` | Collapse all sections |
|
||||
|
||||
### Dashboard Data Sync
|
||||
|
||||
The dashboard automatically syncs data between documentation files:
|
||||
|
||||
- **Deliverables → TODOs**: When loading a project dashboard, deliverables from `milestones.md` tables are synced to `todos.md`. If a deliverable doesn't exist as a todo (matched by text + milestone ID), it's added with the appropriate `@M#` tag and priority.
|
||||
- **TODOs → Milestone Progress**: The milestone progress bar is calculated from linked todos (items tagged with `@M#`), not from the deliverables table.
|
||||
|
||||
This means you can define major work items in milestone deliverable tables, and they'll automatically appear in your todo list for tracking.
|
||||
|
||||
## CLI Scripts
|
||||
|
||||
### `bin/new-project`
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from PySide6.QtWidgets import (
|
|||
QMenu,
|
||||
QMessageBox,
|
||||
QProgressBar,
|
||||
QPushButton,
|
||||
QTextEdit,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ from development_hub.services.git_service import GitService
|
|||
from development_hub.services.health_checker import HealthChecker
|
||||
from development_hub.models.health import HealthStatus
|
||||
from development_hub.parsers.todos_parser import TodosParser
|
||||
from development_hub.models.todo import Todo, TodoList
|
||||
from development_hub.parsers.goals_parser import MilestonesParser, GoalsParser
|
||||
from development_hub.widgets.progress_bar import MilestoneProgressBar
|
||||
from development_hub.widgets.stat_card import StatCardRow
|
||||
|
|
@ -1107,6 +1108,19 @@ class ProjectDashboard(QWidget):
|
|||
if todos_path.exists():
|
||||
parser = TodosParser(todos_path)
|
||||
todo_list = parser.parse()
|
||||
else:
|
||||
# Create empty TodoList if file doesn't exist
|
||||
todo_list = TodoList(source_file=str(todos_path))
|
||||
|
||||
# Sync deliverables from milestones to todos (adds missing items)
|
||||
if self._milestones_list and todo_list:
|
||||
synced = self._sync_deliverables_to_todos(
|
||||
self._milestones_list, todo_list, todos_path
|
||||
)
|
||||
if synced:
|
||||
# Reload todo_list after sync to get fresh data
|
||||
parser = TodosParser(todos_path)
|
||||
todo_list = parser.parse()
|
||||
|
||||
# Create MilestoneWidget for each milestone (keep original file order)
|
||||
first_active_shown = False
|
||||
|
|
@ -3141,6 +3155,81 @@ generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|||
if changed:
|
||||
self._save_milestones()
|
||||
|
||||
def _sync_deliverables_to_todos(
|
||||
self, milestones: list, todo_list, todos_path
|
||||
) -> bool:
|
||||
"""Sync milestone deliverables to todos.
|
||||
|
||||
For each deliverable in a milestone, check if a matching todo exists.
|
||||
If not, create a new todo with the appropriate milestone tag and status.
|
||||
|
||||
Args:
|
||||
milestones: List of Milestone objects
|
||||
todo_list: TodoList to add deliverables to
|
||||
todos_path: Path to todos.md for saving
|
||||
|
||||
Returns:
|
||||
True if any todos were added, False otherwise
|
||||
"""
|
||||
from development_hub.models.goal import DeliverableStatus
|
||||
|
||||
if not milestones or not todo_list:
|
||||
return False
|
||||
|
||||
added_count = 0
|
||||
|
||||
for milestone in milestones:
|
||||
if not milestone.deliverables:
|
||||
continue
|
||||
|
||||
# Get existing todos for this milestone
|
||||
existing_todos = todo_list.get_by_milestone(milestone.id)
|
||||
existing_texts = {t.text.lower().strip() for t in existing_todos}
|
||||
|
||||
for deliverable in milestone.deliverables:
|
||||
deliverable_text = deliverable.name.strip()
|
||||
deliverable_text_lower = deliverable_text.lower()
|
||||
|
||||
# Check if this deliverable already exists as a todo
|
||||
if deliverable_text_lower in existing_texts:
|
||||
continue
|
||||
|
||||
# Map deliverable status to todo properties
|
||||
if deliverable.status == DeliverableStatus.DONE:
|
||||
completed = True
|
||||
priority = "high"
|
||||
else:
|
||||
completed = False
|
||||
# In Progress gets high priority, Not Started gets medium
|
||||
if deliverable.status == DeliverableStatus.IN_PROGRESS:
|
||||
priority = "high"
|
||||
else:
|
||||
priority = "medium"
|
||||
|
||||
# Create new todo
|
||||
new_todo = Todo(
|
||||
text=deliverable_text,
|
||||
completed=completed,
|
||||
priority=priority,
|
||||
milestone=milestone.id,
|
||||
)
|
||||
|
||||
# Mark complete with today's date if done
|
||||
if completed:
|
||||
new_todo.mark_complete()
|
||||
|
||||
todo_list.add_todo(new_todo)
|
||||
added_count += 1
|
||||
|
||||
# Save if any todos were added
|
||||
if added_count > 0 and todos_path:
|
||||
parser = TodosParser(todos_path)
|
||||
parser._frontmatter = parser.frontmatter # Preserve frontmatter
|
||||
parser.save(todo_list)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _on_milestone_todo_toggled(self, todo, completed):
|
||||
"""Handle todo toggle from within milestone widget.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue