patchrelay 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "service": "patchrelay",
3
- "version": "0.7.1",
4
- "commit": "c70c0c3117a6",
5
- "builtAt": "2026-03-13T18:54:01.444Z"
3
+ "version": "0.7.2",
4
+ "commit": "ae8c74a3c09a",
5
+ "builtAt": "2026-03-14T07:49:19.447Z"
6
6
  }
@@ -46,7 +46,15 @@ export class ServiceStageRunner {
46
46
  if (!issue) {
47
47
  return;
48
48
  }
49
- const plan = buildStageLaunchPlan(project, issue, desiredStage);
49
+ const existingWorkspace = this.stores.workspaceOwnership.getWorkspaceOwnershipForIssue(item.projectId, item.issueId);
50
+ const defaultPlan = buildStageLaunchPlan(project, issue, desiredStage);
51
+ const plan = existingWorkspace
52
+ ? {
53
+ ...defaultPlan,
54
+ branchName: existingWorkspace.branchName,
55
+ worktreePath: existingWorkspace.worktreePath,
56
+ }
57
+ : defaultPlan;
50
58
  this.feed?.publish({
51
59
  level: "info",
52
60
  kind: "stage",
@@ -73,7 +81,9 @@ export class ServiceStageRunner {
73
81
  let threadLaunch;
74
82
  let turn;
75
83
  try {
76
- await this.worktreeManager.ensureIssueWorktree(project.repoPath, project.worktreeRoot, plan.worktreePath, plan.branchName);
84
+ await this.worktreeManager.ensureIssueWorktree(project.repoPath, project.worktreeRoot, plan.worktreePath, plan.branchName, {
85
+ allowExistingOutsideRoot: existingWorkspace !== undefined,
86
+ });
77
87
  await this.lifecyclePublisher.markStageActive(project, claim.issue, claim.stageRun);
78
88
  threadLaunch = await this.launchStageThread(item.projectId, item.issueId, claim.stageRun.id, plan.worktreePath, issue.issueKey);
79
89
  turn = await this.codex.startTurn({
@@ -6,9 +6,9 @@ export class WorktreeManager {
6
6
  constructor(config) {
7
7
  this.config = config;
8
8
  }
9
- async ensureIssueWorktree(repoPath, worktreeRoot, worktreePath, branchName) {
9
+ async ensureIssueWorktree(repoPath, worktreeRoot, worktreePath, branchName, options) {
10
10
  if (existsSync(worktreePath)) {
11
- await this.assertTrustedExistingWorktree(repoPath, worktreeRoot, worktreePath);
11
+ await this.assertTrustedExistingWorktree(repoPath, worktreeRoot, worktreePath, options);
12
12
  return;
13
13
  }
14
14
  await ensureDir(path.dirname(worktreePath));
@@ -16,7 +16,7 @@ export class WorktreeManager {
16
16
  timeoutMs: 120_000,
17
17
  });
18
18
  }
19
- async assertTrustedExistingWorktree(repoPath, worktreeRoot, worktreePath) {
19
+ async assertTrustedExistingWorktree(repoPath, worktreeRoot, worktreePath, options) {
20
20
  const worktreeStats = lstatSync(worktreePath);
21
21
  if (worktreeStats.isSymbolicLink()) {
22
22
  throw new Error(`Refusing to reuse symlinked worktree path: ${worktreePath}`);
@@ -24,10 +24,12 @@ export class WorktreeManager {
24
24
  if (!worktreeStats.isDirectory()) {
25
25
  throw new Error(`Refusing to reuse non-directory worktree path: ${worktreePath}`);
26
26
  }
27
- const resolvedRoot = realpathSync(worktreeRoot);
28
27
  const resolvedWorktree = realpathSync(worktreePath);
29
- if (!isPathWithinRoot(resolvedRoot, resolvedWorktree)) {
30
- throw new Error(`Refusing to reuse worktree outside configured root: ${worktreePath}`);
28
+ if (!options?.allowExistingOutsideRoot) {
29
+ const resolvedRoot = realpathSync(worktreeRoot);
30
+ if (!isPathWithinRoot(resolvedRoot, resolvedWorktree)) {
31
+ throw new Error(`Refusing to reuse worktree outside configured root: ${worktreePath}`);
32
+ }
31
33
  }
32
34
  const listedWorktrees = await this.listRegisteredWorktrees(repoPath);
33
35
  if (!listedWorktrees.has(resolvedWorktree)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchrelay",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {