@proletariat/cli 0.3.95 → 0.3.97

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 (260) hide show
  1. package/dist/commands/config/index.js +1 -1
  2. package/dist/commands/config/index.js.map +1 -1
  3. package/dist/commands/db/repair.js +160 -35
  4. package/dist/commands/db/repair.js.map +1 -1
  5. package/dist/commands/gc.d.ts +14 -0
  6. package/dist/commands/gc.js +208 -0
  7. package/dist/commands/gc.js.map +1 -0
  8. package/dist/commands/linear/connect.d.ts +5 -0
  9. package/dist/commands/linear/connect.js +84 -0
  10. package/dist/commands/linear/connect.js.map +1 -1
  11. package/dist/commands/pr/merge.js +1 -1
  12. package/dist/commands/pr/merge.js.map +1 -1
  13. package/dist/commands/session/attach.d.ts +3 -3
  14. package/dist/commands/session/attach.js +37 -79
  15. package/dist/commands/session/attach.js.map +1 -1
  16. package/dist/commands/session/exec.js +1 -1
  17. package/dist/commands/session/exec.js.map +1 -1
  18. package/dist/commands/session/health.js +1 -1
  19. package/dist/commands/session/health.js.map +1 -1
  20. package/dist/commands/session/inspect.js +1 -1
  21. package/dist/commands/session/inspect.js.map +1 -1
  22. package/dist/commands/session/list.d.ts +1 -0
  23. package/dist/commands/session/list.js +111 -78
  24. package/dist/commands/session/list.js.map +1 -1
  25. package/dist/commands/session/peek.js +1 -1
  26. package/dist/commands/session/peek.js.map +1 -1
  27. package/dist/commands/session/poke.js +1 -1
  28. package/dist/commands/session/poke.js.map +1 -1
  29. package/dist/commands/session/prune.js +50 -11
  30. package/dist/commands/session/prune.js.map +1 -1
  31. package/dist/commands/session/restart.js +1 -1
  32. package/dist/commands/session/restart.js.map +1 -1
  33. package/dist/commands/session/watch.d.ts +1 -0
  34. package/dist/commands/session/watch.js +46 -2
  35. package/dist/commands/session/watch.js.map +1 -1
  36. package/dist/commands/{action → sync}/index.d.ts +2 -6
  37. package/dist/commands/sync/index.js +68 -0
  38. package/dist/commands/sync/index.js.map +1 -0
  39. package/dist/commands/sync/pause.d.ts +10 -0
  40. package/dist/commands/sync/pause.js +35 -0
  41. package/dist/commands/sync/pause.js.map +1 -0
  42. package/dist/commands/sync/queue.d.ts +10 -0
  43. package/dist/commands/sync/queue.js +106 -0
  44. package/dist/commands/sync/queue.js.map +1 -0
  45. package/dist/commands/sync/resume.d.ts +10 -0
  46. package/dist/commands/sync/resume.js +34 -0
  47. package/dist/commands/sync/resume.js.map +1 -0
  48. package/dist/commands/sync/start.d.ts +11 -0
  49. package/dist/commands/sync/start.js +73 -0
  50. package/dist/commands/sync/start.js.map +1 -0
  51. package/dist/commands/sync/status.d.ts +10 -0
  52. package/dist/commands/sync/status.js +40 -0
  53. package/dist/commands/sync/status.js.map +1 -0
  54. package/dist/commands/sync/stop.d.ts +10 -0
  55. package/dist/commands/sync/stop.js +39 -0
  56. package/dist/commands/sync/stop.js.map +1 -0
  57. package/dist/commands/ticket/create.js +1 -1
  58. package/dist/commands/ticket/create.js.map +1 -1
  59. package/dist/commands/ticket/edit.js +1 -1
  60. package/dist/commands/ticket/edit.js.map +1 -1
  61. package/dist/commands/ticket/list.js +1 -1
  62. package/dist/commands/ticket/list.js.map +1 -1
  63. package/dist/commands/ticket/update.js +1 -1
  64. package/dist/commands/ticket/update.js.map +1 -1
  65. package/dist/commands/work/complete.d.ts +1 -0
  66. package/dist/commands/work/complete.js +27 -25
  67. package/dist/commands/work/complete.js.map +1 -1
  68. package/dist/commands/work/drop.d.ts +14 -0
  69. package/dist/commands/work/drop.js +215 -0
  70. package/dist/commands/work/drop.js.map +1 -0
  71. package/dist/commands/work/ready.d.ts +1 -0
  72. package/dist/commands/work/ready.js +26 -25
  73. package/dist/commands/work/ready.js.map +1 -1
  74. package/dist/commands/work/resolve.js +1 -1
  75. package/dist/commands/work/resolve.js.map +1 -1
  76. package/dist/commands/work/ship.d.ts +1 -0
  77. package/dist/commands/work/ship.js +33 -32
  78. package/dist/commands/work/ship.js.map +1 -1
  79. package/dist/commands/work/start.d.ts +2 -0
  80. package/dist/commands/work/start.js +172 -125
  81. package/dist/commands/work/start.js.map +1 -1
  82. package/dist/commands/work/stop.d.ts +1 -0
  83. package/dist/commands/work/stop.js +40 -0
  84. package/dist/commands/work/stop.js.map +1 -1
  85. package/dist/lib/agents/commands.js +7 -5
  86. package/dist/lib/agents/commands.js.map +1 -1
  87. package/dist/lib/dashboard/data.js +1 -1
  88. package/dist/lib/dashboard/data.js.map +1 -1
  89. package/dist/lib/database/db-safety.d.ts +22 -0
  90. package/dist/lib/database/db-safety.js +91 -4
  91. package/dist/lib/database/db-safety.js.map +1 -1
  92. package/dist/lib/database/drizzle-schema.d.ts +17 -0
  93. package/dist/lib/database/drizzle-schema.js +1 -0
  94. package/dist/lib/database/drizzle-schema.js.map +1 -1
  95. package/dist/lib/database/index.d.ts +1 -1
  96. package/dist/lib/database/index.js +1 -1
  97. package/dist/lib/database/index.js.map +1 -1
  98. package/dist/lib/database/migrations/0017_drop_agent_work_fk.d.ts +13 -0
  99. package/dist/lib/database/migrations/0017_drop_agent_work_fk.js +85 -0
  100. package/dist/lib/database/migrations/0017_drop_agent_work_fk.js.map +1 -0
  101. package/dist/lib/database/migrations/0018_create_ticket_refs.d.ts +11 -0
  102. package/dist/lib/database/migrations/0018_create_ticket_refs.js +40 -0
  103. package/dist/lib/database/migrations/0018_create_ticket_refs.js.map +1 -0
  104. package/dist/lib/database/migrations/0019_gc_artifact_cleanup.d.ts +9 -0
  105. package/dist/lib/database/migrations/0019_gc_artifact_cleanup.js +23 -0
  106. package/dist/lib/database/migrations/0019_gc_artifact_cleanup.js.map +1 -0
  107. package/dist/lib/database/migrations/0020_transition_map.d.ts +2 -0
  108. package/dist/lib/database/migrations/0020_transition_map.js +27 -0
  109. package/dist/lib/database/migrations/0020_transition_map.js.map +1 -0
  110. package/dist/lib/database/migrations/index.js +8 -0
  111. package/dist/lib/database/migrations/index.js.map +1 -1
  112. package/dist/lib/execution/config.d.ts +10 -0
  113. package/dist/lib/execution/config.js +24 -0
  114. package/dist/lib/execution/config.js.map +1 -1
  115. package/dist/lib/execution/devcontainer.js +1 -1
  116. package/dist/lib/execution/devcontainer.js.map +1 -1
  117. package/dist/lib/execution/preflight.d.ts +51 -0
  118. package/dist/lib/execution/preflight.js +278 -0
  119. package/dist/lib/execution/preflight.js.map +1 -0
  120. package/dist/lib/execution/runners/docker-management.d.ts +9 -0
  121. package/dist/lib/execution/runners/docker-management.js +14 -3
  122. package/dist/lib/execution/runners/docker-management.js.map +1 -1
  123. package/dist/lib/execution/runners/prompt-builder.d.ts +6 -0
  124. package/dist/lib/execution/runners/prompt-builder.js +53 -10
  125. package/dist/lib/execution/runners/prompt-builder.js.map +1 -1
  126. package/dist/lib/execution/runners/shared.d.ts +1 -1
  127. package/dist/lib/execution/runners/shared.js +1 -1
  128. package/dist/lib/execution/runners/shared.js.map +1 -1
  129. package/dist/lib/execution/session-utils.d.ts +39 -0
  130. package/dist/lib/execution/session-utils.js +111 -0
  131. package/dist/lib/execution/session-utils.js.map +1 -1
  132. package/dist/lib/execution/spawner.d.ts +11 -1
  133. package/dist/lib/execution/spawner.js +46 -17
  134. package/dist/lib/execution/spawner.js.map +1 -1
  135. package/dist/lib/execution/storage.js +2 -1
  136. package/dist/lib/execution/storage.js.map +1 -1
  137. package/dist/lib/execution/ticket-refs.d.ts +71 -0
  138. package/dist/lib/execution/ticket-refs.js +125 -0
  139. package/dist/lib/execution/ticket-refs.js.map +1 -0
  140. package/dist/lib/execution/types.d.ts +7 -2
  141. package/dist/lib/execution/types.js +5 -3
  142. package/dist/lib/execution/types.js.map +1 -1
  143. package/dist/lib/external-issues/utils.d.ts +25 -0
  144. package/dist/lib/external-issues/utils.js +32 -0
  145. package/dist/lib/external-issues/utils.js.map +1 -0
  146. package/dist/lib/gc/index.d.ts +179 -0
  147. package/dist/lib/gc/index.js +655 -0
  148. package/dist/lib/gc/index.js.map +1 -0
  149. package/dist/lib/mcp/tools/action.js +1 -1
  150. package/dist/lib/mcp/tools/action.js.map +1 -1
  151. package/dist/lib/mcp/tools/ticket.js +1 -1
  152. package/dist/lib/mcp/tools/ticket.js.map +1 -1
  153. package/dist/lib/orchestrate/actions.js +30 -0
  154. package/dist/lib/orchestrate/actions.js.map +1 -1
  155. package/dist/lib/orchestrate/poller.js +3 -3
  156. package/dist/lib/orchestrate/poller.js.map +1 -1
  157. package/dist/lib/orchestrate/presets.js +2 -1
  158. package/dist/lib/orchestrate/presets.js.map +1 -1
  159. package/dist/lib/orchestrate/types.d.ts +1 -1
  160. package/dist/lib/orchestrate/types.js +1 -0
  161. package/dist/lib/orchestrate/types.js.map +1 -1
  162. package/dist/lib/pmo/index.js +1 -1
  163. package/dist/lib/pmo/index.js.map +1 -1
  164. package/dist/lib/pmo/markdown.js +1 -1
  165. package/dist/lib/pmo/markdown.js.map +1 -1
  166. package/dist/lib/pmo/schema.d.ts +3 -1
  167. package/dist/lib/pmo/schema.js +26 -2
  168. package/dist/lib/pmo/schema.js.map +1 -1
  169. package/dist/lib/pmo/storage/actions.js +1 -1
  170. package/dist/lib/pmo/storage/actions.js.map +1 -1
  171. package/dist/lib/pmo/storage/base.js +37 -15
  172. package/dist/lib/pmo/storage/base.js.map +1 -1
  173. package/dist/lib/pmo/storage/projects.js +2 -1
  174. package/dist/lib/pmo/storage/projects.js.map +1 -1
  175. package/dist/lib/pmo/storage/statuses.js +1 -1
  176. package/dist/lib/pmo/storage/statuses.js.map +1 -1
  177. package/dist/lib/pmo/storage/subtasks.js +1 -1
  178. package/dist/lib/pmo/storage/subtasks.js.map +1 -1
  179. package/dist/lib/pmo/storage/templates.js +1 -1
  180. package/dist/lib/pmo/storage/templates.js.map +1 -1
  181. package/dist/lib/pmo/storage/tickets.js +2 -1
  182. package/dist/lib/pmo/storage/tickets.js.map +1 -1
  183. package/dist/lib/pmo/storage/workflow-rules.js +1 -1
  184. package/dist/lib/pmo/storage/workflow-rules.js.map +1 -1
  185. package/dist/lib/pmo/utils.d.ts +6 -189
  186. package/dist/lib/pmo/utils.js +6 -306
  187. package/dist/lib/pmo/utils.js.map +1 -1
  188. package/dist/lib/prompt-json.d.ts +31 -0
  189. package/dist/lib/prompt-json.js.map +1 -1
  190. package/dist/lib/providers/auto-mapper.d.ts +45 -0
  191. package/dist/lib/providers/auto-mapper.js +115 -0
  192. package/dist/lib/providers/auto-mapper.js.map +1 -0
  193. package/dist/lib/providers/state-intents.d.ts +20 -0
  194. package/dist/lib/providers/state-intents.js +61 -7
  195. package/dist/lib/providers/state-intents.js.map +1 -1
  196. package/dist/lib/providers/state-resolution.d.ts +15 -11
  197. package/dist/lib/providers/state-resolution.js +54 -48
  198. package/dist/lib/providers/state-resolution.js.map +1 -1
  199. package/dist/lib/providers/transition-map.d.ts +59 -0
  200. package/dist/lib/providers/transition-map.js +113 -0
  201. package/dist/lib/providers/transition-map.js.map +1 -0
  202. package/dist/lib/session/heartbeat.js +1 -1
  203. package/dist/lib/session/heartbeat.js.map +1 -1
  204. package/dist/lib/session/index.d.ts +3 -1
  205. package/dist/lib/session/index.js +3 -1
  206. package/dist/lib/session/index.js.map +1 -1
  207. package/dist/lib/session/tmux-watchdog.d.ts +157 -0
  208. package/dist/lib/session/tmux-watchdog.js +424 -0
  209. package/dist/lib/session/tmux-watchdog.js.map +1 -0
  210. package/dist/lib/session/watcher.d.ts +22 -4
  211. package/dist/lib/session/watcher.js +66 -8
  212. package/dist/lib/session/watcher.js.map +1 -1
  213. package/dist/lib/sync/daemon-process.d.ts +9 -0
  214. package/dist/lib/sync/daemon-process.js +184 -0
  215. package/dist/lib/sync/daemon-process.js.map +1 -0
  216. package/dist/lib/sync/daemon.d.ts +39 -0
  217. package/dist/lib/sync/daemon.js +91 -0
  218. package/dist/lib/sync/daemon.js.map +1 -0
  219. package/dist/lib/sync/engine.d.ts +38 -0
  220. package/dist/lib/sync/engine.js +145 -0
  221. package/dist/lib/sync/engine.js.map +1 -0
  222. package/dist/lib/sync/merge-queue.d.ts +116 -0
  223. package/dist/lib/sync/merge-queue.js +321 -0
  224. package/dist/lib/sync/merge-queue.js.map +1 -0
  225. package/dist/lib/sync/reconciler.d.ts +44 -0
  226. package/dist/lib/sync/reconciler.js +88 -0
  227. package/dist/lib/sync/reconciler.js.map +1 -0
  228. package/dist/lib/utils/text.d.ts +44 -0
  229. package/dist/lib/utils/text.js +72 -0
  230. package/dist/lib/utils/text.js.map +1 -0
  231. package/dist/lib/work-lifecycle/post-execution.js +1 -1
  232. package/dist/lib/work-lifecycle/post-execution.js.map +1 -1
  233. package/dist/lib/work-lifecycle/settings.d.ts +138 -0
  234. package/dist/lib/work-lifecycle/settings.js +213 -0
  235. package/dist/lib/work-lifecycle/settings.js.map +1 -0
  236. package/dist/lib/work-lifecycle/transition.d.ts +73 -0
  237. package/dist/lib/work-lifecycle/transition.js +124 -0
  238. package/dist/lib/work-lifecycle/transition.js.map +1 -0
  239. package/oclif.manifest.json +3235 -3380
  240. package/package.json +1 -1
  241. package/dist/commands/action/create.d.ts +0 -26
  242. package/dist/commands/action/create.js +0 -245
  243. package/dist/commands/action/create.js.map +0 -1
  244. package/dist/commands/action/delete.d.ts +0 -18
  245. package/dist/commands/action/delete.js +0 -78
  246. package/dist/commands/action/delete.js.map +0 -1
  247. package/dist/commands/action/index.js +0 -103
  248. package/dist/commands/action/index.js.map +0 -1
  249. package/dist/commands/action/list.d.ts +0 -15
  250. package/dist/commands/action/list.js +0 -90
  251. package/dist/commands/action/list.js.map +0 -1
  252. package/dist/commands/action/run.d.ts +0 -20
  253. package/dist/commands/action/run.js +0 -188
  254. package/dist/commands/action/run.js.map +0 -1
  255. package/dist/commands/action/show.d.ts +0 -17
  256. package/dist/commands/action/show.js +0 -78
  257. package/dist/commands/action/show.js.map +0 -1
  258. package/dist/commands/action/update.d.ts +0 -27
  259. package/dist/commands/action/update.js +0 -288
  260. package/dist/commands/action/update.js.map +0 -1
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Sync Engine
3
+ *
4
+ * Orchestrates the reconciliation process: gathers ticket state and PR state,
5
+ * runs the reconciler, and applies corrective actions through providers.
6
+ *
7
+ * This is the "run once" engine. The daemon wraps this with a polling loop.
8
+ */
9
+ import { resolveTicketProvider } from '../providers/resolver.js';
10
+ import { ExecutionStorage } from '../execution/storage.js';
11
+ import { getPRForBranch, getPRChecks, listOpenPRs, } from '../pr/index.js';
12
+ import { reconcileTicket, } from './reconciler.js';
13
+ // =============================================================================
14
+ // Engine
15
+ // =============================================================================
16
+ /**
17
+ * Run a single reconciliation cycle.
18
+ *
19
+ * 1. List all tickets in started/review states
20
+ * 2. For each, check PR status
21
+ * 3. Run reconciler to determine actions
22
+ * 4. Apply actions through providers
23
+ */
24
+ export async function runSyncCycle(db, storage, projectId, options = {}) {
25
+ const { cwd, log, dryRun } = options;
26
+ const execStorage = new ExecutionStorage(db);
27
+ // Gather all tickets in active states (started = In Progress / In Review)
28
+ const startedTickets = await storage.listTickets(projectId, { statusCategory: 'started' });
29
+ // Also check recently completed tickets to catch any that were manually moved
30
+ // but whose PR state doesn't match
31
+ log?.(`Checking ${startedTickets.length} active ticket(s)...`);
32
+ // Pre-fetch open PRs to reduce API calls
33
+ const openPRs = listOpenPRs(cwd);
34
+ const prByBranch = new Map();
35
+ for (const pr of openPRs) {
36
+ prByBranch.set(pr.headBranch, pr);
37
+ }
38
+ const actions = [];
39
+ const errors = [];
40
+ for (const ticket of startedTickets) {
41
+ try {
42
+ const ctx = buildContext(ticket, prByBranch, execStorage, cwd);
43
+ const action = reconcileTicket(ctx);
44
+ if (action) {
45
+ actions.push(action);
46
+ }
47
+ }
48
+ catch (err) {
49
+ const msg = err instanceof Error ? err.message : String(err);
50
+ errors.push({ ticketId: ticket.id, error: msg });
51
+ }
52
+ }
53
+ const result = {
54
+ actions,
55
+ checked: startedTickets.length,
56
+ errors,
57
+ };
58
+ // Apply actions
59
+ const applied = [];
60
+ const skipped = [];
61
+ const failed = [];
62
+ for (const action of actions) {
63
+ if (dryRun) {
64
+ skipped.push(action);
65
+ log?.(`[dry-run] Would ${formatAction(action)}`);
66
+ continue;
67
+ }
68
+ if (action.type === 'flag_stale') {
69
+ // Stale tickets are logged but not moved
70
+ log?.(`Stale: ${action.ticketId} — ${action.reason}`);
71
+ applied.push(action);
72
+ continue;
73
+ }
74
+ try {
75
+ const ticket = await storage.getTicket(action.ticketId);
76
+ if (!ticket) {
77
+ failed.push({ action, error: 'Ticket not found' });
78
+ continue;
79
+ }
80
+ const provider = resolveTicketProvider(action.ticketId, ticket.projectId ?? projectId, db, storage, ticket.metadata);
81
+ if (action.targetStatus) {
82
+ const moveResult = await provider.moveTicket(action.ticketId, action.targetStatus);
83
+ if (moveResult.success) {
84
+ log?.(`Synced: ${action.ticketId} → ${action.targetStatus} (${action.reason})`);
85
+ applied.push(action);
86
+ // If moving to Done, also mark any running executions as completed
87
+ if (action.type === 'move_to_done') {
88
+ markExecutionsCompleted(execStorage, action.ticketId);
89
+ }
90
+ }
91
+ else {
92
+ failed.push({ action, error: moveResult.error ?? 'Move failed' });
93
+ log?.(`Failed: ${action.ticketId} — ${moveResult.error}`);
94
+ }
95
+ }
96
+ }
97
+ catch (err) {
98
+ const msg = err instanceof Error ? err.message : String(err);
99
+ failed.push({ action, error: msg });
100
+ log?.(`Error: ${action.ticketId} — ${msg}`);
101
+ }
102
+ }
103
+ return { result, applied, skipped, failed };
104
+ }
105
+ // =============================================================================
106
+ // Helpers
107
+ // =============================================================================
108
+ function buildContext(ticket, prByBranch, execStorage, cwd) {
109
+ // Look up PR by branch — try ticket.branch first, then external key patterns
110
+ let pr = null;
111
+ let checks = [];
112
+ if (ticket.branch) {
113
+ // First check the pre-fetched open PRs
114
+ pr = prByBranch.get(ticket.branch) ?? null;
115
+ // If not found in open PRs, the PR might be merged/closed — fetch directly
116
+ if (!pr) {
117
+ pr = getPRForBranch(ticket.branch, cwd);
118
+ }
119
+ }
120
+ if (pr && pr.state === 'OPEN') {
121
+ checks = getPRChecks(pr.number, cwd);
122
+ }
123
+ // Check for active execution
124
+ const hasActiveExecution = !!execStorage.getRunningExecution(ticket.id);
125
+ return { ticket, pr, checks, hasActiveExecution };
126
+ }
127
+ function markExecutionsCompleted(execStorage, ticketId) {
128
+ const running = execStorage.getRunningExecution(ticketId);
129
+ if (running) {
130
+ execStorage.updateStatus(running.id, 'completed');
131
+ }
132
+ }
133
+ function formatAction(action) {
134
+ switch (action.type) {
135
+ case 'move_to_done':
136
+ return `move ${action.ticketId} to Done (${action.reason})`;
137
+ case 'move_to_review':
138
+ return `move ${action.ticketId} to Review (${action.reason})`;
139
+ case 'move_to_backlog':
140
+ return `move ${action.ticketId} to Backlog (${action.reason})`;
141
+ case 'flag_stale':
142
+ return `flag ${action.ticketId} as stale (${action.reason})`;
143
+ }
144
+ }
145
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../src/lib/sync/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Merge Queue
3
+ *
4
+ * Sequential rebase-test-merge pipeline for parallel agent PRs.
5
+ *
6
+ * With strict branch protection (PR must be up-to-date with main), parallel
7
+ * agent PRs create a rebase treadmill. The merge queue processes green PRs
8
+ * one at a time: rebase on main → wait for CI → merge if green → eject if red.
9
+ *
10
+ * State machine per cycle:
11
+ * 1. If paused → skip
12
+ * 2. If a PR is currently being processed (rebased, waiting for CI):
13
+ * - CI green → merge, rebase siblings
14
+ * - CI failed → eject (label + comment)
15
+ * - CI pending → wait
16
+ * 3. If no current PR:
17
+ * - Build queue: open, non-draft PRs with green CI, ordered by priority then age
18
+ * - Pick first → rebase → set as current
19
+ * - If rebase fails (conflicts) → eject, try next
20
+ */
21
+ import type { PRInfo } from '../pr/index.js';
22
+ import { listOpenPRs, getPRChecks, getPRByNumber, mergePR } from '../pr/index.js';
23
+ import { rebaseSiblingPRs } from '../shipping/rebase.js';
24
+ export interface MergeQueueState {
25
+ /** Whether the merge queue is paused */
26
+ paused: boolean;
27
+ /** ISO timestamp when paused */
28
+ pausedAt: string | null;
29
+ /** PR currently being processed (rebased, waiting for CI) */
30
+ currentPR: CurrentPRState | null;
31
+ /** Last PR that was successfully merged */
32
+ lastMergedPR: MergedPRInfo | null;
33
+ /** ISO timestamp of last cycle */
34
+ lastCycleAt: string | null;
35
+ /** Recently ejected PRs (max 10) */
36
+ ejectedPRs: EjectedPRInfo[];
37
+ }
38
+ export interface CurrentPRState {
39
+ prNumber: number;
40
+ headBranch: string;
41
+ baseBranch: string;
42
+ ticketId: string | null;
43
+ priority: number;
44
+ rebasedAt: string;
45
+ }
46
+ export interface MergedPRInfo {
47
+ prNumber: number;
48
+ headBranch: string;
49
+ mergedAt: string;
50
+ }
51
+ export interface EjectedPRInfo {
52
+ prNumber: number;
53
+ headBranch: string;
54
+ reason: string;
55
+ ejectedAt: string;
56
+ }
57
+ export interface MergeQueueEntry {
58
+ pr: PRInfo;
59
+ ticketId: string | null;
60
+ priority: number;
61
+ }
62
+ export interface MergeQueueCycleResult {
63
+ action: 'none' | 'rebase_started' | 'waiting_ci' | 'merged' | 'ejected' | 'paused' | 'error';
64
+ prNumber?: number;
65
+ reason?: string;
66
+ queueSize: number;
67
+ }
68
+ export interface MergeQueueOptions {
69
+ /** HQ path for state file */
70
+ hqPath: string;
71
+ /** Working directory for git/gh operations */
72
+ cwd?: string;
73
+ /** Logging callback */
74
+ log?: (msg: string) => void;
75
+ /** Merge method (default: squash) */
76
+ mergeMethod?: 'merge' | 'squash' | 'rebase';
77
+ /** Delete branch after merging (default: true) */
78
+ deleteBranch?: boolean;
79
+ /** Map of PR head branch to priority (0=P0, 1=P1, 2=P2, 3=P3). Lower = higher priority */
80
+ branchPriorities?: Map<string, number>;
81
+ }
82
+ /** Injectable dependencies for testing */
83
+ export interface MergeQueueDeps {
84
+ listOpenPRs: typeof listOpenPRs;
85
+ getPRChecks: typeof getPRChecks;
86
+ getPRByNumber: typeof getPRByNumber;
87
+ mergePR: typeof mergePR;
88
+ rebaseSiblingPRs: typeof rebaseSiblingPRs;
89
+ updatePRBranch: (prNumber: number, cwd?: string) => import('../shipping/types.js').UpdateBranchResult;
90
+ addLabel: (prNumber: number, label: string, cwd?: string) => boolean;
91
+ addComment: (prNumber: number, body: string, cwd?: string) => boolean;
92
+ }
93
+ export declare function getMergeQueueStatePath(hqPath: string): string;
94
+ export declare function getDefaultState(): MergeQueueState;
95
+ export declare function getMergeQueueState(hqPath: string): MergeQueueState;
96
+ export declare function saveMergeQueueState(hqPath: string, state: MergeQueueState): void;
97
+ export declare function isMergeQueuePaused(hqPath: string): boolean;
98
+ export declare function pauseMergeQueue(hqPath: string): MergeQueueState;
99
+ export declare function resumeMergeQueue(hqPath: string): MergeQueueState;
100
+ /**
101
+ * Build the merge queue: open, non-draft PRs with all CI checks green,
102
+ * ordered by priority (P0 first) then by creation date (oldest first).
103
+ */
104
+ export declare function buildMergeQueue(options: {
105
+ cwd?: string;
106
+ branchPriorities?: Map<string, number>;
107
+ }, deps?: Pick<MergeQueueDeps, 'listOpenPRs' | 'getPRChecks'>): MergeQueueEntry[];
108
+ /**
109
+ * Run a single merge queue cycle.
110
+ *
111
+ * Called by the daemon on each interval. Handles the state machine:
112
+ * - If paused, returns immediately
113
+ * - If a PR is in progress (rebased, waiting CI), checks status
114
+ * - If no PR in progress, picks next from queue and rebases
115
+ */
116
+ export declare function runMergeQueueCycle(options: MergeQueueOptions, deps?: MergeQueueDeps): MergeQueueCycleResult;
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Merge Queue
3
+ *
4
+ * Sequential rebase-test-merge pipeline for parallel agent PRs.
5
+ *
6
+ * With strict branch protection (PR must be up-to-date with main), parallel
7
+ * agent PRs create a rebase treadmill. The merge queue processes green PRs
8
+ * one at a time: rebase on main → wait for CI → merge if green → eject if red.
9
+ *
10
+ * State machine per cycle:
11
+ * 1. If paused → skip
12
+ * 2. If a PR is currently being processed (rebased, waiting for CI):
13
+ * - CI green → merge, rebase siblings
14
+ * - CI failed → eject (label + comment)
15
+ * - CI pending → wait
16
+ * 3. If no current PR:
17
+ * - Build queue: open, non-draft PRs with green CI, ordered by priority then age
18
+ * - Pick first → rebase → set as current
19
+ * - If rebase fails (conflicts) → eject, try next
20
+ */
21
+ import * as fs from 'node:fs';
22
+ import * as path from 'node:path';
23
+ import { listOpenPRs, getPRChecks, getPRByNumber, mergePR, } from '../pr/index.js';
24
+ import { rebaseSiblingPRs } from '../shipping/rebase.js';
25
+ import { GitHubProvider } from '../shipping/github.js';
26
+ // =============================================================================
27
+ // Constants
28
+ // =============================================================================
29
+ const MERGE_QUEUE_STATE_FILE = 'merge-queue.json';
30
+ const MAX_EJECTED_HISTORY = 10;
31
+ const EJECTED_LABEL = 'merge-queue-ejected';
32
+ const EJECTED_COMMENT_PREFIX = 'Merge queue ejected this PR:';
33
+ const DEFAULT_PRIORITY = 3; // P3 — lowest
34
+ // =============================================================================
35
+ // State Management
36
+ // =============================================================================
37
+ export function getMergeQueueStatePath(hqPath) {
38
+ return path.join(hqPath, '.proletariat', MERGE_QUEUE_STATE_FILE);
39
+ }
40
+ export function getDefaultState() {
41
+ return {
42
+ paused: false,
43
+ pausedAt: null,
44
+ currentPR: null,
45
+ lastMergedPR: null,
46
+ lastCycleAt: null,
47
+ ejectedPRs: [],
48
+ };
49
+ }
50
+ export function getMergeQueueState(hqPath) {
51
+ const statePath = getMergeQueueStatePath(hqPath);
52
+ try {
53
+ const content = fs.readFileSync(statePath, 'utf-8');
54
+ return { ...getDefaultState(), ...JSON.parse(content) };
55
+ }
56
+ catch {
57
+ return getDefaultState();
58
+ }
59
+ }
60
+ export function saveMergeQueueState(hqPath, state) {
61
+ const statePath = getMergeQueueStatePath(hqPath);
62
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
63
+ }
64
+ // =============================================================================
65
+ // Pause / Resume
66
+ // =============================================================================
67
+ export function isMergeQueuePaused(hqPath) {
68
+ return getMergeQueueState(hqPath).paused;
69
+ }
70
+ export function pauseMergeQueue(hqPath) {
71
+ const state = getMergeQueueState(hqPath);
72
+ state.paused = true;
73
+ state.pausedAt = new Date().toISOString();
74
+ saveMergeQueueState(hqPath, state);
75
+ return state;
76
+ }
77
+ export function resumeMergeQueue(hqPath) {
78
+ const state = getMergeQueueState(hqPath);
79
+ state.paused = false;
80
+ state.pausedAt = null;
81
+ saveMergeQueueState(hqPath, state);
82
+ return state;
83
+ }
84
+ // =============================================================================
85
+ // Queue Building
86
+ // =============================================================================
87
+ /**
88
+ * Build the merge queue: open, non-draft PRs with all CI checks green,
89
+ * ordered by priority (P0 first) then by creation date (oldest first).
90
+ */
91
+ export function buildMergeQueue(options, deps = { listOpenPRs, getPRChecks }) {
92
+ const { cwd, branchPriorities } = options;
93
+ const openPRs = deps.listOpenPRs(cwd);
94
+ const entries = [];
95
+ for (const pr of openPRs) {
96
+ // Skip drafts
97
+ if (pr.isDraft)
98
+ continue;
99
+ // Check CI
100
+ const checks = deps.getPRChecks(pr.number, cwd);
101
+ if (checks.length === 0)
102
+ continue; // No checks = not ready
103
+ if (!allChecksGreen(checks))
104
+ continue;
105
+ // Resolve priority from branch
106
+ const priority = branchPriorities?.get(pr.headBranch) ?? DEFAULT_PRIORITY;
107
+ const ticketId = extractTicketIdFromBranch(pr.headBranch);
108
+ entries.push({ pr, ticketId, priority });
109
+ }
110
+ // Sort: lower priority number first (P0 < P1 < P2 < P3), then oldest first
111
+ entries.sort((a, b) => {
112
+ if (a.priority !== b.priority)
113
+ return a.priority - b.priority;
114
+ return new Date(a.pr.createdAt).getTime() - new Date(b.pr.createdAt).getTime();
115
+ });
116
+ return entries;
117
+ }
118
+ // =============================================================================
119
+ // Main Cycle
120
+ // =============================================================================
121
+ const _defaultProvider = new GitHubProvider();
122
+ const defaultDeps = {
123
+ listOpenPRs,
124
+ getPRChecks,
125
+ getPRByNumber,
126
+ mergePR,
127
+ rebaseSiblingPRs,
128
+ updatePRBranch: (prNumber, cwd) => _defaultProvider.updatePRBranch(prNumber, cwd),
129
+ addLabel: (prNumber, label, cwd) => _defaultProvider.addLabel(prNumber, label, cwd),
130
+ addComment: (prNumber, body, cwd) => _defaultProvider.addComment(prNumber, body, cwd),
131
+ };
132
+ /**
133
+ * Run a single merge queue cycle.
134
+ *
135
+ * Called by the daemon on each interval. Handles the state machine:
136
+ * - If paused, returns immediately
137
+ * - If a PR is in progress (rebased, waiting CI), checks status
138
+ * - If no PR in progress, picks next from queue and rebases
139
+ */
140
+ export function runMergeQueueCycle(options, deps = defaultDeps) {
141
+ const { hqPath, cwd, log, mergeMethod = 'squash', deleteBranch = true, branchPriorities, } = options;
142
+ const state = getMergeQueueState(hqPath);
143
+ state.lastCycleAt = new Date().toISOString();
144
+ // Step 1: Check if paused
145
+ if (state.paused) {
146
+ saveMergeQueueState(hqPath, state);
147
+ log?.('Merge queue is paused');
148
+ return { action: 'paused', queueSize: 0 };
149
+ }
150
+ // Step 2: If a PR is currently being processed, check its status
151
+ if (state.currentPR) {
152
+ const result = processCurrentPR(state, { cwd, log, mergeMethod, deleteBranch }, deps);
153
+ saveMergeQueueState(hqPath, state);
154
+ return result;
155
+ }
156
+ // Step 3: No current PR — build queue and pick next
157
+ const queue = buildMergeQueue({ cwd, branchPriorities }, deps);
158
+ log?.(`Merge queue: ${queue.length} eligible PR(s)`);
159
+ if (queue.length === 0) {
160
+ saveMergeQueueState(hqPath, state);
161
+ return { action: 'none', queueSize: 0 };
162
+ }
163
+ // Try to rebase the top PR — if conflicts, eject and try next
164
+ for (const entry of queue) {
165
+ const rebaseResult = deps.updatePRBranch(entry.pr.number, cwd);
166
+ if (rebaseResult.success) {
167
+ state.currentPR = {
168
+ prNumber: entry.pr.number,
169
+ headBranch: entry.pr.headBranch,
170
+ baseBranch: entry.pr.baseBranch,
171
+ ticketId: entry.ticketId,
172
+ priority: entry.priority,
173
+ rebasedAt: new Date().toISOString(),
174
+ };
175
+ log?.(`Rebased PR #${entry.pr.number} (${entry.pr.headBranch}) — waiting for CI`);
176
+ saveMergeQueueState(hqPath, state);
177
+ return { action: 'rebase_started', prNumber: entry.pr.number, queueSize: queue.length };
178
+ }
179
+ // Rebase failed — eject this PR
180
+ const reason = rebaseResult.hasConflicts
181
+ ? 'Merge conflicts during rebase'
182
+ : `Rebase failed: ${rebaseResult.error ?? 'unknown error'}`;
183
+ ejectPR(state, entry.pr.number, entry.pr.headBranch, reason, cwd, deps);
184
+ log?.(`Ejected PR #${entry.pr.number}: ${reason}`);
185
+ }
186
+ // All PRs in queue had conflicts
187
+ saveMergeQueueState(hqPath, state);
188
+ return { action: 'none', reason: 'All eligible PRs have conflicts', queueSize: queue.length };
189
+ }
190
+ // =============================================================================
191
+ // Process Current PR
192
+ // =============================================================================
193
+ function processCurrentPR(state, options, deps) {
194
+ const { cwd, log, mergeMethod, deleteBranch } = options;
195
+ const current = state.currentPR;
196
+ const queueSize = 0; // We don't rebuild the queue when checking current
197
+ // Verify PR is still open
198
+ const prInfo = deps.getPRByNumber(current.prNumber, cwd);
199
+ if (!prInfo || prInfo.state !== 'OPEN') {
200
+ const reason = prInfo?.state === 'MERGED'
201
+ ? 'PR was merged externally'
202
+ : 'PR is no longer open';
203
+ log?.(`Current PR #${current.prNumber}: ${reason} — clearing`);
204
+ state.currentPR = null;
205
+ if (prInfo?.state === 'MERGED') {
206
+ state.lastMergedPR = {
207
+ prNumber: current.prNumber,
208
+ headBranch: current.headBranch,
209
+ mergedAt: new Date().toISOString(),
210
+ };
211
+ }
212
+ return { action: 'none', prNumber: current.prNumber, reason, queueSize };
213
+ }
214
+ // Check CI status
215
+ const checks = deps.getPRChecks(current.prNumber, cwd);
216
+ // No checks yet — still waiting
217
+ if (checks.length === 0) {
218
+ log?.(`PR #${current.prNumber}: no CI checks yet — waiting`);
219
+ return { action: 'waiting_ci', prNumber: current.prNumber, queueSize };
220
+ }
221
+ const failed = checks.filter(c => c.conclusion === 'FAILURE');
222
+ const pending = checks.filter(c => !c.conclusion || c.status === 'IN_PROGRESS' || c.status === 'QUEUED');
223
+ // CI still pending
224
+ if (pending.length > 0) {
225
+ log?.(`PR #${current.prNumber}: CI pending (${pending.length} check(s)) — waiting`);
226
+ return { action: 'waiting_ci', prNumber: current.prNumber, queueSize };
227
+ }
228
+ // CI failed — eject
229
+ if (failed.length > 0) {
230
+ const failedNames = failed.map(c => c.name).join(', ');
231
+ const reason = `CI failed after rebase: ${failedNames}`;
232
+ ejectPR(state, current.prNumber, current.headBranch, reason, cwd, deps);
233
+ state.currentPR = null;
234
+ log?.(`Ejected PR #${current.prNumber}: ${reason}`);
235
+ return { action: 'ejected', prNumber: current.prNumber, reason, queueSize };
236
+ }
237
+ // All CI green — merge!
238
+ log?.(`PR #${current.prNumber}: CI green — merging (${mergeMethod})`);
239
+ const mergeResult = deps.mergePR(current.prNumber, {
240
+ method: mergeMethod,
241
+ deleteBranch,
242
+ cwd,
243
+ });
244
+ if (!mergeResult.success) {
245
+ // Check if it was merged by someone else
246
+ const recheck = deps.getPRByNumber(current.prNumber, cwd);
247
+ if (recheck?.state === 'MERGED') {
248
+ log?.(`PR #${current.prNumber}: was merged externally`);
249
+ state.currentPR = null;
250
+ state.lastMergedPR = {
251
+ prNumber: current.prNumber,
252
+ headBranch: current.headBranch,
253
+ mergedAt: new Date().toISOString(),
254
+ };
255
+ return { action: 'merged', prNumber: current.prNumber, reason: 'Merged externally', queueSize };
256
+ }
257
+ const reason = `Merge failed: ${mergeResult.error}`;
258
+ ejectPR(state, current.prNumber, current.headBranch, reason, cwd, deps);
259
+ state.currentPR = null;
260
+ log?.(`Ejected PR #${current.prNumber}: ${reason}`);
261
+ return { action: 'ejected', prNumber: current.prNumber, reason, queueSize };
262
+ }
263
+ // Merge succeeded!
264
+ log?.(`Merged PR #${current.prNumber} (${current.headBranch})`);
265
+ state.currentPR = null;
266
+ state.lastMergedPR = {
267
+ prNumber: current.prNumber,
268
+ headBranch: current.headBranch,
269
+ mergedAt: new Date().toISOString(),
270
+ };
271
+ // Rebase sibling PRs
272
+ log?.('Rebasing sibling PRs...');
273
+ const siblingResult = deps.rebaseSiblingPRs({
274
+ excludePRNumber: current.prNumber,
275
+ cwd,
276
+ onProgress: log,
277
+ labelConflicts: true,
278
+ commentOnConflicts: true,
279
+ });
280
+ log?.(`Siblings: ${siblingResult.succeeded.length} rebased, ${siblingResult.failed.length} failed, ${siblingResult.skipped.length} skipped`);
281
+ return { action: 'merged', prNumber: current.prNumber, queueSize };
282
+ }
283
+ // =============================================================================
284
+ // Eject PR
285
+ // =============================================================================
286
+ function ejectPR(state, prNumber, headBranch, reason, cwd, deps) {
287
+ // Record ejection
288
+ state.ejectedPRs.unshift({
289
+ prNumber,
290
+ headBranch,
291
+ reason,
292
+ ejectedAt: new Date().toISOString(),
293
+ });
294
+ // Keep only the last N ejected PRs
295
+ if (state.ejectedPRs.length > MAX_EJECTED_HISTORY) {
296
+ state.ejectedPRs = state.ejectedPRs.slice(0, MAX_EJECTED_HISTORY);
297
+ }
298
+ // Label and comment on the PR (best effort)
299
+ try {
300
+ deps.addLabel(prNumber, EJECTED_LABEL, cwd);
301
+ deps.addComment(prNumber, `${EJECTED_COMMENT_PREFIX} ${reason}`, cwd);
302
+ }
303
+ catch {
304
+ // Best effort — don't fail the cycle
305
+ }
306
+ }
307
+ // =============================================================================
308
+ // Helpers
309
+ // =============================================================================
310
+ function allChecksGreen(checks) {
311
+ return checks.every(c => c.conclusion === 'SUCCESS' || c.conclusion === 'NEUTRAL' || c.conclusion === 'SKIPPED');
312
+ }
313
+ /**
314
+ * Extract a ticket ID from a branch name.
315
+ * Supports patterns like: PRLT-1173/feat/... or TKT-007/...
316
+ */
317
+ function extractTicketIdFromBranch(branch) {
318
+ const match = branch.match(/^([A-Z]+-\d+)\//);
319
+ return match?.[1] ?? null;
320
+ }
321
+ //# sourceMappingURL=merge-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-queue.js","sourceRoot":"","sources":["../../../src/lib/sync/merge-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Board Sync Reconciler
3
+ *
4
+ * Core reconciliation logic that detects drift between GitHub PR state
5
+ * and ticket provider state, then produces corrective actions.
6
+ *
7
+ * Provider-agnostic: works with any ticket provider (Linear, PMO, etc.)
8
+ * and any git host that exposes PR info.
9
+ *
10
+ * Reconciliation rules:
11
+ * 1. Merged PR + ticket not Done → move to Done
12
+ * 2. Green CI + ticket In Progress → move to Review
13
+ * 3. Ticket In Progress + no active agent → flag as stale
14
+ * 4. Ticket In Review + PR closed (not merged) → move to Backlog
15
+ */
16
+ import type { PRInfo, PRCheck } from '../pr/index.js';
17
+ import type { Ticket } from '../pmo/types.js';
18
+ export type ReconcileActionType = 'move_to_done' | 'move_to_review' | 'flag_stale' | 'move_to_backlog';
19
+ export interface ReconcileAction {
20
+ type: ReconcileActionType;
21
+ ticketId: string;
22
+ ticketTitle: string;
23
+ reason: string;
24
+ /** Target status name to move to (for move actions) */
25
+ targetStatus?: string;
26
+ }
27
+ export interface ReconcileContext {
28
+ ticket: Ticket;
29
+ pr: PRInfo | null;
30
+ checks: PRCheck[];
31
+ hasActiveExecution: boolean;
32
+ }
33
+ export interface ReconcileResult {
34
+ actions: ReconcileAction[];
35
+ checked: number;
36
+ errors: Array<{
37
+ ticketId: string;
38
+ error: string;
39
+ }>;
40
+ }
41
+ /**
42
+ * Determine reconciliation actions for a single ticket.
43
+ */
44
+ export declare function reconcileTicket(ctx: ReconcileContext): ReconcileAction | null;