maskweaver 0.9.4 → 0.9.6

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 (229) hide show
  1. package/README.ko.md +638 -592
  2. package/README.md +671 -667
  3. package/dist/cli/doctor.js +5 -21
  4. package/dist/cli/install.d.ts +0 -8
  5. package/dist/cli/install.js +0 -39
  6. package/dist/context/config.d.ts +0 -22
  7. package/dist/context/config.js +0 -28
  8. package/dist/context/feature.d.ts +0 -39
  9. package/dist/context/feature.js +0 -77
  10. package/dist/context/files.d.ts +0 -13
  11. package/dist/context/files.js +1 -24
  12. package/dist/context/index.d.ts +0 -7
  13. package/dist/context/index.js +0 -12
  14. package/dist/context/project.d.ts +0 -21
  15. package/dist/context/project.js +0 -30
  16. package/dist/context/types.d.ts +0 -48
  17. package/dist/context/types.js +0 -12
  18. package/dist/context/utils.d.ts +0 -18
  19. package/dist/context/utils.js +0 -27
  20. package/dist/core/engine/promptBuilder.d.ts +0 -17
  21. package/dist/core/engine/promptBuilder.js +0 -28
  22. package/dist/core/index.d.ts +0 -6
  23. package/dist/core/index.js +0 -9
  24. package/dist/core/loader/MaskLoader.d.ts +0 -23
  25. package/dist/core/loader/MaskLoader.js +0 -29
  26. package/dist/core/schema/types.d.ts +0 -47
  27. package/dist/core/schema/types.js +0 -6
  28. package/dist/core/schema/validator.d.ts +0 -14
  29. package/dist/core/schema/validator.js +0 -18
  30. package/dist/i18n/index.d.ts +0 -18
  31. package/dist/i18n/index.js +4 -23
  32. package/dist/index.d.ts +0 -8
  33. package/dist/index.js +0 -8
  34. package/dist/lib.d.ts +0 -5
  35. package/dist/lib.js +0 -12
  36. package/dist/memory/chunking.d.ts +0 -22
  37. package/dist/memory/chunking.js +2 -37
  38. package/dist/memory/core.d.ts +0 -29
  39. package/dist/memory/core.js +1 -52
  40. package/dist/memory/index.d.ts +0 -5
  41. package/dist/memory/index.js +0 -10
  42. package/dist/memory/indexer.d.ts +0 -21
  43. package/dist/memory/indexer.js +0 -44
  44. package/dist/memory/providers/examples.d.ts +0 -5
  45. package/dist/memory/providers/examples.js +4 -64
  46. package/dist/memory/providers/factory.d.ts +0 -44
  47. package/dist/memory/providers/factory.js +0 -46
  48. package/dist/memory/providers/index.d.ts +0 -26
  49. package/dist/memory/providers/index.js +0 -28
  50. package/dist/memory/providers/ollama.d.ts +0 -6
  51. package/dist/memory/providers/ollama.js +1 -8
  52. package/dist/memory/providers/openai.d.ts +0 -6
  53. package/dist/memory/providers/openai.js +1 -8
  54. package/dist/memory/providers/openrouter.d.ts +0 -6
  55. package/dist/memory/providers/openrouter.js +0 -8
  56. package/dist/memory/providers/text-only.d.ts +0 -13
  57. package/dist/memory/providers/text-only.js +0 -17
  58. package/dist/memory/providers/types.d.ts +0 -39
  59. package/dist/memory/providers/types.js +0 -7
  60. package/dist/memory/providers/voyage.d.ts +0 -22
  61. package/dist/memory/providers/voyage.js +1 -24
  62. package/dist/memory/search/hybrid.d.ts +0 -12
  63. package/dist/memory/search/hybrid.js +1 -22
  64. package/dist/memory/store/sqlite.d.ts +0 -72
  65. package/dist/memory/store/sqlite.js +4 -127
  66. package/dist/plugin/config/index.d.ts +0 -112
  67. package/dist/plugin/config/index.js +0 -115
  68. package/dist/plugin/index.d.ts +0 -13
  69. package/dist/plugin/index.js +1 -123
  70. package/dist/plugin/tools/command-registry.d.ts +0 -6
  71. package/dist/plugin/tools/command-registry.js +0 -14
  72. package/dist/plugin/tools/context.d.ts +0 -12
  73. package/dist/plugin/tools/context.js +0 -58
  74. package/dist/plugin/tools/maskSave.d.ts +0 -3
  75. package/dist/plugin/tools/maskSave.js +0 -3
  76. package/dist/plugin/tools/memoryGet.d.ts +0 -3
  77. package/dist/plugin/tools/memoryGet.js +0 -3
  78. package/dist/plugin/tools/memoryIndexer.d.ts +0 -3
  79. package/dist/plugin/tools/memoryIndexer.js +0 -10
  80. package/dist/plugin/tools/memorySearch.d.ts +0 -31
  81. package/dist/plugin/tools/memorySearch.js +0 -79
  82. package/dist/plugin/tools/memoryWrite.d.ts +0 -8
  83. package/dist/plugin/tools/memoryWrite.js +0 -32
  84. package/dist/plugin/tools/retrospect.d.ts +0 -3
  85. package/dist/plugin/tools/retrospect.js +0 -3
  86. package/dist/plugin/tools/slashcommand.d.ts +0 -11
  87. package/dist/plugin/tools/slashcommand.js +0 -38
  88. package/dist/plugin/tools/squad.d.ts +0 -12
  89. package/dist/plugin/tools/squad.js +11 -83
  90. package/dist/plugin/tools/weave.d.ts +0 -6
  91. package/dist/plugin/tools/weave.js +0 -78
  92. package/dist/plugin/types.d.ts +0 -20
  93. package/dist/plugin/types.js +0 -7
  94. package/dist/retrospect/index.d.ts +0 -7
  95. package/dist/retrospect/index.js +0 -9
  96. package/dist/retrospect/mask-save.d.ts +0 -12
  97. package/dist/retrospect/mask-save.js +1 -80
  98. package/dist/retrospect/retrospect.d.ts +0 -18
  99. package/dist/retrospect/retrospect.js +0 -63
  100. package/dist/retrospect/strategies/base.d.ts +0 -15
  101. package/dist/retrospect/strategies/base.js +0 -7
  102. package/dist/retrospect/strategies/deep.d.ts +0 -12
  103. package/dist/retrospect/strategies/deep.js +0 -24
  104. package/dist/retrospect/strategies/index.d.ts +0 -12
  105. package/dist/retrospect/strategies/index.js +0 -12
  106. package/dist/retrospect/strategies/quick.d.ts +0 -12
  107. package/dist/retrospect/strategies/quick.js +0 -19
  108. package/dist/retrospect/strategies/standard.d.ts +0 -12
  109. package/dist/retrospect/strategies/standard.js +0 -15
  110. package/dist/retrospect/types.d.ts +0 -7
  111. package/dist/retrospect/types.js +0 -7
  112. package/dist/shared/config.d.ts +0 -105
  113. package/dist/shared/config.js +0 -33
  114. package/dist/shared/errors.d.ts +0 -18
  115. package/dist/shared/errors.js +0 -19
  116. package/dist/shared/generate-agents.d.ts +0 -69
  117. package/dist/shared/generate-agents.js +2 -86
  118. package/dist/shared/image.d.ts +0 -67
  119. package/dist/shared/image.js +6 -104
  120. package/dist/shared/index.d.ts +0 -5
  121. package/dist/shared/index.js +0 -7
  122. package/dist/shared/model-registry.d.ts +0 -72
  123. package/dist/shared/model-registry.js +5 -95
  124. package/dist/shared/types.d.ts +0 -15
  125. package/dist/shared/types.js +0 -3
  126. package/dist/shared-context/dag.d.ts +0 -105
  127. package/dist/shared-context/dag.js +3 -114
  128. package/dist/shared-context/index.d.ts +0 -5
  129. package/dist/shared-context/index.js +0 -15
  130. package/dist/shared-context/logger.d.ts +0 -37
  131. package/dist/shared-context/logger.js +0 -41
  132. package/dist/shared-context/parallel-executor.d.ts +0 -54
  133. package/dist/shared-context/parallel-executor.js +4 -56
  134. package/dist/shared-context/session.d.ts +0 -56
  135. package/dist/shared-context/session.js +0 -47
  136. package/dist/shared-context/squad.d.ts +0 -68
  137. package/dist/shared-context/squad.js +0 -63
  138. package/dist/shared-context/storage.d.ts +0 -132
  139. package/dist/shared-context/storage.js +0 -116
  140. package/dist/shared-context/task.d.ts +0 -120
  141. package/dist/shared-context/task.js +0 -152
  142. package/dist/shared-context/test/dag.test.js +9 -14
  143. package/dist/shared-context/test/logger.test.d.ts +0 -8
  144. package/dist/shared-context/test/logger.test.js +0 -52
  145. package/dist/shared-context/test/session.test.d.ts +0 -7
  146. package/dist/shared-context/test/session.test.js +0 -63
  147. package/dist/shared-context/test/squad.test.d.ts +0 -10
  148. package/dist/shared-context/test/squad.test.js +2 -68
  149. package/dist/shared-context/test/storage.test.d.ts +0 -8
  150. package/dist/shared-context/test/storage.test.js +0 -68
  151. package/dist/shared-context/test/task.test.d.ts +0 -7
  152. package/dist/shared-context/test/task.test.js +0 -54
  153. package/dist/shared-context/test/watchdog.test.d.ts +0 -7
  154. package/dist/shared-context/test/watchdog.test.js +3 -58
  155. package/dist/shared-context/types.d.ts +0 -215
  156. package/dist/shared-context/types.js +0 -125
  157. package/dist/shared-context/watchdog.d.ts +0 -127
  158. package/dist/shared-context/watchdog.js +0 -148
  159. package/dist/shared-context/worktree.d.ts +0 -68
  160. package/dist/shared-context/worktree.js +2 -34
  161. package/dist/verify/budget.d.ts +0 -29
  162. package/dist/verify/budget.js +0 -34
  163. package/dist/verify/critical-files.d.ts +0 -17
  164. package/dist/verify/critical-files.js +0 -37
  165. package/dist/verify/escalation.d.ts +0 -20
  166. package/dist/verify/escalation.js +0 -22
  167. package/dist/verify/index.d.ts +0 -5
  168. package/dist/verify/index.js +0 -11
  169. package/dist/verify/prompts.d.ts +0 -20
  170. package/dist/verify/prompts.js +0 -20
  171. package/dist/verify/types.d.ts +0 -26
  172. package/dist/verify/types.js +1 -12
  173. package/dist/verify/verifier.d.ts +0 -29
  174. package/dist/verify/verifier.js +0 -54
  175. package/dist/version.d.ts +1 -16
  176. package/dist/version.js +1 -16
  177. package/dist/weave/bridge.d.ts +0 -35
  178. package/dist/weave/bridge.js +0 -51
  179. package/dist/weave/environment/detector.d.ts +0 -6
  180. package/dist/weave/environment/detector.js +4 -45
  181. package/dist/weave/environment/index.d.ts +0 -19
  182. package/dist/weave/environment/index.js +1 -39
  183. package/dist/weave/environment/issues.d.ts +0 -35
  184. package/dist/weave/environment/issues.js +0 -59
  185. package/dist/weave/git.d.ts +0 -8
  186. package/dist/weave/git.js +0 -8
  187. package/dist/weave/index.d.ts +0 -13
  188. package/dist/weave/index.js +2 -28
  189. package/dist/weave/knowledge/global.d.ts +0 -39
  190. package/dist/weave/knowledge/global.js +2 -78
  191. package/dist/weave/loop.js +0 -3
  192. package/dist/weave/orchestrator.d.ts +0 -69
  193. package/dist/weave/orchestrator.js +1 -101
  194. package/dist/weave/phase-manager.d.ts +0 -64
  195. package/dist/weave/phase-manager.js +0 -89
  196. package/dist/weave/security/secret-scan.d.ts +0 -14
  197. package/dist/weave/security/secret-scan.js +0 -19
  198. package/dist/weave/stages/build.js +0 -15
  199. package/dist/weave/stages/execute.d.ts +0 -42
  200. package/dist/weave/stages/execute.js +4 -86
  201. package/dist/weave/stages/handoff.d.ts +0 -7
  202. package/dist/weave/stages/handoff.js +0 -43
  203. package/dist/weave/stages/index.d.ts +0 -3
  204. package/dist/weave/stages/index.js +0 -3
  205. package/dist/weave/stages/intake.d.ts +0 -8
  206. package/dist/weave/stages/intake.js +5 -65
  207. package/dist/weave/stages/map.d.ts +0 -1
  208. package/dist/weave/stages/openspec.d.ts +0 -1
  209. package/dist/weave/stages/plan.d.ts +0 -11
  210. package/dist/weave/stages/plan.js +1 -53
  211. package/dist/weave/stages/refine.d.ts +0 -7
  212. package/dist/weave/stages/refine.js +0 -7
  213. package/dist/weave/stages/research.d.ts +0 -6
  214. package/dist/weave/stages/research.js +0 -6
  215. package/dist/weave/stages/spec.d.ts +0 -12
  216. package/dist/weave/stages/spec.js +0 -17
  217. package/dist/weave/types.d.ts +0 -20
  218. package/dist/weave/types.js +0 -5
  219. package/dist/weave/verification/commands.d.ts +0 -12
  220. package/dist/weave/verification/commands.js +0 -19
  221. package/dist/weave/verification/index.d.ts +0 -6
  222. package/dist/weave/verification/index.js +1 -19
  223. package/dist/weave/verification/playwright.d.ts +0 -47
  224. package/dist/weave/verification/playwright.js +1 -90
  225. package/dist/weave/worktree.d.ts +0 -16
  226. package/dist/weave/worktree.js +0 -23
  227. package/dist/weave/yaml-repair.d.ts +0 -39
  228. package/dist/weave/yaml-repair.js +13 -116
  229. package/package.json +1 -1
