@lwelliott/cortex-cli 1.0.3

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 (335) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/dist/cli/audit.d.ts +3 -0
  4. package/dist/cli/audit.d.ts.map +1 -0
  5. package/dist/cli/audit.js +78 -0
  6. package/dist/cli/audit.js.map +1 -0
  7. package/dist/cli/doc.d.ts +3 -0
  8. package/dist/cli/doc.d.ts.map +1 -0
  9. package/dist/cli/doc.js +291 -0
  10. package/dist/cli/doc.js.map +1 -0
  11. package/dist/cli/entity.d.ts +3 -0
  12. package/dist/cli/entity.d.ts.map +1 -0
  13. package/dist/cli/entity.js +135 -0
  14. package/dist/cli/entity.js.map +1 -0
  15. package/dist/cli/format-selector.d.ts +30 -0
  16. package/dist/cli/format-selector.d.ts.map +1 -0
  17. package/dist/cli/format-selector.js +101 -0
  18. package/dist/cli/format-selector.js.map +1 -0
  19. package/dist/cli/formatters/audit-formatter.d.ts +33 -0
  20. package/dist/cli/formatters/audit-formatter.d.ts.map +1 -0
  21. package/dist/cli/formatters/audit-formatter.js +204 -0
  22. package/dist/cli/formatters/audit-formatter.js.map +1 -0
  23. package/dist/cli/formatters/json.d.ts +10 -0
  24. package/dist/cli/formatters/json.d.ts.map +1 -0
  25. package/dist/cli/formatters/json.js +31 -0
  26. package/dist/cli/formatters/json.js.map +1 -0
  27. package/dist/cli/formatters/markdown.d.ts +12 -0
  28. package/dist/cli/formatters/markdown.d.ts.map +1 -0
  29. package/dist/cli/formatters/markdown.js +341 -0
  30. package/dist/cli/formatters/markdown.js.map +1 -0
  31. package/dist/cli/formatters/quiet.d.ts +15 -0
  32. package/dist/cli/formatters/quiet.d.ts.map +1 -0
  33. package/dist/cli/formatters/quiet.js +75 -0
  34. package/dist/cli/formatters/quiet.js.map +1 -0
  35. package/dist/cli/formatters/table.d.ts +11 -0
  36. package/dist/cli/formatters/table.d.ts.map +1 -0
  37. package/dist/cli/formatters/table.js +726 -0
  38. package/dist/cli/formatters/table.js.map +1 -0
  39. package/dist/cli/graph.d.ts +3 -0
  40. package/dist/cli/graph.d.ts.map +1 -0
  41. package/dist/cli/graph.js +209 -0
  42. package/dist/cli/graph.js.map +1 -0
  43. package/dist/cli/index.d.ts +3 -0
  44. package/dist/cli/index.d.ts.map +1 -0
  45. package/dist/cli/index.js +144 -0
  46. package/dist/cli/index.js.map +1 -0
  47. package/dist/cli/lesson.d.ts +3 -0
  48. package/dist/cli/lesson.d.ts.map +1 -0
  49. package/dist/cli/lesson.js +85 -0
  50. package/dist/cli/lesson.js.map +1 -0
  51. package/dist/cli/project-resolver.d.ts +17 -0
  52. package/dist/cli/project-resolver.d.ts.map +1 -0
  53. package/dist/cli/project-resolver.js +25 -0
  54. package/dist/cli/project-resolver.js.map +1 -0
  55. package/dist/cli/project.d.ts +12 -0
  56. package/dist/cli/project.d.ts.map +1 -0
  57. package/dist/cli/project.js +169 -0
  58. package/dist/cli/project.js.map +1 -0
  59. package/dist/cli/relation.d.ts +3 -0
  60. package/dist/cli/relation.d.ts.map +1 -0
  61. package/dist/cli/relation.js +128 -0
  62. package/dist/cli/relation.js.map +1 -0
  63. package/dist/cli/review.d.ts +3 -0
  64. package/dist/cli/review.d.ts.map +1 -0
  65. package/dist/cli/review.js +131 -0
  66. package/dist/cli/review.js.map +1 -0
  67. package/dist/cli/router.d.ts +38 -0
  68. package/dist/cli/router.d.ts.map +1 -0
  69. package/dist/cli/router.js +49 -0
  70. package/dist/cli/router.js.map +1 -0
  71. package/dist/cli/schema.d.ts +3 -0
  72. package/dist/cli/schema.d.ts.map +1 -0
  73. package/dist/cli/schema.js +94 -0
  74. package/dist/cli/schema.js.map +1 -0
  75. package/dist/cli/sprint.d.ts +12 -0
  76. package/dist/cli/sprint.d.ts.map +1 -0
  77. package/dist/cli/sprint.js +200 -0
  78. package/dist/cli/sprint.js.map +1 -0
  79. package/dist/cli/task.d.ts +12 -0
  80. package/dist/cli/task.d.ts.map +1 -0
  81. package/dist/cli/task.js +252 -0
  82. package/dist/cli/task.js.map +1 -0
  83. package/dist/cli/test-report.d.ts +3 -0
  84. package/dist/cli/test-report.d.ts.map +1 -0
  85. package/dist/cli/test-report.js +134 -0
  86. package/dist/cli/test-report.js.map +1 -0
  87. package/dist/cli/validate.d.ts +8 -0
  88. package/dist/cli/validate.d.ts.map +1 -0
  89. package/dist/cli/validate.js +86 -0
  90. package/dist/cli/validate.js.map +1 -0
  91. package/dist/config/config.d.ts +18 -0
  92. package/dist/config/config.d.ts.map +1 -0
  93. package/dist/config/config.js +70 -0
  94. package/dist/config/config.js.map +1 -0
  95. package/dist/config/registry.d.ts +52 -0
  96. package/dist/config/registry.d.ts.map +1 -0
  97. package/dist/config/registry.js +193 -0
  98. package/dist/config/registry.js.map +1 -0
  99. package/dist/config/resolver.d.ts +26 -0
  100. package/dist/config/resolver.d.ts.map +1 -0
  101. package/dist/config/resolver.js +87 -0
  102. package/dist/config/resolver.js.map +1 -0
  103. package/dist/db/builtin-schema-constants.d.ts +109 -0
  104. package/dist/db/builtin-schema-constants.d.ts.map +1 -0
  105. package/dist/db/builtin-schema-constants.js +32 -0
  106. package/dist/db/builtin-schema-constants.js.map +1 -0
  107. package/dist/db/connection.d.ts +34 -0
  108. package/dist/db/connection.d.ts.map +1 -0
  109. package/dist/db/connection.js +140 -0
  110. package/dist/db/connection.js.map +1 -0
  111. package/dist/db/generated/schema-types.d.ts +134 -0
  112. package/dist/db/generated/schema-types.d.ts.map +1 -0
  113. package/dist/db/generated/schema-types.js +31 -0
  114. package/dist/db/generated/schema-types.js.map +1 -0
  115. package/dist/db/locks.d.ts +106 -0
  116. package/dist/db/locks.d.ts.map +1 -0
  117. package/dist/db/locks.js +211 -0
  118. package/dist/db/locks.js.map +1 -0
  119. package/dist/db/migration.d.ts +65 -0
  120. package/dist/db/migration.d.ts.map +1 -0
  121. package/dist/db/migration.js +233 -0
  122. package/dist/db/migration.js.map +1 -0
  123. package/dist/db/retry.d.ts +26 -0
  124. package/dist/db/retry.d.ts.map +1 -0
  125. package/dist/db/retry.js +71 -0
  126. package/dist/db/retry.js.map +1 -0
  127. package/dist/db/schema.d.ts +21 -0
  128. package/dist/db/schema.d.ts.map +1 -0
  129. package/dist/db/schema.js +830 -0
  130. package/dist/db/schema.js.map +1 -0
  131. package/dist/models/common.d.ts +27 -0
  132. package/dist/models/common.d.ts.map +1 -0
  133. package/dist/models/common.js +4 -0
  134. package/dist/models/common.js.map +1 -0
  135. package/dist/models/document.d.ts +89 -0
  136. package/dist/models/document.d.ts.map +1 -0
  137. package/dist/models/document.js +5 -0
  138. package/dist/models/document.js.map +1 -0
  139. package/dist/models/entity-model.d.ts +30 -0
  140. package/dist/models/entity-model.d.ts.map +1 -0
  141. package/dist/models/entity-model.js +99 -0
  142. package/dist/models/entity-model.js.map +1 -0
  143. package/dist/models/entity.d.ts +88 -0
  144. package/dist/models/entity.d.ts.map +1 -0
  145. package/dist/models/entity.js +5 -0
  146. package/dist/models/entity.js.map +1 -0
  147. package/dist/models/graph-schema.d.ts +43 -0
  148. package/dist/models/graph-schema.d.ts.map +1 -0
  149. package/dist/models/graph-schema.js +76 -0
  150. package/dist/models/graph-schema.js.map +1 -0
  151. package/dist/models/project.d.ts +49 -0
  152. package/dist/models/project.d.ts.map +1 -0
  153. package/dist/models/project.js +18 -0
  154. package/dist/models/project.js.map +1 -0
  155. package/dist/models/relation.d.ts +35 -0
  156. package/dist/models/relation.d.ts.map +1 -0
  157. package/dist/models/relation.js +212 -0
  158. package/dist/models/relation.js.map +1 -0
  159. package/dist/models/review.d.ts +86 -0
  160. package/dist/models/review.d.ts.map +1 -0
  161. package/dist/models/review.js +5 -0
  162. package/dist/models/review.js.map +1 -0
  163. package/dist/models/sprint.d.ts +38 -0
  164. package/dist/models/sprint.d.ts.map +1 -0
  165. package/dist/models/sprint.js +4 -0
  166. package/dist/models/sprint.js.map +1 -0
  167. package/dist/models/task.d.ts +75 -0
  168. package/dist/models/task.d.ts.map +1 -0
  169. package/dist/models/task.js +4 -0
  170. package/dist/models/task.js.map +1 -0
  171. package/dist/models/validation.d.ts +25 -0
  172. package/dist/models/validation.d.ts.map +1 -0
  173. package/dist/models/validation.js +4 -0
  174. package/dist/models/validation.js.map +1 -0
  175. package/dist/services/audit-check-service.d.ts +16 -0
  176. package/dist/services/audit-check-service.d.ts.map +1 -0
  177. package/dist/services/audit-check-service.js +137 -0
  178. package/dist/services/audit-check-service.js.map +1 -0
  179. package/dist/services/audit-checks/dangling-relations-check.d.ts +7 -0
  180. package/dist/services/audit-checks/dangling-relations-check.d.ts.map +1 -0
  181. package/dist/services/audit-checks/dangling-relations-check.js +52 -0
  182. package/dist/services/audit-checks/dangling-relations-check.js.map +1 -0
  183. package/dist/services/audit-checks/duplicate-ids-check.d.ts +4 -0
  184. package/dist/services/audit-checks/duplicate-ids-check.d.ts.map +1 -0
  185. package/dist/services/audit-checks/duplicate-ids-check.js +49 -0
  186. package/dist/services/audit-checks/duplicate-ids-check.js.map +1 -0
  187. package/dist/services/audit-checks/fix-engine.d.ts +24 -0
  188. package/dist/services/audit-checks/fix-engine.d.ts.map +1 -0
  189. package/dist/services/audit-checks/fix-engine.js +114 -0
  190. package/dist/services/audit-checks/fix-engine.js.map +1 -0
  191. package/dist/services/audit-checks/fr-nfr-to-fs-nfs-check.d.ts +9 -0
  192. package/dist/services/audit-checks/fr-nfr-to-fs-nfs-check.d.ts.map +1 -0
  193. package/dist/services/audit-checks/fr-nfr-to-fs-nfs-check.js +56 -0
  194. package/dist/services/audit-checks/fr-nfr-to-fs-nfs-check.js.map +1 -0
  195. package/dist/services/audit-checks/fs-nfs-to-sad-ad-qa-check.d.ts +10 -0
  196. package/dist/services/audit-checks/fs-nfs-to-sad-ad-qa-check.d.ts.map +1 -0
  197. package/dist/services/audit-checks/fs-nfs-to-sad-ad-qa-check.js +57 -0
  198. package/dist/services/audit-checks/fs-nfs-to-sad-ad-qa-check.js.map +1 -0
  199. package/dist/services/audit-checks/fs-nfs-to-ts-check.d.ts +9 -0
  200. package/dist/services/audit-checks/fs-nfs-to-ts-check.d.ts.map +1 -0
  201. package/dist/services/audit-checks/fs-nfs-to-ts-check.js +56 -0
  202. package/dist/services/audit-checks/fs-nfs-to-ts-check.js.map +1 -0
  203. package/dist/services/audit-checks/inverse-consistency-check.d.ts +7 -0
  204. package/dist/services/audit-checks/inverse-consistency-check.d.ts.map +1 -0
  205. package/dist/services/audit-checks/inverse-consistency-check.js +54 -0
  206. package/dist/services/audit-checks/inverse-consistency-check.js.map +1 -0
  207. package/dist/services/audit-checks/mapping-check.d.ts +4 -0
  208. package/dist/services/audit-checks/mapping-check.d.ts.map +1 -0
  209. package/dist/services/audit-checks/mapping-check.js +74 -0
  210. package/dist/services/audit-checks/mapping-check.js.map +1 -0
  211. package/dist/services/audit-checks/orphan-documents-check.d.ts +9 -0
  212. package/dist/services/audit-checks/orphan-documents-check.d.ts.map +1 -0
  213. package/dist/services/audit-checks/orphan-documents-check.js +54 -0
  214. package/dist/services/audit-checks/orphan-documents-check.js.map +1 -0
  215. package/dist/services/audit-checks/orphan-entities-check.d.ts +4 -0
  216. package/dist/services/audit-checks/orphan-entities-check.d.ts.map +1 -0
  217. package/dist/services/audit-checks/orphan-entities-check.js +39 -0
  218. package/dist/services/audit-checks/orphan-entities-check.js.map +1 -0
  219. package/dist/services/audit-checks/sad-ad-qa-tc-to-src-check.d.ts +9 -0
  220. package/dist/services/audit-checks/sad-ad-qa-tc-to-src-check.d.ts.map +1 -0
  221. package/dist/services/audit-checks/sad-ad-qa-tc-to-src-check.js +56 -0
  222. package/dist/services/audit-checks/sad-ad-qa-tc-to-src-check.js.map +1 -0
  223. package/dist/services/audit-checks/sprint-consistency-check.d.ts +4 -0
  224. package/dist/services/audit-checks/sprint-consistency-check.d.ts.map +1 -0
  225. package/dist/services/audit-checks/sprint-consistency-check.js +80 -0
  226. package/dist/services/audit-checks/sprint-consistency-check.js.map +1 -0
  227. package/dist/services/audit-checks/src-filesystem-only-check.d.ts +10 -0
  228. package/dist/services/audit-checks/src-filesystem-only-check.d.ts.map +1 -0
  229. package/dist/services/audit-checks/src-filesystem-only-check.js +106 -0
  230. package/dist/services/audit-checks/src-filesystem-only-check.js.map +1 -0
  231. package/dist/services/audit-checks/ts-to-tc-check.d.ts +10 -0
  232. package/dist/services/audit-checks/ts-to-tc-check.d.ts.map +1 -0
  233. package/dist/services/audit-checks/ts-to-tc-check.js +58 -0
  234. package/dist/services/audit-checks/ts-to-tc-check.js.map +1 -0
  235. package/dist/services/audit-checks/type-mismatch-check.d.ts +4 -0
  236. package/dist/services/audit-checks/type-mismatch-check.d.ts.map +1 -0
  237. package/dist/services/audit-checks/type-mismatch-check.js +55 -0
  238. package/dist/services/audit-checks/type-mismatch-check.js.map +1 -0
  239. package/dist/services/audit-checks/types.d.ts +66 -0
  240. package/dist/services/audit-checks/types.d.ts.map +1 -0
  241. package/dist/services/audit-checks/types.js +30 -0
  242. package/dist/services/audit-checks/types.js.map +1 -0
  243. package/dist/services/audit-checks/us-to-fr-nfr-check.d.ts +9 -0
  244. package/dist/services/audit-checks/us-to-fr-nfr-check.d.ts.map +1 -0
  245. package/dist/services/audit-checks/us-to-fr-nfr-check.js +56 -0
  246. package/dist/services/audit-checks/us-to-fr-nfr-check.js.map +1 -0
  247. package/dist/services/doc-entity-mapping.d.ts +19 -0
  248. package/dist/services/doc-entity-mapping.d.ts.map +1 -0
  249. package/dist/services/doc-entity-mapping.js +35 -0
  250. package/dist/services/doc-entity-mapping.js.map +1 -0
  251. package/dist/services/document-service.d.ts +58 -0
  252. package/dist/services/document-service.d.ts.map +1 -0
  253. package/dist/services/document-service.js +409 -0
  254. package/dist/services/document-service.js.map +1 -0
  255. package/dist/services/graph-export-service.d.ts +12 -0
  256. package/dist/services/graph-export-service.d.ts.map +1 -0
  257. package/dist/services/graph-export-service.js +112 -0
  258. package/dist/services/graph-export-service.js.map +1 -0
  259. package/dist/services/graph-service.d.ts +21 -0
  260. package/dist/services/graph-service.d.ts.map +1 -0
  261. package/dist/services/graph-service.js +49 -0
  262. package/dist/services/graph-service.js.map +1 -0
  263. package/dist/services/graph-validation-service.d.ts +13 -0
  264. package/dist/services/graph-validation-service.d.ts.map +1 -0
  265. package/dist/services/graph-validation-service.js +171 -0
  266. package/dist/services/graph-validation-service.js.map +1 -0
  267. package/dist/services/project.d.ts +80 -0
  268. package/dist/services/project.d.ts.map +1 -0
  269. package/dist/services/project.js +256 -0
  270. package/dist/services/project.js.map +1 -0
  271. package/dist/services/review-service.d.ts +81 -0
  272. package/dist/services/review-service.d.ts.map +1 -0
  273. package/dist/services/review-service.js +242 -0
  274. package/dist/services/review-service.js.map +1 -0
  275. package/dist/services/schema-migration-service.d.ts +44 -0
  276. package/dist/services/schema-migration-service.d.ts.map +1 -0
  277. package/dist/services/schema-migration-service.js +894 -0
  278. package/dist/services/schema-migration-service.js.map +1 -0
  279. package/dist/services/sprint.d.ts +71 -0
  280. package/dist/services/sprint.d.ts.map +1 -0
  281. package/dist/services/sprint.js +238 -0
  282. package/dist/services/sprint.js.map +1 -0
  283. package/dist/services/stage-validator.d.ts +36 -0
  284. package/dist/services/stage-validator.d.ts.map +1 -0
  285. package/dist/services/stage-validator.js +78 -0
  286. package/dist/services/stage-validator.js.map +1 -0
  287. package/dist/services/task.d.ts +75 -0
  288. package/dist/services/task.d.ts.map +1 -0
  289. package/dist/services/task.js +487 -0
  290. package/dist/services/task.js.map +1 -0
  291. package/dist/services/validation.d.ts +24 -0
  292. package/dist/services/validation.d.ts.map +1 -0
  293. package/dist/services/validation.js +53 -0
  294. package/dist/services/validation.js.map +1 -0
  295. package/dist/state-machines/sprint-state.d.ts +25 -0
  296. package/dist/state-machines/sprint-state.d.ts.map +1 -0
  297. package/dist/state-machines/sprint-state.js +64 -0
  298. package/dist/state-machines/sprint-state.js.map +1 -0
  299. package/dist/state-machines/task-state.d.ts +31 -0
  300. package/dist/state-machines/task-state.d.ts.map +1 -0
  301. package/dist/state-machines/task-state.js +83 -0
  302. package/dist/state-machines/task-state.js.map +1 -0
  303. package/dist/utils/errors.d.ts +673 -0
  304. package/dist/utils/errors.d.ts.map +1 -0
  305. package/dist/utils/errors.js +1447 -0
  306. package/dist/utils/errors.js.map +1 -0
  307. package/dist/utils/format.d.ts +52 -0
  308. package/dist/utils/format.d.ts.map +1 -0
  309. package/dist/utils/format.js +65 -0
  310. package/dist/utils/format.js.map +1 -0
  311. package/dist/validators/board-integrity.d.ts +25 -0
  312. package/dist/validators/board-integrity.d.ts.map +1 -0
  313. package/dist/validators/board-integrity.js +102 -0
  314. package/dist/validators/board-integrity.js.map +1 -0
  315. package/dist/validators/board-shape.d.ts +13 -0
  316. package/dist/validators/board-shape.d.ts.map +1 -0
  317. package/dist/validators/board-shape.js +74 -0
  318. package/dist/validators/board-shape.js.map +1 -0
  319. package/dist/validators/owner-validator.d.ts +30 -0
  320. package/dist/validators/owner-validator.d.ts.map +1 -0
  321. package/dist/validators/owner-validator.js +70 -0
  322. package/dist/validators/owner-validator.js.map +1 -0
  323. package/dist/validators/spawn-legality.d.ts +19 -0
  324. package/dist/validators/spawn-legality.d.ts.map +1 -0
  325. package/dist/validators/spawn-legality.js +77 -0
  326. package/dist/validators/spawn-legality.js.map +1 -0
  327. package/dist/validators/state-field-checker.d.ts +8 -0
  328. package/dist/validators/state-field-checker.d.ts.map +1 -0
  329. package/dist/validators/state-field-checker.js +47 -0
  330. package/dist/validators/state-field-checker.js.map +1 -0
  331. package/dist/validators/state-field-recorder.d.ts +39 -0
  332. package/dist/validators/state-field-recorder.d.ts.map +1 -0
  333. package/dist/validators/state-field-recorder.js +62 -0
  334. package/dist/validators/state-field-recorder.js.map +1 -0
  335. package/package.json +53 -0
