@telora/daemon 0.21.134 → 0.21.144

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 (268) hide show
  1. package/build-info.json +2 -2
  2. package/dist/audit/audit-hooks.d.ts +15 -6
  3. package/dist/audit/audit-hooks.d.ts.map +1 -1
  4. package/dist/audit/audit-hooks.js +56 -28
  5. package/dist/audit/audit-hooks.js.map +1 -1
  6. package/dist/ci/ci-escalation.js +1 -1
  7. package/dist/ci/ci-escalation.js.map +1 -1
  8. package/dist/ci/ci-sensor-loop.d.ts +24 -0
  9. package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
  10. package/dist/ci/ci-sensor-loop.js +35 -9
  11. package/dist/ci/ci-sensor-loop.js.map +1 -1
  12. package/dist/cli/connect/config-write.d.ts +87 -0
  13. package/dist/cli/connect/config-write.d.ts.map +1 -0
  14. package/dist/cli/connect/config-write.js +255 -0
  15. package/dist/cli/connect/config-write.js.map +1 -0
  16. package/dist/cli/connect/registration.d.ts +76 -0
  17. package/dist/cli/connect/registration.d.ts.map +1 -0
  18. package/dist/cli/connect/registration.js +207 -0
  19. package/dist/cli/connect/registration.js.map +1 -0
  20. package/dist/cli/connect.d.ts +3 -141
  21. package/dist/cli/connect.d.ts.map +1 -1
  22. package/dist/cli/connect.js +9 -441
  23. package/dist/cli/connect.js.map +1 -1
  24. package/dist/completion/coverage-stabilized.d.ts +1 -1
  25. package/dist/completion/coverage-stabilized.d.ts.map +1 -1
  26. package/dist/completion/coverage-stabilized.js +1 -1
  27. package/dist/completion/coverage-stabilized.js.map +1 -1
  28. package/dist/completion/escalation-provenance.d.ts +2 -1
  29. package/dist/completion/escalation-provenance.d.ts.map +1 -1
  30. package/dist/completion/escalation-provenance.js +2 -1
  31. package/dist/completion/escalation-provenance.js.map +1 -1
  32. package/dist/completion/escalations.js +1 -1
  33. package/dist/completion/escalations.js.map +1 -1
  34. package/dist/completion/event-escalations.d.ts +1 -1
  35. package/dist/completion/event-escalations.d.ts.map +1 -1
  36. package/dist/completion/event-escalations.js +1 -1
  37. package/dist/completion/event-escalations.js.map +1 -1
  38. package/dist/completion/event.d.ts +1 -1
  39. package/dist/completion/event.d.ts.map +1 -1
  40. package/dist/completion/event.js +2 -2
  41. package/dist/completion/event.js.map +1 -1
  42. package/dist/completion/machine-remediable-demotion.d.ts +1 -1
  43. package/dist/completion/machine-remediable-demotion.d.ts.map +1 -1
  44. package/dist/completion/machine-remediable-demotion.js +2 -1
  45. package/dist/completion/machine-remediable-demotion.js.map +1 -1
  46. package/dist/completion/read-only-pristine.d.ts +1 -1
  47. package/dist/completion/read-only-pristine.d.ts.map +1 -1
  48. package/dist/completion/read-only-pristine.js +1 -1
  49. package/dist/completion/read-only-pristine.js.map +1 -1
  50. package/dist/completion/review-exit-escalations.d.ts +1 -1
  51. package/dist/completion/review-exit-escalations.d.ts.map +1 -1
  52. package/dist/completion/review-exit-phase.d.ts +5 -2
  53. package/dist/completion/review-exit-phase.d.ts.map +1 -1
  54. package/dist/completion/review-exit-phase.js +5 -2
  55. package/dist/completion/review-exit-phase.js.map +1 -1
  56. package/dist/completion/state-cascade.js +1 -1
  57. package/dist/completion/state-cascade.js.map +1 -1
  58. package/dist/completion/status-advance-phase.d.ts.map +1 -1
  59. package/dist/completion/status-advance-phase.js +2 -1
  60. package/dist/completion/status-advance-phase.js.map +1 -1
  61. package/dist/completion/team-lifecycle.d.ts.map +1 -1
  62. package/dist/completion/team-lifecycle.js +2 -1
  63. package/dist/completion/team-lifecycle.js.map +1 -1
  64. package/dist/delivery/delivery-advance-block.d.ts +1 -1
  65. package/dist/delivery/delivery-advance-block.d.ts.map +1 -1
  66. package/dist/delivery/delivery-advance-block.js +1 -1
  67. package/dist/delivery/delivery-advance-block.js.map +1 -1
  68. package/dist/delivery/delivery-lifecycle.js +1 -1
  69. package/dist/delivery/delivery-lifecycle.js.map +1 -1
  70. package/dist/delivery/delivery-merge-ops.js +1 -1
  71. package/dist/delivery/delivery-merge-ops.js.map +1 -1
  72. package/dist/delivery/delivery-reconciler-shadow.d.ts +1 -1
  73. package/dist/delivery/delivery-reconciler-shadow.d.ts.map +1 -1
  74. package/dist/delivery/delivery-reconciler-shadow.js +1 -1
  75. package/dist/delivery/delivery-reconciler-shadow.js.map +1 -1
  76. package/dist/delivery/operator-hold.d.ts +1 -1
  77. package/dist/delivery/operator-hold.d.ts.map +1 -1
  78. package/dist/delivery/paused-revival.d.ts.map +1 -1
  79. package/dist/delivery/paused-revival.js +2 -1
  80. package/dist/delivery/paused-revival.js.map +1 -1
  81. package/dist/directive/directive-dispatch.d.ts +1 -1
  82. package/dist/directive/directive-dispatch.d.ts.map +1 -1
  83. package/dist/directive/directive-dispatch.js +1 -1
  84. package/dist/directive/directive-dispatch.js.map +1 -1
  85. package/dist/directive/directive-executor.js +1 -1
  86. package/dist/directive/directive-executor.js.map +1 -1
  87. package/dist/drift/staleness-sensor.js +1 -1
  88. package/dist/drift/staleness-sensor.js.map +1 -1
  89. package/dist/focus/focus-engine.js +3 -2
  90. package/dist/focus/focus-engine.js.map +1 -1
  91. package/dist/focus/focus-lifecycle.d.ts.map +1 -1
  92. package/dist/focus/focus-lifecycle.js +2 -1
  93. package/dist/focus/focus-lifecycle.js.map +1 -1
  94. package/dist/focus/focus-respawn-guard.d.ts +1 -1
  95. package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
  96. package/dist/focus/focus-respawn-guard.js +1 -1
  97. package/dist/focus/focus-respawn-guard.js.map +1 -1
  98. package/dist/focus/listener-auto-advance.d.ts +4 -45
  99. package/dist/focus/listener-auto-advance.d.ts.map +1 -1
  100. package/dist/focus/listener-auto-advance.js +5 -238
  101. package/dist/focus/listener-auto-advance.js.map +1 -1
  102. package/dist/focus/listener-spawn.d.ts +62 -0
  103. package/dist/focus/listener-spawn.d.ts.map +1 -0
  104. package/dist/focus/listener-spawn.js +418 -0
  105. package/dist/focus/listener-spawn.js.map +1 -0
  106. package/dist/focus/listener-state.d.ts +57 -0
  107. package/dist/focus/listener-state.d.ts.map +1 -0
  108. package/dist/focus/listener-state.js +63 -0
  109. package/dist/focus/listener-state.js.map +1 -0
  110. package/dist/focus/listener.d.ts +0 -58
  111. package/dist/focus/listener.d.ts.map +1 -1
  112. package/dist/focus/listener.js +25 -453
  113. package/dist/focus/listener.js.map +1 -1
  114. package/dist/focus/verify-open-issues.d.ts +62 -0
  115. package/dist/focus/verify-open-issues.d.ts.map +1 -0
  116. package/dist/focus/verify-open-issues.js +265 -0
  117. package/dist/focus/verify-open-issues.js.map +1 -0
  118. package/dist/focus-stage-lifecycle/index.d.ts +1 -1
  119. package/dist/focus-stage-lifecycle/index.d.ts.map +1 -1
  120. package/dist/focus-stage-lifecycle/index.js +1 -1
  121. package/dist/focus-stage-lifecycle/index.js.map +1 -1
  122. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +1 -1
  123. package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
  124. package/dist/focus-stage-lifecycle/wedge-watchdog.js +1 -1
  125. package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
  126. package/dist/git/index.d.ts +2 -0
  127. package/dist/git/index.d.ts.map +1 -1
  128. package/dist/git/index.js +2 -0
  129. package/dist/git/index.js.map +1 -1
  130. package/dist/git/merge-strategies.d.ts +80 -0
  131. package/dist/git/merge-strategies.d.ts.map +1 -0
  132. package/dist/git/merge-strategies.js +218 -0
  133. package/dist/git/merge-strategies.js.map +1 -0
  134. package/dist/git/merge-to-main.d.ts +76 -0
  135. package/dist/git/merge-to-main.d.ts.map +1 -0
  136. package/dist/git/merge-to-main.js +256 -0
  137. package/dist/git/merge-to-main.js.map +1 -0
  138. package/dist/git/merge.d.ts +27 -81
  139. package/dist/git/merge.d.ts.map +1 -1
  140. package/dist/git/merge.js +4 -448
  141. package/dist/git/merge.js.map +1 -1
  142. package/dist/git/state-detector.js +1 -1
  143. package/dist/git/state-detector.js.map +1 -1
  144. package/dist/loops/engine.js +1 -1
  145. package/dist/loops/engine.js.map +1 -1
  146. package/dist/merge/focus-merge.d.ts +1 -1
  147. package/dist/merge/focus-merge.d.ts.map +1 -1
  148. package/dist/merge/focus-merge.js +1 -1
  149. package/dist/merge/focus-merge.js.map +1 -1
  150. package/dist/merge/merge-back-loop.d.ts +1 -1
  151. package/dist/merge/merge-back-loop.d.ts.map +1 -1
  152. package/dist/merge/merge-back-loop.js +2 -2
  153. package/dist/merge/merge-back-loop.js.map +1 -1
  154. package/dist/merge/merge-detector.js +1 -1
  155. package/dist/merge/merge-detector.js.map +1 -1
  156. package/dist/nexus/return.js +1 -1
  157. package/dist/nexus/return.js.map +1 -1
  158. package/dist/prd/controller-shared.d.ts +223 -0
  159. package/dist/prd/controller-shared.d.ts.map +1 -0
  160. package/dist/prd/controller-shared.js +77 -0
  161. package/dist/prd/controller-shared.js.map +1 -0
  162. package/dist/prd/controller.d.ts +5 -256
  163. package/dist/prd/controller.d.ts.map +1 -1
  164. package/dist/prd/controller.js +9 -166
  165. package/dist/prd/controller.js.map +1 -1
  166. package/dist/prd/done-sweep.d.ts +55 -0
  167. package/dist/prd/done-sweep.d.ts.map +1 -0
  168. package/dist/prd/done-sweep.js +112 -0
  169. package/dist/prd/done-sweep.js.map +1 -0
  170. package/dist/prompt-sections/execution-sections.d.ts +1 -1
  171. package/dist/prompt-sections/execution-sections.d.ts.map +1 -1
  172. package/dist/prompt-sections/execution-sections.js +12 -2
  173. package/dist/prompt-sections/execution-sections.js.map +1 -1
  174. package/dist/prompt-sections/shared.d.ts +1 -1
  175. package/dist/prompt-sections/shared.d.ts.map +1 -1
  176. package/dist/queries/agent-resolvable-promotion.d.ts +1 -1
  177. package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
  178. package/dist/queries/agent-resolvable-promotion.js +2 -1
  179. package/dist/queries/agent-resolvable-promotion.js.map +1 -1
  180. package/dist/queries/deliveries.d.ts +0 -195
  181. package/dist/queries/deliveries.d.ts.map +1 -1
  182. package/dist/queries/deliveries.js +1 -83
  183. package/dist/queries/deliveries.js.map +1 -1
  184. package/dist/queries/escalation-consumption.d.ts +120 -0
  185. package/dist/queries/escalation-consumption.d.ts.map +1 -0
  186. package/dist/queries/escalation-consumption.js +143 -0
  187. package/dist/queries/escalation-consumption.js.map +1 -0
  188. package/dist/queries/escalation-dispositions.d.ts +19 -0
  189. package/dist/queries/escalation-dispositions.d.ts.map +1 -0
  190. package/dist/queries/escalation-dispositions.js +18 -0
  191. package/dist/queries/escalation-dispositions.js.map +1 -0
  192. package/dist/queries/escalation-suppression.d.ts +1 -1
  193. package/dist/queries/escalation-suppression.d.ts.map +1 -1
  194. package/dist/queries/escalation-suppression.js +1 -1
  195. package/dist/queries/escalation-suppression.js.map +1 -1
  196. package/dist/queries/escalations.d.ts +343 -0
  197. package/dist/queries/escalations.d.ts.map +1 -0
  198. package/dist/queries/escalations.js +493 -0
  199. package/dist/queries/escalations.js.map +1 -0
  200. package/dist/queries/focus-discovery.d.ts +204 -0
  201. package/dist/queries/focus-discovery.d.ts.map +1 -0
  202. package/dist/queries/focus-discovery.js +92 -0
  203. package/dist/queries/focus-discovery.js.map +1 -0
  204. package/dist/queries/index.d.ts +4 -2
  205. package/dist/queries/index.d.ts.map +1 -1
  206. package/dist/queries/index.js +4 -2
  207. package/dist/queries/index.js.map +1 -1
  208. package/dist/queries/issues.d.ts +8 -436
  209. package/dist/queries/issues.d.ts.map +1 -1
  210. package/dist/queries/issues.js +7 -615
  211. package/dist/queries/issues.js.map +1 -1
  212. package/dist/resolvers/agent-escalations.d.ts +20 -0
  213. package/dist/resolvers/agent-escalations.d.ts.map +1 -1
  214. package/dist/resolvers/agent-escalations.js +40 -10
  215. package/dist/resolvers/agent-escalations.js.map +1 -1
  216. package/dist/resolvers/index.d.ts +2 -2
  217. package/dist/resolvers/index.d.ts.map +1 -1
  218. package/dist/resolvers/index.js +1 -1
  219. package/dist/resolvers/index.js.map +1 -1
  220. package/dist/resolvers/shared/git-diff.d.ts +99 -18
  221. package/dist/resolvers/shared/git-diff.d.ts.map +1 -1
  222. package/dist/resolvers/shared/git-diff.js +98 -27
  223. package/dist/resolvers/shared/git-diff.js.map +1 -1
  224. package/dist/shell/control-state.d.ts +1 -1
  225. package/dist/shell/control-state.d.ts.map +1 -1
  226. package/dist/shell/control-state.js +1 -1
  227. package/dist/shell/control-state.js.map +1 -1
  228. package/dist/shell/crash-recovery.d.ts +1 -1
  229. package/dist/shell/crash-recovery.d.ts.map +1 -1
  230. package/dist/shell/crash-recovery.js +2 -2
  231. package/dist/shell/crash-recovery.js.map +1 -1
  232. package/dist/shell/install-probe-tick.d.ts +1 -1
  233. package/dist/shell/install-probe-tick.d.ts.map +1 -1
  234. package/dist/shell/install-probe-tick.js +1 -1
  235. package/dist/shell/install-probe-tick.js.map +1 -1
  236. package/dist/shell/unified-shell.js +1 -1
  237. package/dist/shell/unified-shell.js.map +1 -1
  238. package/dist/spawn/model-routing.d.ts +1 -1
  239. package/dist/spawn/model-routing.d.ts.map +1 -1
  240. package/dist/spawn/model-routing.js +1 -1
  241. package/dist/spawn/model-routing.js.map +1 -1
  242. package/dist/spawn/prompt-assembly.d.ts +1 -1
  243. package/dist/spawn/prompt-assembly.d.ts.map +1 -1
  244. package/dist/spawn/prompt-assembly.js +1 -1
  245. package/dist/spawn/prompt-assembly.js.map +1 -1
  246. package/dist/spawn/spawn-decision.js +1 -1
  247. package/dist/spawn/spawn-decision.js.map +1 -1
  248. package/dist/spawn/worktree-session.d.ts +1 -1
  249. package/dist/spawn/worktree-session.d.ts.map +1 -1
  250. package/dist/spawn/worktree-session.js +1 -1
  251. package/dist/spawn/worktree-session.js.map +1 -1
  252. package/dist/templates/claude-md.d.ts +1 -1
  253. package/dist/templates/claude-md.d.ts.map +1 -1
  254. package/dist/templates/claude-md.js +12 -0
  255. package/dist/templates/claude-md.js.map +1 -1
  256. package/dist/verify/verification-default-on.d.ts +1 -1
  257. package/dist/verify/verification-default-on.d.ts.map +1 -1
  258. package/dist/verify/verification-deps.d.ts +1 -1
  259. package/dist/verify/verification-deps.d.ts.map +1 -1
  260. package/dist/verify/verification-engine.d.ts.map +1 -1
  261. package/dist/verify/verification-engine.js +20 -12
  262. package/dist/verify/verification-engine.js.map +1 -1
  263. package/dist/worktree/provision.js +1 -1
  264. package/dist/worktree/provision.js.map +1 -1
  265. package/dist/worktree/scratch-guard.d.ts.map +1 -1
  266. package/dist/worktree/scratch-guard.js +23 -2
  267. package/dist/worktree/scratch-guard.js.map +1 -1
  268. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-to-main.js","sourceRoot":"","sources":["../../src/git/merge-to-main.ts"],"names":[],"mappings":"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"}
