pi-cohort 7.4.3 → 7.4.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.4.4] - 2026-10-22
4
+
5
+ ### Fixed
6
+ - Async run status reads no longer serve a stale `running` state after an atomic `status.json` replacement that lands on the same mtime; the read cache now also compares inode and size.
7
+ - The Tests workflow passes on Windows again: CRLF, POSIX file-mode, ENOTDIR, and SIGUSR2 assumptions in tests are normalized or skipped on win32, and run-dir cleanup retries while detached runners finish writing.
8
+
3
9
  ## [7.4.3] - 2026-10-20
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cohort",
3
- "version": "7.4.3",
3
+ "version": "7.4.4",
4
4
  "description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
5
5
  "author": "Jacek Juraszek",
6
6
  "license": "MIT",
@@ -16,7 +16,9 @@ import type { AgentProgress, AsyncStatus, Details, DisplayItem, ErrorInfo, Singl
16
16
 
17
17
  export { getAgentDir } from "./agent-dir.ts";
18
18
 
19
- const statusCache = new Map<string, { mtime: number; status: AsyncStatus }>();
19
+ // Keyed on inode too: writeAtomicJson renames a fresh file in, and a coarse
20
+ // filesystem clock can give the replacement the same mtime as the original.
21
+ const statusCache = new Map<string, { mtime: number; ino: number; size: number; status: AsyncStatus }>();
20
22
 
21
23
  function getErrorMessage(error: unknown): string {
22
24
  return error instanceof Error ? error.message : String(error);
@@ -35,7 +37,7 @@ function isNotFoundError(error: unknown): boolean {
35
37
  }
36
38
 
37
39
  /**
38
- * Read async job status from disk (with mtime-based caching)
40
+ * Read async job status from disk (with stat-based caching)
39
41
  */
40
42
  export function readStatus(asyncDir: string): AsyncStatus | null {
41
43
  const statusPath = path.join(asyncDir, "status.json");
@@ -51,7 +53,7 @@ export function readStatus(asyncDir: string): AsyncStatus | null {
51
53
  }
52
54
 
53
55
  const cached = statusCache.get(statusPath);
54
- if (cached && cached.mtime === stat.mtimeMs) {
56
+ if (cached && cached.mtime === stat.mtimeMs && cached.ino === stat.ino && cached.size === stat.size) {
55
57
  return cached.status;
56
58
  }
57
59
 
@@ -74,7 +76,7 @@ export function readStatus(asyncDir: string): AsyncStatus | null {
74
76
  });
75
77
  }
76
78
 
77
- statusCache.set(statusPath, { mtime: stat.mtimeMs, status });
79
+ statusCache.set(statusPath, { mtime: stat.mtimeMs, ino: stat.ino, size: stat.size, status });
78
80
  if (statusCache.size > 50) {
79
81
  const firstKey = statusCache.keys().next().value;
80
82
  if (firstKey) statusCache.delete(firstKey);