patchrelay 0.35.11 → 0.35.12

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 (50) hide show
  1. package/README.md +41 -9
  2. package/dist/build-info.json +3 -3
  3. package/dist/cli/args.js +0 -1
  4. package/dist/cli/commands/issues.js +2 -56
  5. package/dist/cli/commands/watch.js +5 -0
  6. package/dist/cli/data.js +110 -47
  7. package/dist/cli/formatters/text.js +6 -90
  8. package/dist/cli/help.js +3 -8
  9. package/dist/cli/index.js +0 -48
  10. package/dist/cli/operator-client.js +0 -82
  11. package/dist/cli/watch/App.js +1 -12
  12. package/dist/cli/watch/HelpBar.js +2 -2
  13. package/dist/cli/watch/IssueDetailView.js +57 -26
  14. package/dist/cli/watch/IssueRow.js +71 -27
  15. package/dist/cli/watch/StatusBar.js +7 -4
  16. package/dist/cli/watch/state-visualization.js +48 -23
  17. package/dist/cli/watch/timeline-builder.js +2 -1
  18. package/dist/cli/watch/use-detail-stream.js +10 -104
  19. package/dist/cli/watch/use-watch-stream.js +11 -102
  20. package/dist/cli/watch/watch-state.js +18 -50
  21. package/dist/codex-thread-utils.js +3 -0
  22. package/dist/db/migrations.js +239 -2
  23. package/dist/db.js +628 -39
  24. package/dist/github-app-token.js +7 -0
  25. package/dist/github-failure-context.js +44 -1
  26. package/dist/github-rollup.js +47 -0
  27. package/dist/github-webhook-handler.js +248 -51
  28. package/dist/github-webhooks.js +5 -0
  29. package/dist/http.js +12 -264
  30. package/dist/idle-reconciliation.js +268 -76
  31. package/dist/issue-query-service.js +221 -129
  32. package/dist/issue-session-events.js +151 -0
  33. package/dist/issue-session.js +99 -0
  34. package/dist/linear-client.js +39 -25
  35. package/dist/linear-session-reporting.js +12 -0
  36. package/dist/linear-session-sync.js +253 -24
  37. package/dist/linear-workflow.js +33 -0
  38. package/dist/merge-queue-protocol.js +0 -51
  39. package/dist/preflight.js +1 -4
  40. package/dist/queue-health-monitor.js +11 -7
  41. package/dist/run-orchestrator.js +1295 -146
  42. package/dist/run-reporting.js +5 -3
  43. package/dist/service.js +279 -102
  44. package/dist/status-note.js +56 -0
  45. package/dist/waiting-reason.js +65 -0
  46. package/dist/webhook-handler.js +270 -79
  47. package/package.json +1 -1
  48. package/dist/cli/commands/feed.js +0 -60
  49. package/dist/cli/watch/FeedView.js +0 -28
  50. package/dist/cli/watch/use-feed-stream.js +0 -92