@@ -5,9 +5,8 @@
5
5
  * push failure handling with rollback, and stash management.
6
6
  */
7
7
  import type { DaemonConfig, MergeBackResult } from '../types/index.js';
8
- import { type MergeResult, type MergeStrategy } from './types.js';
8
+ import { type MergeResult } from './types.js';
9
9
  import { type CorruptionDetails } from './merge-lock.js';
10
- import { type CiStatusReader, type CiMergeGateDecision } from '../ci/ci-status.js';
11
10
  /**
12
11
  * Classify the post-merge sha observation for a merge that already succeeded.
13
12
  * Given whether a commit sha was already captured (e.g. from a --no-ff merge),
@@ -32,6 +31,32 @@ export declare function classifyMergeShaObservation(alreadyHasCommitSha: boolean
32
31
  commitSha?: string;
33
32
  shaUnobserved?: boolean;
34
33
  };
34
+ /**
35
+ * Returns true if the repo at repoPath has an 'origin' remote configured.
36
+ * Used to gate all network operations so the daemon works in remote-less repos.
37
+ */
38
+ export declare function hasOriginRemote(repoPath: string): boolean;
39
+ /**
40
+ * Parse `git worktree list --porcelain` to find worktrees -- other than
41
+ * `excludePath` -- that currently have `branch` ATTACHED (a non-detached
42
+ * checkout). Returns absolute worktree paths.
43
+ *
44
+ * Used by the integration->main merge to locate any worktree that holds
45
+ * `main` (typically the daemon's primary repoPath, where a developer keeps
46
+ * main checked out) so it can be re-synced after the ref is advanced
47
+ * out-of-band.
48
+ */
49
+ export declare function worktreesWithBranchCheckedOut(repoPath: string, branch: string, excludePath: string): string[];
50
+ /**
51
+ * True when a worktree has uncommitted TRACKED changes (staged or unstaged
52
+ * modifications/additions/deletions). Untracked files (porcelain "?? ") do NOT
53
+ * count: `git reset --hard` preserves untracked files, so a worktree whose only
54
+ * dirt is untracked scratch is still safe to re-sync after an out-of-band ref
55
+ * advance -- and MUST be, because a dev box almost always carries stray
56
+ * untracked files, and skipping the re-sync leaves main advanced over a stale
57
+ * working tree (the phantom diff that confuses downstream tooling like /sync).
58
+ */
59
+ export declare function hasTrackedChanges(worktreePath: string): boolean;
35
60
  interface PushFailureRollbackParams {
36
61
  repoPath: string;
37
62
  branch: string;
@@ -77,22 +102,6 @@ export declare function handlePushFailureWithRollback(params: PushFailureRollbac
77
102
  * became a pathspec).
78
103
  */
79
104
  export declare function handleUntrackedFilesBlockingMerge(repoPath: string, errorOutput: string): boolean;
80
- /**
81
- * Attempt a squash merge: stages all changes from the source branch as a
82
- * single commit on the current branch.
83
- *
84
- * 1. `git merge --squash <sourceBranch>` -- stages all diffs without committing
85
- * 2. `git commit -m <commitMessage>` -- creates a single squashed commit
86
- * 3. Captures the resulting commit SHA via `git rev-parse HEAD`
87
- *
88
- * On conflict, aborts the merge and returns conflicted file details.
89
- */
90
- export declare function trySquashMerge(repoPath: string, sourceBranch: string, commitMessage: string): MergeResult;
91
- /**
92
- * Dispatch to the appropriate merge strategy helper.
93
- * For 'merge-commit', tries fast-forward first, then falls back to --no-ff.
94
- */
95
- export declare function applyMergeStrategy(repoPath: string, sourceBranch: string, targetBranch: string, strategy: MergeStrategy): MergeResult;
96
105
  export interface MergeToIntegrationParams {
97
106
  config: DaemonConfig;
98
107
  branchName: string;
@@ -114,69 +123,6 @@ export interface MergeToIntegrationParams {
114
123
  * Serialized via the merge lock to prevent concurrent checkout races.
115
124
  */
116
125
  export declare function mergeToIntegration(params: MergeToIntegrationParams): Promise<MergeResult>;
117
- export interface MergeIntegrationToMainParams {
118
- config: DaemonConfig;
119
- /** Focus ID for log attribution / push-failure escalations. */
120
- focusId: string;
121
- /** Focus name for logging/commit messages. */
122
- focusName: string;
123
- /** Whether to also push main to remote (CD). */
124
- pushToRemote: boolean;
125
- /**
126
- * Merge strategy honored when integrating into main. Defaults to
127
- * `merge-commit` (which itself attempts fast-forward first, then falls
128
- * back to `--no-ff`) so legacy callers that omit the field get the
129
- * pre-existing behavior.
130
- */
131
- mergeStrategy?: MergeStrategy;
132
- /**
133
- * The green-wait CI status reader, evaluated at THIS write point -- the single
134
- * place `refs/heads/main` is advanced -- so the gate governs every daemon route
135
- * to main identically, not whichever call path performed the merge. REQUIRED so
136
- * no caller can advance main without making the CI decision explicit:
137
- * - a reader (`resolveCiStatusReader()`) => the merged SHA's status is read
138
- * and `resolveCiMergeGate` decides; pending/red/unknown HOLD (fail-closed),
139
- * green proceeds. On a hold the ref is NOT advanced and the result carries
140
- * `ciHeld` (not an error -- nothing was written, nothing to roll back).
141
- * - `undefined` => an explicit, visible opt-out (used by tests and legacy
142
- * callers with no CI source); the gate is skipped and the merge proceeds
143
- * exactly as before.
144
- */
145
- getCiStatus: CiStatusReader | undefined;
146
- }
147
- /**
148
- * Resolved remote name + URL describing the push target for a CD push.
149
- * Exported only so the focus-completion / focus-team-lifecycle log lines
150
- * can surface the remote without re-querying git.
151
- */
152
- export interface PushedRemoteInfo {
153
- /** Remote name (always `origin` today). */
154
- name: string;
155
- /** Remote URL as configured in the repo. */
156
- url: string;
157
- }
158
- /**
159
- * Merge the integration branch into the default (main) branch.
160
- *
161
- * Called when a focus completes with CI enabled. Optionally pushes
162
- * to remote when CD is also enabled. The push step is gated by
163
- * `pushToRemote` and only runs when an `origin` remote is configured.
164
- *
165
- * Uses the merge worktree and merge lock (same as mergeToIntegration)
166
- * to avoid concurrent merge conflicts. The merge worktree checks main out
167
- * DETACHED and advances refs/heads/main via `git update-ref`, so the merge
168
- * completes even when main is attached in the daemon's primary worktree
169
- * (where `git checkout main` would fail with "'main' is already used by
170
- * worktree"). Clean worktrees holding main are re-synced afterward.
171
- *
172
- * The result's `pushedTo` field is set only when CD push happened and
173
- * succeeded. Callers can read it to emit a focus-attributed log line
174
- * with the resolved remote name + URL.
175
- */
176
- export declare function mergeIntegrationToMain(params: MergeIntegrationToMainParams): Promise<MergeResult & {
177
- pushedTo?: PushedRemoteInfo;
178
- ciHeld?: CiMergeGateDecision;
179
- }>;
180
126
  export interface MergeBackFromIntegrationParams {
181
127
  /** Path to the focus/delivery worktree. */
182
128
  worktreePath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/git/merge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/git/merge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAc,KAAK,WAAW,EAAwB,MAAM,YAAY,CAAC;AAChF,OAAO,EAAyC,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAahG;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,mBAAmB,EAAE,OAAO,EAC5B,SAAS,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC/C,WAAW,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAOjD;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,EAAE,CAoBV;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAI/D;AAYD,UAAU,yBAAyB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC1D;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,yBAAyB,GAAG,WAAW,CAoD5F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAmCT;AAKD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,oBAAoB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC9G;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,WAAW,CAAC,CAyKtB;AAKD,MAAM,WAAW,8BAA8B;IAC7C,2CAA2C;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,eAAe,CAAC,CAgE1B"}
package/dist/git/merge.js CHANGED
@@ -8,12 +8,9 @@ import { resolve } from 'node:path';
8
8
  import { existsSync } from 'node:fs';
9
9
  import { runGitSync, getMergeWorktreePath } from './types.js';
10
10
  import { withMergeLock, markIntegrationCorrupt } from './merge-lock.js';
11
- import { getDefaultBranch } from './branch.js';
12
- import { resolveRef } from './utils.js';
13
11
  import { withRetry, healthEvents } from '@telora/daemon-core';
14
- import { resolveCiMergeGate, classifyCiMergeWait, } from '../ci/ci-status.js';
15
- import { describeWaitReachability } from '../delivery/wait-reachability.js';
16
12
  import { createLogger } from '../shell/log.js';
13
+ import { extractConflictedFiles, recoverDirtyMergeWorktree, tryFastForwardMerge, tryMergeCommit, } from './merge-strategies.js';
17
14
  const log = createLogger({ module: 'CI' });
18
15
  /**
19
16
  * Classify the post-merge sha observation for a merge that already succeeded.
@@ -43,7 +40,7 @@ export function classifyMergeShaObservation(alreadyHasCommitSha, shaResult, preM
43
40
  * Returns true if the repo at repoPath has an 'origin' remote configured.
44
41
  * Used to gate all network operations so the daemon works in remote-less repos.
45
42
  */
46
- function hasOriginRemote(repoPath) {
43
+ export function hasOriginRemote(repoPath) {
47
44
  return runGitSync(['remote', 'get-url', 'origin'], repoPath, { timeoutMs: 3_000 }).success;
48
45
  }
49
46
  /**
@@ -56,7 +53,7 @@ function hasOriginRemote(repoPath) {
56
53
  * main checked out) so it can be re-synced after the ref is advanced
57
54
  * out-of-band.
58
55
  */
59
- function worktreesWithBranchCheckedOut(repoPath, branch, excludePath) {
56
+ export function worktreesWithBranchCheckedOut(repoPath, branch, excludePath) {
60
57
  const list = runGitSync(['worktree', 'list', '--porcelain'], repoPath);
61
58
  if (!list.success)
62
59
  return [];
@@ -89,23 +86,12 @@ function worktreesWithBranchCheckedOut(repoPath, branch, excludePath) {
89
86
  * untracked files, and skipping the re-sync leaves main advanced over a stale
90
87
  * working tree (the phantom diff that confuses downstream tooling like /sync).
91
88
  */
92
- function hasTrackedChanges(worktreePath) {
89
+ export function hasTrackedChanges(worktreePath) {
93
90
  const st = runGitSync(['status', '--porcelain'], worktreePath);
94
91
  if (!st.success)
95
92
  return true; // can't determine -> treat as unsafe to reset
96
93
  return st.output.split('\n').some(line => line.trim() !== '' && !line.startsWith('??'));
97
94
  }
98
- /**
99
- * Extract the list of conflicted files from git status.
100
- * Returns an empty array if the command fails or there are no conflicts.
101
- */
102
- function extractConflictedFiles(repoPath) {
103
- const statusResult = runGitSync(['diff', '--name-only', '--diff-filter=U'], repoPath);
104
- if (!statusResult.success) {
105
- return [];
106
- }
107
- return statusResult.output.split('\n').filter(Boolean);
108
- }
109
95
  /**
110
96
  * Hard-reset the current branch to a given ref.
111
97
  * Returns whether the rollback succeeded.
@@ -220,200 +206,6 @@ export function handleUntrackedFilesBlockingMerge(repoPath, errorOutput) {
220
206
  }
221
207
  return true;
222
208
  }
223
- /**
224
- * Recover a merge worktree left dirty by an EARLIER failed merge, BEFORE the next
225
- * merge runs, so an already-wedged worktree heals itself with no operator. The
226
- * residue is detected by a non-empty `git status --porcelain` (staged / unstaged
227
- * / untracked) -- deliberately NOT by MERGE_HEAD: the 2026-08-18 wedge held 85
228
- * staged files with MERGE_HEAD already cleared and AUTO_MERGE present, so a
229
- * MERGE_HEAD probe would miss it. Any residue is discarded via
230
- * restoreWorktreeAfterFailedMerge, which moves no branch ref -- focus-branch
231
- * commits are untouched. Merge-worktree only; idempotent (a clean worktree is a
232
- * no-op).
233
- *
234
- * CONFIRMS the outcome rather than recovering-and-hoping: it re-reads
235
- * `git status --porcelain` AFTER any recovery and reports whether the worktree
236
- * is now clean. `clean` is true only when the worktree is empty at exit -- it
237
- * was already clean, or the recovery restored it. It is false when residue
238
- * survived the recovery attempt, or when status itself could not be read (so
239
- * cleanliness cannot be vouched for). A merge must REFUSE to run when this
240
- * returns clean:false: running against an unconfirmed worktree is exactly the
241
- * wedge this guard exists to prevent.
242
- */
243
- function recoverDirtyMergeWorktree(repoPath) {
244
- const status = runGitSync(['status', '--porcelain'], repoPath);
245
- if (!status.success) {
246
- // Can't read status -> can't confirm clean. Treat as unsafe to merge.
247
- return { clean: false, residue: `git status failed: ${status.error}` };
248
- }
249
- if (status.output.trim().length === 0) {
250
- return { clean: true };
251
- }
252
- log.warn(`[merge] Merge worktree was left dirty by an earlier failed merge ` +
253
- `(git status --porcelain non-empty); recovering it before the next merge ` +
254
- `so an already-wedged worktree does not require an operator.`);
255
- restoreWorktreeAfterFailedMerge(repoPath);
256
- // Confirm the recovery actually cleaned the worktree before we let a merge run.
257
- const after = runGitSync(['status', '--porcelain'], repoPath);
258
- if (after.success && after.output.trim().length === 0) {
259
- return { clean: true };
260
- }
261
- return {
262
- clean: false,
263
- residue: after.success ? after.output.trim() : `git status failed: ${after.error}`,
264
- };
265
- }
266
- /**
267
- * Attempt a fast-forward-only merge.
268
- * Returns a MergeResult; on failure, no abort is needed (ff-only is atomic).
269
- */
270
- function tryFastForwardMerge(repoPath, sourceBranch) {
271
- const ffResult = runGitSync(['merge', '--ff-only', sourceBranch], repoPath);
272
- if (ffResult.success) {
273
- return { success: true };
274
- }
275
- return {
276
- success: false,
277
- error: `Fast-forward merge not possible: ${ffResult.error}`,
278
- };
279
- }
280
- /**
281
- * Attempt a rebase merge.
282
- * On conflict, aborts the rebase and returns conflicted file details.
283
- */
284
- function tryRebaseMerge(repoPath, sourceBranch) {
285
- const rebaseResult = runGitSync(['rebase', sourceBranch], repoPath);
286
- if (rebaseResult.success) {
287
- return { success: true };
288
- }
289
- const conflictedFiles = extractConflictedFiles(repoPath);
290
- runGitSync(['rebase', '--abort'], repoPath);
291
- return {
292
- success: false,
293
- error: `Rebase failed: ${conflictedFiles.length} file(s) conflicted`,
294
- conflictedFiles,
295
- };
296
- }
297
- /**
298
- * Restore the merge worktree to the clean commit + index it had before a failed
299
- * merge attempt. The conflict branches abort explicitly; this covers EVERY OTHER
300
- * failure -- a commit-msg hook rejection (leaves MERGE_HEAD set + staged), a
301
- * squash that staged changes with no MERGE_HEAD, or an unrecognised error --
302
- * which git otherwise leaves as a staged, uncommitted merge that NOTHING clears.
303
- * Because the merge worktree is shared by every focus in the product, that
304
- * residue would fail the next focus's merge too; clearing it here contains one
305
- * focus's failure to that focus. It discards ONLY the failed-merge residue --
306
- * `git merge --abort` (or, when no merge is in progress, `reset --hard HEAD`)
307
- * moves no branch ref, so commits on any focus branch are untouched. Only ever
308
- * called against the merge worktree (never a developer checkout).
309
- */
310
- function restoreWorktreeAfterFailedMerge(repoPath) {
311
- // MERGE_HEAD present (a --no-ff merge that stopped, e.g. hook-rejected) aborts
312
- // cleanly. --squash stages changes WITHOUT a MERGE_HEAD, so abort is a no-op
313
- // there; fall back to a hard reset + untracked clean to drop the staged residue.
314
- const abort = runGitSync(['merge', '--abort'], repoPath);
315
- if (!abort.success) {
316
- runGitSync(['reset', '--hard', 'HEAD'], repoPath);
317
- runGitSync(['clean', '-fd'], repoPath);
318
- }
319
- }
320
- /**
321
- * Attempt a merge-commit merge (--no-ff).
322
- * On conflict, aborts the merge and returns conflicted file details.
323
- */
324
- function tryMergeCommit(repoPath, sourceBranch, commitMessage) {
325
- const mergeResult = runGitSync(['merge', sourceBranch, '--no-ff', '-m', commitMessage], repoPath);
326
- if (mergeResult.success) {
327
- return { success: true };
328
- }
329
- const isConflict = mergeResult.error.includes('CONFLICT') ||
330
- mergeResult.error.includes('Automatic merge failed') ||
331
- mergeResult.output.includes('CONFLICT');
332
- if (isConflict) {
333
- const conflictedFiles = extractConflictedFiles(repoPath);
334
- runGitSync(['merge', '--abort'], repoPath);
335
- return {
336
- success: false,
337
- error: `Merge conflict: ${conflictedFiles.length} file(s) conflicted`,
338
- conflictedFiles,
339
- };
340
- }
341
- // Non-conflict failure (hook rejection / unrecognised error) still leaves a
342
- // staged, uncommitted merge behind -- restore the worktree so it does not
343
- // poison the next focus's merge.
344
- restoreWorktreeAfterFailedMerge(repoPath);
345
- return {
346
- success: false,
347
- error: `Merge failed: ${mergeResult.error}`,
348
- };
349
- }
350
- /**
351
- * Attempt a squash merge: stages all changes from the source branch as a
352
- * single commit on the current branch.
353
- *
354
- * 1. `git merge --squash <sourceBranch>` -- stages all diffs without committing
355
- * 2. `git commit -m <commitMessage>` -- creates a single squashed commit
356
- * 3. Captures the resulting commit SHA via `git rev-parse HEAD`
357
- *
358
- * On conflict, aborts the merge and returns conflicted file details.
359
- */
360
- export function trySquashMerge(repoPath, sourceBranch, commitMessage) {
361
- const squashResult = runGitSync(['merge', '--squash', sourceBranch], repoPath);
362
- if (!squashResult.success) {
363
- const isConflict = squashResult.error.includes('CONFLICT') ||
364
- squashResult.error.includes('Automatic merge failed') ||
365
- squashResult.output.includes('CONFLICT');
366
- if (isConflict) {
367
- const conflictedFiles = extractConflictedFiles(repoPath);
368
- runGitSync(['merge', '--abort'], repoPath);
369
- return {
370
- success: false,
371
- error: `Squash merge conflict: ${conflictedFiles.length} file(s) conflicted`,
372
- conflictedFiles,
373
- };
374
- }
375
- // Non-conflict squash failure leaves the staged squash residue behind
376
- // (no MERGE_HEAD) -- restore so it does not poison the next focus's merge.
377
- restoreWorktreeAfterFailedMerge(repoPath);
378
- return {
379
- success: false,
380
- error: `Squash merge failed: ${squashResult.error}`,
381
- };
382
- }
383
- // Commit the squashed changes
384
- const commitResult = runGitSync(['commit', '-m', commitMessage], repoPath);
385
- if (!commitResult.success) {
386
- // The squash staged everything; a failed commit (e.g. a rejecting hook)
387
- // leaves it staged. Restore so the residue does not wedge the next merge.
388
- restoreWorktreeAfterFailedMerge(repoPath);
389
- return {
390
- success: false,
391
- error: `Squash merge commit failed: ${commitResult.error}`,
392
- };
393
- }
394
- // Capture the resulting commit SHA
395
- const shaResult = runGitSync(['rev-parse', 'HEAD'], repoPath);
396
- const commitSha = shaResult.success ? shaResult.output : undefined;
397
- return { success: true, commitSha };
398
- }
399
- /**
400
- * Dispatch to the appropriate merge strategy helper.
401
- * For 'merge-commit', tries fast-forward first, then falls back to --no-ff.
402
- */
403
- export function applyMergeStrategy(repoPath, sourceBranch, targetBranch, strategy) {
404
- if (strategy === 'fast-forward') {
405
- return tryFastForwardMerge(repoPath, sourceBranch);
406
- }
407
- if (strategy === 'rebase') {
408
- return tryRebaseMerge(repoPath, sourceBranch);
409
- }
410
- // 'merge-commit': try ff first, fall back to --no-ff
411
- const ffResult = tryFastForwardMerge(repoPath, sourceBranch);
412
- if (ffResult.success) {
413
- return ffResult;
414
- }
415
- return tryMergeCommit(repoPath, sourceBranch, `Advance ${targetBranch} to ${sourceBranch}`);
416
- }
417
209
  /**
418
210
  * Merge an agent branch into the integration branch.
419
211
  *
@@ -566,242 +358,6 @@ export function mergeToIntegration(params) {
566
358
  };
567
359
  }, `mergeToIntegration:${deliveryName}`);
568
360
  }
569
- /**
570
- * Merge the integration branch into the default (main) branch.
571
- *
572
- * Called when a focus completes with CI enabled. Optionally pushes
573
- * to remote when CD is also enabled. The push step is gated by
574
- * `pushToRemote` and only runs when an `origin` remote is configured.
575
- *
576
- * Uses the merge worktree and merge lock (same as mergeToIntegration)
577
- * to avoid concurrent merge conflicts. The merge worktree checks main out
578
- * DETACHED and advances refs/heads/main via `git update-ref`, so the merge
579
- * completes even when main is attached in the daemon's primary worktree
580
- * (where `git checkout main` would fail with "'main' is already used by
581
- * worktree"). Clean worktrees holding main are re-synced afterward.
582
- *
583
- * The result's `pushedTo` field is set only when CD push happened and
584
- * succeeded. Callers can read it to emit a focus-attributed log line
585
- * with the resolved remote name + URL.
586
- */
587
- export function mergeIntegrationToMain(params) {
588
- const { config, focusName, pushToRemote, mergeStrategy = 'merge-commit', getCiStatus } = params;
589
- return withMergeLock(async () => {
590
- const mwt = getMergeWorktreePath(config);
591
- const mainBranch = getDefaultBranch(config.repoPath);
592
- // Same self-heal as mergeToIntegration: this route shares the merge worktree,
593
- // so an earlier failed merge's residue would break the detached checkout
594
- // below. REFUSE if the worktree cannot be CONFIRMED clean afterward.
595
- const recovery = recoverDirtyMergeWorktree(mwt);
596
- if (!recovery.clean) {
597
- return {
598
- success: false,
599
- error: `Merge worktree could not be confirmed clean before merging ${config.integrationBranch} into ` +
600
- `${mainBranch}; residue survived recovery: ${recovery.residue ?? 'unknown'}`,
601
- sourceBranch: config.integrationBranch,
602
- targetBranch: mainBranch,
603
- };
604
- }
605
- const withRemote = hasOriginRemote(config.repoPath);
606
- // Fail early when a CD push was requested but no remote is configured.
607
- // Returning a push-failure result (rather than silently skipping) lets
608
- // the caller's shouldFileCdPushEscalation check fire so the operator
609
- // sees a visible escalation instead of a silent no-op.
610
- if (pushToRemote && !withRemote) {
611
- return {
612
- success: false,
613
- error: 'Push failed: no origin remote configured for this repository',
614
- sourceBranch: config.integrationBranch,
615
- targetBranch: mainBranch,
616
- };
617
- }
618
- // Fetch latest refs
619
- if (withRemote) {
620
- runGitSync(['fetch', 'origin'], config.repoPath);
621
- }
622
- // ── Green-wait gate (the single write-to-main point) ────────────────
623
- // This is the ONE place refs/heads/main advances, so the CI verdict is
624
- // evaluated HERE -- not in whichever completion/eager code path called us.
625
- // Every daemon route to main inherits the same fail-closed gate. With a
626
- // reader wired, the SHA about to reach main (the integration tip) must be
627
- // GREEN; pending/red/unknown HOLD without advancing the ref (nothing was
628
- // written -- no rollback, and the caller reports a hold, never an error or
629
- // an escalation). An explicit `undefined` reader skips the gate (opt-out).
630
- if (getCiStatus) {
631
- let mergedSha = '';
632
- try {
633
- // resolveRef checks the git exit status, so an unresolvable
634
- // integrationBranch yields null rather than git's stdout echo of the
635
- // branch NAME -- which would otherwise reach the CI gate as if it were
636
- // a commit SHA (echo-into-SHA-field bug class).
637
- mergedSha = resolveRef(config.repoPath, config.integrationBranch) ?? '';
638
- }
639
- catch (err) {
640
- log.warn(`Could not resolve integration tip SHA for the CI gate: ${err.message}`);
641
- }
642
- if (mergedSha) {
643
- let status = 'unknown';
644
- try {
645
- status = await getCiStatus({ sha: mergedSha, integrationBranch: config.integrationBranch, config });
646
- }
647
- catch (err) {
648
- log.warn(`CI status read failed for ${mergedSha.slice(0, 12)}: ${err.message}`);
649
- }
650
- const gate = resolveCiMergeGate(status);
651
- if (!gate.proceed) {
652
- // Classify whether this hold can still fire: a settled RED result on
653
- // this (unchanged) sha never re-evaluates without a NEW commit -- a
654
- // stall that reads as patience -- whereas a pending run can still be
655
- // finished by the CI runner. Record the distinction on the hold and in
656
- // the log so a stall is legible from the daemon log alone.
657
- const wait = classifyCiMergeWait(status, mergedSha);
658
- const marker = describeWaitReachability(wait);
659
- log.info(`Holding ${config.integrationBranch}->${mainBranch} for "${focusName}": ${gate.reason} ` +
660
- `(sha ${mergedSha.slice(0, 12)}) [${marker}].`);
661
- return {
662
- success: false,
663
- ciHeld: { ...gate, reachable: wait.reachable, reachabilityReason: marker },
664
- sourceBranch: config.integrationBranch,
665
- targetBranch: mainBranch,
666
- };
667
- }
668
- log.info(`CI gate passed for "${focusName}": ${gate.reason} (sha ${mergedSha.slice(0, 12)}).`);
669
- }
670
- }
671
- // Record where main was BEFORE we touch anything, so push failure can roll
672
- // the ref back, and classify any worktrees that currently have main attached
673
- // (the daemon's primary repoPath usually keeps main checked out). Advancing
674
- // the ref out-of-band leaves such a worktree with a stale tree, so after a
675
- // successful merge we re-sync every worktree that has NO tracked uncommitted
676
- // changes -- `git reset --hard` restores tracked files and preserves
677
- // untracked ones, so a worktree dirty only with untracked scratch (the
678
- // common dev-box state) is still safe to sync. Worktrees with tracked WIP are
679
- // left untouched (never clobber a developer's edits) but flagged loudly
680
- // afterward. Classification MUST happen before the advance: afterward the
681
- // phantom diff masquerades as tracked changes and defeats the check.
682
- const mainRefBefore = runGitSync(['rev-parse', mainBranch], config.repoPath);
683
- const mainWorktrees = worktreesWithBranchCheckedOut(config.repoPath, mainBranch, mwt);
684
- const resyncableMainWorktrees = mainWorktrees.filter(wt => !hasTrackedChanges(wt));
685
- const trackedDirtyMainWorktrees = mainWorktrees.filter(wt => hasTrackedChanges(wt));
686
- // Detached checkout of main's tip in the merge worktree. We deliberately
687
- // do NOT attach the main branch (`git checkout main`): main is frequently
688
- // checked out in the daemon's primary worktree, and git refuses to attach
689
- // the same branch in two worktrees ("'main' is already used by worktree").
690
- // Detaching lets us build the merge result and advance the ref via
691
- // update-ref, which bypasses that guard.
692
- const checkoutResult = runGitSync(['checkout', '--detach', mainBranch], mwt);
693
- if (!checkoutResult.success) {
694
- return {
695
- success: false,
696
- error: `Failed to checkout ${mainBranch}: ${checkoutResult.error}`,
697
- sourceBranch: config.integrationBranch,
698
- targetBranch: mainBranch,
699
- };
700
- }
701
- // Bring the detached HEAD up to the latest remote main (best-effort ff).
702
- // If remote main has diverged, a later push would be rejected and rolled
703
- // back; we don't merge it here to keep the integration->main result clean.
704
- if (withRemote) {
705
- runGitSync(['merge', '--ff-only', `origin/${mainBranch}`], mwt);
706
- }
707
- // Save pre-merge state for rollback
708
- const preMergeRef = runGitSync(['rev-parse', 'HEAD'], mwt);
709
- // Dispatch to the requested strategy. The strategy helpers operate on the
710
- // merge worktree's current HEAD (here a detached HEAD at main's tip), so
711
- // they work identically whether or not main is attached.
712
- let result = applyMergeStrategy(mwt, config.integrationBranch, mainBranch, mergeStrategy);
713
- if (!result.success) {
714
- return {
715
- ...result,
716
- error: `${result.error ?? 'Unknown error'} (merging ${config.integrationBranch} into ${mainBranch} via ${mergeStrategy})`,
717
- sourceBranch: config.integrationBranch,
718
- targetBranch: mainBranch,
719
- };
720
- }
721
- // Capture merge commit SHA
722
- const shaResult = runGitSync(['rev-parse', 'HEAD'], mwt);
723
- if (!result.commitSha && shaResult.success) {
724
- result = { ...result, commitSha: shaResult.output };
725
- }
726
- const newMainSha = shaResult.success ? shaResult.output : result.commitSha;
727
- // Advance refs/heads/main out-of-band to the merge result. update-ref is a
728
- // plumbing command without the checked-out-branch guard that `git checkout`
729
- // and `git branch -f` enforce, so this succeeds even when main is attached
730
- // in the primary worktree.
731
- if (newMainSha) {
732
- const updateRef = runGitSync(['update-ref', `refs/heads/${mainBranch}`, newMainSha], mwt);
733
- if (!updateRef.success) {
734
- return {
735
- success: false,
736
- error: `Failed to advance ${mainBranch} ref to merge result: ${updateRef.error}`,
737
- sourceBranch: config.integrationBranch,
738
- targetBranch: mainBranch,
739
- };
740
- }
741
- }
742
- // Push main to remote (gated on CD opt-in).
743
- let pushedTo;
744
- if (withRemote && pushToRemote) {
745
- const pushResult = runGitSync(['push', 'origin', mainBranch], mwt);
746
- if (!pushResult.success) {
747
- // Roll the main ref back to its pre-merge value before the generic
748
- // rollback (which resets the detached HEAD and marks corruption on a
749
- // failed rollback) runs.
750
- if (mainRefBefore.success) {
751
- runGitSync(['update-ref', `refs/heads/${mainBranch}`, mainRefBefore.output], mwt);
752
- }
753
- if (preMergeRef.success) {
754
- return handlePushFailureWithRollback({
755
- repoPath: mwt,
756
- branch: mainBranch,
757
- preMergeRef: preMergeRef.output,
758
- pushError: pushResult.error,
759
- sourceBranch: config.integrationBranch,
760
- targetBranch: mainBranch,
761
- });
762
- }
763
- return {
764
- success: false,
765
- error: `Push failed (no pre-merge ref for rollback): ${pushResult.error}`,
766
- sourceBranch: config.integrationBranch,
767
- targetBranch: mainBranch,
768
- };
769
- }
770
- const urlResult = runGitSync(['remote', 'get-url', 'origin'], mwt, { timeoutMs: 3_000 });
771
- pushedTo = { name: 'origin', url: urlResult.success ? urlResult.output : '' };
772
- }
773
- // Re-sync worktrees that held main and had no tracked changes before the ref
774
- // advance, so the out-of-band update leaves no phantom diff. `reset --hard`
775
- // restores tracked files to the new main and leaves untracked files intact,
776
- // so this is safe for both clean and untracked-only worktrees.
777
- if (newMainSha) {
778
- for (const wt of resyncableMainWorktrees) {
779
- const reset = runGitSync(['reset', '--hard', newMainSha], wt);
780
- if (!reset.success) {
781
- log.warn(`[CI] Advanced ${mainBranch} but failed to re-sync worktree ${wt}: ${reset.error}`);
782
- }
783
- }
784
- // Worktrees with tracked WIP can't be reset without clobbering work, but
785
- // leaving them silently stale is exactly what confused downstream tooling
786
- // (the /sync that committed a stale tree). Flag each loudly with the
787
- // recovery command so the staleness is never silent.
788
- for (const wt of trackedDirtyMainWorktrees) {
789
- log.warn(`[CI] Advanced ${mainBranch} to ${newMainSha} but worktree ${wt} has uncommitted ` +
790
- `TRACKED changes -- left unsynced to avoid clobbering work. Its tree now lags ` +
791
- `${mainBranch} and will show a phantom diff (e.g. to /sync). Commit or stash there, ` +
792
- `then 'git -C ${wt} reset --hard ${mainBranch}' to reconcile.`);
793
- }
794
- }
795
- const action = pushToRemote ? 'merged and pushed' : 'merged';
796
- log.info(`Successfully ${action} ${config.integrationBranch} into ${mainBranch}`);
797
- return {
798
- success: true,
799
- commitSha: result.commitSha,
800
- preMergeRef: preMergeRef.success ? preMergeRef.output : undefined,
801
- pushedTo,
802
- };
803
- }, `mergeIntegrationToMain:${focusName}`);
804
- }
805
361
  /**
806
362
  * Merge the latest integration HEAD into the current branch of a worktree.
807
363
  *