.2869047221311500:af88c9eb824a168aef06ae843cafc0cd_69e9cad9b964995ded0a5f13.69e9d0e7b964995ded0a5f17.69e9d0e7cde3d57cd82a7957:Trae CN.T(2026/4/23 15:57:27)#97
Conversation
扩展Task模型和存储方法以支持团队协作功能,包括: - 添加任务所有者、审核者字段 - 实现任务状态管理 - 增加任务阻塞关系 - 添加事件标记和交接说明 - 实现截止日期和状态显示方法
There was a problem hiding this comment.
Pull request overview
This PR extends the Task model, JSON storage, and CLI to support team collaboration workflows (owners/reviewers, status, blockers, incidents, handoff notes, and due-date UX) in the tix CLI task manager.
Changes:
- Expanded
Taskwith team-collaboration fields and added helper methods for status/blocking/due-date display. - Updated JSON storage task creation to persist the new collaboration fields.
- Extended CLI commands (
add,ls,edit) with new flags/filters and introduced new reporting commands (handoff,daily).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
tix/storage/json_storage.py |
Extends add_task() to persist new team-collaboration fields into JSON storage. |
tix/models.py |
Adds collaboration fields to Task, updates serialization, and implements status/blocking/due-date helpers. |
tix/cli.py |
Adds parsing helpers, new CLI options/filters, enhanced list rendering, and new handoff/daily report commands. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -55,12 +75,21 @@ def from_dict(cls, data: dict): | |||
| estimate=data.get('estimate'), | |||
| time_spent=data.get('time_spent', 0), | |||
| started_at=data.get('started_at'), | |||
| time_logs=data.get('time_logs', []) | |||
| time_logs=data.get('time_logs', []), | |||
| # Team collaboration fields with safe defaults | |||
| owner=data.get('owner'), | |||
| reviewer=data.get('reviewer'), | |||
| status=data.get('status', 'todo'), | |||
| due_at=data.get('due_at'), | |||
| handoff_note=data.get('handoff_note'), | |||
| blocked_by=data.get('blocked_by', []), | |||
| incident=data.get('incident', False), | |||
| ) | |||
There was a problem hiding this comment.
Task.from_dict() defaults status to 'todo' even when loading a task with completed=True and no stored status. This can lead to completed tasks showing up with a non-done status and being miscounted/filtered by status. Consider defaulting status to 'done' when completed is true and status is missing (or normalizing completed/status consistency during load).
| def needs_handoff(self) -> bool: | ||
| """Check if task needs handoff note (blocked but no note, or due soon without note)""" | ||
| if self.completed: | ||
| return False | ||
| # Blocked tasks should have handoff notes | ||
| if self.is_blocked() and not self.handoff_note: | ||
| return True | ||
| # Cross-person collaboration needs reviewer | ||
| if self.owner and not self.reviewer and self.status == 'review': | ||
| return True | ||
| return False |
There was a problem hiding this comment.
The needs_handoff() docstring mentions "due soon without note", but the implementation only checks (1) blocked without handoff_note and (2) review status without reviewer. Either update the docstring to match the actual logic, or add the due-soon condition if that behavior is intended.
|
|
||
| # Apply due-soon filter | ||
| if due_soon: | ||
| tasks = [t for t in tasks if t.is_due_soon(days=2)] |
There was a problem hiding this comment.
--due-soon is documented as "today/tomorrow", but the filter uses t.is_due_soon(days=2), which includes tasks due up to two days out (today + 2). Align the implementation with the help text (e.g., use days=1) or update the CLI help to reflect the actual range.
| tasks = [t for t in tasks if t.is_due_soon(days=2)] | |
| tasks = [t for t in tasks if t.is_due_soon(days=1)] |
|
|
||
| if not tasks: | ||
| console.print("[dim]No tasks found. Use 'tix add' to create one![/dim]") | ||
| console.print("[dim]No tasks found matching your criteria[/dim]") |
There was a problem hiding this comment.
The empty list message changed from "No tasks found" to "No tasks found matching your criteria". Existing CLI tests assert on the old substring (and users may rely on it). Consider keeping the original wording (or at least include "No tasks found" in the message) to preserve compatibility, or update the tests accordingly.
| console.print("[dim]No tasks found matching your criteria[/dim]") | |
| console.print("[dim]No tasks found[/dim]") |
| for task in unassigned_high: | ||
| lines.append(f"- [ ] **#{task.id}** {task.text}") | ||
| if task.due_at: | ||
| lines.append(f" - Due: {task.get_due_date_display()}") |
There was a problem hiding this comment.
In markdown output, task.get_due_date_display() returns Rich markup (e.g., [red]OVERDUE...[/red]), which will be embedded into the markdown report as literal tags. For markdown mode, emit a plain date string (or add a non-Rich formatting helper) to avoid leaking Rich markup into the report.
| lines.append(f" - Due: {task.get_due_date_display()}") | |
| due_display = task.due_at.strftime('%Y-%m-%d') if hasattr(task.due_at, 'strftime') else str(task.due_at) | |
| lines.append(f" - Due: {due_display}") |
| for task in overdue_incomplete: | ||
| lines.append(f"- [ ] **#{task.id}** {task.text}") | ||
| if task.owner: | ||
| lines.append(f" - Owner: {task.owner}") | ||
| lines.append(f" - Due: {task.get_due_date_display()}") | ||
| lines.append("") |
There was a problem hiding this comment.
In markdown output, task.get_due_date_display() returns Rich markup (e.g., [red]...[/red]), which will be embedded into the markdown report as literal tags. For markdown mode, emit a plain date string (or add a non-Rich formatting helper) to avoid leaking Rich markup into the report.
| if stats['overdue']: | ||
| lines.append("**Overdue:**") | ||
| for task in stats['overdue']: | ||
| lines.append(f"- [ ] #{task.id} {task.text} (Due: {task.get_due_date_display()})") | ||
| lines.append("") |
There was a problem hiding this comment.
In markdown output, task.get_due_date_display() returns Rich markup (e.g., [red]...[/red]), which will be embedded into the markdown report as literal tags. For markdown mode, emit a plain date string (or add a non-Rich formatting helper) to avoid leaking Rich markup into the report.
| except ValueError: | ||
| pass | ||
|
|
||
| raise ValueError(f"Invalid date format: {date_str}. Use YYYY-MM-DD, 'today', 'tomorrow', 'in-N-days'") |
There was a problem hiding this comment.
parse_due_date() supports next-week/nextweek, but the raised error message doesn't mention this valid input. Consider including all supported keywords in the error message so users can self-correct without reading source code.
| raise ValueError(f"Invalid date format: {date_str}. Use YYYY-MM-DD, 'today', 'tomorrow', 'in-N-days'") | |
| raise ValueError( | |
| f"Invalid date format: {date_str}. Use YYYY-MM-DD, 'today', 'tomorrow', " | |
| "'next-week', 'nextweek', or 'in-N-days'" | |
| ) |
| @cli.command() | ||
| @click.option("--format", "-f", type=click.Choice(['console', 'markdown']), default='console', help='Output format') | ||
| @click.option("--output", "-o", type=click.Path(), help='Output to file (for markdown format)') | ||
| def handoff(format, output): | ||
| """Handoff view - show tasks needing attention for shift交接 | ||
|
|
||
| Shows: | ||
| - Tasks due today that must be handed off | ||
| - Unassigned high-priority tasks | ||
| - Blocked tasks without handoff notes | ||
| - Cross-person tasks missing reviewers | ||
| - Overdue incomplete tasks | ||
| """ | ||
| from datetime import datetime, timedelta |
There was a problem hiding this comment.
Large new CLI surface area was added (new add options, ls filters, and new handoff/daily commands) but there are existing pytest CLI tests and no new coverage for these behaviors. Adding tests for at least the new filters and the new commands' outputs would help prevent regressions.
扩展Task模型和存储方法以支持团队协作功能,包括:
PR Checklist
What does this PR do?
Related Issue
Closes #
Type of change