@productbrain/cli 0.1.0-beta.4957 → 0.1.0-beta.4965

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,25 +1,83 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ const HOUR_MS = 60 * 60 * 1000;
3
+ const DAY_MS = 24 * HOUR_MS;
4
+ const NOW_MS = new Date('2026-08-25T12:00:00.000Z').getTime();
5
+ const CONFIG_DIR = '/mock-home/.config/productbrain';
2
6
  const files = new Map();
7
+ const statErrors = new Set();
8
+ const unlinkErrors = new Set();
9
+ let onUnlink = null;
10
+ let nextIno = 1;
11
+ function markerPath(name) {
12
+ return `${CONFIG_DIR}/${name}`;
13
+ }
14
+ function putMarker(name, ageMs = 0) {
15
+ const path = markerPath(name);
16
+ files.set(path, { mtimeMs: Date.now() - ageMs, ino: nextIno++ });
17
+ return path;
18
+ }
3
19
  vi.mock('node:os', () => ({ homedir: () => '/mock-home' }));
4
20
  vi.mock('node:fs', () => ({
5
21
  existsSync: (p) => files.has(p),
6
22
  mkdirSync: vi.fn(),
7
- statSync: () => ({ mtimeMs: Date.now() }),
23
+ statSync: (p) => {
24
+ if (statErrors.has(p))
25
+ throw new Error('stat race');
26
+ const file = files.get(p);
27
+ if (!file)
28
+ throw new Error('ENOENT');
29
+ return { mtimeMs: file.mtimeMs, dev: 1, ino: file.ino };
30
+ },
8
31
  readdirSync: () => [...files.keys()].map((p) => p.split('/').pop()),
9
- unlinkSync: (p) => { files.delete(p); },
10
- openSync: (p) => { if (files.has(p)) {
11
- const e = new Error('EEXIST');
12
- e.code = 'EEXIST';
13
- throw e;
14
- } files.set(p, ''); return 1; },
32
+ unlinkSync: (p) => {
33
+ if (unlinkErrors.has(p))
34
+ throw new Error('unlink race');
35
+ onUnlink?.(p);
36
+ files.delete(p);
37
+ },
38
+ utimesSync: (p, _atime, mtime) => {
39
+ const file = files.get(p);
40
+ if (!file)
41
+ throw new Error('ENOENT');
42
+ file.mtimeMs = mtime.getTime();
43
+ },
44
+ linkSync: (source, destination) => {
45
+ const file = files.get(source);
46
+ if (!file)
47
+ throw new Error('ENOENT');
48
+ if (files.has(destination))
49
+ throw new Error('EEXIST');
50
+ files.set(destination, file);
51
+ },
52
+ openSync: (p) => {
53
+ if (files.has(p)) {
54
+ const error = new Error('EEXIST');
55
+ error.code = 'EEXIST';
56
+ throw error;
57
+ }
58
+ files.set(p, { mtimeMs: Date.now(), ino: nextIno++ });
59
+ return 1;
60
+ },
15
61
  closeSync: vi.fn(),
16
62
  }));
17
63
  vi.mock('../lib/session.js', () => ({ readSession: vi.fn(() => ({ sessionId: 'sess-abc' })) }));
18
64
  import { claimNotice } from '../lib/notice-marker.js';
19
65
  import { readSession } from '../lib/session.js';
