diff --git a/packages/core/src/__tests__/session-manager.test.ts b/packages/core/src/__tests__/session-manager.test.ts index 39f2382c0..a88ad5cfc 100644 --- a/packages/core/src/__tests__/session-manager.test.ts +++ b/packages/core/src/__tests__/session-manager.test.ts @@ -199,6 +199,20 @@ describe("spawn", () => { expect(session.branch!.replace("feat/", "").length).toBeLessThanOrEqual(60); }); + it("does not leave trailing dash after truncation", async () => { + const sm = createSessionManager({ config, registry: mockRegistry }); + + // Craft input where the 60th char falls on a word boundary (dash) + const session = await sm.spawn({ + projectId: "my-app", + issueId: "ab ".repeat(30), // "ab ab ab ..." → "ab-ab-ab-..." truncated at 60 + }); + + const slug = session.branch!.replace("feat/", ""); + expect(slug).not.toMatch(/-$/); + expect(slug).not.toMatch(/^-/); + }); + it("falls back to sessionId when issueId sanitizes to empty string", async () => { const sm = createSessionManager({ config, registry: mockRegistry }); diff --git a/packages/core/src/session-manager.ts b/packages/core/src/session-manager.ts index e3050024e..cabb6eaa8 100644 --- a/packages/core/src/session-manager.ts +++ b/packages/core/src/session-manager.ts @@ -404,8 +404,8 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager { : id .toLowerCase() .replace(/[^a-z0-9]+/g, "-") - .replace(/^-+|-+$/g, "") - .slice(0, 60); + .slice(0, 60) + .replace(/^-+|-+$/g, ""); branch = `feat/${slug || sessionId}`; } else { branch = `session/${sessionId}`;