todos
Task and project management with recurrence and reminders
Full-featured todo system supporting GTD, Kanban, and custom workflows. Tasks live in containers (lists, projects, areas) with hierarchical nesting. Supports recurring tasks, reminders, tags, and priority management. Explain feature shows why a task has its current state/priority.
2
Ports
6
Schemas
6
Hooks
18
Events
01
Ports
Required
Optional
- RecurrenceEngine optional
- ReminderScheduler optional
- ActivityLogRepo optional
- HierarchyReaderPort optional
- Policy optional
Adapters Provided
- InMemoryTaskRepository
- InMemoryTaskContainerRepository
- InMemoryTagRepository
- InMemoryTaskNoteRepository
- InMemoryTaskLinkRepository
02
Schemas
03
Hooks
- before_task_create
- after_task_create
- before_task_complete
- after_task_complete
- before_task_update
- on_task_overdue
04
Events
- TaskCreated v1
- TaskUpdated v1
- TaskCompleted v1
- TaskReopened v1
- TaskDeleted v1
- TaskMoved v1
- TaskOverdue v1
- RecurrenceTriggered v1
- ReminderRequested v1
- ContainerCreated v1
- ContainerArchived v1
- TagCreated v1
- TagAddedToTask v1
- TagRemovedFromTask v1
- ContainerMoved v1
- NoteAddedToTask v1
- TasksLinked v1
- TasksUnlinked v1
05
Stories
06
Examples
from uuid import uuid4
from psp.components.todos.application.use_cases import CreateTask
from psp.components.todos.application.dto import CreateTaskInput
from psp.components.todos.domain import Priority
from psp.platform.clock import SystemClock
from psp.platform.eventbus import InMemoryEventBus
# Wire dependencies (in real app, use DI container)
create_task = CreateTask(
task_repo=task_repo,
container_repo=container_repo,
clock=SystemClock(),
event_bus=InMemoryEventBus(),
)
# Execute use case
result = create_task.execute(
owner_id=current_user_id,
input=CreateTaskInput(
container_id=inbox_id,
title="Review pull request",
priority=Priority.HIGH,
due_at=tomorrow,
),
)
print(f"Created task {result.id}: {result.title}")
Inject dependencies and call CreateTask use case.
from psp.platform.query import QuerySpec, FilterOp, SortDir
# Find high-priority incomplete tasks due this week
spec = QuerySpec(
filters=[
("status", FilterOp.NE, "completed"),
("priority", FilterOp.IN, ["high", "urgent"]),
("due_at", FilterOp.LTE, end_of_week),
],
sort_by="due_at",
sort_dir=SortDir.ASC,
limit=20,
)
result = task_repo.query(spec)
for task in result.items:
print(f"[{task.priority.value}] {task.title} - due {task.due_at}")
Use QuerySpec to filter and paginate tasks.
from psp.components.todos.application.use_cases import CompleteTask
complete_task = CompleteTask(
task_repo=task_repo,
clock=clock,
event_bus=event_bus,
recurrence_engine=recurrence_engine, # Optional
)
# Complete the task - if it has recurrence, a new task is created
result = complete_task.execute(task_id=recurring_task_id)
if result.next_task_id:
print(f"Completed! Next occurrence created: {result.next_task_id}")
else:
print("Completed (no recurrence)")
Completing a recurring task triggers next instance.
from psp.components.todos.application.use_cases import ExplainTaskPriority
explain = ExplainTaskPriority(
task_repo=task_repo,
container_repo=container_repo,
activity_log=activity_log, # Optional
)
result = explain.execute(task_id=task.id)
print(f"Priority: {result.priority}")
print(f"Source: {result.source}") # explicit, inherited, computed
print(f"Reason: {result.reason}")
# e.g., "Inherited from project 'Q4 Goals' which has priority=high"
# e.g., "Computed: due in < 24 hours with no priority set"
for entry in result.history:
print(f" {entry.timestamp}: {entry.action}")
Understand why a task has its current priority.
API Reference
This component mounts routes under /v1/todos.
View OpenAPI specification