@@ -1,176 +1,114 @@
1
- /**
2
- * Storage Adapter Unit Tests
3
- *
4
- * "First make it work, then make it right, then make it fast."
5
- * - Kent Beck
6
- *
7
- * @author Kent Beck's Dummy Human
8
- */
9
1
  import { test, expect, describe, beforeEach, afterEach } from "vitest";
10
2
  import { mkdtemp, rm, readFile, writeFile } from "fs/promises";
11
3
  import { tmpdir } from "os";
12
4
  import { join } from "path";
13
5
  import { validatePath, FileStorageAdapter } from "../storage.js";
14
- // ============================================================
15
- // validatePath Tests - Path Traversal Prevention
16
- // ============================================================
17
6
  describe("validatePath", () => {
18
7
  const baseDir = "/home/user/data";
19
8
  test("should allow valid paths within base directory", () => {
20
- // Arrange & Act & Assert
21
9
  expect(validatePath("/home/user/data/file.json", baseDir)).toBe(true);
22
10
  expect(validatePath("/home/user/data/sub/file.json", baseDir)).toBe(true);
23
11
  });
24
12
  test("should reject paths containing '..' (Path Traversal attack)", () => {
25
- // Arrange
26
13
  const maliciousPath = "/home/user/data/../secret/passwords.json";
27
- // Act & Assert
28
14
  expect(validatePath(maliciousPath, baseDir)).toBe(false);
29
15
  });
30
16
  test("should reject paths outside base directory", () => {
31
- // Arrange
32
17
  const outsidePath = "/home/user/other/file.json";
33
- // Act & Assert
34
18
  expect(validatePath(outsidePath, baseDir)).toBe(false);
35
19
  });
36
20
  test("should reject hidden path traversal attempts", () => {
37
- // Arrange - Various traversal attempts
38
21
  const traversalAttempts = [
39
22
  "/home/user/data/../../etc/passwd",
40
23
  "/home/user/data/sub/../../../root",
41
24
  "/home/user/data/..\\windows\\system32",
42
25
  ];
43
- // Act & Assert
44
26
  for (const path of traversalAttempts) {
45
27
  expect(validatePath(path, baseDir)).toBe(false);
46
28
  }
47
29
  });
48
30
  });
