fix(worktree): gate sessions on bootstrap readiness (#1762)

Co-authored-by: Leonid Skorobogatyy <bash@opencode.itc.local>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
bashrusakh
2026-06-26 12:16:42 +03:00
committed by GitHub
co-authored by Leonid Skorobogatyy Bohdan Triapitsyn
parent 03e6f789a4
commit 8c1a24089d
20 changed files with 252 additions and 14 deletions
@@ -1124,6 +1124,8 @@ export const settingsDict = {
'settings.openchamber.worktrees.setup.commandPlaceholder': 'e.g., bun install',
'settings.openchamber.worktrees.setup.removeCommandAria': 'Remove command',
'settings.openchamber.worktrees.setup.addCommand': 'Add command',
'settings.openchamber.worktrees.setup.waitForCommands': 'Wait for setup commands before creating or sending a session',
'settings.openchamber.worktrees.setup.waitForCommandsAria': 'Wait for Worktree setup commands before creating or sending a session',
'settings.openchamber.worktrees.list.title': 'Existing worktrees',
'settings.openchamber.worktrees.list.tooltip': 'Worktrees live outside the repo (OpenCode-managed). Deleting a worktree also removes linked sessions.',
'settings.openchamber.worktrees.list.loading': 'Loading worktrees...',
@@ -1091,6 +1091,8 @@ export const settingsDict = {
"settings.openchamber.worktrees.setup.commandPlaceholder": "ej.: bun install",
"settings.openchamber.worktrees.setup.removeCommandAria": "Eliminar comando",
"settings.openchamber.worktrees.setup.addCommand": "Añadir comando",
"settings.openchamber.worktrees.setup.waitForCommands": "Esperar a que terminen los comandos de configuración antes de crear o enviar una sesión",
"settings.openchamber.worktrees.setup.waitForCommandsAria": "Esperar los comandos de configuración de Worktree antes de crear o enviar una sesión",
"settings.openchamber.worktrees.list.title": "Worktrees existentes",
"settings.openchamber.worktrees.list.tooltip": "Los worktrees existen fuera del repositorio (gestionados por OpenCode). Eliminar un worktree también elimina las sesiones vinculadas.",
"settings.openchamber.worktrees.list.loading": "Cargando worktrees...",
@@ -1080,6 +1080,8 @@ export const settingsDict = {
'settings.openchamber.worktrees.setup.commandPlaceholder': 'p. ex. bun install',
'settings.openchamber.worktrees.setup.removeCommandAria': 'Supprimer la commande',
'settings.openchamber.worktrees.setup.addCommand': 'Ajouter une commande',
'settings.openchamber.worktrees.setup.waitForCommands': 'Attendre la fin des commandes de configuration avant de créer ou d\'envoyer une session',
'settings.openchamber.worktrees.setup.waitForCommandsAria': 'Attendre les commandes de configuration Worktree avant de créer ou d\'envoyer une session',
'settings.openchamber.worktrees.list.title': 'Worktrees existants',
'settings.openchamber.worktrees.list.tooltip': 'Les worktrees vivent en dehors du dépôt (gérés par OpenCode). La suppression d\'un worktree supprime également les sessions liées.',
'settings.openchamber.worktrees.list.loading': 'Chargement des worktrees...',
@@ -1091,6 +1091,8 @@ export const settingsDict = {
'settings.openchamber.worktrees.setup.commandPlaceholder': '예: bun install',
'settings.openchamber.worktrees.setup.removeCommandAria': '명령어 제거',
'settings.openchamber.worktrees.setup.addCommand': '명령어 추가',
'settings.openchamber.worktrees.setup.waitForCommands': '세션을 만들거나 보내기 전에 설정 명령이 끝날 때까지 기다리기',
'settings.openchamber.worktrees.setup.waitForCommandsAria': '세션을 만들거나 보내기 전에 Worktree 설정 명령이 끝날 때까지 기다리기',
'settings.openchamber.worktrees.list.title': '기존 worktree',
'settings.openchamber.worktrees.list.tooltip': 'Worktree는 repo 외부에 있습니다(OpenCode 관리). worktree를 삭제하면 연결된 세션도 제거됩니다.',
'settings.openchamber.worktrees.list.loading': 'worktree 로딩 중...',
@@ -1070,6 +1070,8 @@ export const settingsDict = {
'settings.openchamber.worktrees.setup.loading': 'Ładowanie...',
'settings.openchamber.worktrees.setup.removeCommandAria': 'Usuń polecenie',
'settings.openchamber.worktrees.setup.title': 'Polecenia konfiguracji',
'settings.openchamber.worktrees.setup.waitForCommands': 'Czekaj na zakończenie poleceń konfiguracji przed utworzeniem lub wysłaniem sesji',
'settings.openchamber.worktrees.setup.waitForCommandsAria': 'Czekaj na polecenia konfiguracji Worktree przed utworzeniem lub wysłaniem sesji',
'settings.openchamber.worktrees.setup.tooltipPrefix': 'Uruchamiaj automatycznie w nowym katalogu worktree po jego utworzeniu. Użyj',
'settings.openchamber.worktrees.setup.tooltipSuffix': 'dla katalogu głównego projektu.',
'settings.openchamber.worktrees.state.gitOnly': 'Ustawienia worktree są dostępne tylko dla repozytoriów Git.',
@@ -1091,6 +1091,8 @@ export const settingsDict = {
"settings.openchamber.worktrees.setup.commandPlaceholder": "ej.: bun install",
"settings.openchamber.worktrees.setup.removeCommandAria": "Excluir comando",
"settings.openchamber.worktrees.setup.addCommand": "Adicionar comando",
"settings.openchamber.worktrees.setup.waitForCommands": "Aguardar os comandos de configuração antes de criar ou enviar uma sessão",
"settings.openchamber.worktrees.setup.waitForCommandsAria": "Aguardar os comandos de configuração do Worktree antes de criar ou enviar uma sessão",
"settings.openchamber.worktrees.list.title": "Worktrees existentes",
"settings.openchamber.worktrees.list.tooltip": "Os worktrees existem fuera do repositório (gerenciados por OpenCode). Excluir um worktree também remove sessões vinculadas.",
"settings.openchamber.worktrees.list.loading": "Carregando worktrees...",
@@ -1091,6 +1091,8 @@ export const settingsDict = {
"settings.openchamber.worktrees.setup.commandPlaceholder": "наприклад, bun install",
"settings.openchamber.worktrees.setup.removeCommandAria": "Видалити команду",
"settings.openchamber.worktrees.setup.addCommand": "Додати команду",
"settings.openchamber.worktrees.setup.waitForCommands": "Чекати завершення команд налаштування перед створенням або надсиланням сесії",
"settings.openchamber.worktrees.setup.waitForCommandsAria": "Чекати завершення команд налаштування Worktree перед створенням або надсиланням сесії",
"settings.openchamber.worktrees.list.title": "Наявні worktree",
"settings.openchamber.worktrees.list.tooltip": "Worktree розташовані поза репозиторієм і керуються OpenCode. Видалення worktree також видаляє пов’язані сесії.",
"settings.openchamber.worktrees.list.loading": "Завантаження worktree...",
@@ -1091,6 +1091,8 @@ export const settingsDict = {
'settings.openchamber.worktrees.setup.commandPlaceholder': '例如:bun install',
'settings.openchamber.worktrees.setup.removeCommandAria': '移除命令',
'settings.openchamber.worktrees.setup.addCommand': '添加命令',
'settings.openchamber.worktrees.setup.waitForCommands': '创建或发送会话前等待初始化命令完成',
'settings.openchamber.worktrees.setup.waitForCommandsAria': '创建或发送会话前等待 Worktree 初始化命令完成',
'settings.openchamber.worktrees.list.title': '现有工作树',
'settings.openchamber.worktrees.list.tooltip': '工作树位于仓库外部(由 OpenCode 管理)。删除工作树也会删除关联会话。',
'settings.openchamber.worktrees.list.loading': '正在加载工作树...',
@@ -1012,6 +1012,8 @@
'settings.openchamber.worktrees.setup.commandPlaceholder': '例如:bun install',
'settings.openchamber.worktrees.setup.removeCommandAria': '移除命令',
'settings.openchamber.worktrees.setup.addCommand': '新增命令',
'settings.openchamber.worktrees.setup.waitForCommands': '建立或傳送工作階段前等待設定命令完成',
'settings.openchamber.worktrees.setup.waitForCommandsAria': '建立或傳送工作階段前等待 Worktree 設定命令完成',
'settings.openchamber.worktrees.list.title': '現有 worktree',
'settings.openchamber.worktrees.list.tooltip': 'Worktree 位於儲存庫外部(由 OpenCode 管理)。刪除 worktree 也會刪除關聯工作階段。',
'settings.openchamber.worktrees.list.loading': '正在載入 worktree...',
+10
View File
@@ -33,6 +33,7 @@ function getRuntimeFilesAPI(): FilesAPI | null {
export interface OpenChamberConfig {
projectPath?: string;
'setup-worktree'?: string[];
'setup-worktree-wait'?: boolean;
projectNotes?: string;
projectTodos?: OpenChamberProjectTodoItem[];
projectPlanFiles?: OpenChamberProjectPlanFileLink[];
@@ -699,6 +700,15 @@ export async function saveWorktreeSetupCommands(project: ProjectRef, commands: s
return updateOpenChamberConfig(project, { 'setup-worktree': filtered });
}
export async function getWorktreeSetupWaitEnabled(project: ProjectRef): Promise<boolean> {
const config = await readOpenChamberConfig(project);
return config?.['setup-worktree-wait'] === true;
}
export async function saveWorktreeSetupWaitEnabled(project: ProjectRef, enabled: boolean): Promise<boolean> {
return updateOpenChamberConfig(project, { 'setup-worktree-wait': enabled });
}
/**
* Get this project's pinned draft welcome starters.
*/
+6
View File
@@ -373,6 +373,12 @@ export const SETTINGS_SEARCH_ITEMS: readonly SettingsSearchItem[] = [
titleKey: 'settings.projects.page.section.worktree',
keywords: ['worktree', 'branch', 'repository'],
},
{
id: 'projects.worktree.setup.wait',
page: 'projects',
titleKey: 'settings.openchamber.worktrees.setup.waitForCommands',
keywords: ['worktree', 'setup commands', 'bootstrap', 'wait'],
},
{
id: 'remote-instances.client-auth',
page: 'remote-instances',
+12 -1
View File
@@ -14,7 +14,7 @@ import { checkIsGitRepository, previewGitWorktree } from '@/lib/gitApi';
import { generateBranchName } from '@/lib/git/branchNameGenerator';
import { parseModelIdentifier } from '@/lib/modelIdentifier';
import { getRootBranch } from '@/lib/worktrees/worktreeStatus';
import { getWorktreeSetupCommands } from '@/lib/openchamberConfig';
import { getWorktreeSetupCommands, getWorktreeSetupWaitEnabled } from '@/lib/openchamberConfig';
import {
removeProjectWorktree,
type ProjectRef,
@@ -25,9 +25,16 @@ import {
rejectPendingDraftWorktreeRequest,
resolvePendingDraftWorktreeRequest,
} from '@/lib/worktrees/pendingDraftWorktree';
import { waitForWorktreeBootstrap } from '@/lib/worktrees/worktreeBootstrap';
const normalizePath = (value: string): string => value.replace(/\\/g, '/').replace(/\/+$/, '') || value;
const waitForWorktreeBootstrapIfEnabled = async (project: ProjectRef, directory: string): Promise<void> => {
if (await getWorktreeSetupWaitEnabled(project)) {
await waitForWorktreeBootstrap(directory);
}
};
const resolveProjectRef = (directory: string): ProjectRef | null => {
const normalized = normalizePath(directory);
const projects = useProjectsStore.getState().projects;
@@ -417,6 +424,8 @@ export async function createWorktreeSessionForBranch(
kind,
};
await waitForWorktreeBootstrapIfEnabled(projectRef, metadata.path);
// Create the session
const sessionStore = useSessionUIStore.getState();
const session = await sessionStore.createSession(undefined, metadata.path);
@@ -520,6 +529,8 @@ export async function createWorktreeSessionForNewBranch(
kind,
};
await waitForWorktreeBootstrapIfEnabled(projectRef, metadata.path);
const sessionStore = useSessionUIStore.getState();
const session = await sessionStore.createSession(undefined, metadata.path);
if (!session) {