@sagentlab/navarch-runtime 0.1.60 → 0.1.61

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.
@@ -37,6 +37,8 @@ class GitWorktree {
37
37
  sessionTokenMarker;
38
38
  hostedCheckpointRef;
39
39
  hostedCheckpointLocalRef;
40
+ /** Set when the resumed remote branch had lost its ownership commit and must be re-marked. */
41
+ adoptedUnmarkedBranch = false;
40
42
  constructor(options) {
41
43
  const projectKey = safePathSegment(options.projectId);
42
44
  const sessionKey = safePathSegment(options.sessionId);
@@ -185,7 +187,9 @@ class GitWorktree {
185
187
  this.worktreePath,
186
188
  startRef,
187
189
  ], false);
188
- if (!taskRef && !staleTip)
190
+ // An adopted branch gets its marker back, or the pre-push guard refuses
191
+ // every delivery push and the next retry is stuck again.
192
+ if ((!taskRef && !staleTip) || this.adoptedUnmarkedBranch)
189
193
  await this.createTaskOwnershipCommit();
190
194
  if (checkpoint)
191
195
  await this.applyHostedCheckpoint(checkpoint);
@@ -293,6 +297,7 @@ class GitWorktree {
293
297
  if (tip.code !== 0 || !/^[0-9a-f]{40,64}$/i.test(staleSha))
294
298
  return null;
295
299
  const owned = this.branch.endsWith(this.legacyTaskBranchSuffix) ||
300
+ this.adoptedUnmarkedBranch ||
296
301
  (await this.hasTaskOwnershipMarker(localRef));
297
302
  if (!owned) {
298
303
  throw new Error(`Local branch ${this.branch} already exists in the cached repository but does not ` +
@@ -360,6 +365,9 @@ class GitWorktree {
360
365
  const matches = [...ownedShortMatches, ...legacyMatches];
361
366
  if (matches.length === 0) {
362
367
  if (shortMatches.length > 0) {
368
+ const adopted = await this.adoptUnmarkedOwnBranch(shortMatches);
369
+ if (adopted)
370
+ return adopted;
363
371
  throw new Error(`Remote branch ${shortMatches[0]} shares task suffix ${this.taskBranchSuffix} but does not ` +
364
372
  `contain ownership marker ${this.taskOwnershipTrailer}; refusing to resume or fork.`);
365
373
  }
@@ -372,6 +380,36 @@ class GitWorktree {
372
380
  this.branch = remoteRef.replace(/^refs\/remotes\/origin\//, "");
373
381
  return remoteRef;
374
382
  }
383
+ /**
384
+ * An agent that amends, resets, or rebases away the empty claim commit and
385
+ * pushes leaves this task's branch without its marker, and every retry then
386
+ * refused it (carova #10). The branch is still ours when it carries exactly
387
+ * the name this task allocates (slug and suffix) and none of its own commits
388
+ * claim another task; anything else stays refused.
389
+ */
390
+ async adoptUnmarkedOwnBranch(shortMatches) {
391
+ const ownRef = `refs/remotes/origin/${this.branch}`;
392
+ if (shortMatches.length !== 1 || shortMatches[0] !== ownRef)
393
+ return null;
394
+ const base = await this.resolveStartRef().catch(() => null);
395
+ if (!base)
396
+ return null;
397
+ const log = await this.runner.run("git", [
398
+ "--git-dir",
399
+ this.repositoryPath,
400
+ "log",
401
+ "--format=%B",
402
+ ownRef,
403
+ "--not",
404
+ base,
405
+ ]);
406
+ if (log.code !== 0)
407
+ return null;
408
+ if (log.stdout.split("\n").some((line) => /^Navarch-Task-ID:/i.test(line.trim())))
409
+ return null;
410
+ this.adoptedUnmarkedBranch = true;
411
+ return ownRef;
412
+ }
375
413
  /** Short branch names are ambiguous without a full task-ID marker in their history. */
376
414
  async hasTaskOwnershipMarker(ref) {
377
415
  const log = await this.runGit([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sagentlab/navarch-runtime",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "description": "Navarch machine-side session manager: claims delivery tasks and runs them through local coding agents.",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",