49
- // ============================================================
50
- // FileStorageAdapter Tests
51
- // ============================================================
52
31
  describe("FileStorageAdapter", () => {
53
32
  let tempDir;
54
33
  let storage;
55
34
  beforeEach(async () => {
56
- // Create isolated temp directory for each test
57
35
  tempDir = await mkdtemp(join(tmpdir(), "storage-test-"));
58
36
  storage = new FileStorageAdapter(tempDir);
59
37
  });
60
38
  afterEach(async () => {
61
- // Clean up temp directory
62
39
  await rm(tempDir, { recursive: true, force: true });
63
40
  });
64
- // ----------------------------------------------------------
65
- // write & read Tests
66
- // ----------------------------------------------------------
67
41
  describe("write and read", () => {
68
42
  test("should write and read JSON data successfully", async () => {
69
- // Arrange
70
43
  const testData = { name: "Kent Beck", role: "TDD Master" };
71
44
  const path = "test.json";
72
- // Act
73
45
  await storage.write(path, testData);
74
46
  const result = await storage.read(path);
75
- // Assert
76
47
  expect(result).toEqual(testData);
77
48
  });
78
49
  test("should return null when reading non-existent file", async () => {
79
- // Arrange
80
50
  const path = "non-existent.json";
81
- // Act
82
51
  const result = await storage.read(path);
83
- // Assert
84
52
  expect(result).toBeNull();
85
53
  });
86
54
  test("should handle nested paths correctly", async () => {
87
- // Arrange
88
55
  const testData = { nested: true };
89
56
  const path = "deep/nested/path/data.json";
90
- // Act
91
57
  await storage.write(path, testData);
92
58
  const result = await storage.read(path);
93
- // Assert
94
59
  expect(result).toEqual(testData);
95
60
  });
96
61
  test("should throw error on Path Traversal in read", async () => {
97
- // Arrange
98
62
  const maliciousPath = "../../../etc/passwd";
99
- // Act & Assert
100
63
  await expect(storage.read(maliciousPath)).rejects.toThrow("Path traversal detected");
101
64
  });
102
65
  test("should throw error on Path Traversal in write", async () => {
103
- // Arrange
104
66
  const maliciousPath = "../../../tmp/hack.json";
105
67
  const data = { hacked: true };
106
- // Act & Assert
107
68
  await expect(storage.write(maliciousPath, data)).rejects.toThrow("Path traversal detected");
108
69
  });
109
70
  });
110
- // ----------------------------------------------------------
111
- // append Tests
112
- // ----------------------------------------------------------
113
71
  describe("append", () => {
114
72
  test("should append line to file", async () => {
115
- // Arrange
116
73
  const path = "log.txt";
117
74
  const line1 = "First line";
118
75
  const line2 = "Second line";
119
- // Act
120
76
  await storage.append(path, line1);
121
77
  await storage.append(path, line2);
122
- // Assert - Read raw file content
123
78
  const fullPath = storage.getFullPath(path);
124
79
  const content = await readFile(fullPath, "utf-8");
125
80
  expect(content).toBe("First line\nSecond line\n");
126
81
  });
127
82
  test("should throw error on Path Traversal in append", async () => {
128
- // Arrange
129
83
  const maliciousPath = "../../../tmp/inject.txt";
130
- // Act & Assert
131
84
  await expect(storage.append(maliciousPath, "hacked")).rejects.toThrow("Path traversal detected");
132
85
  });
133
86
  });
134
- // ----------------------------------------------------------
135
- // exists Tests
136
- // ----------------------------------------------------------
137
87
  describe("exists", () => {
138
88
  test("should return true for existing file", async () => {
139
- // Arrange
140
89
  const path = "exists.json";
141
90
  await storage.write(path, { exists: true });
142
- // Act & Assert
143
91
  expect(storage.exists(path)).toBe(true);
144
92
  });
145
93
  test("should return false for non-existing file", () => {
146
- // Arrange
147
94
  const path = "ghost.json";
148
- // Act & Assert
149
95
  expect(storage.exists(path)).toBe(false);
150
96
  });
151
97
  });
152
- // ----------------------------------------------------------
153
- // ensureDir Tests
154
- // ----------------------------------------------------------
155
98
  describe("ensureDir", () => {
156
99
  test("should create nested directories", async () => {
157
- // Arrange - use absolute path directly (ensureDir takes absolute paths)
158
100
  const nestedPath = join(tempDir, "level1", "level2", "level3");
159
- // Act - ensureDir expects absolute paths
160
101
  const { mkdir } = await import("fs/promises");
161
102
  await mkdir(nestedPath, { recursive: true });
162
- // Assert - Write a file to verify directory exists
163
103
  const testFile = join(nestedPath, "test.txt");
164
104
  await writeFile(testFile, "test");
165
105
  const content = await readFile(testFile, "utf-8");
166
106
  expect(content).toBe("test");
167
107
  });
168
108
  test("should not throw when directory already exists", async () => {
169
- // Arrange - use absolute path directly
170
109
  const existingPath = join(tempDir, "existing");
171
110
  const { mkdir } = await import("fs/promises");
172
111
  await mkdir(existingPath, { recursive: true });
173
- // Act & Assert - mkdir with recursive: true is idempotent
174
112
  let error = null;
175
113
  try {
176
114
  await mkdir(existingPath, { recursive: true });
@@ -181,16 +119,10 @@ describe("FileStorageAdapter", () => {
181
119
  expect(error).toBeNull();
182
120
  });
183
121
  });
184
- // ----------------------------------------------------------
185
- // getFullPath Tests
186
- // ----------------------------------------------------------
187
122
  describe("getFullPath", () => {
188
123
  test("should return correct full path", () => {
189
- // Arrange
190
124
  const relativePath = "sub/data.json";
191
- // Act
192
125
  const fullPath = storage.getFullPath(relativePath);
193
- // Assert
194
126
  expect(fullPath).toBe(join(tempDir, "sub/data.json"));
195
127
  });
196
128
  });
@@ -1,8 +1 @@
1
- /**
2
- * Task Management Tests
3
- *
4
- * "테스트가 없으면 버그가 아니다" - Kent Beck
5
- *
6
- * @author Kent Beck's TDD Approach
7
- */
8
1
  export {};
@@ -1,10 +1,3 @@
1
- /**
2
- * Task Management Tests
3
- *
4
- * "테스트가 없으면 버그가 아니다" - Kent Beck
5
- *
6
- * @author Kent Beck's TDD Approach
7
- */
8
1
  import { describe, test, expect, beforeEach, afterEach } from "vitest";
9
2
  import { mkdtemp, rm } from "fs/promises";
10
3
  import { tmpdir } from "os";
@@ -15,9 +8,6 @@ import { createSession } from "../session.js";
15
8
  import { createSquad, getSquad } from "../squad.js";
16
9
  import { assignTask, getTask, updateTask, completeTask } from "../task.js";
17
10
  import { LIMITS } from "../types.js";
18
- // ============================================================================
19
- // Test Fixtures
20
- // ============================================================================
21
11
  let tempDir;
22
12
  let storage;
23
13
  let session;
@@ -40,9 +30,6 @@ afterEach(async () => {
40
30
  await rm(tempDir, { recursive: true, force: true });
41
31
  }
42
32
  });
43
- // ============================================================================
44
- // assignTask Tests
45
- // ============================================================================
46
33
  describe("assignTask", () => {
47
34
  test("should successfully assign a task to a squad", async () => {
48
35
  const task = await assignTask(session, squadId, {
@@ -71,14 +58,12 @@ describe("assignTask", () => {
71
58
  })).rejects.toThrow("Squad not found: squad-nonexistent");
72
59
  });
73
60
  test("should throw ValidationError when max tasks per squad exceeded", async () => {
74
- // Arrange: 최대 개수만큼 Task 생성
75
61
  for (let i = 0; i < LIMITS.maxTasksPerSquad; i++) {
76
62
  await assignTask(session, squadId, {
77
63
  assignee: `worker-${i}`,
78
64
  description: `Task ${i}`,
79
65
  });
80
66
  }
81
- // Act & Assert: 한도 초과 시 에러
82
67
  await expect(assignTask(session, squadId, {
83
68
  assignee: "worker-overflow",
84
69
  description: "One too many",
@@ -141,9 +126,6 @@ describe("assignTask", () => {
141
126
  expect(squad?.state.tasks.map((t) => t.taskId)).toContain(task3.taskId);
142
127
  });
143
128
  });
144
- // ============================================================================
145
- // updateTask Tests - State Transition through Aggregate Root
146
- // ============================================================================
147
129
  describe("updateTask", () => {
148
130
  test("should successfully update a task status", async () => {
149
131
  const task = await assignTask(session, squadId, {
@@ -232,20 +214,14 @@ describe("updateTask", () => {
232
214
  expect(completed.result).toEqual({ done: true });
233
215
  });
234
216
  });
235
- // ============================================================================
236
- // getTask Tests - Domain Entity Lookup
237
- // ============================================================================
238
217
  describe("getTask", () => {
239
218
  test("should return task when it exists", async () => {
240
- // Arrange: assign a task
241
219
  const createdTask = await assignTask(session, squadId, {
242
220
  assignee: "worker-1",
243
221
  description: "Find me later",
244
222
  priority: "high",
245
223
  });
246
- // Act: retrieve the task
247
224
  const foundTask = await getTask(session, squadId, createdTask.taskId);
248
- // Assert: task should match
249
225
  expect(foundTask).not.toBeNull();
250
226
  expect(foundTask?.taskId).toBe(createdTask.taskId);
251
227
  expect(foundTask?.description).toBe("Find me later");
@@ -253,17 +229,13 @@ describe("getTask", () => {
253
229
  expect(foundTask?.priority).toBe("high");
254
230
  });
255
231
  test("should return null when task does not exist", async () => {
256
- // Act: try to find non-existent task
257
232
  const result = await getTask(session, squadId, "task-nonexistent");
258
- // Assert: should be null, not throw
259
233
  expect(result).toBeNull();
260
234
  });
261
235
  test("should throw StorageError when squad does not exist", async () => {
262
- // Act & Assert: should throw for missing squad
263
236
  await expect(getTask(session, "squad-nonexistent", "task-12345678")).rejects.toThrow("Squad not found: squad-nonexistent");
264
237
  });
265
238
  test("should find correct task among multiple tasks", async () => {
266
- // Arrange: create multiple tasks
267
239
  const task1 = await assignTask(session, squadId, {
268
240
  assignee: "worker-1",
269
241
  description: "First task",
@@ -276,24 +248,19 @@ describe("getTask", () => {
276
248
  assignee: "worker-3",
277
249
  description: "Third task",
278
250
  });
279
- // Act: find the middle task
280
251
  const foundTask = await getTask(session, squadId, task2.taskId);
281
- // Assert: should find correct task
282
252
  expect(foundTask).not.toBeNull();
283
253
  expect(foundTask?.taskId).toBe(task2.taskId);
284
254
  expect(foundTask?.description).toBe("Second task");
285
255
  });
286
256
  test("should return task with all fields intact", async () => {
287
- // Arrange: create task with all optional fields
288
257
  const createdTask = await assignTask(session, squadId, {
289
258
  assignee: "worker-alpha",
290
259
  description: "Complete task with all fields",
291
260
  priority: "critical",
292
261
  dependencies: ["dep-1", "dep-2"],
293
262
  });
294
- // Act: retrieve the task
295
263
  const foundTask = await getTask(session, squadId, createdTask.taskId);
296
- // Assert: all fields should be preserved
297
264
  expect(foundTask).not.toBeNull();
298
265
  expect(foundTask?.status).toBe("pending");
299
266
  expect(foundTask?.priority).toBe("critical");
@@ -301,9 +268,6 @@ describe("getTask", () => {
301
268
  expect(foundTask?.createdAt).toBe(createdTask.createdAt);
302
269
  });
303
270
  });
304
- // ============================================================================
305
- // completeTask Tests - Convenience Wrapper for Task Completion
306
- // ============================================================================
307
271
  describe("completeTask", () => {
308
272
  test("should mark task as completed on success", async () => {
309
273
  const task = await assignTask(session, squadId, {
@@ -354,13 +318,8 @@ describe("completeTask", () => {
354
318
  expect(found?.result).toEqual(result);
355
319
  });
356
320
  });
357
- // ============================================================================
358
- // Task Lifecycle Integration Tests
359
- // ============================================================================
360
321
  describe("Task Lifecycle Integration", () => {
361
322
  test("full task lifecycle: assign → start → complete", async () => {
362
- // 1. 세션과 squad는 beforeEach에서 이미 생성됨
363
- // 2. assignTask()로 task 할당
364
323
  const task = await assignTask(session, squadId, {
365
324
  assignee: "worker-alpha",
366
325
  description: "Integration test task",
@@ -369,7 +328,6 @@ describe("Task Lifecycle Integration", () => {
369
328
  expect(task.status).toBe("pending");
370
329
  expect(task.startedAt).toBeUndefined();
371
330
  expect(task.completedAt).toBeUndefined();
372
- // 3. updateTask()로 status: "active", startedAt 설정
373
331
  const startedAt = new Date().toISOString();
374
332
  const activeTask = await updateTask(session, squadId, task.taskId, {
375
333
  status: "active",
@@ -377,7 +335,6 @@ describe("Task Lifecycle Integration", () => {
377
335
  });
378
336
  expect(activeTask.status).toBe("active");
379
337
  expect(activeTask.startedAt).toBe(startedAt);
380
- // 4. completeTask()로 성공 결과와 함께 완료
381
338
  const result = {
382
339
  success: true,
383
340
  output: { filesCreated: ["src/auth.ts", "src/auth.test.ts"] },
@@ -387,7 +344,6 @@ describe("Task Lifecycle Integration", () => {
387
344
  expect(completedTask.status).toBe("completed");
388
345
  expect(completedTask.completedAt).toBeDefined();
389
346
  expect(completedTask.result).toEqual(result);
390
- // 5. getTask()로 최종 상태 확인
391
347
  const finalTask = await getTask(session, squadId, task.taskId);
392
348
  expect(finalTask).not.toBeNull();
393
349
  expect(finalTask?.status).toBe("completed");
@@ -398,20 +354,17 @@ describe("Task Lifecycle Integration", () => {
398
354
  expect(finalTask?.priority).toBe("high");
399
355
  });
400
356
  test("failed task lifecycle: assign → start → fail", async () => {
401
- // 1. assignTask()로 task 할당
402
357
  const task = await assignTask(session, squadId, {
403
358
  assignee: "worker-beta",
404
359
  description: "This task will fail",
405
360
  priority: "critical",
406
361
  });
407
362
  expect(task.status).toBe("pending");
408
- // 2. updateTask()로 시작
409
363
  const startedAt = new Date().toISOString();
410
364
  await updateTask(session, squadId, task.taskId, {
411
365
  status: "active",
412
366
  startedAt,
413
367
  });
414
- // 3. completeTask()로 실패 결과와 함께 완료
415
368
  const result = {
416
369
  success: false,
417
370
  error: {
@@ -424,13 +377,11 @@ describe("Task Lifecycle Integration", () => {
424
377
  expect(failedTask.status).toBe("failed");
425
378
  expect(failedTask.completedAt).toBeDefined();
426
379
  expect(failedTask.result).toEqual(result);
427
- // 4. getTask()로 최종 상태 확인
428
380
  const finalTask = await getTask(session, squadId, task.taskId);
429
381
  expect(finalTask?.status).toBe("failed");
430
382
  expect(finalTask?.startedAt).toBe(startedAt);
431
383
  });
432
384
  test("multiple tasks in squad lifecycle", async () => {
433
- // 여러 태스크가 독립적으로 실행되는 시나리오
434
385
  const task1 = await assignTask(session, squadId, {
435
386
  assignee: "worker-1",
436
387
  description: "Task 1",
@@ -439,27 +390,22 @@ describe("Task Lifecycle Integration", () => {
439
390
  assignee: "worker-2",
440
391
  description: "Task 2",
441
392
  });
442
- // task1 시작
443
393
  await updateTask(session, squadId, task1.taskId, {
444
394
  status: "active",
445
395
  startedAt: new Date().toISOString(),
446
396
  });
447
- // task2 시작
448
397
  await updateTask(session, squadId, task2.taskId, {
449
398
  status: "active",
450
399
  startedAt: new Date().toISOString(),
451
400
  });
452
- // task1 완료
453
401
  await completeTask(session, squadId, task1.taskId, {
454
402
  success: true,
455
403
  output: "Task 1 done",
456
404
  });
457
- // task2 실패
458
405
  await completeTask(session, squadId, task2.taskId, {
459
406
  success: false,
460
407
  error: { code: "ERROR", message: "Task 2 failed" },
461
408
  });
462
- // 최종 상태 확인
463
409
  const squad = await getSquad(session, squadId);
464
410
  expect(squad?.state.tasks).toHaveLength(2);
465
411
  const t1 = squad?.state.tasks.find((t) => t.taskId === task1.taskId);
@@ -1,8 +1 @@
1
- /**
2
- * Watchdog Tests - Squad Timeout Monitoring
3
- *
4
- * "If you can't measure it, you can't improve it." - Brendan Gregg
5
- *
6
- * @author Kent Beck's TDD Approach
7
- */
8
1
  export {};
@@ -1,10 +1,3 @@
1
- /**
2
- * Watchdog Tests - Squad Timeout Monitoring
3
- *
4
- * "If you can't measure it, you can't improve it." - Brendan Gregg
5
- *
6
- * @author Kent Beck's TDD Approach
7
- */
8
1
  import { describe, test, expect, beforeEach, afterEach } from "vitest";
9
2
  import { mkdtemp, rm } from "fs/promises";
10
3
  import { tmpdir } from "os";
@@ -16,9 +9,6 @@ import { createSquad, getSquad, updateSquadState } from "../squad.js";
16
9
  import { assignTask, updateTask } from "../task.js";
17
10
  import { checkSquadTimeout, checkTaskTimeout, markSquadExpired, runWatchdog } from "../watchdog.js";
18
11
  import { LIMITS } from "../types.js";
19
- // ============================================================================
20
- // Test Fixtures
21
- // ============================================================================
22
12
  let tempDir;
23
13
  let storage;
24
14
  let session;
@@ -41,9 +31,6 @@ afterEach(async () => {
41
31
  await rm(tempDir, { recursive: true, force: true });
42
32
  }
43
33
  });
44
- // ============================================================================
45
- // Helper Functions
46
- // ============================================================================
47
34
  function createMockSquadState(updatedAt) {
48
35
  return {
49
36
  squadId: "squad-mock",
@@ -65,9 +52,6 @@ function createMockTaskState(options) {
65
52
  startedAt: options.startedAt,
66
53
  };
67
54
  }
68
- // ============================================================================
69
- // checkSquadTimeout Tests
70
- // ============================================================================
71
55
  describe("checkSquadTimeout", () => {
72
56
  test("should return isExpired: false before timeout", () => {
73
57
  const now = new Date();
@@ -101,7 +85,7 @@ describe("checkSquadTimeout", () => {
101
85
  const now = new Date();
102
86
  const twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000);
103
87
  const squadState = createMockSquadState(twoMinutesAgo.toISOString());
104
- const customTimeoutMs = 60 * 1000; // 1분
88
+ const customTimeoutMs = 60 * 1000;
105
89
  const status = checkSquadTimeout(squadState, customTimeoutMs);
106
90
  expect(status.isExpired).toBe(true);
107
91
  expect(status.elapsedMs).toBeGreaterThanOrEqual(2 * 60 * 1000);
@@ -110,7 +94,7 @@ describe("checkSquadTimeout", () => {
110
94
  const now = new Date();
111
95
  const tenMinutesAgo = new Date(now.getTime() - 10 * 60 * 1000);
112
96
  const squadState = createMockSquadState(tenMinutesAgo.toISOString());
113
- const customTimeoutMs = 30 * 60 * 1000; // 30분
97
+ const customTimeoutMs = 30 * 60 * 1000;
114
98
  const status = checkSquadTimeout(squadState, customTimeoutMs);
115
99
  expect(status.isExpired).toBe(false);
116
100
  expect(status.remainingMs).toBeGreaterThan(0);
@@ -131,9 +115,6 @@ describe("checkSquadTimeout", () => {
131
115
  expect(status.lastActivityAt).toBe(timestamp);
132
116
  });
133
117
  });
134
- // ============================================================================
135
- // markSquadExpired Tests
136
- // ============================================================================
137
118
  describe("markSquadExpired", () => {
138
119
  test("should change status to 'failed'", async () => {
139
120
  const squad = await getSquad(session, squadId);
@@ -178,29 +159,20 @@ describe("markSquadExpired", () => {
178
159
  expect(expiredState.sharedContext).toEqual({ key: "value" });
179
160
  });
180
161
  });
181
- // ============================================================================
182
- // checkTaskTimeout Tests - Fine-grained task observability
183
- // ============================================================================
184
162
  describe("checkTaskTimeout", () => {
185
163
  test("should return null for task that hasn't started", () => {
186
- // Arrange: task without startedAt (pending task)
187
164
  const task = createMockTaskState({ status: "pending" });
188
- // Act
189
165
  const status = checkTaskTimeout(task);
190
- // Assert: null means no timeout check needed
191
166
  expect(status).toBeNull();
192
167
  });
193
168
  test("should return isExpired: false for recently started task", () => {
194
- // Arrange: task started 1 minute ago
195
169
  const now = new Date();
196
170
  const oneMinuteAgo = new Date(now.getTime() - 60 * 1000);
197
171
  const task = createMockTaskState({
198
172
  startedAt: oneMinuteAgo.toISOString(),
199
173
  status: "active",
200
174
  });
201
- // Act
202
175
  const status = checkTaskTimeout(task);
203
- // Assert
204
176
  expect(status).not.toBeNull();
205
177
  expect(status.isExpired).toBe(false);
206
178
  expect(status.elapsedMs).toBeGreaterThanOrEqual(60 * 1000);
@@ -208,59 +180,45 @@ describe("checkTaskTimeout", () => {
208
180
  expect(status.lastActivityAt).toBe(oneMinuteAgo.toISOString());
209
181
  });
210
182
  test("should return isExpired: true for expired task", () => {
211
- // Arrange: task started 10 minutes ago (beyond 5 min default)
212
183
  const now = new Date();
213
184
  const tenMinutesAgo = new Date(now.getTime() - 10 * 60 * 1000);
214
185
  const task = createMockTaskState({
215
186
  startedAt: tenMinutesAgo.toISOString(),
216
187
  status: "active",
217
188
  });
218
- // Act
219
189
  const status = checkTaskTimeout(task);
220
- // Assert
221
190
  expect(status).not.toBeNull();
222
191
  expect(status.isExpired).toBe(true);
223
192
  expect(status.elapsedMs).toBeGreaterThanOrEqual(10 * 60 * 1000);
224
193
  expect(status.remainingMs).toBeLessThan(0);
225
194
  });
226
195
  test("should respect custom timeout parameter", () => {
227
- // Arrange: task started 2 minutes ago
228
196
  const now = new Date();
229
197
  const twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000);
230
198
  const task = createMockTaskState({
231
199
  startedAt: twoMinutesAgo.toISOString(),
232
200
  status: "active",
233
201
  });
234
- const customTimeoutMs = 60 * 1000; // 1 minute
235
- // Act
202
+ const customTimeoutMs = 60 * 1000;
236
203
  const status = checkTaskTimeout(task, customTimeoutMs);
237
- // Assert: 2 minutes > 1 minute, should be expired
238
204
  expect(status).not.toBeNull();
239
205
  expect(status.isExpired).toBe(true);
240
206
  });
241
207
  test("should use default LIMITS.watchdogTimeoutMs when no timeout specified", () => {
242
- // Arrange: task started 3 minutes ago (within default 5 min)
243
208
  const now = new Date();
244
209
  const threeMinutesAgo = new Date(now.getTime() - 3 * 60 * 1000);
245
210
  const task = createMockTaskState({
246
211
  startedAt: threeMinutesAgo.toISOString(),
247
212
  status: "active",
248
213
  });
249
- // Act
250
214
  const status = checkTaskTimeout(task);
251
- // Assert: 3 minutes < 5 minutes, should not be expired
252
215
  expect(status).not.toBeNull();
253
216
  expect(status.isExpired).toBe(false);
254
217
  expect(status.remainingMs).toBeGreaterThan(0);
255
218
  });
256
219
  });
257
- // ============================================================================
258
- // runWatchdog Tests - Periodic Health Check
259
- // "If you can't measure it, you can't improve it." - Brendan Gregg
260
- // ============================================================================
261
220
  describe("runWatchdog", () => {
262
221
  test("should return empty results for fresh squads", async () => {
263
- // Squad was just created, not expired yet
264
222
  const summary = await runWatchdog(session);
265
223
  expect(summary.checkedSquads).toBe(1);
266
224
  expect(summary.checkedTasks).toBe(0);
@@ -268,9 +226,7 @@ describe("runWatchdog", () => {
268
226
  expect(summary.expiredTasks).toHaveLength(0);
269
227
  });
270
228
  test("should detect expired squad", async () => {
271
- // Simulate squad that was updated 10 minutes ago
272
229
  const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
273
- // Manually update the squad's updatedAt via storage (simulate old state)
274
230
  const squadPath = `${session.sessionPath}/squads/${squadId}/state.json`;
275
231
  const currentState = await session.storage.read(squadPath);
276
232
  await session.storage.write(squadPath, {
@@ -283,7 +239,6 @@ describe("runWatchdog", () => {
283
239
  expect(summary.expiredSquads).toHaveLength(1);
284
240
  });
285
241
  test("should mark expired squad as failed when not in dryRun mode", async () => {
286
- // Simulate old squad
287
242
  const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
288
243
  const squadPath = `${session.sessionPath}/squads/${squadId}/state.json`;
289
244
  const currentState = await session.storage.read(squadPath);
@@ -297,7 +252,6 @@ describe("runWatchdog", () => {
297
252
  expect(squad?.state.status).toBe("failed");
298
253
  });
299
254
  test("should NOT mark expired squad as failed in dryRun mode", async () => {
300
- // Simulate old squad
301
255
  const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
302
256
  const squadPath = `${session.sessionPath}/squads/${squadId}/state.json`;
303
257
  const currentState = await session.storage.read(squadPath);
@@ -308,17 +262,14 @@ describe("runWatchdog", () => {
308
262
  });
309
263
  const summary = await runWatchdog(session, { dryRun: true });
310
264
  expect(summary.expiredSquads).toContain(squadId);
311
- // Squad should still be active (not mutated)
312
265
  const squad = await getSquad(session, squadId);
313
266
  expect(squad?.state.status).toBe("active");
314
267
  });
315
268
  test("should detect expired tasks", async () => {
316
- // Create and start a task
317
269
  const task = await assignTask(session, squadId, {
318
270
  assignee: "worker-1",
319
271
  description: "Long running task",
320
272
  });
321
- // Start the task with old startedAt (10 minutes ago)
322
273
  const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000).toISOString();
323
274
  await updateTask(session, squadId, task.taskId, {
324
275
  status: "active",
@@ -348,7 +299,6 @@ describe("runWatchdog", () => {
348
299
  expect(updatedTask?.result).toBeDefined();
349
300
  });
350
301
  test("should use custom timeouts", async () => {
351
- // Create a task that started 2 minutes ago
352
302
  const task = await assignTask(session, squadId, {
353
303
  assignee: "worker-1",
354
304
  description: "Quick task",
@@ -358,10 +308,8 @@ describe("runWatchdog", () => {
358
308
  status: "active",
359
309
  startedAt: twoMinutesAgo,
360
310
  });
361
- // With default timeout (5 min), should NOT be expired
362
311
  const defaultSummary = await runWatchdog(session, { dryRun: true });
363
312
  expect(defaultSummary.expiredTasks).toHaveLength(0);
364
- // With short timeout (1 min), SHOULD be expired
365
313
  const shortSummary = await runWatchdog(session, {
366
314
  taskTimeoutMs: 60 * 1000,
367
315
  dryRun: true,
@@ -372,7 +320,6 @@ describe("runWatchdog", () => {
372
320
  await updateSquadState(session, squadId, {
373
321
  status: "completed",
374
322
  });
375
- // Even with old updatedAt, completed squads are skipped
376
323
  const squadPath = `${session.sessionPath}/squads/${squadId}/state.json`;
377
324
  const currentState = await session.storage.read(squadPath);
378
325
  await session.storage.write(squadPath, {
@@ -384,12 +331,10 @@ describe("runWatchdog", () => {
384
331
  expect(summary.expiredSquads).toHaveLength(0);
385
332
  });
386
333
  test("should only check active tasks with startedAt", async () => {
387
- // Task 1: pending (no startedAt) - should not be checked
388
334
  await assignTask(session, squadId, {
389
335
  assignee: "worker-1",
390
336
  description: "Pending task",
391
337
  });
392
- // Task 2: active with startedAt - should be checked
393
338
  const activeTask = await assignTask(session, squadId, {
394
339
  assignee: "worker-2",
395
340
  description: "Active task",