@@ -0,0 +1,830 @@
1
+ "use strict";
2
+ // Database schema DDL statements and migration definitions
3
+ // All tables from plan §4.3 + §9.7 (document content model)
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.EXPECTED_INDEXES = exports.EXPECTED_TABLES = exports.LATEST_VERSION = exports.MIGRATIONS = exports.INITIAL_SCHEMA_SQL = void 0;
6
+ /** All DDL for the initial schema (migration version 1) */
7
+ exports.INITIAL_SCHEMA_SQL = `
8
+ -- ─── Sprints ───
9
+ CREATE TABLE IF NOT EXISTS sprints (
10
+ id TEXT PRIMARY KEY,
11
+ name TEXT NOT NULL,
12
+ status TEXT NOT NULL DEFAULT 'PLANNING'
13
+ CHECK (status IN ('PLANNING','IN EXECUTION','CLOSING','CLOSED')),
14
+ goal TEXT,
15
+ started_at TEXT,
16
+ closed_at TEXT,
17
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
18
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
19
+ );
20
+
21
+ -- ─── Tasks ───
22
+ CREATE TABLE IF NOT EXISTS tasks (
23
+ id TEXT NOT NULL,
24
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
25
+ parent_id TEXT,
26
+ description TEXT NOT NULL,
27
+ task_type TEXT NOT NULL DEFAULT 'Feature'
28
+ CHECK (task_type IN ('Feature','Bug','Chore')),
29
+ priority TEXT NOT NULL DEFAULT 'P2'
30
+ CHECK (priority IN ('P0','P1','P2')),
31
+ owner TEXT NOT NULL,
32
+ points INTEGER DEFAULT 0,
33
+ status TEXT NOT NULL DEFAULT 'Backlog'
34
+ CHECK (status IN ('Backlog','To Do','In Progress','In Review','Done','Dropped')),
35
+ stage TEXT NOT NULL DEFAULT 'Planning'
36
+ CHECK (stage IN ('Planning','Requirement','Specification','Design','Test Plan','Implementation','Testing','Release')),
37
+ session_key TEXT,
38
+ started_at TEXT,
39
+ completed_at TEXT,
40
+ scope_in TEXT,
41
+ scope_out TEXT,
42
+ decisions TEXT,
43
+ dependencies TEXT,
44
+ acceptance TEXT,
45
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
46
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
47
+ PRIMARY KEY (sprint_id, id)
48
+ );
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_tasks_sprint ON tasks(sprint_id);
51
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
52
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(sprint_id, status);
53
+ CREATE INDEX IF NOT EXISTS idx_tasks_owner ON tasks(sprint_id, owner);
54
+
55
+ -- ─── Graph: Entity Types ───
56
+ -- builtin:3.0 (Sprint 31): entity_types table DROPPED. Doc types are the sole source of truth.
57
+ -- Do NOT recreate this table.
58
+
59
+ -- ─── Graph: Entities ───
60
+ CREATE TABLE IF NOT EXISTS entities (
61
+ id TEXT PRIMARY KEY,
62
+ type_name TEXT NOT NULL,
63
+ properties TEXT NOT NULL DEFAULT '{}',
64
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
65
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
66
+ );
67
+
68
+ CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type_name);
69
+
70
+ -- ─── Graph: Relation Types ───
71
+ CREATE TABLE IF NOT EXISTS relation_types (
72
+ name TEXT PRIMARY KEY,
73
+ from_types TEXT NOT NULL DEFAULT '[]',
74
+ to_types TEXT NOT NULL DEFAULT '[]',
75
+ cardinality TEXT DEFAULT 'many_to_many',
76
+ inverse_of TEXT REFERENCES relation_types(name),
77
+ description TEXT
78
+ );
79
+
80
+ -- ─── Graph: Relations ───
81
+ CREATE TABLE IF NOT EXISTS relations (
82
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
83
+ from_id TEXT NOT NULL REFERENCES entities(id),
84
+ rel_type TEXT NOT NULL REFERENCES relation_types(name),
85
+ to_id TEXT NOT NULL REFERENCES entities(id),
86
+ properties TEXT NOT NULL DEFAULT '{}',
87
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
88
+ UNIQUE(from_id, rel_type, to_id)
89
+ );
90
+
91
+ CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_id, rel_type);
92
+ CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_id, rel_type);
93
+ CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(rel_type);
94
+
95
+ -- ─── Graph: Schema Version ───
96
+ CREATE TABLE IF NOT EXISTS graph_schema (
97
+ key TEXT PRIMARY KEY,
98
+ value TEXT NOT NULL
99
+ );
100
+
101
+ -- ─── Reviews ───
102
+ CREATE TABLE IF NOT EXISTS reviews (
103
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
104
+ review_num TEXT NOT NULL,
105
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
106
+ asset_name TEXT NOT NULL,
107
+ review_type TEXT NOT NULL
108
+ CHECK (review_type IN ('requirements','architecture','code','test','document')),
109
+ verdict TEXT NOT NULL
110
+ CHECK (verdict IN ('APPROVED','NEEDS_REWORK','OPEN_QUESTION')),
111
+ critical_count INTEGER NOT NULL DEFAULT 0,
112
+ major_count INTEGER NOT NULL DEFAULT 0,
113
+ minor_count INTEGER NOT NULL DEFAULT 0,
114
+ suggestion_count INTEGER NOT NULL DEFAULT 0,
115
+ open_questions_count INTEGER NOT NULL DEFAULT 0,
116
+ content TEXT NOT NULL,
117
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
118
+ );
119
+
120
+ CREATE INDEX IF NOT EXISTS idx_reviews_sprint ON reviews(sprint_id);
121
+ CREATE INDEX IF NOT EXISTS idx_reviews_verdict ON reviews(verdict);
122
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_reviews_sprint_num ON reviews(sprint_id, review_num);
123
+
124
+ -- ─── Documents (canonical artifact registry) ───
125
+ CREATE TABLE IF NOT EXISTS documents (
126
+ id TEXT PRIMARY KEY,
127
+ type TEXT NOT NULL,
128
+ title TEXT NOT NULL,
129
+ path TEXT,
130
+ status TEXT DEFAULT 'Draft',
131
+ module_id TEXT,
132
+ sprint_id TEXT REFERENCES sprints(id),
133
+ content_hash TEXT,
134
+ content_storage TEXT NOT NULL DEFAULT 'filesystem'
135
+ CHECK (content_storage IN ('filesystem', 'db')),
136
+ metadata TEXT DEFAULT '{}',
137
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
138
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
139
+ );
140
+
141
+ CREATE INDEX IF NOT EXISTS idx_documents_type ON documents(type);
142
+ CREATE INDEX IF NOT EXISTS idx_documents_module ON documents(module_id);
143
+ CREATE INDEX IF NOT EXISTS idx_documents_sprint ON documents(sprint_id);
144
+
145
+ -- ─── Document Contents (DB-stored content + FTS5) ───
146
+ CREATE TABLE IF NOT EXISTS document_contents (
147
+ document_id TEXT PRIMARY KEY REFERENCES documents(id),
148
+ content TEXT NOT NULL,
149
+ content_hash TEXT NOT NULL,
150
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
151
+ );
152
+
153
+ CREATE VIRTUAL TABLE IF NOT EXISTS document_contents_fts USING fts5(
154
+ document_id,
155
+ content,
156
+ content='document_contents',
157
+ content_rowid='rowid'
158
+ );
159
+
160
+ -- FTS sync triggers
161
+ CREATE TRIGGER IF NOT EXISTS document_contents_ai AFTER INSERT ON document_contents BEGIN
162
+ INSERT INTO document_contents_fts(rowid, document_id, content)
163
+ VALUES (new.rowid, new.document_id, new.content);
164
+ END;
165
+
166
+ CREATE TRIGGER IF NOT EXISTS document_contents_ad AFTER DELETE ON document_contents BEGIN
167
+ INSERT INTO document_contents_fts(document_contents_fts, rowid, document_id, content)
168
+ VALUES ('delete', old.rowid, old.document_id, old.content);
169
+ END;
170
+
171
+ CREATE TRIGGER IF NOT EXISTS document_contents_au AFTER UPDATE ON document_contents BEGIN
172
+ INSERT INTO document_contents_fts(document_contents_fts, rowid, document_id, content)
173
+ VALUES ('delete', old.rowid, old.document_id, old.content);
174
+ INSERT INTO document_contents_fts(rowid, document_id, content)
175
+ VALUES (new.rowid, new.document_id, new.content);
176
+ END;
177
+
178
+ -- ─── Test Execution Reports ───
179
+ CREATE TABLE IF NOT EXISTS test_reports (
180
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
181
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
182
+ task_id TEXT REFERENCES tasks(id),
183
+ run_number INTEGER NOT NULL,
184
+ total INTEGER NOT NULL,
185
+ passed INTEGER NOT NULL,
186
+ failed INTEGER NOT NULL,
187
+ skipped INTEGER NOT NULL,
188
+ blocked INTEGER NOT NULL DEFAULT 0,
189
+ result TEXT NOT NULL CHECK (result IN ('PASS','FAIL','BLOCKED')),
190
+ command TEXT,
191
+ duration_s REAL,
192
+ report_path TEXT,
193
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
194
+ );
195
+
196
+ -- ─── Lessons ───
197
+ CREATE TABLE IF NOT EXISTS lessons (
198
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
199
+ sprint_id TEXT REFERENCES sprints(id),
200
+ category TEXT NOT NULL
201
+ CHECK (category IN ('Process','Technical','Tool','Model')),
202
+ title TEXT NOT NULL,
203
+ description TEXT NOT NULL,
204
+ severity TEXT DEFAULT 'minor' CHECK (severity IN ('critical','major','minor','suggestion')),
205
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
206
+ );
207
+
208
+ -- ─── Advisory Locks ───
209
+ CREATE TABLE IF NOT EXISTS locks (
210
+ name TEXT PRIMARY KEY,
211
+ owner TEXT NOT NULL,
212
+ acquired_at TEXT NOT NULL,
213
+ last_heartbeat TEXT
214
+ );
215
+
216
+ -- ─── Migrations ───
217
+ CREATE TABLE IF NOT EXISTS _migrations (
218
+ version INTEGER PRIMARY KEY,
219
+ applied_at TEXT NOT NULL DEFAULT (datetime('now')),
220
+ checksum TEXT NOT NULL DEFAULT ''
221
+ );
222
+ `;
223
+ /**
224
+ * Sequential versioned migrations.
225
+ * Version 1 is the initial schema applied on fresh database creation.
226
+ * Future migrations add entries here with incrementing version numbers.
227
+ */
228
+ exports.MIGRATIONS = [
229
+ {
230
+ version: 1,
231
+ description: 'Initial schema: all core tables',
232
+ sql: exports.INITIAL_SCHEMA_SQL,
233
+ },
234
+ {
235
+ version: 2,
236
+ description: 'Add removed_at column to tasks for soft delete (AD-0011)',
237
+ sql: `
238
+ -- Conditionally add removed_at column (idempotent via error handling in runner)
239
+ ALTER TABLE tasks ADD COLUMN removed_at TEXT;
240
+ `,
241
+ },
242
+ {
243
+ version: 3,
244
+ description: 'SAD-0003: Extend graph/doc/review tables for MOD-008 through MOD-013',
245
+ sql: `
246
+ -- MOD-008: entity_types extensions (builtin:3.0: skip if table dropped)
247
+ -- ALTER TABLE entity_types ADD COLUMN required_fields TEXT DEFAULT '{}';
248
+ -- ALTER TABLE entity_types ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
249
+
250
+ -- MOD-008: relation_types extensions
251
+ ALTER TABLE relation_types ADD COLUMN created_at TEXT NOT NULL DEFAULT (datetime('now'));
252
+ ALTER TABLE relation_types ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
253
+
254
+ -- MOD-009: entities extension (updated_at already in v1 schema)
255
+ -- ALTER TABLE entities ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
256
+
257
+ -- MOD-010: relations extension
258
+ ALTER TABLE relations ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
259
+
260
+ -- MOD-012: document_contents doc_id alias support
261
+ -- (keeping document_id as canonical column name; services use document_id)
262
+
263
+ -- MOD-013: reviews extensions for auto-numbering + asset_ref
264
+ ALTER TABLE reviews ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
265
+ ALTER TABLE reviews ADD COLUMN asset_ref TEXT;
266
+
267
+ -- MOD-013: test_reports — add SAD-0003 columns
268
+ ALTER TABLE test_reports ADD COLUMN module_id TEXT;
269
+ ALTER TABLE test_reports ADD COLUMN status TEXT DEFAULT 'PASS';
270
+ ALTER TABLE test_reports ADD COLUMN content TEXT DEFAULT '';
271
+ ALTER TABLE test_reports ADD COLUMN pass_count INTEGER DEFAULT 0;
272
+ ALTER TABLE test_reports ADD COLUMN fail_count INTEGER DEFAULT 0;
273
+ ALTER TABLE test_reports ADD COLUMN skip_count INTEGER DEFAULT 0;
274
+ ALTER TABLE test_reports ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
275
+
276
+ -- MOD-013: lessons — add SAD-0003 columns
277
+ ALTER TABLE lessons ADD COLUMN text TEXT DEFAULT '';
278
+ ALTER TABLE lessons ADD COLUMN updated_at TEXT NOT NULL DEFAULT (datetime('now'));
279
+
280
+ -- Deferred indexes (require columns from this migration)
281
+ CREATE INDEX IF NOT EXISTS idx_test_reports_module ON test_reports(module_id);
282
+ `,
283
+ },
284
+ {
285
+ version: 4,
286
+ description: 'Restructure graph_schema table + add missing indexes',
287
+ sql: `
288
+ -- Restructure graph_schema: drop key/value, create columnar schema
289
+ DROP TABLE IF EXISTS graph_schema;
290
+ CREATE TABLE graph_schema (
291
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
292
+ source_file TEXT,
293
+ version TEXT NOT NULL DEFAULT '1',
294
+ bootstrapped_at TEXT NOT NULL DEFAULT (datetime('now')),
295
+ checksum TEXT
296
+ );
297
+
298
+ -- SAD-0003 Additional Indexes
299
+ CREATE INDEX IF NOT EXISTS idx_reviews_type ON reviews(review_type);
300
+ CREATE INDEX IF NOT EXISTS idx_test_reports_sprint ON test_reports(sprint_id);
301
+ CREATE INDEX IF NOT EXISTS idx_lessons_sprint ON lessons(sprint_id);
302
+ `,
303
+ },
304
+ {
305
+ version: 5,
306
+ description: 'URD-G001: Remove document metadata columns, rename entity/doc types, update relation type arrays',
307
+ sql: `
308
+ -- Step 1: Drop indexes on removed columns (sprint-consistency removal before column drop)
309
+ DROP INDEX IF EXISTS idx_documents_module;
310
+ DROP INDEX IF EXISTS idx_documents_sprint;
311
+
312
+ -- Step 2: Re-type documents (MODULE→MDL, SOURCEFILE→SRC)
313
+ UPDATE documents SET type = 'MDL' WHERE type = 'MODULE';
314
+ UPDATE documents SET type = 'SRC' WHERE type = 'SOURCEFILE';
315
+
316
+ -- Step 3: Rename entity types in entity_types table (builtin:3.0: skip if table dropped)
317
+ -- UPDATE entity_types SET name = 'MDL' WHERE name = 'Module';
318
+ -- UPDATE entity_types SET name = 'SRC' WHERE name = 'SourceFile';
319
+
320
+ -- Step 4: Rename entity type_name in entities table
321
+ UPDATE entities SET type_name = 'MDL' WHERE type_name = 'Module';
322
+ UPDATE entities SET type_name = 'SRC' WHERE type_name = 'SourceFile';
323
+
324
+ -- Step 5: Update relation_types from_types/to_types JSON arrays
325
+ -- Replace 'Module' with 'MDL' and 'SourceFile' with 'SRC' in from_types arrays
326
+ UPDATE relation_types
327
+ SET from_types = (
328
+ SELECT json_group_array(
329
+ CASE value
330
+ WHEN 'Module' THEN 'MDL'
331
+ WHEN 'SourceFile' THEN 'SRC'
332
+ ELSE value END
333
+ )
334
+ FROM json_each(from_types)
335
+ )
336
+ WHERE from_types LIKE '%Module%' OR from_types LIKE '%SourceFile%';
337
+
338
+ -- Replace 'Module' with 'MDL' and 'SourceFile' with 'SRC' in to_types arrays
339
+ UPDATE relation_types
340
+ SET to_types = (
341
+ SELECT json_group_array(
342
+ CASE value
343
+ WHEN 'Module' THEN 'MDL'
344
+ WHEN 'SourceFile' THEN 'SRC'
345
+ ELSE value END
346
+ )
347
+ FROM json_each(to_types)
348
+ )
349
+ WHERE to_types LIKE '%Module%' OR to_types LIKE '%SourceFile%';
350
+
351
+ -- Step 6: Remove status, module_id, sprint_id columns from documents table
352
+ -- SQLite 3.35.0+ supports ALTER TABLE DROP COLUMN
353
+ ALTER TABLE documents DROP COLUMN status;
354
+ ALTER TABLE documents DROP COLUMN module_id;
355
+ ALTER TABLE documents DROP COLUMN sprint_id;
356
+ `,
357
+ },
358
+ {
359
+ version: 6,
360
+ description: 'URD-024: Entity type name alignment — rename 12 long-form entity type names to short codes',
361
+ sql: `
362
+ -- Phase A: Rename entity type names in entity_types table (builtin:3.0: skip if table dropped)
363
+ -- UPDATE entity_types SET name = 'AD' WHERE name = 'ArchitectureDecision';
364
+ -- UPDATE entity_types SET name = 'SAD' WHERE name = 'ArchitectureOverview';
365
+ -- UPDATE entity_types SET name = 'FR' WHERE name = 'FunctionalRequirement';
366
+ -- UPDATE entity_types SET name = 'NFR' WHERE name = 'NonFunctionalRequirement';
367
+ -- UPDATE entity_types SET name = 'US' WHERE name = 'UserStory';
368
+ -- UPDATE entity_types SET name = 'FS' WHERE name = 'FunctionalSpecification';
369
+ -- UPDATE entity_types SET name = 'NFS' WHERE name = 'NonFunctionalSpecification';
370
+ -- UPDATE entity_types SET name = 'QA' WHERE name = 'QualityScenario';
371
+ -- UPDATE entity_types SET name = 'TC' WHERE name = 'TestCase';
372
+ -- UPDATE entity_types SET name = 'TR' WHERE name = 'TestReport';
373
+ -- UPDATE entity_types SET name = 'TS' WHERE name = 'TestSuite';
374
+ -- UPDATE entity_types SET name = 'URD' WHERE name = 'UserRequirement';
375
+
376
+ -- Phase B: Update entity type_name in entities table (SAD-0029 §3.1)
377
+ -- Idempotent: WHERE old-type filters ensure already-updated rows are unaffected
378
+ UPDATE entities SET type_name = 'AD' WHERE type_name = 'ArchitectureDecision';
379
+ UPDATE entities SET type_name = 'SAD' WHERE type_name = 'ArchitectureOverview';
380
+ UPDATE entities SET type_name = 'FR' WHERE type_name = 'FunctionalRequirement';
381
+ UPDATE entities SET type_name = 'NFR' WHERE type_name = 'NonFunctionalRequirement';
382
+ UPDATE entities SET type_name = 'US' WHERE type_name = 'UserStory';
383
+ UPDATE entities SET type_name = 'FS' WHERE type_name = 'FunctionalSpecification';
384
+ UPDATE entities SET type_name = 'NFS' WHERE type_name = 'NonFunctionalSpecification';
385
+ UPDATE entities SET type_name = 'QA' WHERE type_name = 'QualityScenario';
386
+ UPDATE entities SET type_name = 'TC' WHERE type_name = 'TestCase';
387
+ UPDATE entities SET type_name = 'TR' WHERE type_name = 'TestReport';
388
+ UPDATE entities SET type_name = 'TS' WHERE type_name = 'TestSuite';
389
+ UPDATE entities SET type_name = 'URD' WHERE type_name = 'UserRequirement';
390
+
391
+ -- Phase C: Update relation_types from_types/to_types JSON arrays (SAD-0029 §3.1)
392
+ -- Use json_group_array with CASE to replace long names with short codes.
393
+ -- Only rows that contain long-form names are matched (LIKE filter for efficiency).
394
+
395
+ -- Update from_types arrays
396
+ UPDATE relation_types
397
+ SET from_types = (
398
+ SELECT json_group_array(
399
+ CASE value
400
+ WHEN 'ArchitectureDecision' THEN 'AD'
401
+ WHEN 'ArchitectureOverview' THEN 'SAD'
402
+ WHEN 'FunctionalRequirement' THEN 'FR'
403
+ WHEN 'FunctionalSpecification' THEN 'FS'
404
+ WHEN 'NonFunctionalRequirement' THEN 'NFR'
405
+ WHEN 'NonFunctionalSpecification' THEN 'NFS'
406
+ WHEN 'QualityScenario' THEN 'QA'
407
+ WHEN 'TestCase' THEN 'TC'
408
+ WHEN 'TestReport' THEN 'TR'
409
+ WHEN 'TestSuite' THEN 'TS'
410
+ WHEN 'UserRequirement' THEN 'URD'
411
+ WHEN 'UserStory' THEN 'US'
412
+ ELSE value END
413
+ )
414
+ FROM json_each(from_types)
415
+ )
416
+ WHERE from_types LIKE '%ArchitectureDecision%'
417
+ OR from_types LIKE '%ArchitectureOverview%'
418
+ OR from_types LIKE '%FunctionalRequirement%'
419
+ OR from_types LIKE '%FunctionalSpecification%'
420
+ OR from_types LIKE '%NonFunctionalRequirement%'
421
+ OR from_types LIKE '%NonFunctionalSpecification%'
422
+ OR from_types LIKE '%QualityScenario%'
423
+ OR from_types LIKE '%TestCase%'
424
+ OR from_types LIKE '%TestReport%'
425
+ OR from_types LIKE '%TestSuite%'
426
+ OR from_types LIKE '%UserRequirement%'
427
+ OR from_types LIKE '%UserStory%';
428
+
429
+ -- Update to_types arrays
430
+ UPDATE relation_types
431
+ SET to_types = (
432
+ SELECT json_group_array(
433
+ CASE value
434
+ WHEN 'ArchitectureDecision' THEN 'AD'
435
+ WHEN 'ArchitectureOverview' THEN 'SAD'
436
+ WHEN 'FunctionalRequirement' THEN 'FR'
437
+ WHEN 'FunctionalSpecification' THEN 'FS'
438
+ WHEN 'NonFunctionalRequirement' THEN 'NFR'
439
+ WHEN 'NonFunctionalSpecification' THEN 'NFS'
440
+ WHEN 'QualityScenario' THEN 'QA'
441
+ WHEN 'TestCase' THEN 'TC'
442
+ WHEN 'TestReport' THEN 'TR'
443
+ WHEN 'TestSuite' THEN 'TS'
444
+ WHEN 'UserRequirement' THEN 'URD'
445
+ WHEN 'UserStory' THEN 'US'
446
+ ELSE value END
447
+ )
448
+ FROM json_each(to_types)
449
+ )
450
+ WHERE to_types LIKE '%ArchitectureDecision%'
451
+ OR to_types LIKE '%ArchitectureOverview%'
452
+ OR to_types LIKE '%FunctionalRequirement%'
453
+ OR to_types LIKE '%FunctionalSpecification%'
454
+ OR to_types LIKE '%NonFunctionalRequirement%'
455
+ OR to_types LIKE '%NonFunctionalSpecification%'
456
+ OR to_types LIKE '%QualityScenario%'
457
+ OR to_types LIKE '%TestCase%'
458
+ OR to_types LIKE '%TestReport%'
459
+ OR to_types LIKE '%TestSuite%'
460
+ OR to_types LIKE '%UserRequirement%'
461
+ OR to_types LIKE '%UserStory%';
462
+ `,
463
+ },
464
+ {
465
+ version: 7,
466
+ description: 'URD-026: Remove lesson metadata columns (sprint_id, category, title, severity)',
467
+ sql: `
468
+ -- URD-026 Part B: Remove metadata columns from lessons table
469
+ -- Lesson becomes content-only cumulative document (AD-0029)
470
+ -- Idempotent via recreate-swap pattern
471
+
472
+ -- Drop dependent index first (idempotent)
473
+ DROP INDEX IF EXISTS idx_lessons_sprint;
474
+
475
+ -- Recreate lessons table without metadata columns
476
+ CREATE TABLE IF NOT EXISTS lessons_v7 (
477
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
478
+ description TEXT NOT NULL,
479
+ text TEXT NOT NULL DEFAULT '',
480
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
481
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
482
+ );
483
+
484
+ -- Migrate data from old table (runs whether or not old columns exist)
485
+ INSERT INTO lessons_v7 (id, description, text, created_at, updated_at)
486
+ SELECT id, description, COALESCE(text, description),
487
+ created_at, COALESCE(updated_at, created_at)
488
+ FROM lessons;
489
+
490
+ DROP TABLE IF EXISTS lessons;
491
+ ALTER TABLE lessons_v7 RENAME TO lessons;
492
+ `,
493
+ },
494
+ {
495
+ version: 8,
496
+ description: 'SAD-0031: Drop task_id/run_number from test_reports, change id to TEXT, add composite indexes',
497
+ sql: `
498
+ -- SAD-0031 Part B: Rebuild test_reports without task_id, run_number.
499
+ -- id changes from INTEGER AUTOINCREMENT to TEXT (caller-provided).
500
+ -- Primary key becomes (sprint_id, id).
501
+ -- Idempotent via recreate-swap pattern.
502
+
503
+ -- Drop old indexes
504
+ DROP INDEX IF EXISTS idx_test_reports_sprint;
505
+ DROP INDEX IF EXISTS idx_test_reports_module;
506
+
507
+ -- Recreate test_reports table with new schema
508
+ CREATE TABLE IF NOT EXISTS test_reports_v8 (
509
+ id TEXT NOT NULL,
510
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
511
+ total INTEGER NOT NULL,
512
+ passed INTEGER NOT NULL,
513
+ failed INTEGER NOT NULL,
514
+ skipped INTEGER NOT NULL,
515
+ blocked INTEGER NOT NULL DEFAULT 0,
516
+ result TEXT NOT NULL CHECK (result IN ('PASS','FAIL','BLOCKED')),
517
+ command TEXT,
518
+ duration_ms INTEGER,
519
+ report_path TEXT,
520
+ module_id TEXT,
521
+ status TEXT DEFAULT 'PASS',
522
+ content TEXT DEFAULT '',
523
+ pass_count INTEGER DEFAULT 0,
524
+ fail_count INTEGER DEFAULT 0,
525
+ skip_count INTEGER DEFAULT 0,
526
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
527
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
528
+ PRIMARY KEY (sprint_id, id)
529
+ );
530
+
531
+ -- Migrate data: old autoincrement id becomes string, drop task_id, run_number
532
+ INSERT INTO test_reports_v8 (
533
+ id, sprint_id, total, passed, failed, skipped, blocked, result,
534
+ command, duration_ms, report_path, module_id, status, content,
535
+ pass_count, fail_count, skip_count, created_at, updated_at
536
+ )
537
+ SELECT
538
+ CAST(id AS TEXT), sprint_id, total, passed, failed, skipped, blocked, result,
539
+ command, CAST(ROUND(COALESCE(duration_s, 0) * 1000) AS INTEGER), report_path,
540
+ module_id, status, content, pass_count, fail_count, skip_count,
541
+ created_at, COALESCE(updated_at, created_at)
542
+ FROM test_reports;
543
+
544
+ DROP TABLE IF EXISTS test_reports;
545
+ ALTER TABLE test_reports_v8 RENAME TO test_reports;
546
+
547
+ -- SAD-0031 Part A: Composite indexes for duplicate ID validation
548
+ CREATE INDEX IF NOT EXISTS idx_test_reports_sprint ON test_reports(sprint_id);
549
+ CREATE INDEX IF NOT EXISTS idx_test_reports_module ON test_reports(module_id);
550
+ CREATE INDEX IF NOT EXISTS idx_tasks_sprint_id_id ON tasks(sprint_id, id);
551
+ CREATE INDEX IF NOT EXISTS idx_reviews_sprint_id_id ON reviews(sprint_id, review_num);
552
+ CREATE INDEX IF NOT EXISTS idx_test_reports_sprint_id_id ON test_reports(sprint_id, id);
553
+ `,
554
+ },
555
+ {
556
+ version: 9,
557
+ description: 'URD-030: Add stage column, remove blocker column, migrate Blocked tasks to Dropped',
558
+ sql: `
559
+ -- Step 1: Migrate existing Blocked tasks to Dropped (FS-SS-030-0004 RM-003)
560
+ UPDATE tasks SET status = 'Dropped' WHERE status = 'Blocked';
561
+
562
+ -- Step 2: Drop the blocker column (idempotent via ALTER TABLE DROP COLUMN in SQLite 3.35+)
563
+ ALTER TABLE tasks DROP COLUMN blocker;
564
+
565
+ -- Step 3: Add stage column with default 'Planning' and CHECK constraint
566
+ ALTER TABLE tasks ADD COLUMN stage TEXT NOT NULL DEFAULT 'Planning'
567
+ CHECK (stage IN ('Planning','Requirement','Specification','Design','Test Plan','Implementation','Testing','Release'));
568
+
569
+ -- Step 4: Recreate status CHECK constraint without Blocked
570
+ -- SQLite doesn't support ALTER COLUMN, so recreate the table
571
+ CREATE TABLE IF NOT EXISTS tasks_v9 (
572
+ id TEXT PRIMARY KEY,
573
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
574
+ parent_id TEXT REFERENCES tasks(id),
575
+ description TEXT NOT NULL,
576
+ task_type TEXT NOT NULL DEFAULT 'Feature'
577
+ CHECK (task_type IN ('Feature','Bug','Chore')),
578
+ priority TEXT NOT NULL DEFAULT 'P2'
579
+ CHECK (priority IN ('P0','P1','P2')),
580
+ owner TEXT NOT NULL,
581
+ points INTEGER DEFAULT 0,
582
+ status TEXT NOT NULL DEFAULT 'Backlog'
583
+ CHECK (status IN ('Backlog','To Do','In Progress','In Review','Done','Dropped')),
584
+ stage TEXT NOT NULL DEFAULT 'Planning'
585
+ CHECK (stage IN ('Planning','Requirement','Specification','Design','Test Plan','Implementation','Testing','Release')),
586
+ session_key TEXT,
587
+ started_at TEXT,
588
+ reviewer TEXT,
589
+ review_notes TEXT,
590
+ completed_at TEXT,
591
+ scope_in TEXT,
592
+ scope_out TEXT,
593
+ decisions TEXT,
594
+ dependencies TEXT,
595
+ acceptance TEXT,
596
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
597
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
598
+ removed_at TEXT
599
+ );
600
+
601
+ -- Copy data from old table
602
+ INSERT INTO tasks_v9 (
603
+ id, sprint_id, parent_id, description, task_type, priority,
604
+ owner, points, status, stage, session_key, started_at,
605
+ reviewer, review_notes, completed_at, scope_in, scope_out,
606
+ decisions, dependencies, acceptance, created_at, updated_at, removed_at
607
+ )
608
+ SELECT
609
+ id, sprint_id, parent_id, description, task_type, priority,
610
+ owner, points, status, stage, session_key, started_at,
611
+ reviewer, review_notes, completed_at, scope_in, scope_out,
612
+ decisions, dependencies, acceptance, created_at, updated_at, removed_at
613
+ FROM tasks;
614
+
615
+ DROP TABLE IF EXISTS tasks;
616
+ ALTER TABLE tasks_v9 RENAME TO tasks;
617
+
618
+ -- Recreate indexes
619
+ CREATE INDEX IF NOT EXISTS idx_tasks_sprint ON tasks(sprint_id);
620
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
621
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(sprint_id, status);
622
+ CREATE INDEX IF NOT EXISTS idx_tasks_owner ON tasks(sprint_id, owner);
623
+ `,
624
+ },
625
+ {
626
+ version: 10,
627
+ description: 'FS-SS-028-0002: Migrate task IDs from sprint-XXX-N to 4-digit NNNN format',
628
+ sql: `
629
+ -- Create a mapping of old IDs to new sequential NNNN IDs, ordered by creation date.
630
+ -- Old IDs in the format "sprint-YYYYMMDDNN-N" are assigned new NNNN IDs.
631
+ -- This is necessary because multiple sprints may have tasks with the same N suffix,
632
+ -- which would cause PRIMARY KEY conflicts if N values were preserved directly.
633
+ -- (MR-001: preserve the N value from sprint-XXX-N — caveat: renumbered to avoid PK collisions.)
634
+
635
+ -- Step 1: Create temporary mapping table
636
+ CREATE TABLE IF NOT EXISTS _task_id_migration (
637
+ old_id TEXT PRIMARY KEY,
638
+ new_id TEXT NOT NULL
639
+ );
640
+
641
+ -- Step 2: Assign new sequential IDs in creation order
642
+ INSERT INTO _task_id_migration (old_id, new_id)
643
+ SELECT id, SUBSTR('0000' || CAST(ROW_NUMBER() OVER (ORDER BY created_at, id) AS TEXT), -4)
644
+ FROM tasks;
645
+
646
+ -- Step 3: Disable FK checks temporarily for renumbering
647
+ PRAGMA foreign_keys = OFF;
648
+
649
+ -- Step 4: Update parent_id references to new IDs
650
+ UPDATE tasks SET parent_id = (
651
+ SELECT m.new_id FROM _task_id_migration m WHERE m.old_id = tasks.parent_id
652
+ ) WHERE parent_id IS NOT NULL;
653
+
654
+ -- Step 5: Update task IDs
655
+ UPDATE tasks SET id = (
656
+ SELECT m.new_id FROM _task_id_migration m WHERE m.old_id = tasks.id
657
+ );
658
+
659
+ -- Step 6: Clean up and re-enable FK checks
660
+ DROP TABLE IF EXISTS _task_id_migration;
661
+ PRAGMA foreign_keys = ON;
662
+ `,
663
+ },
664
+ {
665
+ version: 11,
666
+ description: 'SAD-0035: Drop entities table, rebuild relations to reference documents(id)',
667
+ sql: `
668
+ -- ⚠️ WARNING: This migration is IRREVERSIBLE.
669
+ -- The entities table is permanently dropped and the relations table is rebuilt
670
+ -- to reference documents(id) instead of entities(id).
671
+ -- There is no rollback path. Ensure you have a database backup before applying.
672
+
673
+ -- Step 1: Drop indexes that depend on entities
674
+ DROP INDEX IF EXISTS idx_entities_type;
675
+
676
+ -- Step 2: Drop the entities table
677
+ DROP TABLE IF EXISTS entities;
678
+
679
+ -- Step 3: Rebuild relations table with FK referencing documents(id) instead of entities(id)
680
+ CREATE TABLE IF NOT EXISTS relations_v11 (
681
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
682
+ from_id TEXT NOT NULL REFERENCES documents(id),
683
+ rel_type TEXT NOT NULL REFERENCES relation_types(name),
684
+ to_id TEXT NOT NULL REFERENCES documents(id),
685
+ properties TEXT NOT NULL DEFAULT '{}',
686
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
687
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
688
+ UNIQUE(from_id, rel_type, to_id)
689
+ );
690
+
691
+ -- Step 4: Migrate surviving relations (only rows where both from_id and to_id exist in documents)
692
+ INSERT INTO relations_v11 (id, from_id, rel_type, to_id, properties, created_at, updated_at)
693
+ SELECT r.id, r.from_id, r.rel_type, r.to_id, r.properties, r.created_at, r.updated_at
694
+ FROM relations r
695
+ WHERE EXISTS (SELECT 1 FROM documents d WHERE d.id = r.from_id)
696
+ AND EXISTS (SELECT 1 FROM documents d WHERE d.id = r.to_id);
697
+
698
+ -- Step 5: Swap tables
699
+ DROP TABLE IF EXISTS relations;
700
+ ALTER TABLE relations_v11 RENAME TO relations;
701
+
702
+ -- Step 6: Recreate indexes
703
+ CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_id, rel_type);
704
+ CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_id, rel_type);
705
+ CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(rel_type);
706
+ `,
707
+ },
708
+ {
709
+ version: 12,
710
+ description: 'URD-034: Drop reviewer and review_notes columns from tasks table (unified owner model)',
711
+ sql: `
712
+ -- Drop reviewer column if it exists (ownership unified under existing owner column)
713
+ -- Dropping a non-existent column in SQLite 3.35+ throws an error.
714
+ -- Use PRAGMA table_info to check existence before dropping.
715
+ -- This migration is idempotent: for fresh DBs created with the updated initial
716
+ -- schema (which excludes these columns), the DROP is skipped automatically
717
+ -- via the migration runner's version check; but to be safe, we catch the case.
718
+
719
+ -- Drop reviewer column
720
+ ALTER TABLE tasks DROP COLUMN reviewer;
721
+ -- Drop review_notes column
722
+ ALTER TABLE tasks DROP COLUMN review_notes;
723
+ `,
724
+ },
725
+ {
726
+ version: 13,
727
+ description: 'Per-sprint task ID policy: change tasks PK from id to composite (sprint_id, id)',
728
+ sql: `
729
+ -- Allow task IDs to reset per sprint by changing PK from global id to composite (sprint_id, id)
730
+ -- This matches the PMP workflow contract ID policy where sprint tasks, reviews, and test-reports
731
+ -- reset their 4-digit IDs at 0001 for each new sprint.
732
+
733
+ -- Step 1: Create new tasks table with composite PK
734
+ CREATE TABLE IF NOT EXISTS tasks_v13 (
735
+ id TEXT NOT NULL,
736
+ sprint_id TEXT NOT NULL REFERENCES sprints(id),
737
+ parent_id TEXT, -- cross-sprint parent references use global-unique IDs
738
+ description TEXT NOT NULL,
739
+ task_type TEXT NOT NULL DEFAULT 'Feature'
740
+ CHECK (task_type IN ('Feature','Bug','Chore')),
741
+ priority TEXT NOT NULL DEFAULT 'P2'
742
+ CHECK (priority IN ('P0','P1','P2')),
743
+ owner TEXT NOT NULL,
744
+ points INTEGER DEFAULT 0,
745
+ status TEXT NOT NULL DEFAULT 'Backlog'
746
+ CHECK (status IN ('Backlog','To Do','In Progress','In Review','Done','Dropped')),
747
+ stage TEXT NOT NULL DEFAULT 'Planning'
748
+ CHECK (stage IN ('Planning','Requirement','Specification','Design','Test Plan','Implementation','Testing','Release')),
749
+ session_key TEXT,
750
+ started_at TEXT,
751
+ completed_at TEXT,
752
+ scope_in TEXT,
753
+ scope_out TEXT,
754
+ decisions TEXT,
755
+ dependencies TEXT,
756
+ acceptance TEXT,
757
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
758
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
759
+ removed_at TEXT,
760
+ PRIMARY KEY (sprint_id, id)
761
+ );
762
+
763
+ -- Step 2: Copy data
764
+ INSERT INTO tasks_v13 (
765
+ id, sprint_id, parent_id, description, task_type, priority,
766
+ owner, points, status, stage, session_key, started_at,
767
+ completed_at, scope_in, scope_out, decisions, dependencies,
768
+ acceptance, created_at, updated_at, removed_at
769
+ )
770
+ SELECT
771
+ id, sprint_id, parent_id, description, task_type, priority,
772
+ owner, points, status, stage, session_key, started_at,
773
+ completed_at, scope_in, scope_out, decisions, dependencies,
774
+ acceptance, created_at, updated_at, removed_at
775
+ FROM tasks;
776
+
777
+ -- Step 3: Swap tables
778
+ DROP TABLE IF EXISTS tasks;
779
+ ALTER TABLE tasks_v13 RENAME TO tasks;
780
+
781
+ -- Step 4: Recreate indexes
782
+ CREATE INDEX IF NOT EXISTS idx_tasks_sprint ON tasks(sprint_id);
783
+ CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
784
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(sprint_id, status);
785
+ CREATE INDEX IF NOT EXISTS idx_tasks_owner ON tasks(sprint_id, owner);
786
+ CREATE INDEX IF NOT EXISTS idx_tasks_sprint_id_id ON tasks(sprint_id, id);
787
+ `,
788
+ },
789
+ ];
790
+ /** Current latest migration version */
791
+ exports.LATEST_VERSION = exports.MIGRATIONS[exports.MIGRATIONS.length - 1].version;
792
+ /** List of all table names expected in the schema */
793
+ exports.EXPECTED_TABLES = [
794
+ '_migrations',
795
+ 'locks',
796
+ 'sprints',
797
+ 'tasks',
798
+ // builtin:3.0 (Sprint 31): entity_types table removed — doc types are sole source of truth
799
+ // SAD-0035: entities table dropped — replaced by documents(id) FKs on relations
800
+ 'relation_types',
801
+ 'relations',
802
+ 'graph_schema',
803
+ 'reviews',
804
+ 'documents',
805
+ 'document_contents',
806
+ 'document_contents_fts',
807
+ 'test_reports',
808
+ 'lessons',
809
+ ];
810
+ /** List of all index names expected in the schema */
811
+ exports.EXPECTED_INDEXES = [
812
+ 'idx_tasks_sprint',
813
+ 'idx_tasks_parent',
814
+ 'idx_tasks_status',
815
+ 'idx_tasks_owner',
816
+ 'idx_relations_from',
817
+ 'idx_relations_to',
818
+ 'idx_relations_type',
819
+ 'idx_reviews_sprint',
820
+ 'idx_reviews_verdict',
821
+ 'idx_reviews_sprint_num',
822
+ 'idx_documents_type',
823
+ 'idx_reviews_type',
824
+ 'idx_test_reports_sprint',
825
+ 'idx_test_reports_module',
826
+ 'idx_tasks_sprint_id_id',
827
+ 'idx_reviews_sprint_id_id',
828
+ 'idx_test_reports_sprint_id_id',
829
+ ];
830
+ //# sourceMappingURL=schema.js.map