gsd-pi 2.73.0-dev.27730dc → 2.73.0-dev.e1c09f2

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.
Files changed (51) hide show
  1. package/dist/resources/extensions/gsd/auto.js +1 -5
  2. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +7 -18
  3. package/dist/resources/extensions/gsd/crash-recovery.js +0 -51
  4. package/dist/resources/extensions/gsd/gsd-db.js +2 -36
  5. package/dist/resources/extensions/gsd/milestone-actions.js +1 -19
  6. package/dist/web/standalone/.next/BUILD_ID +1 -1
  7. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  8. package/dist/web/standalone/.next/build-manifest.json +2 -2
  9. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  10. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/index.html +1 -1
  27. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  34. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  36. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  37. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  38. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  39. package/package.json +1 -1
  40. package/src/resources/extensions/gsd/auto.ts +0 -5
  41. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +7 -19
  42. package/src/resources/extensions/gsd/crash-recovery.ts +0 -59
  43. package/src/resources/extensions/gsd/gsd-db.ts +2 -52
  44. package/src/resources/extensions/gsd/milestone-actions.ts +1 -19
  45. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -59
  46. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +0 -64
  47. package/dist/resources/extensions/gsd/bootstrap/crash-log.js +0 -31
  48. package/src/resources/extensions/gsd/bootstrap/crash-log.ts +0 -32
  49. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +0 -235
  50. /package/dist/web/standalone/.next/static/{jNiH700EcljeLnbQ2_RCv → _XD_gUDcZNBbWV5rI8RgS}/_buildManifest.js +0 -0
  51. /package/dist/web/standalone/.next/static/{jNiH700EcljeLnbQ2_RCv → _XD_gUDcZNBbWV5rI8RgS}/_ssgManifest.js +0 -0
