diff --git a/AGENTS.md b/AGENTS.md index c033f05..42cdb76 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -101,3 +101,4 @@ Never: - Commit secrets or credentials. - Edit generated output folders unless explicitly requested. - Modify node_modules or lockfiles unless explicitly requested. +- Reintroduce cross-workspace overwrite/delete behavior with any changes. diff --git a/playwright/github-pr-drawer/open-pr-create.spec.ts b/playwright/github-pr-drawer/open-pr-create.spec.ts index 47fd39d..8ed3c74 100644 --- a/playwright/github-pr-drawer/open-pr-create.spec.ts +++ b/playwright/github-pr-drawer/open-pr-create.spec.ts @@ -1116,6 +1116,91 @@ test('Local New workspace always creates a new stored workspace snapshot', async await expect.poll(async () => countLocalRecords()).toBe(initialLocalRecordCount + 1) }) +test('Local New workspace shows forked head immediately when source workspace has prTitle', async ({ + page, +}) => { + const seededWorkspaceId = 'local_seed_title_carryover_guard' + const seededWorkspaceTitle = 'Seed local context title' + + await waitForAppReady(page, `${appEntryPath}`) + + await seedLocalWorkspaceContexts(page, [ + { + id: seededWorkspaceId, + repo: '', + base: 'main', + head: 'feat/component-seeded-title-guard', + prTitle: seededWorkspaceTitle, + prNumber: null, + prContextState: 'inactive', + }, + ]) + + await page.reload() + await waitForAppReady(page, `${appEntryPath}`) + await connectByotWithSingleRepo(page) + await selectWorkspacesRepositoryFilter(page, '__local__') + + await page.getByRole('button', { name: 'New workspace', exact: true }).click() + await expect(page.getByRole('complementary', { name: 'Workspaces' })).toBeHidden() + + let createdWorkspaceId = '' + await expect + .poll(async () => { + const records = await getAllWorkspaceRecords(page) + const createdRecord = records.find(record => { + const id = String(record?.id ?? '').trim() + const repo = String(record?.repo ?? '').trim() + const prTitle = String(record?.prTitle ?? '').trim() + return id !== seededWorkspaceId && !repo && !prTitle + }) + + createdWorkspaceId = String(createdRecord?.id ?? '').trim() + return createdWorkspaceId + }) + .not.toBe('') + + const createdRecords = await getAllWorkspaceRecords(page) + const createdRecord = createdRecords.find(record => { + const id = String(record?.id ?? '').trim() + return id === createdWorkspaceId + }) + + const createdHead = String(createdRecord?.head ?? '').trim() + expect(createdHead).toBeTruthy() + + await page.getByRole('button', { name: 'Workspaces' }).click() + await selectWorkspacesRepositoryFilter(page, '__local__') + + const storedWorkspaceSelect = page.getByRole('combobox', { + name: 'Stored workspace', + }) + const selectedWorkspaceId = String( + await storedWorkspaceSelect.evaluate(element => (element as HTMLSelectElement).value), + ).trim() + expect(selectedWorkspaceId).not.toBe(seededWorkspaceId) + + const selectedLabelText = await storedWorkspaceSelect.evaluate(element => { + const select = element as HTMLSelectElement + return select.selectedOptions.item(0)?.textContent ?? '' + }) + const selectedLabel = String(selectedLabelText ?? '').trim() + + const selectedRecord = createdRecords.find(record => { + const id = String(record?.id ?? '').trim() + return id === selectedWorkspaceId + }) + const selectedHead = String(selectedRecord?.head ?? '').trim() + const selectedPrTitle = String(selectedRecord?.prTitle ?? '').trim() + + expect(selectedPrTitle).toBe('') + expect(selectedHead).toBeTruthy() + expect(selectedLabel).toBe(selectedHead) + expect(selectedLabel).not.toBe(seededWorkspaceTitle) + await expect(page.locator('#workspace-context-status')).toContainText(selectedHead) + await expect(page.locator('#workspace-context-status')).toContainText('local') +}) + test('Non-Local New workspace forks a new repository-scoped workspace when entries exist', async ({ page, }) => { diff --git a/src/app.js b/src/app.js index 725394b..da509f1 100644 --- a/src/app.js +++ b/src/app.js @@ -950,6 +950,10 @@ const { syncActiveWorkspaceRepositoryScope, forkWorkspaceFromCurrentState } = githubPrTitle.value = value } }, + setActiveWorkspacePersistedMetadata: ({ prTitle, head } = {}) => { + activeWorkspacePersistedPrTitle = toNonEmptyWorkspaceText(prTitle) + activeWorkspacePersistedHeadBranch = toNonEmptyWorkspaceText(head) + }, }) editedIndicatorVisibilityController.setRefreshHandlers({ diff --git a/src/modules/app-core/workspace-scope-fork-actions.js b/src/modules/app-core/workspace-scope-fork-actions.js index 0a9d409..d76a9fc 100644 --- a/src/modules/app-core/workspace-scope-fork-actions.js +++ b/src/modules/app-core/workspace-scope-fork-actions.js @@ -26,6 +26,7 @@ export const createWorkspaceScopeForkActions = ({ setWorkspaceScopeMarker, setHeadBranchValue, setPrTitleValue, + setActiveWorkspacePersistedMetadata, }) => { const syncActiveWorkspaceRepositoryScope = async ( repositoryFullName, @@ -172,6 +173,14 @@ export const createWorkspaceScopeForkActions = ({ typeof saved?.createdAt === 'number' && Number.isFinite(saved.createdAt) ? saved.createdAt : now + + if (typeof setActiveWorkspacePersistedMetadata === 'function') { + setActiveWorkspacePersistedMetadata({ + prTitle: '', + head: forkedHeadBranch, + }) + } + setActiveWorkspaceRecordId(savedId) setActiveWorkspaceCreatedAt(savedCreatedAt)