types.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """
  2. Core type definitions for Trellis task data.
  3. Provides:
  4. TaskData — TypedDict for task.json shape (read-path type hints only)
  5. TaskInfo — Frozen dataclass for loaded task (the public API type)
  6. AgentRecord — TypedDict for registry.json agent entries
  7. """
  8. from __future__ import annotations
  9. from dataclasses import dataclass
  10. from pathlib import Path
  11. from typing import TypedDict
  12. # =============================================================================
  13. # task.json shape (TypedDict — used only for read-path type hints)
  14. # =============================================================================
  15. class TaskData(TypedDict, total=False):
  16. """Shape of task.json on disk.
  17. Used only for type annotations when reading task.json.
  18. Writes must use the original dict to avoid losing unknown fields.
  19. """
  20. id: str
  21. name: str
  22. title: str
  23. description: str
  24. status: str
  25. dev_type: str
  26. scope: str | None
  27. package: str | None
  28. priority: str
  29. creator: str
  30. assignee: str
  31. createdAt: str
  32. completedAt: str | None
  33. branch: str | None
  34. base_branch: str | None
  35. worktree_path: str | None
  36. commit: str | None
  37. pr_url: str | None
  38. subtasks: list[str]
  39. children: list[str]
  40. parent: str | None
  41. relatedFiles: list[str]
  42. notes: str
  43. meta: dict
  44. # =============================================================================
  45. # Loaded task object (frozen dataclass — the public API type)
  46. # =============================================================================
  47. @dataclass(frozen=True)
  48. class TaskInfo:
  49. """Immutable view of a loaded task.
  50. Created by load_task() / iter_active_tasks().
  51. Contains the commonly accessed fields; the original dict
  52. is preserved in `raw` for write-back and uncommon field access.
  53. """
  54. dir_name: str
  55. directory: Path
  56. title: str
  57. status: str
  58. assignee: str
  59. priority: str
  60. children: tuple[str, ...]
  61. parent: str | None
  62. package: str | None
  63. raw: dict # original dict — use for writes and uncommon fields
  64. @property
  65. def name(self) -> str:
  66. """Task name (id or name field)."""
  67. return self.raw.get("name") or self.raw.get("id") or self.dir_name
  68. @property
  69. def description(self) -> str:
  70. return self.raw.get("description", "")
  71. @property
  72. def branch(self) -> str | None:
  73. return self.raw.get("branch")
  74. @property
  75. def meta(self) -> dict:
  76. return self.raw.get("meta", {})
  77. # =============================================================================
  78. # registry.json agent entry
  79. # =============================================================================
  80. class AgentRecord(TypedDict, total=False):
  81. """Shape of an agent entry in registry.json."""
  82. id: str
  83. pid: int
  84. task_dir: str
  85. worktree_path: str
  86. branch: str
  87. platform: str
  88. started_at: str
  89. status: str