@telora/daemon 0.21.134 → 0.21.146
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.
- package/build-info.json +2 -2
- package/dist/audit/audit-hooks.d.ts +15 -6
- package/dist/audit/audit-hooks.d.ts.map +1 -1
- package/dist/audit/audit-hooks.js +56 -28
- package/dist/audit/audit-hooks.js.map +1 -1
- package/dist/ci/ci-escalation.js +1 -1
- package/dist/ci/ci-escalation.js.map +1 -1
- package/dist/ci/ci-sensor-loop.d.ts +24 -0
- package/dist/ci/ci-sensor-loop.d.ts.map +1 -1
- package/dist/ci/ci-sensor-loop.js +35 -9
- package/dist/ci/ci-sensor-loop.js.map +1 -1
- package/dist/cli/connect/config-write.d.ts +87 -0
- package/dist/cli/connect/config-write.d.ts.map +1 -0
- package/dist/cli/connect/config-write.js +255 -0
- package/dist/cli/connect/config-write.js.map +1 -0
- package/dist/cli/connect/registration.d.ts +76 -0
- package/dist/cli/connect/registration.d.ts.map +1 -0
- package/dist/cli/connect/registration.js +207 -0
- package/dist/cli/connect/registration.js.map +1 -0
- package/dist/cli/connect.d.ts +3 -141
- package/dist/cli/connect.d.ts.map +1 -1
- package/dist/cli/connect.js +9 -441
- package/dist/cli/connect.js.map +1 -1
- package/dist/completion/coverage-stabilized.d.ts +1 -1
- package/dist/completion/coverage-stabilized.d.ts.map +1 -1
- package/dist/completion/coverage-stabilized.js +1 -1
- package/dist/completion/coverage-stabilized.js.map +1 -1
- package/dist/completion/escalation-provenance.d.ts +2 -1
- package/dist/completion/escalation-provenance.d.ts.map +1 -1
- package/dist/completion/escalation-provenance.js +2 -1
- package/dist/completion/escalation-provenance.js.map +1 -1
- package/dist/completion/escalations.js +1 -1
- package/dist/completion/escalations.js.map +1 -1
- package/dist/completion/event-escalations.d.ts +1 -1
- package/dist/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +1 -1
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event.d.ts +1 -1
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js +2 -2
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/machine-remediable-demotion.d.ts +1 -1
- package/dist/completion/machine-remediable-demotion.d.ts.map +1 -1
- package/dist/completion/machine-remediable-demotion.js +2 -1
- package/dist/completion/machine-remediable-demotion.js.map +1 -1
- package/dist/completion/read-only-pristine.d.ts +1 -1
- package/dist/completion/read-only-pristine.d.ts.map +1 -1
- package/dist/completion/read-only-pristine.js +1 -1
- package/dist/completion/read-only-pristine.js.map +1 -1
- package/dist/completion/review-exit-escalations.d.ts +1 -1
- package/dist/completion/review-exit-escalations.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.d.ts +5 -2
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +5 -2
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/settle-injection-sweep.d.ts +9 -7
- package/dist/completion/settle-injection-sweep.d.ts.map +1 -1
- package/dist/completion/settle-injection-sweep.js +17 -15
- package/dist/completion/settle-injection-sweep.js.map +1 -1
- package/dist/completion/state-cascade.js +2 -2
- package/dist/completion/state-cascade.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +2 -1
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/completion/team-lifecycle.d.ts.map +1 -1
- package/dist/completion/team-lifecycle.js +2 -1
- package/dist/completion/team-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-advance-block.d.ts +1 -1
- package/dist/delivery/delivery-advance-block.d.ts.map +1 -1
- package/dist/delivery/delivery-advance-block.js +1 -1
- package/dist/delivery/delivery-advance-block.js.map +1 -1
- package/dist/delivery/delivery-lifecycle.js +1 -1
- package/dist/delivery/delivery-lifecycle.js.map +1 -1
- package/dist/delivery/delivery-merge-ops.js +1 -1
- package/dist/delivery/delivery-merge-ops.js.map +1 -1
- package/dist/delivery/delivery-reconciler-shadow.d.ts +1 -1
- package/dist/delivery/delivery-reconciler-shadow.d.ts.map +1 -1
- package/dist/delivery/delivery-reconciler-shadow.js +1 -1
- package/dist/delivery/delivery-reconciler-shadow.js.map +1 -1
- package/dist/delivery/operator-hold.d.ts +1 -1
- package/dist/delivery/operator-hold.d.ts.map +1 -1
- package/dist/delivery/paused-revival.d.ts.map +1 -1
- package/dist/delivery/paused-revival.js +2 -1
- package/dist/delivery/paused-revival.js.map +1 -1
- package/dist/directive/close-loop-dispatcher.d.ts +35 -12
- package/dist/directive/close-loop-dispatcher.d.ts.map +1 -1
- package/dist/directive/close-loop-dispatcher.js +31 -11
- package/dist/directive/close-loop-dispatcher.js.map +1 -1
- package/dist/directive/directive-dispatch.d.ts +1 -1
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +1 -1
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/directive/directive-executor.js +1 -1
- package/dist/directive/directive-executor.js.map +1 -1
- package/dist/drift/staleness-sensor.js +1 -1
- package/dist/drift/staleness-sensor.js.map +1 -1
- package/dist/focus/focus-engine.js +3 -2
- package/dist/focus/focus-engine.js.map +1 -1
- package/dist/focus/focus-lifecycle.d.ts.map +1 -1
- package/dist/focus/focus-lifecycle.js +2 -1
- package/dist/focus/focus-lifecycle.js.map +1 -1
- package/dist/focus/focus-respawn-guard.d.ts +1 -1
- package/dist/focus/focus-respawn-guard.d.ts.map +1 -1
- package/dist/focus/focus-respawn-guard.js +1 -1
- package/dist/focus/focus-respawn-guard.js.map +1 -1
- package/dist/focus/listener-auto-advance.d.ts +4 -45
- package/dist/focus/listener-auto-advance.d.ts.map +1 -1
- package/dist/focus/listener-auto-advance.js +5 -238
- package/dist/focus/listener-auto-advance.js.map +1 -1
- package/dist/focus/listener-spawn.d.ts +62 -0
- package/dist/focus/listener-spawn.d.ts.map +1 -0
- package/dist/focus/listener-spawn.js +418 -0
- package/dist/focus/listener-spawn.js.map +1 -0
- package/dist/focus/listener-state.d.ts +57 -0
- package/dist/focus/listener-state.d.ts.map +1 -0
- package/dist/focus/listener-state.js +63 -0
- package/dist/focus/listener-state.js.map +1 -0
- package/dist/focus/listener.d.ts +0 -58
- package/dist/focus/listener.d.ts.map +1 -1
- package/dist/focus/listener.js +25 -453
- package/dist/focus/listener.js.map +1 -1
- package/dist/focus/verify-open-issues.d.ts +62 -0
- package/dist/focus/verify-open-issues.d.ts.map +1 -0
- package/dist/focus/verify-open-issues.js +265 -0
- package/dist/focus/verify-open-issues.js.map +1 -0
- package/dist/focus-stage-lifecycle/index.d.ts +1 -1
- package/dist/focus-stage-lifecycle/index.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/index.js +1 -1
- package/dist/focus-stage-lifecycle/index.js.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.js +1 -1
- package/dist/focus-stage-lifecycle/wedge-watchdog.js.map +1 -1
- package/dist/git/index.d.ts +2 -0
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +2 -0
- package/dist/git/index.js.map +1 -1
- package/dist/git/merge-strategies.d.ts +80 -0
- package/dist/git/merge-strategies.d.ts.map +1 -0
- package/dist/git/merge-strategies.js +218 -0
- package/dist/git/merge-strategies.js.map +1 -0
- package/dist/git/merge-to-main.d.ts +76 -0
- package/dist/git/merge-to-main.d.ts.map +1 -0
- package/dist/git/merge-to-main.js +256 -0
- package/dist/git/merge-to-main.js.map +1 -0
- package/dist/git/merge.d.ts +27 -81
- package/dist/git/merge.d.ts.map +1 -1
- package/dist/git/merge.js +4 -448
- package/dist/git/merge.js.map +1 -1
- package/dist/git/state-detector.js +1 -1
- package/dist/git/state-detector.js.map +1 -1
- package/dist/loops/engine.js +1 -1
- package/dist/loops/engine.js.map +1 -1
- package/dist/merge/focus-merge.d.ts +1 -1
- package/dist/merge/focus-merge.d.ts.map +1 -1
- package/dist/merge/focus-merge.js +1 -1
- package/dist/merge/focus-merge.js.map +1 -1
- package/dist/merge/merge-back-loop.d.ts +1 -1
- package/dist/merge/merge-back-loop.d.ts.map +1 -1
- package/dist/merge/merge-back-loop.js +2 -2
- package/dist/merge/merge-back-loop.js.map +1 -1
- package/dist/merge/merge-detector.js +1 -1
- package/dist/merge/merge-detector.js.map +1 -1
- package/dist/nexus/return.js +1 -1
- package/dist/nexus/return.js.map +1 -1
- package/dist/prd/controller-shared.d.ts +223 -0
- package/dist/prd/controller-shared.d.ts.map +1 -0
- package/dist/prd/controller-shared.js +77 -0
- package/dist/prd/controller-shared.js.map +1 -0
- package/dist/prd/controller.d.ts +5 -256
- package/dist/prd/controller.d.ts.map +1 -1
- package/dist/prd/controller.js +9 -166
- package/dist/prd/controller.js.map +1 -1
- package/dist/prd/done-sweep.d.ts +55 -0
- package/dist/prd/done-sweep.d.ts.map +1 -0
- package/dist/prd/done-sweep.js +112 -0
- package/dist/prd/done-sweep.js.map +1 -0
- package/dist/prompt-sections/execution-sections.d.ts +1 -1
- package/dist/prompt-sections/execution-sections.d.ts.map +1 -1
- package/dist/prompt-sections/execution-sections.js +12 -2
- package/dist/prompt-sections/execution-sections.js.map +1 -1
- package/dist/prompt-sections/shared.d.ts +1 -1
- package/dist/prompt-sections/shared.d.ts.map +1 -1
- package/dist/queries/agent-resolvable-promotion.d.ts +1 -1
- package/dist/queries/agent-resolvable-promotion.d.ts.map +1 -1
- package/dist/queries/agent-resolvable-promotion.js +2 -1
- package/dist/queries/agent-resolvable-promotion.js.map +1 -1
- package/dist/queries/deliveries.d.ts +0 -195
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +1 -83
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/escalation-consumption.d.ts +120 -0
- package/dist/queries/escalation-consumption.d.ts.map +1 -0
- package/dist/queries/escalation-consumption.js +143 -0
- package/dist/queries/escalation-consumption.js.map +1 -0
- package/dist/queries/escalation-dispositions.d.ts +19 -0
- package/dist/queries/escalation-dispositions.d.ts.map +1 -0
- package/dist/queries/escalation-dispositions.js +18 -0
- package/dist/queries/escalation-dispositions.js.map +1 -0
- package/dist/queries/escalation-suppression.d.ts +1 -1
- package/dist/queries/escalation-suppression.d.ts.map +1 -1
- package/dist/queries/escalation-suppression.js +1 -1
- package/dist/queries/escalation-suppression.js.map +1 -1
- package/dist/queries/escalations.d.ts +343 -0
- package/dist/queries/escalations.d.ts.map +1 -0
- package/dist/queries/escalations.js +493 -0
- package/dist/queries/escalations.js.map +1 -0
- package/dist/queries/focus-discovery.d.ts +204 -0
- package/dist/queries/focus-discovery.d.ts.map +1 -0
- package/dist/queries/focus-discovery.js +92 -0
- package/dist/queries/focus-discovery.js.map +1 -0
- package/dist/queries/index.d.ts +4 -2
- package/dist/queries/index.d.ts.map +1 -1
- package/dist/queries/index.js +4 -2
- package/dist/queries/index.js.map +1 -1
- package/dist/queries/issues.d.ts +8 -436
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +7 -615
- package/dist/queries/issues.js.map +1 -1
- package/dist/resolvers/agent-escalations.d.ts +20 -0
- package/dist/resolvers/agent-escalations.d.ts.map +1 -1
- package/dist/resolvers/agent-escalations.js +40 -10
- package/dist/resolvers/agent-escalations.js.map +1 -1
- package/dist/resolvers/index.d.ts +2 -2
- package/dist/resolvers/index.d.ts.map +1 -1
- package/dist/resolvers/index.js +1 -1
- package/dist/resolvers/index.js.map +1 -1
- package/dist/resolvers/shared/git-diff.d.ts +99 -18
- package/dist/resolvers/shared/git-diff.d.ts.map +1 -1
- package/dist/resolvers/shared/git-diff.js +98 -27
- package/dist/resolvers/shared/git-diff.js.map +1 -1
- package/dist/shell/control-state.d.ts +1 -1
- package/dist/shell/control-state.d.ts.map +1 -1
- package/dist/shell/control-state.js +1 -1
- package/dist/shell/control-state.js.map +1 -1
- package/dist/shell/crash-recovery.d.ts +1 -1
- package/dist/shell/crash-recovery.d.ts.map +1 -1
- package/dist/shell/crash-recovery.js +2 -2
- package/dist/shell/crash-recovery.js.map +1 -1
- package/dist/shell/install-probe-tick.d.ts +1 -1
- package/dist/shell/install-probe-tick.d.ts.map +1 -1
- package/dist/shell/install-probe-tick.js +1 -1
- package/dist/shell/install-probe-tick.js.map +1 -1
- package/dist/shell/unified-shell.js +1 -1
- package/dist/shell/unified-shell.js.map +1 -1
- package/dist/spawn/model-routing.d.ts +1 -1
- package/dist/spawn/model-routing.d.ts.map +1 -1
- package/dist/spawn/model-routing.js +1 -1
- package/dist/spawn/model-routing.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts +1 -1
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +1 -1
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/spawn-decision.js +1 -1
- package/dist/spawn/spawn-decision.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts +1 -1
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +1 -1
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/templates/claude-md.d.ts +1 -1
- package/dist/templates/claude-md.d.ts.map +1 -1
- package/dist/templates/claude-md.js +12 -0
- package/dist/templates/claude-md.js.map +1 -1
- package/dist/verify/verification-default-on.d.ts +1 -1
- package/dist/verify/verification-default-on.d.ts.map +1 -1
- package/dist/verify/verification-deps.d.ts +1 -1
- package/dist/verify/verification-deps.d.ts.map +1 -1
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +20 -12
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/worktree/provision.js +1 -1
- package/dist/worktree/provision.js.map +1 -1
- package/dist/worktree/scratch-guard.d.ts.map +1 -1
- package/dist/worktree/scratch-guard.js +23 -2
- package/dist/worktree/scratch-guard.js.map +1 -1
- 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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"}
|
package/dist/git/merge.d.ts
CHANGED
|
@@ -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
|
|
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;
|
package/dist/git/merge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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
|
*
|