FN-5644: default AI merge commit body to summarize changes
Fusion-Task-Id: FN-5644 Fusion-Task-Lineage: 5cf0133b-2a14-4bb4-a1c4-2d5bef5793bc
This commit is contained in:
7
.changeset/fn-5644-merge-commit-summary-default-docs.md
Normal file
7
.changeset/fn-5644-merge-commit-summary-default-docs.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Update `useAiMergeCommitSummary` docs/JSDoc to match the intended default of `true`, including that merge commit summaries include a subject plus body summary (narrative + bullets + diff-stat).
|
||||||
|
|
||||||
|
Also fixes AI merge-mode prompt guidance so AI-authored squash commits include a summarized body instead of subject-only commit messages.
|
||||||
@@ -388,7 +388,7 @@ Default notes:
|
|||||||
| `memoryBackupDir` | `string` | `".fusion/backups/memory"` | Relative memory backup directory path. |
|
| `memoryBackupDir` | `string` | `".fusion/backups/memory"` | Relative memory backup directory path. |
|
||||||
| `memoryBackupScope` | `"project" \| "agents" \| "all"` | `"all"` | Backup scope: project memory, agent memory, or both. |
|
| `memoryBackupScope` | `"project" \| "agents" \| "all"` | `"all"` | Backup scope: project memory, agent memory, or both. |
|
||||||
| `autoSummarizeTitles` | `boolean` | `false` | Auto-generate titles for long untitled descriptions across dashboard/API task creation and agent/tool-created tasks. |
|
| `autoSummarizeTitles` | `boolean` | `false` | Auto-generate titles for long untitled descriptions across dashboard/API task creation and agent/tool-created tasks. |
|
||||||
| `useAiMergeCommitSummary` | `boolean` | `false` | Use AI-generated merge commit summaries instead of raw step-commit subject lists. |
|
| `useAiMergeCommitSummary` | `boolean` | `true` | Use AI-generated merge commit summaries (subject + bullet body + diff-stat) instead of raw step-commit subject lists. |
|
||||||
| `titleSummarizerProvider` | `string` | `undefined` | Provider for title summarization. |
|
| `titleSummarizerProvider` | `string` | `undefined` | Provider for title summarization. |
|
||||||
| `titleSummarizerModelId` | `string` | `undefined` | Model ID for title summarization. |
|
| `titleSummarizerModelId` | `string` | `undefined` | Model ID for title summarization. |
|
||||||
| `titleSummarizerFallbackProvider` | `string` | `undefined` | Fallback provider for title summarization. |
|
| `titleSummarizerFallbackProvider` | `string` | `undefined` | Fallback provider for title summarization. |
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ describe("settings defaults invariants", () => {
|
|||||||
expect("githubCloseSourceIssueOnDone" in DEFAULT_GLOBAL_SETTINGS).toBe(false);
|
expect("githubCloseSourceIssueOnDone" in DEFAULT_GLOBAL_SETTINGS).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("defaults AI merge commit summaries to enabled", () => {
|
||||||
|
// FN-5642/FN-5644 intentionally default this on for subject + body summary coverage.
|
||||||
|
expect(DEFAULT_PROJECT_SETTINGS.useAiMergeCommitSummary).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
describe("recycleWorktrees default", () => {
|
describe("recycleWorktrees default", () => {
|
||||||
it("keeps recycleWorktrees explicitly false in project defaults", () => {
|
it("keeps recycleWorktrees explicitly false in project defaults", () => {
|
||||||
expect(DEFAULT_PROJECT_SETTINGS.recycleWorktrees).toBe(false);
|
expect(DEFAULT_PROJECT_SETTINGS.recycleWorktrees).toBe(false);
|
||||||
|
|||||||
@@ -3378,8 +3378,9 @@ export interface ProjectSettings {
|
|||||||
* Default: false. */
|
* Default: false. */
|
||||||
autoSummarizeTitles?: boolean;
|
autoSummarizeTitles?: boolean;
|
||||||
/** When true, merge commit messages include an AI-generated summary of the
|
/** When true, merge commit messages include an AI-generated summary of the
|
||||||
* changes instead of just listing step commit subjects. Uses the title
|
* changes instead of just listing step commit subjects. Body composition
|
||||||
* summarizer model. Default: false. */
|
* includes a narrative line, bullet summary, and `git diff --stat` when
|
||||||
|
* available. Uses the title summarizer model. Default: true. */
|
||||||
useAiMergeCommitSummary?: boolean;
|
useAiMergeCommitSummary?: boolean;
|
||||||
/** AI model provider for title summarization (when autoSummarizeTitles is enabled).
|
/** AI model provider for title summarization (when autoSummarizeTitles is enabled).
|
||||||
* Must be set together with `titleSummarizerModelId`. Falls back to planningProvider,
|
* Must be set together with `titleSummarizerModelId`. Falls back to planningProvider,
|
||||||
|
|||||||
@@ -3583,7 +3583,7 @@ export function SettingsModal({
|
|||||||
AI merge commit summaries
|
AI merge commit summaries
|
||||||
</label>
|
</label>
|
||||||
<small>
|
<small>
|
||||||
When enabled, merge commit messages will include an AI-generated summary of the changes instead of just listing step commit subjects. Uses the title summarization model.
|
When enabled, merge commit messages include an AI-generated subject plus body summary (narrative + bullets + diff-stat) instead of just listing step commit subjects. Uses the title summarization model.
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
landSquash,
|
landSquash,
|
||||||
parseReviewVerdict,
|
parseReviewVerdict,
|
||||||
buildMergeSystemPrompt,
|
buildMergeSystemPrompt,
|
||||||
|
buildMergePrompt,
|
||||||
buildReviewSystemPrompt,
|
buildReviewSystemPrompt,
|
||||||
REVIEW_VERDICT_MARKER,
|
REVIEW_VERDICT_MARKER,
|
||||||
AiMergeBlockedError,
|
AiMergeBlockedError,
|
||||||
@@ -116,9 +117,11 @@ describe("parseReviewVerdict", () => {
|
|||||||
expect(buildMergeSystemPrompt().toLowerCase()).toContain("conflict");
|
expect(buildMergeSystemPrompt().toLowerCase()).toContain("conflict");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("merge system prompt enforces new-breakage verification + uses the editable merger prompt", () => {
|
it("merge system prompt enforces new-breakage verification + commit body summary guidance", () => {
|
||||||
expect(buildMergeSystemPrompt().toLowerCase()).toContain("type-check");
|
expect(buildMergeSystemPrompt().toLowerCase()).toContain("type-check");
|
||||||
expect(buildMergeSystemPrompt()).toMatch(/new failure/i);
|
expect(buildMergeSystemPrompt()).toMatch(/new failure/i);
|
||||||
|
expect(buildMergeSystemPrompt()).toMatch(/bullet list of key changes/i);
|
||||||
|
expect(buildMergeSystemPrompt()).toMatch(/Files changed:/i);
|
||||||
// A custom 'merger' role prompt is incorporated as the base, while the hard
|
// A custom 'merger' role prompt is incorporated as the base, while the hard
|
||||||
// rules (verification + trailers) are still appended.
|
// rules (verification + trailers) are still appended.
|
||||||
const cfg = {
|
const cfg = {
|
||||||
@@ -129,6 +132,23 @@ describe("parseReviewVerdict", () => {
|
|||||||
expect(p).toContain("CUSTOM MERGER PERSONA");
|
expect(p).toContain("CUSTOM MERGER PERSONA");
|
||||||
expect(p).toContain("Verify before committing");
|
expect(p).toContain("Verify before committing");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("merge prompt requires subject, body summary, and diff-stat in commit message", () => {
|
||||||
|
const prompt = buildMergePrompt({
|
||||||
|
taskId: "FN-1",
|
||||||
|
branch: "fusion/fn-1",
|
||||||
|
integrationBranch: "main",
|
||||||
|
tipSha: "0123456789abcdef0123456789abcdef01234567",
|
||||||
|
taskTitle: "Do the thing",
|
||||||
|
includeTaskId: true,
|
||||||
|
trailers: ["Fusion-Task-Id: FN-1"],
|
||||||
|
});
|
||||||
|
expect(prompt).toMatch(/Build a merge body from the staged squash diff/i);
|
||||||
|
expect(prompt).toMatch(/bullet list of key changes/i);
|
||||||
|
expect(prompt).toMatch(/Files changed:/i);
|
||||||
|
expect(prompt).toMatch(/git diff --stat/i);
|
||||||
|
expect(prompt).toMatch(/git commit -m "FN-1: <concise imperative summary of the squashed changes>" -m/i);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("runAiMerge", () => {
|
describe("runAiMerge", () => {
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ export function buildMergeSystemPrompt(agentPrompts?: AgentPromptsConfig): strin
|
|||||||
" - The subject line must CONCISELY SUMMARIZE the squashed changes in",
|
" - The subject line must CONCISELY SUMMARIZE the squashed changes in",
|
||||||
" imperative mood (e.g. \"add X\", \"fix Y\") based on the actual diff — do",
|
" imperative mood (e.g. \"add X\", \"fix Y\") based on the actual diff — do",
|
||||||
" not just restate the task title.",
|
" not just restate the task title.",
|
||||||
|
" - The commit BODY must include:",
|
||||||
|
" 1) one short narrative summary line,",
|
||||||
|
" 2) a bullet list of key changes, and",
|
||||||
|
" 3) a `Files changed:` section populated from `git diff --stat`.",
|
||||||
" - Include the task-id prefix and the trailer lines EXACTLY as given in the",
|
" - Include the task-id prefix and the trailer lines EXACTLY as given in the",
|
||||||
" task instructions (they associate the commit with the board task).",
|
" task instructions (they associate the commit with the board task).",
|
||||||
].filter((l) => l !== undefined).join("\n");
|
].filter((l) => l !== undefined).join("\n");
|
||||||
@@ -253,11 +257,15 @@ export function buildMergePrompt(input: {
|
|||||||
"Steps:",
|
"Steps:",
|
||||||
` 1. Run: git merge --squash ${input.branch}`,
|
` 1. Run: git merge --squash ${input.branch}`,
|
||||||
" 2. If there are conflicts, resolve them (favor the task's intent), then `git add` the resolved files.",
|
" 2. If there are conflicts, resolve them (favor the task's intent), then `git add` the resolved files.",
|
||||||
" 3. Commit the staged result as a SINGLE commit whose subject summarizes the",
|
" 3. Build a merge body from the staged squash diff:",
|
||||||
` actual changes${input.taskTitle ? ` (task title hint: ${JSON.stringify(input.taskTitle)})` : ""}, including the required trailers:`,
|
" - one short narrative summary line",
|
||||||
` git commit -m ${subjectShape}${trailerArgs}`,
|
" - bullet list of key changes",
|
||||||
|
" - `Files changed:` + the output of `git diff --stat`",
|
||||||
|
" 4. Commit the staged result as a SINGLE commit whose subject summarizes the",
|
||||||
|
` actual changes${input.taskTitle ? ` (task title hint: ${JSON.stringify(input.taskTitle)})` : ""}, including the body above and required trailers:`,
|
||||||
|
` git commit -m ${subjectShape} -m "<narrative + bullet list + Files changed: ...>"${trailerArgs}`,
|
||||||
" Keep the trailer line(s) verbatim — they link the commit to the board task.",
|
" Keep the trailer line(s) verbatim — they link the commit to the board task.",
|
||||||
" 4. Verify `git log --oneline ${tip}..HEAD` shows exactly one new commit and `git status` is clean.".replace("${tip}", short(input.tipSha)),
|
" 5. Verify `git log --oneline ${tip}..HEAD` shows exactly one new commit and `git status` is clean.".replace("${tip}", short(input.tipSha)),
|
||||||
"",
|
"",
|
||||||
"If `git merge --squash` reports the branch is already up to date (nothing to",
|
"If `git merge --squash` reports the branch is already up to date (nothing to",
|
||||||
"merge), do nothing and leave HEAD unchanged.",
|
"merge), do nothing and leave HEAD unchanged.",
|
||||||
|
|||||||
Reference in New Issue
Block a user