draft-board 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/app/backend/.env.example +9 -0
  2. package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_patch.txt +195 -0
  3. package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_stat.txt +6 -0
  4. package/app/backend/CURL_EXAMPLES.md +335 -0
  5. package/app/backend/ENV_SETUP.md +65 -0
  6. package/app/backend/alembic/env.py +71 -0
  7. package/app/backend/alembic/script.py.mako +28 -0
  8. package/app/backend/alembic/versions/001_initial_schema.py +104 -0
  9. package/app/backend/alembic/versions/002_add_jobs_table.py +52 -0
  10. package/app/backend/alembic/versions/003_add_workspace_table.py +48 -0
  11. package/app/backend/alembic/versions/004_add_evidence_table.py +56 -0
  12. package/app/backend/alembic/versions/005_add_verification_commands.py +32 -0
  13. package/app/backend/alembic/versions/006_add_planner_lock_table.py +39 -0
  14. package/app/backend/alembic/versions/007_add_revision_review_tables.py +126 -0
  15. package/app/backend/alembic/versions/008_add_revision_idempotency_and_traceability.py +52 -0
  16. package/app/backend/alembic/versions/009_add_job_health_fields.py +46 -0
  17. package/app/backend/alembic/versions/010_add_review_comment_line_content.py +36 -0
  18. package/app/backend/alembic/versions/011_add_analysis_cache.py +47 -0
  19. package/app/backend/alembic/versions/012_add_boards_table.py +102 -0
  20. package/app/backend/alembic/versions/013_add_ticket_blocking.py +45 -0
  21. package/app/backend/alembic/versions/014_add_agent_sessions.py +220 -0
  22. package/app/backend/alembic/versions/015_add_ticket_sort_order.py +33 -0
  23. package/app/backend/alembic/versions/03220f0b93ae_add_pr_fields_to_ticket.py +49 -0
  24. package/app/backend/alembic/versions/0c2d89fff3b1_seed_board_configs_from_yaml.py +206 -0
  25. package/app/backend/alembic/versions/3348e5cf54c1_add_merge_checklist_table.py +67 -0
  26. package/app/backend/alembic/versions/357c780ee445_add_goal_status.py +34 -0
  27. package/app/backend/alembic/versions/553340b7e26c_add_autonomy_fields_to_goal.py +65 -0
  28. package/app/backend/alembic/versions/774dc335c679_merge_migration_heads.py +23 -0
  29. package/app/backend/alembic/versions/7b307e847cbd_merge_heads.py +23 -0
  30. package/app/backend/alembic/versions/82ecd978cc70_add_missing_indexes.py +48 -0
  31. package/app/backend/alembic/versions/8ef5054dc280_add_normalized_log_entries.py +173 -0
  32. package/app/backend/alembic/versions/8f3e2bd8ea3b_merge_migration_heads.py +23 -0
  33. package/app/backend/alembic/versions/9d17f0698d3b_add_config_column_to_boards_table.py +30 -0
  34. package/app/backend/alembic/versions/add_agent_conversation_history.py +72 -0
  35. package/app/backend/alembic/versions/add_job_variant.py +34 -0
  36. package/app/backend/alembic/versions/add_performance_indexes.py +95 -0
  37. package/app/backend/alembic/versions/add_repos_and_board_repos.py +174 -0
  38. package/app/backend/alembic/versions/add_session_id_to_jobs.py +27 -0
  39. package/app/backend/alembic/versions/add_sqlite_backend_tables.py +104 -0
  40. package/app/backend/alembic/versions/b10fb0b62240_add_diff_content_to_revisions.py +34 -0
  41. package/app/backend/alembic.ini +89 -0
  42. package/app/backend/app/__init__.py +3 -0
  43. package/app/backend/app/data_dir.py +85 -0
  44. package/app/backend/app/database.py +70 -0
  45. package/app/backend/app/database_sync.py +64 -0
  46. package/app/backend/app/dependencies/__init__.py +5 -0
  47. package/app/backend/app/dependencies/auth.py +80 -0
  48. package/app/backend/app/dependencies.py +43 -0
  49. package/app/backend/app/exceptions.py +178 -0
  50. package/app/backend/app/executors/__init__.py +1 -0
  51. package/app/backend/app/executors/adapters/__init__.py +1 -0
  52. package/app/backend/app/executors/adapters/aider.py +152 -0
  53. package/app/backend/app/executors/adapters/amazon_q.py +103 -0
  54. package/app/backend/app/executors/adapters/amp.py +123 -0
  55. package/app/backend/app/executors/adapters/claude.py +177 -0
  56. package/app/backend/app/executors/adapters/cline.py +127 -0
  57. package/app/backend/app/executors/adapters/codex.py +167 -0
  58. package/app/backend/app/executors/adapters/copilot.py +202 -0
  59. package/app/backend/app/executors/adapters/cursor.py +87 -0
  60. package/app/backend/app/executors/adapters/droid.py +123 -0
  61. package/app/backend/app/executors/adapters/gemini.py +132 -0
  62. package/app/backend/app/executors/adapters/goose.py +131 -0
  63. package/app/backend/app/executors/adapters/opencode.py +123 -0
  64. package/app/backend/app/executors/adapters/qwen.py +123 -0
  65. package/app/backend/app/executors/plugins/__init__.py +1 -0
  66. package/app/backend/app/executors/registry.py +202 -0
  67. package/app/backend/app/executors/spec.py +226 -0
  68. package/app/backend/app/main.py +486 -0
  69. package/app/backend/app/middleware/__init__.py +13 -0
  70. package/app/backend/app/middleware/idempotency.py +426 -0
  71. package/app/backend/app/middleware/rate_limit.py +312 -0
  72. package/app/backend/app/middleware/security_headers.py +43 -0
  73. package/app/backend/app/middleware/timeout.py +37 -0
  74. package/app/backend/app/models/__init__.py +56 -0
  75. package/app/backend/app/models/agent_conversation_history.py +56 -0
  76. package/app/backend/app/models/agent_session.py +127 -0
  77. package/app/backend/app/models/analysis_cache.py +49 -0
  78. package/app/backend/app/models/base.py +9 -0
  79. package/app/backend/app/models/board.py +79 -0
  80. package/app/backend/app/models/board_repo.py +68 -0
  81. package/app/backend/app/models/cost_budget.py +42 -0
  82. package/app/backend/app/models/enums.py +40 -0
  83. package/app/backend/app/models/evidence.py +132 -0
  84. package/app/backend/app/models/goal.py +102 -0
  85. package/app/backend/app/models/idempotency_entry.py +30 -0
  86. package/app/backend/app/models/job.py +163 -0
  87. package/app/backend/app/models/job_queue.py +39 -0
  88. package/app/backend/app/models/kv_store.py +28 -0
  89. package/app/backend/app/models/merge_checklist.py +87 -0
  90. package/app/backend/app/models/normalized_log.py +100 -0
  91. package/app/backend/app/models/planner_lock.py +43 -0
  92. package/app/backend/app/models/rate_limit_entry.py +25 -0
  93. package/app/backend/app/models/repo.py +66 -0
  94. package/app/backend/app/models/review_comment.py +91 -0
  95. package/app/backend/app/models/review_summary.py +69 -0
  96. package/app/backend/app/models/revision.py +130 -0
  97. package/app/backend/app/models/ticket.py +223 -0
  98. package/app/backend/app/models/ticket_event.py +83 -0
  99. package/app/backend/app/models/user.py +47 -0
  100. package/app/backend/app/models/workspace.py +71 -0
  101. package/app/backend/app/redis_client.py +119 -0
  102. package/app/backend/app/routers/__init__.py +29 -0
  103. package/app/backend/app/routers/agents.py +296 -0
  104. package/app/backend/app/routers/auth.py +94 -0
  105. package/app/backend/app/routers/board.py +885 -0
  106. package/app/backend/app/routers/dashboard.py +351 -0
  107. package/app/backend/app/routers/debug.py +528 -0
  108. package/app/backend/app/routers/evidence.py +96 -0
  109. package/app/backend/app/routers/executors.py +324 -0
  110. package/app/backend/app/routers/goals.py +574 -0
  111. package/app/backend/app/routers/jobs.py +448 -0
  112. package/app/backend/app/routers/maintenance.py +172 -0
  113. package/app/backend/app/routers/merge.py +360 -0
  114. package/app/backend/app/routers/planner.py +537 -0
  115. package/app/backend/app/routers/pull_requests.py +382 -0
  116. package/app/backend/app/routers/repos.py +263 -0
  117. package/app/backend/app/routers/revisions.py +939 -0
  118. package/app/backend/app/routers/settings.py +267 -0
  119. package/app/backend/app/routers/tickets.py +2003 -0
  120. package/app/backend/app/routers/webhooks.py +143 -0
  121. package/app/backend/app/routers/websocket.py +249 -0
  122. package/app/backend/app/schemas/__init__.py +109 -0
  123. package/app/backend/app/schemas/board.py +87 -0
  124. package/app/backend/app/schemas/common.py +33 -0
  125. package/app/backend/app/schemas/evidence.py +87 -0
  126. package/app/backend/app/schemas/goal.py +90 -0
  127. package/app/backend/app/schemas/job.py +97 -0
  128. package/app/backend/app/schemas/merge.py +139 -0
  129. package/app/backend/app/schemas/planner.py +500 -0
  130. package/app/backend/app/schemas/repo.py +187 -0
  131. package/app/backend/app/schemas/review.py +137 -0
  132. package/app/backend/app/schemas/revision.py +114 -0
  133. package/app/backend/app/schemas/ticket.py +238 -0
  134. package/app/backend/app/schemas/ticket_event.py +72 -0
  135. package/app/backend/app/schemas/workspace.py +19 -0
  136. package/app/backend/app/services/__init__.py +31 -0
  137. package/app/backend/app/services/agent_memory_service.py +223 -0
  138. package/app/backend/app/services/agent_registry.py +346 -0
  139. package/app/backend/app/services/agent_session_manager.py +318 -0
  140. package/app/backend/app/services/agent_session_service.py +219 -0
  141. package/app/backend/app/services/agent_tools.py +379 -0
  142. package/app/backend/app/services/auth_service.py +98 -0
  143. package/app/backend/app/services/autonomy_service.py +380 -0
  144. package/app/backend/app/services/board_repo_service.py +201 -0
  145. package/app/backend/app/services/board_service.py +326 -0
  146. package/app/backend/app/services/cleanup_service.py +1085 -0
  147. package/app/backend/app/services/config_service.py +908 -0
  148. package/app/backend/app/services/context_gatherer.py +557 -0
  149. package/app/backend/app/services/cost_tracking_service.py +293 -0
  150. package/app/backend/app/services/cursor_log_normalizer.py +536 -0
  151. package/app/backend/app/services/delivery_pipeline.py +440 -0
  152. package/app/backend/app/services/executor_service.py +634 -0
  153. package/app/backend/app/services/git_host/__init__.py +11 -0
  154. package/app/backend/app/services/git_host/factory.py +87 -0
  155. package/app/backend/app/services/git_host/github.py +270 -0
  156. package/app/backend/app/services/git_host/gitlab.py +194 -0
  157. package/app/backend/app/services/git_host/protocol.py +75 -0
  158. package/app/backend/app/services/git_merge_simple.py +346 -0
  159. package/app/backend/app/services/git_ops.py +384 -0
  160. package/app/backend/app/services/github_service.py +233 -0
  161. package/app/backend/app/services/goal_service.py +113 -0
  162. package/app/backend/app/services/job_service.py +423 -0
  163. package/app/backend/app/services/job_watchdog_service.py +424 -0
  164. package/app/backend/app/services/langchain_adapter.py +122 -0
  165. package/app/backend/app/services/llm_provider_clients.py +351 -0
  166. package/app/backend/app/services/llm_service.py +285 -0
  167. package/app/backend/app/services/log_normalizer.py +342 -0
  168. package/app/backend/app/services/log_stream_service.py +276 -0
  169. package/app/backend/app/services/merge_checklist_service.py +264 -0
  170. package/app/backend/app/services/merge_service.py +784 -0
  171. package/app/backend/app/services/orchestrator_log.py +84 -0
  172. package/app/backend/app/services/planner_service.py +1662 -0
  173. package/app/backend/app/services/planner_tick_sync.py +1040 -0
  174. package/app/backend/app/services/queued_message_service.py +156 -0
  175. package/app/backend/app/services/reliability_wrapper.py +389 -0
  176. package/app/backend/app/services/repo_discovery_service.py +318 -0
  177. package/app/backend/app/services/review_service.py +334 -0
  178. package/app/backend/app/services/revision_service.py +389 -0
  179. package/app/backend/app/services/safe_autopilot.py +510 -0
  180. package/app/backend/app/services/sqlite_worker.py +372 -0
  181. package/app/backend/app/services/task_dispatch.py +135 -0
  182. package/app/backend/app/services/ticket_generation_service.py +1781 -0
  183. package/app/backend/app/services/ticket_service.py +486 -0
  184. package/app/backend/app/services/udar_planner_service.py +1007 -0
  185. package/app/backend/app/services/webhook_service.py +126 -0
  186. package/app/backend/app/services/workspace_service.py +465 -0
  187. package/app/backend/app/services/worktree_file_service.py +92 -0
  188. package/app/backend/app/services/worktree_validator.py +213 -0
  189. package/app/backend/app/sqlite_kv.py +278 -0
  190. package/app/backend/app/state_machine.py +128 -0
  191. package/app/backend/app/templates/__init__.py +5 -0
  192. package/app/backend/app/templates/registry.py +243 -0
  193. package/app/backend/app/utils/__init__.py +5 -0
  194. package/app/backend/app/utils/artifact_reader.py +87 -0
  195. package/app/backend/app/utils/circuit_breaker.py +229 -0
  196. package/app/backend/app/utils/db_retry.py +136 -0
  197. package/app/backend/app/utils/ignored_fields.py +123 -0
  198. package/app/backend/app/utils/validators.py +54 -0
  199. package/app/backend/app/websocket/__init__.py +5 -0
  200. package/app/backend/app/websocket/manager.py +179 -0
  201. package/app/backend/app/websocket/state_tracker.py +113 -0
  202. package/app/backend/app/worker.py +3190 -0
  203. package/app/backend/calculator_tickets.json +40 -0
  204. package/app/backend/canary_tests.sh +591 -0
  205. package/app/backend/celerybeat-schedule +0 -0
  206. package/app/backend/celerybeat-schedule-shm +0 -0
  207. package/app/backend/celerybeat-schedule-wal +0 -0
  208. package/app/backend/logs/.gitkeep +3 -0
  209. package/app/backend/multiplication_division_implementation_tickets.json +55 -0
  210. package/app/backend/multiplication_division_tickets.json +42 -0
  211. package/app/backend/pyproject.toml +45 -0
  212. package/app/backend/requirements-dev.txt +8 -0
  213. package/app/backend/requirements.txt +20 -0
  214. package/app/backend/run.sh +30 -0
  215. package/app/backend/run_with_logs.sh +10 -0
  216. package/app/backend/scientific_calculator_tickets.json +40 -0
  217. package/app/backend/scripts/extract_openapi.py +21 -0
  218. package/app/backend/scripts/seed_demo.py +187 -0
  219. package/app/backend/setup_demo_review.py +302 -0
  220. package/app/backend/test_actual_parse.py +41 -0
  221. package/app/backend/test_agent_streaming.py +61 -0
  222. package/app/backend/test_parse.py +51 -0
  223. package/app/backend/test_streaming.py +51 -0
  224. package/app/backend/test_subprocess_streaming.py +50 -0
  225. package/app/backend/tests/__init__.py +1 -0
  226. package/app/backend/tests/conftest.py +46 -0
  227. package/app/backend/tests/test_auth.py +341 -0
  228. package/app/backend/tests/test_autonomy_service.py +391 -0
  229. package/app/backend/tests/test_cleanup_service_safety.py +417 -0
  230. package/app/backend/tests/test_middleware.py +279 -0
  231. package/app/backend/tests/test_planner_providers.py +290 -0
  232. package/app/backend/tests/test_planner_unblock.py +183 -0
  233. package/app/backend/tests/test_revision_invariants.py +618 -0
  234. package/app/backend/tests/test_sqlite_kv.py +290 -0
  235. package/app/backend/tests/test_sqlite_worker.py +353 -0
  236. package/app/backend/tests/test_task_dispatch.py +100 -0
  237. package/app/backend/tests/test_ticket_validation.py +304 -0
  238. package/app/backend/tests/test_udar_agent.py +693 -0
  239. package/app/backend/tests/test_webhook_service.py +184 -0
  240. package/app/backend/tickets_output.json +59 -0
  241. package/app/backend/user_management_tickets.json +50 -0
  242. package/app/backend/uvicorn.log +0 -0
  243. package/app/draft.yaml +313 -0
  244. package/app/frontend/dist/assets/index-LcjCczu5.js +155 -0
  245. package/app/frontend/dist/assets/index-_FP_279e.css +1 -0
  246. package/app/frontend/dist/index.html +14 -0
  247. package/app/frontend/dist/vite.svg +1 -0
  248. package/app/frontend/package.json +101 -0
  249. package/bin/cli.js +527 -0
  250. package/package.json +37 -0
