@stackmemoryai/stackmemory 0.2.7 → 0.2.8

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 (297) hide show
  1. package/README.md +25 -8
  2. package/dist/scripts/cancel-duplicate-tasks.js +2 -1
  3. package/dist/scripts/cancel-duplicate-tasks.js.map +1 -1
  4. package/dist/scripts/list-linear-tasks.js +3 -4
  5. package/dist/scripts/list-linear-tasks.js.map +1 -1
  6. package/dist/scripts/merge-linear-duplicates-safe.js +4 -2
  7. package/dist/scripts/merge-linear-duplicates-safe.js.map +1 -1
  8. package/dist/scripts/show-linear-summary.js +4 -1
  9. package/dist/scripts/show-linear-summary.js.map +1 -1
  10. package/dist/scripts/status.js +6 -2
  11. package/dist/scripts/status.js.map +1 -1
  12. package/dist/src/cli/auto-detect.js.map +1 -1
  13. package/dist/src/cli/claude-sm.js.map +1 -1
  14. package/dist/src/cli/commands/config.d.ts +6 -0
  15. package/dist/src/cli/commands/config.d.ts.map +1 -0
  16. package/dist/src/cli/commands/config.js +224 -0
  17. package/dist/src/cli/commands/config.js.map +1 -0
  18. package/dist/src/cli/commands/linear.d.ts.map +1 -1
  19. package/dist/src/cli/commands/linear.js +123 -47
  20. package/dist/src/cli/commands/linear.js.map +1 -1
  21. package/dist/src/cli/index.d.ts.map +1 -1
  22. package/dist/src/cli/index.js +48 -1
  23. package/dist/src/cli/index.js.map +1 -1
  24. package/dist/src/core/config/config-manager.d.ts +95 -0
  25. package/dist/src/core/config/config-manager.d.ts.map +1 -0
  26. package/dist/src/core/config/config-manager.js +359 -0
  27. package/dist/src/core/config/config-manager.js.map +1 -0
  28. package/dist/src/core/config/types.d.ts +72 -0
  29. package/dist/src/core/config/types.d.ts.map +1 -0
  30. package/dist/src/core/config/types.js +127 -0
  31. package/dist/src/core/config/types.js.map +1 -0
  32. package/dist/src/core/context/compaction-handler.d.ts +119 -0
  33. package/dist/src/core/context/compaction-handler.d.ts.map +1 -0
  34. package/dist/src/core/context/compaction-handler.js +306 -0
  35. package/dist/src/core/context/compaction-handler.js.map +1 -0
  36. package/dist/src/core/context/model-aware-compaction.d.ts +101 -0
  37. package/dist/src/core/context/model-aware-compaction.d.ts.map +1 -0
  38. package/dist/src/core/context/model-aware-compaction.js +616 -0
  39. package/dist/src/core/context/model-aware-compaction.js.map +1 -0
  40. package/dist/src/core/query/query-parser.d.ts +104 -0
  41. package/dist/src/core/query/query-parser.d.ts.map +1 -0
  42. package/dist/src/core/query/query-parser.js +347 -0
  43. package/dist/src/core/query/query-parser.js.map +1 -0
  44. package/dist/src/core/retrieval/index.d.ts +8 -0
  45. package/dist/src/core/retrieval/index.d.ts.map +1 -0
  46. package/dist/src/core/retrieval/index.js +8 -0
  47. package/dist/src/core/retrieval/index.js.map +1 -0
  48. package/dist/src/core/retrieval/llm-context-retrieval.d.ts +71 -0
  49. package/dist/src/core/retrieval/llm-context-retrieval.d.ts.map +1 -0
  50. package/dist/src/core/retrieval/llm-context-retrieval.js +545 -0
  51. package/dist/src/core/retrieval/llm-context-retrieval.js.map +1 -0
  52. package/dist/src/core/retrieval/summary-generator.d.ts +63 -0
  53. package/dist/src/core/retrieval/summary-generator.d.ts.map +1 -0
  54. package/dist/src/core/retrieval/summary-generator.js +622 -0
  55. package/dist/src/core/retrieval/summary-generator.js.map +1 -0
  56. package/dist/src/core/retrieval/types.d.ts +257 -0
  57. package/dist/src/core/retrieval/types.d.ts.map +1 -0
  58. package/dist/src/core/retrieval/types.js +18 -0
  59. package/dist/src/core/retrieval/types.js.map +1 -0
  60. package/dist/src/core/trace/trace-detector.d.ts +108 -0
  61. package/dist/src/core/trace/trace-detector.d.ts.map +1 -0
  62. package/dist/src/core/trace/trace-detector.demo.d.ts +5 -0
  63. package/dist/src/core/trace/trace-detector.demo.d.ts.map +1 -0
  64. package/dist/src/core/trace/trace-detector.demo.js +145 -0
  65. package/dist/src/core/trace/trace-detector.demo.js.map +1 -0
  66. package/dist/src/core/trace/trace-detector.js +425 -0
  67. package/dist/src/core/trace/trace-detector.js.map +1 -0
  68. package/dist/src/core/trace/trace-store.d.ts +60 -0
  69. package/dist/src/core/trace/trace-store.d.ts.map +1 -0
  70. package/dist/src/core/trace/trace-store.js +323 -0
  71. package/dist/src/core/trace/trace-store.js.map +1 -0
  72. package/dist/src/core/trace/types.d.ts +81 -0
  73. package/dist/src/core/trace/types.d.ts.map +1 -0
  74. package/dist/src/core/trace/types.js +70 -0
  75. package/dist/src/core/trace/types.js.map +1 -0
  76. package/dist/src/integrations/linear/sync-manager.d.ts +76 -0
  77. package/dist/src/integrations/linear/sync-manager.d.ts.map +1 -0
  78. package/dist/src/integrations/linear/sync-manager.js +223 -0
  79. package/dist/src/integrations/linear/sync-manager.js.map +1 -0
  80. package/dist/src/integrations/mcp/server.d.ts +8 -0
  81. package/dist/src/integrations/mcp/server.d.ts.map +1 -1
  82. package/dist/src/integrations/mcp/server.js +368 -16
  83. package/dist/src/integrations/mcp/server.js.map +1 -1
  84. package/dist/src/integrations/mcp/trace-test.d.ts +5 -0
  85. package/dist/src/integrations/mcp/trace-test.d.ts.map +1 -0
  86. package/dist/src/integrations/mcp/trace-test.js +54 -0
  87. package/dist/src/integrations/mcp/trace-test.js.map +1 -0
  88. package/dist/src/services/config-service.d.ts +1 -1
  89. package/dist/src/services/config-service.d.ts.map +1 -1
  90. package/dist/src/services/config-service.js.map +1 -1
  91. package/dist/src/types/task.d.ts +11 -1
  92. package/dist/src/types/task.d.ts.map +1 -1
  93. package/dist/src/utils/logger.d.ts +4 -4
  94. package/dist/src/utils/logger.d.ts.map +1 -1
  95. package/dist/src/utils/logger.js.map +1 -1
  96. package/package.json +9 -8
  97. package/dist/attention-scoring/src/attention-tracker.d.ts +0 -79
  98. package/dist/attention-scoring/src/attention-tracker.d.ts.map +0 -1
  99. package/dist/attention-scoring/src/attention-tracker.js +0 -488
  100. package/dist/attention-scoring/src/attention-tracker.js.map +0 -1
  101. package/dist/attention-scoring/src/mcp-integration.d.ts +0 -56
  102. package/dist/attention-scoring/src/mcp-integration.d.ts.map +0 -1
  103. package/dist/attention-scoring/src/mcp-integration.js +0 -369
  104. package/dist/attention-scoring/src/mcp-integration.js.map +0 -1
  105. package/dist/index.js +0 -382
  106. package/dist/p2p-sync/src/p2p-sync.d.ts +0 -81
  107. package/dist/p2p-sync/src/p2p-sync.d.ts.map +0 -1
  108. package/dist/p2p-sync/src/p2p-sync.js +0 -457
  109. package/dist/p2p-sync/src/p2p-sync.js.map +0 -1
  110. package/dist/p2p-sync/src/team-context-sync.d.ts +0 -99
  111. package/dist/p2p-sync/src/team-context-sync.d.ts.map +0 -1
  112. package/dist/p2p-sync/src/team-context-sync.js +0 -491
  113. package/dist/p2p-sync/src/team-context-sync.js.map +0 -1
  114. package/dist/scripts/merge-linear-duplicates.d.ts +0 -7
  115. package/dist/scripts/merge-linear-duplicates.d.ts.map +0 -1
  116. package/dist/scripts/merge-linear-duplicates.js +0 -126
  117. package/dist/scripts/merge-linear-duplicates.js.map +0 -1
  118. package/dist/src/analytics/api/analytics-api.d.ts +0 -24
  119. package/dist/src/analytics/api/analytics-api.d.ts.map +0 -1
  120. package/dist/src/analytics/api/analytics-api.js +0 -279
  121. package/dist/src/analytics/api/analytics-api.js.map +0 -1
  122. package/dist/src/analytics/core/analytics-service.d.ts +0 -23
  123. package/dist/src/analytics/core/analytics-service.d.ts.map +0 -1
  124. package/dist/src/analytics/core/analytics-service.js +0 -160
  125. package/dist/src/analytics/core/analytics-service.js.map +0 -1
  126. package/dist/src/analytics/index.d.ts +0 -12
  127. package/dist/src/analytics/index.d.ts.map +0 -1
  128. package/dist/src/analytics/index.js +0 -11
  129. package/dist/src/analytics/index.js.map +0 -1
  130. package/dist/src/analytics/queries/metrics-queries.d.ts +0 -11
  131. package/dist/src/analytics/queries/metrics-queries.d.ts.map +0 -1
  132. package/dist/src/analytics/queries/metrics-queries.js +0 -179
  133. package/dist/src/analytics/queries/metrics-queries.js.map +0 -1
  134. package/dist/src/analytics/types/metrics.d.ts +0 -60
  135. package/dist/src/analytics/types/metrics.d.ts.map +0 -1
  136. package/dist/src/analytics/types/metrics.js +0 -2
  137. package/dist/src/analytics/types/metrics.js.map +0 -1
  138. package/dist/src/beads/beads-task-store.d.ts +0 -117
  139. package/dist/src/beads/beads-task-store.d.ts.map +0 -1
  140. package/dist/src/beads/beads-task-store.js +0 -318
  141. package/dist/src/beads/beads-task-store.js.map +0 -1
  142. package/dist/src/beads/task-aware-context.d.ts +0 -103
  143. package/dist/src/beads/task-aware-context.d.ts.map +0 -1
  144. package/dist/src/beads/task-aware-context.js +0 -395
  145. package/dist/src/beads/task-aware-context.js.map +0 -1
  146. package/dist/src/beads-task-store.d.ts +0 -117
  147. package/dist/src/beads-task-store.d.ts.map +0 -1
  148. package/dist/src/beads-task-store.js +0 -318
  149. package/dist/src/beads-task-store.js.map +0 -1
  150. package/dist/src/cli/__tests__/index.test.d.ts +0 -5
  151. package/dist/src/cli/__tests__/index.test.d.ts.map +0 -1
  152. package/dist/src/cli/__tests__/index.test.js +0 -726
  153. package/dist/src/cli/__tests__/index.test.js.map +0 -1
  154. package/dist/src/cli/analytics-viewer.d.ts +0 -3
  155. package/dist/src/cli/analytics-viewer.d.ts.map +0 -1
  156. package/dist/src/cli/analytics-viewer.js +0 -89
  157. package/dist/src/cli/analytics-viewer.js.map +0 -1
  158. package/dist/src/cli/cli.d.ts +0 -7
  159. package/dist/src/cli/cli.d.ts.map +0 -1
  160. package/dist/src/cli/cli.js +0 -704
  161. package/dist/src/cli/cli.js.map +0 -1
  162. package/dist/src/cli/project-commands.d.ts +0 -8
  163. package/dist/src/cli/project-commands.d.ts.map +0 -1
  164. package/dist/src/cli/project-commands.js +0 -212
  165. package/dist/src/cli/project-commands.js.map +0 -1
  166. package/dist/src/cli.d.ts +0 -7
  167. package/dist/src/cli.d.ts.map +0 -1
  168. package/dist/src/cli.js +0 -73
  169. package/dist/src/cli.js.map +0 -1
  170. package/dist/src/core/context/__tests__/frame-manager.test.d.ts +0 -5
  171. package/dist/src/core/context/__tests__/frame-manager.test.d.ts.map +0 -1
  172. package/dist/src/core/context/__tests__/frame-manager.test.js +0 -892
  173. package/dist/src/core/context/__tests__/frame-manager.test.js.map +0 -1
  174. package/dist/src/core/error-handler.d.ts +0 -46
  175. package/dist/src/core/error-handler.d.ts.map +0 -1
  176. package/dist/src/core/error-handler.js +0 -212
  177. package/dist/src/core/error-handler.js.map +0 -1
  178. package/dist/src/core/errors/__tests__/error-handling.test.d.ts +0 -5
  179. package/dist/src/core/errors/__tests__/error-handling.test.d.ts.map +0 -1
  180. package/dist/src/core/errors/__tests__/error-handling.test.js +0 -239
  181. package/dist/src/core/errors/__tests__/error-handling.test.js.map +0 -1
  182. package/dist/src/core/frame-manager.d.ts +0 -106
  183. package/dist/src/core/frame-manager.d.ts.map +0 -1
  184. package/dist/src/core/frame-manager.js +0 -387
  185. package/dist/src/core/frame-manager.js.map +0 -1
  186. package/dist/src/core/logger.d.ts +0 -24
  187. package/dist/src/core/logger.d.ts.map +0 -1
  188. package/dist/src/core/logger.js +0 -121
  189. package/dist/src/core/logger.js.map +0 -1
  190. package/dist/src/core/logger.test.d.ts +0 -2
  191. package/dist/src/core/logger.test.d.ts.map +0 -1
  192. package/dist/src/core/logger.test.js +0 -31
  193. package/dist/src/core/logger.test.js.map +0 -1
  194. package/dist/src/core/progress-tracker.d.ts +0 -95
  195. package/dist/src/core/progress-tracker.d.ts.map +0 -1
  196. package/dist/src/core/progress-tracker.js +0 -178
  197. package/dist/src/core/progress-tracker.js.map +0 -1
  198. package/dist/src/core/project-manager.d.ts +0 -130
  199. package/dist/src/core/project-manager.d.ts.map +0 -1
  200. package/dist/src/core/project-manager.js +0 -582
  201. package/dist/src/core/project-manager.js.map +0 -1
  202. package/dist/src/core/update-checker.d.ts +0 -38
  203. package/dist/src/core/update-checker.d.ts.map +0 -1
  204. package/dist/src/core/update-checker.js +0 -156
  205. package/dist/src/core/update-checker.js.map +0 -1
  206. package/dist/src/error-handler.d.ts +0 -42
  207. package/dist/src/error-handler.d.ts.map +0 -1
  208. package/dist/src/error-handler.js +0 -155
  209. package/dist/src/error-handler.js.map +0 -1
  210. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts +0 -5
  211. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.d.ts.map +0 -1
  212. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js +0 -712
  213. package/dist/src/features/tasks/__tests__/pebbles-task-store.test.js.map +0 -1
  214. package/dist/src/frame-manager.d.ts +0 -106
  215. package/dist/src/frame-manager.d.ts.map +0 -1
  216. package/dist/src/frame-manager.js +0 -361
  217. package/dist/src/frame-manager.js.map +0 -1
  218. package/dist/src/integrations/browser-mcp.d.ts +0 -94
  219. package/dist/src/integrations/browser-mcp.d.ts.map +0 -1
  220. package/dist/src/integrations/browser-mcp.js +0 -431
  221. package/dist/src/integrations/browser-mcp.js.map +0 -1
  222. package/dist/src/integrations/linear/__tests__/auth.test.d.ts +0 -5
  223. package/dist/src/integrations/linear/__tests__/auth.test.d.ts.map +0 -1
  224. package/dist/src/integrations/linear/__tests__/auth.test.js +0 -517
  225. package/dist/src/integrations/linear/__tests__/auth.test.js.map +0 -1
  226. package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts +0 -5
  227. package/dist/src/integrations/linear/__tests__/sync-service.test.d.ts.map +0 -1
  228. package/dist/src/integrations/linear/__tests__/sync-service.test.js +0 -700
  229. package/dist/src/integrations/linear/__tests__/sync-service.test.js.map +0 -1
  230. package/dist/src/integrations/linear-auth.d.ts +0 -99
  231. package/dist/src/integrations/linear-auth.d.ts.map +0 -1
  232. package/dist/src/integrations/linear-auth.js +0 -319
  233. package/dist/src/integrations/linear-auth.js.map +0 -1
  234. package/dist/src/integrations/linear-auto-sync.d.ts +0 -77
  235. package/dist/src/integrations/linear-auto-sync.d.ts.map +0 -1
  236. package/dist/src/integrations/linear-auto-sync.js +0 -268
  237. package/dist/src/integrations/linear-auto-sync.js.map +0 -1
  238. package/dist/src/integrations/linear-client.d.ts +0 -86
  239. package/dist/src/integrations/linear-client.d.ts.map +0 -1
  240. package/dist/src/integrations/linear-client.js +0 -277
  241. package/dist/src/integrations/linear-client.js.map +0 -1
  242. package/dist/src/integrations/linear-config.d.ts +0 -51
  243. package/dist/src/integrations/linear-config.d.ts.map +0 -1
  244. package/dist/src/integrations/linear-config.js +0 -103
  245. package/dist/src/integrations/linear-config.js.map +0 -1
  246. package/dist/src/integrations/linear-sync.d.ts +0 -97
  247. package/dist/src/integrations/linear-sync.d.ts.map +0 -1
  248. package/dist/src/integrations/linear-sync.js +0 -391
  249. package/dist/src/integrations/linear-sync.js.map +0 -1
  250. package/dist/src/integrations/mcp/__tests__/server.test.d.ts +0 -5
  251. package/dist/src/integrations/mcp/__tests__/server.test.d.ts.map +0 -1
  252. package/dist/src/integrations/mcp/__tests__/server.test.js +0 -790
  253. package/dist/src/integrations/mcp/__tests__/server.test.js.map +0 -1
  254. package/dist/src/logger.d.ts +0 -24
  255. package/dist/src/logger.d.ts.map +0 -1
  256. package/dist/src/logger.js +0 -120
  257. package/dist/src/logger.js.map +0 -1
  258. package/dist/src/mcp/mcp-server.d.ts +0 -40
  259. package/dist/src/mcp/mcp-server.d.ts.map +0 -1
  260. package/dist/src/mcp/mcp-server.js +0 -828
  261. package/dist/src/mcp/mcp-server.js.map +0 -1
  262. package/dist/src/mcp-server.d.ts +0 -32
  263. package/dist/src/mcp-server.d.ts.map +0 -1
  264. package/dist/src/mcp-server.js +0 -441
  265. package/dist/src/mcp-server.js.map +0 -1
  266. package/dist/src/pebbles/pebbles-task-store.d.ts +0 -117
  267. package/dist/src/pebbles/pebbles-task-store.d.ts.map +0 -1
  268. package/dist/src/pebbles/pebbles-task-store.js +0 -335
  269. package/dist/src/pebbles/pebbles-task-store.js.map +0 -1
  270. package/dist/src/pebbles/task-aware-context.d.ts +0 -103
  271. package/dist/src/pebbles/task-aware-context.d.ts.map +0 -1
  272. package/dist/src/pebbles/task-aware-context.js +0 -412
  273. package/dist/src/pebbles/task-aware-context.js.map +0 -1
  274. package/dist/src/railway/index.d.ts +0 -7
  275. package/dist/src/railway/index.d.ts.map +0 -1
  276. package/dist/src/railway/index.js +0 -401
  277. package/dist/src/railway/index.js.map +0 -1
  278. package/dist/src/runway/auth/auth-middleware.d.ts +0 -66
  279. package/dist/src/runway/auth/auth-middleware.d.ts.map +0 -1
  280. package/dist/src/runway/auth/auth-middleware.js +0 -337
  281. package/dist/src/runway/auth/auth-middleware.js.map +0 -1
  282. package/dist/src/runway/server/runway-mcp-server.d.ts +0 -46
  283. package/dist/src/runway/server/runway-mcp-server.d.ts.map +0 -1
  284. package/dist/src/runway/server/runway-mcp-server.js +0 -601
  285. package/dist/src/runway/server/runway-mcp-server.js.map +0 -1
  286. package/dist/src/runway.bak/auth/auth-middleware.d.ts +0 -66
  287. package/dist/src/runway.bak/auth/auth-middleware.d.ts.map +0 -1
  288. package/dist/src/runway.bak/auth/auth-middleware.js +0 -337
  289. package/dist/src/runway.bak/auth/auth-middleware.js.map +0 -1
  290. package/dist/src/runway.bak/server/runway-mcp-server.d.ts +0 -46
  291. package/dist/src/runway.bak/server/runway-mcp-server.d.ts.map +0 -1
  292. package/dist/src/runway.bak/server/runway-mcp-server.js +0 -601
  293. package/dist/src/runway.bak/server/runway-mcp-server.js.map +0 -1
  294. package/dist/src/task-aware-context.d.ts +0 -103
  295. package/dist/src/task-aware-context.d.ts.map +0 -1
  296. package/dist/src/task-aware-context.js +0 -395
  297. package/dist/src/task-aware-context.js.map +0 -1
