@sagentlab/navarch-runtime 0.1.18 → 0.1.19

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.
@@ -23,13 +23,19 @@ class GitWorktree {
23
23
  runner;
24
24
  cloneUrl;
25
25
  githubToken;
26
+ taskBranchSuffix;
26
27
  constructor(options) {
27
28
  const projectKey = safePathSegment(options.projectId);
28
29
  const sessionKey = safePathSegment(options.sessionId);
30
+ const taskKey = safePathSegment(options.taskId);
29
31
  this.sessionRoot = node_path_1.default.join(options.workspaceRoot, "sessions", sessionKey);
30
32
  this.worktreePath = node_path_1.default.join(this.sessionRoot, "repo");
31
33
  this.repositoryPath = node_path_1.default.join(options.workspaceRoot, "repositories", `${projectKey}.git`);
32
- this.branch = `navarch/${branchSlug(options)}-${sessionKey.toLowerCase().slice(0, 8)}`;
34
+ // One delivery task owns one remote branch across every retry. A
35
+ // session-scoped branch lets a failed attempt push useful work, then makes
36
+ // the retry start from main and open a second PR for the same task.
37
+ this.taskBranchSuffix = `-${taskKey.toLowerCase()}`;
38
+ this.branch = `navarch/${branchSlug(options)}${this.taskBranchSuffix}`;
33
39
  this.runner = options.runner ?? sandbox_cjs_1.nodeCommandRunner;
34
40
  this.cloneUrl = options.cloneUrl;
35
41
  this.githubToken = options.githubToken;
@@ -75,7 +81,10 @@ class GitWorktree {
75
81
  ], true);
76
82
  }
77
83
  async addSessionWorktree() {
78
- const startRef = await this.resolveStartRef();
84
+ // A failed session may have pushed before it could report completion. The
85
+ // next session must resume that task-owned branch, not fork again from the
86
+ // default branch and create a competing delivery PR.
87
+ const startRef = (await this.resolveTaskBranchRef()) ?? (await this.resolveStartRef());
79
88
  if (!startRef) {
80
89
  // Empty remote (no commits yet): there is nothing to base the session on,
81
90
  // so bootstrap an orphan branch the session can push as the first commit.
@@ -93,6 +102,22 @@ class GitWorktree {
93
102
  startRef,
94
103
  ], false);
95
104
  }
105
+ /** Returns the fetched task-owned remote branch when an earlier attempt pushed it. */
106
+ async resolveTaskBranchRef() {
107
+ const refs = await this.runGit(["--git-dir", this.repositoryPath, "for-each-ref", "--format=%(refname)", "refs/remotes/origin/navarch"], false);
108
+ const matches = refs.stdout
109
+ .split("\n")
110
+ .map((line) => line.trim())
111
+ .filter((ref) => ref.endsWith(this.taskBranchSuffix));
112
+ if (matches.length === 0)
113
+ return null;
114
+ if (matches.length > 1) {
115
+ throw new Error(`Multiple remote branches claim task suffix ${this.taskBranchSuffix}; refusing to fork another delivery branch.`);
116
+ }
117
+ const remoteRef = matches[0];
118
+ this.branch = remoteRef.replace(/^refs\/remotes\/origin\//, "");
119
+ return remoteRef;
120
+ }
96
121
  /**
97
122
  * Resolves the remote-tracking ref new session branches start from, or null
98
123
  * when the remote has no branches at all (a freshly provisioned empty repo).
@@ -211,8 +236,8 @@ async function pathExists(value) {
211
236
  }
212
237
  }
213
238
  /**
214
- * Kebab-case slug for the session branch, derived from the task summary so
215
- * branch names read like `navarch/fix-login-redirect-a1b2c3d4` instead of a
239
+ * Kebab-case slug for the task branch, derived from the task summary so
240
+ * branch names read like `navarch/fix-login-redirect-<task-id>` instead of a
216
241
  * UUID mash. Falls back to the task type, then a task-id prefix, when the
217
242
  * summary yields nothing. Output contains only [a-z0-9-] with no leading or
218
243
  * trailing dash, so it is always a valid git ref component (no `..`, no
@@ -239,7 +264,7 @@ function branchSlug(options) {
239
264
  function safePathSegment(value) {
240
265
  const safe = value.replace(/[^A-Za-z0-9_.-]/g, "-").replace(/^-+|-+$/g, "");
241
266
  if (!safe)
242
- throw new Error("Cannot create a git worktree without a valid project/session identifier.");
267
+ throw new Error("Cannot create a git worktree without a valid identifier.");
243
268
  return safe;
244
269
  }
245
270
  function normalizeCloneUrl(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Navarch machine-side session manager: claims delivery tasks and runs them through Claude Code, Codex, or Gemini CLI.",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",