20
66
  describe('notice-marker', () => {
21
- beforeEach(() => files.clear());
22
- afterEach(() => vi.clearAllMocks());
67
+ beforeEach(() => {
68
+ vi.useFakeTimers();
69
+ vi.setSystemTime(NOW_MS);
70
+ files.clear();
71
+ nextIno = 1;
72
+ statErrors.clear();
73
+ unlinkErrors.clear();
74
+ onUnlink = null;
75
+ vi.mocked(readSession).mockReturnValue({ sessionId: 'sess-abc' });
76
+ });
77
+ afterEach(() => {
78
+ vi.useRealTimers();
79
+ vi.clearAllMocks();
80
+ });
23
81
  it('first claim in a session wins, second is silent', () => {
24
82
  expect(claimNotice('agent')).toBe(true);
25
83
  expect(claimNotice('agent')).toBe(false);
@@ -28,10 +86,87 @@ describe('notice-marker', () => {
28
86
  expect(claimNotice('agent')).toBe(true);
29
87
  expect(claimNotice('human')).toBe(true);
30
88
  });
89
+ it('prunes stale markers before a fresh current marker loses the atomic claim', () => {
90
+ const current = putMarker('notice-session-sess-abc');
91
+ const staleSession = putMarker('notice-session-old', 8 * HOUR_MS + 1);
92
+ const staleDaily = putMarker('notice-agent-daily-old', 7 * DAY_MS + 1);
93
+ const freshDaily = putMarker('notice-human-current', 7 * DAY_MS);
94
+ const unrelated = putMarker('profiles.json', 30 * DAY_MS);
95
+ expect(claimNotice('agent')).toBe(false);
96
+ expect(files.has(current)).toBe(true);
97
+ expect(files.has(staleSession)).toBe(false);
98
+ expect(files.has(staleDaily)).toBe(false);
99
+ expect(files.has(freshDaily)).toBe(true);
100
+ expect(files.has(unrelated)).toBe(true);
101
+ });
102
+ it('keeps daily markers that are older than a session but younger than seven days', () => {
103
+ const staleSession = putMarker('notice-session-old', 8 * HOUR_MS + 1);
104
+ const sameAgeDaily = putMarker('notice-agent-daily-recent', 8 * HOUR_MS + 1);
105
+ expect(claimNotice('agent')).toBe(true);
106
+ expect(files.has(staleSession)).toBe(false);
107
+ expect(files.has(sameAgeDaily)).toBe(true);
108
+ });
109
+ it('keeps a stale marker for a renewed current session and refreshes its heartbeat', () => {
110
+ const current = putMarker('notice-session-sess-abc', 8 * HOUR_MS + 1);
111
+ expect(claimNotice('agent')).toBe(false);
112
+ expect(files.get(current)?.mtimeMs).toBe(NOW_MS);
113
+ });
114
+ it('locks stale eviction so a concurrent claim cannot replace the marker before unlink', () => {
115
+ const current = putMarker('notice-session-sess-abc');
116
+ const stale = putMarker('notice-session-old', 9 * HOUR_MS);
117
+ let concurrentClaim = null;
118
+ onUnlink = (path) => {
119
+ if (path !== stale)
120
+ return;
121
+ vi.mocked(readSession).mockReturnValue({ sessionId: 'old' });
122
+ concurrentClaim = claimNotice('agent');
123
+ vi.mocked(readSession).mockReturnValue({ sessionId: 'sess-abc' });
124
+ };
125
+ expect(claimNotice('agent')).toBe(false);
126
+ expect(concurrentClaim).toBe(false);
127
+ expect(files.has(stale)).toBe(false);
128
+ expect(files.has(current)).toBe(true);
129
+ });
130
+ it('reclaims abandoned marker locks and stale recovery residue', () => {
131
+ const currentLock = putMarker('.notice-marker-lock-notice-session-sess-abc', HOUR_MS);
132
+ const orphanLock = putMarker('.notice-marker-lock-notice-session-old', HOUR_MS);
133
+ const orphanRecovery = putMarker('.notice-marker-lock-notice-session-old.reclaim', HOUR_MS);
134
+ const orphanPin = putMarker('.notice-marker-lock-notice-session-old.reclaim.target', HOUR_MS);
135
+ expect(claimNotice('agent')).toBe(true);
136
+ expect(files.has(currentLock)).toBe(false);
137
+ expect(files.has(orphanLock)).toBe(false);
138
+ expect(files.has(orphanRecovery)).toBe(false);
139
+ expect(files.has(orphanPin)).toBe(false);
140
+ expect(files.has(markerPath('notice-session-sess-abc'))).toBe(true);
141
+ });
142
+ it('continues pruning after per-file stat and unlink failures', () => {
143
+ const current = putMarker('notice-session-sess-abc');
144
+ const statRace = putMarker('notice-session-stat-race', 9 * HOUR_MS);
145
+ const unlinkRace = putMarker('notice-session-unlink-race', 9 * HOUR_MS);
146
+ const removable = putMarker('notice-session-removable', 9 * HOUR_MS);
147
+ statErrors.add(statRace);
148
+ unlinkErrors.add(unlinkRace);
149
+ expect(claimNotice('agent')).toBe(false);
150
+ expect(files.has(current)).toBe(true);
151
+ expect(files.has(statRace)).toBe(true);
152
+ expect(files.has(unlinkRace)).toBe(true);
153
+ expect(files.has(removable)).toBe(false);
154
+ });
31
155
  });
32
156
  describe('notice-marker — null session (daily fallback key)', () => {
33
- beforeEach(() => files.clear());
34
- afterEach(() => vi.clearAllMocks());
157
+ beforeEach(() => {
158
+ vi.useFakeTimers();
159
+ vi.setSystemTime(NOW_MS);
160
+ files.clear();
161
+ nextIno = 1;
162
+ statErrors.clear();
163
+ unlinkErrors.clear();
164
+ onUnlink = null;
165
+ });
166
+ afterEach(() => {
167
+ vi.useRealTimers();
168
+ vi.clearAllMocks();
169
+ });
35
170
  it('missing sessionId → daily fallback key: first call true, second false same UTC day', () => {
36
171
  vi.mocked(readSession).mockReturnValue(null);
37
172
  expect(claimNotice('agent')).toBe(true);
@@ -1 +1 @@
1
- {"version":3,"file":"notice-marker.test.js","sourceRoot":"","sources":["../../src/__tests__/notice-marker.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"notice-marker.test.js","sourceRoot":"","sources":["../../src/__tests__/notice-marker.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,MAAM,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/B,MAAM,MAAM,GAAG,EAAE,GAAG,OAAO,CAAC;AAC5B,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,OAAO,EAAE,CAAC;AAC9D,MAAM,UAAU,GAAG,iCAAiC,CAAC;AAErD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA4C,CAAC;AAClE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AACrC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;AACvC,IAAI,QAAQ,GAAoC,IAAI,CAAC;AACrD,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,GAAG,UAAU,IAAI,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,KAAK,GAAG,CAAC;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;AAC5D,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACxB,UAAU,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;IAClB,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE;QACtB,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;IAC1D,CAAC;IACD,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IACnE,UAAU,EAAE,CAAC,CAAS,EAAE,EAAE;QACxB,IAAI,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;QACxD,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACd,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,UAAU,EAAE,CAAC,CAAS,EAAE,MAAY,EAAE,KAAW,EAAE,EAAE;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IACD,QAAQ,EAAE,CAAC,MAAc,EAAE,WAAmB,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtD,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC;IACD,QAAQ,EAAE,CAAC,CAAS,EAAE,EAAE;QACtB,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,CAA0B,CAAC;YAC3D,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC;YACtB,MAAM,KAAK,CAAC;QACd,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;CACnB,CAAC,CAAC,CAAC;AACJ,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEhG,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACzB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,CAAC;QACZ,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,YAAY,CAAC,KAAK,EAAE,CAAC;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,UAAU,EAAoC,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,OAAO,GAAG,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,YAAY,GAAG,SAAS,CAAC,oBAAoB,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,SAAS,CAAC,wBAAwB,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;QACvE,MAAM,UAAU,GAAG,SAAS,CAAC,sBAAsB,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,SAAS,CAAC,eAAe,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC;QAE1D,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,YAAY,GAAG,SAAS,CAAC,oBAAoB,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC;QACtE,MAAM,YAAY,GAAG,SAAS,CAAC,2BAA2B,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC;QAE7E,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,OAAO,GAAG,SAAS,CAAC,yBAAyB,EAAE,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC;QAEtE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,GAAG,EAAE;QAC5F,MAAM,OAAO,GAAG,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,SAAS,CAAC,oBAAoB,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QAC3D,IAAI,eAAe,GAAmB,IAAI,CAAC;QAC3C,QAAQ,GAAG,CAAC,IAAI,EAAE,EAAE;YAClB,IAAI,IAAI,KAAK,KAAK;gBAAE,OAAO;YAC3B,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAoC,CAAC,CAAC;YAC/F,eAAe,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YACvC,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,EAAE,SAAS,EAAE,UAAU,EAAoC,CAAC,CAAC;QACtG,CAAC,CAAC;QAEF,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,WAAW,GAAG,SAAS,CAAC,6CAA6C,EAAE,OAAO,CAAC,CAAC;QACtF,MAAM,UAAU,GAAG,SAAS,CAAC,wCAAwC,EAAE,OAAO,CAAC,CAAC;QAChF,MAAM,cAAc,GAAG,SAAS,CAAC,gDAAgD,EAAE,OAAO,CAAC,CAAC;QAC5F,MAAM,SAAS,GAAG,SAAS,CAAC,uDAAuD,EAAE,OAAO,CAAC,CAAC;QAE9F,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,OAAO,GAAG,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,SAAS,CAAC,0BAA0B,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QACpE,MAAM,UAAU,GAAG,SAAS,CAAC,4BAA4B,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QACxE,MAAM,SAAS,GAAG,SAAS,CAAC,0BAA0B,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC;QACrE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzB,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAE7B,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mDAAmD,EAAE,GAAG,EAAE;IACjE,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACzB,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,GAAG,CAAC,CAAC;QACZ,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,YAAY,CAAC,KAAK,EAAE,CAAC;QACrB,QAAQ,GAAG,IAAI,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oFAAoF,EAAE,GAAG,EAAE;QAC5F,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"notice-marker.d.ts","sourceRoot":"","sources":["../../src/lib/notice-marker.ts"],"names":[],"mappings":"AAwCA,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAU/D"}
1
+ {"version":3,"file":"notice-marker.d.ts","sourceRoot":"","sources":["../../src/lib/notice-marker.ts"],"names":[],"mappings":"AA+KA,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAsB/D"}
@@ -1,15 +1,28 @@
1
1
  /**
2
2
  * Once-per-session (agent) / once-per-day (human) notice claim via O_EXCL.
3
3
  * First process to create the marker wins (returns true => may print). All others,
4
- * and any error, fail OPEN to no-print (return false). Markers self-expire by key
5
- * (session id rotates per session; daily key rolls per UTC day). DEC-2.
4
+ * and any error, fail OPEN to no-print (return false). A per-marker O_EXCL lock
5
+ * serializes stale-marker eviction against that claim. Markers expire by key
6
+ * (session id rotates per session; daily key rolls per UTC day) and by age, except
7
+ * that a losing claim heartbeats the current marker so a renewed session survives
8
+ * the sweep. DEC-2.
6
9
  */
7
- import { openSync, closeSync, mkdirSync, statSync, readdirSync, unlinkSync } from 'node:fs';
10
+ import { openSync, closeSync, existsSync, linkSync, mkdirSync, statSync, readdirSync, unlinkSync, utimesSync } from 'node:fs';
8
11
  import { homedir } from 'node:os';
9
12
  import { join } from 'node:path';
10
13
  import { readSession } from './session.js';
11
14
  const CONFIG_DIR = join(homedir(), '.config', 'productbrain');
15
+ // Non-current session residue becomes stale after one server expiry lease. The
16
+ // same ID can be renewed, so claimNotice heartbeats the locked current marker.
17
+ // Lease source: convex/intelligence/agentSessions.ts MAX_SESSION_DURATION_MS.
18
+ const SESSION_STALE_MS = 8 * 60 * 60 * 1000;
12
19
  const DAILY_STALE_MS = 7 * 24 * 60 * 60 * 1000;
20
+ // Lock actions contain only synchronous local filesystem calls. A one-minute
21
+ // lease bounds crash residue while leaving ample headroom for a live holder.
22
+ const LOCK_STALE_MS = 60 * 1000;
23
+ const LOCK_PREFIX = '.notice-marker-lock-';
24
+ const LOCK_RECLAIM_SUFFIX = '.reclaim';
25
+ const LOCK_PIN_SUFFIX = '.target';
13
26
  function utcDateStamp() {
14
27
  const d = new Date();
15
28
  const p = (n) => String(n).padStart(2, '0');
@@ -23,28 +36,185 @@ function markerKey(channel) {
23
36
  // If no active session, fall back to a daily key so the per-day throttle still holds.
24
37
  return session?.sessionId ? `notice-session-${session.sessionId}` : `notice-agent-daily-${utcDateStamp()}`;
25
38
  }
26
- function cleanupOldNoticeMarkers() {
39
+ function markerRetentionMs(name) {
40
+ if (name.startsWith('notice-session-'))
41
+ return SESSION_STALE_MS;
42
+ if (name.startsWith('notice-human-') || name.startsWith('notice-agent-daily-')) {
43
+ return DAILY_STALE_MS;
44
+ }
45
+ return null;
46
+ }
47
+ function markerLockPath(name) {
48
+ return join(CONFIG_DIR, `${LOCK_PREFIX}${name}`);
49
+ }
50
+ function cleanupStaleRecovery(lockPath, now) {
51
+ const reclaimPath = `${lockPath}${LOCK_RECLAIM_SUFFIX}`;
52
+ const pinPath = `${reclaimPath}${LOCK_PIN_SUFFIX}`;
53
+ try {
54
+ if (now - statSync(reclaimPath).mtimeMs <= LOCK_STALE_MS)
55
+ return;
56
+ try {
57
+ unlinkSync(pinPath);
58
+ }
59
+ catch { /* absent or unavailable identity pin */ }
60
+ unlinkSync(reclaimPath);
61
+ return;
62
+ }
63
+ catch { /* absent, fresh, or unavailable recovery election */ }
64
+ try {
65
+ if (now - statSync(pinPath).mtimeMs > LOCK_STALE_MS)
66
+ unlinkSync(pinPath);
67
+ }
68
+ catch { /* absent or unavailable orphan identity pin */ }
69
+ }
70
+ /**
71
+ * Reclaim one expired lock without a pathname-only unlink race. O_EXCL elects
72
+ * one reclaimer; its hard link pins the exact stale file instance to remove.
73
+ */
74
+ function reclaimStaleLock(lockPath, now) {
75
+ const reclaimPath = `${lockPath}${LOCK_RECLAIM_SUFFIX}`;
76
+ const pinPath = `${reclaimPath}${LOCK_PIN_SUFFIX}`;
77
+ let reclaimFd;
78
+ let observed;
79
+ try {
80
+ observed = statSync(lockPath);
81
+ if (now - observed.mtimeMs <= LOCK_STALE_MS)
82
+ return false;
83
+ cleanupStaleRecovery(lockPath, now);
84
+ reclaimFd = openSync(reclaimPath, 'wx');
85
+ closeSync(reclaimFd);
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ try {
91
+ linkSync(lockPath, pinPath);
92
+ const pinned = statSync(pinPath);
93
+ if (observed.dev !== pinned.dev || observed.ino !== pinned.ino)
94
+ return false;
95
+ const current = statSync(lockPath);
96
+ if (current.dev !== pinned.dev || current.ino !== pinned.ino)
97
+ return false;
98
+ unlinkSync(lockPath);
99
+ return true;
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ finally {
105
+ try {
106
+ unlinkSync(pinPath);
107
+ }
108
+ catch { /* bounded by the next stale sweep */ }
109
+ try {
110
+ unlinkSync(reclaimPath);
111
+ }
112
+ catch { /* bounded by the next stale sweep */ }
113
+ }
114
+ }
115
+ function acquireMarkerLock(name) {
116
+ const lockPath = markerLockPath(name);
27
117
  try {
28
- for (const name of readdirSync(CONFIG_DIR)) {
29
- if (!name.startsWith('notice-human-') &&
30
- !name.startsWith('notice-agent-daily-') &&
31
- !name.startsWith('notice-session-'))
118
+ return openSync(lockPath, 'wx');
119
+ }
120
+ catch {
121
+ if (!reclaimStaleLock(lockPath, Date.now()))
122
+ return null;
123
+ try {
124
+ return openSync(lockPath, 'wx');
125
+ }
126
+ catch {
127
+ return null;
128
+ }
129
+ }
130
+ }
131
+ function withMarkerLock(name, action) {
132
+ const lockPath = markerLockPath(name);
133
+ const lockFd = acquireMarkerLock(name);
134
+ if (lockFd === null)
135
+ return null;
136
+ try {
137
+ closeSync(lockFd);
138
+ return action();
139
+ }
140
+ finally {
141
+ try {
142
+ unlinkSync(lockPath);
143
+ }
144
+ catch { /* best-effort lock release */ }
145
+ }
146
+ }
147
+ function cleanupAbandonedMarkerLocks(names, now) {
148
+ for (const name of names) {
149
+ if (!name.startsWith(LOCK_PREFIX))
150
+ continue;
151
+ if (name.endsWith(LOCK_RECLAIM_SUFFIX)) {
152
+ cleanupStaleRecovery(join(CONFIG_DIR, name.slice(0, -LOCK_RECLAIM_SUFFIX.length)), now);
153
+ continue;
154
+ }
155
+ if (name.endsWith(`${LOCK_RECLAIM_SUFFIX}${LOCK_PIN_SUFFIX}`))
156
+ continue;
157
+ const path = join(CONFIG_DIR, name);
158
+ try {
159
+ if (now - statSync(path).mtimeMs <= LOCK_STALE_MS)
32
160
  continue;
33
- const p = join(CONFIG_DIR, name);
34
- if (Date.now() - statSync(p).mtimeMs > DAILY_STALE_MS)
35
- unlinkSync(p);
161
+ reclaimStaleLock(path, now);
162
+ }
163
+ catch {
164
+ // Lock recovery is best-effort and isolated per file, like marker GC.
36
165
  }
37
166
  }
38
- catch { /* best-effort */ }
167
+ }
168
+ function cleanupOldNoticeMarkers(currentName) {
169
+ let names;
170
+ try {
171
+ names = readdirSync(CONFIG_DIR);
172
+ }
173
+ catch {
174
+ return;
175
+ }
176
+ const now = Date.now();
177
+ cleanupAbandonedMarkerLocks(names, now);
178
+ for (const name of names) {
179
+ if (name === currentName)
180
+ continue;
181
+ const staleAfterMs = markerRetentionMs(name);
182
+ if (staleAfterMs === null)
183
+ continue;
184
+ withMarkerLock(name, () => {
185
+ try {
186
+ const p = join(CONFIG_DIR, name);
187
+ if (now - statSync(p).mtimeMs > staleAfterMs)
188
+ unlinkSync(p);
189
+ }
190
+ catch {
191
+ // A marker can disappear or become unavailable while the sweep runs.
192
+ // Keep cleanup best-effort without skipping the remaining files.
193
+ }
194
+ });
195
+ }
39
196
  }
40
197
  /** Returns true exactly once per (channel, session/day). Never throws. */
41
198
  export function claimNotice(channel) {
42
- const path = join(CONFIG_DIR, markerKey(channel));
199
+ const name = markerKey(channel);
200
+ const path = join(CONFIG_DIR, name);
43
201
  try {
44
202
  mkdirSync(CONFIG_DIR, { recursive: true });
45
- closeSync(openSync(path, 'wx')); // 'wx' = O_EXCL: throws EEXIST if present
46
- cleanupOldNoticeMarkers();
47
- return true;
203
+ return withMarkerLock(name, () => {
204
+ cleanupOldNoticeMarkers(name);
205
+ if (existsSync(path)) {
206
+ // A live server session can be renewed under the same ID. Refreshing the
207
+ // marker on each suppressed notice keeps that active session out of GC.
208
+ try {
209
+ const now = new Date();
210
+ utimesSync(path, now, now);
211
+ }
212
+ catch { /* the existing claim still wins */ }
213
+ return false;
214
+ }
215
+ closeSync(openSync(path, 'wx')); // 'wx' = O_EXCL: throws EEXIST if present
216
+ return true;
217
+ }) ?? false;
48
218
  }
49
219
  catch {
50
220
  return false; // already claimed, or fs error => fail open to no-print
@@ -1 +1 @@
1
- {"version":3,"file":"notice-marker.js","sourceRoot":"","sources":["../../src/lib/notice-marker.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;AAC9D,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE/C,SAAS,YAAY;IACnB,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IACrB,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED,qFAAqF;AACrF,SAAS,SAAS,CAAC,OAA0B;IAC3C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,gBAAgB,YAAY,EAAE,EAAE,CAAC;IACjE,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;IAC9B,sFAAsF;IACtF,OAAO,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,kBAAkB,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,sBAAsB,YAAY,EAAE,EAAE,CAAC;AAC7G,CAAC;AAED,SAAS,uBAAuB;IAC9B,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;gBACjC,CAAC,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC;gBACvC,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC;gBAAE,SAAS;YAClD,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YACjC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,cAAc;gBAAE,UAAU,CAAC,CAAC,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAC/B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,OAA0B;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,IAAI,CAAC;QACH,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,0CAA0C;QAC3E,uBAAuB,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC,CAAC,wDAAwD;IACxE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"notice-marker.js","sourceRoot":"","sources":["../../src/lib/notice-marker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productbrain/cli",
3
- "version": "0.1.0-beta.4957",
3
+ "version": "0.1.0-beta.4965",
4
4
  "description": "Product Brain — Chain knowledge and write-back CLI",
5
5
  "type": "module",
6
6
  "bin": {