@@ -1,712 +0,0 @@
1
- /**
2
- * Tests for PebblesTaskStore - Git-native JSONL storage with SQLite cache
3
- */
4
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
5
- import Database from 'better-sqlite3';
6
- import { PebblesTaskStore } from '../pebbles-task-store.js';
7
- import { DatabaseError, TaskError } from '../../../core/errors/index.js';
8
- import { join } from 'path';
9
- import { mkdtempSync, rmSync, existsSync, readFileSync, writeFileSync } from 'fs';
10
- import { tmpdir } from 'os';
11
- describe('PebblesTaskStore', () => {
12
- let db;
13
- let taskStore;
14
- let tempDir;
15
- let projectRoot;
16
- beforeEach(() => {
17
- // Create a temporary directory for test
18
- tempDir = mkdtempSync(join(tmpdir(), 'stackmemory-task-test-'));
19
- projectRoot = tempDir;
20
- // Initialize database
21
- const dbPath = join(tempDir, '.stackmemory', 'context.db');
22
- db = new Database(dbPath);
23
- // Create task store
24
- taskStore = new PebblesTaskStore(projectRoot, db);
25
- });
26
- afterEach(() => {
27
- // Clean up
28
- if (db) {
29
- db.close();
30
- }
31
- if (tempDir) {
32
- rmSync(tempDir, { recursive: true, force: true });
33
- }
34
- });
35
- describe('Initialization', () => {
36
- it('should create .stackmemory directory if it does not exist', () => {
37
- const stackmemoryDir = join(projectRoot, '.stackmemory');
38
- expect(existsSync(stackmemoryDir)).toBe(true);
39
- });
40
- it('should initialize cache database schema correctly', () => {
41
- // Check if task_cache table exists
42
- const tables = db.prepare(`
43
- SELECT name FROM sqlite_master
44
- WHERE type='table' AND name='task_cache'
45
- `).all();
46
- expect(tables).toHaveLength(1);
47
- });
48
- it('should create required indexes', () => {
49
- const indexes = db.prepare(`
50
- SELECT name FROM sqlite_master
51
- WHERE type='index' AND name LIKE 'idx_task_%'
52
- `).all();
53
- const expectedIndexes = [
54
- 'idx_task_status',
55
- 'idx_task_priority',
56
- 'idx_task_frame',
57
- 'idx_task_timestamp',
58
- 'idx_task_parent'
59
- ];
60
- expectedIndexes.forEach(expectedIndex => {
61
- expect(indexes.some((idx) => idx.name === expectedIndex)).toBe(true);
62
- });
63
- });
64
- it('should load existing tasks from JSONL file', () => {
65
- // Create pre-existing JSONL file
66
- const tasksFile = join(projectRoot, '.stackmemory', 'tasks.jsonl');
67
- const existingTask = {
68
- id: 'test-123',
69
- type: 'task_create',
70
- timestamp: Math.floor(Date.now() / 1000),
71
- frame_id: 'frame-123',
72
- title: 'Existing Task',
73
- description: 'Pre-existing task',
74
- status: 'pending',
75
- priority: 'medium',
76
- created_at: Math.floor(Date.now() / 1000),
77
- depends_on: [],
78
- blocks: [],
79
- tags: ['test'],
80
- context_score: 0.5
81
- };
82
- writeFileSync(tasksFile, JSON.stringify(existingTask) + '\n');
83
- // Create new task store instance
84
- const newTaskStore = new PebblesTaskStore(projectRoot, db);
85
- // Should load the existing task
86
- const loadedTask = newTaskStore.getTask('test-123');
87
- expect(loadedTask).toBeDefined();
88
- expect(loadedTask.title).toBe('Existing Task');
89
- });
90
- it('should handle corrupted JSONL lines gracefully', () => {
91
- const tasksFile = join(projectRoot, '.stackmemory', 'tasks.jsonl');
92
- const content = `
93
- {"valid": "task", "id": "valid-1", "type": "task_create", "timestamp": 1, "frame_id": "f1", "title": "Valid", "status": "pending", "priority": "medium", "created_at": 1, "depends_on": [], "blocks": [], "tags": []}
94
- {invalid json line
95
- {"id": "valid-2", "type": "task_create", "timestamp": 2, "frame_id": "f2", "title": "Valid 2", "status": "pending", "priority": "high", "created_at": 2, "depends_on": [], "blocks": [], "tags": []}
96
- `.trim();
97
- writeFileSync(tasksFile, content);
98
- // Should load only valid tasks and skip corrupted lines
99
- const newTaskStore = new PebblesTaskStore(projectRoot, db);
100
- expect(newTaskStore.getTask('valid-1')).toBeDefined();
101
- expect(newTaskStore.getTask('valid-2')).toBeDefined();
102
- });
103
- });
104
- describe('Task Creation', () => {
105
- it('should create a new task successfully', () => {
106
- const taskId = taskStore.createTask({
107
- title: 'Test Task',
108
- description: 'A test task',
109
- priority: 'high',
110
- frameId: 'frame-123',
111
- tags: ['test', 'urgent'],
112
- estimatedEffort: 60,
113
- assignee: 'developer'
114
- });
115
- expect(taskId).toBeDefined();
116
- expect(taskId).toMatch(/^tsk-[a-f0-9]{8}$/);
117
- const task = taskStore.getTask(taskId);
118
- expect(task).toBeDefined();
119
- expect(task.title).toBe('Test Task');
120
- expect(task.description).toBe('A test task');
121
- expect(task.priority).toBe('high');
122
- expect(task.status).toBe('pending');
123
- expect(task.frame_id).toBe('frame-123');
124
- expect(task.tags).toEqual(['test', 'urgent']);
125
- expect(task.estimated_effort).toBe(60);
126
- expect(task.assignee).toBe('developer');
127
- expect(task.depends_on).toEqual([]);
128
- expect(task.blocks).toEqual([]);
129
- });
130
- it('should create task with minimal required fields', () => {
131
- const taskId = taskStore.createTask({
132
- title: 'Minimal Task',
133
- frameId: 'frame-456'
134
- });
135
- const task = taskStore.getTask(taskId);
136
- expect(task).toBeDefined();
137
- expect(task.title).toBe('Minimal Task');
138
- expect(task.frame_id).toBe('frame-456');
139
- expect(task.priority).toBe('medium');
140
- expect(task.status).toBe('pending');
141
- expect(task.description).toBeUndefined();
142
- expect(task.tags).toEqual([]);
143
- });
144
- it('should create task with parent relationship', () => {
145
- const parentId = taskStore.createTask({
146
- title: 'Parent Task',
147
- frameId: 'frame-parent'
148
- });
149
- const childId = taskStore.createTask({
150
- title: 'Child Task',
151
- frameId: 'frame-child',
152
- parentId
153
- });
154
- const childTask = taskStore.getTask(childId);
155
- expect(childTask.parent_id).toBe(parentId);
156
- });
157
- it('should create task with dependencies', () => {
158
- const dep1Id = taskStore.createTask({
159
- title: 'Dependency 1',
160
- frameId: 'frame-dep1'
161
- });
162
- const dep2Id = taskStore.createTask({
163
- title: 'Dependency 2',
164
- frameId: 'frame-dep2'
165
- });
166
- const taskId = taskStore.createTask({
167
- title: 'Dependent Task',
168
- frameId: 'frame-main',
169
- dependsOn: [dep1Id, dep2Id]
170
- });
171
- const task = taskStore.getTask(taskId);
172
- expect(task.depends_on).toEqual([dep1Id, dep2Id]);
173
- });
174
- it('should append task to JSONL file', () => {
175
- const taskId = taskStore.createTask({
176
- title: 'JSONL Test',
177
- frameId: 'frame-jsonl'
178
- });
179
- const tasksFile = join(projectRoot, '.stackmemory', 'tasks.jsonl');
180
- expect(existsSync(tasksFile)).toBe(true);
181
- const content = readFileSync(tasksFile, 'utf-8');
182
- expect(content).toContain(taskId);
183
- expect(content).toContain('JSONL Test');
184
- });
185
- });
186
- describe('Task Status Updates', () => {
187
- let taskId;
188
- beforeEach(() => {
189
- taskId = taskStore.createTask({
190
- title: 'Status Test Task',
191
- frameId: 'frame-status'
192
- });
193
- });
194
- it('should update task status from pending to in_progress', () => {
195
- taskStore.updateTaskStatus(taskId, 'in_progress');
196
- const task = taskStore.getTask(taskId);
197
- expect(task.status).toBe('in_progress');
198
- expect(task.started_at).toBeDefined();
199
- });
200
- it('should update task status from in_progress to completed', () => {
201
- // First set to in_progress
202
- taskStore.updateTaskStatus(taskId, 'in_progress');
203
- // Then complete it
204
- taskStore.updateTaskStatus(taskId, 'completed');
205
- const task = taskStore.getTask(taskId);
206
- expect(task.status).toBe('completed');
207
- expect(task.completed_at).toBeDefined();
208
- expect(task.actual_effort).toBeDefined();
209
- });
210
- it('should update task status to blocked', () => {
211
- taskStore.updateTaskStatus(taskId, 'blocked', 'Waiting for external API');
212
- const task = taskStore.getTask(taskId);
213
- expect(task.status).toBe('blocked');
214
- expect(task.type).toBe('task_block');
215
- });
216
- it('should update task status to cancelled', () => {
217
- taskStore.updateTaskStatus(taskId, 'cancelled');
218
- const task = taskStore.getTask(taskId);
219
- expect(task.status).toBe('cancelled');
220
- });
221
- it('should throw error when updating non-existent task', () => {
222
- expect(() => {
223
- taskStore.updateTaskStatus('non-existent', 'completed');
224
- }).toThrow(TaskError);
225
- });
226
- it('should throw error when changing completed task status', () => {
227
- taskStore.updateTaskStatus(taskId, 'in_progress');
228
- taskStore.updateTaskStatus(taskId, 'completed');
229
- expect(() => {
230
- taskStore.updateTaskStatus(taskId, 'in_progress');
231
- }).toThrow(TaskError);
232
- });
233
- it('should allow changing completed task to cancelled', () => {
234
- taskStore.updateTaskStatus(taskId, 'in_progress');
235
- taskStore.updateTaskStatus(taskId, 'completed');
236
- // Should allow completed -> cancelled
237
- expect(() => {
238
- taskStore.updateTaskStatus(taskId, 'cancelled');
239
- }).not.toThrow();
240
- const task = taskStore.getTask(taskId);
241
- expect(task.status).toBe('cancelled');
242
- });
243
- it('should calculate actual effort correctly', () => {
244
- const startTime = Date.now();
245
- taskStore.updateTaskStatus(taskId, 'in_progress');
246
- // Simulate some work time
247
- vi.useFakeTimers();
248
- vi.advanceTimersByTime(30 * 60 * 1000); // 30 minutes
249
- taskStore.updateTaskStatus(taskId, 'completed');
250
- const task = taskStore.getTask(taskId);
251
- expect(task.actual_effort).toBe(30); // 30 minutes
252
- vi.useRealTimers();
253
- });
254
- it('should update task type based on status change', () => {
255
- const task1 = taskStore.getTask(taskId);
256
- expect(task1.type).toBe('task_create');
257
- taskStore.updateTaskStatus(taskId, 'in_progress');
258
- const task2 = taskStore.getTask(taskId);
259
- expect(task2.type).toBe('task_update');
260
- taskStore.updateTaskStatus(taskId, 'completed');
261
- const task3 = taskStore.getTask(taskId);
262
- expect(task3.type).toBe('task_complete');
263
- const blockedTaskId = taskStore.createTask({
264
- title: 'Blocked Task',
265
- frameId: 'frame-blocked'
266
- });
267
- taskStore.updateTaskStatus(blockedTaskId, 'blocked');
268
- const task4 = taskStore.getTask(blockedTaskId);
269
- expect(task4.type).toBe('task_block');
270
- });
271
- });
272
- describe('Task Dependencies', () => {
273
- let task1Id;
274
- let task2Id;
275
- beforeEach(() => {
276
- task1Id = taskStore.createTask({
277
- title: 'Task 1',
278
- frameId: 'frame-1'
279
- });
280
- task2Id = taskStore.createTask({
281
- title: 'Task 2',
282
- frameId: 'frame-2'
283
- });
284
- });
285
- it('should add dependency relationship', () => {
286
- taskStore.addDependency(task2Id, task1Id);
287
- const task2 = taskStore.getTask(task2Id);
288
- const task1 = taskStore.getTask(task1Id);
289
- expect(task2.depends_on).toContain(task1Id);
290
- expect(task1.blocks).toContain(task2Id);
291
- });
292
- it('should prevent duplicate dependencies', () => {
293
- taskStore.addDependency(task2Id, task1Id);
294
- taskStore.addDependency(task2Id, task1Id); // Add again
295
- const task2 = taskStore.getTask(task2Id);
296
- expect(task2.depends_on.filter(id => id === task1Id)).toHaveLength(1);
297
- });
298
- it('should throw error for non-existent task', () => {
299
- expect(() => {
300
- taskStore.addDependency('non-existent', task1Id);
301
- }).toThrow(TaskError);
302
- expect(() => {
303
- taskStore.addDependency(task2Id, 'non-existent');
304
- }).toThrow(TaskError);
305
- });
306
- it('should detect circular dependencies', () => {
307
- taskStore.addDependency(task2Id, task1Id); // task2 depends on task1
308
- // Try to make task1 depend on task2 (circular)
309
- expect(() => {
310
- taskStore.addDependency(task1Id, task2Id);
311
- }).toThrow(TaskError);
312
- });
313
- it('should detect complex circular dependencies', () => {
314
- const task3Id = taskStore.createTask({
315
- title: 'Task 3',
316
- frameId: 'frame-3'
317
- });
318
- const task4Id = taskStore.createTask({
319
- title: 'Task 4',
320
- frameId: 'frame-4'
321
- });
322
- // Create chain: task1 -> task2 -> task3 -> task4
323
- taskStore.addDependency(task2Id, task1Id);
324
- taskStore.addDependency(task3Id, task2Id);
325
- taskStore.addDependency(task4Id, task3Id);
326
- // Try to make task1 depend on task4 (circular)
327
- expect(() => {
328
- taskStore.addDependency(task1Id, task4Id);
329
- }).toThrow(TaskError);
330
- });
331
- it('should handle self-dependency prevention', () => {
332
- expect(() => {
333
- taskStore.addDependency(task1Id, task1Id);
334
- }).toThrow(TaskError);
335
- });
336
- });
337
- describe('Task Queries', () => {
338
- beforeEach(() => {
339
- // Create various tasks for testing
340
- const tasks = [
341
- { title: 'Active Task 1', status: 'pending', priority: 'high', frameId: 'frame-1' },
342
- { title: 'Active Task 2', status: 'in_progress', priority: 'medium', frameId: 'frame-1' },
343
- { title: 'Completed Task', status: 'completed', priority: 'low', frameId: 'frame-2' },
344
- { title: 'Blocked Task', status: 'blocked', priority: 'urgent', frameId: 'frame-3' },
345
- { title: 'Cancelled Task', status: 'cancelled', priority: 'medium', frameId: 'frame-3' }
346
- ];
347
- tasks.forEach(task => {
348
- const taskId = taskStore.createTask(task);
349
- if (task.status !== 'pending') {
350
- taskStore.updateTaskStatus(taskId, task.status);
351
- }
352
- });
353
- });
354
- it('should get all active tasks', () => {
355
- const activeTasks = taskStore.getActiveTasks();
356
- expect(activeTasks).toHaveLength(3); // pending, in_progress, blocked
357
- expect(activeTasks.every(t => ['pending', 'in_progress'].includes(t.status))).toBe(true);
358
- });
359
- it('should get active tasks filtered by frame', () => {
360
- const frame1Tasks = taskStore.getActiveTasks('frame-1');
361
- expect(frame1Tasks).toHaveLength(2);
362
- expect(frame1Tasks.every(t => t.frame_id === 'frame-1')).toBe(true);
363
- });
364
- it('should order active tasks by priority and creation time', () => {
365
- const activeTasks = taskStore.getActiveTasks();
366
- // Should be ordered by priority desc, created_at asc
367
- const priorities = activeTasks.map(t => t.priority);
368
- expect(priorities[0]).toBe('high'); // Highest priority first
369
- });
370
- it('should get blocking tasks', () => {
371
- // Create tasks with blocking relationships
372
- const blockingTaskId = taskStore.createTask({
373
- title: 'Blocking Task',
374
- frameId: 'frame-blocker'
375
- });
376
- const dependentTaskId = taskStore.createTask({
377
- title: 'Dependent Task',
378
- frameId: 'frame-dependent'
379
- });
380
- taskStore.addDependency(dependentTaskId, blockingTaskId);
381
- const blockingTasks = taskStore.getBlockingTasks();
382
- expect(blockingTasks.length).toBeGreaterThan(0);
383
- const foundBlocking = blockingTasks.find(t => t.id === blockingTaskId);
384
- expect(foundBlocking).toBeDefined();
385
- });
386
- it('should return empty array when no active tasks exist', () => {
387
- // Complete all tasks
388
- const activeTasks = taskStore.getActiveTasks();
389
- activeTasks.forEach(task => {
390
- if (task.status === 'pending') {
391
- taskStore.updateTaskStatus(task.id, 'in_progress');
392
- }
393
- if (task.status === 'in_progress') {
394
- taskStore.updateTaskStatus(task.id, 'completed');
395
- }
396
- if (task.status === 'blocked') {
397
- taskStore.updateTaskStatus(task.id, 'cancelled');
398
- }
399
- });
400
- const remainingActive = taskStore.getActiveTasks();
401
- expect(remainingActive).toHaveLength(0);
402
- });
403
- });
404
- describe('Task Metrics', () => {
405
- beforeEach(() => {
406
- // Create tasks with various states for metrics testing
407
- const tasks = [
408
- { title: 'Task 1', status: 'pending', priority: 'high' },
409
- { title: 'Task 2', status: 'pending', priority: 'medium' },
410
- { title: 'Task 3', status: 'completed', priority: 'low', effort: 30 },
411
- { title: 'Task 4', status: 'completed', priority: 'high', effort: 60 },
412
- { title: 'Task 5', status: 'blocked', priority: 'urgent' },
413
- { title: 'Task 6', status: 'cancelled', priority: 'medium' }
414
- ];
415
- tasks.forEach(task => {
416
- const taskId = taskStore.createTask({
417
- title: task.title,
418
- frameId: 'frame-metrics',
419
- priority: task.priority,
420
- estimatedEffort: task.effort
421
- });
422
- if (task.status !== 'pending') {
423
- if (task.status === 'completed') {
424
- taskStore.updateTaskStatus(taskId, 'in_progress');
425
- }
426
- taskStore.updateTaskStatus(taskId, task.status);
427
- }
428
- });
429
- });
430
- it('should calculate basic metrics correctly', () => {
431
- const metrics = taskStore.getMetrics();
432
- expect(metrics.total_tasks).toBe(6);
433
- expect(metrics.by_status.pending).toBe(2);
434
- expect(metrics.by_status.completed).toBe(2);
435
- expect(metrics.by_status.blocked).toBe(1);
436
- expect(metrics.by_status.cancelled).toBe(1);
437
- expect(metrics.by_priority.high).toBe(2);
438
- expect(metrics.by_priority.medium).toBe(2);
439
- expect(metrics.by_priority.low).toBe(1);
440
- expect(metrics.by_priority.urgent).toBe(1);
441
- expect(metrics.completion_rate).toBe(2 / 6); // 2 completed out of 6 total
442
- expect(metrics.blocked_tasks).toBe(1);
443
- });
444
- it('should handle metrics when no tasks exist', () => {
445
- // Create empty task store
446
- const emptyDb = new Database(':memory:');
447
- const emptyStore = new PebblesTaskStore(projectRoot, emptyDb);
448
- const metrics = emptyStore.getMetrics();
449
- expect(metrics.total_tasks).toBe(0);
450
- expect(metrics.completion_rate).toBe(0);
451
- expect(metrics.blocked_tasks).toBe(0);
452
- emptyDb.close();
453
- });
454
- it('should calculate effort accuracy', () => {
455
- // Create task with estimated effort for accuracy calculation
456
- const taskId = taskStore.createTask({
457
- title: 'Effort Test',
458
- frameId: 'frame-effort',
459
- estimatedEffort: 60 // 1 hour estimate
460
- });
461
- taskStore.updateTaskStatus(taskId, 'in_progress');
462
- // Mock actual effort to be close to estimate
463
- const task = taskStore.getTask(taskId);
464
- if (task) {
465
- // Manually update actual effort for testing
466
- const stmt = db.prepare(`
467
- UPDATE task_cache
468
- SET actual_effort = ?
469
- WHERE id = ?
470
- `);
471
- stmt.run(50, taskId); // Actual: 50 min vs Estimate: 60 min
472
- }
473
- const metrics = taskStore.getMetrics();
474
- expect(metrics.avg_effort_accuracy).toBeGreaterThan(0);
475
- });
476
- });
477
- describe('Linear Integration Export', () => {
478
- beforeEach(() => {
479
- // Create tasks without Linear integration
480
- taskStore.createTask({
481
- title: 'Local Task 1',
482
- frameId: 'frame-1',
483
- description: 'Task for Linear export',
484
- priority: 'high',
485
- estimatedEffort: 120
486
- });
487
- taskStore.createTask({
488
- title: 'Local Task 2',
489
- frameId: 'frame-2',
490
- priority: 'medium'
491
- });
492
- // Create task with existing Linear reference (should be excluded)
493
- const taskWithLinear = taskStore.createTask({
494
- title: 'Already Synced',
495
- frameId: 'frame-3'
496
- });
497
- // Manually add Linear external reference
498
- db.prepare(`
499
- UPDATE task_cache
500
- SET external_refs = ?
501
- WHERE id = ?
502
- `).run(JSON.stringify({ linear: { id: 'LIN-123' } }), taskWithLinear);
503
- });
504
- it('should export tasks for Linear integration', () => {
505
- const exported = taskStore.exportForLinear();
506
- expect(exported).toHaveLength(2); // Should exclude task with existing Linear ref
507
- const task1 = exported.find(t => t.title === 'Local Task 1');
508
- expect(task1).toBeDefined();
509
- expect(task1.description).toBe('Task for Linear export');
510
- expect(task1.priority).toBe(3); // high -> 3 in Linear
511
- expect(task1.estimate).toBe(120);
512
- const task2 = exported.find(t => t.title === 'Local Task 2');
513
- expect(task2).toBeDefined();
514
- expect(task2.priority).toBe(2); // medium -> 2 in Linear
515
- });
516
- it('should map priorities correctly for Linear', () => {
517
- const priorities = {
518
- low: 1,
519
- medium: 2,
520
- high: 3,
521
- urgent: 4
522
- };
523
- Object.entries(priorities).forEach(([priority, expectedValue]) => {
524
- const taskId = taskStore.createTask({
525
- title: `Priority ${priority}`,
526
- frameId: 'frame-priority',
527
- priority: priority
528
- });
529
- const exported = taskStore.exportForLinear();
530
- const task = exported.find(t => t.title === `Priority ${priority}`);
531
- expect(task.priority).toBe(expectedValue);
532
- });
533
- });
534
- it('should map statuses correctly for Linear', () => {
535
- const statusMappings = {
536
- pending: 'Backlog',
537
- in_progress: 'In Progress',
538
- completed: 'Done',
539
- blocked: 'Blocked',
540
- cancelled: 'Cancelled'
541
- };
542
- Object.entries(statusMappings).forEach(([status, expectedLinearStatus]) => {
543
- const taskId = taskStore.createTask({
544
- title: `Status ${status}`,
545
- frameId: 'frame-status'
546
- });
547
- if (status !== 'pending') {
548
- taskStore.updateTaskStatus(taskId, status);
549
- }
550
- const exported = taskStore.exportForLinear();
551
- const task = exported.find(t => t.title === `Status ${status}`);
552
- expect(task.status).toBe(expectedLinearStatus);
553
- });
554
- });
555
- });
556
- describe('Error Handling and Edge Cases', () => {
557
- it('should handle database errors gracefully', () => {
558
- const taskId = taskStore.createTask({
559
- title: 'Error Test',
560
- frameId: 'frame-error'
561
- });
562
- // Close database to simulate error
563
- db.close();
564
- expect(() => {
565
- taskStore.getTask(taskId);
566
- }).toThrow(DatabaseError);
567
- expect(() => {
568
- taskStore.updateTaskStatus(taskId, 'completed');
569
- }).toThrow();
570
- });
571
- it('should handle JSONL file write errors gracefully', () => {
572
- // Mock fs.appendFile to throw error
573
- const originalAppendFile = require('fs').appendFile;
574
- require('fs').appendFile = vi.fn((path, data, callback) => {
575
- callback(new Error('Write failed'));
576
- });
577
- // Should not throw but should log error
578
- expect(() => {
579
- taskStore.createTask({
580
- title: 'Write Error Test',
581
- frameId: 'frame-write-error'
582
- });
583
- }).not.toThrow();
584
- // Restore original
585
- require('fs').appendFile = originalAppendFile;
586
- });
587
- it('should handle concurrent task operations', () => {
588
- const tasks = [];
589
- // Create multiple tasks quickly
590
- for (let i = 0; i < 20; i++) {
591
- const taskId = taskStore.createTask({
592
- title: `Concurrent Task ${i}`,
593
- frameId: `frame-${i}`,
594
- priority: i % 2 === 0 ? 'high' : 'low'
595
- });
596
- tasks.push(taskId);
597
- }
598
- expect(tasks).toHaveLength(20);
599
- // Update all tasks concurrently
600
- tasks.forEach(taskId => {
601
- taskStore.updateTaskStatus(taskId, 'completed');
602
- });
603
- // Verify all updates
604
- const allCompleted = tasks.every(taskId => {
605
- const task = taskStore.getTask(taskId);
606
- return task?.status === 'completed';
607
- });
608
- expect(allCompleted).toBe(true);
609
- });
610
- it('should handle empty or malformed task IDs', () => {
611
- expect(taskStore.getTask('')).toBeUndefined();
612
- expect(taskStore.getTask('invalid-format')).toBeUndefined();
613
- expect(taskStore.getTask('null')).toBeUndefined();
614
- });
615
- it('should validate task status transitions', () => {
616
- const taskId = taskStore.createTask({
617
- title: 'Status Validation',
618
- frameId: 'frame-validation'
619
- });
620
- // Valid transitions should work
621
- expect(() => {
622
- taskStore.updateTaskStatus(taskId, 'in_progress');
623
- }).not.toThrow();
624
- expect(() => {
625
- taskStore.updateTaskStatus(taskId, 'blocked');
626
- }).not.toThrow();
627
- expect(() => {
628
- taskStore.updateTaskStatus(taskId, 'in_progress');
629
- }).not.toThrow();
630
- expect(() => {
631
- taskStore.updateTaskStatus(taskId, 'completed');
632
- }).not.toThrow();
633
- // Invalid transition: completed -> in_progress
634
- expect(() => {
635
- taskStore.updateTaskStatus(taskId, 'in_progress');
636
- }).toThrow(TaskError);
637
- });
638
- it('should handle tasks with complex metadata', () => {
639
- const taskId = taskStore.createTask({
640
- title: 'Complex Metadata Task',
641
- frameId: 'frame-complex',
642
- tags: ['complex', 'metadata', 'test'],
643
- dependsOn: [],
644
- });
645
- // Add dependency to test complex relationships
646
- const depTaskId = taskStore.createTask({
647
- title: 'Dependency Task',
648
- frameId: 'frame-dep'
649
- });
650
- taskStore.addDependency(taskId, depTaskId);
651
- const task = taskStore.getTask(taskId);
652
- expect(task.tags).toEqual(['complex', 'metadata', 'test']);
653
- expect(task.depends_on).toContain(depTaskId);
654
- });
655
- });
656
- describe('Data Integrity and Consistency', () => {
657
- it('should maintain referential integrity in dependencies', () => {
658
- const task1Id = taskStore.createTask({
659
- title: 'Task 1',
660
- frameId: 'frame-1'
661
- });
662
- const task2Id = taskStore.createTask({
663
- title: 'Task 2',
664
- frameId: 'frame-2'
665
- });
666
- taskStore.addDependency(task2Id, task1Id);
667
- // Check both sides of relationship
668
- const task1 = taskStore.getTask(task1Id);
669
- const task2 = taskStore.getTask(task2Id);
670
- expect(task2.depends_on).toContain(task1Id);
671
- expect(task1.blocks).toContain(task2Id);
672
- });
673
- it('should ensure content-based task IDs are deterministic for same content', () => {
674
- const now = Math.floor(Date.now() / 1000);
675
- // Mock Math.random to return consistent value
676
- const originalRandom = Math.random;
677
- Math.random = vi.fn(() => 0.5);
678
- // Mock Date.now to return consistent value
679
- vi.spyOn(Date, 'now').mockReturnValue(now * 1000);
680
- try {
681
- const taskId1 = taskStore.createTask({
682
- title: 'Deterministic Task',
683
- frameId: 'frame-det'
684
- });
685
- const taskId2 = taskStore.createTask({
686
- title: 'Deterministic Task',
687
- frameId: 'frame-det'
688
- });
689
- // Should be different due to timestamp and random components
690
- expect(taskId1).not.toBe(taskId2);
691
- // But should follow consistent format
692
- expect(taskId1).toMatch(/^tsk-[a-f0-9]{8}$/);
693
- expect(taskId2).toMatch(/^tsk-[a-f0-9]{8}$/);
694
- }
695
- finally {
696
- Math.random = originalRandom;
697
- vi.restoreAllMocks();
698
- }
699
- });
700
- it('should handle JSON serialization edge cases', () => {
701
- const taskId = taskStore.createTask({
702
- title: 'JSON Edge Cases',
703
- frameId: 'frame-json',
704
- tags: ['tag with spaces', 'tag"with"quotes', 'tag\\with\\slashes'],
705
- dependsOn: []
706
- });
707
- const task = taskStore.getTask(taskId);
708
- expect(task.tags).toEqual(['tag with spaces', 'tag"with"quotes', 'tag\\with\\slashes']);
709
- });
710
- });
711
- });
712
- //# sourceMappingURL=pebbles-task-store.test.js.map