claude-code-swarm 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1129) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CLAUDE.md +4 -0
  4. package/README.md +65 -0
  5. package/package.json +4 -4
  6. package/src/__tests__/config.test.mjs +128 -0
  7. package/src/__tests__/index.test.mjs +2 -0
  8. package/src/__tests__/paths.test.mjs +13 -0
  9. package/src/config.mjs +46 -16
  10. package/src/index.mjs +3 -1
  11. package/src/map-connection.mjs +3 -3
  12. package/src/paths.mjs +5 -0
  13. package/src/sidecar-client.mjs +2 -2
  14. package/references/multi-agent-protocol/.sudocode/issues.jsonl +0 -120
  15. package/references/multi-agent-protocol/.sudocode/specs.jsonl +0 -15
  16. package/references/multi-agent-protocol/LICENSE +0 -21
  17. package/references/multi-agent-protocol/README.md +0 -113
  18. package/references/multi-agent-protocol/docs/00-design-specification.md +0 -496
  19. package/references/multi-agent-protocol/docs/01-open-questions.md +0 -1050
  20. package/references/multi-agent-protocol/docs/02-wire-protocol.md +0 -296
  21. package/references/multi-agent-protocol/docs/03-streaming-semantics.md +0 -252
  22. package/references/multi-agent-protocol/docs/04-error-handling.md +0 -231
  23. package/references/multi-agent-protocol/docs/05-connection-model.md +0 -244
  24. package/references/multi-agent-protocol/docs/06-visibility-permissions.md +0 -243
  25. package/references/multi-agent-protocol/docs/07-federation.md +0 -335
  26. package/references/multi-agent-protocol/docs/08-macro-agent-migration.md +0 -253
  27. package/references/multi-agent-protocol/docs/09-authentication.md +0 -748
  28. package/references/multi-agent-protocol/docs/10-environment-awareness.md +0 -242
  29. package/references/multi-agent-protocol/docs/10-mail-protocol.md +0 -553
  30. package/references/multi-agent-protocol/docs/11-anp-inspired-improvements.md +0 -1079
  31. package/references/multi-agent-protocol/docs/11-trajectory-protocol.md +0 -292
  32. package/references/multi-agent-protocol/docs/12-anp-implementation-plan.md +0 -641
  33. package/references/multi-agent-protocol/docs/agent-iam-integration.md +0 -877
  34. package/references/multi-agent-protocol/docs/agentic-mesh-integration-draft.md +0 -459
  35. package/references/multi-agent-protocol/docs/git-transport-draft.md +0 -251
  36. package/references/multi-agent-protocol/docs-site/Gemfile +0 -22
  37. package/references/multi-agent-protocol/docs-site/README.md +0 -82
  38. package/references/multi-agent-protocol/docs-site/_config.yml +0 -91
  39. package/references/multi-agent-protocol/docs-site/_includes/head_custom.html +0 -20
  40. package/references/multi-agent-protocol/docs-site/_sass/color_schemes/map.scss +0 -42
  41. package/references/multi-agent-protocol/docs-site/_sass/custom/custom.scss +0 -34
  42. package/references/multi-agent-protocol/docs-site/examples/full-integration.md +0 -510
  43. package/references/multi-agent-protocol/docs-site/examples/index.md +0 -138
  44. package/references/multi-agent-protocol/docs-site/examples/simple-chat.md +0 -282
  45. package/references/multi-agent-protocol/docs-site/examples/task-queue.md +0 -399
  46. package/references/multi-agent-protocol/docs-site/getting-started/index.md +0 -98
  47. package/references/multi-agent-protocol/docs-site/getting-started/installation.md +0 -219
  48. package/references/multi-agent-protocol/docs-site/getting-started/overview.md +0 -172
  49. package/references/multi-agent-protocol/docs-site/getting-started/quickstart.md +0 -237
  50. package/references/multi-agent-protocol/docs-site/index.md +0 -136
  51. package/references/multi-agent-protocol/docs-site/protocol/authentication.md +0 -391
  52. package/references/multi-agent-protocol/docs-site/protocol/connection-model.md +0 -376
  53. package/references/multi-agent-protocol/docs-site/protocol/design.md +0 -284
  54. package/references/multi-agent-protocol/docs-site/protocol/error-handling.md +0 -312
  55. package/references/multi-agent-protocol/docs-site/protocol/federation.md +0 -449
  56. package/references/multi-agent-protocol/docs-site/protocol/index.md +0 -129
  57. package/references/multi-agent-protocol/docs-site/protocol/permissions.md +0 -398
  58. package/references/multi-agent-protocol/docs-site/protocol/streaming.md +0 -353
  59. package/references/multi-agent-protocol/docs-site/protocol/wire-protocol.md +0 -369
  60. package/references/multi-agent-protocol/docs-site/sdk/api/agent.md +0 -357
  61. package/references/multi-agent-protocol/docs-site/sdk/api/client.md +0 -380
  62. package/references/multi-agent-protocol/docs-site/sdk/api/index.md +0 -62
  63. package/references/multi-agent-protocol/docs-site/sdk/api/server.md +0 -453
  64. package/references/multi-agent-protocol/docs-site/sdk/api/types.md +0 -468
  65. package/references/multi-agent-protocol/docs-site/sdk/guides/agent.md +0 -375
  66. package/references/multi-agent-protocol/docs-site/sdk/guides/authentication.md +0 -405
  67. package/references/multi-agent-protocol/docs-site/sdk/guides/client.md +0 -352
  68. package/references/multi-agent-protocol/docs-site/sdk/guides/index.md +0 -89
  69. package/references/multi-agent-protocol/docs-site/sdk/guides/server.md +0 -360
  70. package/references/multi-agent-protocol/docs-site/sdk/guides/testing.md +0 -446
  71. package/references/multi-agent-protocol/docs-site/sdk/guides/transports.md +0 -363
  72. package/references/multi-agent-protocol/docs-site/sdk/index.md +0 -206
  73. package/references/multi-agent-protocol/package-lock.json +0 -4230
  74. package/references/multi-agent-protocol/package.json +0 -56
  75. package/references/multi-agent-protocol/schema/meta.json +0 -584
  76. package/references/multi-agent-protocol/schema/schema.json +0 -3067
  77. package/references/openhive/.claude/settings.json +0 -6
  78. package/references/openhive/.dockerignore +0 -54
  79. package/references/openhive/.github/workflows/docker.yml +0 -52
  80. package/references/openhive/.sudocode/issues.jsonl +0 -24
  81. package/references/openhive/.sudocode/specs.jsonl +0 -4
  82. package/references/openhive/CLAUDE.md +0 -88
  83. package/references/openhive/Dockerfile +0 -105
  84. package/references/openhive/README.md +0 -745
  85. package/references/openhive/bin/openhive.js +0 -6
  86. package/references/openhive/cloudbuild.yaml +0 -80
  87. package/references/openhive/deploy/cloud-run.sh +0 -106
  88. package/references/openhive/deploy/openhive.env.example +0 -80
  89. package/references/openhive/deploy/openhive.service +0 -91
  90. package/references/openhive/docker-compose.yml +0 -67
  91. package/references/openhive/docker-entrypoint.sh +0 -117
  92. package/references/openhive/docs/API_MIGRATION.md +0 -176
  93. package/references/openhive/docs/DEPLOYMENT.md +0 -847
  94. package/references/openhive/docs/DESIGN_v1.md +0 -489
  95. package/references/openhive/docs/DESIGN_v2.md +0 -564
  96. package/references/openhive/docs/HEADSCALE_HOSTING_SPEC.md +0 -513
  97. package/references/openhive/docs/HIVE_SYNC_DESIGN.md +0 -2362
  98. package/references/openhive/docs/HIVE_SYNC_IMPLEMENTATION_PLAN.md +0 -1169
  99. package/references/openhive/docs/HOSTING.md +0 -601
  100. package/references/openhive/docs/IMPLEMENTATION_PLAN.md +0 -428
  101. package/references/openhive/docs/LOCAL_SETUP.md +0 -506
  102. package/references/openhive/docs/MACRO_AGENT_ATLAS_EXTENSION.md +0 -351
  103. package/references/openhive/docs/MEMORY_BANK_SYNC_SPEC.md +0 -909
  104. package/references/openhive/docs/PLAN_v1.md +0 -471
  105. package/references/openhive/docs/PLAN_v2.md +0 -623
  106. package/references/openhive/docs/WEBSOCKET.md +0 -267
  107. package/references/openhive/docs/openswarm-bootstrap-token-spec.md +0 -240
  108. package/references/openhive/ecosystem.config.cjs +0 -76
  109. package/references/openhive/fly.toml +0 -63
  110. package/references/openhive/package-lock.json +0 -17640
  111. package/references/openhive/package.json +0 -128
  112. package/references/openhive/packages/openhive-types/package-lock.json +0 -1473
  113. package/references/openhive/packages/openhive-types/package.json +0 -42
  114. package/references/openhive/packages/openhive-types/src/index.ts +0 -36
  115. package/references/openhive/packages/openhive-types/src/map-coordination.ts +0 -92
  116. package/references/openhive/packages/openhive-types/src/map-session-sync.ts +0 -50
  117. package/references/openhive/packages/openhive-types/src/map-sync.ts +0 -68
  118. package/references/openhive/packages/openhive-types/tsconfig.json +0 -15
  119. package/references/openhive/packages/openhive-types/tsconfig.tsbuildinfo +0 -1
  120. package/references/openhive/packages/openhive-types/tsup.config.ts +0 -12
  121. package/references/openhive/railway.json +0 -13
  122. package/references/openhive/railway.toml +0 -24
  123. package/references/openhive/render.yaml +0 -51
  124. package/references/openhive/src/__tests__/auth.test.ts +0 -148
  125. package/references/openhive/src/__tests__/bridge/credentials.test.ts +0 -65
  126. package/references/openhive/src/__tests__/bridge/dal.test.ts +0 -279
  127. package/references/openhive/src/__tests__/bridge/inbound.test.ts +0 -349
  128. package/references/openhive/src/__tests__/bridge/manager.test.ts +0 -419
  129. package/references/openhive/src/__tests__/bridge/mentions.test.ts +0 -83
  130. package/references/openhive/src/__tests__/bridge/outbound.test.ts +0 -209
  131. package/references/openhive/src/__tests__/bridge/slack-adapter.test.ts +0 -276
  132. package/references/openhive/src/__tests__/cli.test.ts +0 -342
  133. package/references/openhive/src/__tests__/config.test.ts +0 -205
  134. package/references/openhive/src/__tests__/coordination/coordination.test.ts +0 -1072
  135. package/references/openhive/src/__tests__/coordination/cross-instance.test.ts +0 -540
  136. package/references/openhive/src/__tests__/coordination/e2e.test.ts +0 -780
  137. package/references/openhive/src/__tests__/data-dir.test.ts +0 -332
  138. package/references/openhive/src/__tests__/db.test.ts +0 -258
  139. package/references/openhive/src/__tests__/discovery.test.ts +0 -288
  140. package/references/openhive/src/__tests__/events/dal.test.ts +0 -371
  141. package/references/openhive/src/__tests__/events/dispatch.test.ts +0 -202
  142. package/references/openhive/src/__tests__/events/e2e.test.ts +0 -528
  143. package/references/openhive/src/__tests__/events/normalizers.test.ts +0 -263
  144. package/references/openhive/src/__tests__/events/router.test.ts +0 -314
  145. package/references/openhive/src/__tests__/events/routes.test.ts +0 -407
  146. package/references/openhive/src/__tests__/follows.test.ts +0 -328
  147. package/references/openhive/src/__tests__/helpers/test-dirs.ts +0 -44
  148. package/references/openhive/src/__tests__/ingest-keys.test.ts +0 -925
  149. package/references/openhive/src/__tests__/map/sync-client-content.test.ts +0 -288
  150. package/references/openhive/src/__tests__/map/sync-client.test.ts +0 -500
  151. package/references/openhive/src/__tests__/map/sync-listener.test.ts +0 -504
  152. package/references/openhive/src/__tests__/middleware/hostname-guard.test.ts +0 -73
  153. package/references/openhive/src/__tests__/migrations.test.ts +0 -260
  154. package/references/openhive/src/__tests__/opentasks/client.test.ts +0 -497
  155. package/references/openhive/src/__tests__/opentasks/discovery.test.ts +0 -283
  156. package/references/openhive/src/__tests__/opentasks/e2e.test.ts +0 -767
  157. package/references/openhive/src/__tests__/routes/agents.test.ts +0 -417
  158. package/references/openhive/src/__tests__/routes/opentasks-content.test.ts +0 -493
  159. package/references/openhive/src/__tests__/routes/resource-content.test.ts +0 -1741
  160. package/references/openhive/src/__tests__/sessions/adapters.test.ts +0 -524
  161. package/references/openhive/src/__tests__/sessions/routes.test.ts +0 -1053
  162. package/references/openhive/src/__tests__/sessions/storage.test.ts +0 -545
  163. package/references/openhive/src/__tests__/sessions/trajectory-checkpoints.test.ts +0 -349
  164. package/references/openhive/src/__tests__/sessions/trajectory-routes.test.ts +0 -290
  165. package/references/openhive/src/__tests__/swarm/config.test.ts +0 -125
  166. package/references/openhive/src/__tests__/swarm/credentials.test.ts +0 -254
  167. package/references/openhive/src/__tests__/swarm/dal.test.ts +0 -290
  168. package/references/openhive/src/__tests__/swarm/e2e.test.ts +0 -827
  169. package/references/openhive/src/__tests__/swarm/fixtures/exit-immediately.js +0 -3
  170. package/references/openhive/src/__tests__/swarm/fixtures/map-server.js +0 -147
  171. package/references/openhive/src/__tests__/swarm/fixtures/sleep-server.js +0 -52
  172. package/references/openhive/src/__tests__/swarm/local-provider.test.ts +0 -279
  173. package/references/openhive/src/__tests__/swarm/manager.test.ts +0 -305
  174. package/references/openhive/src/__tests__/swarm/routes.test.ts +0 -396
  175. package/references/openhive/src/__tests__/swarm/workspace.test.ts +0 -257
  176. package/references/openhive/src/__tests__/swarmhub/client.test.ts +0 -324
  177. package/references/openhive/src/__tests__/swarmhub/config.test.ts +0 -213
  178. package/references/openhive/src/__tests__/swarmhub/connector.test.ts +0 -581
  179. package/references/openhive/src/__tests__/swarmhub/routes.test.ts +0 -639
  180. package/references/openhive/src/__tests__/swarmhub/slack-client.test.ts +0 -164
  181. package/references/openhive/src/__tests__/swarmhub/slack-connector.test.ts +0 -164
  182. package/references/openhive/src/__tests__/swarmhub/slack-routes.test.ts +0 -373
  183. package/references/openhive/src/__tests__/swarmhub/webhook-handler.test.ts +0 -295
  184. package/references/openhive/src/__tests__/sync/resource-sync.test.ts +0 -1418
  185. package/references/openhive/src/__tests__/sync/sync.test.ts +0 -800
  186. package/references/openhive/src/api/index.ts +0 -65
  187. package/references/openhive/src/api/middleware/auth.ts +0 -227
  188. package/references/openhive/src/api/middleware/hostname-guard.ts +0 -38
  189. package/references/openhive/src/api/routes/admin.ts +0 -366
  190. package/references/openhive/src/api/routes/agents.ts +0 -223
  191. package/references/openhive/src/api/routes/auth.ts +0 -164
  192. package/references/openhive/src/api/routes/bridges.ts +0 -384
  193. package/references/openhive/src/api/routes/comments.ts +0 -294
  194. package/references/openhive/src/api/routes/coordination.ts +0 -312
  195. package/references/openhive/src/api/routes/events.ts +0 -158
  196. package/references/openhive/src/api/routes/federation.ts +0 -367
  197. package/references/openhive/src/api/routes/feed.ts +0 -212
  198. package/references/openhive/src/api/routes/hives.ts +0 -264
  199. package/references/openhive/src/api/routes/map.ts +0 -674
  200. package/references/openhive/src/api/routes/memory-banks.ts +0 -971
  201. package/references/openhive/src/api/routes/posts.ts +0 -342
  202. package/references/openhive/src/api/routes/resource-content.ts +0 -727
  203. package/references/openhive/src/api/routes/resources.ts +0 -1013
  204. package/references/openhive/src/api/routes/search.ts +0 -45
  205. package/references/openhive/src/api/routes/sessions.ts +0 -1187
  206. package/references/openhive/src/api/routes/swarm-hosting.ts +0 -313
  207. package/references/openhive/src/api/routes/sync-protocol.ts +0 -168
  208. package/references/openhive/src/api/routes/sync.ts +0 -279
  209. package/references/openhive/src/api/routes/uploads.ts +0 -174
  210. package/references/openhive/src/api/routes/webhooks.ts +0 -603
  211. package/references/openhive/src/api/schemas/agents.ts +0 -26
  212. package/references/openhive/src/api/schemas/comments.ts +0 -22
  213. package/references/openhive/src/api/schemas/hives.ts +0 -33
  214. package/references/openhive/src/api/schemas/posts.ts +0 -37
  215. package/references/openhive/src/api/schemas/sync.ts +0 -56
  216. package/references/openhive/src/auth/index.ts +0 -2
  217. package/references/openhive/src/auth/jwks.ts +0 -58
  218. package/references/openhive/src/bridge/adapters/slack.ts +0 -306
  219. package/references/openhive/src/bridge/credentials.ts +0 -72
  220. package/references/openhive/src/bridge/inbound.ts +0 -288
  221. package/references/openhive/src/bridge/index.ts +0 -42
  222. package/references/openhive/src/bridge/manager.ts +0 -425
  223. package/references/openhive/src/bridge/mentions.ts +0 -42
  224. package/references/openhive/src/bridge/outbound.ts +0 -103
  225. package/references/openhive/src/bridge/schema.ts +0 -82
  226. package/references/openhive/src/bridge/types.ts +0 -238
  227. package/references/openhive/src/cli/network.ts +0 -480
  228. package/references/openhive/src/cli.ts +0 -620
  229. package/references/openhive/src/config.ts +0 -611
  230. package/references/openhive/src/coordination/index.ts +0 -43
  231. package/references/openhive/src/coordination/listener.ts +0 -92
  232. package/references/openhive/src/coordination/schema.ts +0 -79
  233. package/references/openhive/src/coordination/service.ts +0 -233
  234. package/references/openhive/src/coordination/types.ts +0 -177
  235. package/references/openhive/src/data-dir.ts +0 -105
  236. package/references/openhive/src/db/adapters/index.ts +0 -21
  237. package/references/openhive/src/db/adapters/postgres.ts +0 -310
  238. package/references/openhive/src/db/adapters/sqlite.ts +0 -56
  239. package/references/openhive/src/db/adapters/types.ts +0 -65
  240. package/references/openhive/src/db/dal/agents.ts +0 -430
  241. package/references/openhive/src/db/dal/bridge.ts +0 -336
  242. package/references/openhive/src/db/dal/comments.ts +0 -213
  243. package/references/openhive/src/db/dal/coordination.ts +0 -361
  244. package/references/openhive/src/db/dal/events.ts +0 -381
  245. package/references/openhive/src/db/dal/follows.ts +0 -96
  246. package/references/openhive/src/db/dal/hives.ts +0 -198
  247. package/references/openhive/src/db/dal/ingest-keys.ts +0 -176
  248. package/references/openhive/src/db/dal/instances.ts +0 -196
  249. package/references/openhive/src/db/dal/invites.ts +0 -123
  250. package/references/openhive/src/db/dal/map.ts +0 -750
  251. package/references/openhive/src/db/dal/posts.ts +0 -274
  252. package/references/openhive/src/db/dal/remote-agents.ts +0 -56
  253. package/references/openhive/src/db/dal/search.ts +0 -238
  254. package/references/openhive/src/db/dal/sync-events.ts +0 -160
  255. package/references/openhive/src/db/dal/sync-groups.ts +0 -100
  256. package/references/openhive/src/db/dal/sync-peer-configs.ts +0 -216
  257. package/references/openhive/src/db/dal/sync-peers.ts +0 -145
  258. package/references/openhive/src/db/dal/syncable-resources.ts +0 -888
  259. package/references/openhive/src/db/dal/trajectory-checkpoints.ts +0 -291
  260. package/references/openhive/src/db/dal/uploads.ts +0 -124
  261. package/references/openhive/src/db/dal/votes.ts +0 -124
  262. package/references/openhive/src/db/index.ts +0 -293
  263. package/references/openhive/src/db/providers/index.ts +0 -75
  264. package/references/openhive/src/db/providers/postgres.ts +0 -529
  265. package/references/openhive/src/db/providers/sqlite.ts +0 -1383
  266. package/references/openhive/src/db/providers/turso.ts +0 -1360
  267. package/references/openhive/src/db/providers/types.ts +0 -516
  268. package/references/openhive/src/db/schema.ts +0 -641
  269. package/references/openhive/src/discovery/index.ts +0 -403
  270. package/references/openhive/src/events/dispatch.ts +0 -106
  271. package/references/openhive/src/events/index.ts +0 -17
  272. package/references/openhive/src/events/normalizers/github.ts +0 -133
  273. package/references/openhive/src/events/normalizers/index.ts +0 -62
  274. package/references/openhive/src/events/normalizers/slack.ts +0 -50
  275. package/references/openhive/src/events/router.ts +0 -156
  276. package/references/openhive/src/events/schema.ts +0 -66
  277. package/references/openhive/src/events/types.ts +0 -130
  278. package/references/openhive/src/federation/index.ts +0 -1
  279. package/references/openhive/src/federation/service.ts +0 -776
  280. package/references/openhive/src/headscale/client.ts +0 -256
  281. package/references/openhive/src/headscale/config.ts +0 -212
  282. package/references/openhive/src/headscale/index.ts +0 -23
  283. package/references/openhive/src/headscale/manager.ts +0 -249
  284. package/references/openhive/src/headscale/sync.ts +0 -272
  285. package/references/openhive/src/headscale/types.ts +0 -231
  286. package/references/openhive/src/index.ts +0 -225
  287. package/references/openhive/src/map/client-entry.ts +0 -26
  288. package/references/openhive/src/map/index.ts +0 -76
  289. package/references/openhive/src/map/schema.ts +0 -119
  290. package/references/openhive/src/map/service.ts +0 -323
  291. package/references/openhive/src/map/sync-client.ts +0 -696
  292. package/references/openhive/src/map/sync-listener.ts +0 -409
  293. package/references/openhive/src/map/types.ts +0 -290
  294. package/references/openhive/src/network/factory.ts +0 -118
  295. package/references/openhive/src/network/headscale-provider.ts +0 -437
  296. package/references/openhive/src/network/index.ts +0 -43
  297. package/references/openhive/src/network/tailscale-client.ts +0 -289
  298. package/references/openhive/src/network/tailscale-provider.ts +0 -287
  299. package/references/openhive/src/network/types.ts +0 -178
  300. package/references/openhive/src/opentasks-client/client.ts +0 -374
  301. package/references/openhive/src/opentasks-client/index.ts +0 -7
  302. package/references/openhive/src/realtime/index.ts +0 -282
  303. package/references/openhive/src/server.ts +0 -1069
  304. package/references/openhive/src/services/email.ts +0 -177
  305. package/references/openhive/src/services/sitemap.ts +0 -135
  306. package/references/openhive/src/sessions/adapters/claude.ts +0 -466
  307. package/references/openhive/src/sessions/adapters/codex.ts +0 -265
  308. package/references/openhive/src/sessions/adapters/index.ts +0 -263
  309. package/references/openhive/src/sessions/adapters/raw.ts +0 -144
  310. package/references/openhive/src/sessions/adapters/types.ts +0 -83
  311. package/references/openhive/src/sessions/index.ts +0 -50
  312. package/references/openhive/src/sessions/storage/adapters/gcs.ts +0 -277
  313. package/references/openhive/src/sessions/storage/adapters/local.ts +0 -240
  314. package/references/openhive/src/sessions/storage/adapters/s3.ts +0 -321
  315. package/references/openhive/src/sessions/storage/index.ts +0 -231
  316. package/references/openhive/src/sessions/storage/types.ts +0 -189
  317. package/references/openhive/src/sessions/types.ts +0 -415
  318. package/references/openhive/src/shared/types/index.ts +0 -45
  319. package/references/openhive/src/shared/types/map-coordination.ts +0 -92
  320. package/references/openhive/src/shared/types/map-session-sync.ts +0 -170
  321. package/references/openhive/src/shared/types/map-sync.ts +0 -68
  322. package/references/openhive/src/skill.ts +0 -203
  323. package/references/openhive/src/storage/adapters/local.ts +0 -169
  324. package/references/openhive/src/storage/adapters/s3.ts +0 -195
  325. package/references/openhive/src/storage/index.ts +0 -64
  326. package/references/openhive/src/storage/types.ts +0 -69
  327. package/references/openhive/src/swarm/credentials.ts +0 -98
  328. package/references/openhive/src/swarm/dal.ts +0 -206
  329. package/references/openhive/src/swarm/index.ts +0 -28
  330. package/references/openhive/src/swarm/manager.ts +0 -917
  331. package/references/openhive/src/swarm/providers/local.ts +0 -338
  332. package/references/openhive/src/swarm/providers/sandboxed-local.ts +0 -478
  333. package/references/openhive/src/swarm/providers/workspace.ts +0 -52
  334. package/references/openhive/src/swarm/schema.ts +0 -43
  335. package/references/openhive/src/swarm/types.ts +0 -333
  336. package/references/openhive/src/swarmhub/client.ts +0 -279
  337. package/references/openhive/src/swarmhub/connector.ts +0 -463
  338. package/references/openhive/src/swarmhub/index.ts +0 -43
  339. package/references/openhive/src/swarmhub/routes.ts +0 -296
  340. package/references/openhive/src/swarmhub/types.ts +0 -213
  341. package/references/openhive/src/swarmhub/webhook-handler.ts +0 -126
  342. package/references/openhive/src/sync/compaction.ts +0 -193
  343. package/references/openhive/src/sync/coordination-hooks.ts +0 -154
  344. package/references/openhive/src/sync/crypto.ts +0 -79
  345. package/references/openhive/src/sync/gossip.ts +0 -136
  346. package/references/openhive/src/sync/hooks.ts +0 -202
  347. package/references/openhive/src/sync/materializer-repo.ts +0 -256
  348. package/references/openhive/src/sync/materializer.ts +0 -682
  349. package/references/openhive/src/sync/middleware.ts +0 -140
  350. package/references/openhive/src/sync/peer-resolver.ts +0 -157
  351. package/references/openhive/src/sync/resource-hooks.ts +0 -161
  352. package/references/openhive/src/sync/schema.ts +0 -158
  353. package/references/openhive/src/sync/service.ts +0 -990
  354. package/references/openhive/src/sync/types.ts +0 -369
  355. package/references/openhive/src/terminal/index.ts +0 -4
  356. package/references/openhive/src/terminal/pty-manager.ts +0 -337
  357. package/references/openhive/src/terminal/resolve-tui.ts +0 -44
  358. package/references/openhive/src/terminal/terminal-ws.ts +0 -251
  359. package/references/openhive/src/types.ts +0 -442
  360. package/references/openhive/src/utils/git-remote.ts +0 -329
  361. package/references/openhive/src/web/App.tsx +0 -77
  362. package/references/openhive/src/web/__tests__/components/dashboard/RecentActivity.test.tsx +0 -77
  363. package/references/openhive/src/web/__tests__/components/dashboard/StatsOverview.test.tsx +0 -62
  364. package/references/openhive/src/web/__tests__/components/dashboard/SwarmStatusSummary.test.tsx +0 -122
  365. package/references/openhive/src/web/__tests__/components/dashboard/SyncResourcesStatus.test.tsx +0 -104
  366. package/references/openhive/src/web/__tests__/components/layout/Sidebar.test.tsx +0 -110
  367. package/references/openhive/src/web/__tests__/components/swarm/StatusBadges.test.tsx +0 -65
  368. package/references/openhive/src/web/__tests__/components/terminal/query-responses.test.ts +0 -143
  369. package/references/openhive/src/web/__tests__/components/terminal/terminal-mouse.test.ts +0 -509
  370. package/references/openhive/src/web/__tests__/hooks/useEventsApi.test.ts +0 -378
  371. package/references/openhive/src/web/__tests__/pages/Dashboard.test.tsx +0 -57
  372. package/references/openhive/src/web/__tests__/pages/Events.test.tsx +0 -886
  373. package/references/openhive/src/web/__tests__/pages/Explore.test.tsx +0 -63
  374. package/references/openhive/src/web/__tests__/routing.test.tsx +0 -79
  375. package/references/openhive/src/web/__tests__/setup.ts +0 -37
  376. package/references/openhive/src/web/__tests__/stores/dashboard.test.ts +0 -49
  377. package/references/openhive/src/web/components/common/AgentBadge.tsx +0 -58
  378. package/references/openhive/src/web/components/common/Avatar.tsx +0 -78
  379. package/references/openhive/src/web/components/common/ErrorBoundary.tsx +0 -76
  380. package/references/openhive/src/web/components/common/Highlight.tsx +0 -79
  381. package/references/openhive/src/web/components/common/ImageUpload.tsx +0 -209
  382. package/references/openhive/src/web/components/common/LoadingSpinner.tsx +0 -37
  383. package/references/openhive/src/web/components/common/Logo.tsx +0 -21
  384. package/references/openhive/src/web/components/common/Markdown.tsx +0 -53
  385. package/references/openhive/src/web/components/common/ProtectedRoute.tsx +0 -18
  386. package/references/openhive/src/web/components/common/ThemeToggle.tsx +0 -38
  387. package/references/openhive/src/web/components/common/TimeAgo.tsx +0 -17
  388. package/references/openhive/src/web/components/common/Toast.tsx +0 -70
  389. package/references/openhive/src/web/components/common/VoteButtons.tsx +0 -100
  390. package/references/openhive/src/web/components/dashboard/RecentActivity.tsx +0 -100
  391. package/references/openhive/src/web/components/dashboard/StatsOverview.tsx +0 -40
  392. package/references/openhive/src/web/components/dashboard/SwarmStatusSummary.tsx +0 -89
  393. package/references/openhive/src/web/components/dashboard/SyncResourcesStatus.tsx +0 -81
  394. package/references/openhive/src/web/components/feed/FeedControls.tsx +0 -38
  395. package/references/openhive/src/web/components/feed/NewPostsIndicator.tsx +0 -75
  396. package/references/openhive/src/web/components/feed/PostCard.tsx +0 -129
  397. package/references/openhive/src/web/components/feed/PostList.tsx +0 -83
  398. package/references/openhive/src/web/components/layout/Footer.tsx +0 -5
  399. package/references/openhive/src/web/components/layout/Layout.tsx +0 -29
  400. package/references/openhive/src/web/components/layout/Sidebar.tsx +0 -348
  401. package/references/openhive/src/web/components/post/CommentForm.tsx +0 -59
  402. package/references/openhive/src/web/components/post/CommentTree.tsx +0 -145
  403. package/references/openhive/src/web/components/resources/MemoryBrowser.tsx +0 -208
  404. package/references/openhive/src/web/components/resources/OpenTasksSummary.tsx +0 -138
  405. package/references/openhive/src/web/components/resources/SkillBrowser.tsx +0 -284
  406. package/references/openhive/src/web/components/swarm/StatusBadges.tsx +0 -56
  407. package/references/openhive/src/web/components/terminal/TerminalPanel.tsx +0 -485
  408. package/references/openhive/src/web/components/terminal/index.ts +0 -2
  409. package/references/openhive/src/web/components/terminal/query-responses.ts +0 -70
  410. package/references/openhive/src/web/components/terminal/terminal-mouse.ts +0 -222
  411. package/references/openhive/src/web/hooks/useApi.ts +0 -740
  412. package/references/openhive/src/web/hooks/useDocumentTitle.ts +0 -49
  413. package/references/openhive/src/web/hooks/useInfiniteScroll.ts +0 -58
  414. package/references/openhive/src/web/hooks/useRealtimeUpdates.ts +0 -154
  415. package/references/openhive/src/web/hooks/useWebSocket.ts +0 -225
  416. package/references/openhive/src/web/index.html +0 -73
  417. package/references/openhive/src/web/lib/api.ts +0 -518
  418. package/references/openhive/src/web/main.tsx +0 -32
  419. package/references/openhive/src/web/pages/About.tsx +0 -131
  420. package/references/openhive/src/web/pages/Agent.tsx +0 -130
  421. package/references/openhive/src/web/pages/Agents.tsx +0 -69
  422. package/references/openhive/src/web/pages/AuthCallback.tsx +0 -75
  423. package/references/openhive/src/web/pages/Dashboard.tsx +0 -41
  424. package/references/openhive/src/web/pages/Events.tsx +0 -1025
  425. package/references/openhive/src/web/pages/Explore.tsx +0 -43
  426. package/references/openhive/src/web/pages/Hive.tsx +0 -134
  427. package/references/openhive/src/web/pages/Hives.tsx +0 -64
  428. package/references/openhive/src/web/pages/Home.tsx +0 -43
  429. package/references/openhive/src/web/pages/Login.tsx +0 -122
  430. package/references/openhive/src/web/pages/Post.tsx +0 -216
  431. package/references/openhive/src/web/pages/ResourceDetail.tsx +0 -426
  432. package/references/openhive/src/web/pages/Resources.tsx +0 -276
  433. package/references/openhive/src/web/pages/Search.tsx +0 -234
  434. package/references/openhive/src/web/pages/SessionDetail.tsx +0 -703
  435. package/references/openhive/src/web/pages/Sessions.tsx +0 -129
  436. package/references/openhive/src/web/pages/Settings.tsx +0 -826
  437. package/references/openhive/src/web/pages/SwarmCraft.tsx +0 -16
  438. package/references/openhive/src/web/pages/Swarms.tsx +0 -981
  439. package/references/openhive/src/web/pages/Terminal.tsx +0 -69
  440. package/references/openhive/src/web/postcss.config.js +0 -5
  441. package/references/openhive/src/web/public/favicon.svg +0 -11
  442. package/references/openhive/src/web/public/manifest.json +0 -21
  443. package/references/openhive/src/web/stores/auth.ts +0 -207
  444. package/references/openhive/src/web/stores/dashboard.ts +0 -23
  445. package/references/openhive/src/web/stores/realtime.ts +0 -90
  446. package/references/openhive/src/web/stores/theme.ts +0 -70
  447. package/references/openhive/src/web/stores/toast.ts +0 -63
  448. package/references/openhive/src/web/styles/globals.css +0 -503
  449. package/references/openhive/src/web/sw.ts +0 -228
  450. package/references/openhive/src/web/utils/serviceWorker.ts +0 -86
  451. package/references/openhive/src/web/vite.config.ts +0 -81
  452. package/references/openhive/tsconfig.json +0 -32
  453. package/references/openhive/tsup.config.ts +0 -17
  454. package/references/openhive/vitest.config.ts +0 -30
  455. package/references/openhive/vitest.web.config.ts +0 -20
  456. package/references/opentasks/.claude/settings.json +0 -6
  457. package/references/opentasks/.claude-plugin/plugin.json +0 -20
  458. package/references/opentasks/.lintstagedrc.json +0 -4
  459. package/references/opentasks/.prettierignore +0 -4
  460. package/references/opentasks/.prettierrc.json +0 -11
  461. package/references/opentasks/.sudocode/issues.jsonl +0 -89
  462. package/references/opentasks/.sudocode/specs.jsonl +0 -24
  463. package/references/opentasks/README.md +0 -401
  464. package/references/opentasks/docs/ARCHITECTURE.md +0 -841
  465. package/references/opentasks/docs/DESIGN.md +0 -689
  466. package/references/opentasks/docs/INTERFACE.md +0 -670
  467. package/references/opentasks/docs/PERSISTENCE.md +0 -1638
  468. package/references/opentasks/docs/PROVIDERS.md +0 -1412
  469. package/references/opentasks/docs/SCHEMA.md +0 -815
  470. package/references/opentasks/docs/TESTING.md +0 -1081
  471. package/references/opentasks/eslint.config.js +0 -58
  472. package/references/opentasks/package-lock.json +0 -4348
  473. package/references/opentasks/package.json +0 -81
  474. package/references/opentasks/skills/opentasks/SKILL.md +0 -139
  475. package/references/opentasks/skills/opentasks/dependency-management.md +0 -119
  476. package/references/opentasks/skills/opentasks/feedback-and-review.md +0 -100
  477. package/references/opentasks/skills/opentasks/linking-external-data.md +0 -103
  478. package/references/opentasks/skills/opentasks/spec-to-implementation.md +0 -98
  479. package/references/opentasks/src/__tests__/cli-tools.test.ts +0 -800
  480. package/references/opentasks/src/__tests__/cli.test.ts +0 -97
  481. package/references/opentasks/src/__tests__/p1-p3-gaps.test.ts +0 -635
  482. package/references/opentasks/src/cli.ts +0 -929
  483. package/references/opentasks/src/client/__tests__/client-crud.test.ts +0 -546
  484. package/references/opentasks/src/client/__tests__/client.test.ts +0 -658
  485. package/references/opentasks/src/client/__tests__/socket-discovery.test.ts +0 -122
  486. package/references/opentasks/src/client/client.ts +0 -560
  487. package/references/opentasks/src/client/index.ts +0 -32
  488. package/references/opentasks/src/config/__tests__/defaults.test.ts +0 -66
  489. package/references/opentasks/src/config/__tests__/env.test.ts +0 -155
  490. package/references/opentasks/src/config/__tests__/index.test.ts +0 -148
  491. package/references/opentasks/src/config/__tests__/loader.test.ts +0 -173
  492. package/references/opentasks/src/config/__tests__/merge.test.ts +0 -121
  493. package/references/opentasks/src/config/__tests__/schema.test.ts +0 -446
  494. package/references/opentasks/src/config/defaults.ts +0 -18
  495. package/references/opentasks/src/config/env.ts +0 -170
  496. package/references/opentasks/src/config/errors.ts +0 -33
  497. package/references/opentasks/src/config/index.ts +0 -63
  498. package/references/opentasks/src/config/loader.ts +0 -90
  499. package/references/opentasks/src/config/merge.ts +0 -64
  500. package/references/opentasks/src/config/schema.ts +0 -767
  501. package/references/opentasks/src/core/__tests__/conditional-redirects.test.ts +0 -116
  502. package/references/opentasks/src/core/__tests__/connections.test.ts +0 -194
  503. package/references/opentasks/src/core/__tests__/hash.test.ts +0 -161
  504. package/references/opentasks/src/core/__tests__/id.test.ts +0 -175
  505. package/references/opentasks/src/core/__tests__/init.test.ts +0 -115
  506. package/references/opentasks/src/core/__tests__/location.test.ts +0 -94
  507. package/references/opentasks/src/core/__tests__/merge-driver.test.ts +0 -300
  508. package/references/opentasks/src/core/__tests__/redirects.test.ts +0 -169
  509. package/references/opentasks/src/core/__tests__/resolve-location-target.test.ts +0 -468
  510. package/references/opentasks/src/core/__tests__/uri.test.ts +0 -228
  511. package/references/opentasks/src/core/__tests__/worktree.test.ts +0 -160
  512. package/references/opentasks/src/core/conditional-redirects.ts +0 -100
  513. package/references/opentasks/src/core/connections.ts +0 -217
  514. package/references/opentasks/src/core/discover.ts +0 -195
  515. package/references/opentasks/src/core/hash.ts +0 -74
  516. package/references/opentasks/src/core/id.ts +0 -174
  517. package/references/opentasks/src/core/index.ts +0 -108
  518. package/references/opentasks/src/core/init.ts +0 -66
  519. package/references/opentasks/src/core/location.ts +0 -139
  520. package/references/opentasks/src/core/merge-driver.ts +0 -280
  521. package/references/opentasks/src/core/redirects.ts +0 -182
  522. package/references/opentasks/src/core/uri.ts +0 -270
  523. package/references/opentasks/src/core/worktree.ts +0 -504
  524. package/references/opentasks/src/daemon/__tests__/e2e-live-agent.test.ts +0 -344
  525. package/references/opentasks/src/daemon/__tests__/e2e-session-pipeline.test.ts +0 -447
  526. package/references/opentasks/src/daemon/__tests__/e2e-watch.test.ts +0 -279
  527. package/references/opentasks/src/daemon/__tests__/entire-linker.test.ts +0 -1074
  528. package/references/opentasks/src/daemon/__tests__/entire-watcher.test.ts +0 -659
  529. package/references/opentasks/src/daemon/__tests__/flush.test.ts +0 -306
  530. package/references/opentasks/src/daemon/__tests__/integration.test.ts +0 -338
  531. package/references/opentasks/src/daemon/__tests__/ipc.test.ts +0 -406
  532. package/references/opentasks/src/daemon/__tests__/lifecycle.test.ts +0 -378
  533. package/references/opentasks/src/daemon/__tests__/lock.test.ts +0 -240
  534. package/references/opentasks/src/daemon/__tests__/methods/graph.test.ts +0 -372
  535. package/references/opentasks/src/daemon/__tests__/methods/provider.test.ts +0 -238
  536. package/references/opentasks/src/daemon/__tests__/methods/tools.test.ts +0 -690
  537. package/references/opentasks/src/daemon/__tests__/multi-location.test.ts +0 -945
  538. package/references/opentasks/src/daemon/__tests__/registry.test.ts +0 -268
  539. package/references/opentasks/src/daemon/__tests__/watcher.test.ts +0 -329
  540. package/references/opentasks/src/daemon/entire-linker.ts +0 -615
  541. package/references/opentasks/src/daemon/entire-watcher.ts +0 -415
  542. package/references/opentasks/src/daemon/factory.ts +0 -133
  543. package/references/opentasks/src/daemon/flush.ts +0 -168
  544. package/references/opentasks/src/daemon/index.ts +0 -120
  545. package/references/opentasks/src/daemon/ipc.ts +0 -491
  546. package/references/opentasks/src/daemon/lifecycle.ts +0 -1106
  547. package/references/opentasks/src/daemon/location-state.ts +0 -481
  548. package/references/opentasks/src/daemon/lock.ts +0 -168
  549. package/references/opentasks/src/daemon/methods/__tests__/graph.test.ts +0 -359
  550. package/references/opentasks/src/daemon/methods/__tests__/provider.test.ts +0 -227
  551. package/references/opentasks/src/daemon/methods/__tests__/tools.test.ts +0 -360
  552. package/references/opentasks/src/daemon/methods/__tests__/watch.test.ts +0 -656
  553. package/references/opentasks/src/daemon/methods/archive.ts +0 -193
  554. package/references/opentasks/src/daemon/methods/graph.ts +0 -274
  555. package/references/opentasks/src/daemon/methods/lifecycle.ts +0 -112
  556. package/references/opentasks/src/daemon/methods/location.ts +0 -118
  557. package/references/opentasks/src/daemon/methods/provider.ts +0 -159
  558. package/references/opentasks/src/daemon/methods/tools.ts +0 -221
  559. package/references/opentasks/src/daemon/methods/watch.ts +0 -206
  560. package/references/opentasks/src/daemon/registry.ts +0 -244
  561. package/references/opentasks/src/daemon/types.ts +0 -163
  562. package/references/opentasks/src/daemon/watcher.ts +0 -248
  563. package/references/opentasks/src/entire/__tests__/agent-registry.test.ts +0 -127
  564. package/references/opentasks/src/entire/__tests__/claude-generator.test.ts +0 -49
  565. package/references/opentasks/src/entire/__tests__/commit-msg.test.ts +0 -89
  566. package/references/opentasks/src/entire/__tests__/cursor-agent.test.ts +0 -224
  567. package/references/opentasks/src/entire/__tests__/flush-sentinel.test.ts +0 -93
  568. package/references/opentasks/src/entire/__tests__/gemini-agent.test.ts +0 -375
  569. package/references/opentasks/src/entire/__tests__/git-hooks.test.ts +0 -85
  570. package/references/opentasks/src/entire/__tests__/hook-managers.test.ts +0 -128
  571. package/references/opentasks/src/entire/__tests__/opencode-agent.test.ts +0 -329
  572. package/references/opentasks/src/entire/__tests__/redaction.test.ts +0 -143
  573. package/references/opentasks/src/entire/__tests__/session-store.test.ts +0 -83
  574. package/references/opentasks/src/entire/__tests__/summarize.test.ts +0 -346
  575. package/references/opentasks/src/entire/__tests__/transcript-timestamp.test.ts +0 -127
  576. package/references/opentasks/src/entire/__tests__/types.test.ts +0 -112
  577. package/references/opentasks/src/entire/__tests__/utils.test.ts +0 -296
  578. package/references/opentasks/src/entire/__tests__/validation.test.ts +0 -103
  579. package/references/opentasks/src/entire/__tests__/worktree.test.ts +0 -66
  580. package/references/opentasks/src/entire/agent/registry.ts +0 -143
  581. package/references/opentasks/src/entire/agent/session-types.ts +0 -117
  582. package/references/opentasks/src/entire/agent/types.ts +0 -217
  583. package/references/opentasks/src/entire/commands/clean.ts +0 -134
  584. package/references/opentasks/src/entire/commands/disable.ts +0 -85
  585. package/references/opentasks/src/entire/commands/doctor.ts +0 -152
  586. package/references/opentasks/src/entire/commands/enable.ts +0 -149
  587. package/references/opentasks/src/entire/commands/explain.ts +0 -271
  588. package/references/opentasks/src/entire/commands/reset.ts +0 -105
  589. package/references/opentasks/src/entire/commands/resume.ts +0 -194
  590. package/references/opentasks/src/entire/commands/rewind.ts +0 -204
  591. package/references/opentasks/src/entire/commands/status.ts +0 -150
  592. package/references/opentasks/src/entire/config.ts +0 -153
  593. package/references/opentasks/src/entire/git-operations.ts +0 -485
  594. package/references/opentasks/src/entire/hooks/git-hooks.ts +0 -171
  595. package/references/opentasks/src/entire/hooks/lifecycle.ts +0 -224
  596. package/references/opentasks/src/entire/index.ts +0 -644
  597. package/references/opentasks/src/entire/security/redaction.ts +0 -263
  598. package/references/opentasks/src/entire/session/state-machine.ts +0 -463
  599. package/references/opentasks/src/entire/store/checkpoint-store.ts +0 -489
  600. package/references/opentasks/src/entire/store/native-store.ts +0 -178
  601. package/references/opentasks/src/entire/store/provider-types.ts +0 -99
  602. package/references/opentasks/src/entire/store/session-store.ts +0 -233
  603. package/references/opentasks/src/entire/strategy/attribution.ts +0 -300
  604. package/references/opentasks/src/entire/strategy/common.ts +0 -222
  605. package/references/opentasks/src/entire/strategy/content-overlap.ts +0 -242
  606. package/references/opentasks/src/entire/strategy/manual-commit.ts +0 -1008
  607. package/references/opentasks/src/entire/strategy/types.ts +0 -285
  608. package/references/opentasks/src/entire/summarize/claude-generator.ts +0 -119
  609. package/references/opentasks/src/entire/summarize/summarize.ts +0 -432
  610. package/references/opentasks/src/entire/types.ts +0 -408
  611. package/references/opentasks/src/entire/utils/chunk-files.ts +0 -49
  612. package/references/opentasks/src/entire/utils/commit-message.ts +0 -65
  613. package/references/opentasks/src/entire/utils/detect-agent.ts +0 -36
  614. package/references/opentasks/src/entire/utils/hook-managers.ts +0 -118
  615. package/references/opentasks/src/entire/utils/ide-tags.ts +0 -32
  616. package/references/opentasks/src/entire/utils/paths.ts +0 -59
  617. package/references/opentasks/src/entire/utils/preview-rewind.ts +0 -86
  618. package/references/opentasks/src/entire/utils/rewind-conflict.ts +0 -121
  619. package/references/opentasks/src/entire/utils/shadow-branch.ts +0 -113
  620. package/references/opentasks/src/entire/utils/string-utils.ts +0 -46
  621. package/references/opentasks/src/entire/utils/todo-extract.ts +0 -193
  622. package/references/opentasks/src/entire/utils/trailers.ts +0 -190
  623. package/references/opentasks/src/entire/utils/transcript-parse.ts +0 -177
  624. package/references/opentasks/src/entire/utils/transcript-timestamp.ts +0 -61
  625. package/references/opentasks/src/entire/utils/tree-ops.ts +0 -227
  626. package/references/opentasks/src/entire/utils/tty.ts +0 -72
  627. package/references/opentasks/src/entire/utils/validation.ts +0 -67
  628. package/references/opentasks/src/entire/utils/worktree.ts +0 -58
  629. package/references/opentasks/src/graph/EdgeTypeRegistry.ts +0 -330
  630. package/references/opentasks/src/graph/FederatedGraph.ts +0 -796
  631. package/references/opentasks/src/graph/GraphologyAdapter.ts +0 -374
  632. package/references/opentasks/src/graph/HydratingFederatedGraph.ts +0 -533
  633. package/references/opentasks/src/graph/__tests__/EdgeTypeRegistry.test.ts +0 -263
  634. package/references/opentasks/src/graph/__tests__/FederatedGraph.test.ts +0 -821
  635. package/references/opentasks/src/graph/__tests__/GraphologyAdapter.test.ts +0 -408
  636. package/references/opentasks/src/graph/__tests__/HydratingFederatedGraph.test.ts +0 -735
  637. package/references/opentasks/src/graph/__tests__/debounce.test.ts +0 -276
  638. package/references/opentasks/src/graph/__tests__/e2e-store-roundtrip.test.ts +0 -349
  639. package/references/opentasks/src/graph/__tests__/edge-cases.test.ts +0 -595
  640. package/references/opentasks/src/graph/__tests__/expansion.test.ts +0 -304
  641. package/references/opentasks/src/graph/__tests__/git-graph-syncer.test.ts +0 -572
  642. package/references/opentasks/src/graph/__tests__/provider-store.test.ts +0 -1091
  643. package/references/opentasks/src/graph/__tests__/query.test.ts +0 -991
  644. package/references/opentasks/src/graph/__tests__/store.test.ts +0 -998
  645. package/references/opentasks/src/graph/__tests__/sync.test.ts +0 -178
  646. package/references/opentasks/src/graph/__tests__/validation.test.ts +0 -657
  647. package/references/opentasks/src/graph/coordination.ts +0 -454
  648. package/references/opentasks/src/graph/debounce.ts +0 -154
  649. package/references/opentasks/src/graph/expansion.ts +0 -364
  650. package/references/opentasks/src/graph/git-graph-syncer.ts +0 -321
  651. package/references/opentasks/src/graph/history.ts +0 -438
  652. package/references/opentasks/src/graph/index.ts +0 -145
  653. package/references/opentasks/src/graph/provider-store.ts +0 -1077
  654. package/references/opentasks/src/graph/query.ts +0 -651
  655. package/references/opentasks/src/graph/store.ts +0 -861
  656. package/references/opentasks/src/graph/sync.ts +0 -116
  657. package/references/opentasks/src/graph/types.ts +0 -420
  658. package/references/opentasks/src/graph/validation.ts +0 -520
  659. package/references/opentasks/src/index.ts +0 -270
  660. package/references/opentasks/src/materialization/CLAUDE.md +0 -88
  661. package/references/opentasks/src/materialization/README.md +0 -187
  662. package/references/opentasks/src/materialization/__tests__/archive-methods.test.ts +0 -194
  663. package/references/opentasks/src/materialization/__tests__/archiver.test.ts +0 -528
  664. package/references/opentasks/src/materialization/__tests__/config.test.ts +0 -123
  665. package/references/opentasks/src/materialization/__tests__/git-remote-store.test.ts +0 -533
  666. package/references/opentasks/src/materialization/__tests__/graph-id.test.ts +0 -82
  667. package/references/opentasks/src/materialization/__tests__/http-remote-store.test.ts +0 -263
  668. package/references/opentasks/src/materialization/__tests__/materialize-before-archive.test.ts +0 -246
  669. package/references/opentasks/src/materialization/__tests__/remote-store-factory.test.ts +0 -152
  670. package/references/opentasks/src/materialization/__tests__/snapshot.test.ts +0 -209
  671. package/references/opentasks/src/materialization/archiver.ts +0 -318
  672. package/references/opentasks/src/materialization/git-archive-store.ts +0 -568
  673. package/references/opentasks/src/materialization/git-remote-store.ts +0 -551
  674. package/references/opentasks/src/materialization/graph-id.ts +0 -173
  675. package/references/opentasks/src/materialization/http-remote-store.ts +0 -190
  676. package/references/opentasks/src/materialization/index.ts +0 -62
  677. package/references/opentasks/src/materialization/remote-store-factory.ts +0 -55
  678. package/references/opentasks/src/materialization/snapshot.ts +0 -230
  679. package/references/opentasks/src/materialization/types.ts +0 -410
  680. package/references/opentasks/src/providers/__tests__/beads.test.ts +0 -752
  681. package/references/opentasks/src/providers/__tests__/claude-tasks.test.ts +0 -485
  682. package/references/opentasks/src/providers/__tests__/entire-e2e.test.ts +0 -692
  683. package/references/opentasks/src/providers/__tests__/entire-sessionlog-e2e.test.ts +0 -1113
  684. package/references/opentasks/src/providers/__tests__/entire.test.ts +0 -1016
  685. package/references/opentasks/src/providers/__tests__/from-config.test.ts +0 -183
  686. package/references/opentasks/src/providers/__tests__/global.test.ts +0 -515
  687. package/references/opentasks/src/providers/__tests__/materialization.test.ts +0 -567
  688. package/references/opentasks/src/providers/__tests__/native.test.ts +0 -693
  689. package/references/opentasks/src/providers/__tests__/registry.test.ts +0 -232
  690. package/references/opentasks/src/providers/beads.ts +0 -1155
  691. package/references/opentasks/src/providers/claude-tasks.ts +0 -402
  692. package/references/opentasks/src/providers/entire.ts +0 -608
  693. package/references/opentasks/src/providers/from-config.ts +0 -210
  694. package/references/opentasks/src/providers/global.ts +0 -460
  695. package/references/opentasks/src/providers/index.ts +0 -147
  696. package/references/opentasks/src/providers/location.ts +0 -237
  697. package/references/opentasks/src/providers/materialization.ts +0 -346
  698. package/references/opentasks/src/providers/native.ts +0 -725
  699. package/references/opentasks/src/providers/registry.ts +0 -114
  700. package/references/opentasks/src/providers/sudocode.ts +0 -1292
  701. package/references/opentasks/src/providers/sync.ts +0 -485
  702. package/references/opentasks/src/providers/traits/RelationshipQueryable.ts +0 -169
  703. package/references/opentasks/src/providers/traits/TaskManageable.ts +0 -211
  704. package/references/opentasks/src/providers/traits/Watchable.ts +0 -260
  705. package/references/opentasks/src/providers/traits/__tests__/RelationshipQueryable.test.ts +0 -217
  706. package/references/opentasks/src/providers/traits/__tests__/TaskManageable.test.ts +0 -241
  707. package/references/opentasks/src/providers/traits/index.ts +0 -42
  708. package/references/opentasks/src/providers/types.ts +0 -439
  709. package/references/opentasks/src/schema/__tests__/validation.test.ts +0 -283
  710. package/references/opentasks/src/schema/base.ts +0 -88
  711. package/references/opentasks/src/schema/edges.ts +0 -78
  712. package/references/opentasks/src/schema/index.ts +0 -37
  713. package/references/opentasks/src/schema/nodes.ts +0 -119
  714. package/references/opentasks/src/schema/storage.ts +0 -130
  715. package/references/opentasks/src/schema/validation.ts +0 -209
  716. package/references/opentasks/src/storage/__tests__/atomic-write.test.ts +0 -227
  717. package/references/opentasks/src/storage/__tests__/file-lock.test.ts +0 -120
  718. package/references/opentasks/src/storage/__tests__/jsonl.test.ts +0 -267
  719. package/references/opentasks/src/storage/__tests__/locked-writer.test.ts +0 -134
  720. package/references/opentasks/src/storage/__tests__/sqlite.test.ts +0 -572
  721. package/references/opentasks/src/storage/atomic-write.ts +0 -86
  722. package/references/opentasks/src/storage/file-lock.ts +0 -215
  723. package/references/opentasks/src/storage/index.ts +0 -24
  724. package/references/opentasks/src/storage/interface.ts +0 -289
  725. package/references/opentasks/src/storage/jsonl.ts +0 -264
  726. package/references/opentasks/src/storage/locked-writer.ts +0 -140
  727. package/references/opentasks/src/storage/sqlite-schema.ts +0 -177
  728. package/references/opentasks/src/storage/sqlite.ts +0 -791
  729. package/references/opentasks/src/tools/__tests__/annotate.test.ts +0 -381
  730. package/references/opentasks/src/tools/__tests__/link.test.ts +0 -299
  731. package/references/opentasks/src/tools/__tests__/query.test.ts +0 -350
  732. package/references/opentasks/src/tools/__tests__/task.test.ts +0 -218
  733. package/references/opentasks/src/tools/annotate.ts +0 -277
  734. package/references/opentasks/src/tools/index.ts +0 -57
  735. package/references/opentasks/src/tools/link.ts +0 -163
  736. package/references/opentasks/src/tools/query.ts +0 -468
  737. package/references/opentasks/src/tools/task.ts +0 -213
  738. package/references/opentasks/src/tools/types.ts +0 -451
  739. package/references/opentasks/src/tracking/__tests__/claude-tool-categorizer.test.ts +0 -223
  740. package/references/opentasks/src/tracking/__tests__/transcript-extractor.test.ts +0 -262
  741. package/references/opentasks/src/tracking/claude-tool-categorizer.ts +0 -155
  742. package/references/opentasks/src/tracking/index.ts +0 -32
  743. package/references/opentasks/src/tracking/skill-tracker.ts +0 -322
  744. package/references/opentasks/src/tracking/transcript-extractor.ts +0 -225
  745. package/references/opentasks/tests/e2e/helpers/assertions.ts +0 -211
  746. package/references/opentasks/tests/e2e/helpers/beads-helpers.ts +0 -487
  747. package/references/opentasks/tests/e2e/helpers/fixtures.ts +0 -236
  748. package/references/opentasks/tests/e2e/helpers/index.ts +0 -122
  749. package/references/opentasks/tests/e2e/helpers/sudocode-helpers.ts +0 -341
  750. package/references/opentasks/tests/e2e/helpers/system-setup.ts +0 -504
  751. package/references/opentasks/tests/e2e/helpers/test-agent.ts +0 -504
  752. package/references/opentasks/tests/e2e/infrastructure.e2e.test.ts +0 -521
  753. package/references/opentasks/tests/e2e/skill-tracking.e2e.test.ts +0 -625
  754. package/references/opentasks/tests/e2e/workflows/feedback-loop.e2e.test.ts +0 -279
  755. package/references/opentasks/tests/e2e/workflows/multi-agent.e2e.test.ts +0 -304
  756. package/references/opentasks/tests/e2e/workflows/provider-sync/background-sync.e2e.test.ts +0 -292
  757. package/references/opentasks/tests/e2e/workflows/provider-sync/beads-provider-compat.e2e.test.ts +0 -249
  758. package/references/opentasks/tests/e2e/workflows/provider-sync/cross-provider-edges.e2e.test.ts +0 -407
  759. package/references/opentasks/tests/e2e/workflows/provider-sync/federated-ready.e2e.test.ts +0 -504
  760. package/references/opentasks/tests/e2e/workflows/provider-sync/hydration.e2e.test.ts +0 -340
  761. package/references/opentasks/tests/e2e/workflows/provider-sync/materialization.e2e.test.ts +0 -370
  762. package/references/opentasks/tests/e2e/workflows/provider-sync/sudocode-provider-compat.e2e.test.ts +0 -683
  763. package/references/opentasks/tests/e2e/workflows/provider-sync/watchable-beads.e2e.test.ts +0 -573
  764. package/references/opentasks/tests/e2e/workflows/spec-driven.e2e.test.ts +0 -244
  765. package/references/opentasks/tests/e2e/worktree-location.e2e.test.ts +0 -699
  766. package/references/opentasks/tests/integration/daemon/helpers.ts +0 -147
  767. package/references/opentasks/tests/integration/daemon/ipc.integration.test.ts +0 -343
  768. package/references/opentasks/tests/integration/daemon/lifecycle.integration.test.ts +0 -407
  769. package/references/opentasks/tests/integration/graph/federated-graph.integration.test.ts +0 -660
  770. package/references/opentasks/tests/integration/helpers/flags.ts +0 -28
  771. package/references/opentasks/tests/integration/helpers/index.ts +0 -47
  772. package/references/opentasks/tests/integration/helpers/process.ts +0 -133
  773. package/references/opentasks/tests/integration/helpers/temp.ts +0 -105
  774. package/references/opentasks/tests/integration/helpers/wait.ts +0 -133
  775. package/references/opentasks/tests/integration/helpers.test.ts +0 -120
  776. package/references/opentasks/tests/integration/providers/beads-task-manageable.integration.test.ts +0 -450
  777. package/references/opentasks/tests/integration/providers/beads.integration.test.ts +0 -388
  778. package/references/opentasks/tests/integration/providers/native-task-manageable.integration.test.ts +0 -667
  779. package/references/opentasks/tests/integration/providers/sudocode-task-manageable.integration.test.ts +0 -406
  780. package/references/opentasks/tests/integration/providers/sudocode.integration.test.ts +0 -342
  781. package/references/opentasks/tests/integration/storage/jsonl-durability.integration.test.ts +0 -390
  782. package/references/opentasks/tests/integration/storage/sqlite-durability.integration.test.ts +0 -527
  783. package/references/opentasks/tests/integration/worktree/redirect-location-resolution.integration.test.ts +0 -578
  784. package/references/opentasks/tests/integration/worktree/worktree-flow.integration.test.ts +0 -656
  785. package/references/opentasks/tsconfig.json +0 -18
  786. package/references/opentasks/vitest.config.ts +0 -27
  787. package/references/opentasks/vitest.e2e.config.ts +0 -35
  788. package/references/opentasks/vitest.integration.config.ts +0 -19
  789. package/references/openteams/.claude/settings.json +0 -6
  790. package/references/openteams/CLAUDE.md +0 -98
  791. package/references/openteams/README.md +0 -508
  792. package/references/openteams/SKILL.md +0 -198
  793. package/references/openteams/design.md +0 -250
  794. package/references/openteams/docs/visual-editor-design.md +0 -1225
  795. package/references/openteams/editor/index.html +0 -15
  796. package/references/openteams/editor/package.json +0 -39
  797. package/references/openteams/editor/src/App.tsx +0 -48
  798. package/references/openteams/editor/src/components/canvas/Canvas.tsx +0 -131
  799. package/references/openteams/editor/src/components/canvas/QuickAddMenu.tsx +0 -134
  800. package/references/openteams/editor/src/components/edges/PeerRouteEdge.tsx +0 -82
  801. package/references/openteams/editor/src/components/edges/SignalFlowEdge.tsx +0 -77
  802. package/references/openteams/editor/src/components/edges/SpawnEdge.tsx +0 -54
  803. package/references/openteams/editor/src/components/inspector/ChannelInspector.tsx +0 -158
  804. package/references/openteams/editor/src/components/inspector/EdgeInspector.tsx +0 -168
  805. package/references/openteams/editor/src/components/inspector/Inspector.tsx +0 -46
  806. package/references/openteams/editor/src/components/inspector/RoleInspector.tsx +0 -508
  807. package/references/openteams/editor/src/components/inspector/TeamInspector.tsx +0 -126
  808. package/references/openteams/editor/src/components/nodes/ChannelNode.tsx +0 -103
  809. package/references/openteams/editor/src/components/nodes/RoleNode.tsx +0 -157
  810. package/references/openteams/editor/src/components/nodes/node-styles.ts +0 -101
  811. package/references/openteams/editor/src/components/sidebar/Sidebar.tsx +0 -227
  812. package/references/openteams/editor/src/components/toolbar/ExportModal.tsx +0 -110
  813. package/references/openteams/editor/src/components/toolbar/ImportModal.tsx +0 -139
  814. package/references/openteams/editor/src/components/toolbar/Toolbar.tsx +0 -190
  815. package/references/openteams/editor/src/hooks/use-autosave.ts +0 -126
  816. package/references/openteams/editor/src/hooks/use-keyboard.ts +0 -106
  817. package/references/openteams/editor/src/hooks/use-validation.ts +0 -45
  818. package/references/openteams/editor/src/index.css +0 -245
  819. package/references/openteams/editor/src/lib/auto-layout.ts +0 -51
  820. package/references/openteams/editor/src/lib/bundled-templates.ts +0 -42
  821. package/references/openteams/editor/src/lib/compiler.ts +0 -75
  822. package/references/openteams/editor/src/lib/load-template.ts +0 -103
  823. package/references/openteams/editor/src/lib/rebuild-edges.ts +0 -104
  824. package/references/openteams/editor/src/lib/serializer.ts +0 -408
  825. package/references/openteams/editor/src/lib/signal-catalog.ts +0 -50
  826. package/references/openteams/editor/src/lib/validator.ts +0 -172
  827. package/references/openteams/editor/src/main.tsx +0 -10
  828. package/references/openteams/editor/src/stores/canvas-store.ts +0 -80
  829. package/references/openteams/editor/src/stores/config-store.ts +0 -243
  830. package/references/openteams/editor/src/stores/history-store.ts +0 -143
  831. package/references/openteams/editor/src/stores/theme-store.ts +0 -66
  832. package/references/openteams/editor/src/stores/ui-store.ts +0 -46
  833. package/references/openteams/editor/src/stores/validation-store.ts +0 -27
  834. package/references/openteams/editor/src/types/editor.ts +0 -74
  835. package/references/openteams/editor/src/vite-env.d.ts +0 -1
  836. package/references/openteams/editor/tests/compiler.test.ts +0 -151
  837. package/references/openteams/editor/tests/e2e-add-remove.test.ts +0 -386
  838. package/references/openteams/editor/tests/e2e-components.test.tsx +0 -424
  839. package/references/openteams/editor/tests/e2e-export-roundtrip.test.ts +0 -299
  840. package/references/openteams/editor/tests/e2e-template-load.test.ts +0 -204
  841. package/references/openteams/editor/tests/e2e-ui-store.test.ts +0 -126
  842. package/references/openteams/editor/tests/e2e-undo-redo.test.ts +0 -203
  843. package/references/openteams/editor/tests/e2e-validation.test.ts +0 -307
  844. package/references/openteams/editor/tests/serializer.test.ts +0 -142
  845. package/references/openteams/editor/tests/setup.ts +0 -52
  846. package/references/openteams/editor/tests/validator.test.ts +0 -92
  847. package/references/openteams/editor/tsconfig.json +0 -21
  848. package/references/openteams/editor/tsconfig.tsbuildinfo +0 -1
  849. package/references/openteams/editor/vite.config.ts +0 -28
  850. package/references/openteams/examples/bmad-method/prompts/analyst/ROLE.md +0 -16
  851. package/references/openteams/examples/bmad-method/prompts/analyst/SOUL.md +0 -5
  852. package/references/openteams/examples/bmad-method/prompts/architect/ROLE.md +0 -24
  853. package/references/openteams/examples/bmad-method/prompts/architect/SOUL.md +0 -5
  854. package/references/openteams/examples/bmad-method/prompts/developer/ROLE.md +0 -25
  855. package/references/openteams/examples/bmad-method/prompts/developer/SOUL.md +0 -5
  856. package/references/openteams/examples/bmad-method/prompts/master/ROLE.md +0 -21
  857. package/references/openteams/examples/bmad-method/prompts/master/SOUL.md +0 -5
  858. package/references/openteams/examples/bmad-method/prompts/pm/ROLE.md +0 -20
  859. package/references/openteams/examples/bmad-method/prompts/pm/SOUL.md +0 -5
  860. package/references/openteams/examples/bmad-method/prompts/qa/ROLE.md +0 -17
  861. package/references/openteams/examples/bmad-method/prompts/qa/SOUL.md +0 -5
  862. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/ROLE.md +0 -23
  863. package/references/openteams/examples/bmad-method/prompts/quick-flow-dev/SOUL.md +0 -5
  864. package/references/openteams/examples/bmad-method/prompts/scrum-master/ROLE.md +0 -27
  865. package/references/openteams/examples/bmad-method/prompts/scrum-master/SOUL.md +0 -5
  866. package/references/openteams/examples/bmad-method/prompts/tech-writer/ROLE.md +0 -21
  867. package/references/openteams/examples/bmad-method/prompts/tech-writer/SOUL.md +0 -5
  868. package/references/openteams/examples/bmad-method/prompts/ux-designer/ROLE.md +0 -16
  869. package/references/openteams/examples/bmad-method/prompts/ux-designer/SOUL.md +0 -5
  870. package/references/openteams/examples/bmad-method/roles/analyst.yaml +0 -9
  871. package/references/openteams/examples/bmad-method/roles/architect.yaml +0 -9
  872. package/references/openteams/examples/bmad-method/roles/developer.yaml +0 -8
  873. package/references/openteams/examples/bmad-method/roles/master.yaml +0 -8
  874. package/references/openteams/examples/bmad-method/roles/pm.yaml +0 -9
  875. package/references/openteams/examples/bmad-method/roles/qa.yaml +0 -8
  876. package/references/openteams/examples/bmad-method/roles/quick-flow-dev.yaml +0 -8
  877. package/references/openteams/examples/bmad-method/roles/scrum-master.yaml +0 -9
  878. package/references/openteams/examples/bmad-method/roles/tech-writer.yaml +0 -8
  879. package/references/openteams/examples/bmad-method/roles/ux-designer.yaml +0 -8
  880. package/references/openteams/examples/bmad-method/team.yaml +0 -161
  881. package/references/openteams/examples/bug-fix-pipeline/roles/fixer.yaml +0 -9
  882. package/references/openteams/examples/bug-fix-pipeline/roles/investigator.yaml +0 -8
  883. package/references/openteams/examples/bug-fix-pipeline/roles/pr-creator.yaml +0 -6
  884. package/references/openteams/examples/bug-fix-pipeline/roles/triager.yaml +0 -7
  885. package/references/openteams/examples/bug-fix-pipeline/roles/verifier.yaml +0 -8
  886. package/references/openteams/examples/bug-fix-pipeline/team.yaml +0 -88
  887. package/references/openteams/examples/codebase-migration/roles/assessor.yaml +0 -7
  888. package/references/openteams/examples/codebase-migration/roles/migrator.yaml +0 -9
  889. package/references/openteams/examples/codebase-migration/roles/planner.yaml +0 -5
  890. package/references/openteams/examples/codebase-migration/roles/test-extractor.yaml +0 -9
  891. package/references/openteams/examples/codebase-migration/roles/validator.yaml +0 -7
  892. package/references/openteams/examples/codebase-migration/team.yaml +0 -81
  893. package/references/openteams/examples/docs-sync/roles/adr-writer.yaml +0 -7
  894. package/references/openteams/examples/docs-sync/roles/api-doc-writer.yaml +0 -7
  895. package/references/openteams/examples/docs-sync/roles/change-detector.yaml +0 -7
  896. package/references/openteams/examples/docs-sync/roles/doc-reviewer.yaml +0 -7
  897. package/references/openteams/examples/docs-sync/roles/guide-writer.yaml +0 -7
  898. package/references/openteams/examples/docs-sync/team.yaml +0 -84
  899. package/references/openteams/examples/gsd/prompts/codebase-mapper/ROLE.md +0 -17
  900. package/references/openteams/examples/gsd/prompts/codebase-mapper/SOUL.md +0 -5
  901. package/references/openteams/examples/gsd/prompts/debugger/ROLE.md +0 -25
  902. package/references/openteams/examples/gsd/prompts/debugger/SOUL.md +0 -5
  903. package/references/openteams/examples/gsd/prompts/executor/ROLE.md +0 -34
  904. package/references/openteams/examples/gsd/prompts/executor/SOUL.md +0 -5
  905. package/references/openteams/examples/gsd/prompts/integration-checker/ROLE.md +0 -18
  906. package/references/openteams/examples/gsd/prompts/integration-checker/SOUL.md +0 -3
  907. package/references/openteams/examples/gsd/prompts/orchestrator/ROLE.md +0 -42
  908. package/references/openteams/examples/gsd/prompts/orchestrator/SOUL.md +0 -5
  909. package/references/openteams/examples/gsd/prompts/phase-researcher/ROLE.md +0 -15
  910. package/references/openteams/examples/gsd/prompts/phase-researcher/SOUL.md +0 -3
  911. package/references/openteams/examples/gsd/prompts/plan-checker/ROLE.md +0 -17
  912. package/references/openteams/examples/gsd/prompts/plan-checker/SOUL.md +0 -3
  913. package/references/openteams/examples/gsd/prompts/planner/ROLE.md +0 -28
  914. package/references/openteams/examples/gsd/prompts/planner/SOUL.md +0 -5
  915. package/references/openteams/examples/gsd/prompts/project-researcher/ROLE.md +0 -16
  916. package/references/openteams/examples/gsd/prompts/project-researcher/SOUL.md +0 -3
  917. package/references/openteams/examples/gsd/prompts/research-synthesizer/ROLE.md +0 -13
  918. package/references/openteams/examples/gsd/prompts/research-synthesizer/SOUL.md +0 -3
  919. package/references/openteams/examples/gsd/prompts/roadmapper/ROLE.md +0 -14
  920. package/references/openteams/examples/gsd/prompts/roadmapper/SOUL.md +0 -3
  921. package/references/openteams/examples/gsd/prompts/verifier/ROLE.md +0 -19
  922. package/references/openteams/examples/gsd/prompts/verifier/SOUL.md +0 -5
  923. package/references/openteams/examples/gsd/roles/codebase-mapper.yaml +0 -8
  924. package/references/openteams/examples/gsd/roles/debugger.yaml +0 -8
  925. package/references/openteams/examples/gsd/roles/executor.yaml +0 -8
  926. package/references/openteams/examples/gsd/roles/integration-checker.yaml +0 -8
  927. package/references/openteams/examples/gsd/roles/orchestrator.yaml +0 -9
  928. package/references/openteams/examples/gsd/roles/phase-researcher.yaml +0 -7
  929. package/references/openteams/examples/gsd/roles/plan-checker.yaml +0 -8
  930. package/references/openteams/examples/gsd/roles/planner.yaml +0 -8
  931. package/references/openteams/examples/gsd/roles/project-researcher.yaml +0 -8
  932. package/references/openteams/examples/gsd/roles/research-synthesizer.yaml +0 -7
  933. package/references/openteams/examples/gsd/roles/roadmapper.yaml +0 -7
  934. package/references/openteams/examples/gsd/roles/verifier.yaml +0 -8
  935. package/references/openteams/examples/gsd/team.yaml +0 -154
  936. package/references/openteams/examples/incident-response/roles/communicator.yaml +0 -5
  937. package/references/openteams/examples/incident-response/roles/fix-proposer.yaml +0 -7
  938. package/references/openteams/examples/incident-response/roles/incident-triager.yaml +0 -8
  939. package/references/openteams/examples/incident-response/roles/investigator.yaml +0 -8
  940. package/references/openteams/examples/incident-response/team.yaml +0 -68
  941. package/references/openteams/examples/pr-review-checks/roles/code-reviewer.yaml +0 -7
  942. package/references/openteams/examples/pr-review-checks/roles/security-scanner.yaml +0 -6
  943. package/references/openteams/examples/pr-review-checks/roles/summarizer.yaml +0 -6
  944. package/references/openteams/examples/pr-review-checks/roles/test-checker.yaml +0 -8
  945. package/references/openteams/examples/pr-review-checks/team.yaml +0 -64
  946. package/references/openteams/examples/security-audit/roles/code-analyzer.yaml +0 -6
  947. package/references/openteams/examples/security-audit/roles/dep-scanner.yaml +0 -7
  948. package/references/openteams/examples/security-audit/roles/fixer.yaml +0 -9
  949. package/references/openteams/examples/security-audit/roles/pr-creator.yaml +0 -6
  950. package/references/openteams/examples/security-audit/roles/prioritizer.yaml +0 -6
  951. package/references/openteams/examples/security-audit/roles/secrets-scanner.yaml +0 -6
  952. package/references/openteams/examples/security-audit/roles/verifier.yaml +0 -8
  953. package/references/openteams/examples/security-audit/team.yaml +0 -102
  954. package/references/openteams/media/banner.png +0 -0
  955. package/references/openteams/media/editor.png +0 -0
  956. package/references/openteams/package-lock.json +0 -4804
  957. package/references/openteams/package.json +0 -58
  958. package/references/openteams/schema/role.schema.json +0 -147
  959. package/references/openteams/schema/team.schema.json +0 -311
  960. package/references/openteams/src/cli/editor.ts +0 -170
  961. package/references/openteams/src/cli/generate.test.ts +0 -191
  962. package/references/openteams/src/cli/generate.ts +0 -220
  963. package/references/openteams/src/cli/prompt-utils.ts +0 -42
  964. package/references/openteams/src/cli/template.test.ts +0 -365
  965. package/references/openteams/src/cli/template.ts +0 -205
  966. package/references/openteams/src/cli.ts +0 -22
  967. package/references/openteams/src/generators/agent-prompt-generator.test.ts +0 -332
  968. package/references/openteams/src/generators/agent-prompt-generator.ts +0 -527
  969. package/references/openteams/src/generators/package-generator.test.ts +0 -129
  970. package/references/openteams/src/generators/package-generator.ts +0 -102
  971. package/references/openteams/src/generators/skill-generator.test.ts +0 -246
  972. package/references/openteams/src/generators/skill-generator.ts +0 -388
  973. package/references/openteams/src/index.ts +0 -84
  974. package/references/openteams/src/template/builtins.test.ts +0 -74
  975. package/references/openteams/src/template/builtins.ts +0 -108
  976. package/references/openteams/src/template/install-service.test.ts +0 -452
  977. package/references/openteams/src/template/install-service.ts +0 -332
  978. package/references/openteams/src/template/loader.test.ts +0 -1696
  979. package/references/openteams/src/template/loader.ts +0 -804
  980. package/references/openteams/src/template/resolver.test.ts +0 -304
  981. package/references/openteams/src/template/resolver.ts +0 -251
  982. package/references/openteams/src/template/types.ts +0 -229
  983. package/references/openteams/tsconfig.cjs.json +0 -7
  984. package/references/openteams/tsconfig.esm.json +0 -8
  985. package/references/openteams/tsconfig.json +0 -16
  986. package/references/openteams/vitest.config.ts +0 -9
  987. package/references/sessionlog/.husky/pre-commit +0 -1
  988. package/references/sessionlog/.lintstagedrc.json +0 -4
  989. package/references/sessionlog/.prettierignore +0 -4
  990. package/references/sessionlog/.prettierrc.json +0 -11
  991. package/references/sessionlog/LICENSE +0 -21
  992. package/references/sessionlog/README.md +0 -453
  993. package/references/sessionlog/eslint.config.js +0 -58
  994. package/references/sessionlog/package-lock.json +0 -3672
  995. package/references/sessionlog/package.json +0 -65
  996. package/references/sessionlog/src/__tests__/agent-hooks.test.ts +0 -570
  997. package/references/sessionlog/src/__tests__/agent-registry.test.ts +0 -127
  998. package/references/sessionlog/src/__tests__/claude-code-hooks.test.ts +0 -225
  999. package/references/sessionlog/src/__tests__/claude-generator.test.ts +0 -46
  1000. package/references/sessionlog/src/__tests__/commit-msg.test.ts +0 -86
  1001. package/references/sessionlog/src/__tests__/cursor-agent.test.ts +0 -224
  1002. package/references/sessionlog/src/__tests__/e2e-live.test.ts +0 -890
  1003. package/references/sessionlog/src/__tests__/event-log.test.ts +0 -183
  1004. package/references/sessionlog/src/__tests__/flush-sentinel.test.ts +0 -105
  1005. package/references/sessionlog/src/__tests__/gemini-agent.test.ts +0 -375
  1006. package/references/sessionlog/src/__tests__/git-hooks.test.ts +0 -78
  1007. package/references/sessionlog/src/__tests__/hook-managers.test.ts +0 -121
  1008. package/references/sessionlog/src/__tests__/lifecycle-tasks.test.ts +0 -759
  1009. package/references/sessionlog/src/__tests__/opencode-agent.test.ts +0 -338
  1010. package/references/sessionlog/src/__tests__/redaction.test.ts +0 -136
  1011. package/references/sessionlog/src/__tests__/session-repo.test.ts +0 -353
  1012. package/references/sessionlog/src/__tests__/session-store.test.ts +0 -166
  1013. package/references/sessionlog/src/__tests__/setup-ccweb.test.ts +0 -466
  1014. package/references/sessionlog/src/__tests__/skill-live.test.ts +0 -461
  1015. package/references/sessionlog/src/__tests__/summarize.test.ts +0 -348
  1016. package/references/sessionlog/src/__tests__/task-plan-e2e.test.ts +0 -610
  1017. package/references/sessionlog/src/__tests__/task-plan-live.test.ts +0 -632
  1018. package/references/sessionlog/src/__tests__/transcript-timestamp.test.ts +0 -121
  1019. package/references/sessionlog/src/__tests__/types.test.ts +0 -166
  1020. package/references/sessionlog/src/__tests__/utils.test.ts +0 -333
  1021. package/references/sessionlog/src/__tests__/validation.test.ts +0 -103
  1022. package/references/sessionlog/src/__tests__/worktree.test.ts +0 -57
  1023. package/references/sessionlog/src/agent/registry.ts +0 -143
  1024. package/references/sessionlog/src/agent/session-types.ts +0 -113
  1025. package/references/sessionlog/src/agent/types.ts +0 -220
  1026. package/references/sessionlog/src/cli.ts +0 -597
  1027. package/references/sessionlog/src/commands/clean.ts +0 -133
  1028. package/references/sessionlog/src/commands/disable.ts +0 -84
  1029. package/references/sessionlog/src/commands/doctor.ts +0 -145
  1030. package/references/sessionlog/src/commands/enable.ts +0 -202
  1031. package/references/sessionlog/src/commands/explain.ts +0 -261
  1032. package/references/sessionlog/src/commands/reset.ts +0 -105
  1033. package/references/sessionlog/src/commands/resume.ts +0 -180
  1034. package/references/sessionlog/src/commands/rewind.ts +0 -195
  1035. package/references/sessionlog/src/commands/setup-ccweb.ts +0 -275
  1036. package/references/sessionlog/src/commands/status.ts +0 -172
  1037. package/references/sessionlog/src/config.ts +0 -165
  1038. package/references/sessionlog/src/events/event-log.ts +0 -126
  1039. package/references/sessionlog/src/git-operations.ts +0 -558
  1040. package/references/sessionlog/src/hooks/git-hooks.ts +0 -165
  1041. package/references/sessionlog/src/hooks/lifecycle.ts +0 -391
  1042. package/references/sessionlog/src/index.ts +0 -650
  1043. package/references/sessionlog/src/security/redaction.ts +0 -283
  1044. package/references/sessionlog/src/session/state-machine.ts +0 -452
  1045. package/references/sessionlog/src/store/checkpoint-store.ts +0 -509
  1046. package/references/sessionlog/src/store/native-store.ts +0 -173
  1047. package/references/sessionlog/src/store/provider-types.ts +0 -99
  1048. package/references/sessionlog/src/store/session-store.ts +0 -266
  1049. package/references/sessionlog/src/strategy/attribution.ts +0 -296
  1050. package/references/sessionlog/src/strategy/common.ts +0 -207
  1051. package/references/sessionlog/src/strategy/content-overlap.ts +0 -228
  1052. package/references/sessionlog/src/strategy/manual-commit.ts +0 -988
  1053. package/references/sessionlog/src/strategy/types.ts +0 -279
  1054. package/references/sessionlog/src/summarize/claude-generator.ts +0 -115
  1055. package/references/sessionlog/src/summarize/summarize.ts +0 -432
  1056. package/references/sessionlog/src/types.ts +0 -508
  1057. package/references/sessionlog/src/utils/chunk-files.ts +0 -49
  1058. package/references/sessionlog/src/utils/commit-message.ts +0 -65
  1059. package/references/sessionlog/src/utils/detect-agent.ts +0 -36
  1060. package/references/sessionlog/src/utils/hook-managers.ts +0 -125
  1061. package/references/sessionlog/src/utils/ide-tags.ts +0 -32
  1062. package/references/sessionlog/src/utils/paths.ts +0 -79
  1063. package/references/sessionlog/src/utils/preview-rewind.ts +0 -80
  1064. package/references/sessionlog/src/utils/rewind-conflict.ts +0 -121
  1065. package/references/sessionlog/src/utils/shadow-branch.ts +0 -109
  1066. package/references/sessionlog/src/utils/string-utils.ts +0 -46
  1067. package/references/sessionlog/src/utils/todo-extract.ts +0 -188
  1068. package/references/sessionlog/src/utils/trailers.ts +0 -187
  1069. package/references/sessionlog/src/utils/transcript-parse.ts +0 -177
  1070. package/references/sessionlog/src/utils/transcript-timestamp.ts +0 -59
  1071. package/references/sessionlog/src/utils/tree-ops.ts +0 -219
  1072. package/references/sessionlog/src/utils/tty.ts +0 -72
  1073. package/references/sessionlog/src/utils/validation.ts +0 -65
  1074. package/references/sessionlog/src/utils/worktree.ts +0 -58
  1075. package/references/sessionlog/src/wire-types.ts +0 -59
  1076. package/references/sessionlog/templates/setup-env.sh +0 -153
  1077. package/references/sessionlog/tsconfig.json +0 -18
  1078. package/references/sessionlog/vitest.config.ts +0 -12
  1079. package/references/swarmkit/LICENSE +0 -21
  1080. package/references/swarmkit/README.md +0 -130
  1081. package/references/swarmkit/docs/design.md +0 -453
  1082. package/references/swarmkit/docs/package-setup-reference.md +0 -519
  1083. package/references/swarmkit/package-lock.json +0 -1938
  1084. package/references/swarmkit/package.json +0 -43
  1085. package/references/swarmkit/src/cli.ts +0 -41
  1086. package/references/swarmkit/src/commands/add.ts +0 -126
  1087. package/references/swarmkit/src/commands/doctor.ts +0 -117
  1088. package/references/swarmkit/src/commands/hive.ts +0 -279
  1089. package/references/swarmkit/src/commands/init/phases/configure.ts +0 -96
  1090. package/references/swarmkit/src/commands/init/phases/global-setup.ts +0 -102
  1091. package/references/swarmkit/src/commands/init/phases/packages.ts +0 -44
  1092. package/references/swarmkit/src/commands/init/phases/project.ts +0 -81
  1093. package/references/swarmkit/src/commands/init/phases/use-case.ts +0 -47
  1094. package/references/swarmkit/src/commands/init/state.test.ts +0 -23
  1095. package/references/swarmkit/src/commands/init/state.ts +0 -22
  1096. package/references/swarmkit/src/commands/init/wizard.ts +0 -160
  1097. package/references/swarmkit/src/commands/init.ts +0 -17
  1098. package/references/swarmkit/src/commands/login.ts +0 -106
  1099. package/references/swarmkit/src/commands/logout.ts +0 -22
  1100. package/references/swarmkit/src/commands/remove.ts +0 -72
  1101. package/references/swarmkit/src/commands/status.ts +0 -101
  1102. package/references/swarmkit/src/commands/update.ts +0 -62
  1103. package/references/swarmkit/src/commands/whoami.ts +0 -41
  1104. package/references/swarmkit/src/config/global.test.ts +0 -258
  1105. package/references/swarmkit/src/config/global.ts +0 -141
  1106. package/references/swarmkit/src/config/keys.test.ts +0 -109
  1107. package/references/swarmkit/src/config/keys.ts +0 -49
  1108. package/references/swarmkit/src/doctor/checks.test.ts +0 -366
  1109. package/references/swarmkit/src/doctor/checks.ts +0 -292
  1110. package/references/swarmkit/src/doctor/types.ts +0 -33
  1111. package/references/swarmkit/src/hub/auth-flow.test.ts +0 -127
  1112. package/references/swarmkit/src/hub/auth-flow.ts +0 -144
  1113. package/references/swarmkit/src/hub/client.test.ts +0 -224
  1114. package/references/swarmkit/src/hub/client.ts +0 -185
  1115. package/references/swarmkit/src/hub/credentials.test.ts +0 -132
  1116. package/references/swarmkit/src/hub/credentials.ts +0 -51
  1117. package/references/swarmkit/src/index.ts +0 -116
  1118. package/references/swarmkit/src/packages/installer.test.ts +0 -365
  1119. package/references/swarmkit/src/packages/installer.ts +0 -206
  1120. package/references/swarmkit/src/packages/plugin.test.ts +0 -141
  1121. package/references/swarmkit/src/packages/plugin.ts +0 -46
  1122. package/references/swarmkit/src/packages/registry.test.ts +0 -235
  1123. package/references/swarmkit/src/packages/registry.ts +0 -209
  1124. package/references/swarmkit/src/packages/setup.test.ts +0 -1349
  1125. package/references/swarmkit/src/packages/setup.ts +0 -635
  1126. package/references/swarmkit/src/utils/ui.test.ts +0 -115
  1127. package/references/swarmkit/src/utils/ui.ts +0 -62
  1128. package/references/swarmkit/tsconfig.json +0 -17
  1129. package/references/swarmkit/vitest.config.ts +0 -9