@@ -22,6 +22,8 @@ CREATE TABLE IF NOT EXISTS issues (
22
22
  pr_number INTEGER,
23
23
  pr_url TEXT,
24
24
  pr_state TEXT,
25
+ pr_head_sha TEXT,
26
+ pr_author_login TEXT,
25
27
  pr_review_state TEXT,
26
28
  pr_check_status TEXT,
27
29
  ci_repair_attempts INTEGER NOT NULL DEFAULT 0,
@@ -48,6 +50,48 @@ CREATE TABLE IF NOT EXISTS runs (
48
50
  ended_at TEXT
49
51
  );
50
52
 
53
+ CREATE TABLE IF NOT EXISTS issue_sessions (
54
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
55
+ project_id TEXT NOT NULL,
56
+ linear_issue_id TEXT NOT NULL,
57
+ issue_key TEXT,
58
+ repo_id TEXT NOT NULL,
59
+ branch_name TEXT,
60
+ worktree_path TEXT,
61
+ pr_number INTEGER,
62
+ pr_head_sha TEXT,
63
+ pr_author_login TEXT,
64
+ session_state TEXT NOT NULL DEFAULT 'idle',
65
+ waiting_reason TEXT,
66
+ summary_text TEXT,
67
+ active_thread_id TEXT,
68
+ thread_generation INTEGER NOT NULL DEFAULT 0,
69
+ active_run_id INTEGER,
70
+ last_run_type TEXT,
71
+ last_wake_reason TEXT,
72
+ ci_repair_attempts INTEGER NOT NULL DEFAULT 0,
73
+ queue_repair_attempts INTEGER NOT NULL DEFAULT 0,
74
+ review_fix_attempts INTEGER NOT NULL DEFAULT 0,
75
+ lease_id TEXT,
76
+ worker_id TEXT,
77
+ leased_until TEXT,
78
+ created_at TEXT NOT NULL,
79
+ updated_at TEXT NOT NULL,
80
+ UNIQUE(project_id, linear_issue_id)
81
+ );
82
+
83
+ CREATE TABLE IF NOT EXISTS issue_session_events (
84
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
85
+ project_id TEXT NOT NULL,
86
+ linear_issue_id TEXT NOT NULL,
87
+ event_type TEXT NOT NULL,
88
+ event_json TEXT,
89
+ dedupe_key TEXT,
90
+ created_at TEXT NOT NULL,
91
+ processed_at TEXT,
92
+ consumed_by_run_id INTEGER
93
+ );
94
+
51
95
  CREATE TABLE IF NOT EXISTS webhook_events (
52
96
  id INTEGER PRIMARY KEY AUTOINCREMENT,
53
97
  webhook_id TEXT NOT NULL UNIQUE,
@@ -167,6 +211,11 @@ CREATE INDEX IF NOT EXISTS idx_issues_branch ON issues(branch_name);
167
211
  CREATE INDEX IF NOT EXISTS idx_runs_issue ON runs(issue_id);
168
212
  CREATE INDEX IF NOT EXISTS idx_runs_active ON runs(status, project_id, linear_issue_id);
169
213
  CREATE INDEX IF NOT EXISTS idx_runs_thread ON runs(thread_id);
214
+ CREATE INDEX IF NOT EXISTS idx_issue_sessions_issue ON issue_sessions(project_id, linear_issue_id);
215
+ CREATE INDEX IF NOT EXISTS idx_issue_sessions_key ON issue_sessions(issue_key);
216
+ CREATE INDEX IF NOT EXISTS idx_issue_sessions_lease ON issue_sessions(leased_until, session_state);
217
+ CREATE INDEX IF NOT EXISTS idx_issue_session_events_issue ON issue_session_events(project_id, linear_issue_id, id);
218
+ CREATE INDEX IF NOT EXISTS idx_issue_session_events_pending ON issue_session_events(processed_at, project_id, linear_issue_id, id);
170
219
  CREATE INDEX IF NOT EXISTS idx_run_thread_events_run ON run_thread_events(run_id, id);
171
220
  CREATE INDEX IF NOT EXISTS idx_operator_feed_events_issue ON operator_feed_events(issue_key, id);
172
221
  CREATE INDEX IF NOT EXISTS idx_operator_feed_events_project ON operator_feed_events(project_id, id);
@@ -185,6 +234,7 @@ export function runPatchRelayMigrations(connection) {
185
234
  // Explicit PR branch ownership hand-off between PatchRelay and MergeSteward
186
235
  addColumnIfMissing(connection, "issues", "branch_owner", "TEXT NOT NULL DEFAULT 'patchrelay'");
187
236
  addColumnIfMissing(connection, "issues", "branch_ownership_changed_at", "TEXT");
237
+ connection.prepare("UPDATE issues SET branch_owner = 'patchrelay' WHERE branch_owner IS NULL OR branch_owner != 'patchrelay'").run();
188
238
  // Add merge_prep_attempts for retry budget / escalation
189
239
  addColumnIfMissing(connection, "issues", "merge_prep_attempts", "INTEGER NOT NULL DEFAULT 0");
190
240
  // Add review_fix_attempts counter
@@ -195,6 +245,7 @@ export function runPatchRelayMigrations(connection) {
195
245
  addColumnIfMissing(connection, "issues", "description", "TEXT");
196
246
  addColumnIfMissing(connection, "issues", "priority", "INTEGER");
197
247
  addColumnIfMissing(connection, "issues", "estimate", "REAL");
248
+ addColumnIfMissing(connection, "issues", "status_comment_id", "TEXT");
198
249
  addColumnIfMissing(connection, "issues", "current_linear_state_type", "TEXT");
199
250
  addColumnIfMissing(connection, "issue_dependencies", "blocker_current_linear_state_type", "TEXT");
200
251
  // Zombie/stale recovery backoff
@@ -202,6 +253,8 @@ export function runPatchRelayMigrations(connection) {
202
253
  addColumnIfMissing(connection, "issues", "last_zombie_recovery_at", "TEXT");
203
254
  // Preserve GitHub failure provenance so reconciliation can distinguish
204
255
  // branch CI failures from merge-queue evictions after webhook delivery.
256
+ addColumnIfMissing(connection, "issues", "pr_head_sha", "TEXT");
257
+ addColumnIfMissing(connection, "issues", "pr_author_login", "TEXT");
205
258
  addColumnIfMissing(connection, "issues", "last_github_failure_source", "TEXT");
206
259
  addColumnIfMissing(connection, "issues", "last_github_failure_head_sha", "TEXT");
207
260
  addColumnIfMissing(connection, "issues", "last_github_failure_signature", "TEXT");
@@ -218,8 +271,7 @@ export function runPatchRelayMigrations(connection) {
218
271
  addColumnIfMissing(connection, "issues", "last_queue_incident_json", "TEXT");
219
272
  addColumnIfMissing(connection, "issues", "last_attempted_failure_head_sha", "TEXT");
220
273
  addColumnIfMissing(connection, "issues", "last_attempted_failure_signature", "TEXT");
221
- // Track whether the merge queue label was successfully applied.
222
- addColumnIfMissing(connection, "issues", "queue_label_applied", "INTEGER NOT NULL DEFAULT 0");
274
+ removeRetiredIssueColumnsIfPresent(connection);
223
275
  }
224
276
  function addColumnIfMissing(connection, table, column, definition) {
225
277
  const cols = connection.prepare(`PRAGMA table_info(${table})`).all();
@@ -227,3 +279,188 @@ function addColumnIfMissing(connection, table, column, definition) {
227
279
  return;
228
280
  connection.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
229
281
  }
282
+ function removeRetiredIssueColumnsIfPresent(connection) {
283
+ const cols = connection.prepare("PRAGMA table_info(issues)").all();
284
+ const columnNames = new Set(cols.map((column) => String(column.name)));
285
+ const retired = ["queue_label_applied", "pending_merge_prep", "merge_prep_attempts"];
286
+ if (!retired.some((name) => columnNames.has(name))) {
287
+ return;
288
+ }
289
+ connection.exec("PRAGMA foreign_keys = OFF");
290
+ try {
291
+ connection.exec(`
292
+ CREATE TABLE issues_new (
293
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
294
+ project_id TEXT NOT NULL,
295
+ linear_issue_id TEXT NOT NULL,
296
+ issue_key TEXT,
297
+ title TEXT,
298
+ description TEXT,
299
+ url TEXT,
300
+ priority INTEGER,
301
+ estimate REAL,
302
+ current_linear_state TEXT,
303
+ current_linear_state_type TEXT,
304
+ factory_state TEXT NOT NULL DEFAULT 'delegated',
305
+ pending_run_type TEXT,
306
+ pending_run_context_json TEXT,
307
+ branch_name TEXT,
308
+ branch_owner TEXT NOT NULL DEFAULT 'patchrelay',
309
+ branch_ownership_changed_at TEXT,
310
+ worktree_path TEXT,
311
+ thread_id TEXT,
312
+ active_run_id INTEGER,
313
+ status_comment_id TEXT,
314
+ agent_session_id TEXT,
315
+ pr_number INTEGER,
316
+ pr_url TEXT,
317
+ pr_state TEXT,
318
+ pr_head_sha TEXT,
319
+ pr_author_login TEXT,
320
+ pr_review_state TEXT,
321
+ pr_check_status TEXT,
322
+ last_github_failure_source TEXT,
323
+ last_github_failure_head_sha TEXT,
324
+ last_github_failure_signature TEXT,
325
+ last_github_failure_check_name TEXT,
326
+ last_github_failure_check_url TEXT,
327
+ last_github_failure_context_json TEXT,
328
+ last_github_failure_at TEXT,
329
+ last_github_ci_snapshot_head_sha TEXT,
330
+ last_github_ci_snapshot_gate_check_name TEXT,
331
+ last_github_ci_snapshot_gate_check_status TEXT,
332
+ last_github_ci_snapshot_json TEXT,
333
+ last_github_ci_snapshot_settled_at TEXT,
334
+ last_queue_signal_at TEXT,
335
+ last_queue_incident_json TEXT,
336
+ last_attempted_failure_head_sha TEXT,
337
+ last_attempted_failure_signature TEXT,
338
+ ci_repair_attempts INTEGER NOT NULL DEFAULT 0,
339
+ queue_repair_attempts INTEGER NOT NULL DEFAULT 0,
340
+ review_fix_attempts INTEGER NOT NULL DEFAULT 0,
341
+ zombie_recovery_attempts INTEGER NOT NULL DEFAULT 0,
342
+ last_zombie_recovery_at TEXT,
343
+ updated_at TEXT NOT NULL,
344
+ UNIQUE(project_id, linear_issue_id)
345
+ );
346
+
347
+ INSERT INTO issues_new (
348
+ id,
349
+ project_id,
350
+ linear_issue_id,
351
+ issue_key,
352
+ title,
353
+ description,
354
+ url,
355
+ priority,
356
+ estimate,
357
+ current_linear_state,
358
+ current_linear_state_type,
359
+ factory_state,
360
+ pending_run_type,
361
+ pending_run_context_json,
362
+ branch_name,
363
+ branch_owner,
364
+ branch_ownership_changed_at,
365
+ worktree_path,
366
+ thread_id,
367
+ active_run_id,
368
+ status_comment_id,
369
+ agent_session_id,
370
+ pr_number,
371
+ pr_url,
372
+ pr_state,
373
+ pr_head_sha,
374
+ pr_author_login,
375
+ pr_review_state,
376
+ pr_check_status,
377
+ last_github_failure_source,
378
+ last_github_failure_head_sha,
379
+ last_github_failure_signature,
380
+ last_github_failure_check_name,
381
+ last_github_failure_check_url,
382
+ last_github_failure_context_json,
383
+ last_github_failure_at,
384
+ last_github_ci_snapshot_head_sha,
385
+ last_github_ci_snapshot_gate_check_name,
386
+ last_github_ci_snapshot_gate_check_status,
387
+ last_github_ci_snapshot_json,
388
+ last_github_ci_snapshot_settled_at,
389
+ last_queue_signal_at,
390
+ last_queue_incident_json,
391
+ last_attempted_failure_head_sha,
392
+ last_attempted_failure_signature,
393
+ ci_repair_attempts,
394
+ queue_repair_attempts,
395
+ review_fix_attempts,
396
+ zombie_recovery_attempts,
397
+ last_zombie_recovery_at,
398
+ updated_at
399
+ )
400
+ SELECT
401
+ id,
402
+ project_id,
403
+ linear_issue_id,
404
+ issue_key,
405
+ title,
406
+ description,
407
+ url,
408
+ priority,
409
+ estimate,
410
+ current_linear_state,
411
+ current_linear_state_type,
412
+ COALESCE(factory_state, 'delegated'),
413
+ pending_run_type,
414
+ pending_run_context_json,
415
+ branch_name,
416
+ COALESCE(branch_owner, 'patchrelay'),
417
+ branch_ownership_changed_at,
418
+ worktree_path,
419
+ thread_id,
420
+ active_run_id,
421
+ status_comment_id,
422
+ agent_session_id,
423
+ pr_number,
424
+ pr_url,
425
+ pr_state,
426
+ pr_head_sha,
427
+ pr_author_login,
428
+ pr_review_state,
429
+ pr_check_status,
430
+ last_github_failure_source,
431
+ last_github_failure_head_sha,
432
+ last_github_failure_signature,
433
+ last_github_failure_check_name,
434
+ last_github_failure_check_url,
435
+ last_github_failure_context_json,
436
+ last_github_failure_at,
437
+ last_github_ci_snapshot_head_sha,
438
+ last_github_ci_snapshot_gate_check_name,
439
+ last_github_ci_snapshot_gate_check_status,
440
+ last_github_ci_snapshot_json,
441
+ last_github_ci_snapshot_settled_at,
442
+ last_queue_signal_at,
443
+ last_queue_incident_json,
444
+ last_attempted_failure_head_sha,
445
+ last_attempted_failure_signature,
446
+ COALESCE(ci_repair_attempts, 0),
447
+ COALESCE(queue_repair_attempts, 0),
448
+ COALESCE(review_fix_attempts, 0),
449
+ COALESCE(zombie_recovery_attempts, 0),
450
+ last_zombie_recovery_at,
451
+ updated_at
452
+ FROM issues;
453
+
454
+ DROP TABLE issues;
455
+ ALTER TABLE issues_new RENAME TO issues;
456
+
457
+ CREATE INDEX IF NOT EXISTS idx_issues_project ON issues(project_id, linear_issue_id);
458
+ CREATE INDEX IF NOT EXISTS idx_issues_key ON issues(issue_key);
459
+ CREATE INDEX IF NOT EXISTS idx_issues_ready ON issues(pending_run_type, active_run_id);
460
+ CREATE INDEX IF NOT EXISTS idx_issues_branch ON issues(branch_name);
461
+ `);
462
+ }
463
+ finally {
464
+ connection.exec("PRAGMA foreign_keys = ON");
465
+ }
466
+ }