feat(FN-5255): flip directMergeCommitStrategy default from squash to direct
Changes the default merge strategy from squash to direct by flipping `directMergeCommitStrategy` in the settings schema and types, with the core implementation in `merger-ref-update-advance.ts`. Also aligns a heartbeat executor test assertion with the FN-5060 deduplication shape. Fusion-Task-Id: FN-5255 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai> Fusion-Task-Id: FN-5255
This commit is contained in:
7
.changeset/FN-5255-default-squash.md
Normal file
7
.changeset/FN-5255-default-squash.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
New projects now default `directMergeCommitStrategy` to `"always-squash"` for direct merges.
|
||||
Existing projects keep their persisted setting value.
|
||||
Per-project Settings UI controls and per-task `**Direct Merge Commit Strategy:** ...` PROMPT overrides are unchanged.
|
||||
@@ -122,7 +122,7 @@ Use `superviseSpawn(...)` from `@fusion/core` for managed child processes; do no
|
||||
### Merging Branches Into Main
|
||||
|
||||
1. **Drop duplicate commits before merging.** Rebase away duplicates already on main.
|
||||
2. **Rebase over squash for multi-commit branches.** `directMergeCommitStrategy="auto"` squashes only tiny branches.
|
||||
2. **Squash is now the project default; history-preserving merge paths require opt-in.** New projects default `directMergeCommitStrategy="always-squash"`. To preserve multi-commit history, explicitly set project `directMergeCommitStrategy` to `"auto"` or `"always-rebase"`, or set a per-task `**Direct Merge Commit Strategy:** ...` override in `PROMPT.md`.
|
||||
3. **Empty cherry-picks are no-ops.** Do not create empty commits.
|
||||
4. **Already-on-main classifier applies.** Allow finalize/self-healing recovery when lineage is landed.
|
||||
5. **Contamination auto-recovery is bounded.** First pass can auto-drop upstream foreign commits; repeated/ambiguous cases escalate.
|
||||
|
||||
@@ -201,7 +201,7 @@ Defaults from `DEFAULT_PROJECT_SETTINGS`; key scope from `PROJECT_SETTINGS_KEYS`
|
||||
| `overlapIgnorePaths` | `string[]` | `[]` | Optional project-relative file or directory paths to exclude from overlap blocking (for example `docs` or `generated/openapi.json`). Entries are trimmed, deduplicated, and must not be absolute or contain `..` traversal. |
|
||||
| `autoMerge` | `boolean` | `true` | Auto-finalize tasks from `in-review`. |
|
||||
| `mergeStrategy` | `"direct" \| "pull-request"` | `"direct"` | Completion mode (local direct merge vs PR-first). |
|
||||
| `directMergeCommitStrategy` | `"auto" \| "always-squash" \| "always-rebase"` | `"auto"` | Direct-merge commit routing mode. `auto` keeps the legacy squash path for branches with zero or one substantive commit, but switches multi-substantive direct merges to a history-preserving rebase-and-merge/cherry-pick path so commit boundaries, subjects, and `Fusion-Task-Id` trailers survive on `main`. `always-squash` forces the legacy squash path; `always-rebase` always preserves per-commit history. Only applies when `mergeStrategy="direct"`. |
|
||||
| `directMergeCommitStrategy` | `"auto" \| "always-squash" \| "always-rebase"` | `"always-squash"` | Direct-merge commit routing mode. `always-squash` (default) forces the legacy squash path. `auto` keeps the legacy squash path for branches with zero or one substantive commit, but switches multi-substantive direct merges to a history-preserving rebase-and-merge/cherry-pick path so commit boundaries, subjects, and `Fusion-Task-Id` trailers survive on `main`. `always-rebase` always preserves per-commit history. Only applies when `mergeStrategy="direct"`. |
|
||||
| `mergeIntegrationWorktree` | `"reuse-task-worktree" \| "cwd-main"` | `"reuse-task-worktree"` | Auto-merge integration-root mode for direct merges only. `reuse-task-worktree` runs the rebase/conflict/audit/finalize cascade inside the task worktree so project-root `HEAD` and dirty state stay untouched. `cwd-main` preserves the legacy project-root integration path as an escape hatch. When `worktrunk.enabled=true`, worktrunk-managed merge/worktree handling still takes precedence and this setting is effectively advisory until the native path is used. |
|
||||
| `integrationBranch` | `string` | `undefined` | Optional canonical project integration branch override. Resolution order for merge/self-healing/branch-conflict defaults is `integrationBranch` → legacy `baseBranch` → `origin/HEAD` symbolic ref → fallback `"main"`. This resolved value is used as `projectDefaultBranch` for `resolveTaskMergeTarget(...)`; task-level overrides still come from task metadata. |
|
||||
| `prerebaseAutoEnabled` | `boolean` | `true` | Master switch for pre-merge auto-prerebase policy. When enabled, merger checks divergence from `<task.baseCommitSha>` to local `main` and may rebase before Stage 1/2 rebases. Ignored when `worktrunk.enabled=true` (worktrunk-managed path defers this layer). |
|
||||
@@ -235,7 +235,7 @@ Accepted values:
|
||||
Override precedence for direct merges is:
|
||||
1. Task `PROMPT.md` line `**Direct Merge Commit Strategy:** ...`
|
||||
2. Project `directMergeCommitStrategy`
|
||||
3. Default `"auto"`
|
||||
3. Default `"always-squash"`
|
||||
|
||||
### Sandbox settings
|
||||
|
||||
|
||||
@@ -151,8 +151,8 @@ describe("settings key parity", () => {
|
||||
expect(DEFAULT_PROJECT_SETTINGS.completionDocumentationMode).toBe("off");
|
||||
});
|
||||
|
||||
it("keeps directMergeCommitStrategy project-scoped with auto default", () => {
|
||||
expect(DEFAULT_PROJECT_SETTINGS.directMergeCommitStrategy).toBe("auto");
|
||||
it("defaults directMergeCommitStrategy to always-squash and keeps it project-scoped", () => {
|
||||
expect(DEFAULT_PROJECT_SETTINGS.directMergeCommitStrategy).toBe("always-squash");
|
||||
expect(isProjectSettingsKey("directMergeCommitStrategy")).toBe(true);
|
||||
expect(isGlobalSettingsKey("directMergeCommitStrategy")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -103,9 +103,9 @@ describe("TaskStore", () => {
|
||||
expect(settings.mergeStrategy).toBe("pull-request");
|
||||
});
|
||||
|
||||
it("defaults directMergeCommitStrategy to auto and persists updates", async () => {
|
||||
it("defaults directMergeCommitStrategy to always-squash and persists updates", async () => {
|
||||
const defaults = await harness.store().getSettings();
|
||||
expect(defaults.directMergeCommitStrategy).toBe("auto");
|
||||
expect(defaults.directMergeCommitStrategy).toBe("always-squash");
|
||||
|
||||
await harness.store().updateSettings({ directMergeCommitStrategy: "always-rebase" });
|
||||
const settings = await harness.store().getSettings();
|
||||
|
||||
@@ -195,7 +195,7 @@ export const DEFAULT_PROJECT_SETTINGS = {
|
||||
autoMerge: true,
|
||||
testMode: undefined,
|
||||
mergeStrategy: "direct",
|
||||
directMergeCommitStrategy: "auto",
|
||||
directMergeCommitStrategy: "always-squash",
|
||||
mergeIntegrationWorktree: "reuse-task-worktree",
|
||||
integrationBranch: undefined,
|
||||
requirePrApproval: false,
|
||||
|
||||
@@ -2733,7 +2733,7 @@ export interface ProjectSettings {
|
||||
* - "auto": squash single-substantive branches, preserve history for multi-substantive branches
|
||||
* - "always-squash": always use the legacy squash path for direct merges
|
||||
* - "always-rebase": always preserve individual branch commits during direct merges
|
||||
* Only applies when mergeStrategy is "direct". Default: "auto". */
|
||||
* Only applies when mergeStrategy is "direct". Default: "always-squash". */
|
||||
directMergeCommitStrategy?: DirectMergeCommitStrategy;
|
||||
/** Auto-merge integration-root mode.
|
||||
* - "reuse-task-worktree" (default): run the auto-merge cascade in the task worktree
|
||||
|
||||
@@ -2461,6 +2461,7 @@ describe("aiMergeTask post-squash audit gate", () => {
|
||||
mergeIntegrationWorktree: "cwd-main" as const,
|
||||
testCommand: "pnpm test",
|
||||
mergeConflictStrategy: "ai-only",
|
||||
directMergeCommitStrategy: "auto",
|
||||
worktreeRebaseBeforeMerge: false,
|
||||
worktreeRebaseLocalBase: false,
|
||||
...overrides,
|
||||
|
||||
@@ -163,7 +163,7 @@ export async function advanceIntegrationBranchRef(args: {
|
||||
["merge-base", "--is-ancestor", expectedCurrentSha, newSha],
|
||||
rootDir,
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
} catch (_error: unknown) {
|
||||
const diagnostic = `newSha ${newSha} is not a descendant of ${expectedCurrentSha} on ${ref}`;
|
||||
await emitRefAdvance({
|
||||
succeeded: false,
|
||||
|
||||
@@ -5824,7 +5824,7 @@ function resolveDirectMergeCommitStrategy(
|
||||
return { strategy: promptOverride, source: "prompt" };
|
||||
}
|
||||
return {
|
||||
strategy: settings.directMergeCommitStrategy ?? "auto",
|
||||
strategy: settings.directMergeCommitStrategy ?? "always-squash",
|
||||
source: "project",
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user