@@ -0,0 +1,87 @@
1
+ """Pydantic schemas for Evidence entity."""
2
+
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class EvidenceKind(StrEnum):
10
+ """Enum representing the kind of evidence.
11
+
12
+ Metadata evidence (JSON in stdout_path):
13
+ - EXECUTOR_META: JSON with exit_code, duration, executor_type, mode, command
14
+ - VERIFY_META: JSON with exit_code, commands_run, duration, results per command
15
+
16
+ Evidence types for execution:
17
+ - EXECUTOR_STDOUT: stdout from executor CLI (Claude/Cursor)
18
+ - EXECUTOR_STDERR: stderr from executor CLI
19
+ - GIT_DIFF_STAT: output of `git diff --stat`
20
+ - GIT_DIFF_PATCH: full git diff patch
21
+
22
+ Evidence types for verification:
23
+ - VERIFY_STDOUT: stdout from verification command
24
+ - VERIFY_STDERR: stderr from verification command
25
+
26
+ Legacy types (kept for backwards compatibility):
27
+ - COMMAND_LOG: generic command output
28
+ - TEST_REPORT: test framework report
29
+ """
30
+
31
+ # Metadata evidence (JSON)
32
+ EXECUTOR_META = "executor_meta"
33
+ VERIFY_META = "verify_meta"
34
+
35
+ # Executor evidence
36
+ EXECUTOR_STDOUT = "executor_stdout"
37
+ EXECUTOR_STDERR = "executor_stderr"
38
+
39
+ # Git diff evidence
40
+ GIT_DIFF_STAT = "git_diff_stat"
41
+ GIT_DIFF_PATCH = "git_diff_patch"
42
+
43
+ # Verification evidence
44
+ VERIFY_STDOUT = "verify_stdout"
45
+ VERIFY_STDERR = "verify_stderr"
46
+
47
+ # Merge evidence
48
+ MERGE_STDOUT = "merge_stdout"
49
+ MERGE_STDERR = "merge_stderr"
50
+ MERGE_META = "merge_meta"
51
+
52
+ # Legacy types (backwards compatibility)
53
+ COMMAND_LOG = "command_log"
54
+ TEST_REPORT = "test_report"
55
+
56
+
57
+ class EvidenceResponse(BaseModel):
58
+ """Schema for evidence response."""
59
+
60
+ id: str
61
+ ticket_id: str
62
+ job_id: str
63
+ kind: EvidenceKind
64
+ command: str
65
+ exit_code: int
66
+ stdout_path: str | None
67
+ stderr_path: str | None
68
+ created_at: datetime
69
+ succeeded: bool = Field(
70
+ description="Whether the command succeeded (exit_code == 0)"
71
+ )
72
+
73
+ model_config = {"from_attributes": True}
74
+
75
+
76
+ class EvidenceDetailResponse(EvidenceResponse):
77
+ """Schema for detailed evidence response including stdout/stderr content."""
78
+
79
+ stdout: str | None = Field(None, description="Content of stdout (if available)")
80
+ stderr: str | None = Field(None, description="Content of stderr (if available)")
81
+
82
+
83
+ class EvidenceListResponse(BaseModel):
84
+ """Schema for list of evidence records."""
85
+
86
+ evidence: list[EvidenceResponse]
87
+ total: int
@@ -0,0 +1,90 @@
1
+ """Pydantic schemas for Goal entity."""
2
+
3
+ from datetime import datetime
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class AutonomySettings(BaseModel):
9
+ """Schema for autonomy settings on a goal."""
10
+
11
+ autonomy_enabled: bool = False
12
+ auto_approve_tickets: bool = False
13
+ auto_approve_revisions: bool = False
14
+ auto_merge: bool = False
15
+ auto_approve_followups: bool = False
16
+ max_auto_approvals: int | None = None
17
+
18
+
19
+ class GoalCreate(BaseModel):
20
+ """Schema for creating a new goal."""
21
+
22
+ title: str = Field(..., min_length=1, max_length=255)
23
+ description: str | None = None
24
+ board_id: str | None = Field(
25
+ None,
26
+ description="Board ID to scope this goal to (recommended for multi-board setups)",
27
+ )
28
+ # Autonomy fields (optional at creation)
29
+ autonomy_enabled: bool = False
30
+ auto_approve_tickets: bool = False
31
+ auto_approve_revisions: bool = False
32
+ auto_merge: bool = False
33
+ auto_approve_followups: bool = False
34
+ max_auto_approvals: int | None = None
35
+
36
+
37
+ class GoalUpdate(BaseModel):
38
+ """Schema for updating a goal."""
39
+
40
+ title: str | None = Field(None, min_length=1, max_length=255)
41
+ description: str | None = None
42
+ autonomy_enabled: bool | None = None
43
+ auto_approve_tickets: bool | None = None
44
+ auto_approve_revisions: bool | None = None
45
+ auto_merge: bool | None = None
46
+ auto_approve_followups: bool | None = None
47
+ max_auto_approvals: int | None = None
48
+
49
+
50
+ class GoalResponse(BaseModel):
51
+ """Schema for goal response."""
52
+
53
+ id: str
54
+ board_id: str | None = None
55
+ title: str
56
+ description: str | None
57
+ created_at: datetime
58
+ updated_at: datetime
59
+ # Autonomy fields
60
+ autonomy_enabled: bool = False
61
+ auto_approve_tickets: bool = False
62
+ auto_approve_revisions: bool = False
63
+ auto_merge: bool = False
64
+ auto_approve_followups: bool = False
65
+ max_auto_approvals: int | None = None
66
+ auto_approval_count: int = 0
67
+
68
+ model_config = {"from_attributes": True}
69
+
70
+
71
+ class GoalListResponse(BaseModel):
72
+ """Schema for list of goals response."""
73
+
74
+ goals: list[GoalResponse]
75
+ total: int
76
+
77
+
78
+ class AutonomyStatusResponse(BaseModel):
79
+ """Schema for autonomy status response."""
80
+
81
+ goal_id: str
82
+ autonomy_enabled: bool
83
+ auto_approve_tickets: bool
84
+ auto_approve_revisions: bool
85
+ auto_merge: bool
86
+ auto_approve_followups: bool
87
+ max_auto_approvals: int | None
88
+ auto_approval_count: int
89
+ budget_remaining: float | None = None
90
+ safety_checks: list[dict] = Field(default_factory=list)
@@ -0,0 +1,97 @@
1
+ """Pydantic schemas for Job entity."""
2
+
3
+ from datetime import datetime
4
+ from enum import StrEnum
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+
9
+ class JobKind(StrEnum):
10
+ """Enum representing the kind of job."""
11
+
12
+ EXECUTE = "execute"
13
+ VERIFY = "verify"
14
+ RESUME = "resume"
15
+
16
+
17
+ class JobStatus(StrEnum):
18
+ """Enum representing the status of a job."""
19
+
20
+ QUEUED = "queued"
21
+ RUNNING = "running"
22
+ SUCCEEDED = "succeeded"
23
+ FAILED = "failed"
24
+ CANCELED = "canceled"
25
+
26
+
27
+ class JobResponse(BaseModel):
28
+ """Schema for job response."""
29
+
30
+ id: str
31
+ ticket_id: str
32
+ kind: JobKind
33
+ status: JobStatus
34
+ created_at: datetime
35
+ started_at: datetime | None = None
36
+ finished_at: datetime | None = None
37
+ exit_code: int | None = None
38
+ log_path: str | None = None
39
+
40
+ model_config = {"from_attributes": True}
41
+
42
+
43
+ class JobDetailResponse(JobResponse):
44
+ """Schema for detailed job response including logs."""
45
+
46
+ logs: str | None = Field(None, description="Job log content")
47
+
48
+
49
+ class JobListResponse(BaseModel):
50
+ """Schema for list of jobs response."""
51
+
52
+ jobs: list[JobResponse]
53
+ total: int
54
+
55
+
56
+ class JobCreateResponse(JobResponse):
57
+ """Schema for job creation response."""
58
+
59
+ celery_task_id: str | None = None
60
+
61
+
62
+ class CancelJobResponse(BaseModel):
63
+ """Schema for job cancellation response."""
64
+
65
+ id: str
66
+ status: JobStatus
67
+ message: str
68
+
69
+
70
+ class QueuedJobResponse(BaseModel):
71
+ """Schema for a job in the queue with ticket info."""
72
+
73
+ id: str
74
+ ticket_id: str
75
+ ticket_title: str
76
+ kind: JobKind
77
+ status: JobStatus
78
+ created_at: datetime
79
+ started_at: datetime | None = None
80
+ queue_position: int | None = Field(
81
+ None, description="Position in queue (1-based, None if running)"
82
+ )
83
+
84
+ model_config = {"from_attributes": True}
85
+
86
+
87
+ class QueueStatusResponse(BaseModel):
88
+ """Schema for queue status response."""
89
+
90
+ running: list[QueuedJobResponse] = Field(
91
+ default_factory=list, description="Currently running jobs"
92
+ )
93
+ queued: list[QueuedJobResponse] = Field(
94
+ default_factory=list, description="Jobs waiting in queue (ordered)"
95
+ )
96
+ total_running: int = 0
97
+ total_queued: int = 0
@@ -0,0 +1,139 @@
1
+ """Pydantic schemas for merge operations."""
2
+
3
+ from enum import StrEnum
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class MergeStrategy(StrEnum):
9
+ """Supported merge strategies."""
10
+
11
+ MERGE = "merge"
12
+ REBASE = "rebase"
13
+
14
+
15
+ class MergeRequest(BaseModel):
16
+ """Request body for merging a ticket's changes."""
17
+
18
+ strategy: MergeStrategy = MergeStrategy.MERGE
19
+ delete_worktree: bool = Field(
20
+ default=True,
21
+ description="Delete the worktree after successful merge",
22
+ )
23
+ cleanup_artifacts: bool = Field(
24
+ default=True,
25
+ description="Clean up evidence files after successful merge",
26
+ )
27
+
28
+
29
+ class MergeResponse(BaseModel):
30
+ """Response from a merge operation."""
31
+
32
+ success: bool
33
+ message: str
34
+ exit_code: int
35
+ evidence_id: str | None = None
36
+ pull_warning: str | None = Field(
37
+ default=None,
38
+ description="Warning if merge succeeded without pulling latest (local-only merge)",
39
+ )
40
+
41
+
42
+ class MergeStatusResponse(BaseModel):
43
+ """Response for merge status query."""
44
+
45
+ ticket_id: str
46
+ can_merge: bool = Field(
47
+ description="Whether the ticket can be merged (done state, approved revision, active worktree)"
48
+ )
49
+ is_merged: bool = Field(
50
+ description="Whether the ticket has been successfully merged"
51
+ )
52
+ has_approved_revision: bool
53
+ workspace: dict | None = Field(
54
+ description="Workspace info with worktree_path and branch_name"
55
+ )
56
+ last_merge_attempt: dict | None = Field(
57
+ description="Info about the last merge attempt event"
58
+ )
59
+
60
+
61
+ class ConflictStatusResponse(BaseModel):
62
+ """Response for conflict status query."""
63
+
64
+ has_conflict: bool
65
+ operation: str | None = Field(
66
+ description="Type of conflict operation: rebase, merge, cherry_pick, revert"
67
+ )
68
+ conflicted_files: list[str] = Field(default_factory=list)
69
+ can_continue: bool = False
70
+ can_abort: bool = False
71
+ divergence: dict | None = Field(
72
+ default=None,
73
+ description="Branch divergence info (ahead/behind counts)",
74
+ )
75
+
76
+
77
+ class RebaseResponse(BaseModel):
78
+ """Response from a rebase operation."""
79
+
80
+ success: bool
81
+ message: str
82
+ has_conflicts: bool = False
83
+ conflicted_files: list[str] = Field(default_factory=list)
84
+
85
+
86
+ class AbortResponse(BaseModel):
87
+ """Response from an abort operation."""
88
+
89
+ success: bool
90
+ message: str
91
+
92
+
93
+ class PushResponse(BaseModel):
94
+ """Response from a push operation."""
95
+
96
+ success: bool
97
+ message: str
98
+
99
+
100
+ class PushStatusResponse(BaseModel):
101
+ """Response for push status query."""
102
+
103
+ ahead: int = 0
104
+ behind: int = 0
105
+ remote_exists: bool = False
106
+ needs_push: bool = False
107
+
108
+
109
+ class CleanupRequest(BaseModel):
110
+ """Request body for maintenance cleanup."""
111
+
112
+ dry_run: bool = Field(
113
+ default=True,
114
+ description="If true, only report what would be deleted without actually deleting",
115
+ )
116
+ delete_worktrees: bool = Field(
117
+ default=True,
118
+ description="Delete stale and orphaned worktrees",
119
+ )
120
+ delete_evidence: bool = Field(
121
+ default=True,
122
+ description="Delete old evidence files",
123
+ )
124
+
125
+
126
+ class CleanupResponse(BaseModel):
127
+ """Response from a cleanup operation."""
128
+
129
+ dry_run: bool
130
+ worktrees_deleted: int
131
+ worktrees_failed: int
132
+ worktrees_skipped: int = Field(
133
+ default=0,
134
+ description="Worktrees skipped due to ticket state (executing/verifying/needs_human)",
135
+ )
136
+ evidence_files_deleted: int
137
+ evidence_files_failed: int
138
+ bytes_freed: int
139
+ details: list[str]