FN-5819: allow shared-group members to merge into group branch with auto-merge off

Allow shared branch-group members to continue member→group integration even when project auto-merge is disabled.

- add core helper to detect shared branch-group member integrations and export it
- update engine in-review enqueue/handoff/startup flows to permit shared-member integration while still honoring global/engine pause gates
- keep self-healing from pulling shared-member in-review tasks backward during auto-merge-off maintenance
- expand project-engine and reliability-interaction tests for shared-member autoMerge-off behavior and add FN-5819 backstop docs updates

Files changed:
 AGENTS.md                                          |  2 +
 docs/architecture.md                               |  3 +-
 packages/core/src/__tests__/task-merge.test.ts     | 37 +++++++++
 packages/core/src/index.ts                         |  1 +
 packages/core/src/task-merge.ts                    | 13 +++
 packages/engine/src/__tests__/project-engine.test.ts    | 64 ++++++++++++++
 packages/engine/src/__tests__/reliability-interactions/branch-group-merge-routing.test.ts             | 48 +++++++++++
 packages/engine/src/__tests__/reliability-interactions/shared-group-member-integration.test.ts        | 97 ++++++++++++++++++++++
 packages/engine/src/project-engine.ts              | 35 ++++----
 packages/engine/src/self-healing.ts                |  3 +-
 10 files changed, 284 insertions(+), 19 deletions(-)

Fusion-Task-Id: FN-5819

Fusion-Task-Lineage: b5eeced7-7c47-4573-9ab4-ca577bec65e1
This commit is contained in:
gsxdsm
2026-06-01 02:42:36 -07:00
parent e854d33375
commit 35ce9bd244
10 changed files with 284 additions and 19 deletions

View File

@@ -8,6 +8,7 @@ import {
getTaskHardMergeBlocker,
getTaskMergeBlocker,
isTaskReadyForMerge,
isSharedBranchGroupMemberIntegration,
resolveEffectiveAutoMerge,
resolveEffectiveGroupAutoMerge,
resolveTaskMergeTarget,
@@ -62,6 +63,42 @@ describe("resolveEffectiveGroupAutoMerge", () => {
});
});
describe("isSharedBranchGroupMemberIntegration", () => {
it("returns true for shared members with a resolvable group id", () => {
expect(isSharedBranchGroupMemberIntegration({
branchContext: {
assignmentMode: "shared",
groupId: "BG-1",
source: "planning",
},
})).toBe(true);
});
it("returns false for per-task-derived grouped members", () => {
expect(isSharedBranchGroupMemberIntegration({
branchContext: {
assignmentMode: "per-task-derived",
groupId: "BG-1",
source: "planning",
},
})).toBe(false);
});
it("returns false when no group id is present", () => {
expect(isSharedBranchGroupMemberIntegration({
branchContext: {
assignmentMode: "shared",
groupId: " ",
source: "planning",
},
})).toBe(false);
});
it("returns false when branch context is absent", () => {
expect(isSharedBranchGroupMemberIntegration({ branchContext: undefined })).toBe(false);
});
});
describe("resolveTaskMergeTarget", () => {
it("prefers task baseBranch when present", () => {
expect(resolveTaskMergeTarget({ baseBranch: "release/1.2", branchContext: undefined })).toEqual({

View File

@@ -318,6 +318,7 @@ export {
getTaskHardMergeBlocker,
getTaskCompletionBlocker,
isTaskReadyForMerge,
isSharedBranchGroupMemberIntegration,
resolveEffectiveAutoMerge,
resolveEffectiveGroupAutoMerge,
resolveTaskMergeTarget,

View File

@@ -55,6 +55,19 @@ export function resolveEffectiveGroupAutoMerge(
return group.autoMerge ?? settings.autoMerge;
}
/**
* Shared-branch-group members perform a soft pre-integration step:
* member branch → shared group branch. This path is exempt from the global
* `autoMerge:false` in-review terminal gate so member integration can proceed,
* but shared-branch → default-branch promotion remains separately gated.
*/
export function isSharedBranchGroupMemberIntegration(
task: Pick<Task, "branchContext">,
): boolean {
return task.branchContext?.assignmentMode === "shared"
&& Boolean(task.branchContext.groupId?.trim());
}
export function resolveTaskMergeTarget(
task: Pick<Task, "baseBranch" | "branchContext">,
options: MergeTargetResolverOptions = {},