M0001_Initial.vala 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. using InvercargillSql.Migrations;
  2. namespace ClankerSaddle.Data.Migrations {
  3. /**
  4. * The full ClankerSaddle schema in one migration (plan 11: every
  5. * earlier migration flattened — the database is not deployed, so
  6. * tear-down-and-recreate replaced incremental evolution; plan 17
  7. * flattened again over the force-wake era). Covers: projects and
  8. * their links; the plan-12 goals (with links — plan 15 C1/C2
  9. * deleted the goal-side block/comment tables: the goal agent's
  10. * blocks and comments ride the run task's own rows) and the
  11. * flattened task shape (`goal_id`, `kind`, `mode`, `creator`,
  12. * `enqueue_after_task_id`, plus the force-wake flags folded in
  13. * from the deleted M0002); tasks with comments and typed blocks;
  14. * saddle registrations with git identity and the per-saddle
  15. * token; sessions; the plan-17 UNIFIED EVENT LEDGER (`events`)
  16. * that replaced `session_commands`, `task_progress`,
  17. * `session_raw_log` and `saddle_entry_dedup` — every timeline,
  18. * narrative, resume-notice and log-view surface derives from it,
  19. * and daemon chunk pushes are idempotent on its
  20. * UNIQUE(session_id, seq); rules and their attachments; project
  21. * notes; capability tags; the plan-11 notification tables.
  22. */
  23. public class M0001_Initial : Migration {
  24. public override string name { get { return "Create the ClankerSaddle schema"; } }
  25. public override uint64 serial { get { return 1; } }
  26. public override string migration_namespace { get { return "clanker-saddle"; } }
  27. public override void up(MigrationBuilder b) {
  28. b.create_table("projects", (t) => {
  29. t.column<int64?>("id")
  30. .auto_increment()
  31. .primary_key();
  32. t.column<string>("name")
  33. .not_null();
  34. t.column<string>("git_url")
  35. .not_null();
  36. t.column<string>("default_branch")
  37. .default_value<string>("main");
  38. t.column<string>("summary");
  39. t.column<DateTime?>("created_at");
  40. });
  41. b.create_table("project_links", (t) => {
  42. t.column<int64?>("id")
  43. .auto_increment()
  44. .primary_key();
  45. t.column<int64?>("parent_project_id")
  46. .not_null()
  47. .references("projects", "id");
  48. t.column<int64?>("linked_project_id")
  49. .not_null()
  50. .references("projects", "id");
  51. t.column<string>("relationship")
  52. .not_null();
  53. });
  54. // Stacks (goal 8 task 2): a named bundle of reference
  55. // projects applied one-click to a project's or a goal's
  56. // link set — the common reference collections a fleet of
  57. // repos always wants attached.
  58. b.create_table("stacks", (t) => {
  59. t.column<int64?>("id")
  60. .auto_increment()
  61. .primary_key();
  62. t.column<string>("name")
  63. .not_null()
  64. .unique();
  65. t.column<string>("summary");
  66. t.column<DateTime?>("created_at");
  67. });
  68. b.create_table("stack_projects", (t) => {
  69. t.column<int64?>("id")
  70. .auto_increment()
  71. .primary_key();
  72. t.column<int64?>("stack_id")
  73. .not_null()
  74. .references("stacks", "id")
  75. .indexed();
  76. t.column<int64?>("project_id")
  77. .not_null()
  78. .references("projects", "id")
  79. .indexed();
  80. t.column<string>("relationship")
  81. .not_null()
  82. .default_value<string>("reference");
  83. t.column<DateTime?>("created_at");
  84. t.unique("uq_stack_projects_pair", {"stack_id", "project_id"});
  85. });
  86. b.create_table("goals", (t) => {
  87. t.column<int64?>("id")
  88. .auto_increment()
  89. .primary_key();
  90. t.column<int64?>("project_id")
  91. .not_null()
  92. .references("projects", "id")
  93. .indexed();
  94. t.column<string>("title")
  95. .not_null();
  96. t.column<string>("brief")
  97. .not_null();
  98. t.column<string>("branch_name")
  99. .not_null();
  100. t.column<string>("base_branch");
  101. t.column<string>("status")
  102. .default_value<string>("draft")
  103. .indexed();
  104. t.column<int64?>("priority")
  105. .default_value<int64?>(3);
  106. t.column<string>("plan");
  107. t.column<string>("completion_report");
  108. t.column<string>("creator")
  109. .default_value<string>("human");
  110. t.column<DateTime?>("last_terminal_watermark");
  111. t.column<DateTime?>("created_at");
  112. t.column<DateTime?>("started_at");
  113. t.column<DateTime?>("completed_at");
  114. t.column<DateTime?>("paused_at");
  115. t.column<bool>("force_wake")
  116. .default_value<bool>(false);
  117. });
  118. b.create_table("goal_links", (t) => {
  119. t.column<int64?>("id")
  120. .auto_increment()
  121. .primary_key();
  122. t.column<int64?>("goal_id")
  123. .not_null()
  124. .references("goals", "id")
  125. .indexed();
  126. t.column<int64?>("project_id")
  127. .not_null()
  128. .references("projects", "id")
  129. .indexed();
  130. t.column<string>("relationship")
  131. .not_null();
  132. });
  133. b.create_table("tasks", (t) => {
  134. t.column<int64?>("id")
  135. .auto_increment()
  136. .primary_key();
  137. t.column<int64?>("project_id")
  138. .not_null()
  139. .references("projects", "id")
  140. .indexed();
  141. t.column<int64?>("goal_id")
  142. .references("goals", "id")
  143. .indexed();
  144. t.column<string>("kind")
  145. .default_value<string>("task");
  146. t.column<string>("mode")
  147. .default_value<string>("implement");
  148. t.column<string>("creator")
  149. .default_value<string>("human");
  150. t.column<int64?>("enqueue_after_task_id")
  151. .references("tasks", "id");
  152. t.column<string>("title")
  153. .not_null();
  154. t.column<string>("description");
  155. t.column<string>("status")
  156. .default_value<string>("draft")
  157. .indexed();
  158. t.column<int64?>("priority")
  159. .default_value<int64?>(5);
  160. t.column<string>("summary");
  161. t.column<string>("task_branch");
  162. t.column<string>("branch_from");
  163. t.column<DateTime?>("created_at");
  164. t.column<DateTime?>("started_at");
  165. t.column<DateTime?>("completed_at");
  166. t.column<DateTime?>("suspended_at");
  167. t.column<bool>("force_wake")
  168. .default_value<bool>(false);
  169. });
  170. b.create_table("task_comments", (t) => {
  171. t.column<int64?>("id")
  172. .auto_increment()
  173. .primary_key();
  174. t.column<int64?>("task_id")
  175. .not_null()
  176. .references("tasks", "id")
  177. .indexed();
  178. t.column<string>("author")
  179. .not_null();
  180. t.column<string>("body")
  181. .not_null();
  182. t.column<string>("quote");
  183. t.column<DateTime?>("created_at");
  184. });
  185. b.create_table("task_blocks", (t) => {
  186. t.column<int64?>("id")
  187. .auto_increment()
  188. .primary_key();
  189. t.column<int64?>("task_id")
  190. .not_null()
  191. .references("tasks", "id")
  192. .indexed();
  193. t.column<string>("question")
  194. .not_null();
  195. t.column<string>("answer");
  196. t.column<string>("type")
  197. .default_value<string>("question");
  198. t.column<string>("options");
  199. t.column<string>("body");
  200. t.column<DateTime?>("answered_at");
  201. t.column<string>("answered_by");
  202. t.column<DateTime?>("created_at");
  203. });
  204. b.create_table("saddles", (t) => {
  205. t.column<int64?>("id")
  206. .auto_increment()
  207. .primary_key();
  208. t.column<string>("name")
  209. .not_null();
  210. t.column<string>("host")
  211. .not_null();
  212. t.column<string>("status")
  213. .default_value<string>("idle");
  214. t.column<string>("git_name");
  215. t.column<string>("git_email");
  216. t.column<string>("ssh_public_key");
  217. t.column<string>("token");
  218. });
  219. b.create_table("sessions", (t) => {
  220. t.column<int64?>("id")
  221. .auto_increment()
  222. .primary_key();
  223. t.column<int64?>("task_id")
  224. .not_null()
  225. .references("tasks", "id")
  226. .indexed();
  227. t.column<int64?>("saddle_id")
  228. .not_null()
  229. .references("saddles", "id");
  230. t.column<DateTime?>("started_at");
  231. t.column<DateTime?>("ended_at");
  232. t.column<string>("outcome");
  233. t.column<string>("resume_notice");
  234. });
  235. // Plan 17 §7.3: the unified append-only event ledger.
  236. // Daemon events carry their session and its monotonic seq
  237. // (idempotent upsert on UNIQUE(session_id, seq) — the
  238. // entire dedup machinery in one constraint); web-originated
  239. // events carry neither (NULL session_id rows never collide
  240. // under SQLite's NULLs-distinct unique indexes). Everything
  241. // downstream — timelines, goal narratives, resume-notice
  242. // coalescing, the event log view — derives from this table.
  243. b.create_table("events", (t) => {
  244. t.column<int64?>("id")
  245. .auto_increment()
  246. .primary_key();
  247. t.column<int64?>("session_id")
  248. .references("sessions", "id");
  249. t.column<int64?>("seq");
  250. t.column<string>("kind")
  251. .not_null();
  252. t.column<string>("actor")
  253. .not_null();
  254. t.column<string>("scope_type")
  255. .not_null();
  256. t.column<int64?>("scope_id")
  257. .not_null();
  258. t.column<string>("payload")
  259. .not_null();
  260. t.column<DateTime?>("created_at")
  261. .not_null();
  262. t.unique("uq_events_session_seq", {"session_id", "seq"});
  263. });
  264. b.create_table("rules", (t) => {
  265. t.column<int64?>("id")
  266. .auto_increment()
  267. .primary_key();
  268. t.column<string>("name")
  269. .not_null()
  270. .unique();
  271. t.column<string>("body")
  272. .not_null();
  273. t.column<DateTime?>("created_at");
  274. t.column<DateTime?>("updated_at");
  275. });
  276. b.create_table("rule_projects", (t) => {
  277. t.column<int64?>("id")
  278. .auto_increment()
  279. .primary_key();
  280. t.column<int64?>("rule_id")
  281. .not_null()
  282. .references("rules", "id")
  283. .indexed();
  284. t.column<int64?>("project_id")
  285. .not_null()
  286. .references("projects", "id")
  287. .indexed();
  288. t.column<DateTime?>("created_at");
  289. t.unique("uq_rule_projects_pair", {"rule_id", "project_id"});
  290. });
  291. b.create_table("task_rules", (t) => {
  292. t.column<int64?>("id")
  293. .auto_increment()
  294. .primary_key();
  295. t.column<int64?>("rule_id")
  296. .not_null()
  297. .references("rules", "id")
  298. .indexed();
  299. t.column<int64?>("task_id")
  300. .not_null()
  301. .references("tasks", "id")
  302. .indexed();
  303. t.column<DateTime?>("created_at");
  304. t.unique("uq_task_rules_pair", {"rule_id", "task_id"});
  305. });
  306. b.create_table("project_notes", (t) => {
  307. t.column<int64?>("id")
  308. .auto_increment()
  309. .primary_key();
  310. t.column<int64?>("project_id")
  311. .not_null()
  312. .references("projects", "id")
  313. .indexed();
  314. t.column<int64?>("source_task_id")
  315. .references("tasks", "id");
  316. t.column<string>("body")
  317. .not_null();
  318. t.column<DateTime?>("created_at");
  319. });
  320. b.create_table("capabilities", (t) => {
  321. t.column<int64?>("id")
  322. .auto_increment()
  323. .primary_key();
  324. t.column<string>("name")
  325. .not_null()
  326. .unique();
  327. t.column<DateTime?>("created_at");
  328. });
  329. b.create_table("saddle_capabilities", (t) => {
  330. t.column<int64?>("id")
  331. .auto_increment()
  332. .primary_key();
  333. t.column<int64?>("capability_id")
  334. .not_null()
  335. .references("capabilities", "id")
  336. .indexed();
  337. t.column<int64?>("saddle_id")
  338. .not_null()
  339. .references("saddles", "id")
  340. .indexed();
  341. t.unique("uq_saddle_capabilities_pair", {"capability_id", "saddle_id"});
  342. });
  343. b.create_table("task_capabilities", (t) => {
  344. t.column<int64?>("id")
  345. .auto_increment()
  346. .primary_key();
  347. t.column<int64?>("capability_id")
  348. .not_null()
  349. .references("capabilities", "id")
  350. .indexed();
  351. t.column<int64?>("task_id")
  352. .not_null()
  353. .references("tasks", "id")
  354. .indexed();
  355. t.unique("uq_task_capabilities_pair", {"capability_id", "task_id"});
  356. });
  357. b.create_table("push_subscriptions", (t) => {
  358. t.column<int64?>("id")
  359. .auto_increment()
  360. .primary_key();
  361. t.column<int64?>("user_id")
  362. .not_null()
  363. .indexed();
  364. t.column<string>("endpoint")
  365. .not_null()
  366. .unique();
  367. t.column<string>("p256dh")
  368. .not_null();
  369. t.column<string>("auth")
  370. .not_null();
  371. t.column<DateTime?>("created_at");
  372. });
  373. b.create_table("task_notify_prefs", (t) => {
  374. t.column<int64?>("id")
  375. .auto_increment()
  376. .primary_key();
  377. t.column<int64?>("task_id")
  378. .not_null()
  379. .references("tasks", "id")
  380. .indexed();
  381. t.column<int64?>("user_id")
  382. .not_null()
  383. .indexed();
  384. t.unique("uq_task_notify_prefs_pair", {"task_id", "user_id"});
  385. });
  386. }
  387. public override void down(MigrationBuilder b) {
  388. b.drop_table("task_notify_prefs");
  389. b.drop_table("push_subscriptions");
  390. b.drop_table("task_capabilities");
  391. b.drop_table("saddle_capabilities");
  392. b.drop_table("capabilities");
  393. b.drop_table("project_notes");
  394. b.drop_table("task_rules");
  395. b.drop_table("rule_projects");
  396. b.drop_table("rules");
  397. b.drop_table("events");
  398. b.drop_table("sessions");
  399. b.drop_table("saddles");
  400. b.drop_table("task_blocks");
  401. b.drop_table("task_comments");
  402. b.drop_table("tasks");
  403. b.drop_table("goal_links");
  404. b.drop_table("goals");
  405. b.drop_table("project_links");
  406. b.drop_table("stack_projects");
  407. b.drop_table("stacks");
  408. b.drop_table("projects");
  409. }
  410. }
  411. }