@@ -1,235 +0,0 @@
1
- /**
2
- * Regression tests for #3348 secondary issues — crash handler gaps surfaced after #3696
3
- *
4
- * 1. register-extension.ts: writeCrashLog writes to ~/.gsd/crash/ directory
5
- * 2. register-extension.ts: _gsdRejectionGuard registered for unhandledRejection
6
- * 3. register-extension.ts: _gsdEpipeGuard exits with code 1 for unrecoverable errors (no log-and-continue)
7
- * 4. crash-recovery.ts: emitCrashRecoveredUnitEnd closes open unit-start journal entries
8
- */
9
-
10
- import { describe, test } from 'node:test';
11
- import assert from 'node:assert/strict';
12
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs';
13
- import { join } from 'node:path';
14
- import { tmpdir } from 'node:os';
15
- import { randomUUID } from 'node:crypto';
16
- import { fileURLToPath } from 'node:url';
17
- import { dirname } from 'node:path';
18
-
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = dirname(__filename);
21
-
22
- function makeTmpBase(): string {
23
- const base = join(tmpdir(), `gsd-test-${randomUUID()}`);
24
- mkdirSync(join(base, '.gsd'), { recursive: true });
25
- return base;
26
- }
27
-
28
- // ─── register-extension source assertions ────────────────────────────────────
29
-
30
- const registerExtSrc = readFileSync(
31
- join(__dirname, '..', 'bootstrap', 'register-extension.ts'),
32
- 'utf-8',
33
- );
34
-
35
- describe('register-extension crash handler secondary fixes (#3348)', () => {
36
- test('writeCrashLog is exported and writes a file to the crash directory', async () => {
37
- // Dynamic import so GSD_HOME can be pointed at a temp dir without polluting ~/.gsd
38
- const tmpHome = join(tmpdir(), `gsd-crash-test-${randomUUID()}`);
39
- const origHome = process.env.GSD_HOME;
40
- process.env.GSD_HOME = tmpHome;
41
- try {
42
- const { writeCrashLog } = await import('../bootstrap/crash-log.ts');
43
- const err = new Error('test crash from secondary regression test');
44
- writeCrashLog(err, 'uncaughtException');
45
-
46
- const crashDir = join(tmpHome, 'crash');
47
- assert.ok(existsSync(crashDir), 'crash directory should be created');
48
-
49
- const logs = readdirSync(crashDir).filter((f) => f.endsWith('.log'));
50
- assert.equal(logs.length, 1, 'exactly one crash log should be written');
51
-
52
- const content = readFileSync(join(crashDir, logs[0]), 'utf-8');
53
- assert.ok(content.includes('test crash from secondary regression test'), 'log should contain error message');
54
- assert.ok(content.includes('uncaughtException'), 'log should identify the source');
55
- assert.ok(content.includes('pid:'), 'log should include process pid');
56
- } finally {
57
- process.env.GSD_HOME = origHome;
58
- rmSync(tmpHome, { recursive: true, force: true });
59
- }
60
- });
61
-
62
- test('_gsdRejectionGuard is registered for unhandledRejection', () => {
63
- assert.match(
64
- registerExtSrc,
65
- /_gsdRejectionGuard/,
66
- '_gsdRejectionGuard handler should be defined',
67
- );
68
- assert.match(
69
- registerExtSrc,
70
- /unhandledRejection/,
71
- 'installEpipeGuard should register an unhandledRejection handler',
72
- );
73
- });
74
-
75
- test('_gsdEpipeGuard calls process.exit(1) for unrecoverable errors, not log-and-continue', () => {
76
- // The original #3696 fix replaced "throw err" with a log-and-continue.
77
- // The secondary fix replaces that with writeCrashLog + process.exit(1).
78
- assert.ok(
79
- !registerExtSrc.includes('process.stderr.write(`[gsd] uncaught extension error (non-fatal)'),
80
- '_gsdEpipeGuard should NOT log errors as non-fatal and continue',
81
- );
82
- assert.match(
83
- registerExtSrc,
84
- /process\.exit\(1\)/,
85
- '_gsdEpipeGuard should call process.exit(1) for unrecoverable errors',
86
- );
87
- });
88
-
89
- test('writeCrashLog never throws even when directory is unwritable', async () => {
90
- const { writeCrashLog } = await import('../bootstrap/crash-log.ts');
91
- const origHome = process.env.GSD_HOME;
92
- // Point at a path that will fail to mkdir (e.g. a file that exists as non-dir)
93
- const tmpFile = join(tmpdir(), `gsd-not-a-dir-${randomUUID()}`);
94
- // Don't create it — mkdirSync with bad path should be caught internally
95
- process.env.GSD_HOME = join(tmpFile, 'nested', 'deeply');
96
- try {
97
- // Should not throw
98
- assert.doesNotThrow(() => {
99
- writeCrashLog(new Error('should not throw'), 'test');
100
- });
101
- } finally {
102
- process.env.GSD_HOME = origHome;
103
- }
104
- });
105
- });
106
-
107
- // ─── emitCrashRecoveredUnitEnd ────────────────────────────────────────────────
108
-
109
- describe('emitCrashRecoveredUnitEnd (#3348)', () => {
110
- test('emits synthetic unit-end when unit-start has no matching unit-end', async () => {
111
- const base = makeTmpBase();
112
- try {
113
- const { emitJournalEvent, queryJournal } = await import('../journal.ts');
114
- const { emitCrashRecoveredUnitEnd } = await import('../crash-recovery.ts');
115
-
116
- const flowId = randomUUID();
117
- const unitStartSeq = 5;
118
-
119
- // Emit a unit-start with no corresponding unit-end (simulating a crash)
120
- emitJournalEvent(base, {
121
- ts: new Date().toISOString(),
122
- flowId,
123
- seq: unitStartSeq,
124
- eventType: 'unit-start',
125
- data: { unitType: 'execute-task', unitId: 'M001/S01/T01' },
126
- });
127
-
128
- const lock = {
129
- pid: 99999,
130
- startedAt: new Date().toISOString(),
131
- unitType: 'execute-task',
132
- unitId: 'M001/S01/T01',
133
- unitStartedAt: new Date().toISOString(),
134
- };
135
-
136
- emitCrashRecoveredUnitEnd(base, lock);
137
-
138
- const events = queryJournal(base);
139
- const ends = events.filter((e) => e.eventType === 'unit-end');
140
- assert.equal(ends.length, 1, 'should emit exactly one unit-end');
141
- assert.equal(ends[0].data?.unitId, 'M001/S01/T01');
142
- assert.equal(ends[0].data?.status, 'crash-recovered');
143
- assert.equal(ends[0].causedBy?.flowId, flowId);
144
- assert.equal(ends[0].causedBy?.seq, unitStartSeq);
145
- assert.ok(ends[0].seq > unitStartSeq, 'unit-end seq must be higher than unit-start seq');
146
- } finally {
147
- rmSync(base, { recursive: true, force: true });
148
- }
149
- });
150
-
151
- test('is a no-op when unit-end was already emitted (e.g. hard timeout fired)', async () => {
152
- const base = makeTmpBase();
153
- try {
154
- const { emitJournalEvent, queryJournal } = await import('../journal.ts');
155
- const { emitCrashRecoveredUnitEnd } = await import('../crash-recovery.ts');
156
-
157
- const flowId = randomUUID();
158
- emitJournalEvent(base, {
159
- ts: new Date().toISOString(),
160
- flowId,
161
- seq: 3,
162
- eventType: 'unit-start',
163
- data: { unitType: 'plan-slice', unitId: 'M001/S02' },
164
- });
165
- // Hard timeout already emitted a unit-end
166
- emitJournalEvent(base, {
167
- ts: new Date().toISOString(),
168
- flowId,
169
- seq: 4,
170
- eventType: 'unit-end',
171
- data: { unitType: 'plan-slice', unitId: 'M001/S02', status: 'cancelled' },
172
- causedBy: { flowId, seq: 3 },
173
- });
174
-
175
- const lock = {
176
- pid: 99999,
177
- startedAt: new Date().toISOString(),
178
- unitType: 'plan-slice',
179
- unitId: 'M001/S02',
180
- unitStartedAt: new Date().toISOString(),
181
- };
182
- emitCrashRecoveredUnitEnd(base, lock);
183
-
184
- const ends = queryJournal(base).filter((e) => e.eventType === 'unit-end');
185
- assert.equal(ends.length, 1, 'should not emit a duplicate unit-end');
186
- assert.equal(ends[0].data?.status, 'cancelled', 'original unit-end should be preserved');
187
- } finally {
188
- rmSync(base, { recursive: true, force: true });
189
- }
190
- });
191
-
192
- test('is a no-op for "starting" pseudo-units (bootstrap crash)', async () => {
193
- const base = makeTmpBase();
194
- try {
195
- const { queryJournal } = await import('../journal.ts');
196
- const { emitCrashRecoveredUnitEnd } = await import('../crash-recovery.ts');
197
-
198
- const lock = {
199
- pid: 99999,
200
- startedAt: new Date().toISOString(),
201
- unitType: 'starting',
202
- unitId: 'bootstrap',
203
- unitStartedAt: new Date().toISOString(),
204
- };
205
- emitCrashRecoveredUnitEnd(base, lock);
206
-
207
- const events = queryJournal(base);
208
- assert.equal(events.length, 0, 'should emit nothing for starting/bootstrap pseudo-units');
209
- } finally {
210
- rmSync(base, { recursive: true, force: true });
211
- }
212
- });
213
-
214
- test('is a no-op when no unit-start exists in the journal', async () => {
215
- const base = makeTmpBase();
216
- try {
217
- const { queryJournal } = await import('../journal.ts');
218
- const { emitCrashRecoveredUnitEnd } = await import('../crash-recovery.ts');
219
-
220
- const lock = {
221
- pid: 99999,
222
- startedAt: new Date().toISOString(),
223
- unitType: 'execute-task',
224
- unitId: 'M002/S01/T03',
225
- unitStartedAt: new Date().toISOString(),
226
- };
227
- emitCrashRecoveredUnitEnd(base, lock);
228
-
229
- const events = queryJournal(base);
230
- assert.equal(events.length, 0, 'should emit nothing when there is no journal entry to close');
231
- } finally {
232
- rmSync(base, { recursive: true, force: true });
233
- }
234
- });
235
- });