@@ -1,24 +0,0 @@
1
- {"id":"s-7u8f","uuid":"605bb90c-778e-4a77-95b8-f136577f4f67","title":"OpenTasks Implementation Plan","file_path":"specs/s-7u8f_opentasks_implementation_plan.md","content":"# OpenTasks Implementation Plan\n\nThis spec outlines the phased implementation approach for OpenTasks - a universal work graph data structure.\n\n## Overview\n\nOpenTasks provides:\n1. **Persistent task tracking** - Replaces Claude's ephemeral tasks with git-friendly persistence\n2. **Unified abstraction** - Works over beads/sudocode/other task systems\n3. **External integration** - Bridges to Jira, Linear, GitHub Issues\n\n## Architecture Summary\n\n- **4 Node Types**: Spec (intent), Issue (work), Feedback (comments), External (references)\n- **Graph-Native**: Edges are first-class (blocks, implements, references)\n- **Hierarchical Locations**: `~/.opentasks/` → workspace → project → subproject\n- **Daemon Per Location**: IPC, file watching, flush coordination\n- **Federated Providers**: OpenTasks owns graph; providers own content\n\n## Implementation Phases\n\n### Phase 1: Core Data Layer\n**Goal**: Establish foundational types and persistence\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| Schema/Types | TypeScript types for nodes, edges, storage | `src/schema/` |\n| ID Generation | Hash-based IDs with adaptive length | `src/core/id.ts` |\n| JSONL Persister | Read/write graph.jsonl with atomic writes | `src/persistence/jsonl.ts` |\n| SQLite Persister | Query cache, indexes, views | `src/persistence/sqlite.ts` |\n\n**Deliverables**:\n- [ ] `StoredNode`, `Node`, `Edge` types\n- [ ] `generateId()` with collision-resistant hashing\n- [ ] `JSONLPersister` with atomic writes\n- [ ] `SQLitePersister` with query indexes\n\n### Phase 2: Graph Operations\n**Goal**: CRUD and query capabilities\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| Graph Store | Node/edge CRUD, transaction support | `src/graph/store.ts` |\n| Query Engine | Filter, traverse, ready computation | `src/graph/query.ts` |\n| Validation | Type-specific validation rules | `src/graph/validation.ts` |\n\n**Deliverables**:\n- [ ] `GraphStore` with create/read/update/delete\n- [ ] `QueryEngine` with filter and traversal\n- [ ] `ready()` computation (no active blockers)\n- [ ] Node validation per type\n\n### Phase 3: Daemon & IPC\n**Goal**: Background process for coordination\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| Daemon Lifecycle | Start, stop, lock, signal handling | `src/daemon/lifecycle.ts` |\n| IPC Server | Unix socket, JSON-RPC protocol | `src/daemon/ipc.ts` |\n| Flush Manager | Debounced writes, dirty tracking | `src/daemon/flush.ts` |\n| File Watcher | Detect external JSONL/markdown changes | `src/daemon/watcher.ts` |\n\n**Deliverables**:\n- [ ] Daemon with PID file and lock\n- [ ] Unix socket IPC server\n- [ ] Debounced flush with max delay\n- [ ] File watcher with change detection\n\n### Phase 4: Agent Interface\n**Goal**: 3-tool API for agents\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| link | Create/remove relationships | `src/tools/link.ts` |\n| query | Query graph, blockers, ready | `src/tools/query.ts` |\n| annotate | Add feedback, tags, metadata | `src/tools/annotate.ts` |\n| Client Library | Connect to daemon, make requests | `src/client/` |\n\n**Deliverables**:\n- [ ] `link()` tool implementation\n- [ ] `query()` tool with all query types\n- [ ] `annotate()` tool with routing logic\n- [ ] `OpenTasksClient` class\n\n### Phase 5: Providers\n**Goal**: External system integration\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| Provider Interface | Base contract for all providers | `src/providers/interface.ts` |\n| Native Providers | Fallback spec/issue storage | `src/providers/native.ts` |\n| Beads Provider | Integration with beads CLI | `src/providers/beads.ts` |\n| Taskmaster Provider | Integration with taskmaster | `src/providers/taskmaster.ts` |\n\n**Deliverables**:\n- [ ] `ContentProvider` interface\n- [ ] `NativeSpecProvider` and `NativeIssueProvider`\n- [ ] `BeadsProvider` with CLI integration\n- [ ] `TaskmasterProvider` with file parsing\n\n### Phase 6: Advanced Features\n**Goal**: Full feature set\n\n| Component | Description | Key Files |\n|-----------|-------------|-----------|\n| Cross-Location | URI resolution, daemon discovery | `src/location/` |\n| Markdown Sync | Bidirectional JSONL ↔ markdown | `src/persistence/markdown.ts` |\n| Compaction | Tiered cleanup, tombstone management | `src/persistence/compaction.ts` |\n\n**Deliverables**:\n- [ ] `LocationResolver` with URI parsing\n- [ ] `MarkdownPersister` with sync\n- [ ] `Compactor` with tiered cleanup\n- [ ] Global daemon registry\n\n## Dependency Graph\n\n```\nPhase 1 ──────────────────────────────────────────┐\n │ │\n ▼ │\nPhase 2 ──────────────────────────────────────────┤\n │ │\n ├────────────────┐ │\n ▼ ▼ │\nPhase 3 Phase 4 (partial - types only) │\n │ │ │\n └────────┬───────┘ │\n ▼ │\n Phase 4 (full - needs daemon) │\n │ │\n ▼ │\n Phase 5 ────────────────────────────────────┤\n │ │\n ▼ │\n Phase 6 ◄───────────────────────────────────┘\n```\n\n## Tech Stack\n\n- **Language**: TypeScript\n- **Runtime**: Node.js\n- **Storage**: JSONL (source of truth) + SQLite (cache)\n- **IPC**: Unix domain sockets, JSON-RPC\n- **Testing**: Vitest\n- **Build**: tsup or esbuild\n\n## Open Questions\n\n- [ ] Package structure: monorepo or single package?\n- [ ] CLI interface beyond MCP tools?\n- [ ] Which provider to implement first beyond native?\n\n## References\n\n- [DESIGN.md](./docs/DESIGN.md) - Design rationale\n- [SCHEMA.md](./docs/SCHEMA.md) - Data model\n- [ARCHITECTURE.md](./docs/ARCHITECTURE.md) - Daemon, locations\n- [PERSISTENCE.md](./docs/PERSISTENCE.md) - Storage layer\n- [PROVIDERS.md](./docs/PROVIDERS.md) - Provider integration\n- [INTERFACE.md](./docs/INTERFACE.md) - 3-tool API\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-27 10:10:03","updated_at":"2026-01-27 10:10:03","parent_id":null,"parent_uuid":null,"relationships":[],"tags":["architecture","implementation","opentasks"]}
2
- {"id":"s-4l4m","uuid":"2142456d-a492-4e5c-b078-4891b533fea7","title":"Phase 1: Core Data Layer","file_path":"specs/s-4l4m_phase_1_core_data_layer.md","content":"# Phase 1: Core Data Layer\n\nFoundation layer for OpenTasks. Establishes types, ID generation, and persistence.\n\n**Key Decisions (following beads patterns):**\n- Join table for tags (not JSON array)\n- Edge direction: `from` blocks `to`\n- Custom Storage interface (not TinyBase)\n- Permissive storage, strict application validation\n\n## Overview\n\nThis phase delivers:\n1. **TypeScript Schema** - Node types, edges, storage format\n2. **ID Generation** - Hash-based, collision-resistant IDs\n3. **JSONL Persister** - Git-friendly source of truth\n4. **SQLite Persister** - Fast query cache with join tables\n\n## 1. TypeScript Schema\n\n### File Structure\n\n```\nsrc/\n├── schema/\n│ ├── index.ts # Re-exports\n│ ├── base.ts # BaseNode, common types\n│ ├── nodes.ts # Spec, Issue, Feedback, ExternalNode\n│ ├── edges.ts # Edge types\n│ ├── storage.ts # StoredNode (flexible JSONL format)\n│ └── validation.ts # Type guards, validators\n```\n\n### Core Design Decisions\n\n**Decision 1: Hybrid Storage/Application Model**\n\n- **Storage layer**: Permissive - unknown fields preserved, forward compatible\n- **Application layer**: Strict - type guards validate required fields\n\n```typescript\n// Storage: flexible for JSONL (permissive)\ninterface StoredNode {\n id: string\n uuid: string\n type: string\n title: string\n created_at: string\n updated_at: string\n [key: string]: unknown // preserve unknown fields\n}\n\n// Application: strict discriminated union\ntype Node = Spec | Issue | Feedback | ExternalNode\n```\n\n**Decision 2: ID Format**\n\nPrefix indicates type, hash provides uniqueness:\n- `s-` Spec (e.g., `s-a2b3`)\n- `i-` Issue (e.g., `i-x7k9`)\n- `f-` Feedback (e.g., `f-m4n5`)\n- `e-` External (e.g., `e-p6q7`)\n- `x-` Edge (e.g., `x-r8s9`)\n\n**Decision 3: Edge Direction**\n\n`from` blocks `to` - the source node must complete before target can start:\n```typescript\n// i-123 must complete before i-456 can start\n{ from_id: \"i-123\", to_id: \"i-456\", type: \"blocks\" }\n```\n\n**Decision 4: Timestamps as ISO 8601 strings**\n\nStrings are JSON-native and git-diff friendly. Parse to Date when needed.\n\n### Node Types\n\n#### BaseNode (shared fields)\n\n```typescript\ninterface BaseNode {\n // Identity\n id: string // hash-based: prefix + base36\n uuid: string // UUID v4 for distributed sync\n\n // Core\n title: string\n content?: string // markdown body\n content_hash?: string // SHA256 for dedup\n\n // Timestamps\n created_at: string // ISO 8601\n updated_at: string\n\n // Organization\n priority?: number // 0 (highest) to 4 (lowest)\n tags?: string[] // NOTE: stored in join table in SQLite\n parent_id?: string // hierarchy support\n\n // Context\n location?: string // opentasks:// URI\n branch?: string // git branch\n source?: string // origin: \"local\" | provider name\n\n // Coordination\n claimed_by?: string // agent/user ID\n claimed_at?: string\n lock_until?: string // soft lock expiry\n\n // Lifecycle\n archived?: boolean\n archived_at?: string\n\n // Extensibility\n metadata?: Record<string, unknown>\n}\n```\n\n#### Spec\n\n```typescript\ninterface Spec extends BaseNode {\n type: 'spec'\n status?: 'draft' | 'active' | 'archived' | string\n}\n```\n\n#### Issue\n\n```typescript\ninterface Issue extends BaseNode {\n type: 'issue'\n status: 'open' | 'in_progress' | 'blocked' | 'closed' | string // required\n assignee?: string\n closed_at?: string\n}\n```\n\n#### Feedback\n\n```typescript\ninterface Feedback extends BaseNode {\n type: 'feedback'\n target_id: string // required: what this is about\n target_anchor?: Anchor // optional: location in target\n feedback_type: 'comment' | 'suggestion' | 'request' | string // required\n thread_id?: string\n reply_to_id?: string\n resolved?: boolean\n resolved_at?: string\n dismissed?: boolean\n dismissed_at?: string\n}\n\ninterface Anchor {\n line?: number // 1-indexed\n text?: string // fuzzy match if line moves\n section?: string // heading\n context_before?: string\n context_after?: string\n anchor_status?: 'valid' | 'relocated' | 'stale'\n}\n```\n\n#### ExternalNode\n\n```typescript\ninterface ExternalNode extends BaseNode {\n type: 'external'\n uri: string // required: canonical URI\n source: string // required: provider name\n materialized: boolean // has data been fetched?\n cached_at?: string\n stale?: boolean\n external_status?: string // provider's status\n external_data?: Record<string, unknown>\n}\n```\n\n#### Node Union & Type Guards\n\n```typescript\ntype Node = Spec | Issue | Feedback | ExternalNode\n\nfunction isSpec(node: Node): node is Spec { return node.type === 'spec' }\nfunction isIssue(node: Node): node is Issue { return node.type === 'issue' }\nfunction isFeedback(node: Node): node is Feedback { return node.type === 'feedback' }\nfunction isExternal(node: Node): node is ExternalNode { return node.type === 'external' }\n```\n\n### Edge Types\n\n```typescript\ninterface Edge {\n id: string // x-xxxx\n uuid: string\n from_id: string // source node ID or URI\n to_id: string // target node ID or URI\n type: EdgeType\n created_at: string\n created_by?: string\n source?: string // origin system\n metadata?: Record<string, unknown>\n}\n\n// Edge semantics: from -> to\n// \"blocks\": from must complete before to can start\n// \"implements\": from (issue) implements to (spec)\n\ntype CoreEdgeType =\n | 'blocks' // from blocks to (dependency)\n | 'implements' // issue implements spec\n | 'references' // general reference\n | 'related' // loose association\n\ntype ExtendedEdgeType =\n | 'parent-of'\n | 'child-of'\n | 'duplicates'\n | 'supersedes'\n | 'depends-on'\n | 'discovered-from'\n\ntype EdgeType = CoreEdgeType | ExtendedEdgeType | string\n```\n\n## 2. ID Generation\n\n### Algorithm (following beads)\n\n```typescript\nfunction generateId(type: NodeType, existingCount: number): { id: string; uuid: string } {\n // 1. Generate UUID v4\n const uuid = crypto.randomUUID()\n \n // 2. SHA256 hash the UUID\n const hash = sha256(uuid)\n \n // 3. Convert to base36 (0-9, a-z)\n const base36 = toBase36(hash)\n \n // 4. Adaptive length based on collision probability\n const length = adaptiveLength(existingCount)\n \n // 5. Prepend type prefix\n const prefix = typePrefix(type)\n \n return {\n id: `${prefix}-${base36.slice(0, length)}`,\n uuid\n }\n}\n\nfunction adaptiveLength(count: number): number {\n // Birthday paradox: ~1% collision probability\n if (count < 980) return 4 // s-a2b3\n if (count < 5900) return 5 // s-a2b3f\n if (count < 35000) return 6 // s-a2b3fg\n if (count < 212000) return 7 // s-a2b3fgh\n return 8 // s-a2b3fghi\n}\n\nfunction typePrefix(type: string): string {\n const prefixes: Record<string, string> = {\n spec: 's', issue: 'i', feedback: 'f', external: 'e', edge: 'x'\n }\n return prefixes[type] || 'n'\n}\n```\n\n### Content Hashing (for dedup on merge)\n\n```typescript\nfunction computeContentHash(node: StoredNode): string {\n // Only substantive fields (excludes id, uuid, timestamps, coordination)\n const substantive = {\n type: node.type,\n title: node.title,\n content: node.content,\n status: node.status,\n priority: node.priority,\n tags: node.tags?.sort(),\n parent_id: node.parent_id,\n // Type-specific\n ...(node.type === 'issue' && { assignee: node.assignee }),\n ...(node.type === 'feedback' && {\n target_id: node.target_id,\n feedback_type: node.feedback_type,\n }),\n ...(node.type === 'external' && { uri: node.uri, source: node.source }),\n }\n \n return sha256(JSON.stringify(substantive, Object.keys(substantive).sort()))\n}\n```\n\n## 3. Storage Interface (beads pattern)\n\n### Core Interface\n\n```typescript\n/**\n * Storage interface following beads pattern.\n * Domain-driven, not generic table operations.\n */\ninterface Storage {\n // === Nodes ===\n createNode(node: StoredNode, actor?: string): Promise<void>\n getNode(id: string): Promise<StoredNode | null>\n updateNode(id: string, updates: Partial<StoredNode>, actor?: string): Promise<void>\n deleteNode(id: string, actor?: string): Promise<void>\n queryNodes(filter: NodeFilter): Promise<StoredNode[]>\n \n // === Edges ===\n createEdge(edge: Edge, actor?: string): Promise<void>\n getEdge(id: string): Promise<Edge | null>\n deleteEdge(id: string, actor?: string): Promise<void>\n getEdgesFrom(nodeId: string, type?: EdgeType): Promise<Edge[]>\n getEdgesTo(nodeId: string, type?: EdgeType): Promise<Edge[]>\n \n // === Tags (join table) ===\n addTag(nodeId: string, tag: string, actor?: string): Promise<void>\n removeTag(nodeId: string, tag: string, actor?: string): Promise<void>\n getTags(nodeId: string): Promise<string[]>\n getTagsForNodes(nodeIds: string[]): Promise<Map<string, string[]>>\n getNodesByTag(tag: string): Promise<StoredNode[]>\n \n // === Queries ===\n getReady(): Promise<StoredNode[]> // issues with no active blockers\n \n // === Transactions ===\n runInTransaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T>\n \n // === Dirty Tracking ===\n markDirty(nodeId: string): Promise<void>\n getDirtyNodes(): Promise<string[]>\n clearDirty(nodeIds: string[]): Promise<void>\n \n // === Lifecycle ===\n close(): Promise<void>\n}\n\ninterface Transaction {\n createNode(node: StoredNode, actor?: string): Promise<void>\n updateNode(id: string, updates: Partial<StoredNode>, actor?: string): Promise<void>\n createEdge(edge: Edge, actor?: string): Promise<void>\n addTag(nodeId: string, tag: string, actor?: string): Promise<void>\n}\n\ninterface NodeFilter {\n type?: string | string[]\n status?: string | string[]\n tags?: string[] // AND semantics\n parent_id?: string\n archived?: boolean\n search?: string // title/content search\n limit?: number\n offset?: number\n}\n```\n\n## 4. JSONL Persister\n\n### File Format\n\n```\n.opentasks/\n├── graph.jsonl # Nodes + edges (source of truth)\n├── tombstones.jsonl # Soft deletes (optional gitignore)\n└── config.json # Location config\n```\n\n**graph.jsonl format:**\n```jsonl\n{\"id\":\"s-a2b3\",\"uuid\":\"...\",\"type\":\"spec\",\"title\":\"Auth requirements\",\"tags\":[\"auth\"],...}\n{\"id\":\"i-x7k9\",\"uuid\":\"...\",\"type\":\"issue\",\"title\":\"Implement login\",\"status\":\"open\",...}\n{\"id\":\"x-r8s9\",\"uuid\":\"...\",\"from_id\":\"i-x7k9\",\"to_id\":\"s-a2b3\",\"type\":\"implements\",...}\n```\n\nNote: Tags are stored inline in JSONL but in a join table in SQLite.\n\n### Implementation\n\n```typescript\ninterface JSONLPersisterConfig {\n path: string // e.g., \".opentasks/graph.jsonl\"\n tombstonesPath?: string // e.g., \".opentasks/tombstones.jsonl\"\n atomicWrite?: boolean // default: true\n}\n\nclass JSONLPersister {\n async load(): Promise<{ nodes: StoredNode[]; edges: Edge[] }>\n async save(nodes: StoredNode[], edges: Edge[]): Promise<void>\n async append(entry: StoredNode | Edge): Promise<void>\n watch(callback: () => void): () => void // returns unsubscribe\n}\n\nasync function atomicWrite(path: string, content: string): Promise<void> {\n const tempPath = `${path}.${process.pid}.tmp`\n await fs.writeFile(tempPath, content)\n await fs.rename(tempPath, path)\n}\n```\n\n## 5. SQLite Persister\n\n### Schema (with join table for tags)\n\n```sql\n-- Nodes table\nCREATE TABLE nodes (\n id TEXT PRIMARY KEY,\n uuid TEXT UNIQUE NOT NULL,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n content TEXT,\n content_hash TEXT,\n status TEXT,\n priority INTEGER,\n assignee TEXT,\n parent_id TEXT,\n source TEXT,\n archived INTEGER DEFAULT 0,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n \n -- Feedback fields\n target_id TEXT,\n feedback_type TEXT,\n \n -- External fields\n uri TEXT,\n materialized INTEGER,\n \n FOREIGN KEY (parent_id) REFERENCES nodes(id)\n);\n\n-- Tags join table (following beads pattern)\nCREATE TABLE node_tags (\n node_id TEXT NOT NULL,\n tag TEXT NOT NULL,\n PRIMARY KEY (node_id, tag),\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n);\nCREATE INDEX idx_node_tags_tag ON node_tags(tag);\n\n-- Edges table\nCREATE TABLE edges (\n id TEXT PRIMARY KEY,\n uuid TEXT UNIQUE NOT NULL,\n from_id TEXT NOT NULL,\n to_id TEXT NOT NULL,\n type TEXT NOT NULL,\n created_at TEXT NOT NULL,\n created_by TEXT,\n source TEXT\n);\n\n-- Indexes\nCREATE INDEX idx_nodes_type ON nodes(type);\nCREATE INDEX idx_nodes_status ON nodes(status);\nCREATE INDEX idx_nodes_parent ON nodes(parent_id);\nCREATE INDEX idx_nodes_archived ON nodes(archived);\nCREATE INDEX idx_edges_from ON edges(from_id);\nCREATE INDEX idx_edges_to ON edges(to_id);\nCREATE INDEX idx_edges_type ON edges(type);\n\n-- Ready issues view (no active blockers)\nCREATE VIEW ready_issues AS\nSELECT n.* FROM nodes n\nWHERE n.type = 'issue'\n AND n.status = 'open'\n AND n.archived = 0\n AND NOT EXISTS (\n SELECT 1 FROM edges e\n JOIN nodes blocker ON e.from_id = blocker.id\n WHERE e.to_id = n.id\n AND e.type = 'blocks'\n AND blocker.status != 'closed'\n AND blocker.archived = 0\n );\n\n-- Dirty tracking (for incremental export)\nCREATE TABLE dirty_nodes (\n node_id TEXT PRIMARY KEY,\n marked_at TEXT NOT NULL\n);\n\n-- Export hashes (for content-based change detection)\nCREATE TABLE export_hashes (\n node_id TEXT PRIMARY KEY,\n content_hash TEXT NOT NULL\n);\n```\n\n### Implementation\n\n```typescript\ninterface SQLitePersisterConfig {\n path: string // e.g., \".opentasks/cache.db\"\n walMode?: boolean // default: true (better concurrency)\n}\n\nclass SQLitePersister implements Storage {\n private db: Database // better-sqlite3\n \n // Rebuild cache from JSONL\n async rebuildFromJsonl(jsonlPath: string): Promise<void>\n \n // Tag operations use join table\n async addTag(nodeId: string, tag: string): Promise<void> {\n this.db.prepare(`\n INSERT OR IGNORE INTO node_tags (node_id, tag) VALUES (?, ?)\n `).run(nodeId, tag)\n await this.markDirty(nodeId)\n }\n \n async getNodesByTag(tag: string): Promise<StoredNode[]> {\n return this.db.prepare(`\n SELECT n.* FROM nodes n\n JOIN node_tags t ON n.id = t.node_id\n WHERE t.tag = ?\n `).all(tag)\n }\n \n async getReady(): Promise<StoredNode[]> {\n return this.db.prepare('SELECT * FROM ready_issues').all()\n }\n}\n```\n\n## Deliverables Checklist\n\n- [ ] `src/schema/base.ts` - BaseNode interface\n- [ ] `src/schema/nodes.ts` - Spec, Issue, Feedback, ExternalNode\n- [ ] `src/schema/edges.ts` - Edge interface and types\n- [ ] `src/schema/storage.ts` - StoredNode (flexible format)\n- [ ] `src/schema/validation.ts` - Type guards and validators\n- [ ] `src/schema/index.ts` - Re-exports\n- [ ] `src/core/id.ts` - generateId, computeContentHash\n- [ ] `src/storage/interface.ts` - Storage interface\n- [ ] `src/storage/jsonl.ts` - JSONLPersister\n- [ ] `src/storage/sqlite.ts` - SQLitePersister (with better-sqlite3)\n- [ ] `src/storage/index.ts` - Re-exports\n- [ ] Tests for all modules\n\n## Resolved Decisions\n\n| Question | Decision | Rationale |\n|----------|----------|-----------|\n| Tags storage | Join table `node_tags(node_id, tag)` | Follows beads, fast queries by tag |\n| Edge direction | `from` blocks `to` | Intuitive: source must complete first |\n| Storage abstraction | Custom Storage interface | Follows beads, simpler than TinyBase |\n| SQLite library | `better-sqlite3` | Sync API, fast, well-maintained |\n| Validation | Permissive storage, strict app | Forward compatible, type-safe usage |\n\n## References\n\n- [[s-7u8f|OpenTasks Implementation Plan]]\n- [SCHEMA.md](../docs/SCHEMA.md)\n- [PERSISTENCE.md](../docs/PERSISTENCE.md)\n- Beads reference: `references/beads/internal/storage/`\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-27 10:18:37","updated_at":"2026-01-27 20:04:29","parent_id":"s-7u8f","parent_uuid":"605bb90c-778e-4a77-95b8-f136577f4f67","relationships":[],"tags":["opentasks","persistence","phase-1","schema"]}
3
- {"id":"s-9jju","uuid":"50f4aa8e-9378-4238-909d-4e1a117452fb","title":"OpenTasks Core Architecture","file_path":"specs/s-9jju_opentasks_core_architecture.md","content":"# OpenTasks Core Architecture\n\n## Overview\n\nOpenTasks is a **graph connector** that links heterogeneous task and spec systems. It does not replace existing tools — each keeps its own interface, storage, and semantics. OpenTasks provides the **relationship layer** that existing tools lack.\n\n### What OpenTasks Is\n- A graph layer over existing tools (Claude Tasks, Beads, Taskmaster, Jira, etc.)\n- Cross-system edges that span system boundaries\n- Unified queries for blockers, ready items, and dependencies across all connected systems\n- Optional native storage for lightweight specs/issues when external providers aren't needed\n\n### What OpenTasks Is NOT\n- Not a replacement for Claude's built-in tasks (use `TaskCreate`/`TaskUpdate` directly)\n- Not a replacement for Beads (use `bd` CLI directly)\n- Not a unified CRUD API (each system keeps its own interface)\n\n---\n\n## Core Concepts\n\n### Node Types\n\n| Type | Purpose | Storage |\n|------|---------|---------|\n| `spec` | User intent, requirements, context | Native (graph.jsonl) or provider |\n| `issue` | Actionable work items | Native (graph.jsonl) or provider |\n| `feedback` | Comments, suggestions, anchored discussion | Native (graph.jsonl) |\n| `external` | Cached references to external systems | Native (graph.jsonl) |\n\n### Edges\n\nEdges are the primary value of OpenTasks. They connect nodes across system boundaries:\n\n```\nclaude://current/t-abc ──implements──▶ beads://./bd-x7k9\nbeads://./bd-x7k9 ──blocks──▶ jira://PROJ-123\ntaskmaster://./prd ◀──discovered-from── beads://./bd-y8z0\n```\n\n**Core edge types**: `blocks`, `implements`, `references`, `related`\n**Extended types**: `parent-of`, `child-of`, `duplicates`, `supersedes`, `depends-on`, `discovered-from`\n\n### The 3-Tool Interface\n\nAgents use native tools for CRUD (TaskCreate, bd new, etc.). OpenTasks provides 3 tools for the graph layer:\n\n| Tool | Purpose |\n|------|---------|\n| `link()` | Create/remove edges between any nodes |\n| `query()` | Find relationships, blockers, ready items |\n| `annotate()` | Add cross-system feedback |\n\n---\n\n## URI Schemes\n\n### Provider URIs\n\nReference nodes in external systems:\n\n```\nclaude://[session]/[task-id] # Claude Code task\nbeads://[workspace]/[id] # Beads issue \ntaskmaster://[project]/[id] # Taskmaster PRD/task\nlinear://[team]/[id] # Linear issue\njira://[project]/[key] # Jira issue\ngithub://[owner]/[repo]/[num] # GitHub issue\nnative://[type]/[id] # OpenTasks native node\n```\n\n**Relative notation**: `beads://./bd-123` means current workspace\n**Implicit current**: `beads://bd-123` equivalent to `beads://./bd-123`\n\n### OpenTasks URIs (Phase 2+)\n\nReference nodes in other OpenTasks locations:\n\n```\nopentasks://./i-x7k9 # Current location\nopentasks://~/.opentasks/s-a2b3 # User-level location\nopentasks://../.opentasks/i-e6f7 # Parent directory\nopentasks:///abs/path/.opentasks/s-g8h9 # Absolute path\n```\n\n---\n\n## Cross-Location Model\n\n### Location Hierarchy\n\n```\n~/.opentasks/ # User level\n~/projects/.opentasks/ # Workspace level\n~/projects/myapp/.opentasks/ # Project level\n~/projects/myapp/packages/core/.opentasks/ # Subproject level\n```\n\n### Isolation by Default\n\nEach location is **isolated by default**:\n- Queries only return nodes from the current location\n- No automatic inheritance from parent locations\n- No automatic discovery of child locations\n- Explicit connectivity via edges with URIs\n\n### Worktree Model\n\nFor agent swarms in git worktrees:\n\n```\nrepo/\n├── .git/\n├── main-worktree/\n│ └── .opentasks/ # Manager agent's graph\n├── feature-a-worktree/\n│ └── .opentasks/ # Worker A's graph (cloned, with redirect)\n│ └── config.json # redirect → main-worktree\n└── feature-b-worktree/\n └── .opentasks/ # Worker B's graph (cloned, with redirect)\n └── config.json # redirect → main-worktree\n```\n\n**Redirect Rules**:\n- Sub-agents get their own cloned `.opentasks/` directory\n- Config contains redirect rules pointing to manager's location\n- Manager can update worker's config during worktree setup\n- Supports chained redirects (worker → manager → orchestrator)\n\n```json\n{\n \"redirects\": [\n {\n \"operations\": [\"read\", \"write\"],\n \"pattern\": \"*\",\n \"target\": \"opentasks://../main-worktree/.opentasks/\"\n }\n ]\n}\n```\n\n---\n\n## Storage Design\n\n### File Structure\n\n```\n.opentasks/\n├── graph.jsonl # Nodes + edges (source of truth)\n├── tombstones.jsonl # Soft deletes (configurable gitignore)\n├── cache.db # SQLite (queries, indexes) - gitignored\n├── config.json # Configuration, redirects\n├── specs/ # Optional: markdown expansion\n├── issues/ # Optional: markdown expansion\n└── daemon.sock # Daemon socket (when running)\n```\n\n### Edge Storage Format (Option C)\n\nShort IDs for local nodes, full URIs for external:\n\n```jsonl\n{\"id\":\"x-r8s9\",\"from_id\":\"i-x7k9\",\"to_id\":\"s-a2b3\",\"type\":\"implements\",...}\n{\"id\":\"x-t1u2\",\"from_id\":\"i-x7k9\",\"to_id\":\"beads://./bd-123\",\"type\":\"blocks\",...}\n{\"id\":\"x-v3w4\",\"from_id\":\"i-x7k9\",\"to_id\":\"opentasks://../other/.opentasks/i-y8z0\",\"type\":\"references\",...}\n```\n\n### ID Generation\n\nHash-based IDs for collision resistance in multi-agent scenarios:\n- Prefix indicates type: `s-` (spec), `i-` (issue), `f-` (feedback), `e-` (external), `x-` (edge)\n- Adaptive length based on entity count (4-8 chars)\n- Example: `i-x7k9`, `s-a2b3f`\n\n---\n\n## Implementation Phases\n\n### Phase 1 / L2: Single-Location + Provider URIs (v1)\n\n**Scope**:\n- One `.opentasks/` directory per working context\n- Edges reference provider URIs: `beads://`, `claude://`, `jira://`, etc.\n- Native provider for optional local specs/issues (toggleable)\n- Direct file access (no daemon)\n- No `opentasks://` URIs to other locations\n- No cross-location queries\n\n**Deliverables**:\n- [ ] Core schema (nodes, edges) with TypeScript types\n- [ ] JSONL persister (read/write graph.jsonl)\n- [ ] SQLite cache persister (indexes, queries)\n- [ ] 3-tool interface: link, query, annotate\n- [ ] Native provider for specs/issues\n- [ ] Provider registry and URI resolution\n- [ ] Basic provider implementations (Beads, Claude Tasks stubs)\n\n**Schema preparation for future phases**:\n- Store location metadata in config.json\n- Design edge schema to support full URIs\n- Include `source` field on nodes for provider tracking\n\n### Phase 2 / L3: Cross-Location References (v2)\n\n**Scope**:\n- `opentasks://` URIs for referencing other locations\n- URI resolution (relative paths, absolute paths, `~` expansion)\n- Daemon per location (auto-start on first operation)\n- Global registry for daemon discovery (`~/.opentasks/registry.json`)\n- Redirect rules (basic: single target per location)\n\n**Deliverables**:\n- [ ] opentasks:// URI parser and resolver\n- [ ] Daemon architecture (socket, IPC protocol)\n- [ ] Global daemon registry\n- [ ] Redirect configuration and resolution\n- [ ] Cross-location node resolution (follow URIs to other daemons)\n\n### Phase 3 / L4-L5: Multi-Location Queries (v3)\n\n**Scope**:\n- Location discovery (find .opentasks/ in ancestors/descendants)\n- Query expansion modes: `follow-refs`, `ancestors`, `descendants`, `siblings`, `all`\n- Advanced redirect rules (conditional, pattern-based)\n- Cross-location `ready()` queries\n- Worktree detection and automatic redirect setup\n\n**Deliverables**:\n- [ ] Location discovery service\n- [ ] Expansion mode implementation\n- [ ] Conditional redirect rules\n- [ ] Cross-daemon query coordination\n- [ ] Worktree context detection\n\n---\n\n## Design Decisions\n\n| Decision | Choice | Rationale |\n|----------|--------|-----------|\n| Core identity | Graph connector | Existing tools handle CRUD; OpenTasks adds relationships |\n| Interface | 3 tools (link, query, annotate) | Providers have their own CRUD; OpenTasks is additive |\n| Edge storage | Option C (short local, URI external) | Simple common case, explicit for cross-boundary |\n| Worktree model | Clone + redirect | Sub-agents get isolation with configurable routing |\n| Daemon | Required for multi-agent (Phase 2+) | Coordination, caching, cross-location resolution |\n| Native provider | Optional, toggleable | Lightweight use without external dependencies |\n\n---\n\n## Open Questions\n\n### For Phase 1\n- [ ] Exact provider interface for Beads, Claude Tasks\n- [ ] How to handle `claude://current/` session scoping\n- [ ] Feedback routing rules (native vs provider comments)\n\n### For Phase 2+\n- [ ] Daemon auto-start behavior and lifecycle\n- [ ] Registry cleanup for stale daemons\n- [ ] Authentication for cross-location access\n\n### Deferred\n- [ ] Remote repository locations (`opentasks://github.com/...`)\n- [ ] P2P sync between locations\n- [ ] Compaction and archival strategies\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-27 19:09:25","updated_at":"2026-01-27 19:09:25","parent_id":null,"parent_uuid":null,"relationships":[],"tags":["architecture","core","cross-location","multi-agent"]}
4
- {"id":"s-9hf5","uuid":"9d1db0e3-f66b-40d3-a4df-45a09ba8dda8","title":"Phase 1: Single-Location + Provider URIs","file_path":"specs/s-9hf5_phase_1_single_location_provider_uris.md","content":"# Phase 1: Single-Location + Provider URIs\n\n**Parent**: [[s-9jju]] OpenTasks Core Architecture\n\n## Scope\n\nThe minimal viable OpenTasks that provides the core graph connector value:\n- One `.opentasks/` directory per working context\n- Edges that reference provider URIs (`beads://`, `claude://`, `jira://`, etc.)\n- Optional native specs/issues for lightweight use\n- Direct file access (no daemon)\n\n## What's Included\n\n### Core Data Layer\n- Node types: `spec`, `issue`, `feedback`, `external`\n- Edge types: `blocks`, `implements`, `references`, `related`, etc.\n- JSONL storage (`graph.jsonl`) as source of truth\n- SQLite cache (`cache.db`) for fast queries\n\n### Provider System\n- Provider registry with URI scheme routing\n- Native provider (optional, toggleable) for local specs/issues\n- Provider interface for external systems\n- Stub implementations for Beads, Claude Tasks\n\n### 3-Tool Interface\n- `link(from, to, type)` — create/remove edges\n- `query(find, node?, filters)` — query graph relationships\n- `annotate(target, feedback)` — add feedback to nodes\n\n### Storage Format\n\n**graph.jsonl** — Nodes and edges:\n```jsonl\n{\"id\":\"s-a2b3\",\"uuid\":\"...\",\"type\":\"spec\",\"title\":\"Auth requirements\",\"content\":\"...\",\"created_at\":\"...\",\"updated_at\":\"...\"}\n{\"id\":\"i-x7k9\",\"uuid\":\"...\",\"type\":\"issue\",\"title\":\"Implement login\",\"status\":\"open\",\"created_at\":\"...\",\"updated_at\":\"...\"}\n{\"id\":\"x-r8s9\",\"uuid\":\"...\",\"from_id\":\"i-x7k9\",\"to_id\":\"s-a2b3\",\"type\":\"implements\",\"created_at\":\"...\"}\n{\"id\":\"x-t1u2\",\"uuid\":\"...\",\"from_id\":\"i-x7k9\",\"to_id\":\"beads://./bd-123\",\"type\":\"blocks\",\"created_at\":\"...\"}\n```\n\n**config.json** — Location configuration:\n```json\n{\n \"version\": \"1.0\",\n \"location\": {\n \"name\": \"myapp\",\n \"uri\": \"opentasks:///Users/alex/projects/myapp/.opentasks/\"\n },\n \"providers\": {\n \"native\": { \"enabled\": true },\n \"beads\": { \"workspace\": \".\" }\n }\n}\n```\n\n## What's NOT Included (Phase 2+)\n\n- `opentasks://` URIs to other locations\n- Cross-location queries\n- Daemon architecture\n- Redirect rules\n- Location discovery\n- Global registry\n\n## Schema Preparation for Future\n\nEven though we're not implementing cross-location yet, the schema should support it:\n\n1. **Location URI in config** — Store canonical location URI\n2. **Full URI support in edges** — Parser handles `opentasks://` even if not resolved\n3. **Source tracking on nodes** — Know where nodes came from\n\n## Deliverables\n\n### Core Package (`@opentasks/core`)\n- [ ] TypeScript types for all node/edge types\n- [ ] ID generation (hash-based, prefix by type)\n- [ ] Content hashing for dedup\n- [ ] Validation functions\n\n### Storage Package (`@opentasks/storage`)\n- [ ] Persister interface (TinyBase-inspired)\n- [ ] JSONL persister\n- [ ] SQLite persister (cache/index)\n- [ ] Persistence manager (coordinates persisters)\n\n### Graph Package (`@opentasks/graph`)\n- [ ] Graph data structure (nodes + edges)\n- [ ] Query engine (filters, traversal)\n- [ ] Mutation handlers (create, update, delete, archive)\n\n### Interface Package (`@opentasks/interface`)\n- [ ] `link()` implementation\n- [ ] `query()` implementation \n- [ ] `annotate()` implementation\n- [ ] Error types and handling\n\n### Provider Package (`@opentasks/providers`)\n- [ ] Provider interface\n- [ ] Provider registry\n- [ ] Native provider (specs, issues)\n- [ ] External node management (phantom → materialized)\n- [ ] Beads provider stub\n- [ ] Claude Tasks provider stub\n\n### CLI (`@opentasks/cli`)\n- [ ] `opentasks init` — Initialize .opentasks/ directory\n- [ ] `opentasks link` — Create edges\n- [ ] `opentasks query` — Query graph\n- [ ] `opentasks show` — Show node details\n\n## Technical Decisions\n\n### URI Parsing\n```typescript\ninterface ParsedURI {\n scheme: string // \"beads\", \"claude\", \"opentasks\", etc.\n path: string // \"./bd-123\", \"current/t-abc\", etc.\n nodeId: string // \"bd-123\", \"t-abc\", etc.\n isRelative: boolean // true for \"./\", false for absolute\n}\n\nfunction parseURI(uri: string): ParsedURI\nfunction isLocalId(ref: string): boolean // \"i-x7k9\" vs \"beads://...\"\nfunction toCanonicalURI(ref: string, location: string): string\n```\n\n### Query Types\n```typescript\ntype QueryType =\n | 'edges' // Get edges for a node\n | 'blockers' // What blocks this node?\n | 'blocking' // What does this node block?\n | 'ready' // What's ready to work on?\n | 'implementers' // Issues implementing a spec\n | 'specs' // Specs that an issue implements\n | 'children' // Child nodes\n | 'parents' // Parent nodes\n | 'resolve' // Get node metadata by URI\n```\n\n### Provider Interface\n```typescript\ninterface Provider {\n name: string\n schemes: string[]\n capabilities: { read, write, search, watch, ready }\n \n get(id: string): Promise<ProviderNode | null>\n list(filter?: Filter): Promise<ProviderNode[]>\n create?(input: CreateInput): Promise<ProviderNode>\n update?(id: string, updates: Updates): Promise<ProviderNode>\n}\n```\n\n## Success Criteria\n\nPhase 1 is complete when:\n1. Can initialize `.opentasks/` in a directory\n2. Can create native specs and issues\n3. Can create edges between native nodes\n4. Can create edges to provider URIs (e.g., `beads://./bd-123`)\n5. Can query blockers, ready items across the graph\n6. Can add feedback/annotations to nodes\n7. Storage persists correctly to JSONL and rebuilds SQLite cache\n","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-27 19:10:37","updated_at":"2026-01-27 19:10:37","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-9hf5","from_type":"spec","to":"s-9jju","to_type":"spec","type":"implements"}],"tags":["implementation","phase-1","v1"]}
5
- {"id":"s-2qms","uuid":"dbe26ed7-8169-4818-a84e-89ddb0071aff","title":"Phase 3: Multi-Location Queries","file_path":"specs/s-2qms_phase_3_multi_location_queries.md","content":"# Phase 3: Multi-Location Queries\n\n**Parent**: [[s-9jju]] OpenTasks Core Architecture\n\n## Scope\n\nEnable queries that span multiple OpenTasks locations, with automatic discovery and expansion.\n\n## Prerequisites\n\n- [[s-ph1]] Phase 1 complete\n- [[s-ph2]] Phase 2 complete (daemon, cross-location URIs)\n\n## What's Included\n\n### Location Discovery\n\nFind `.opentasks/` directories in the filesystem:\n```typescript\ninterface DiscoveryOptions {\n from: string // Starting path\n direction: 'ancestors' | 'descendants' | 'siblings' | 'all'\n maxDepth?: number // Default: 10\n includeInactive?: boolean // Include locations without running daemon\n skip?: string[] // Default: ['node_modules', '.git', 'vendor']\n}\n\nconst locations = await discover({\n from: '~/projects/myapp/.opentasks/',\n direction: 'ancestors'\n})\n// Returns:\n// - opentasks://~/projects/.opentasks/\n// - opentasks://~/.opentasks/\n```\n\n### Query Expansion Modes\n\n```typescript\ntype ExpansionMode =\n | 'none' // Only current location (default)\n | 'follow-refs' // Follow outbound edge references\n | 'ancestors' // Include parent locations (upward)\n | 'descendants' // Include child locations (downward)\n | 'siblings' // Include sibling locations\n | 'all' // Full hierarchy traversal\n```\n\n**Usage**:\n```typescript\n// Default: isolated query\nconst issues = await query({ find: 'ready' })\n\n// Expand to follow references (resolve external URIs)\nconst issues = await query({ find: 'ready' }, { expand: 'follow-refs' })\n\n// Expand to include ancestor locations\nconst specs = await query({ find: 'specs' }, { expand: 'ancestors' })\n```\n\n### Conditional Redirect Rules\n\nAdvanced redirect configuration:\n```json\n{\n \"redirects\": [\n {\n \"operations\": [\"write\"],\n \"pattern\": \"i-*\",\n \"target\": \"opentasks://../main-worktree/.opentasks/\",\n \"when\": {\n \"branch\": \"feature-*\",\n \"worktree\": true\n }\n },\n {\n \"operations\": [\"read\"],\n \"pattern\": \"s-*\",\n \"target\": \"opentasks://~/.opentasks/\",\n \"when\": {\n \"agent\": \"sub-agent-*\"\n }\n }\n ]\n}\n```\n\n### Cross-Location Ready Query\n\nFind ready items across multiple locations:\n```typescript\nconst ready = await query(\n { find: 'ready' },\n { expand: 'descendants' } // Check all child locations\n)\n\n// Returns nodes from:\n// - Current location\n// - All discovered child locations\n// - With blockers resolved across locations\n```\n\n### Worktree Detection\n\nAutomatic detection of git worktree context:\n```typescript\ninterface WorktreeContext {\n isWorktree: boolean\n mainWorktreePath?: string\n worktreeName?: string\n branch?: string\n}\n\nfunction detectWorktreeContext(): WorktreeContext\n```\n\n## Deliverables\n\n### Discovery Package (`@opentasks/discovery`)\n- [ ] Location scanner (filesystem traversal)\n- [ ] Worktree detection\n- [ ] Location hierarchy builder\n- [ ] Connection detection (which locations have edges to current)\n\n### Expansion Package (`@opentasks/expansion`)\n- [ ] Expansion mode handler\n- [ ] Multi-daemon query coordinator\n- [ ] Result aggregation and deduplication\n\n### Advanced Redirects\n- [ ] Conditional rule matching\n- [ ] Pattern-based redirects\n- [ ] Agent/branch/worktree conditions\n\n## Technical Design\n\n### Multi-Daemon Query Flow\n```\n1. Query with expand: 'descendants'\n2. Discover child locations via filesystem scan\n3. For each location:\n a. Find/start daemon\n b. Send query via IPC\n c. Collect results\n4. Aggregate results\n5. Resolve cross-location blockers\n6. Return unified result set\n```\n\n### Expansion Result Format\n```typescript\ninterface ExpandedResult {\n local: Node[] // Results from current location\n external: Map<string, Node[]> // Results by location URI\n crossLocationEdges: Edge[] // Edges spanning locations\n queriedLocations: string[] // All locations queried\n}\n```\n\n## Success Criteria\n\nPhase 3 is complete when:\n1. Location discovery finds .opentasks/ in ancestors/descendants/siblings\n2. Queries with expansion modes return results from multiple locations\n3. Cross-location blockers are correctly resolved in ready queries\n4. Conditional redirect rules match on branch/worktree/agent\n5. Worktree context is automatically detected\n","priority":3,"archived":0,"archived_at":null,"created_at":"2026-01-27 19:10:38","updated_at":"2026-01-27 19:10:38","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-2qms","from_type":"spec","to":"s-7es6","to_type":"spec","type":"depends-on"},{"from":"s-2qms","from_type":"spec","to":"s-9jju","to_type":"spec","type":"implements"}],"tags":["discovery","expansion","phase-3","v3"]}
6
- {"id":"s-7es6","uuid":"68f28048-f30c-423b-82aa-25fed31ab614","title":"Phase 2: Cross-Location References","file_path":"specs/s-7es6_phase_2_cross_location_references.md","content":"# Phase 2: Cross-Location References\n\n**Parent**: [[s-9jju]] OpenTasks Core Architecture\n\n## Scope\n\nAdd the ability to reference nodes in other OpenTasks locations, enabling multi-repo and multi-worktree scenarios.\n\n## Prerequisites\n\n- [[s-ph1]] Phase 1 complete (single-location + provider URIs)\n\n## What's Included\n\n### opentasks:// URI Resolution\n```\nopentasks://./i-x7k9 # Current location\nopentasks://~/.opentasks/s-a2b3 # User-level location\nopentasks://../.opentasks/i-e6f7 # Parent directory\nopentasks://../other-repo/.opentasks/s-g8h9 # Sibling directory\nopentasks:///abs/path/.opentasks/i-j0k1 # Absolute path\n```\n\n### Daemon Architecture\n\nOne daemon per `.opentasks/` location:\n- Auto-starts on first operation (configurable)\n- Unix socket for IPC (`.opentasks/daemon.sock`)\n- JSON-RPC protocol\n- Graceful shutdown with final flush\n\n**Lifecycle**:\n```\nSTART → acquire lock → init persistence → start socket → register in global registry\nRUNNING → handle IPC requests → watch files → flush changes\nSTOP → flush → unregister → cleanup socket/lock\n```\n\n### Global Registry\n\nCentral tracking of all running daemons (`~/.opentasks/registry.json`):\n```json\n{\n \"daemons\": [\n {\n \"workspacePath\": \"/Users/alex/projects/myapp/.opentasks\",\n \"socketPath\": \"/Users/alex/projects/myapp/.opentasks/daemon.sock\",\n \"pid\": 12345,\n \"version\": \"1.0.0\",\n \"startedAt\": \"2025-01-27T10:00:00Z\"\n }\n ]\n}\n```\n\n### Redirect Rules\n\nConfiguration for routing operations to another location:\n```json\n{\n \"redirects\": [\n {\n \"operations\": [\"read\", \"write\"],\n \"pattern\": \"*\",\n \"target\": \"opentasks://../main-worktree/.opentasks/\"\n }\n ]\n}\n```\n\n**Use case**: Manager agent spawns worker in worktree, configures worker's `.opentasks/` to redirect to manager's location.\n\n**Chained redirects**: Worker → Manager → Orchestrator (each hop resolves)\n\n## What's NOT Included (Phase 3)\n\n- Location discovery (find .opentasks/ in ancestors/descendants)\n- Query expansion modes (follow-refs, ancestors, descendants)\n- Conditional redirect rules\n- Cross-location aggregated queries\n\n## Deliverables\n\n### Daemon Package (`@opentasks/daemon`)\n- [ ] Daemon process with socket server\n- [ ] Lock file management\n- [ ] IPC protocol (JSON-RPC over Unix socket)\n- [ ] Auto-start behavior\n- [ ] Graceful shutdown\n- [ ] Health monitoring\n\n### Registry Package (`@opentasks/registry`)\n- [ ] Global registry read/write\n- [ ] Daemon registration/unregistration\n- [ ] Stale entry cleanup (dead PIDs)\n- [ ] Find daemon for path\n\n### URI Resolution (`@opentasks/uri`)\n- [ ] opentasks:// URI parser\n- [ ] Relative path resolution\n- [ ] Tilde (~) expansion\n- [ ] Cross-location node fetching (via daemon IPC)\n\n### Redirect System\n- [ ] Redirect rule configuration\n- [ ] Redirect resolution logic\n- [ ] Chained redirect support\n\n## Technical Design\n\n### Daemon IPC Protocol\n```typescript\n// Request\n{ \"id\": \"uuid\", \"method\": \"graph.query\", \"params\": { \"find\": \"ready\" } }\n\n// Response\n{ \"id\": \"uuid\", \"result\": { \"nodes\": [...] } }\n// or\n{ \"id\": \"uuid\", \"error\": { \"code\": -32000, \"message\": \"...\" } }\n```\n\n**Methods**:\n- Lifecycle: `ping`, `health`, `status`, `shutdown`\n- Graph: `query`, `get`, `create`, `update`, `delete`\n- Sync: `flush`, `import`, `export`\n- Discovery: `resolve-uri`\n\n### Cross-Location Resolution Flow\n```\n1. Parse URI: opentasks://../other/.opentasks/i-x7k9\n2. Resolve path: /Users/alex/projects/other/.opentasks/\n3. Find daemon in registry (or start if auto-start enabled)\n4. Send IPC request to that daemon\n5. Return resolved node\n```\n\n### Redirect Resolution Flow\n```\n1. Operation requested on local location\n2. Check config.json for matching redirect rule\n3. If redirect found, resolve target location\n4. Forward operation to target location\n5. Handle chained redirects (max depth)\n```\n\n## Success Criteria\n\nPhase 2 is complete when:\n1. Can create edges referencing `opentasks://` URIs in other locations\n2. Daemon starts automatically and handles IPC requests\n3. Global registry tracks all running daemons\n4. Can query a node in another location via URI\n5. Redirect rules route operations to configured target\n6. Chained redirects work (worker → manager → orchestrator)\n","priority":2,"archived":0,"archived_at":null,"created_at":"2026-01-27 19:10:38","updated_at":"2026-01-27 19:10:38","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-7es6","from_type":"spec","to":"s-9hf5","to_type":"spec","type":"depends-on"},{"from":"s-7es6","from_type":"spec","to":"s-9jju","to_type":"spec","type":"implements"}],"tags":["cross-location","daemon","phase-2","v2"]}
7
- {"id":"s-zm8l","uuid":"ddb42b28-6aca-4da9-805b-6b637203c26d","title":"Phase 2: Graph Operations","file_path":"specs/s-zm8l_phase_2_graph_operations.md","content":"# Phase 2: Graph Operations\n\nBuild the graph layer on top of Phase 1's persistence layer. Provides unified CRUD, rich queries, and business logic validation.\n\n## Overview\n\nPhase 2 creates the **application layer** that sits between the raw persistence (JSONL/SQLite) and the eventual agent interface (Phase 4's 3-tool API).\n\n```\n┌─────────────────────────────────────────────────────────┐\n│ Agent Interface │\n│ (Phase 4: link, query, annotate) │\n└─────────────────────────────────────────────────────────┘\n │\n ▼\n┌─────────────────────────────────────────────────────────┐\n│ Phase 2: Graph Layer │\n│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │\n│ │ GraphStore │ │QueryEngine │ │ Validation │ │\n│ │ │ │ │ │ │ │\n│ │ • CRUD │ │ • Filter │ │ • Type rules │ │\n│ │ • Sync │ │ • Traverse │ │ • Cycle detect │ │\n│ │ • Txn │ │ • Ready │ │ • Edge rules │ │\n│ └─────────────┘ └─────────────┘ └─────────────────┘ │\n└─────────────────────────────────────────────────────────┘\n │\n ▼\n┌─────────────────────────────────────────────────────────┐\n│ Phase 1: Persistence │\n│ JSONLPersister ◄──► SQLitePersister │\n└─────────────────────────────────────────────────────────┘\n```\n\n## Components\n\n### 1. GraphStore (`src/graph/store.ts`)\n\nUnified API coordinating JSONL (source of truth) and SQLite (query cache).\n\n```typescript\ninterface GraphStoreConfig {\n /** Base path for .opentasks directory */\n basePath: string\n \n /** Auto-initialize if not exists */\n autoInit?: boolean\n \n /** Flush strategy */\n flush?: {\n /** Debounce delay in ms (default: 5000) */\n debounceMs?: number\n /** Max delay before forced flush (default: 30000) */\n maxDelayMs?: number\n }\n}\n\ninterface GraphStore {\n // === Lifecycle ===\n \n /** Initialize store (create files, load data) */\n initialize(): Promise<void>\n \n /** Close store (flush pending, close connections) */\n close(): Promise<void>\n \n /** Force flush pending changes to JSONL */\n flush(): Promise<void>\n \n // === Node Operations ===\n \n /** Create a new node */\n createNode(input: CreateNodeInput): Promise<Node>\n \n /** Get node by ID */\n getNode(id: string): Promise<Node | null>\n \n /** Update node fields */\n updateNode(id: string, updates: UpdateNodeInput): Promise<Node>\n \n /** Delete node (soft delete by default) */\n deleteNode(id: string, options?: DeleteOptions): Promise<void>\n \n /** Restore archived node */\n restoreNode(id: string): Promise<Node>\n \n // === Edge Operations ===\n \n /** Create edge between nodes */\n createEdge(input: CreateEdgeInput): Promise<Edge>\n \n /** Get edge by ID */\n getEdge(id: string): Promise<Edge | null>\n \n /** Delete edge */\n deleteEdge(id: string): Promise<void>\n \n // === Tag Operations ===\n \n /** Add tags to node */\n addTags(nodeId: string, tags: string[]): Promise<void>\n \n /** Remove tags from node */\n removeTags(nodeId: string, tags: string[]): Promise<void>\n \n /** Set tags (replace all) */\n setTags(nodeId: string, tags: string[]): Promise<void>\n \n // === Query (delegated to QueryEngine) ===\n \n /** Access query engine */\n query: QueryEngine\n \n // === Transactions ===\n \n /** Run operations in a transaction */\n transaction<T>(fn: (tx: GraphTransaction) => Promise<T>): Promise<T>\n}\n```\n\n#### Input/Output Types\n\n```typescript\ninterface CreateNodeInput {\n type: 'spec' | 'issue' | 'feedback' | 'external'\n title: string\n content?: string\n \n // Common optional\n priority?: number\n tags?: string[]\n parent_id?: string\n \n // Issue-specific\n status?: string\n assignee?: string\n \n // Feedback-specific\n target_id?: string\n target_anchor?: Anchor\n feedback_type?: 'comment' | 'suggestion' | 'request'\n \n // External-specific\n uri?: string\n source?: string\n \n // Metadata\n metadata?: Record<string, unknown>\n}\n\ninterface UpdateNodeInput {\n title?: string\n content?: string\n priority?: number\n status?: string\n assignee?: string\n archived?: boolean\n // ... other updatable fields\n}\n\ninterface CreateEdgeInput {\n from_id: string\n to_id: string\n type: EdgeType\n metadata?: Record<string, unknown>\n}\n\ninterface DeleteOptions {\n /** Hard delete (default: false = archive) */\n hard?: boolean\n}\n```\n\n#### Sync Strategy\n\nThe GraphStore coordinates between JSONL and SQLite:\n\n```\nWrite Path:\n1. Validate input\n2. Generate ID (if new)\n3. Write to SQLite (immediate, for queries)\n4. Mark dirty for JSONL sync\n5. Debounced flush to JSONL\n\nRead Path:\n1. Query SQLite (fast)\n2. Return result\n\nFlush:\n1. Get dirty node IDs\n2. Read current JSONL\n3. Apply changes (upsert/delete)\n4. Atomic write new JSONL\n5. Clear dirty flags\n```\n\n### 2. QueryEngine (`src/graph/query.ts`)\n\nRich query capabilities for graph traversal and filtering.\n\n```typescript\ninterface QueryEngine {\n // === Basic Queries ===\n \n /** Query nodes with filters */\n nodes(filter: NodeFilter): Promise<Node[]>\n \n /** Query edges with filters */\n edges(filter: EdgeFilter): Promise<Edge[]>\n \n // === Relationship Queries ===\n \n /** Get edges from a node */\n edgesFrom(nodeId: string, type?: EdgeType): Promise<Edge[]>\n \n /** Get edges to a node */\n edgesTo(nodeId: string, type?: EdgeType): Promise<Edge[]>\n \n /** Get all edges for a node (both directions) */\n edgesFor(nodeId: string, type?: EdgeType): Promise<Edge[]>\n \n // === Dependency Queries ===\n \n /** What blocks this node? */\n blockers(nodeId: string, options?: BlockerOptions): Promise<Node[]>\n \n /** What does this node block? */\n blocking(nodeId: string, options?: BlockerOptions): Promise<Node[]>\n \n /** Is there a path from A blocking B? */\n isBlocking(fromId: string, toId: string): Promise<boolean>\n \n // === Spec/Issue Queries ===\n \n /** Issues that implement a spec */\n implementers(specId: string): Promise<Issue[]>\n \n /** Specs that an issue implements */\n specs(issueId: string): Promise<Spec[]>\n \n // === Hierarchy Queries ===\n \n /** Get children of a node */\n children(nodeId: string): Promise<Node[]>\n \n /** Get parent of a node */\n parent(nodeId: string): Promise<Node | null>\n \n /** Get all ancestors */\n ancestors(nodeId: string): Promise<Node[]>\n \n /** Get all descendants */\n descendants(nodeId: string): Promise<Node[]>\n \n // === Ready Query ===\n \n /** Get issues ready to work on (no active blockers) */\n ready(options?: ReadyOptions): Promise<Issue[]>\n \n // === Feedback Queries ===\n \n /** Get feedback on a node */\n feedback(targetId: string, options?: FeedbackOptions): Promise<Feedback[]>\n \n /** Get unresolved feedback */\n unresolvedFeedback(targetId?: string): Promise<Feedback[]>\n}\n```\n\n#### Filter Types\n\n```typescript\ninterface NodeFilter {\n /** Filter by type(s) */\n type?: NodeType | NodeType[]\n \n /** Filter by status(es) */\n status?: string | string[]\n \n /** Filter by tags (AND semantics) */\n tags?: string[]\n \n /** Filter by parent */\n parent_id?: string\n \n /** Filter by archived status */\n archived?: boolean | null // null = include all\n \n /** Text search in title/content */\n search?: string\n \n /** Filter by priority range */\n priority?: number | { min?: number; max?: number }\n \n /** Filter by assignee */\n assignee?: string\n \n /** Pagination */\n limit?: number\n offset?: number\n \n /** Sort */\n orderBy?: 'created_at' | 'updated_at' | 'priority' | 'title'\n orderDirection?: 'asc' | 'desc'\n}\n\ninterface EdgeFilter {\n /** Filter by type(s) */\n type?: EdgeType | EdgeType[]\n \n /** Filter by source node */\n from_id?: string\n \n /** Filter by target node */\n to_id?: string\n \n /** Pagination */\n limit?: number\n offset?: number\n}\n\ninterface BlockerOptions {\n /** Include transitive blockers (blockers of blockers) */\n transitive?: boolean\n \n /** Only include active (non-closed) blockers */\n activeOnly?: boolean\n \n /** Max depth for transitive queries */\n maxDepth?: number\n}\n\ninterface ReadyOptions {\n /** Filter by type (default: 'issue') */\n type?: 'issue' | 'spec'\n \n /** Filter by tags */\n tags?: string[]\n \n /** Filter by priority */\n priority?: number | { min?: number; max?: number }\n \n /** Filter by assignee */\n assignee?: string\n \n /** Limit results */\n limit?: number\n}\n\ninterface FeedbackOptions {\n /** Filter by feedback type */\n type?: 'comment' | 'suggestion' | 'request'\n \n /** Filter by resolution status */\n resolved?: boolean\n \n /** Include dismissed */\n includeDismissed?: boolean\n}\n```\n\n#### Ready Computation\n\n```typescript\n/**\n * Ready = open issues with no active (non-closed) blockers\n * \n * Algorithm:\n * 1. Get all open, non-archived issues\n * 2. For each, check incoming 'blocks' edges\n * 3. Filter out edges where blocker is closed or archived\n * 4. Issue is ready if no active blockers remain\n * \n * Optimization: Use SQLite view (already created in Phase 1)\n */\nasync function ready(options?: ReadyOptions): Promise<Issue[]> {\n // Primary: use ready_issues view\n let results = await sqlite.getReady()\n \n // Apply additional filters\n if (options?.tags) {\n results = results.filter(r => \n options.tags!.every(t => r.tags?.includes(t))\n )\n }\n // ... other filters\n \n return results.map(parseNode).filter(isIssue)\n}\n```\n\n### 3. Validation (`src/graph/validation.ts`)\n\nBusiness rule validation beyond basic schema validation.\n\n```typescript\ninterface ValidationService {\n /** Validate node creation */\n validateCreate(input: CreateNodeInput): ValidationResult\n \n /** Validate node update */\n validateUpdate(id: string, updates: UpdateNodeInput): Promise<ValidationResult>\n \n /** Validate edge creation */\n validateEdge(input: CreateEdgeInput): Promise<ValidationResult>\n \n /** Check for cycles in blocks graph */\n detectCycle(fromId: string, toId: string): Promise<CycleResult>\n}\n\ninterface ValidationResult {\n valid: boolean\n errors: ValidationError[]\n warnings: ValidationWarning[]\n}\n\ninterface ValidationError {\n code: string\n field?: string\n message: string\n}\n\ninterface ValidationWarning {\n code: string\n field?: string\n message: string\n}\n\ninterface CycleResult {\n hasCycle: boolean\n cycle?: string[] // Path if cycle detected\n}\n```\n\n#### Validation Rules\n\n```typescript\n// === Node Validation ===\n\nconst NODE_RULES = {\n // All nodes\n common: [\n { field: 'title', rule: 'required' },\n { field: 'title', rule: 'maxLength', value: 500 },\n { field: 'content', rule: 'maxLength', value: 100000 },\n { field: 'priority', rule: 'range', min: 0, max: 4 },\n ],\n \n // Issue-specific\n issue: [\n { field: 'status', rule: 'required' },\n { field: 'status', rule: 'enum', values: ['open', 'in_progress', 'blocked', 'closed'] },\n ],\n \n // Feedback-specific\n feedback: [\n { field: 'target_id', rule: 'required' },\n { field: 'target_id', rule: 'exists' }, // Target must exist\n { field: 'feedback_type', rule: 'required' },\n { field: 'feedback_type', rule: 'enum', values: ['comment', 'suggestion', 'request'] },\n ],\n \n // External-specific\n external: [\n { field: 'uri', rule: 'required' },\n { field: 'uri', rule: 'validUri' },\n { field: 'source', rule: 'required' },\n ],\n}\n\n// === Edge Validation ===\n\nconst EDGE_RULES = {\n common: [\n { rule: 'noSelfReference' }, // from_id !== to_id\n { rule: 'noDuplicate' }, // No duplicate edges\n ],\n \n blocks: [\n { rule: 'noCycle' }, // No circular dependencies\n { rule: 'validBlocker' }, // from must be issue/spec\n ],\n \n implements: [\n { rule: 'fromIsIssue' }, // from must be issue\n { rule: 'toIsSpec' }, // to must be spec\n ],\n}\n```\n\n#### Cycle Detection\n\n```typescript\n/**\n * Detect cycles in the blocks graph using DFS\n * \n * @param fromId - Source node (would block toId)\n * @param toId - Target node (would be blocked by fromId)\n * @returns Whether adding edge would create a cycle\n */\nasync function detectCycle(fromId: string, toId: string): Promise<CycleResult> {\n // If toId already blocks fromId (directly or transitively),\n // adding fromId → toId would create a cycle\n \n const visited = new Set<string>()\n const path: string[] = []\n \n async function dfs(current: string): Promise<boolean> {\n if (current === fromId) {\n path.push(current)\n return true // Found cycle\n }\n \n if (visited.has(current)) {\n return false\n }\n \n visited.add(current)\n path.push(current)\n \n // Get nodes that 'current' blocks\n const edges = await query.edgesFrom(current, 'blocks')\n \n for (const edge of edges) {\n if (await dfs(edge.to_id)) {\n return true\n }\n }\n \n path.pop()\n return false\n }\n \n const hasCycle = await dfs(toId)\n \n return {\n hasCycle,\n cycle: hasCycle ? [...path, fromId] : undefined,\n }\n}\n```\n\n## File Structure\n\n```\nsrc/graph/\n├── store.ts # GraphStore implementation\n├── query.ts # QueryEngine implementation\n├── validation.ts # Validation rules and cycle detection\n├── types.ts # Input/output types\n├── sync.ts # JSONL ↔ SQLite sync logic\n├── index.ts # Public exports\n└── __tests__/\n ├── store.test.ts\n ├── query.test.ts\n ├── validation.test.ts\n └── sync.test.ts\n```\n\n## Implementation Issues\n\n### Issue 1: Define Graph Types\n- Input/output types for all operations\n- Filter types\n- Validation types\n\n### Issue 2: Implement GraphStore\n- Lifecycle (init, close, flush)\n- Node CRUD operations\n- Edge operations\n- Tag operations\n- Coordinate JSONL/SQLite\n\n### Issue 3: Implement QueryEngine\n- Basic node/edge queries\n- Relationship queries (edgesFrom, edgesTo)\n- Dependency queries (blockers, blocking)\n- Ready computation\n- Hierarchy queries\n\n### Issue 4: Implement Validation\n- Node validation rules\n- Edge validation rules\n- Cycle detection algorithm\n\n### Issue 5: Implement Sync\n- Dirty tracking\n- Debounced flush\n- JSONL ↔ SQLite coordination\n\n### Issue 6: Add Tests\n- Unit tests for each component\n- Integration tests for full workflows\n- Edge cases (cycles, transactions, concurrent access)\n\n## Dependencies\n\n- **Phase 1 complete**: Schema types, ID generation, JSONL persister, SQLite persister\n- **From Phase 1**: `Storage` interface, `StoredNode`, `StoredEdge`, persisters\n\n## Acceptance Criteria\n\n- [ ] `GraphStore` provides unified CRUD over both persisters\n- [ ] `QueryEngine` supports all query types from INTERFACE.md\n- [ ] `ready()` correctly identifies unblocked issues\n- [ ] Cycle detection prevents circular `blocks` dependencies\n- [ ] Validation catches invalid inputs with helpful errors\n- [ ] Changes are durably persisted (flush on close)\n- [ ] 90%+ test coverage for graph layer\n\n## Open Questions\n\n1. **Flush trigger**: Should flush happen on every write, or debounced?\n - Recommendation: Debounced with max delay (like beads)\n\n2. **Transaction semantics**: What happens if transaction fails mid-way?\n - Recommendation: Rollback SQLite, don't write to JSONL\n\n3. **Concurrent access**: Multiple processes writing?\n - Recommendation: Phase 3 daemon handles this; for now, single-writer assumption\n\n4. **Soft delete cascading**: When archiving a node, archive its edges too?\n - Recommendation: Keep edges, filter in queries\n\n## References\n\n- [[s-7u8f|OpenTasks Implementation Plan]]\n- [[s-4l4m|Phase 1: Core Data Layer]]\n- [INTERFACE.md](./docs/INTERFACE.md) - Query types for the 3-tool API\n- [SCHEMA.md](./docs/SCHEMA.md) - Node and edge types\n","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-28 01:36:15","updated_at":"2026-01-28 01:36:15","parent_id":"s-7u8f","parent_uuid":"605bb90c-778e-4a77-95b8-f136577f4f67","relationships":[{"from":"s-zm8l","from_type":"spec","to":"s-4l4m","to_type":"spec","type":"depends-on"}],"tags":["graph","implementation","phase-2","query"]}
8
- {"id":"s-9r97","uuid":"ae3aa08b-dcb3-4645-93ef-4e5dd28cf797","title":"Phase 3: Daemon & IPC","file_path":"specs/s-9r97_phase_3_daemon_ipc.md","content":"# Phase 3: Daemon & IPC\n\nBackground daemon for coordination, IPC, file watching, and flush management.\n\n## Overview\n\nEach `.opentasks/` location has its own daemon process that:\n- Handles IPC requests via Unix socket\n- Coordinates writes with debounced flush\n- Watches for external file changes\n- Registers in global registry for discovery\n\n## Components\n\n### 3.1 Refactored SyncManager\n\n**Decision:** Refactor existing SyncManager to decouple from Storage, making it reusable by both GraphStore and daemon FlushManager.\n\n#### Current State (Phase 2)\n```typescript\n// Coupled to Storage\nfunction createSyncManager(\n config: SyncConfig,\n storage: Storage, // ← coupling\n onFlush: FlushCallback\n): SyncManager\n```\n\n#### Refactored Design\n```typescript\n// Core timing utility - no external dependencies\ninterface DebouncedFlusher {\n /** Mark that changes exist */\n markDirty(): void\n \n /** Schedule debounced flush */\n schedule(): void\n \n /** Force immediate flush */\n flush(): Promise<void>\n \n /** Cancel pending flush */\n cancel(): void\n \n /** Check if flush pending */\n hasPending(): boolean\n}\n\nfunction createDebouncedFlusher(\n config: { debounceMs: number; maxDelayMs: number },\n onFlush: () => Promise<void>\n): DebouncedFlusher\n```\n\nGraphStore and daemon FlushManager both compose this utility.\n\n### 3.2 Daemon Lifecycle\n\n#### Files\n- `src/daemon/lifecycle.ts` - Start, stop, lock management\n- `src/daemon/lock.ts` - Lock file operations\n- `src/daemon/registry.ts` - Global registry operations\n\n#### Lock File Format\n```typescript\n// .opentasks/daemon.lock\ninterface DaemonLock {\n pid: number\n parentPid: number\n version: string\n startedAt: string // ISO timestamp\n socketPath: string\n databasePath: string\n}\n```\n\n#### Startup Sequence\n```\n1. Check for existing daemon (lock file exists + process alive)\n2. If existing daemon running → return socket path (or error)\n3. Acquire exclusive flock on .opentasks/daemon.lock\n4. Write lock file contents (PID, metadata)\n5. Initialize GraphStore (loads from JSONL → SQLite)\n6. Start IPC server on .opentasks/daemon.sock\n7. Register in global registry (~/.opentasks/registry.json)\n8. Start file watchers\n9. Start flush manager\n10. Ready to accept connections\n```\n\n#### Shutdown Sequence\n```\n1. Stop accepting new connections\n2. Finish in-flight requests (2s timeout)\n3. Stop file watchers\n4. Final flush to JSONL\n5. Unregister from global registry\n6. Close IPC server\n7. Remove socket file\n8. Release lock file\n```\n\n#### Graceful Shutdown Triggers\n- IPC `shutdown` request\n- SIGTERM signal\n- SIGINT signal (Ctrl+C)\n\n### 3.3 Global Registry\n\n#### Location\n`~/.opentasks/registry.json`\n\n#### Auto-initialization\n- **Global location (`~/.opentasks/`)**: Auto-created when first daemon starts\n - Creates directory if missing\n - Creates `registry.json`\n - Does NOT initialize as full OpenTasks location (no graph.jsonl, cache.db)\n- **Project/workspace locations**: User-triggered only (explicit init command)\n\n#### Registry Format\n```typescript\ninterface DaemonRegistry {\n version: string\n daemons: DaemonEntry[]\n}\n\ninterface DaemonEntry {\n /** Absolute path to .opentasks/ directory */\n locationPath: string\n \n /** Socket path for IPC */\n socketPath: string\n \n /** Process ID */\n pid: number\n \n /** OpenTasks version */\n version: string\n \n /** When daemon started */\n startedAt: string\n \n /** Last activity timestamp */\n lastActivity: string\n}\n```\n\n#### Registry Operations\n```typescript\ninterface RegistryManager {\n /** Register daemon on startup */\n register(entry: DaemonEntry): Promise<void>\n \n /** Unregister daemon on shutdown */\n unregister(locationPath: string): Promise<void>\n \n /** Find daemon for a location */\n find(locationPath: string): Promise<DaemonEntry | null>\n \n /** List all registered daemons */\n list(): Promise<DaemonEntry[]>\n \n /** Remove stale entries (dead PIDs) */\n cleanup(): Promise<number>\n}\n```\n\n### 3.4 IPC Server\n\n#### Transport\n- Unix domain socket: `.opentasks/daemon.sock`\n- Line-based JSON-RPC 2.0\n\n#### Protocol\n```\nRequest: {\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"ping\",\"params\":{}}\\n\nResponse: {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"pong\":true}}\\n\nError: {\"jsonrpc\":\"2.0\",\"id\":1,\"error\":{\"code\":-32600,\"message\":\"...\"}}\\n\n```\n\n#### Phase 3a Methods (Lifecycle - implement first)\n| Method | Description |\n|--------|-------------|\n| `ping` | Health check, returns `{pong: true}` |\n| `health` | Detailed health status |\n| `status` | Daemon status and stats |\n| `shutdown` | Graceful shutdown |\n\n#### Phase 3b Methods (Graph operations - implement later)\n| Method | Description |\n|--------|-------------|\n| `graph.query` | Query nodes/edges |\n| `graph.get` | Get node by ID |\n| `graph.create` | Create node |\n| `graph.update` | Update node |\n| `graph.delete` | Delete node |\n| `graph.createEdge` | Create edge |\n| `graph.deleteEdge` | Delete edge |\n| `flush` | Force immediate flush |\n\n#### IPC Types\n```typescript\ninterface IPCServer {\n /** Start listening on socket */\n start(): Promise<void>\n \n /** Stop accepting connections */\n stop(): Promise<void>\n \n /** Register method handler */\n handle<P, R>(method: string, handler: (params: P) => Promise<R>): void\n}\n\ninterface IPCRequest {\n jsonrpc: '2.0'\n id: string | number\n method: string\n params?: unknown\n}\n\ninterface IPCResponse {\n jsonrpc: '2.0'\n id: string | number\n result?: unknown\n error?: {\n code: number\n message: string\n data?: unknown\n }\n}\n```\n\n### 3.5 File Watcher\n\n#### Watched Files\n1. `graph.jsonl` - Source of truth, detect external edits\n2. `specs/*.md` - Markdown files (if expansion enabled)\n3. `issues/*.md` - Markdown files (if expansion enabled)\n4. `config.json` - Configuration changes\n\n#### Behavior\n```typescript\ninterface FileWatcher {\n /** Start watching */\n start(): void\n \n /** Stop watching */\n stop(): void\n \n /** Pause during internal writes */\n pause(): void\n \n /** Resume after internal writes */\n resume(): void\n}\n```\n\n#### Change Detection Flow\n```\nExternal edit detected:\n1. Pause flush manager (prevent write conflicts)\n2. Reload affected data from file\n3. Merge with in-memory state (or replace if conflict)\n4. Mark as dirty for next flush cycle\n5. Resume flush manager\n```\n\n### 3.6 Daemon Flush Manager\n\nWraps `DebouncedFlusher` with daemon-specific coordination.\n\n```typescript\ninterface DaemonFlushManager {\n /** Mark node as dirty */\n markDirty(nodeId: string): void\n \n /** Schedule debounced flush */\n schedule(): void\n \n /** Force immediate flush */\n flush(): Promise<void>\n \n /** Pause flushing (for file watcher) */\n pause(): void\n \n /** Resume flushing */\n resume(): void\n \n /** Final flush on shutdown */\n finalFlush(): Promise<void>\n}\n```\n\n## File Structure\n\n```\nsrc/daemon/\n├── index.ts # Module exports\n├── lifecycle.ts # Daemon start/stop\n├── lock.ts # Lock file operations \n├── registry.ts # Global registry\n├── ipc.ts # IPC server\n├── methods/ # IPC method handlers\n│ ├── lifecycle.ts # ping, health, status, shutdown\n│ └── graph.ts # graph.* methods\n├── watcher.ts # File watcher\n├── flush.ts # Flush manager\n└── __tests__/\n ├── lifecycle.test.ts\n ├── lock.test.ts\n ├── registry.test.ts\n ├── ipc.test.ts\n └── watcher.test.ts\n```\n\n## Deferred Items\n\n### Parent Process Monitoring\n- Daemon should exit if parent process dies (prevents orphans)\n- **Deferred**: Note in code, implement in future iteration\n\n### Cross-Platform Support\n- Unix domain sockets are Linux/macOS only\n- Windows would need named pipes\n- **Deferred**: Note in code, implement when needed\n\n## Dependencies\n\n- Phase 2 complete (GraphStore, QueryEngine, ValidationService)\n- Refactored SyncManager → DebouncedFlusher\n\n## Implementation Order\n\n1. Refactor SyncManager → extract DebouncedFlusher\n2. Lock file operations (`lock.ts`)\n3. Global registry (`registry.ts`)\n4. Basic daemon lifecycle (`lifecycle.ts`)\n5. IPC server with lifecycle methods (`ipc.ts`, `methods/lifecycle.ts`)\n6. File watcher (`watcher.ts`)\n7. Daemon flush manager (`flush.ts`)\n8. IPC graph methods (`methods/graph.ts`)\n\n## References\n\n- [[s-7u8f|OpenTasks Implementation Plan]]\n- [[s-zm8l|Phase 2: Graph Operations]]\n- [ARCHITECTURE.md](./docs/ARCHITECTURE.md) - Daemon design\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-28 05:57:09","updated_at":"2026-01-28 05:57:09","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-9r97","from_type":"spec","to":"s-7u8f","to_type":"spec","type":"references"},{"from":"s-9r97","from_type":"spec","to":"s-zm8l","to_type":"spec","type":"depends-on"}],"tags":["architecture","daemon","ipc","phase-3"]}
9
- {"id":"s-4dv1","uuid":"6bba5bd4-f329-4d3a-8e74-21eeddab877e","title":"Phase 4: Agent Interface","file_path":"specs/s-4dv1_phase_4_agent_interface.md","content":"# Phase 4: Agent Interface\n\n## Overview\n\nPhase 4 implements the **3-tool MCP interface** for agents to interact with the OpenTasks graph. These tools provide the relationship layer over existing task systems - agents use native tools (TaskCreate, bd new, etc.) for CRUD, and OpenTasks provides graph operations.\n\n## Design Decisions\n\n| Decision | Choice | Rationale |\n|----------|--------|-----------|\n| Interface | 3 MCP tools (link, query, annotate) | Agents use native CRUD; OpenTasks adds relationships |\n| Node CRUD | Deferred to Phase 5 (Providers) | Native storage CRUD comes via provider interface |\n| Node validation | Validate existence before edge creation | Prevents dangling edges in graph |\n| Query results | Reduced by default, verbose flag available | Context economy for agents |\n| Annotate scope | All feedback operations | Complete feedback lifecycle |\n| Client design | Smart client with caching/batching | Expand if needed |\n| URI validation | Deferred to Phase 5 | Store URIs as strings, validate later |\n\n## The 3-Tool Interface\n\n### 1. `link` Tool\n\nCreate and remove edges between nodes.\n\n```typescript\ninterface LinkParams {\n from_id: string // Source node ID or provider URI\n to_id: string // Target node ID or provider URI\n type: EdgeType // blocks, implements, references, etc.\n remove?: boolean // true to remove edge (default: false)\n metadata?: Record<string, unknown>\n}\n\ninterface LinkResult {\n success: boolean\n edge_id?: string // ID of created edge (if not removing)\n error?: string\n}\n```\n\n**Behavior:**\n- Validates that both `from_id` and `to_id` reference existing nodes\n- For provider URIs (e.g., `beads://./bd-123`), validation deferred to Phase 5\n- Returns error if nodes don't exist (for local IDs)\n- Idempotent: creating existing edge succeeds, removing non-existent edge succeeds\n\n**Edge Types:**\n- `blocks` - Hard dependency (to_id blocks from_id)\n- `implements` - Issue implements spec\n- `references` - Soft reference\n- `depends-on` - General dependency\n- `discovered-from` - Work discovered during implementation\n- `related` - Related entities\n\n### 2. `query` Tool\n\nUnified query interface for graph traversal.\n\n```typescript\ninterface QueryParams {\n // Query type (exactly one required)\n nodes?: NodeFilter // Query nodes\n edges?: EdgeFilter // Query edges\n ready?: ReadyOptions // Get unblocked work\n blockers?: BlockerParams // Get what blocks a node\n blocking?: BlockerParams // Get what a node blocks\n feedback?: FeedbackParams // Get feedback on a node\n \n // Output control\n verbose?: boolean // Return full objects (default: false)\n limit?: number // Max results (default: 50)\n offset?: number // Pagination offset\n}\n\n// Reduced format (verbose=false)\ninterface NodeSummary {\n id: string\n type: NodeType\n title: string\n status?: string // For issues\n priority?: number\n archived: boolean\n}\n\n// Reduced format for edges\ninterface EdgeSummary {\n id: string\n from_id: string\n to_id: string\n type: EdgeType\n}\n\ninterface QueryResult {\n items: NodeSummary[] | EdgeSummary[] | Node[] | Edge[]\n total?: number // Total count (if available)\n has_more: boolean\n}\n```\n\n**Query Types:**\n\n| Type | Purpose | Returns |\n|------|---------|---------|\n| `nodes` | Filter nodes by type, status, tags, etc. | Node summaries/objects |\n| `edges` | Filter edges by type, from/to | Edge summaries/objects |\n| `ready` | Get unblocked issues ready to work on | Issue summaries/objects |\n| `blockers` | Get nodes blocking a specific node | Node summaries/objects |\n| `blocking` | Get nodes blocked by a specific node | Node summaries/objects |\n| `feedback` | Get feedback on a node | Feedback summaries/objects |\n\n### 3. `annotate` Tool\n\nComplete feedback lifecycle management.\n\n```typescript\ninterface AnnotateParams {\n // Target specification\n target_id: string // Node receiving annotation\n \n // Operation (exactly one required)\n create?: CreateFeedback // Create new feedback\n resolve?: string // Resolve feedback by ID\n dismiss?: string // Dismiss feedback by ID\n reopen?: string // Reopen resolved/dismissed feedback\n \n // Optional: from which issue\n from_id?: string // Issue providing feedback\n}\n\ninterface CreateFeedback {\n content: string // Feedback content (markdown)\n type?: FeedbackType // comment, suggestion, request (default: comment)\n anchor?: FeedbackAnchor // Line/text anchor in target\n}\n\ninterface FeedbackAnchor {\n line?: number // Line number in target content\n text?: string // Text snippet to anchor to\n}\n\ninterface AnnotateResult {\n success: boolean\n feedback_id?: string // ID of created/modified feedback\n error?: string\n}\n```\n\n**Operations:**\n- `create` - Create new feedback node linked to target\n- `resolve` - Mark feedback as resolved\n- `dismiss` - Dismiss feedback (won't fix)\n- `reopen` - Reopen previously resolved/dismissed feedback\n\n## Client Library\n\n### OpenTasksClient\n\nSmart client with connection management and convenience methods.\n\n```typescript\nclass OpenTasksClient {\n constructor(options?: ClientOptions)\n \n // Connection lifecycle\n connect(): Promise<void>\n disconnect(): void\n readonly connected: boolean\n \n // The 3 tools\n link(params: LinkParams): Promise<LinkResult>\n query(params: QueryParams): Promise<QueryResult>\n annotate(params: AnnotateParams): Promise<AnnotateResult>\n \n // Convenience methods (built on query)\n ready(options?: ReadyOptions): Promise<NodeSummary[]>\n blockers(nodeId: string): Promise<NodeSummary[]>\n blocking(nodeId: string): Promise<NodeSummary[]>\n feedback(nodeId: string): Promise<FeedbackSummary[]>\n}\n\ninterface ClientOptions {\n socketPath?: string // Custom socket path\n autoConnect?: boolean // Connect on instantiation (default: true)\n timeout?: number // Request timeout (default: 30000)\n}\n```\n\n**Features:**\n- Auto-connects to daemon on first request\n- Caches connection for reuse\n- Request timeout handling\n- Graceful disconnect\n\n## File Structure\n\n```\nsrc/\n├── tools/\n│ ├── index.ts # Tool exports\n│ ├── link.ts # link tool implementation\n│ ├── query.ts # query tool implementation\n│ ├── annotate.ts # annotate tool implementation\n│ └── types.ts # Tool parameter/result types\n├── client/\n│ ├── index.ts # Client exports\n│ └── client.ts # OpenTasksClient class\n└── daemon/\n └── methods/\n └── tools.ts # IPC handlers for 3 tools\n```\n\n## IPC Protocol\n\nTools register as IPC methods:\n\n| Method | Params | Returns |\n|--------|--------|---------|\n| `tools.link` | LinkParams | LinkResult |\n| `tools.query` | QueryParams | QueryResult |\n| `tools.annotate` | AnnotateParams | AnnotateResult |\n\n## Implementation Notes\n\n### Node Validation (link tool)\n\nFor local node IDs (s-xxx, i-xxx, f-xxx, e-xxx):\n- Validate existence via `store.getNode(id)`\n- Return error if node not found\n\nFor provider URIs (beads://, claude://, jira://):\n- Accept as-is (validation deferred to Phase 5)\n- Store URI string directly in edge\n\n### Reduced vs Verbose Output (query tool)\n\n**Reduced (default):** Minimal fields for context economy\n- Nodes: id, type, title, status, priority, archived\n- Edges: id, from_id, to_id, type\n\n**Verbose:** Full objects from storage\n- Nodes: All fields including content, timestamps, tags\n- Edges: All fields including metadata, created_at\n\n### Feedback Anchoring (annotate tool)\n\nWhen creating feedback with anchor:\n- `line` - Direct line number reference\n- `text` - Search for text in target content, extract line\n\nAnchor stored in feedback node's `target_anchor` field.\n\n## Dependencies\n\n- [[s-9r97|Phase 3: Daemon & IPC]] - IPC server, flush manager\n- [[s-zm8l|Phase 2: Graph Operations]] - GraphStore, QueryEngine\n\n## Acceptance Criteria\n\n- [ ] `link` tool creates/removes edges with validation\n- [ ] `query` tool supports all query types with reduced/verbose output\n- [ ] `annotate` tool manages full feedback lifecycle\n- [ ] `OpenTasksClient` connects to daemon and exposes tools\n- [ ] IPC handlers registered for all 3 tools\n- [ ] Tests for each tool and client\n- [ ] Error handling for missing nodes, invalid params\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-28 07:04:16","updated_at":"2026-01-28 07:04:16","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-4dv1","from_type":"spec","to":"s-7u8f","to_type":"spec","type":"implements"}],"tags":["agent-interface","mcp","opentasks","phase-4"]}
10
- {"id":"s-9x15","uuid":"5088681a-e9a6-45d3-ba25-f8155c9c6873","title":"Phase 5: Providers","file_path":"specs/s-9x15_phase_5_providers.md","content":"# Phase 5: Providers\n\n**Parent**: [[s-7u8f]] OpenTasks Implementation Plan\n\n## Overview\n\nPhase 5 adds the provider layer that enables OpenTasks to connect heterogeneous task systems. Providers own node content while GraphStore owns relationships (edges). This maintains OpenTasks' identity as a \"graph connector\" rather than a replacement for existing tools.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│ GraphStore │\n│ ┌─────────────────────────────────────────────────────────┐ │\n│ │ Edge Operations (Source of Truth) │ │\n│ │ createEdge(), deleteEdge(), getEdge() │ │\n│ │ query.edges(), query.blockers(), query.ready() │ │\n│ └─────────────────────────────────────────────────────────┘ │\n│ │ │\n│ routes node ops via │\n│ ▼ │\n│ ┌─────────────────────────────────────────────────────────┐ │\n│ │ Provider Registry │ │\n│ │ resolveNode(idOrUri) → routes to appropriate provider │ │\n│ │ │ │\n│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │\n│ │ │ Native │ │ Beads │ │ Claude Tasks │ │ │\n│ │ │ Provider │ │ Provider │ │ Provider │ │ │\n│ │ │ (wraps │ │ (bd CLI) │ │ (TaskCreate/ │ │ │\n│ │ │ existing)│ │ │ │ TaskUpdate) │ │ │\n│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │\n│ └─────────────────────────────────────────────────────────┘ │\n└─────────────────────────────────────────────────────────────┘\n```\n\n### Key Principles\n\n1. **GraphStore owns edges** - All OpenTasks relationships stored in SQLite/JSONL\n2. **Providers own node content** - Each provider is source of truth for its nodes\n3. **NativeProvider wraps existing** - No breaking changes to Phase 1-4 code\n4. **Full CRUD on providers** - Providers support create, read, update, delete\n5. **URI parsing per provider** - Each provider implements its own scheme parsing\n\n### Data Flow\n\n**Local node operations** (e.g., `s-abc1`, `i-xyz2`):\n```\nstore.getNode('s-abc1') \n → registry.resolve('s-abc1')\n → NativeProvider.get('s-abc1')\n → existing SQLite/JSONL storage\n```\n\n**External node resolution** (e.g., `beads://./bd-123`):\n```\nstore.resolveNode('beads://./bd-123')\n → registry.resolve('beads://./bd-123')\n → BeadsProvider.get('bd-123')\n → bd show bd-123 (CLI)\n → creates/updates ExternalNode in GraphStore\n```\n\n## Provider Interface\n\n### Core Interface\n\n```typescript\ninterface Provider {\n /** Provider identifier */\n readonly name: string\n \n /** URI schemes this provider handles (e.g., ['beads', 'bd']) */\n readonly schemes: string[]\n \n /** What operations this provider supports */\n readonly capabilities: ProviderCapabilities\n \n // URI Parsing (each provider implements its own)\n parseUri(uri: string): ParsedUri | null\n buildUri(id: string, options?: UriOptions): string\n isValidUri(uri: string): boolean\n \n // CRUD Operations\n get(id: string): Promise<ProviderNode | null>\n list(filter?: ProviderFilter): Promise<ProviderNode[]>\n create(input: ProviderCreateInput): Promise<ProviderNode>\n update(id: string, updates: ProviderUpdateInput): Promise<ProviderNode>\n delete(id: string): Promise<void>\n \n // Optional: Search\n search?(query: string, options?: SearchOptions): Promise<ProviderNode[]>\n \n // Optional: Watch for changes (for background sync)\n watch?(callback: WatchCallback): Unsubscribe\n}\n\ninterface ProviderCapabilities {\n read: boolean\n write: boolean\n search: boolean\n watch: boolean\n}\n\ninterface ParsedUri {\n scheme: string // 'beads', 'claude', 'native'\n workspace?: string // '.', 'current', absolute path\n id: string // 'bd-123', 't-abc'\n isRelative: boolean\n}\n```\n\n### Provider Node\n\n```typescript\ninterface ProviderNode {\n /** ID in provider's format */\n id: string\n \n /** Canonical URI */\n uri: string\n \n /** Node type equivalent */\n type: 'spec' | 'issue' | 'task' | 'external'\n \n /** Display title */\n title: string\n \n /** Content/description */\n content?: string\n \n /** Provider-specific status */\n status?: string\n \n /** Priority (normalized 0-4) */\n priority?: number\n \n /** Raw data from provider */\n rawData?: Record<string, unknown>\n \n /** When this data was fetched */\n fetchedAt: string\n}\n```\n\n## Provider Registry\n\n```typescript\ninterface ProviderRegistry {\n /** Register a provider */\n register(provider: Provider): void\n \n /** Unregister a provider */\n unregister(name: string): void\n \n /** Get provider by name */\n get(name: string): Provider | undefined\n \n /** Find provider for a URI or ID */\n resolveProvider(idOrUri: string): Provider | null\n \n /** List all registered providers */\n list(): Provider[]\n \n /** Check if a URI/ID can be resolved */\n canResolve(idOrUri: string): boolean\n}\n```\n\n## Materialization Strategy\n\nExternal nodes can be materialized (cached in GraphStore) with configurable strategy:\n\n```typescript\ninterface MaterializationConfig {\n /** Default strategy */\n default: 'on-demand' | 'lazy' | 'eager'\n \n /** Per-provider overrides */\n providers?: Record<string, MaterializationStrategy>\n \n /** Background sync interval (ms), 0 to disable */\n backgroundSyncInterval?: number\n \n /** How long before cached data is considered stale (ms) */\n staleAfter?: number\n}\n\ntype MaterializationStrategy = \n | 'on-demand' // Only when explicitly requested\n | 'lazy' // On first access\n | 'eager' // When edge is created\n | 'none' // Never materialize, always fetch\n```\n\n### Materialization Flow\n\n```typescript\n// On-demand (default)\nawait store.resolveNode('beads://./bd-123', { materialize: true })\n\n// Lazy (on access)\nconst edge = await store.getEdge('x-abc1') // edge.to_id = 'beads://./bd-123'\nconst node = await store.resolveNode(edge.to_id) // auto-materializes\n\n// Background sync\nstore.startBackgroundSync({ interval: 60000 }) // refresh every minute\n```\n\n## Implementations\n\n### NativeProvider\n\nWraps existing GraphStore node operations. No separate storage needed.\n\n```typescript\nclass NativeProvider implements Provider {\n readonly name = 'native'\n readonly schemes = ['native', 'opentasks']\n readonly capabilities = { read: true, write: true, search: true, watch: false }\n \n constructor(private store: GraphStore) {}\n \n parseUri(uri: string): ParsedUri | null {\n // native://spec/s-abc1 or just s-abc1\n const match = uri.match(/^(?:native:\\/\\/)?(\\w+\\/)?([sifex]-[a-z0-9]+)$/)\n if (!match) return null\n return { scheme: 'native', id: match[2], isRelative: false }\n }\n \n buildUri(id: string): string {\n return `native://${id}`\n }\n \n async get(id: string): Promise<ProviderNode | null> {\n const node = await this.store.getNode(id)\n if (!node) return null\n return this.toProviderNode(node)\n }\n \n async create(input: ProviderCreateInput): Promise<ProviderNode> {\n const node = await this.store.createNode(input)\n return this.toProviderNode(node)\n }\n \n // ... delegates to existing GraphStore methods\n}\n```\n\n### BeadsProvider\n\nIntegrates with Beads via CLI (`bd` command).\n\n```typescript\nclass BeadsProvider implements Provider {\n readonly name = 'beads'\n readonly schemes = ['beads', 'bd']\n readonly capabilities = { read: true, write: true, search: true, watch: false }\n \n constructor(private config: BeadsConfig) {}\n \n parseUri(uri: string): ParsedUri | null {\n // beads://./bd-123 or beads://workspace/bd-123\n const match = uri.match(/^beads:\\/\\/([^/]+)\\/(.+)$/)\n if (!match) return null\n return {\n scheme: 'beads',\n workspace: match[1],\n id: match[2],\n isRelative: match[1] === '.'\n }\n }\n \n async get(id: string): Promise<ProviderNode | null> {\n const result = await this.exec(['show', id, '--json'])\n if (!result) return null\n return this.parseBeadsIssue(result)\n }\n \n async create(input: ProviderCreateInput): Promise<ProviderNode> {\n const args = ['create', input.title]\n if (input.content) args.push('--description', input.content)\n const result = await this.exec(args)\n return this.parseBeadsIssue(result)\n }\n \n private async exec(args: string[]): Promise<any> {\n // Execute bd CLI command\n }\n}\n```\n\n### ClaudeTasksProvider\n\nIntegrates with Claude Code's native task system.\n\n```typescript\nclass ClaudeTasksProvider implements Provider {\n readonly name = 'claude'\n readonly schemes = ['claude', 'task']\n readonly capabilities = { read: true, write: true, search: false, watch: false }\n \n parseUri(uri: string): ParsedUri | null {\n // claude://current/t-abc or claude://session-id/t-abc\n const match = uri.match(/^claude:\\/\\/([^/]+)\\/(.+)$/)\n if (!match) return null\n return {\n scheme: 'claude',\n workspace: match[1], // 'current' or session ID\n id: match[2],\n isRelative: match[1] === 'current'\n }\n }\n \n // Implementation uses TaskCreate/TaskUpdate/TaskList internally\n // This bridges Claude's ephemeral tasks with OpenTasks persistence\n}\n```\n\n## GraphStore Integration\n\n### New Methods\n\n```typescript\ninterface GraphStore {\n // Existing methods unchanged...\n \n /** Provider registry */\n readonly providers: ProviderRegistry\n \n /** Resolve any node by ID or URI */\n resolveNode(\n idOrUri: string, \n options?: ResolveOptions\n ): Promise<Node | ExternalNode | null>\n \n /** Materialize an external node */\n materializeNode(uri: string): Promise<ExternalNode>\n \n /** Refresh a materialized node */\n refreshNode(id: string): Promise<ExternalNode>\n \n /** Start background sync for external nodes */\n startBackgroundSync(config?: BackgroundSyncConfig): void\n \n /** Stop background sync */\n stopBackgroundSync(): void\n}\n\ninterface ResolveOptions {\n /** Force fresh fetch (ignore cache) */\n refresh?: boolean\n \n /** Materialize if external */\n materialize?: boolean\n \n /** Include raw provider data */\n includeRawData?: boolean\n}\n```\n\n### Resolution Logic\n\n```typescript\nasync function resolveNode(idOrUri: string, options?: ResolveOptions) {\n // 1. Check if it's a local ID\n if (isLocalId(idOrUri)) {\n return this.getNode(idOrUri)\n }\n \n // 2. Check if already materialized\n const existing = await this.findExternalNode(idOrUri)\n if (existing && !options?.refresh && !isStale(existing)) {\n return existing\n }\n \n // 3. Find provider and fetch\n const provider = this.providers.resolveProvider(idOrUri)\n if (!provider) {\n throw new Error(`No provider for: ${idOrUri}`)\n }\n \n const parsed = provider.parseUri(idOrUri)\n const providerNode = await provider.get(parsed.id)\n \n // 4. Materialize if requested\n if (options?.materialize && providerNode) {\n return this.materializeNode(idOrUri, providerNode)\n }\n \n return providerNode\n}\n```\n\n## File Structure\n\n```\nsrc/providers/\n├── index.ts # Module exports\n├── types.ts # Provider interfaces and types\n├── registry.ts # ProviderRegistry implementation\n├── materialization.ts # Materialization logic\n├── native.ts # NativeProvider\n├── beads.ts # BeadsProvider \n├── claude-tasks.ts # ClaudeTasksProvider\n└── __tests__/\n ├── registry.test.ts\n ├── native.test.ts\n ├── beads.test.ts\n └── claude-tasks.test.ts\n```\n\n## Deliverables\n\n### Provider Types (`src/providers/types.ts`)\n- [ ] `Provider` interface with full CRUD\n- [ ] `ProviderCapabilities` type\n- [ ] `ProviderNode` type\n- [ ] `ParsedUri` type\n- [ ] `MaterializationConfig` type\n- [ ] `ProviderCreateInput`, `ProviderUpdateInput`, `ProviderFilter` types\n\n### Provider Registry (`src/providers/registry.ts`)\n- [ ] `ProviderRegistry` implementation\n- [ ] URI/ID routing logic\n- [ ] Provider registration/lookup\n\n### Native Provider (`src/providers/native.ts`)\n- [ ] Wraps existing GraphStore node operations\n- [ ] URI parsing for `native://` and local IDs\n- [ ] Full CRUD delegation\n\n### Beads Provider (`src/providers/beads.ts`)\n- [ ] CLI integration (`bd` command execution)\n- [ ] URI parsing for `beads://` scheme\n- [ ] Issue CRUD via CLI\n- [ ] Response parsing\n\n### Claude Tasks Provider (`src/providers/claude-tasks.ts`)\n- [ ] Integration with Claude's task system\n- [ ] URI parsing for `claude://` scheme\n- [ ] Task CRUD mapping\n\n### Materialization (`src/providers/materialization.ts`)\n- [ ] On-demand materialization\n- [ ] Lazy materialization on access\n- [ ] Background sync with configurable interval\n- [ ] Stale detection and refresh\n\n### GraphStore Integration\n- [ ] Add `providers` registry to GraphStore\n- [ ] Add `resolveNode()` method\n- [ ] Add `materializeNode()` method\n- [ ] Add background sync methods\n\n### Tests\n- [ ] Provider interface compliance tests\n- [ ] Registry routing tests\n- [ ] NativeProvider delegation tests\n- [ ] BeadsProvider CLI integration tests\n- [ ] ClaudeTasksProvider tests\n- [ ] Materialization strategy tests\n- [ ] Background sync tests\n\n## Dependencies\n\n- Phase 1-4 complete (GraphStore, edges, tools)\n- `bd` CLI available for BeadsProvider tests\n- Claude Code environment for ClaudeTasksProvider\n\n## Success Criteria\n\n1. Can register multiple providers with GraphStore\n2. `resolveNode()` correctly routes to appropriate provider\n3. NativeProvider passes all existing node operation tests\n4. BeadsProvider can CRUD beads issues via CLI\n5. ClaudeTasksProvider can bridge Claude's task system\n6. Materialization works with all three strategies\n7. Background sync refreshes stale external nodes\n8. All providers implement URI parsing correctly\n\n## Open Questions (Resolved)\n\n| Question | Decision |\n|----------|----------|\n| Provider CRUD scope | Full CRUD |\n| GraphStore vs Provider | GraphStore routes to providers, owns edges |\n| Materialization default | On-demand, with lazy and background sync available |\n| Registry location | Per-GraphStore |\n| Integration method | Per-provider (CLI for Beads) |\n| Which providers | Native + Beads + Claude Tasks |\n| URI parsing | Part of provider interface |\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-28 08:02:10","updated_at":"2026-01-28 08:02:10","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-9x15","from_type":"spec","to":"s-7u8f","to_type":"spec","type":"implements"}],"tags":["implementation","phase-5","providers"]}
11
- {"id":"s-1pai","uuid":"740c1fb7-69e1-404f-b671-dfc1fd8f331c","title":"Provider Interface Enhancement - Dependency & Relationship Support","file_path":"specs/s-1pai_provider_interface_enhancement_dependency_relation.md","content":"# Provider Interface Enhancement - Typed Extensions & Relationship Queries\n\n**Parent**: [[s-9x15]] Phase 5: Providers\n**Related**: [[s-4lv6]] Federated Graph Architecture\n\n## Overview\n\nThis spec defines how providers expose their full capabilities through typed extensions rather than a bloated base interface. Each provider defines its own node type and optional traits for features like relationship queries.\n\n## Design Principles\n\nBased on architectural discussions, we've established these principles:\n\n1. **Providers own their data** - OpenTasks queries providers for their internal relationships, not sync them\n2. **Materialization preserves everything** - External nodes store full provider data in `rawData`\n3. **Typed extensions over optional fields** - Each provider defines its own typed interface\n4. **Capabilities via traits** - Providers implement optional traits for features they support\n5. **Query, don't sync** - Use `RelationshipQueryable` to ask providers about their edges\n\n## What This Spec Does NOT Cover\n\n- Federated traversal API (see [[s-4lv6]])\n- Graphology integration (see [[s-4lv6]])\n- Schema changes for cached edges (see [[s-4lv6]])\n\n---\n\n## Base Interface (Unchanged)\n\nThe base `Provider` interface from [[s-9x15]] remains minimal:\n\n```typescript\ninterface Provider {\n readonly name: string\n readonly schemes: string[]\n readonly capabilities: ProviderCapabilities\n \n // URI operations\n parseUri(uri: string): ParsedUri | null\n buildUri(id: string, options?: UriOptions): string\n isValidUri(uri: string): boolean\n \n // CRUD\n get(id: string): Promise<ProviderNode | null>\n list(filter?: ProviderFilter): Promise<ProviderNode[]>\n create(input: ProviderCreateInput): Promise<ProviderNode>\n update(id: string, updates: ProviderUpdateInput): Promise<ProviderNode>\n delete(id: string): Promise<void>\n \n // Optional\n search?(query: string, options?: SearchOptions): Promise<ProviderNode[]>\n watch?(callback: WatchCallback): Unsubscribe\n}\n```\n\n---\n\n## Provider-Specific Extensions\n\nInstead of adding optional fields to base `ProviderNode`, each provider defines typed extensions.\n\n### BeadsProvider\n\n```typescript\n// Beads-specific node type with full type safety\ninterface BeadsNode extends ProviderNode {\n // Override with more specific return type\n type: 'spec' | 'issue' // Mapped from Beads\n \n // Beads-specific fields (not optional - Beads always has these)\n issueType: 'bug' | 'feature' | 'task' | 'epic' | 'chore'\n labels: string[]\n \n // Relationship data (from bd show --json)\n blockedBy: string[] // Local Beads IDs\n blocks: string[] // Local Beads IDs\n parent?: string // Parent issue ID\n children?: string[] // Child issue IDs\n \n // Beads-specific metadata\n assignee?: string\n closedAt?: string\n}\n\n// Beads-specific create input\ninterface BeadsCreateInput extends ProviderCreateInput {\n issueType?: 'bug' | 'feature' | 'task' | 'epic' | 'chore'\n labels?: string[]\n blockedBy?: string[] // Initial blockers\n parent?: string // Parent issue\n}\n\n// Beads-specific filter\ninterface BeadsFilter extends ProviderFilter {\n issueType?: string\n labels?: string[]\n labelAny?: string[] // OR semantics\n assignee?: string\n}\n\n// Full BeadsProvider interface\ninterface BeadsProvider extends Provider, RelationshipQueryable {\n readonly name: 'beads'\n \n // Typed overrides\n get(id: string): Promise<BeadsNode | null>\n list(filter?: BeadsFilter): Promise<BeadsNode[]>\n create(input: BeadsCreateInput): Promise<BeadsNode>\n \n // Beads-specific methods\n getReady(options?: BeadsReadyOptions): Promise<BeadsNode[]>\n \n // Relationship modifications\n addDependency(from: string, to: string, type: BeadsDependencyType): Promise<void>\n removeDependency(from: string, to: string): Promise<void>\n \n // Label management\n addLabel(id: string, label: string): Promise<void>\n removeLabel(id: string, label: string): Promise<void>\n}\n\ntype BeadsDependencyType = 'blocks' | 'related' | 'parent-child' | 'discovered-from'\n\ninterface BeadsReadyOptions {\n priority?: number\n issueType?: string\n labels?: string[]\n limit?: number\n}\n```\n\n### JiraProvider (Future Example)\n\n```typescript\ninterface JiraNode extends ProviderNode {\n issueType: 'story' | 'bug' | 'task' | 'epic' | 'subtask'\n labels: string[]\n components: string[]\n sprint?: string\n epicLink?: string\n \n // Jira link types\n blocks: string[]\n isBlockedBy: string[]\n relatesTo: string[]\n}\n\ninterface JiraProvider extends Provider, RelationshipQueryable {\n readonly name: 'jira'\n \n get(id: string): Promise<JiraNode | null>\n // ... Jira-specific methods\n}\n```\n\n---\n\n## RelationshipQueryable Trait\n\nProviders that track relationships implement this trait:\n\n```typescript\n/**\n * Trait for providers that can answer relationship queries.\n * Used by FederatedGraph to traverse across provider boundaries.\n */\ninterface RelationshipQueryable {\n /**\n * Query edges for a node.\n * Returns edges in provider's local ID format.\n */\n queryEdges(\n nodeId: string,\n options?: EdgeQueryOptions\n ): Promise<ProviderEdge[]>\n \n /**\n * What edge types does this provider support?\n */\n supportedEdgeTypes(): EdgeTypeSupport[]\n}\n\ninterface EdgeQueryOptions {\n type?: string | string[] // Filter by edge type\n direction?: 'in' | 'out' | 'both'\n}\n\ninterface ProviderEdge {\n from: string // Local ID (e.g., 'bd-123')\n to: string // Local ID (e.g., 'bd-456')\n type: string // Edge type ('blocks', 'parent-child', etc.)\n metadata?: Record<string, unknown>\n}\n\ninterface EdgeTypeSupport {\n type: string // 'blocks', 'parent-child', etc.\n canQuery: boolean // Can read existing edges\n canCreate: boolean // Can create new edges\n canDelete: boolean // Can delete edges\n affectsReady: boolean // Does this edge type affect ready status?\n}\n```\n\n### Implementation in BeadsProvider\n\n```typescript\nclass BeadsProviderImpl implements BeadsProvider, RelationshipQueryable {\n \n async queryEdges(nodeId: string, options?: EdgeQueryOptions): Promise<ProviderEdge[]> {\n // Fetch node with relationships\n const node = await this.get(nodeId)\n if (!node) return []\n \n const edges: ProviderEdge[] = []\n const direction = options?.direction ?? 'both'\n const typeFilter = options?.type \n ? (Array.isArray(options.type) ? options.type : [options.type])\n : null\n \n // Outgoing edges (this node blocks others)\n if (direction === 'out' || direction === 'both') {\n if (!typeFilter || typeFilter.includes('blocks')) {\n for (const targetId of node.blocks) {\n edges.push({ from: nodeId, to: targetId, type: 'blocks' })\n }\n }\n if (!typeFilter || typeFilter.includes('parent-child')) {\n for (const childId of node.children ?? []) {\n edges.push({ from: nodeId, to: childId, type: 'parent-child' })\n }\n }\n }\n \n // Incoming edges (others block this node)\n if (direction === 'in' || direction === 'both') {\n if (!typeFilter || typeFilter.includes('blocks')) {\n for (const sourceId of node.blockedBy) {\n edges.push({ from: sourceId, to: nodeId, type: 'blocks' })\n }\n }\n if (!typeFilter || typeFilter.includes('parent-child')) {\n if (node.parent) {\n edges.push({ from: node.parent, to: nodeId, type: 'parent-child' })\n }\n }\n }\n \n return edges\n }\n \n supportedEdgeTypes(): EdgeTypeSupport[] {\n return [\n { type: 'blocks', canQuery: true, canCreate: true, canDelete: true, affectsReady: true },\n { type: 'parent-child', canQuery: true, canCreate: true, canDelete: true, affectsReady: false },\n { type: 'discovered-from', canQuery: true, canCreate: true, canDelete: true, affectsReady: false },\n { type: 'related', canQuery: true, canCreate: true, canDelete: true, affectsReady: false },\n ]\n }\n}\n```\n\n---\n\n## Capability Discovery\n\nUpdate `ProviderCapabilities` to expose what each provider supports:\n\n```typescript\ninterface ProviderCapabilities {\n // Existing\n read: boolean\n write: boolean\n search: boolean\n watch: boolean\n \n // NEW: Relationship capabilities\n relationships: boolean // Implements RelationshipQueryable\n \n // NEW: Feature flags (provider-specific features)\n features: {\n labels?: boolean // Supports labels/tags\n hierarchy?: boolean // Supports parent-child\n nativeTypes?: boolean // Has multiple issue types\n ready?: boolean // Has native ready query\n }\n}\n```\n\n### Type Guards for Runtime Checks\n\n```typescript\nfunction isRelationshipQueryable(provider: Provider): provider is Provider & RelationshipQueryable {\n return provider.capabilities.relationships === true\n}\n\nfunction isBeadsProvider(provider: Provider): provider is BeadsProvider {\n return provider.name === 'beads'\n}\n\n// Usage\nconst provider = registry.resolveProvider(uri)\nif (isRelationshipQueryable(provider)) {\n const edges = await provider.queryEdges(id, { type: 'blocks' })\n}\n\nif (isBeadsProvider(provider)) {\n const ready = await provider.getReady({ priority: 1 })\n}\n```\n\n---\n\n## Data Flow: Materialization\n\nWhen an external node is materialized, all provider data is preserved:\n\n```typescript\n// Materializing beads://./bd-123\nconst beadsNode: BeadsNode = await beadsProvider.get('bd-123')\n\n// Stored as ExternalNode\nconst externalNode: ExternalNode = {\n id: 'x-abc1',\n type: 'external',\n uri: 'beads://./bd-123',\n source: 'beads',\n materialized: true,\n cached_at: new Date().toISOString(),\n \n // Mapped fields for display\n title: beadsNode.title,\n content: beadsNode.content,\n priority: beadsNode.priority,\n \n // FULL provider data preserved\n external_data: {\n issueType: 'bug',\n labels: ['urgent', 'backend'],\n blockedBy: ['bd-456'],\n blocks: [],\n assignee: 'alice',\n // ... everything from BeadsNode\n }\n}\n```\n\nTo access provider-specific data:\n\n```typescript\n// Option 1: Access rawData directly (untyped)\nconst issueType = externalNode.external_data?.issueType\n\n// Option 2: Re-fetch from provider (typed)\nconst beadsNode = await beadsProvider.get('bd-123')\nconsole.log(beadsNode.issueType) // Type-safe: 'bug' | 'feature' | ...\n\n// Option 3: Cast rawData (if you know the type)\nconst beadsData = externalNode.external_data as BeadsNode\nconsole.log(beadsData.labels) // ['urgent', 'backend']\n```\n\n---\n\n## Relationship with [[s-4lv6]]\n\n| Concern | Handled By |\n|---------|------------|\n| `RelationshipQueryable` trait definition | **This spec (s-1pai)** |\n| Provider-specific types (`BeadsNode`, etc.) | **This spec (s-1pai)** |\n| How `FederatedGraph` uses `RelationshipQueryable` | [[s-4lv6]] |\n| Graphology integration | [[s-4lv6]] |\n| Cached edge storage/schema | [[s-4lv6]] |\n| Cross-provider traversal | [[s-4lv6]] |\n\n---\n\n## Implementation Phases\n\n### Phase 1: Provider Type Extensions\n- [ ] Define `BeadsNode` interface\n- [ ] Define `BeadsCreateInput`, `BeadsFilter`\n- [ ] Update `BeadsProvider` interface\n- [ ] Add type guards (`isBeadsProvider`, etc.)\n\n### Phase 2: RelationshipQueryable Trait\n- [ ] Define `RelationshipQueryable` interface\n- [ ] Define `ProviderEdge`, `EdgeTypeSupport`\n- [ ] Implement in `BeadsProvider`\n- [ ] Implement in `NativeProvider`\n\n### Phase 3: Capability Updates\n- [ ] Add `relationships` to `ProviderCapabilities`\n- [ ] Add `features` object to `ProviderCapabilities`\n- [ ] Update provider implementations\n\n### Phase 4: Beads CLI Integration\n- [ ] Implement `queryEdges()` using `bd show --json`\n- [ ] Implement `addDependency()` using `bd dep add`\n- [ ] Implement `removeDependency()` using `bd dep remove`\n- [ ] Implement `getReady()` using `bd ready --json`\n\n---\n\n## Success Criteria\n\n1. ✅ `BeadsProvider` returns typed `BeadsNode` with `issueType`, `labels`, `blockedBy`, etc.\n2. ✅ `BeadsProvider` implements `RelationshipQueryable`\n3. ✅ Can query Beads relationships without syncing them to OpenTasks\n4. ✅ Materialized nodes preserve all provider data in `external_data`\n5. ✅ Type guards work correctly for runtime capability checks\n6. ✅ `FederatedGraph` (from [[s-4lv6]]) can use `queryEdges()` for traversal\n\n---\n\n## Beads CLI Commands Reference\n\n| Operation | Command | Used By |\n|-----------|---------|---------|\n| Get node with relationships | `bd show <id> --json` | `get()`, `queryEdges()` |\n| Create with dependencies | `bd create \"Title\" --deps blocks:<id> --json` | `create()` |\n| Add dependency | `bd dep add <from> <to> --type <type>` | `addDependency()` |\n| Remove dependency | `bd dep remove <from> <to>` | `removeDependency()` |\n| Get ready issues | `bd ready --json` | `getReady()` |\n| Add label | `bd label add <id> <label> --json` | `addLabel()` |\n| Remove label | `bd label remove <id> <label> --json` | `removeLabel()` |\n\n---\n\n## Original Gaps Analysis (Revised)\n\n| Original Gap | Resolution |\n|--------------|------------|\n| No dependency support in ProviderNode | Providers expose via typed extensions (`BeadsNode.blockedBy`) |\n| Type system mismatch | Providers define their own types (`BeadsNode.issueType`) |\n| No tags support | Providers expose via typed extensions (`BeadsNode.labels`) |\n| Create/Update can't express relationships | Provider-specific inputs (`BeadsCreateInput.blockedBy`) |\n| No ready query | Provider-specific method (`BeadsProvider.getReady()`) |\n\nThe key insight: **These aren't gaps in the base interface - they're provider-specific features that should be expressed through typed extensions, not optional fields on a bloated base type.**\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-29 00:20:07","updated_at":"2026-01-29 06:00:19","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-1pai","from_type":"spec","to":"s-4lv6","to_type":"spec","type":"related"},{"from":"s-1pai","from_type":"spec","to":"s-9x15","to_type":"spec","type":"depends-on"}],"tags":["dependencies","enhancement","integration","providers"]}
12
- {"id":"s-4lv6","uuid":"6c0db039-4d7a-4bea-9e6b-713521a2f8ed","title":"Federated Graph Architecture","file_path":"specs/s-4lv6_federated_graph_architecture.md","content":"# Federated Graph Architecture\n\n**Parent**: [[s-9x15]] Phase 5: Providers\n**Related**: [[s-1pai]] Provider Interface Enhancement\n\n## Overview\n\nThis spec defines the architecture for a federated graph system that enables OpenTasks to query relationships across multiple providers (Native, Beads, Jira, etc.) through a unified traversal API. The design uses Graphology as an in-memory graph layer over the existing SQLite persistence.\n\n## Goals\n\n1. **Unified traversal API** - Query relationships without knowing which provider owns them\n2. **Provider autonomy** - Each provider owns its internal relationships; OpenTasks owns cross-provider edges\n3. **Minimal schema changes** - Extend existing SQLite schema, don't replace it\n4. **Performance at scale** - Fast in-memory queries for ~1K nodes, lazy hydration for external data\n5. **Extensible edge types** - Register new relationship types without code changes\n\n## Non-Goals\n\n- Full graph query language (Cypher, SPARQL, GraphQL)\n- Real-time sync with external providers\n- Conflict resolution for bidirectional sync\n\n---\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────────┐\n│ FederatedGraph │\n│ ┌────────────────────────────────────────────────────────────┐ │\n│ │ Graphology (in-memory) │ │\n│ │ - All native nodes (s-xxx, i-xxx, f-xxx) │ │\n│ │ - Materialized external nodes (x-xxx) │ │\n│ │ - All edges (native + cached from providers) │ │\n│ └────────────────────────────────────────────────────────────┘ │\n│ │ │\n│ ┌────────────────────────────────────────────────────────────┐ │\n│ │ Edge Registry │ │\n│ │ - Registered edge types with metadata │ │\n│ │ - Inverse relationships (blocks ↔ blocked-by) │ │\n│ │ - Ready-affecting edges (only 'blocks') │ │\n│ └────────────────────────────────────────────────────────────┘ │\n│ │ │\n│ ┌────────────────────────────────────────────────────────────┐ │\n│ │ Traversal API │ │\n│ │ traverse(start, pattern) → AsyncIterable<Result> │ │\n│ │ related(uri, edgeType, direction) → Promise<URI[]> │ │\n│ │ reachable(uri, edgeType, direction) → Promise<URI[]> │ │\n│ │ ready(options) → Promise<URI[]> │ │\n│ └────────────────────────────────────────────────────────────┘ │\n│ │ │\n│ ┌────────────────────────────────────────────────────────────┐ │\n│ │ Provider Federation │ │\n│ │ - Routes URI to correct provider │ │\n│ │ - Caches provider responses (nodes + edges) │ │\n│ │ - Hydrates graph on-demand │ │\n│ └────────────────────────────────────────────────────────────┘ │\n└──────────────────────────────────────────────────────────────────┘\n │\n ┌────────────────────┼────────────────────┐\n ▼ ▼ ▼\n ┌──────────┐ ┌──────────┐ ┌──────────┐\n │ Native │ │ Beads │ │ Jira │\n │ Provider │ │ Provider │ │ Provider │\n └──────────┘ └──────────┘ └──────────┘\n```\n\n### Key Principles\n\n1. **URIs as universal identifiers** - Every node has a URI (`native://s-abc`, `beads://./bd-123`)\n2. **OpenTasks owns cross-provider edges** - e.g., `bd-123` blocks `s-abc` stored in OpenTasks\n3. **Providers own intra-provider edges** - e.g., `bd-123` blocks `bd-456` queried from Beads\n4. **Lazy hydration** - External nodes/edges fetched on-demand, cached in SQLite\n5. **Graphology for traversal** - All queries run against in-memory graph\n\n---\n\n## Data Ownership Model\n\n| Data | Owner | Storage | Refresh |\n|------|-------|---------|---------|\n| Native nodes (s-, i-, f-) | OpenTasks | SQLite (authoritative) | N/A |\n| External nodes (x-) | Provider | SQLite (cache) | On-demand or TTL |\n| Cross-provider edges | OpenTasks | SQLite (authoritative) | N/A |\n| Intra-provider edges | Provider | SQLite (cache) | On hydration |\n\n### Edge Source Semantics\n\n```typescript\n// Edge.source field values:\nsource = null | 'local' → Created in OpenTasks (authoritative)\nsource = 'beads' → Cached from Beads (can be refreshed)\nsource = 'jira' → Cached from Jira (can be refreshed)\n```\n\n---\n\n## Schema Changes (Minimal)\n\n### Edge Table Additions\n\n```sql\n-- Add to existing edges table\nALTER TABLE edges ADD COLUMN metadata TEXT; -- JSON for relationship attributes\nALTER TABLE edges ADD COLUMN cached_at TEXT; -- When fetched from provider (ISO 8601)\n\n-- Index for cache management\nCREATE INDEX idx_edges_source ON edges(source);\nCREATE INDEX idx_edges_cached_at ON edges(cached_at);\n```\n\n### Edge Schema (Updated)\n\n```typescript\ninterface StoredEdge {\n id: string // 'x-xxxx'\n uuid: string // UUID v4\n from_id: string // Node ID or external URI\n to_id: string // Node ID or external URI\n type: string // 'blocks', 'implements', etc.\n created_at: string // ISO 8601\n created_by?: string // Actor\n source?: string // 'local' | 'beads' | 'jira' | null\n cached_at?: string // When cached from provider (ISO 8601)\n metadata?: Record<string, unknown> // NEW: Relationship attributes\n}\n```\n\n### No Changes Needed\n\n- **Nodes table** - Already supports ExternalNode with `uri`, `materialized`, `cached_at`, `external_data`\n- **Tags table** - Works as-is\n- **JSONL format** - Already flexible with `[key: string]: unknown`\n\n---\n\n## Edge Type Registry\n\nEdge types are registered, not hardcoded. Providers can contribute custom types.\n\n```typescript\ninterface EdgeTypeDefinition {\n name: string // 'blocks', 'implements', 'parent-of'\n description: string // Human-readable description\n inverseOf?: string // 'blocks' ↔ 'blocked-by' (virtual)\n affectsReady: boolean // Does this edge type affect ready status?\n direction: 'directed' | 'undirected'\n providers: string[] // Which providers support this type\n}\n\n// Built-in edge types\nconst BUILTIN_EDGE_TYPES: EdgeTypeDefinition[] = [\n {\n name: 'blocks',\n description: 'Source must complete before target can start',\n inverseOf: 'blocked-by',\n affectsReady: true,\n direction: 'directed',\n providers: ['native', 'beads']\n },\n {\n name: 'implements',\n description: 'Issue implements a spec',\n affectsReady: false,\n direction: 'directed',\n providers: ['native']\n },\n {\n name: 'parent-of',\n description: 'Hierarchical parent relationship',\n inverseOf: 'child-of',\n affectsReady: false,\n direction: 'directed',\n providers: ['native', 'beads']\n },\n {\n name: 'discovered-from',\n description: 'Found while working on another node',\n affectsReady: false,\n direction: 'directed',\n providers: ['native', 'beads']\n },\n {\n name: 'related',\n description: 'Loosely associated nodes',\n affectsReady: false,\n direction: 'undirected',\n providers: ['native', 'beads']\n },\n {\n name: 'references',\n description: 'General reference link',\n affectsReady: false,\n direction: 'directed',\n providers: ['native']\n }\n]\n```\n\n---\n\n## Traversal API\n\n### Core Interface\n\n```typescript\ntype NodeURI = string // 'native://s-abc', 'beads://./bd-123'\n\ninterface FederatedGraph {\n // === Traversal ===\n \n /**\n * Generic traversal with pattern matching\n * Crosses provider boundaries transparently\n */\n traverse(\n start: NodeURI | NodeURI[],\n pattern: TraversalPattern\n ): AsyncIterable<TraversalResult>\n \n /**\n * Get directly related nodes (single hop)\n */\n related(\n uri: NodeURI,\n edgeType: string,\n direction: 'in' | 'out' | 'both'\n ): Promise<NodeURI[]>\n \n /**\n * Get transitively reachable nodes\n */\n reachable(\n uri: NodeURI,\n edgeType: string,\n direction: 'in' | 'out'\n ): Promise<NodeURI[]>\n \n /**\n * Find shortest path between nodes\n */\n shortestPath(\n from: NodeURI,\n to: NodeURI,\n edgeTypes?: string[]\n ): Promise<NodeURI[] | null>\n \n // === Ready Query ===\n \n /**\n * Get nodes ready for work (no active blockers)\n * Considers full graph including external blockers\n */\n ready(options?: ReadyOptions): Promise<NodeURI[]>\n \n // === Resolution ===\n \n /**\n * Resolve URI to full node data\n * Fetches from provider if not cached\n */\n resolve(uri: NodeURI): Promise<ResolvedNode | null>\n \n /**\n * Batch resolve for efficiency\n */\n resolveAll(uris: NodeURI[]): Promise<Map<NodeURI, ResolvedNode>>\n \n // === Hydration ===\n \n /**\n * Ensure node and its edges are in the graph\n * Fetches from provider if needed\n */\n hydrate(uri: NodeURI): Promise<void>\n \n /**\n * Invalidate cached data for a provider\n */\n invalidateCache(provider: string): Promise<void>\n \n // === Introspection ===\n \n /**\n * Get registered edge types\n */\n edgeTypes(): EdgeTypeDefinition[]\n \n /**\n * Get graph capabilities\n */\n capabilities(): GraphCapabilities\n}\n\ninterface TraversalPattern {\n steps: EdgeStep[]\n filter?: NodePredicate\n limit?: number\n}\n\ninterface EdgeStep {\n type: string | string[] // Edge type(s) to follow\n direction: 'in' | 'out' | 'both'\n minHops?: number // Default: 1\n maxHops?: number // Default: 1, Infinity for transitive\n}\n\ninterface TraversalResult {\n uri: NodeURI\n depth: number // Hops from start\n path: NodeURI[] // Path taken to reach this node\n edge?: { // Edge that led here\n type: string\n from: NodeURI\n to: NodeURI\n }\n}\n\ninterface ReadyOptions {\n type?: string | string[] // Node type filter\n status?: string | string[] // Status filter\n tags?: string[] // Tag filter\n providers?: string[] // Limit to these providers\n limit?: number\n}\n\ninterface ResolvedNode {\n uri: NodeURI\n provider: string\n data: StoredNode // Full node data\n edges: { // Known edges\n incoming: StoredEdge[]\n outgoing: StoredEdge[]\n }\n cached: boolean // Was this from cache?\n cachedAt?: string // When cached\n}\n```\n\n### Usage Examples\n\n```typescript\nconst graph = createFederatedGraph(storage, registry)\n\n// Direct blockers (single hop)\nconst blockers = await graph.related('native://s-abc', 'blocks', 'in')\n// → ['beads://./bd-123']\n\n// Transitive blockers (follows the chain)\nconst allBlockers = await graph.reachable('native://s-abc', 'blocks', 'in')\n// → ['beads://./bd-123', 'beads://./bd-456']\n\n// Complex pattern: specs → implementing issues → their blockers\nfor await (const result of graph.traverse(['native://s-abc'], {\n steps: [\n { type: 'implements', direction: 'in' },\n { type: 'blocks', direction: 'in', maxHops: Infinity }\n ],\n filter: { status: { $ne: 'closed' } }\n})) {\n console.log(`${result.uri} at depth ${result.depth}`)\n}\n\n// Ready across all providers\nconst readyWork = await graph.ready({\n type: 'issue',\n status: 'open',\n providers: ['native', 'beads']\n})\n\n// dbt-style selectors (convenience methods)\nconst upstream = await graph.select('+native://s-abc') // Blockers\nconst downstream = await graph.select('native://s-abc+') // What this blocks\n```\n\n---\n\n## Provider Interface Extensions\n\nProviders that support relationships implement an optional trait:\n\n```typescript\n/**\n * Trait for providers that track relationships\n */\ninterface RelationshipQueryable {\n /**\n * Query edges from this provider\n * Returns edges in provider's local ID format\n */\n queryEdges(\n nodeId: string,\n edgeType?: string,\n direction?: 'in' | 'out' | 'both'\n ): Promise<ProviderEdge[]>\n \n /**\n * What edge types does this provider support?\n */\n supportedEdgeTypes(): EdgeTypeSupport[]\n}\n\ninterface ProviderEdge {\n from: string // Local ID (e.g., 'bd-123')\n to: string // Local ID (e.g., 'bd-456')\n type: string // Edge type\n metadata?: Record<string, unknown>\n}\n\ninterface EdgeTypeSupport {\n type: string\n canQuery: boolean // Can query existing edges\n canCreate: boolean // Can create edges via provider\n canDelete: boolean // Can delete edges via provider\n}\n\n// BeadsProvider implements this\ninterface BeadsProvider extends Provider, RelationshipQueryable {\n // Beads-specific methods...\n \n queryEdges(nodeId: string, edgeType?: string, direction?: 'in' | 'out'): Promise<ProviderEdge[]> {\n // Uses: bd show <id> --json (returns blockedBy, blocks, etc.)\n // Or: bd dep tree <id> --json\n }\n \n supportedEdgeTypes(): EdgeTypeSupport[] {\n return [\n { type: 'blocks', canQuery: true, canCreate: true, canDelete: true },\n { type: 'parent-child', canQuery: true, canCreate: true, canDelete: true },\n { type: 'discovered-from', canQuery: true, canCreate: true, canDelete: true },\n { type: 'related', canQuery: true, canCreate: true, canDelete: true }\n ]\n }\n}\n```\n\n---\n\n## Graphology Integration\n\n### Adapter Layer\n\n```typescript\nimport Graph from 'graphology'\nimport { bfsFromNode } from 'graphology-traversal'\n\ninterface GraphologyAdapter {\n readonly graph: Graph\n \n // Load from SQLite on startup\n loadFromStorage(storage: Storage): Promise<void>\n \n // Sync mutations (called after Storage writes)\n onNodeCreated(node: StoredNode): void\n onNodeUpdated(id: string, updates: Partial<StoredNode>): void\n onNodeDeleted(id: string): void\n onEdgeCreated(edge: StoredEdge): void\n onEdgeDeleted(id: string): void\n \n // Hydration (add external data to graph)\n hydrateNode(uri: NodeURI, node: StoredNode): void\n hydrateEdges(uri: NodeURI, edges: StoredEdge[]): void\n}\n\nclass GraphologyAdapterImpl implements GraphologyAdapter {\n readonly graph = new Graph({ multi: true, type: 'directed' })\n \n async loadFromStorage(storage: Storage): Promise<void> {\n // Load all nodes\n const nodes = await storage.queryNodes({})\n for (const node of nodes) {\n const uri = this.nodeToUri(node)\n this.graph.addNode(uri, { ...node })\n }\n \n // Load all edges\n const edges = await storage.getAllEdges()\n for (const edge of edges) {\n this.graph.addEdge(edge.from_id, edge.to_id, {\n id: edge.id,\n type: edge.type,\n source: edge.source,\n ...edge.metadata\n })\n }\n }\n \n private nodeToUri(node: StoredNode): NodeURI {\n if (node.type === 'external' && node.uri) {\n return node.uri\n }\n return `native://${node.id}`\n }\n}\n```\n\n### Traversal Implementation\n\n```typescript\nclass FederatedGraphImpl implements FederatedGraph {\n constructor(\n private adapter: GraphologyAdapter,\n private storage: Storage,\n private registry: ProviderRegistry,\n private cache: ProviderCache\n ) {}\n \n async related(uri: NodeURI, edgeType: string, direction: 'in' | 'out' | 'both'): Promise<NodeURI[]> {\n // Ensure node is hydrated\n await this.hydrate(uri)\n \n const results: NodeURI[] = []\n const graph = this.adapter.graph\n \n if (direction === 'in' || direction === 'both') {\n for (const edge of graph.inEdges(uri)) {\n if (graph.getEdgeAttribute(edge, 'type') === edgeType) {\n results.push(graph.source(edge))\n }\n }\n }\n \n if (direction === 'out' || direction === 'both') {\n for (const edge of graph.outEdges(uri)) {\n if (graph.getEdgeAttribute(edge, 'type') === edgeType) {\n results.push(graph.target(edge))\n }\n }\n }\n \n return results\n }\n \n async reachable(uri: NodeURI, edgeType: string, direction: 'in' | 'out'): Promise<NodeURI[]> {\n const results: NodeURI[] = []\n const visited = new Set<NodeURI>()\n \n const traverse = async (current: NodeURI): Promise<void> => {\n if (visited.has(current)) return\n visited.add(current)\n \n await this.hydrate(current)\n \n const neighbors = await this.related(current, edgeType, direction)\n for (const neighbor of neighbors) {\n results.push(neighbor)\n await traverse(neighbor)\n }\n }\n \n await traverse(uri)\n return results\n }\n \n async hydrate(uri: NodeURI): Promise<void> {\n // Check if already in graph and fresh\n if (this.adapter.graph.hasNode(uri)) {\n const cachedAt = this.adapter.graph.getNodeAttribute(uri, 'cached_at')\n if (cachedAt && !this.isStale(cachedAt)) {\n return // Already hydrated and fresh\n }\n }\n \n // Determine provider\n const provider = this.registry.resolveProvider(uri)\n if (!provider) return\n \n // Fetch node data\n const parsed = provider.parseUri(uri)\n if (!parsed) return\n \n const node = await this.cache.getOrFetch(uri, () => provider.get(parsed.id))\n if (!node) return\n \n // Add to graph\n this.adapter.hydrateNode(uri, this.toStoredNode(uri, node))\n \n // If provider supports edges, fetch those too\n if ('queryEdges' in provider) {\n const edges = await (provider as RelationshipQueryable).queryEdges(parsed.id)\n const storedEdges = edges.map(e => this.toStoredEdge(uri, e, provider.name))\n this.adapter.hydrateEdges(uri, storedEdges)\n \n // Persist to SQLite cache\n for (const edge of storedEdges) {\n await this.storage.createCachedEdge(edge)\n }\n }\n }\n}\n```\n\n---\n\n## Data Flow\n\n### Startup\n\n```\n1. Open SQLite database\n2. Create Graphology graph\n3. Load all nodes from SQLite → Graphology\n4. Load all edges from SQLite → Graphology\n5. Ready for queries\n```\n\n### Read Operations\n\n```\nQuery → Check Graphology\n ├─ Node exists & fresh → Return immediately\n └─ Node missing or stale → Hydrate\n ├─ Fetch from Provider\n ├─ Cache to SQLite\n ├─ Update Graphology\n └─ Continue traversal\n```\n\n### Write Operations (Native)\n\n```\nMutation → SQLite (authoritative)\n → Graphology (sync)\n → JSONL export (async)\n```\n\n### Write Operations (External Provider)\n\n```\nUser creates edge: bd-123 blocks s-abc\n → SQLite edges table (source='local', authoritative)\n → Graphology\n \nSystem caches edge from Beads: bd-123 blocks bd-456\n → SQLite edges table (source='beads', cached_at=now)\n → Graphology\n```\n\n---\n\n## Caching Strategy\n\n### Cache Invalidation\n\n```typescript\ninterface CacheConfig {\n defaultTTL: number // Default: 5 minutes\n staleWhileRevalidate: boolean // Return stale, refresh in background\n providerTTL: { // Per-provider overrides\n [provider: string]: number\n }\n}\n\n// Example\nconst cacheConfig: CacheConfig = {\n defaultTTL: 5 * 60 * 1000, // 5 minutes\n staleWhileRevalidate: true,\n providerTTL: {\n beads: 2 * 60 * 1000, // 2 minutes for Beads (fast CLI)\n jira: 10 * 60 * 1000 // 10 minutes for Jira (slow API)\n }\n}\n```\n\n### Staleness Check\n\n```typescript\nfunction isStale(cachedAt: string, provider: string, config: CacheConfig): boolean {\n const ttl = config.providerTTL[provider] ?? config.defaultTTL\n const age = Date.now() - new Date(cachedAt).getTime()\n return age > ttl\n}\n```\n\n---\n\n## Ready Calculation\n\nReady status must consider the **full graph**, including external blockers:\n\n```typescript\nasync ready(options?: ReadyOptions): Promise<NodeURI[]> {\n // 1. Get candidate nodes (native issues that are open)\n const candidates = await this.storage.queryNodes({\n type: 'issue',\n status: 'open',\n archived: false\n })\n \n const readyNodes: NodeURI[] = []\n \n for (const node of candidates) {\n const uri = `native://${node.id}`\n \n // 2. Get all blockers (including external)\n const blockers = await this.related(uri, 'blocks', 'in')\n \n // 3. Check if any blocker is active\n let hasActiveBlocker = false\n for (const blockerUri of blockers) {\n const blocker = await this.resolve(blockerUri)\n if (blocker && !this.isClosed(blocker)) {\n hasActiveBlocker = true\n break\n }\n }\n \n if (!hasActiveBlocker) {\n readyNodes.push(uri)\n }\n }\n \n return readyNodes\n}\n\nprivate isClosed(node: ResolvedNode): boolean {\n const status = node.data.status ?? node.data.external_status\n return status === 'closed' || status === 'done' || status === 'resolved'\n}\n```\n\n---\n\n## Performance Considerations\n\n### At ~1K Nodes\n\n| Operation | Complexity | Expected Time |\n|-----------|------------|---------------|\n| BFS traversal | O(V + E) | < 1ms |\n| Transitive closure | O(V × E) | < 5ms |\n| Graph load from SQLite | O(V + E) | < 50ms |\n| External node hydration | Network-bound | 100-500ms |\n\n**Conclusion:** Graph operations are instant. Network latency to providers dominates.\n\n### Optimizations\n\n1. **Batch hydration** - Fetch multiple nodes in one provider call\n2. **Stale-while-revalidate** - Return cached data, refresh in background\n3. **Lazy edge loading** - Only fetch edges when traversal needs them\n4. **Index on `source`** - Fast lookup of cached edges by provider\n\n---\n\n## Implementation Phases\n\n### Phase 1: Schema & Edge Registry\n- [ ] Add `metadata` and `cached_at` columns to edges table\n- [ ] Create edge type registry with built-in types\n- [ ] Add `source` index to edges table\n\n### Phase 2: Graphology Integration\n- [ ] Add `graphology` dependency\n- [ ] Implement GraphologyAdapter\n- [ ] Load graph from SQLite on startup\n- [ ] Sync mutations to graph\n\n### Phase 3: Federated Graph Core\n- [ ] Implement FederatedGraph interface\n- [ ] Implement `related()` and `reachable()`\n- [ ] Implement `hydrate()` with provider fetch\n- [ ] Implement caching layer\n\n### Phase 4: Provider Extensions\n- [ ] Define RelationshipQueryable trait\n- [ ] Implement in BeadsProvider (using `bd show --json`)\n- [ ] Implement in NativeProvider (using GraphStore edges)\n\n### Phase 5: Ready Query\n- [ ] Implement federated `ready()` query\n- [ ] Consider external blockers in calculation\n- [ ] Update existing ready view for compatibility\n\n### Phase 6: Advanced Traversal\n- [ ] Implement `traverse()` with patterns\n- [ ] Implement `shortestPath()`\n- [ ] Add selector syntax (`+uri`, `uri+`, `@uri`)\n\n---\n\n## Decisions Log\n\n| Decision | Options Considered | Chosen | Rationale |\n|----------|-------------------|--------|-----------|\n| Graph library | Graphology, ngraph, LevelGraph, custom | Graphology | Best algorithm library, TypeScript support, active maintenance |\n| Schema changes | Minimal additions, dedicated cache table, new graph DB | Minimal additions | Current schema is 90% compatible, least disruption |\n| Edge ownership | All edges in OpenTasks, provider-only edges, split | Split (cross-provider = OpenTasks, intra-provider = cached) | Clear ownership, single source of truth |\n| Query interface | Hardcoded methods, GraphQL, generic traversal | Generic traversal | Flexible, extensible, no query language overhead |\n| Cache strategy | TTL-based, manual invalidation, no cache | TTL with stale-while-revalidate | Balance freshness and performance |\n| Ready calculation | SQL view only, full graph traversal | Full graph traversal | Must consider external blockers |\n\n---\n\n## Open Questions\n\n| Question | Status | Notes |\n|----------|--------|-------|\n| Should `traverse()` use async iterators or return arrays? | **Decided: AsyncIterable** | Allows lazy evaluation for large traversals |\n| How to handle cycles in traversal? | **Decided: visited set** | Standard BFS/DFS cycle detection |\n| Should we support GraphQL as query layer? | Deferred | Can add later if needed for external clients |\n| How to handle provider rate limits? | TODO | Need backoff/retry strategy |\n\n---\n\n## Success Criteria\n\n1. ✅ Can traverse from native node to external node and back\n2. ✅ Cross-provider edges stored in OpenTasks (authoritative)\n3. ✅ Intra-provider edges cached and refreshable\n4. ✅ Ready query considers full graph (including external blockers)\n5. ✅ < 100ms for typical traversal (excluding network)\n6. ✅ Edge types are extensible without code changes\n7. ✅ Minimal schema migration (2 columns added)\n\n---\n\n## Dependencies\n\n- [[s-9x15]] Phase 5: Providers - Provider interface and registry\n- [[s-1pai]] Provider Interface Enhancement - Extended provider types\n- `graphology` npm package (MIT license)\n- `graphology-traversal` for BFS/DFS algorithms\n\n## References\n\n- [Graphology documentation](https://graphology.github.io/)\n- [Apollo Federation](https://www.apollographql.com/docs/federation/) - Inspiration for federated queries\n- [dbt selector syntax](https://docs.getdbt.com/reference/node-selection/syntax) - Inspiration for `+node`, `node+`\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-29 05:53:58","updated_at":"2026-01-29 05:53:58","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-4lv6","from_type":"spec","to":"s-1pai","to_type":"spec","type":"related"},{"from":"s-4lv6","from_type":"spec","to":"s-9x15","to_type":"spec","type":"depends-on"}],"tags":["architecture","federation","graph","graphology","traversal"]}
13
- {"id":"s-1nsc","uuid":"5094c46c-8d14-46d6-a166-305bb1eb37a5","title":"Configuration System","file_path":"specs/s-1nsc_configuration_system.md","content":"# Configuration System\n\n**Priority:** High\n\n## Overview\n\nImplement a configuration system that allows users to customize OpenTasks behavior. Currently there's no way to configure storage paths, provider settings, daemon behavior, or other options.\n\n## Goals\n\n1. Provide sensible defaults that work out of the box\n2. Allow customization via config file\n3. Support environment variable overrides\n4. Validate configuration on load\n\n## Design Decisions (v1 Scope)\n\n### Simplified v1 Approach\n- **Local config only** - `.opentasks/config.json` (no global `~/.opentasks/` for v1)\n- **Hybrid storage only** - No `type` field; always uses JSONL + SQLite\n- **Minimal logging config** - Level and file path only (no rotation)\n- **No CLI commands** - Programmatic API only for v1\n- **No JSON schema** - Defer IDE autocomplete support\n\n### Provider Auto-Detection\n- Providers enabled by default (`enabled: true`)\n- If executable not found, provider silently skips (no error)\n- Explicit `enabled: false` disables provider entirely\n\n## Configuration Hierarchy (v1)\n\nConfiguration is merged in order (later overrides earlier):\n\n1. **Built-in defaults** (in code)\n2. **Local config** (`.opentasks/config.json`)\n3. **Environment variables** (`OPENTASKS_*`)\n\n## Configuration Schema (v1)\n\n```typescript\ninterface OpenTasksConfig {\n storage: {\n /** JSONL file path (relative to .opentasks/) */\n jsonlPath: string // default: 'graph.jsonl'\n \n /** SQLite database path (relative to .opentasks/) */\n sqlitePath: string // default: 'cache.db'\n \n /** Auto-compact JSONL when ratio exceeds threshold */\n autoCompactRatio: number // default: 2.0\n }\n\n daemon: {\n /** Socket path (relative to .opentasks/) */\n socketPath: string // default: 'daemon.sock'\n \n /** Auto-start daemon on first operation */\n autoStart: boolean // default: true\n \n /** Flush interval (ms) */\n flushInterval: number // default: 1000\n }\n\n providers: {\n beads: {\n /** Enable beads provider (auto-detects executable) */\n enabled: boolean // default: true\n \n /** Path to bd executable */\n executable: string // default: 'bd'\n \n /** Command timeout (ms) */\n timeout: number // default: 30000\n }\n \n claudeTasks: {\n /** Enable Claude Tasks provider */\n enabled: boolean // default: true\n }\n }\n\n logging: {\n /** Log level */\n level: 'debug' | 'info' | 'warn' | 'error' // default: 'info'\n \n /** Log file path (relative to .opentasks/, null = no file) */\n file: string | null // default: null\n }\n}\n```\n\n## Environment Variables\n\nAll config keys map to environment variables:\n\n```bash\nOPENTASKS_STORAGE_JSONL_PATH=custom.jsonl\nOPENTASKS_STORAGE_SQLITE_PATH=custom.db\nOPENTASKS_DAEMON_AUTO_START=false\nOPENTASKS_DAEMON_FLUSH_INTERVAL=2000\nOPENTASKS_PROVIDERS_BEADS_ENABLED=false\nOPENTASKS_PROVIDERS_BEADS_EXECUTABLE=/usr/local/bin/bd\nOPENTASKS_LOGGING_LEVEL=debug\nOPENTASKS_LOGGING_FILE=logs/debug.log\n```\n\nMapping rules:\n- Nested keys use underscore: `storage.jsonlPath` → `OPENTASKS_STORAGE_JSONL_PATH`\n- camelCase converts to SCREAMING_SNAKE_CASE\n- Booleans: `true`, `false`, `1`, `0`\n- `null` values: empty string or literal `null`\n\n## Technical Design\n\n### File Structure\n```\nsrc/config/\n├── index.ts # Public exports\n├── schema.ts # Zod schema & types\n├── defaults.ts # Default values\n├── loader.ts # Config file loader\n├── env.ts # Environment variable parser\n├── merge.ts # Config merger\n└── __tests__/\n ├── schema.test.ts\n ├── loader.test.ts\n ├── env.test.ts\n └── merge.test.ts\n```\n\n### Config Loading API\n```typescript\n// src/config/index.ts\n\n/** Load merged configuration for a location */\nexport async function loadConfig(location?: string): Promise<OpenTasksConfig>\n\n/** Get default configuration */\nexport function getDefaults(): OpenTasksConfig\n\n/** Validate a config object */\nexport function validateConfig(config: unknown): ValidationResult\n\n/** Parse environment variables into partial config */\nexport function parseEnvConfig(): Partial<OpenTasksConfig>\n```\n\n### Zod Schema\n```typescript\n// src/config/schema.ts\nimport { z } from 'zod'\n\nexport const StorageConfigSchema = z.object({\n jsonlPath: z.string().default('graph.jsonl'),\n sqlitePath: z.string().default('cache.db'),\n autoCompactRatio: z.number().min(1).default(2.0),\n})\n\nexport const DaemonConfigSchema = z.object({\n socketPath: z.string().default('daemon.sock'),\n autoStart: z.boolean().default(true),\n flushInterval: z.number().min(100).default(1000),\n})\n\nexport const BeadsProviderConfigSchema = z.object({\n enabled: z.boolean().default(true),\n executable: z.string().default('bd'),\n timeout: z.number().min(1000).default(30000),\n})\n\nexport const ClaudeTasksProviderConfigSchema = z.object({\n enabled: z.boolean().default(true),\n})\n\nexport const ProvidersConfigSchema = z.object({\n beads: BeadsProviderConfigSchema.default({}),\n claudeTasks: ClaudeTasksProviderConfigSchema.default({}),\n})\n\nexport const LoggingConfigSchema = z.object({\n level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),\n file: z.string().nullable().default(null),\n})\n\nexport const OpenTasksConfigSchema = z.object({\n storage: StorageConfigSchema.default({}),\n daemon: DaemonConfigSchema.default({}),\n providers: ProvidersConfigSchema.default({}),\n logging: LoggingConfigSchema.default({}),\n})\n\nexport type OpenTasksConfig = z.infer<typeof OpenTasksConfigSchema>\n```\n\n### Config File Format\n```json\n{\n \"storage\": {\n \"jsonlPath\": \"graph.jsonl\",\n \"sqlitePath\": \"cache.db\"\n },\n \"daemon\": {\n \"autoStart\": true,\n \"flushInterval\": 1000\n },\n \"providers\": {\n \"beads\": {\n \"enabled\": true\n }\n },\n \"logging\": {\n \"level\": \"info\"\n }\n}\n```\n\n### Integration Points\n\n1. **Daemon startup** - Load config before initializing storage/providers\n2. **Provider registry** - Check `providers.*.enabled` before loading\n3. **Storage creation** - Use `storage.*Path` values\n4. **Future logging** - Use `logging.level` and `logging.file`\n\n## Deliverables (v1)\n\n- [ ] Config schema with Zod validation (`src/config/schema.ts`)\n- [ ] Default configuration values (`src/config/defaults.ts`)\n- [ ] Config file loader (`src/config/loader.ts`)\n- [ ] Environment variable parser (`src/config/env.ts`)\n- [ ] Config merger (`src/config/merge.ts`)\n- [ ] Public API exports (`src/config/index.ts`)\n- [ ] Unit tests for all modules\n- [ ] Integration with daemon startup\n- [ ] Integration with provider registry\n\n## Deferred to v2\n\n- Global config (`~/.opentasks/config.json`)\n- `opentasks config` CLI commands\n- JSON schema for IDE autocomplete\n- Config migration for schema changes\n- Log rotation (`maxSize`, `maxFiles`)\n- Additional daemon options (`idleTimeout`, `maxConnections`)\n\n## Success Criteria\n\n1. Works out of the box with zero configuration\n2. Config file is human-readable and editable\n3. Environment variables override file config\n4. Invalid config produces helpful error messages\n5. Tests can override config programmatically\n\n## Dependencies\n\n- [[s-6w1s]] CLI Implementation (will use config for future commands)\n- [[s-7toh]] Logging System (will consume logging config)","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-29 18:52:47","updated_at":"2026-02-03 07:46:40","parent_id":null,"parent_uuid":null,"relationships":[],"tags":["config","infrastructure","production"]}
14
- {"id":"s-6w1s","uuid":"4ac3fbea-d196-4da5-8f21-0f15c1bc8ae4","title":"CLI Implementation","file_path":"specs/s-6w1s_cli_implementation.md","content":"# CLI Implementation\n\n**Priority:** Critical (Blocking)\n\n## Overview\n\nImplement a full-featured command-line interface for OpenTasks. Currently, only `help` works. Users cannot interact with the system without a functional CLI.\n\n## Goals\n\n1. Enable users to manage the daemon lifecycle\n2. Provide CRUD operations for specs, issues, and edges\n3. Support querying (ready, blockers, search)\n4. Enable data backup and restore\n5. Provide initialization and configuration commands\n\n## Commands\n\n### Initialization\n```bash\nopentasks init [--force]\n```\n- Creates `.opentasks/` directory structure\n- Initializes empty graph.jsonl and cache.db\n- Creates default config.json\n- Optionally overwrites existing (--force)\n\n### Daemon Management\n```bash\nopentasks daemon start [--foreground]\nopentasks daemon stop\nopentasks daemon status\nopentasks daemon restart\n```\n- `start`: Start daemon (background by default, --foreground for debug)\n- `stop`: Graceful shutdown with flush\n- `status`: Show PID, uptime, connection count, graph stats\n- `restart`: Stop then start\n\n### Node Operations\n```bash\nopentasks create <type> --title <title> [--content <content>] [--status <status>] [--priority <0-4>]\nopentasks get <id>\nopentasks list [type] [--status <status>] [--limit <n>] [--format json|table]\nopentasks update <id> [--title <title>] [--status <status>] [--content <content>]\nopentasks delete <id> [--force]\n```\n- `type`: spec | issue | feedback\n- Default output format: table (human-readable)\n- JSON output for scripting: `--format json`\n\n### Edge Operations\n```bash\nopentasks link <from> <to> --type <edge-type>\nopentasks unlink <from> <to> [--type <edge-type>]\nopentasks edges <id> [--direction in|out|both] [--type <edge-type>]\n```\n- Edge types: blocks, implements, parent-of, related, etc.\n\n### Query Operations\n```bash\nopentasks ready [--type <type>] [--limit <n>]\nopentasks blockers <id>\nopentasks blocked-by <id>\nopentasks search <query> [--type <type>]\nopentasks path <from> <to>\n```\n\n### Data Operations\n```bash\nopentasks export [--output <file>] [--format json|jsonl]\nopentasks import <file> [--merge|--replace]\nopentasks compact\nopentasks stats\n```\n- `export`: Full graph dump\n- `import`: Restore from backup\n- `compact`: Compact JSONL file\n- `stats`: Node/edge counts, storage sizes\n\n### Configuration\n```bash\nopentasks config get [key]\nopentasks config set <key> <value>\nopentasks config list\nopentasks config reset [key]\n```\n\n## Technical Design\n\n### Argument Parsing\nUse a lightweight argument parser. Options:\n- Native `process.argv` parsing (no deps)\n- `commander` (popular, typed)\n- `yargs` (feature-rich)\n\n**Recommendation:** Start with native parsing for simplicity, add library if complexity grows.\n\n### Command Structure\n```typescript\n// src/cli/index.ts\ninterface Command {\n name: string\n description: string\n options: Option[]\n run(args: ParsedArgs): Promise<number> // Exit code\n}\n\n// src/cli/commands/\n// ├── init.ts\n// ├── daemon.ts\n// ├── create.ts\n// ├── get.ts\n// ├── list.ts\n// ├── update.ts\n// ├── delete.ts\n// ├── link.ts\n// ├── query.ts\n// ├── export.ts\n// ├── import.ts\n// ├── config.ts\n// └── index.ts\n```\n\n### Client Integration\nCommands connect to daemon via existing `OpenTasksClient`:\n```typescript\nimport { createClient } from '../client/index.js'\n\nasync function run() {\n const client = createClient()\n await client.connect()\n // ... execute command\n await client.disconnect()\n}\n```\n\n### Output Formatting\n```typescript\ninterface OutputFormatter {\n table(rows: Record<string, unknown>[], columns?: string[]): string\n json(data: unknown): string\n error(message: string, details?: unknown): string\n success(message: string): string\n}\n```\n\n### Error Handling\n- Exit code 0: Success\n- Exit code 1: User error (invalid args, not found)\n- Exit code 2: System error (daemon not running, connection failed)\n- Helpful error messages with suggestions\n\n## Deliverables\n\n- [ ] Argument parser and command router\n- [ ] `init` command\n- [ ] `daemon start/stop/status/restart` commands\n- [ ] `create/get/list/update/delete` commands\n- [ ] `link/unlink/edges` commands\n- [ ] `ready/blockers/blocked-by/search/path` commands\n- [ ] `export/import/compact/stats` commands\n- [ ] `config get/set/list/reset` commands\n- [ ] Output formatters (table, JSON)\n- [ ] Error handling with exit codes\n- [ ] `--help` for each command\n- [ ] Shell completion scripts (bash, zsh)\n\n## Success Criteria\n\n1. All commands work end-to-end with running daemon\n2. Helpful error messages when daemon not running\n3. JSON output works for all commands (scriptable)\n4. Exit codes are consistent and documented\n5. `--help` provides useful guidance for each command\n6. Can complete full workflow: init → daemon start → create → query → export\n\n## Dependencies\n\n- [[s-config]] Configuration System (for `config` commands)\n- [[s-backup]] Backup & Export (for `export/import` commands)\n","priority":0,"archived":0,"archived_at":null,"created_at":"2026-01-29 18:52:47","updated_at":"2026-01-29 18:52:47","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-6w1s","from_type":"spec","to":"s-1nsc","to_type":"spec","type":"depends-on"},{"from":"s-6w1s","from_type":"spec","to":"s-2oag","to_type":"spec","type":"depends-on"}],"tags":["cli","critical","production","user-facing"]}
15
- {"id":"s-7toh","uuid":"df6ad6b9-e28e-418a-8b11-2563b3f30fe0","title":"Logging System","file_path":"specs/s-7toh_logging_system.md","content":"# Logging System\n\n**Priority:** High\n\n## Overview\n\nImplement structured logging for OpenTasks. Currently there's no logging - users and operators cannot debug issues or understand system behavior.\n\n## Goals\n\n1. Provide structured, parseable logs\n2. Support multiple output targets (file, stderr)\n3. Enable log levels for filtering\n4. Include context (timestamps, component, request IDs)\n5. Support log rotation to prevent disk exhaustion\n\n## Log Levels\n\n| Level | Use Case | Example |\n|-------|----------|---------|\n| `debug` | Detailed debugging info | \"Parsing node i-x7k9\", \"Cache hit for URI\" |\n| `info` | Normal operations | \"Daemon started\", \"Created node i-x7k9\" |\n| `warn` | Recoverable issues | \"Retrying connection\", \"Stale cache entry\" |\n| `error` | Failures | \"Failed to connect to daemon\", \"Invalid node ID\" |\n\n## Log Format\n\n### Structured JSON (file output)\n```json\n{\n \"timestamp\": \"2025-01-29T10:30:00.123Z\",\n \"level\": \"info\",\n \"component\": \"daemon\",\n \"message\": \"Client connected\",\n \"context\": {\n \"clientId\": \"abc123\",\n \"socketPath\": \"/path/to/.opentasks/daemon.sock\"\n }\n}\n```\n\n### Human-readable (stderr output)\n```\n2025-01-29 10:30:00.123 [INFO] daemon: Client connected clientId=abc123\n2025-01-29 10:30:01.456 [DEBUG] graph: Node created id=i-x7k9 type=issue\n2025-01-29 10:30:02.789 [ERROR] provider: Beads command failed code=1 cmd=\"bd show\"\n```\n\n## Technical Design\n\n### Logger Interface\n```typescript\n// src/logging/index.ts\n\ninterface Logger {\n debug(message: string, context?: Record<string, unknown>): void\n info(message: string, context?: Record<string, unknown>): void\n warn(message: string, context?: Record<string, unknown>): void\n error(message: string, context?: Record<string, unknown>): void\n \n /** Create child logger with preset context */\n child(context: Record<string, unknown>): Logger\n \n /** Create child logger for a component */\n forComponent(name: string): Logger\n}\n\n// Usage\nconst logger = createLogger(config.logging)\nconst daemonLogger = logger.forComponent('daemon')\n\ndaemonLogger.info('Client connected', { clientId: 'abc123' })\n// Output: 2025-01-29 10:30:00 [INFO] daemon: Client connected clientId=abc123\n```\n\n### Component Loggers\nEach major component gets its own logger:\n```typescript\n// In daemon\nconst log = logger.forComponent('daemon')\n\n// In graph store\nconst log = logger.forComponent('graph')\n\n// In provider\nconst log = logger.forComponent('provider.beads')\n\n// In CLI\nconst log = logger.forComponent('cli')\n```\n\n### Request Context\nFor tracing requests through the system:\n```typescript\ninterface RequestContext {\n requestId: string\n clientId?: string\n method?: string\n}\n\n// Automatically included in all logs within request scope\nconst reqLogger = logger.child({ requestId: 'req-123', method: 'createNode' })\nreqLogger.info('Processing request')\n// Output includes: requestId=req-123 method=createNode\n```\n\n### Log Rotation\n```typescript\ninterface RotationConfig {\n maxSize: number // Max file size in bytes (default: 10MB)\n maxFiles: number // Number of files to keep (default: 5)\n compress: boolean // Gzip old files (default: true)\n}\n\n// Results in:\n// .opentasks/logs/\n// ├── opentasks.log (current)\n// ├── opentasks.1.log.gz (previous)\n// ├── opentasks.2.log.gz\n// └── ...\n```\n\n### Output Targets\n```typescript\ntype LogTarget = \n | { type: 'file'; path: string; rotation?: RotationConfig }\n | { type: 'stderr' }\n | { type: 'null' } // Disable logging\n\n// Config supports multiple targets\nconst config = {\n logging: {\n level: 'info',\n targets: [\n { type: 'file', path: 'logs/opentasks.log' },\n { type: 'stderr' } // Also log to console\n ]\n }\n}\n```\n\n## Integration Points\n\n### Daemon\n```typescript\n// On startup\nlog.info('Daemon starting', { socketPath, pid: process.pid })\n\n// On client connect\nlog.info('Client connected', { clientId })\n\n// On request\nlog.debug('Handling request', { method, params })\n\n// On error\nlog.error('Request failed', { method, error: err.message })\n\n// On shutdown\nlog.info('Daemon shutting down', { reason: 'SIGTERM' })\n```\n\n### Graph Operations\n```typescript\n// On node create\nlog.debug('Node created', { id, type })\n\n// On validation error\nlog.warn('Validation failed', { nodeId, errors })\n\n// On cycle detected\nlog.warn('Cycle detected', { path })\n```\n\n### Providers\n```typescript\n// On external call\nlog.debug('Calling provider', { provider: 'beads', command: 'show', args })\n\n// On timeout\nlog.warn('Provider timeout', { provider: 'beads', timeout: 30000 })\n\n// On error\nlog.error('Provider failed', { provider: 'beads', error: err.message })\n```\n\n### CLI\n```typescript\n// On command start\nlog.debug('Executing command', { command: 'create', args })\n\n// On success\nlog.info('Command completed', { command: 'create', nodeId: 'i-x7k9' })\n\n// On error\nlog.error('Command failed', { command: 'create', error: err.message })\n```\n\n## Deliverables\n\n- [ ] Logger interface and factory\n- [ ] JSON formatter for file output\n- [ ] Human-readable formatter for stderr\n- [ ] Log rotation implementation\n- [ ] Component logger factory\n- [ ] Request context propagation\n- [ ] Integration with daemon\n- [ ] Integration with graph operations\n- [ ] Integration with providers\n- [ ] Integration with CLI\n- [ ] Log level filtering\n- [ ] Environment variable override (`OPENTASKS_LOG_LEVEL`)\n\n## Success Criteria\n\n1. All major operations produce log entries\n2. Logs are structured and parseable\n3. Log rotation prevents disk exhaustion\n4. Debug level provides enough detail to diagnose issues\n5. Info level shows normal operation without noise\n6. Request IDs allow tracing requests through system\n7. No performance impact when logging disabled\n\n## Dependencies\n\n- [[s-config]] Configuration System (for logging config)\n","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-29 18:52:47","updated_at":"2026-01-29 18:52:47","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-7toh","from_type":"spec","to":"s-1nsc","to_type":"spec","type":"depends-on"}],"tags":["infrastructure","logging","observability","production"]}
16
- {"id":"s-2oag","uuid":"fe3f41ec-6dab-4be2-8cfa-8bf57d7149d2","title":"Backup & Data Recovery","file_path":"specs/s-2oag_backup_data_recovery.md","content":"# Backup & Data Recovery\n\n**Priority:** High\n\n## Overview\n\nImplement backup, export, import, and data recovery capabilities. Currently, if data is lost or corrupted, it's unrecoverable. Users need confidence their work is safe.\n\n## Goals\n\n1. Enable full data export for backup\n2. Support import/restore from backup\n3. Provide data integrity verification\n4. Enable recovery from corruption\n5. Support migration between storage formats\n\n## Export Formats\n\n### JSON (default, human-readable)\n```json\n{\n \"version\": \"1.0\",\n \"exportedAt\": \"2025-01-29T10:30:00Z\",\n \"location\": \"/path/to/.opentasks\",\n \"stats\": {\n \"nodes\": 150,\n \"edges\": 87\n },\n \"nodes\": [\n {\n \"id\": \"s-abc1\",\n \"uuid\": \"...\",\n \"type\": \"spec\",\n \"title\": \"My Spec\",\n \"content\": \"...\",\n \"created_at\": \"...\",\n \"updated_at\": \"...\"\n }\n ],\n \"edges\": [\n {\n \"id\": \"e-xyz9\",\n \"from_id\": \"i-def2\",\n \"to_id\": \"s-abc1\",\n \"type\": \"implements\"\n }\n ]\n}\n```\n\n### JSONL (streaming, large datasets)\n```jsonl\n{\"type\":\"meta\",\"version\":\"1.0\",\"exportedAt\":\"2025-01-29T10:30:00Z\"}\n{\"type\":\"node\",\"data\":{\"id\":\"s-abc1\",\"type\":\"spec\",...}}\n{\"type\":\"node\",\"data\":{\"id\":\"i-def2\",\"type\":\"issue\",...}}\n{\"type\":\"edge\",\"data\":{\"id\":\"e-xyz9\",\"from_id\":\"i-def2\",\"to_id\":\"s-abc1\",...}}\n```\n\n## Commands\n\n### Export\n```bash\n# Export to stdout (default: JSON)\nopentasks export\n\n# Export to file\nopentasks export --output backup.json\n\n# Export as JSONL (for large graphs)\nopentasks export --format jsonl --output backup.jsonl\n\n# Export specific types\nopentasks export --types spec,issue\n\n# Export with compression\nopentasks export --output backup.json.gz --compress\n```\n\n### Import\n```bash\n# Import from file (merge mode - add new, skip existing)\nopentasks import backup.json\n\n# Import with replace mode (clear existing, then import)\nopentasks import backup.json --replace\n\n# Import with update mode (update existing, add new)\nopentasks import backup.json --update\n\n# Dry run (show what would happen)\nopentasks import backup.json --dry-run\n\n# Import from stdin\ncat backup.json | opentasks import -\n```\n\n### Verify\n```bash\n# Verify data integrity\nopentasks verify\n\n# Verify with detailed output\nopentasks verify --verbose\n\n# Verify a backup file without importing\nopentasks verify --file backup.json\n```\n\n### Repair\n```bash\n# Attempt automatic repair\nopentasks repair\n\n# Show what would be repaired (dry run)\nopentasks repair --dry-run\n\n# Repair specific issues\nopentasks repair --fix orphan-edges\nopentasks repair --fix dangling-refs\nopentasks repair --fix duplicate-ids\n```\n\n## Technical Design\n\n### Export Service\n```typescript\n// src/backup/export.ts\n\ninterface ExportOptions {\n format: 'json' | 'jsonl'\n types?: NodeType[]\n compress?: boolean\n includeDeleted?: boolean\n}\n\ninterface ExportService {\n /** Export to writable stream */\n export(output: Writable, options?: ExportOptions): Promise<ExportResult>\n \n /** Export to file */\n exportToFile(path: string, options?: ExportOptions): Promise<ExportResult>\n}\n\ninterface ExportResult {\n nodes: number\n edges: number\n bytes: number\n duration: number\n}\n```\n\n### Import Service\n```typescript\n// src/backup/import.ts\n\ntype ImportMode = 'merge' | 'replace' | 'update'\n\ninterface ImportOptions {\n mode: ImportMode\n dryRun?: boolean\n onConflict?: (existing: Node, imported: Node) => 'keep' | 'replace' | 'skip'\n}\n\ninterface ImportService {\n /** Import from readable stream */\n import(input: Readable, options?: ImportOptions): Promise<ImportResult>\n \n /** Import from file */\n importFromFile(path: string, options?: ImportOptions): Promise<ImportResult>\n}\n\ninterface ImportResult {\n created: number\n updated: number\n skipped: number\n errors: ImportError[]\n}\n```\n\n### Verification Service\n```typescript\n// src/backup/verify.ts\n\ninterface VerifyResult {\n valid: boolean\n issues: VerifyIssue[]\n stats: {\n nodes: number\n edges: number\n orphanEdges: number\n danglingRefs: number\n duplicateIds: number\n }\n}\n\ninterface VerifyIssue {\n type: 'orphan-edge' | 'dangling-ref' | 'duplicate-id' | 'invalid-schema' | 'cycle'\n severity: 'error' | 'warning'\n message: string\n details: Record<string, unknown>\n}\n\ninterface VerifyService {\n /** Verify current storage */\n verify(): Promise<VerifyResult>\n \n /** Verify a backup file */\n verifyFile(path: string): Promise<VerifyResult>\n}\n```\n\n### Repair Service\n```typescript\n// src/backup/repair.ts\n\ninterface RepairOptions {\n dryRun?: boolean\n fixes?: RepairFix[]\n}\n\ntype RepairFix = \n | 'orphan-edges' // Remove edges pointing to non-existent nodes\n | 'dangling-refs' // Clear references to deleted nodes\n | 'duplicate-ids' // Regenerate duplicate IDs\n | 'schema-migration' // Update old schema versions\n\ninterface RepairService {\n /** Repair current storage */\n repair(options?: RepairOptions): Promise<RepairResult>\n}\n\ninterface RepairResult {\n fixed: RepairAction[]\n failed: RepairError[]\n}\n```\n\n### Automatic Backups\n```typescript\n// Optional: automatic backup before dangerous operations\n\ninterface AutoBackupConfig {\n enabled: boolean\n beforeOperations: ('import' | 'repair' | 'compact')[]\n maxBackups: number // Keep last N auto-backups\n path: string // Default: .opentasks/backups/\n}\n\n// Creates: .opentasks/backups/auto-2025-01-29T10-30-00.json.gz\n```\n\n## Integrity Checks\n\n### On Export\n- Count all nodes and edges\n- Include checksum in metadata\n- Verify schema compliance\n\n### On Import\n- Validate schema for each entry\n- Check referential integrity\n- Detect duplicate IDs\n- Verify checksum if present\n\n### On Verify\n- Schema validation for all entries\n- Referential integrity (all edge targets exist)\n- No duplicate IDs\n- No orphan edges\n- No cycles in blocking relationships (optional)\n\n## Deliverables\n\n- [ ] Export service (JSON, JSONL formats)\n- [ ] Import service (merge, replace, update modes)\n- [ ] Verify service\n- [ ] Repair service\n- [ ] CLI commands (export, import, verify, repair)\n- [ ] Compression support (gzip)\n- [ ] Streaming support for large datasets\n- [ ] Dry-run mode for import/repair\n- [ ] Auto-backup before dangerous operations\n- [ ] Progress reporting for large operations\n\n## Success Criteria\n\n1. Can export entire graph and re-import to identical state\n2. Import handles conflicts gracefully\n3. Verify detects all common integrity issues\n4. Repair fixes issues without data loss\n5. Large graphs (10k+ nodes) export/import efficiently\n6. Compressed exports reduce storage significantly\n7. Dry-run shows exactly what would happen\n\n## Dependencies\n\n- [[s-cli]] CLI Implementation (for commands)\n- [[s-logging]] Logging System (for operation logging)\n","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-29 18:52:48","updated_at":"2026-01-29 18:52:48","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-2oag","from_type":"spec","to":"s-6w1s","to_type":"spec","type":"depends-on"},{"from":"s-2oag","from_type":"spec","to":"s-7toh","to_type":"spec","type":"depends-on"}],"tags":["backup","data-safety","production","recovery"]}
17
- {"id":"s-3mg3","uuid":"2adca8cc-e9dc-4a56-8176-658e1a386c3f","title":"User Documentation","file_path":"specs/s-3mg3_user_documentation.md","content":"# User Documentation\n\n**Priority:** High\n\n## Overview\n\nCreate comprehensive user-facing documentation. Currently the README is empty and while technical docs exist, there's no onboarding path for new users.\n\n## Goals\n\n1. Enable new users to get started in under 5 minutes\n2. Provide reference documentation for all commands\n3. Explain common workflows and use cases\n4. Document configuration options\n5. Provide troubleshooting guidance\n\n## Documentation Structure\n\n```\nREADME.md # Project overview, quick start\ndocs/\n├── getting-started.md # Installation, first steps\n├── cli-reference.md # All CLI commands\n├── configuration.md # Config file reference\n├── workflows.md # Common usage patterns\n├── providers.md # Provider setup (Beads, Claude Tasks)\n├── troubleshooting.md # Common issues and solutions\n├── api-reference.md # Programmatic API\n└── architecture.md # (existing) System internals\n```\n\n## README.md\n\n```markdown\n# OpenTasks\n\nUniversal task graph for AI agents and humans.\n\n## What is OpenTasks?\n\nOpenTasks is a graph-based task management system designed for:\n- **AI agents** coordinating work via specs and issues\n- **Developers** tracking tasks with dependencies\n- **Teams** managing work across multiple projects\n\n## Quick Start\n\n# Install\nnpm install -g opentasks\n\n# Initialize in your project\ncd my-project\nopentasks init\n\n# Start the daemon\nopentasks daemon start\n\n# Create your first spec\nopentasks create spec --title \"My First Feature\"\n\n# Create an implementing issue\nopentasks create issue --title \"Implement feature\" --implements s-xxxx\n\n# See what's ready to work on\nopentasks ready\n\n## Key Features\n\n- **Graph-based**: Specs, issues, and their relationships\n- **Provider integration**: Sync with Beads, Claude Tasks, and more\n- **Dependency tracking**: Know what's blocked and what's ready\n- **Multi-location**: Work across repos and worktrees\n- **Agent-friendly**: 3-tool MCP interface for AI agents\n\n## Documentation\n\n- [Getting Started](docs/getting-started.md)\n- [CLI Reference](docs/cli-reference.md)\n- [Configuration](docs/configuration.md)\n- [Workflows](docs/workflows.md)\n\n## License\n\nMIT\n```\n\n## Getting Started Guide\n\n### Content\n1. **Installation**\n - npm/yarn install\n - Verify installation (`opentasks --version`)\n\n2. **Initialize a Project**\n - Run `opentasks init`\n - Explain created files\n\n3. **First Workflow**\n - Create a spec\n - Create implementing issues\n - Add blocking relationships\n - Query ready items\n - Close issues\n\n4. **Agent Integration**\n - MCP server setup\n - Tool overview (link, query, annotate)\n\n## CLI Reference\n\nDocument every command with:\n- Synopsis\n- Description\n- Options (with defaults)\n- Examples\n- Exit codes\n- Related commands\n\nExample entry:\n```markdown\n## opentasks create\n\nCreate a new node (spec, issue, or feedback).\n\n### Synopsis\nopentasks create <type> --title <title> [options]\n\n### Arguments\n- `type` - Node type: `spec`, `issue`, or `feedback`\n\n### Options\n- `--title, -t <string>` - Node title (required)\n- `--content, -c <string>` - Node content/description\n- `--status, -s <string>` - Initial status (default: \"open\")\n- `--priority, -p <0-4>` - Priority level (default: 2)\n- `--implements <id>` - Link to spec this implements (issues only)\n- `--blocked-by <ids>` - Comma-separated blocker IDs\n- `--format <json|table>` - Output format (default: table)\n\n### Examples\n# Create a spec\nopentasks create spec --title \"User Authentication\"\n\n# Create an issue implementing a spec\nopentasks create issue --title \"Add login form\" --implements s-abc1\n\n# Create a blocked issue\nopentasks create issue --title \"Deploy to prod\" --blocked-by i-def2,i-ghi3\n\n### Exit Codes\n- 0: Success\n- 1: Invalid arguments\n- 2: Daemon not running\n```\n\n## Configuration Guide\n\nDocument all config options with:\n- Key path\n- Type\n- Default value\n- Description\n- Example\n- Environment variable\n\n## Workflows Guide\n\n### Spec-Driven Development\n1. Write spec describing feature\n2. Break into issues\n3. Link issues to spec\n4. Track completion\n\n### Multi-Agent Coordination\n1. Manager creates work items\n2. Agents query ready work\n3. Agents claim and complete\n4. Blockers cascade updates\n\n### External Provider Sync\n1. Enable Beads provider\n2. Reference beads:// URIs\n3. Automatic materialization\n\n## Troubleshooting Guide\n\n### Categories\n- **Installation issues**\n- **Daemon problems**\n- **Provider errors**\n- **Data issues**\n- **Performance**\n\n### Format\n```markdown\n## Daemon won't start\n\n### Symptoms\n- `opentasks daemon start` hangs or fails\n- \"Address already in use\" error\n\n### Causes\n1. Another daemon already running\n2. Stale socket file\n3. Permission issues\n\n### Solutions\n\n**Check for existing daemon:**\nopentasks daemon status\n\n**Remove stale socket:**\nrm .opentasks/daemon.sock\nopentasks daemon start\n\n**Check permissions:**\nls -la .opentasks/\n# Socket should be writable by current user\n```\n\n## Deliverables\n\n- [ ] README.md with quick start\n- [ ] docs/getting-started.md\n- [ ] docs/cli-reference.md (all commands)\n- [ ] docs/configuration.md (all options)\n- [ ] docs/workflows.md (common patterns)\n- [ ] docs/providers.md (Beads, Claude Tasks setup)\n- [ ] docs/troubleshooting.md (common issues)\n- [ ] docs/api-reference.md (programmatic usage)\n- [ ] Man pages for CLI (optional)\n- [ ] `--help` text for all commands (sync with docs)\n\n## Success Criteria\n\n1. New user can install and create first task in < 5 minutes\n2. All CLI commands are documented with examples\n3. All config options are documented\n4. Common issues have documented solutions\n5. Workflows show real-world usage patterns\n6. API docs enable programmatic integration\n7. Docs are searchable and navigable\n\n## Dependencies\n\n- [[s-cli]] CLI Implementation (documents CLI)\n- [[s-config]] Configuration System (documents config)\n","priority":2,"archived":0,"archived_at":null,"created_at":"2026-01-29 18:52:48","updated_at":"2026-01-29 18:52:48","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-3mg3","from_type":"spec","to":"s-1nsc","to_type":"spec","type":"depends-on"},{"from":"s-3mg3","from_type":"spec","to":"s-6w1s","to_type":"spec","type":"depends-on"}],"tags":["documentation","onboarding","production","user-facing"]}
18
- {"id":"s-70k2","uuid":"2c86187c-c5fd-40b7-a39f-6a08f0ef5bae","title":"E2E Agent Workflow Testing","file_path":"specs/s-70k2_e2e_agent_workflow_testing.md","content":"# E2E Agent Workflow Testing\n\n## Overview\n\nEnd-to-end tests that validate complete agent workflows using the full OpenTasks stack: providers for node creation, 3-tool interface for relationships/queries/feedback, and IPC for communication.\n\n## Goals\n\n1. Validate the core value proposition: spec-driven development with blocking dependencies\n2. Test multi-agent coordination scenarios\n3. Ensure feedback loops work correctly\n4. Provide coverage for provider CRUD operations in realistic workflows\n\n## Architecture\n\n### Test Infrastructure (Phase 5 - Complete)\n\nThe E2E infrastructure is already in place:\n- `tests/e2e/helpers/system-setup.ts` - Full system setup (IPC, storage, graph store)\n- `tests/e2e/helpers/test-agent.ts` - TestAgent wrapper\n- `vitest.e2e.config.ts` - E2E-specific configuration\n\n### Required Enhancements\n\n#### 1. Provider Integration in E2ESystemContext\n\n```typescript\ninterface E2ESystemContext {\n // ... existing fields (rootDir, socketPath, server, store, client)\n \n /** Provider registry for resolving URIs */\n providerRegistry: ProviderRegistry\n \n /** Native provider for creating specs/issues */\n nativeProvider: Provider\n}\n```\n\n#### 2. Extended TestAgent Interface\n\n```typescript\ninterface TestAgent {\n // === Existing 3-tool interface ===\n link(params: LinkParams): Promise<LinkResult>\n query(params: QueryParams): Promise<QueryResult>\n annotate(params: AnnotateParams): Promise<AnnotateResult>\n \n // === Convenience methods (existing) ===\n ready(options?: ReadyOptions): Promise<NodeSummary[]>\n blockers(nodeId: string): Promise<NodeSummary[]>\n blocks(fromId: string, toId: string): Promise<LinkResult>\n implements(issueId: string, specId: string): Promise<LinkResult>\n addFeedback(targetId: string, content: string, type?: FeedbackType): Promise<AnnotateResult>\n \n // === Provider operations (new) ===\n createSpec(title: string, content?: string, options?: CreateOptions): Promise<ProviderNode>\n createIssue(title: string, options?: CreateIssueOptions): Promise<ProviderNode>\n updateNode(id: string, updates: ProviderUpdateInput): Promise<ProviderNode>\n closeIssue(id: string): Promise<ProviderNode>\n getNode(id: string): Promise<ProviderNode | null>\n}\n\ninterface CreateOptions {\n priority?: number\n metadata?: Record<string, unknown>\n}\n\ninterface CreateIssueOptions extends CreateOptions {\n status?: string // default: 'open'\n}\n```\n\n## Test Suites\n\n### Suite 1: Spec-Driven Development (`spec-driven.e2e.test.ts`)\n\nTests the core workflow: create requirements → implement → complete.\n\n#### Test Cases\n\n1. **Complete spec→issue→close cycle**\n - Create spec with requirements\n - Create implementing issue\n - Link issue to spec with 'implements' edge\n - Verify issue appears in ready queue\n - Close issue\n - Verify issue no longer in ready queue\n\n2. **Multiple issues implementing one spec**\n - Create spec\n - Create 3 issues, all implementing the spec\n - Verify all 3 appear in ready queue\n - Close issues one by one\n - Verify ready queue updates correctly\n\n3. **Spec with priority inheritance**\n - Create high-priority spec (priority: 0)\n - Create implementing issue\n - Verify issue inherits or respects priority in queries\n\n4. **Issue without spec (standalone)**\n - Create issue without implements link\n - Verify it appears in ready queue\n - Close and verify removal\n\n### Suite 2: Multi-Agent Coordination (`multi-agent.e2e.test.ts`)\n\nTests blocking dependencies and coordination between agents.\n\n#### Test Cases\n\n1. **Sequential blocking**\n - Agent1 creates foundation issue\n - Agent1 creates dependent issue blocked by foundation\n - Agent2 queries ready → sees only foundation\n - Agent1 closes foundation\n - Agent2 queries ready → now sees dependent\n\n2. **Diamond dependency**\n ```\n A\n / \\\n B C\n \\ /\n D\n ```\n - Create issues A, B, C, D with blocking relationships\n - Verify only A is ready initially\n - Close A → B and C become ready\n - Close B → D still blocked (waiting for C)\n - Close C → D becomes ready\n\n3. **Concurrent issue creation**\n - 3 agents create issues simultaneously\n - Verify all issues have unique IDs\n - Verify all appear in ready queue\n\n4. **Cross-agent blocking**\n - Agent1 creates issue X\n - Agent2 creates issue Y blocked by X\n - Agent1 closes X\n - Agent2 sees Y become ready\n\n5. **Transitive blocking query**\n - Create chain: A blocks B blocks C\n - Query blockers for C with transitive=true\n - Verify both A and B returned\n\n### Suite 3: Feedback Loop (`feedback-loop.e2e.test.ts`)\n\nTests the annotate tool and feedback lifecycle.\n\n#### Test Cases\n\n1. **Add feedback to spec**\n - Create spec\n - Add comment feedback\n - Query feedback on spec\n - Verify feedback content and type\n\n2. **Feedback from implementing issue**\n - Create spec and implementing issue\n - Add feedback to spec with from_id=issue\n - Verify edge created between issue and spec\n\n3. **Resolve and dismiss feedback**\n - Create spec with 3 feedback items\n - Resolve one, dismiss another\n - Query with resolved=false → only unresolved\n - Query with include_dismissed=true → includes dismissed\n\n4. **Feedback types**\n - Add comment, suggestion, request to same spec\n - Query by type filter\n - Verify correct filtering\n\n5. **Anchored feedback**\n - Create spec with multi-line content\n - Add feedback anchored to specific line\n - Verify anchor preserved in query result\n\n## Test Utilities\n\n### Assertion Helpers\n\n```typescript\n// Wait for condition with timeout\nasync function waitForReady(agent: TestAgent, issueId: string, timeout?: number): Promise<void>\n\n// Assert issue is/isn't in ready queue\nasync function expectReady(agent: TestAgent, issueId: string): Promise<void>\nasync function expectNotReady(agent: TestAgent, issueId: string): Promise<void>\n\n// Assert blocking relationship\nasync function expectBlocks(agent: TestAgent, blockerId: string, blockedId: string): Promise<void>\n```\n\n### Fixture Helpers\n\n```typescript\n// Create a spec with standard test content\nasync function createTestSpec(agent: TestAgent, name?: string): Promise<ProviderNode>\n\n// Create an issue with standard test content\nasync function createTestIssue(agent: TestAgent, name?: string): Promise<ProviderNode>\n\n// Create a blocking chain of issues\nasync function createBlockingChain(agent: TestAgent, count: number): Promise<ProviderNode[]>\n```\n\n## Provider Coverage\n\n### Phase 6 Focus: NativeProvider\n- All workflow tests use NativeProvider for node CRUD\n- Tests provider.create, provider.update, provider.get\n\n### Phase 7 (Future): Cross-Provider\n- BeadsProvider integration (requires bd CLI)\n- Cross-provider blocking (native issue blocked by beads task)\n- Provider sync and materialization\n\n## Success Criteria\n\n1. All workflow tests pass with `RUN_FULL_AGENT_TESTS=1`\n2. Tests complete in < 30 seconds total\n3. No flaky tests (run 10x without failure)\n4. Coverage of all 3-tool operations in realistic scenarios\n\n## File Structure\n\n```\ntests/e2e/\n├── helpers/\n│ ├── system-setup.ts # Enhanced with providers\n│ ├── test-agent.ts # Enhanced with provider methods\n│ ├── assertions.ts # New: assertion helpers\n│ ├── fixtures.ts # New: fixture helpers\n│ └── index.ts\n├── infrastructure.e2e.test.ts # Existing (14 tests)\n└── workflows/\n ├── spec-driven.e2e.test.ts\n ├── multi-agent.e2e.test.ts\n └── feedback-loop.e2e.test.ts\n```\n\n## Implementation Order\n\n1. Enhance system-setup.ts with provider registry and native provider\n2. Enhance test-agent.ts with provider operations\n3. Create assertion and fixture helpers\n4. Implement spec-driven.e2e.test.ts\n5. Implement multi-agent.e2e.test.ts\n6. Implement feedback-loop.e2e.test.ts\n7. Update TESTING.md with Phase 6 completion","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-29 19:34:08","updated_at":"2026-01-29 19:34:08","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-70k2","from_type":"spec","to":"s-7u8f","to_type":"spec","type":"references"}],"tags":["e2e","phase-6","testing","workflows"]}
19
- {"id":"s-6uv7","uuid":"c0f4a4e9-e50d-4ca4-8d44-fe6f628048b6","title":"Phase 7: Provider Sync E2E Tests","file_path":"specs/s-6uv7_phase_7_provider_sync_e2e_tests.md","content":"# Phase 7: Provider Sync E2E Tests\n\n## Overview\n\nEnd-to-end tests that validate provider synchronization, cross-provider relationships, and the hydration/materialization lifecycle. Tests real BeadsProvider integration (skipped if `bd` CLI unavailable).\n\n## Goals\n\n1. Validate cross-provider blocking dependencies work correctly\n2. Test hydration and caching behavior with real external providers\n3. Verify background sync refreshes stale materialized nodes\n4. Ensure federated ready queries resolve external blockers correctly\n\n## Prerequisites\n\n- `bd` CLI installed and available in PATH\n- Tests skip gracefully if `bd` unavailable (like Phase 4 integration tests)\n\n## Test Suites\n\n### Suite 1: Hydration and Caching (`hydration.e2e.test.ts`)\n\nTests the HydratingFederatedGraph's on-demand loading and cache management.\n\n#### Test Cases\n\n1. **First access hydration**\n - Create external node via BeadsProvider\n - Access via HydratingFederatedGraph\n - Verify node fetched and cached to SQLite\n - Verify `cached_at` timestamp set\n\n2. **Cache reuse when fresh**\n - Hydrate external node\n - Access again within TTL\n - Verify no provider call made (cache hit)\n\n3. **Stale refresh**\n - Hydrate external node\n - Advance time past TTL (or set short TTL)\n - Access again\n - Verify provider called to refresh\n\n4. **Missing node handling**\n - Request non-existent external node\n - Verify null returned, no crash\n - Verify no cache entry created\n\n5. **Concurrent hydration**\n - Multiple clients request same external node simultaneously\n - Verify only one provider call made\n - All clients receive same data\n\n### Suite 2: Cross-Provider Edges (`cross-provider-edges.e2e.test.ts`)\n\nTests edge resolution across NativeProvider and BeadsProvider.\n\n#### Test Cases\n\n1. **Native blocks external**\n - Create native issue\n - Create Beads task\n - Create 'blocks' edge from native → external\n - Verify edge stored with correct `source` field\n - Query blockers for external node → returns native\n\n2. **External blocks native**\n - Create Beads task\n - Create native issue\n - Create 'blocks' edge from external → native\n - Verify edge traversable in both directions\n\n3. **Edge query from provider**\n - Create Beads task with blocks relationships (via bd CLI)\n - Hydrate the task\n - Verify edges fetched via `RelationshipQueryable`\n - Verify IDs converted to URIs correctly\n\n4. **Transitive cross-provider**\n - Create chain: Native A → Beads B → Native C\n - Query reachable from A with transitive=true\n - Verify both B and C returned\n\n5. **Diamond dependency across providers**\n ```\n Native A\n / \\\n Beads B Native C\n \\ /\n Beads D\n ```\n - Verify ready() behavior as nodes close\n\n### Suite 3: Federated Ready Query (`federated-ready.e2e.test.ts`)\n\nTests the ready query with mixed native and external blockers.\n\n#### Test Cases\n\n1. **Ready with no blockers**\n - Create native issue with no blockers\n - Query ready → issue present\n\n2. **Blocked by open external**\n - Create native issue\n - Create open Beads task that blocks it\n - Query ready → native issue NOT present\n\n3. **Unblocked when external closes**\n - Create native issue blocked by Beads task\n - Close the Beads task (status: 'done')\n - Query ready → native issue NOW present\n\n4. **Multiple external blockers**\n - Create native issue blocked by 2 Beads tasks\n - Close one → still blocked\n - Close both → becomes ready\n\n5. **External status field mapping**\n - Test various external statuses: 'done', 'completed', 'resolved', 'cancelled'\n - Verify all treated as \"closed\" for blocking purposes\n\n6. **Provider filter option**\n - Create issues in both providers\n - Query ready with provider filter\n - Verify only matching provider's issues returned\n\n### Suite 4: Materialization Strategies (`materialization.e2e.test.ts`)\n\nTests different materialization behaviors.\n\n#### Test Cases\n\n1. **On-demand strategy**\n - Configure on-demand materialization\n - Access external node via resolve → NOT materialized\n - Explicit materialize call → NOW cached\n\n2. **Lazy strategy**\n - Configure lazy materialization\n - First resolve access → triggers materialization\n - Subsequent access → uses cache\n\n3. **Eager strategy** (if applicable)\n - Configure eager materialization\n - Register provider\n - Verify known nodes materialized immediately\n\n4. **None strategy**\n - Configure none strategy\n - Always fetches fresh, never caches\n\n5. **Per-provider TTL override**\n - Configure different TTL for BeadsProvider\n - Verify BeadsProvider nodes use custom TTL\n - Verify other providers use default TTL\n\n### Suite 5: Background Sync (`background-sync.e2e.test.ts`)\n\nTests periodic refresh of stale materialized nodes.\n\n#### Test Cases\n\n1. **Start/stop sync**\n - Start background sync\n - Verify isRunning() returns true\n - Stop sync\n - Verify isRunning() returns false\n\n2. **Periodic refresh**\n - Materialize external node\n - Start background sync with short interval\n - Wait for refresh cycle\n - Verify `cached_at` updated\n\n3. **Stale detection**\n - Configure short `staleAfter` threshold\n - Materialize node\n - Wait past threshold\n - Verify node detected as stale\n\n4. **Refresh failure handling**\n - Materialize node\n - Make provider return error on refresh\n - Verify node marked stale, not deleted\n - Verify sync continues for other nodes\n\n5. **No overlapping syncs**\n - Start sync with short interval\n - Make refresh slow\n - Verify second sync waits for first to complete\n\n6. **Provider disconnection**\n - Start sync\n - Unregister/disable provider\n - Verify sync handles gracefully (skips, logs, continues)\n\n## Test Infrastructure\n\n### Helpers Needed\n\n```typescript\n// Check bd CLI availability\nasync function isBdAvailable(): Promise<boolean>\n\n// Create a Beads workspace for testing\nasync function createBeadsWorkspace(dir: string): Promise<void>\n\n// Create a Beads task via bd CLI\nasync function createBeadsTask(workspace: string, title: string, options?: {\n status?: string\n blocks?: string[]\n}): Promise<string> // Returns beads:// URI\n\n// Update Beads task status\nasync function updateBeadsStatus(workspace: string, id: string, status: string): Promise<void>\n\n// Get HydratingFederatedGraph with test configuration\nfunction createTestHydratingGraph(options: {\n store: GraphStore\n registry: ProviderRegistry\n cacheTTL?: number\n}): HydratingFederatedGraph\n```\n\n### System Setup Enhancement\n\nExtend `E2ESystemContext` for Phase 7:\n\n```typescript\ninterface E2ESystemContext {\n // ... existing fields\n \n /** Beads workspace directory (if bd available) */\n beadsWorkspace?: string\n \n /** BeadsProvider instance (if bd available) */\n beadsProvider?: BeadsProvider\n \n /** Hydrating federated graph */\n hydratingGraph: HydratingFederatedGraph\n \n /** Materialization manager */\n materializationManager: MaterializationManager\n}\n```\n\n## File Structure\n\n```\ntests/e2e/\n├── helpers/\n│ ├── beads-helpers.ts # NEW: Beads workspace/CLI helpers\n│ └── ...existing\n└── workflows/\n ├── provider-sync/\n │ ├── hydration.e2e.test.ts\n │ ├── cross-provider-edges.e2e.test.ts\n │ ├── federated-ready.e2e.test.ts\n │ ├── materialization.e2e.test.ts\n │ └── background-sync.e2e.test.ts\n └── ...existing\n```\n\n## Success Criteria\n\n1. All tests pass with `RUN_FULL_AGENT_TESTS=1` when `bd` CLI available\n2. Tests skip gracefully with descriptive message when `bd` unavailable\n3. No flaky tests (10x runs without failure)\n4. Test suite completes in < 60 seconds\n\n## Implementation Order\n\n1. Create beads-helpers.ts with CLI wrappers and workspace setup\n2. Enhance system-setup.ts with BeadsProvider and HydratingGraph\n3. Implement hydration.e2e.test.ts (foundation)\n4. Implement cross-provider-edges.e2e.test.ts\n5. Implement federated-ready.e2e.test.ts\n6. Implement materialization.e2e.test.ts\n7. Implement background-sync.e2e.test.ts\n8. Update TESTING.md with Phase 7 completion","priority":1,"archived":0,"archived_at":null,"created_at":"2026-01-29 20:20:06","updated_at":"2026-01-29 20:20:06","parent_id":null,"parent_uuid":null,"relationships":[{"from":"s-6uv7","from_type":"spec","to":"s-70k2","to_type":"spec","type":"references"}],"tags":["beads","e2e","phase-7","provider-sync","testing"]}
20
- {"id":"s-4qzv","uuid":"6ec7e651-60f3-4c00-8e36-aac87bf94085","title":"Trajectory-Based Tool Tracking via Entire Transcripts","file_path":"specs/s-4qzv_trajectory_based_tool_tracking_via_entire_transcri.md","content":"\n# Trajectory-Based Tool Tracking via Entire Transcripts\n\n> Extends: Entire Integration, SkillTracker\n>\n> Tags: entire, tracking, skill-tracker, claude-tasks, trajectory, agent-session-parser\n>\n> Dependencies: [`agent-session-parser`](https://www.npmjs.com/package/agent-session-parser) (npm)\n\n## Problem\n\nOpenTasks has two tracking gaps:\n\n### Gap 1: SkillTracker is blind to MCP tools\n\nThe current `SkillTracker` (`src/tracking/skill-tracker.ts`) records opentasks daemon IPC calls inline — it hooks into `tools.link`, `tools.query`, `tools.annotate`, `tools.task` handlers in `src/daemon/methods/tools.ts`. But:\n\n- The **sudocode MCP tools** Claude actually calls (`upsert_issue`, `show_spec`, `list_issues`, etc.) go directly to the sudocode MCP server. They never touch the opentasks daemon.\n- The SkillTracker only fires when `_sessionId` is explicitly passed in the IPC request params.\n- Result: the SkillTracker misses all MCP tool usage, which is the primary way agents interact with specs/issues.\n\n### Gap 2: Claude's ephemeral tools are untracked\n\nClaude Code's native tools — `TaskCreate`, `TaskUpdate`, `TaskList`, `EnterPlanMode`, `ExitPlanMode` — are ephemeral, in-session constructs. They:\n\n- Don't persist across conversations\n- Don't emit events to external systems\n- Don't go through any opentasks chokepoint\n- Contain valuable planning/coordination data (task subjects, statuses, dependencies, plan file paths)\n\n### Why Entire transcripts solve both\n\nEntire captures the **complete session transcript** as JSONL — every `tool_use` and `tool_result` block, regardless of which system handled it. This is the single source of truth for what tools an agent actually used during a session.\n\n## Solution: Transcript-Driven Tool Extraction\n\nSupplement the current inline SkillTracker with a **post-hoc transcript parser** that runs when an Entire session ends. Uses the [`agent-session-parser`](https://www.npmjs.com/package/agent-session-parser) package for transcript parsing — a TypeScript port of the Entire CLI's agent-specific parsers.\n\n### Why agent-session-parser\n\nThe Entire CLI stores transcripts in agent-specific formats:\n\n| Agent | Format | Inner structure |\n|---|---|---|\n| Claude Code | JSONL (one JSON per line) | Anthropic API message format with `tool_use` content blocks |\n| Gemini CLI | Single JSON object | `messages` array with `toolCalls` arrays |\n| Cursor | SQLite (planned) | — |\n| Aider | Markdown (planned) | — |\n\nThe filename is always `full.jsonl` but the content varies. The `metadata.json` alongside it has an `agent` field for dispatch. The Entire CLI's Go parsing logic is not exportable (Go-only, no `--json` flag on `entire explain`), so `agent-session-parser` ports the type definitions and parsing logic to TypeScript.\n\n**What the package provides (we don't need to build):**\n- `claude.parseFromString(content)` → `TranscriptLine[]`\n- `claude.ContentBlock` type with `type`, `name`, `input`, `tool_use_id`\n- `detectAgentTypeFromContent()` → agent dispatch\n- `claude.extractSpawnedAgentIds()` → subagent tracking\n- `claude.calculateTotalTokenUsage()` → token accounting\n- `claude.filterAfterUUID()` / `truncateAtUUID()` → checkpoint-scoped parsing\n- Streaming dedup (handles multiple JSONL rows per API message via `message.id`)\n- Chunking/reassembly for >50MB transcripts\n- Gemini CLI support out of the box\n\n**What we build on top (our value-add):**\n- Tool call categorization by system (claude-native vs mcp-sudocode vs opentasks-ipc)\n- Claude task state reconstruction from `TaskCreate`/`TaskUpdate` sequences\n- Plan mode transition tracking\n- SkillTracker backfill\n- Graph materialization (session metadata enrichment, optional external nodes)\n\n### Architecture\n\n```\nEntire session ends\n → EntireWatcher emits 'ended' event\n → TranscriptExtractor runs BEFORE EntireAutoLinker finalizes\n → Fetches transcript via git:\n git show entire/checkpoints/v1:<shard>/<id>/1/full.jsonl\n git show entire/checkpoints/v1:<shard>/<id>/1/metadata.json\n → Reads metadata.json to get agent type\n → Dispatches to agent-session-parser:\n claude.parseFromString() or gemini.parseTranscript()\n → Extracts tool_use blocks, categorizes by system\n → Feeds into:\n 1. SkillTracker records (backfill MCP + claude-native tool usage)\n 2. Claude task state reconstruction\n 3. Plan mode transition log\n → EntireAutoLinker then finalizes:\n - registry.remove(sessionId) captures complete summary\n - Attaches to session ExternalNode metadata\n```\n\n### Ordering constraint\n\nThe TranscriptExtractor **must** run before the EntireAutoLinker's `ended` handler, because the linker calls `skillTrackerRegistry.remove(sessionId)` which finalizes the summary. Backfilled records must be in the tracker before that call.\n\n```typescript\n// In lifecycle.ts or location-state.ts:\nentireWatcher.onSessionEvent(async (event) => {\n // Step 1: Backfill from transcript (BEFORE linker finalizes)\n if (event.type === 'ended' && transcriptExtractor) {\n await transcriptExtractor.extract(event);\n }\n\n // Step 2: Linker finalizes (calls registry.remove())\n await entireLinker.handleSessionEvent(event);\n});\n```\n\n### Component: TranscriptExtractor\n\n```typescript\nimport { claude, gemini, detectAgentTypeFromContent } from 'agent-session-parser';\nimport type { claude as claudeTypes } from 'agent-session-parser';\n\ninterface TranscriptExtractorConfig {\n store: GraphStore;\n flushManager: DaemonFlushManager;\n skillTrackerRegistry?: SkillTrackerRegistry;\n}\n\ninterface ExtractedToolCall {\n /** Tool name as it appears in the transcript */\n toolName: string;\n\n /** tool_use block ID */\n toolUseId: string;\n\n /** Normalized category */\n category: 'claude-native' | 'mcp-sudocode' | 'mcp-other' | 'unknown';\n\n /** Input parameters */\n input: Record<string, unknown>;\n\n /** Output/result content (from matching tool_result) */\n output?: string;\n\n /** Whether the call succeeded (from tool_result.is_error) */\n success: boolean;\n\n /** Position in transcript (line index) */\n lineIndex: number;\n}\n\ninterface TranscriptExtractionResult {\n sessionId: string;\n agentType: string;\n\n /** All extracted tool calls, chronological */\n toolCalls: ExtractedToolCall[];\n\n /** Claude native task state reconstructed from tool calls */\n claudeTasks?: ReconstructedClaudeTaskState;\n\n /** Plan mode transitions */\n planModeTransitions: PlanModeTransition[];\n\n /** Token usage (from agent-session-parser) */\n tokenUsage: TokenUsage;\n\n /** Spawned subagent IDs */\n subagentIds: Map<string, string>;\n}\n```\n\n### Tool Categorization\n\nCategorize by `tool_use` block `name` field:\n\n| Pattern | Category | What to extract |\n|---|---|---|\n| `TaskCreate` | claude-native | `input.subject`, `input.description`, `input.activeForm` |\n| `TaskUpdate` | claude-native | `input.taskId`, `input.status`, `input.owner`, `input.addBlockedBy` |\n| `TaskList` | claude-native | (query only) |\n| `TaskGet` | claude-native | `input.taskId` |\n| `EnterPlanMode` | claude-native | transition timestamp |\n| `ExitPlanMode` | claude-native | `input.plan` (plan content) |\n| `mcp__plugin_sudocode_sudocode__*` | mcp-sudocode | spec/issue CRUD, links, feedback |\n| `mcp__*` | mcp-other | tool name, success/failure |\n| Everything else | unknown | tool name only |\n\n### Transcript JSONL Format (Claude Code)\n\nEach line is a JSON object following the Anthropic API message format:\n\n```jsonl\n{\"type\":\"assistant\",\"uuid\":\"...\",\"message\":{\"id\":\"msg_...\",\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_01...\",\"name\":\"TaskCreate\",\"input\":{\"subject\":\"Fix auth bug\",\"description\":\"...\",\"activeForm\":\"Fixing auth bug\"}}],\"usage\":{...}}}\n{\"type\":\"user\",\"uuid\":\"...\",\"message\":{\"content\":[{\"type\":\"tool_result\",\"tool_use_id\":\"toolu_01...\",\"content\":\"Task #1 created successfully: Fix auth bug\"}]}}\n```\n\n**Streaming detail**: A single API response may be split across multiple JSONL lines chained by `parentUuid`. The `agent-session-parser` handles deduplication via `message.id`.\n\n**Additional JSONL entry types** (filtered out during parsing):\n- `system` — compaction events, turn duration\n- `progress` — subagent streaming\n- `queue-operation` — task queue events\n- `file-history-snapshot` — file backup snapshots\n\n### Transcript Access\n\nTwo paths to get `full.jsonl`:\n\n1. **Git direct** (preferred, no CLI dependency):\n ```\n git show entire/checkpoints/v1:<id[:2]>/<id[2:]>/1/full.jsonl\n git show entire/checkpoints/v1:<id[:2]>/<id[2:]>/1/metadata.json\n ```\n\n2. **Entire CLI** (fallback):\n ```\n entire explain --raw-transcript --checkpoint <id>\n ```\n\nThe metadata.json contains the `agent` field for dispatch:\n```json\n{\"agent\": \"Claude Code\", \"checkpoint_id\": \"8a513f56ed70\", ...}\n```\n\n### Claude Task State Reconstruction\n\nFrom sequential `TaskCreate`/`TaskUpdate` calls, reconstruct the ephemeral task list state.\n\n**Parsing TaskCreate results**: The `tool_result` content is a plain string like `\"Task #1 created successfully: Fix auth bug\"`. Extract task ID via regex: `/Task #(\\d+) created/`.\n\n**Parsing TaskUpdate results**: The `tool_result` content is `\"Updated task #1 status\"` or similar.\n\n```typescript\ninterface ReconstructedClaudeTaskState {\n /** Tasks that existed during the session */\n tasks: ReconstructedTask[];\n\n /** Final task list snapshot at session end */\n finalSnapshot: ReconstructedTask[];\n}\n\ninterface ReconstructedTask {\n /** Task ID (from TaskCreate response or TaskUpdate params) */\n taskId: string;\n\n /** Subject (from TaskCreate input) */\n subject: string;\n\n /** Description (from TaskCreate input) */\n description?: string;\n\n /** Status transitions in order */\n statusHistory: Array<{ status: string; lineIndex: number }>;\n\n /** Final status */\n finalStatus: 'pending' | 'in_progress' | 'completed';\n\n /** Owner if assigned via TaskUpdate */\n owner?: string;\n\n /** Dependencies from TaskUpdate addBlocks/addBlockedBy */\n blocks?: string[];\n blockedBy?: string[];\n}\n```\n\n### Plan Mode Tracking\n\n```typescript\ninterface PlanModeTransition {\n type: 'enter' | 'exit';\n lineIndex: number;\n\n /** For exit: the plan content from input.plan */\n planContent?: string;\n\n /** For exit: whether user approved (inferred from subsequent tool calls) */\n approved?: boolean;\n}\n```\n\n## Integration Points\n\n### 1. Trigger: Entire session 'ended' event\n\nWire into the existing `entireWatcher.onSessionEvent`, running **before** the linker:\n\n```typescript\n// lifecycle.ts or location-state.ts\nentireWatcher.onSessionEvent(async (event) => {\n if (event.type === 'ended' && transcriptExtractor) {\n await transcriptExtractor.extract(event); // backfill first\n }\n await entireLinker.handleSessionEvent(event); // finalize second\n});\n```\n\n### 2. Transcript retrieval\n\nFor each checkpoint in `session.checkpoints[]`:\n```typescript\nconst metadata = JSON.parse(\n execSync(`git show entire/checkpoints/v1:${shard}/${id}/1/metadata.json`).toString()\n);\nconst transcript = execSync(\n `git show entire/checkpoints/v1:${shard}/${id}/1/full.jsonl`\n).toString();\n```\n\n### 3. Agent dispatch via agent-session-parser\n\n```typescript\nimport { claude, gemini, detectAgentTypeFromContent } from 'agent-session-parser';\n\nconst agentType = metadata.agent; // \"Claude Code\" or \"Gemini CLI\"\n// Or auto-detect: detectAgentTypeFromContent(transcript)\n\nif (agentType === 'Claude Code' || !agentType) {\n const lines = claude.parseFromString(transcript);\n return extractClaudeToolCalls(lines);\n} else if (agentType === 'Gemini CLI') {\n const parsed = gemini.parseTranscript(transcript);\n return extractGeminiToolCalls(parsed);\n}\n```\n\n### 4. SkillTracker backfill\n\nFeed extracted tool calls into the existing registry before it's finalized:\n\n```typescript\nconst tracker = skillTrackerRegistry.getOrCreate(sessionId);\n\nfor (const call of result.toolCalls) {\n tracker.record({\n skill: call.toolName,\n success: call.success,\n operation: categorizeOperation(call),\n targets: extractTargets(call),\n });\n}\n// EntireAutoLinker then calls registry.remove(sessionId) to finalize\n```\n\n### 5. Session metadata enrichment\n\nAttach extraction results to the session's ExternalNode:\n\n```typescript\nawait store.updateNode(sessionNodeId, {\n metadata: {\n toolUsage: result.toolCalls.length,\n toolCategories: countByCategory(result.toolCalls),\n claudeTasks: result.claudeTasks?.finalSnapshot,\n planTransitions: result.planModeTransitions,\n tokenUsage: result.tokenUsage,\n subagentIds: Object.fromEntries(result.subagentIds),\n },\n});\n```\n\n### 6. Optional: Claude task graph nodes\n\nMaterialize reconstructed Claude tasks as opentasks external nodes:\n\n```\nclaude://session-id/task-1 (ExternalNode, type: 'external', source: 'claude-tasks')\n --worked-on→ entire://session/...\n --implements→ i-xxxx (if correlatable)\n```\n\nThis is optional for v1 — session metadata enrichment (point 5) may be sufficient.\n\n## Migration: SkillTracker Inline vs Transcript\n\n### Option A: Keep both (recommended)\n- Inline tracker: real-time, available during session, but only sees daemon IPC\n- Transcript tracker: post-hoc, complete coverage, runs on session end\n- Merge on session end: transcript results supplement inline records before `registry.remove()`\n\n### Option B: Transcript-only\n- Remove inline tracking from `tools.ts` handlers\n- All tracking flows through Entire transcript parsing\n- Simpler code, but no real-time visibility during session\n- Hard dependency on Entire being installed\n\n**Recommendation: Option A.** Keep inline tracking for real-time use. Transcript parsing backfills the gaps (MCP tools, Claude native tools). The `registry.remove()` call captures the merged result.\n\n## File Manifest\n\n| File | Action | Description |\n|---|---|---|\n| `package.json` | Edit | Add `agent-session-parser` dependency |\n| `src/tracking/transcript-extractor.ts` | Create | Orchestrator: fetches transcript, dispatches to parser, categorizes tool calls |\n| `src/tracking/claude-tool-categorizer.ts` | Create | Categorizes Claude tool_use blocks by system, extracts relevant fields |\n| `src/tracking/claude-task-reconstructor.ts` | Create | Replays TaskCreate/TaskUpdate sequence to reconstruct task state |\n| `src/tracking/plan-mode-tracker.ts` | Create | Extracts EnterPlanMode/ExitPlanMode transitions |\n| `src/tracking/__tests__/transcript-extractor.test.ts` | Create | Integration tests with sample JSONL fixtures |\n| `src/tracking/__tests__/claude-tool-categorizer.test.ts` | Create | Categorization tests for all tool families |\n| `src/tracking/__tests__/claude-task-reconstructor.test.ts` | Create | Task state reconstruction tests |\n| `src/tracking/__tests__/plan-mode-tracker.test.ts` | Create | Plan mode transition tests |\n| `src/daemon/lifecycle.ts` | Edit | Wire transcript extractor into session ended event (before linker) |\n| `src/daemon/location-state.ts` | Edit | Create transcript extractor in location init |\n| `src/tracking/index.ts` | Edit | Export new types and functions |\n\n## Resolved Questions\n\n1. **Transcript format**: Confirmed. Claude Code uses JSONL with Anthropic API message format. `tool_use` blocks have `type`, `id`, `name`, `input`. Gemini uses single JSON with `messages` array. The `agent-session-parser` package handles both.\n\n2. **Agent-specific formats**: Confirmed. `full.jsonl` is NOT uniform — it's agent-specific content in a uniform filename. `metadata.json` has the `agent` field for dispatch. `agent-session-parser` provides `detectAgentTypeFromContent()` as fallback.\n\n3. **Checkpoint vs session transcript**: Parse per-checkpoint. Each checkpoint's `full.jsonl` contains the cumulative transcript up to that commit. Use `metadata.json.checkpoint_transcript_start` to scope to checkpoint-specific content if needed.\n\n## Open Questions\n\n1. **Correlation with opentasks issues**: If a Claude `TaskCreate` subject matches an opentasks issue title, should we auto-link them? What confidence threshold?\n2. **Graph materialization**: Should reconstructed Claude tasks become external nodes in the graph, or just live as session metadata? Session metadata is simpler for v1.\n3. **Gemini tool categorization**: Gemini uses different tool names (`write_file`, `edit_file`). Do we need a Gemini-specific categorizer, or is Claude-only sufficient for v1?\n","priority":1,"archived":0,"archived_at":null,"created_at":"2026-02-19 01:02:40","updated_at":"2026-02-19 20:43:47","parent_id":null,"parent_uuid":null,"relationships":[],"tags":["agent-session-parser","claude-tasks","entire","skill-tracker","tracking","trajectory"]}
21
- {"id":"s-2i2s","uuid":"675c839e-b18f-466a-8580-540690295176","title":"Federation & Sync Strengthening","file_path":"specs/s-2i2s_federation_sync_strengthening.md","content":"Strengthen the cross-location federation and sync capabilities of OpenTasks across three areas:\n\n1. **Global Provider Watch** — Project-level daemons get real-time notifications when nodes change in the global `~/.opentasks/` graph. The global provider implements the `Watchable` trait via IPC subscription protocol.\n\n2. **Cross-Location Blocker Resolution** — `ready()` in a project graph correctly considers blockers from the global graph by wiring the `NodeResolver` through to the `QueryEngine` and updating `expandedReady()` to cascade cross-location blockers.\n\n3. **Git Graph Sync** — Automated commit/push/pull of `graph.jsonl` via git for cross-machine replication, with configurable auto-commit (default manual) and a custom merge driver for conflict resolution.\n\nSee plan: `/Users/alexngai/.claude/plans/sharded-snacking-hinton.md`","priority":1,"archived":0,"archived_at":null,"created_at":"2026-02-25 20:54:45","updated_at":"2026-02-25 20:54:45","parent_id":null,"parent_uuid":null,"relationships":[],"tags":["architecture","federation","sync"]}
22
- {"id":"s-9w1q","uuid":"e98fa139-5c30-4f0e-8d45-3849fa66d406","title":"Phase 1: Global Provider Watch","file_path":"specs/s-9w1q_phase_1_global_provider_watch.md","content":"Project-level daemons get real-time notifications when nodes change in the global `~/.opentasks/` graph.\n\n## Requirements\n\n1. Extend `IPCClient` to handle server-push notifications (messages with `id: null`)\n2. Add `watch.subscribe`/`watch.unsubscribe` IPC methods to the daemon\n3. Add `broadcastNotification()` to `IPCServer` to push change events to subscribed clients\n4. Global provider implements `Watchable` trait — converts IPC notifications to `ProviderChangeEvent`\n5. Register global provider in project-level daemons (guarded against self-registration on global daemon)\n\n## Key Files\n- `src/daemon/ipc.ts` — IPCClient notification handler, IPCServer broadcast\n- `src/daemon/methods/watch.ts` — New file for subscription methods\n- `src/providers/global.ts` — Watchable trait implementation\n- `src/daemon/lifecycle.ts` — Registration and wiring\n- `src/config/schema.ts` — `providers.global.enabled` config","priority":1,"archived":0,"archived_at":null,"created_at":"2026-02-25 20:54:53","updated_at":"2026-02-25 20:54:53","parent_id":"s-2i2s","parent_uuid":"675c839e-b18f-466a-8580-540690295176","relationships":[],"tags":["federation","ipc","watch"]}
23
- {"id":"s-9dem","uuid":"2648e07b-43f3-434f-9f35-836014b403b0","title":"Phase 2: Cross-Location Blocker Resolution","file_path":"specs/s-9dem_phase_2_cross_location_blocker_resolution.md","content":"`ready()` in a project graph correctly considers blockers from the global graph (and vice versa).\n\n## Requirements\n\n1. Wire `NodeResolver` through to `QueryEngine` via a late-binding `setNodeResolver()` on `GraphStore`\n2. In daemon lifecycle, create a resolver that delegates to `providerStore.resolveNode()` and set it on the store\n3. Update `expandedReady()` to post-filter locally-ready tasks against cross-location blockers\n4. With Phase 1 watch in place, materialized blocker nodes auto-refresh so cached status stays current\n\n## Key Files\n- `src/graph/store.ts` — Add `setNodeResolver()`\n- `src/graph/query.ts` — Already supports `NodeResolver`, just needs wiring\n- `src/daemon/lifecycle.ts` — Wire resolver after provider registration\n- `src/daemon/location-state.ts` — Same for multi-location mode\n- `src/graph/expansion.ts` — Post-filter in `expandedReady()`","priority":1,"archived":0,"archived_at":null,"created_at":"2026-02-25 20:54:58","updated_at":"2026-02-25 20:54:58","parent_id":"s-2i2s","parent_uuid":"675c839e-b18f-466a-8580-540690295176","relationships":[],"tags":["blockers","federation","query"]}
24
- {"id":"s-d4xu","uuid":"891ab6be-3d35-46f7-9fe5-66276da3207a","title":"Phase 3: Git Graph Sync","file_path":"specs/s-d4xu_phase_3_git_graph_sync.md","content":"Automated commit/push/pull of `graph.jsonl` via git for cross-machine replication.\n\n## Requirements\n\n1. New `GitGraphSyncer` module with commit/push/pull/sync operations\n2. Hook into daemon flush lifecycle — optionally auto-commit after flush\n3. Add `reload()` to `GraphStore` for loading external changes after pull\n4. Wire file watcher to trigger reload on external `graph.jsonl` changes\n5. Config schema extension for `sync.git` settings\n6. Default is manual sync (`autoCommit: false`), configurable to auto\n\n## Key Files\n- `src/graph/git-graph-syncer.ts` — New module\n- `src/core/merge-driver.ts` — Reused as-is (already complete)\n- `src/graph/store.ts` — Add `reload()`\n- `src/daemon/location-state.ts` — Init syncer, hook into flush\n- `src/daemon/lifecycle.ts` — Wire file watcher reload\n- `src/config/schema.ts` — `sync.git` config","priority":2,"archived":0,"archived_at":null,"created_at":"2026-02-25 20:55:02","updated_at":"2026-02-25 20:55:02","parent_id":"s-2i2s","parent_uuid":"675c839e-b18f-466a-8580-540690295176","relationships":[],"tags":["git","replication","sync"]}