repo-harness 0.24.2 → 0.24.3

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.
package/README.md CHANGED
@@ -585,8 +585,8 @@ repo-harness commit scripts or hooks unless that repo adopts the same policy.
585
585
 
586
586
  ## Current Release
587
587
 
588
- - npm package: `repo-harness@0.24.2`
589
- - Generated workflow stamp: `repo-harness@0.24.2+template@0.24.2`
588
+ - npm package: `repo-harness@0.24.3`
589
+ - Generated workflow stamp: `repo-harness@0.24.3+template@0.24.3`
590
590
  - GitHub repository: `Ancienttwo/repo-harness`
591
591
  - Release notes and history: [`docs/CHANGELOG.md`](docs/CHANGELOG.md)
592
592
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.24.2",
3
- "templateVersion": "0.24.2",
2
+ "version": "0.24.3",
3
+ "templateVersion": "0.24.3",
4
4
  "skillName": "repo-harness",
5
5
  "contractId": "tasks-first-harness-v1",
6
6
  "compatibility": {
@@ -3220,6 +3220,8 @@ function ownerLiveness(pid, startTime) {
3220
3220
  if (error.code === "ESRCH")
3221
3221
  return "dead";
3222
3222
  }
3223
+ if (startTime === null)
3224
+ return "unverified";
3223
3225
  if (lastLiveOwner !== null && lastLiveOwner.pid === pid && lastLiveOwner.startTime === startTime && Date.now() - lastLiveOwner.checkedAt < LIVE_OWNER_RECHECK_MS)
3224
3226
  return "live";
3225
3227
  let observed;
@@ -9634,8 +9636,8 @@ import { readFileSync as readFileSync17 } from "fs";
9634
9636
  import { join as join27 } from "path";
9635
9637
  var PACKAGE_JSON_URL = new URL("../../../package.json", import.meta.url);
9636
9638
  function runningHarnessVersion() {
9637
- if ("0.24.2".length > 0) {
9638
- return "0.24.2";
9639
+ if ("0.24.3".length > 0) {
9640
+ return "0.24.3";
9639
9641
  }
9640
9642
  const manifest = JSON.parse(readFileSync17(PACKAGE_JSON_URL, "utf-8"));
9641
9643
  if (typeof manifest.version !== "string" || manifest.version.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repo-harness",
3
- "version": "0.24.2",
3
+ "version": "0.24.3",
4
4
  "description": "Installs, migrates, audits, and repairs repo-local agentic development harnesses",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -90,15 +90,22 @@ export interface ExclusiveDirectoryLockHandle {
90
90
  export interface ExclusiveDirectoryLockOwner {
91
91
  readonly lockPath: string;
92
92
  readonly pid: number;
93
- /** OS process start time of the owner; see readProcessStartTime. */
94
- readonly startTime: string;
93
+ /**
94
+ * OS process start time of the owner; see readProcessStartTime. It is null
95
+ * when the record has none: records published before start times were
96
+ * recorded (#934), such as the lock a 0.24.1 parent holds during `update`.
97
+ * Without it, a running owner is never proven live, and the record is never
98
+ * reclaimed.
99
+ */
100
+ readonly startTime: string | null;
95
101
  readonly token: string;
96
102
  }
97
103
 
98
104
  /**
99
105
  * `dead` means no process now runs as the recorded owner: the PID is free, or
100
106
  * a process with a different start time has it. `unverified` means the PID is
101
- * in use but the process table could not be read.
107
+ * in use but the process table could not be read, or the record has no start
108
+ * time to compare.
102
109
  */
103
110
  export type ExclusiveDirectoryLockOwnerLiveness = 'live' | 'dead' | 'unverified';
104
111
 
@@ -142,12 +149,14 @@ function ownerTokenFromFileName(entry: string): { readonly token: string; readon
142
149
 
143
150
  let lastLiveOwner: { readonly pid: number; readonly startTime: string; readonly checkedAt: number } | null = null;
144
151
 
145
- function ownerLiveness(pid: number, startTime: string): ExclusiveDirectoryLockOwnerLiveness {
152
+ function ownerLiveness(pid: number, startTime: string | null): ExclusiveDirectoryLockOwnerLiveness {
146
153
  try {
147
154
  process.kill(pid, 0);
148
155
  } catch (error) {
149
156
  if ((error as NodeJS.ErrnoException).code === 'ESRCH') return 'dead';
150
157
  }
158
+ // Without a start time, a live PID may be a reuse, so it is not proven live.
159
+ if (startTime === null) return 'unverified';
151
160
  // Waiters poll every 10 ms. Reuse of a recently verified owner's PID needs
152
161
  // that owner to exit first, so the table read runs at most once per interval.
153
162
  if (lastLiveOwner !== null
@@ -166,7 +175,7 @@ function ownerLiveness(pid: number, startTime: string): ExclusiveDirectoryLockOw
166
175
  }
167
176
 
168
177
  /** Only a proven dead owner is stale; an unverified owner keeps the lock. */
169
- function ownerIsAlive(pid: number, startTime: string): boolean {
178
+ function ownerIsAlive(pid: number, startTime: string | null): boolean {
170
179
  return ownerLiveness(pid, startTime) !== 'dead';
171
180
  }
172
181
 
@@ -342,7 +351,7 @@ export function readExclusiveDirectoryLockOwner(
342
351
  return null;
343
352
  }
344
353
  const startTime = recordedStartTime(parsed);
345
- if (parsed.pid !== owner.pid || parsed.token !== owner.token || startTime === null) return null;
354
+ if (parsed.pid !== owner.pid || parsed.token !== owner.token) return null;
346
355
  assertLockAncestors(location);
347
356
  const currentDirectory = lstatSync(location.lockPath);
348
357
  const currentOwner = lstatSync(ownerPath);