ndomo 0.1.0

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 (247) hide show
  1. package/.bun-version +1 -0
  2. package/.dockerignore +79 -0
  3. package/.editorconfig +18 -0
  4. package/.env.example +19 -0
  5. package/.github/CODEOWNERS +8 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +2 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
  9. package/.github/dependabot.yml +36 -0
  10. package/.github/pull_request_template.md +24 -0
  11. package/.github/release.yml +30 -0
  12. package/.github/workflows/gitleaks.yml +28 -0
  13. package/.github/workflows/release-please.yml +27 -0
  14. package/.github/workflows/smoke.yml +29 -0
  15. package/.husky/commit-msg +1 -0
  16. package/CHANGELOG.md +114 -0
  17. package/Dockerfile +32 -0
  18. package/README.es.md +174 -0
  19. package/README.md +187 -0
  20. package/agents/chronicler.md +98 -0
  21. package/agents/ci-smith.md +136 -0
  22. package/agents/craftsman.md +341 -0
  23. package/agents/deploy-smith.md +138 -0
  24. package/agents/foreman.md +377 -0
  25. package/agents/go-smith.md +164 -0
  26. package/agents/guild.md +188 -0
  27. package/agents/inspector.md +83 -0
  28. package/agents/js-smith.md +127 -0
  29. package/agents/ops-scout.md +173 -0
  30. package/agents/painter.md +200 -0
  31. package/agents/python-smith.md +120 -0
  32. package/agents/ranger.md +307 -0
  33. package/agents/release-smith.md +165 -0
  34. package/agents/rust-smith.md +159 -0
  35. package/agents/sage.md +178 -0
  36. package/agents/scout.md +144 -0
  37. package/agents/scribe.md +156 -0
  38. package/agents/smith.md +201 -0
  39. package/agents/vue-smith.md +155 -0
  40. package/agents/warden.md +216 -0
  41. package/agents/zig-smith.md +156 -0
  42. package/bin/ndomo-analyses.ts +4 -0
  43. package/bin/ndomo-status.ts +4 -0
  44. package/biome.json +57 -0
  45. package/bun.lock +514 -0
  46. package/commitlint.config.js +3 -0
  47. package/config/ndomo.config.json +258 -0
  48. package/config/ndomo.schema.json +166 -0
  49. package/docs/agents.md +375 -0
  50. package/docs/bugs/plan-create-orphan-fk.md +131 -0
  51. package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
  52. package/docs/configuration.md +276 -0
  53. package/docs/database.md +364 -0
  54. package/docs/features/feature-flexible-builder-v1.md +724 -0
  55. package/docs/features/feature-flexible-builder-v2.md +882 -0
  56. package/docs/features/feature-flexible-builder.md +974 -0
  57. package/docs/http-server.md +244 -0
  58. package/docs/installation.md +259 -0
  59. package/docs/integrations.md +129 -0
  60. package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
  61. package/docs/operations/audit-v1.md +417 -0
  62. package/docs/operations/audit-v2.md +197 -0
  63. package/docs/operations/audit-v3.md +306 -0
  64. package/docs/operations/db-optimize-foundations.md +123 -0
  65. package/docs/operations/verify-gate-architecture.md +82 -0
  66. package/docs/workflows.md +448 -0
  67. package/opencode.json +5 -0
  68. package/package.json +65 -0
  69. package/release-please-config.json +11 -0
  70. package/scripts/dev-bust-cache.sh +164 -0
  71. package/scripts/install.sh +688 -0
  72. package/scripts/smoke-e2e.ts +704 -0
  73. package/scripts/smoke-hot.ts +417 -0
  74. package/scripts/smoke-http.sh +228 -0
  75. package/scripts/smoke-v4.ts +256 -0
  76. package/scripts/smoke-v5.ts +397 -0
  77. package/scripts/smoke.sh +9 -0
  78. package/scripts/uninstall.sh +224 -0
  79. package/skills/api-security-best-practices/SKILL.md +915 -0
  80. package/skills/bash-scripting/SKILL.md +201 -0
  81. package/skills/bun/SKILL.md +313 -0
  82. package/skills/cavecrew/SKILL.md +82 -0
  83. package/skills/caveman/SKILL.md +74 -0
  84. package/skills/caveman-review/README.md +33 -0
  85. package/skills/caveman-review/SKILL.md +55 -0
  86. package/skills/find-skills/SKILL.md +142 -0
  87. package/skills/frontend-design/LICENSE.txt +177 -0
  88. package/skills/frontend-design/SKILL.md +55 -0
  89. package/skills/golang-patterns/SKILL.md +674 -0
  90. package/skills/golang-security/SKILL.md +185 -0
  91. package/skills/golang-security/evals/evals.json +595 -0
  92. package/skills/golang-security/references/architecture.md +268 -0
  93. package/skills/golang-security/references/checklist.md +80 -0
  94. package/skills/golang-security/references/cookies.md +200 -0
  95. package/skills/golang-security/references/cryptography.md +424 -0
  96. package/skills/golang-security/references/filesystem.md +285 -0
  97. package/skills/golang-security/references/injection.md +315 -0
  98. package/skills/golang-security/references/logging.md +163 -0
  99. package/skills/golang-security/references/memory-safety.md +241 -0
  100. package/skills/golang-security/references/network.md +253 -0
  101. package/skills/golang-security/references/secrets.md +189 -0
  102. package/skills/golang-security/references/third-party.md +159 -0
  103. package/skills/golang-security/references/threat-modeling.md +189 -0
  104. package/skills/golang-testing/SKILL.md +720 -0
  105. package/skills/grill-me/SKILL.md +7 -0
  106. package/skills/javascript-testing-patterns/SKILL.md +537 -0
  107. package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
  108. package/skills/modern-javascript-patterns/SKILL.md +43 -0
  109. package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
  110. package/skills/modern-javascript-patterns/references/details.md +457 -0
  111. package/skills/python-anti-patterns/SKILL.md +349 -0
  112. package/skills/python-design-patterns/SKILL.md +85 -0
  113. package/skills/python-design-patterns/references/details.md +353 -0
  114. package/skills/python-error-handling/SKILL.md +193 -0
  115. package/skills/python-error-handling/references/details.md +171 -0
  116. package/skills/python-testing-patterns/SKILL.md +278 -0
  117. package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
  118. package/skills/python-testing-patterns/references/details.md +349 -0
  119. package/skills/rust-patterns/SKILL.md +500 -0
  120. package/skills/rust-testing/SKILL.md +501 -0
  121. package/skills/security-review/SKILL.md +504 -0
  122. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  123. package/skills/vue-best-practices/SKILL.md +154 -0
  124. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  125. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  126. package/skills/vue-best-practices/references/component-async.md +97 -0
  127. package/skills/vue-best-practices/references/component-data-flow.md +307 -0
  128. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  129. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  130. package/skills/vue-best-practices/references/component-slots.md +216 -0
  131. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  132. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  133. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  134. package/skills/vue-best-practices/references/component-transition.md +125 -0
  135. package/skills/vue-best-practices/references/composables.md +290 -0
  136. package/skills/vue-best-practices/references/directives.md +162 -0
  137. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  138. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  139. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  140. package/skills/vue-best-practices/references/plugins.md +166 -0
  141. package/skills/vue-best-practices/references/reactivity.md +344 -0
  142. package/skills/vue-best-practices/references/render-functions.md +201 -0
  143. package/skills/vue-best-practices/references/sfc.md +310 -0
  144. package/skills/vue-best-practices/references/state-management.md +135 -0
  145. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  146. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  147. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  148. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  149. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  150. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  151. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  152. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  153. package/skills/zig-0.16/SKILL.md +840 -0
  154. package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
  155. package/src/cli/analyses.ts +280 -0
  156. package/src/cli/index.ts +108 -0
  157. package/src/cli/serve.ts +192 -0
  158. package/src/cli/smoke.ts +131 -0
  159. package/src/cli/status.test.ts +204 -0
  160. package/src/cli/status.ts +263 -0
  161. package/src/cli/vacuum.test.ts +82 -0
  162. package/src/cli/vacuum.ts +96 -0
  163. package/src/config/schema.test.ts +88 -0
  164. package/src/config/schema.ts +64 -0
  165. package/src/db/analyses-migration.test.ts +210 -0
  166. package/src/db/analyses.test.ts +466 -0
  167. package/src/db/analyses.ts +375 -0
  168. package/src/db/auto-checkpoint.ts +131 -0
  169. package/src/db/client.test.ts +129 -0
  170. package/src/db/client.ts +55 -0
  171. package/src/db/fts-escape.ts +20 -0
  172. package/src/db/incidents.test.ts +201 -0
  173. package/src/db/incidents.ts +93 -0
  174. package/src/db/index.ts +86 -0
  175. package/src/db/migrations-v13.test.ts +141 -0
  176. package/src/db/migrations-v8.test.ts +301 -0
  177. package/src/db/migrations.ts +147 -0
  178. package/src/db/plan-archive.test.ts +180 -0
  179. package/src/db/plan-archive.ts +274 -0
  180. package/src/db/plan-create.test.ts +276 -0
  181. package/src/db/plan-create.ts +78 -0
  182. package/src/db/plan-files.test.ts +289 -0
  183. package/src/db/plan-update-status.ts +287 -0
  184. package/src/db/plans.test.ts +490 -0
  185. package/src/db/plans.ts +534 -0
  186. package/src/db/resolve-project-dir.test.ts +143 -0
  187. package/src/db/resolve-project-dir.ts +75 -0
  188. package/src/db/rollbacks.test.ts +150 -0
  189. package/src/db/rollbacks.ts +67 -0
  190. package/src/db/schema.ts +907 -0
  191. package/src/db/sessions.test.ts +80 -0
  192. package/src/db/sessions.ts +135 -0
  193. package/src/db/shutdown.test.ts +147 -0
  194. package/src/db/shutdown.ts +45 -0
  195. package/src/db/tasks.test.ts +921 -0
  196. package/src/db/tasks.ts +747 -0
  197. package/src/db/types.ts +619 -0
  198. package/src/http/__tests__/auth.test.ts +196 -0
  199. package/src/http/__tests__/routes.test.ts +465 -0
  200. package/src/http/__tests__/sse.test.ts +317 -0
  201. package/src/http/auth.ts +72 -0
  202. package/src/http/middleware/cors.ts +53 -0
  203. package/src/http/middleware/security-headers.ts +21 -0
  204. package/src/http/routes/events.ts +112 -0
  205. package/src/http/routes/health.ts +51 -0
  206. package/src/http/routes/plans.ts +66 -0
  207. package/src/http/routes/sessions.ts +50 -0
  208. package/src/http/routes/tasks.ts +60 -0
  209. package/src/http/server.ts +95 -0
  210. package/src/http/sse.ts +116 -0
  211. package/src/index.ts +37 -0
  212. package/src/lib.ts +65 -0
  213. package/src/mem/scoped.ts +65 -0
  214. package/src/orchestrator/background.test.ts +268 -0
  215. package/src/orchestrator/background.ts +293 -0
  216. package/src/orchestrator/memory-hook.ts +182 -0
  217. package/src/orchestrator/reconciler.ts +123 -0
  218. package/src/orchestrator/scheduler.test.ts +300 -0
  219. package/src/orchestrator/scheduler.ts +243 -0
  220. package/src/plugin.test.ts +2574 -0
  221. package/src/plugin.ts +1690 -0
  222. package/src/sdk/client.ts +66 -0
  223. package/src/worktrees/manager.ts +236 -0
  224. package/src/worktrees/state.ts +87 -0
  225. package/tests/integration/ranger-flow.test.ts +257 -0
  226. package/tools/analysis_archive.ts +28 -0
  227. package/tools/analysis_create.ts +55 -0
  228. package/tools/analysis_get.ts +33 -0
  229. package/tools/analysis_link_plan.ts +44 -0
  230. package/tools/analysis_list.ts +48 -0
  231. package/tools/analysis_search.ts +36 -0
  232. package/tools/analysis_update.ts +44 -0
  233. package/tools/plan_approve.ts +31 -0
  234. package/tools/plan_create.ts +58 -0
  235. package/tools/plan_get.ts +40 -0
  236. package/tools/plan_list.ts +37 -0
  237. package/tools/plan_search.ts +34 -0
  238. package/tools/plan_update_status.ts +71 -0
  239. package/tools/session_checkpoint.ts +31 -0
  240. package/tools/session_end.ts +26 -0
  241. package/tools/session_start.ts +43 -0
  242. package/tools/task_create_batch.ts +70 -0
  243. package/tools/task_list.ts +35 -0
  244. package/tools/task_next_for_agent.ts +30 -0
  245. package/tools/task_search.ts +34 -0
  246. package/tools/task_update_status.ts +37 -0
  247. package/tsconfig.json +31 -0
@@ -0,0 +1,619 @@
1
+ /**
2
+ * ndomo DB — Type definitions for plans, tasks, and sessions.
3
+ *
4
+ * All interfaces use camelCase. Mapper functions convert snake_case
5
+ * SQLite rows into typed objects using nullish coalescing for nullable
6
+ * fields and JSON.parse for serialized columns.
7
+ */
8
+
9
+ export type PlanStatus = "draft" | "approved" | "executing" | "completed" | "failed" | "abandoned";
10
+
11
+ export type TaskStatus = "pending" | "running" | "done" | "failed" | "blocked";
12
+
13
+ export type PlanCategory = "feature" | "refactor" | "bugfix" | "docs" | "infra";
14
+
15
+ export type SessionOutcome = "success" | "partial" | "failed" | "abandoned";
16
+
17
+ export interface PlanMetadata {
18
+ category?: PlanCategory;
19
+ externalRefs?: {
20
+ githubPrUrl?: string;
21
+ jiraTicket?: string;
22
+ };
23
+ }
24
+
25
+ export interface TaskMetadata {
26
+ reviewedBy?: string;
27
+ tokensUsed?: number;
28
+ durationMs?: number;
29
+ artifacts?: string[];
30
+ }
31
+
32
+ export interface SessionMetadata {
33
+ outcome?: SessionOutcome;
34
+ metrics?: {
35
+ totalTokens?: number;
36
+ totalTasks?: number;
37
+ totalDurationMs?: number;
38
+ };
39
+ }
40
+
41
+ export interface Plan {
42
+ id: string;
43
+ slug: string;
44
+ title: string;
45
+ status: PlanStatus;
46
+ priority: number;
47
+ createdAt: number;
48
+ updatedAt: number;
49
+ approvedAt: number | null;
50
+ completedAt: number | null;
51
+ /** @see sessions.id — foreign key constraint enforced at app level (v3 Fix #1) */
52
+ sessionId: string | null;
53
+ overview: string;
54
+ approach: string | null;
55
+ complexity: number;
56
+ createdBy: string;
57
+ updatedBy: string;
58
+ sourceSessionId: string | null;
59
+ sourceMessageId: string | null;
60
+ category: PlanCategory | null;
61
+ metadata: PlanMetadata;
62
+ /** v5: soft delete timestamp (null = active, number = archived epoch ms) */
63
+ archivedAt: number | null;
64
+ /** v6: write-once — JSON snapshot of plan data at creation time */
65
+ originalPlanData?: string | null;
66
+ /** v8: agent that created this plan (distinct from createdBy which is audit user) */
67
+ createdByAgent?: string | null;
68
+ /** v8: agent that last executed work on this plan */
69
+ executedByAgent?: string | null;
70
+ /** v8: session that last executed work on this plan (FK sessions, app-level) */
71
+ executedBySession?: string | null;
72
+ /** v7: files associated with this plan (from plan_files join table) */
73
+ files?: Array<{ filePath: string; role: string }>;
74
+ }
75
+
76
+ export interface PlanTask {
77
+ id: string;
78
+ planId: string;
79
+ orderIndex: number;
80
+ description: string;
81
+ agent: string;
82
+ files: string[];
83
+ complexity: number;
84
+ status: TaskStatus;
85
+ startedAt: number | null;
86
+ completedAt: number | null;
87
+ result: string | null;
88
+ error: string | null;
89
+ dependencies: string[];
90
+ createdBy: string;
91
+ updatedBy: string;
92
+ sourceSessionId: string | null;
93
+ sourceMessageId: string | null;
94
+ reviewedBy: string | null;
95
+ tokensUsed: number | null;
96
+ durationMs: number | null;
97
+ artifacts: string[];
98
+ metadata: TaskMetadata;
99
+ /** v5: soft delete timestamp (null = active, number = archived epoch ms) */
100
+ archivedAt: number | null;
101
+ /** v6: write-once — JSON snapshot of task data at creation time */
102
+ originalPlanData?: string | null;
103
+ }
104
+
105
+ export interface Session {
106
+ id: string;
107
+ startedAt: number;
108
+ endedAt: number | null;
109
+ lastCheckpoint: number | null;
110
+ planId: string | null;
111
+ goal: string;
112
+ state: Record<string, unknown>;
113
+ agentHistory: Array<{
114
+ agent: string;
115
+ taskId: string | null;
116
+ startedAt: number;
117
+ endedAt: number | null;
118
+ }>;
119
+ keyDecisions: string | null;
120
+ createdBy: string;
121
+ sourceMessageId: string | null;
122
+ parentSessionId: string | null;
123
+ outcome: SessionOutcome | null;
124
+ metadata: SessionMetadata;
125
+ /** v5: soft delete timestamp (null = active, number = archived epoch ms) */
126
+ archivedAt: number | null;
127
+ }
128
+
129
+ // ─── Row mappers ─────────────────────────────────────────────────────────────
130
+
131
+ interface PlanRow {
132
+ id: string;
133
+ slug: string;
134
+ title: string;
135
+ status: string;
136
+ priority: number;
137
+ created_at: number;
138
+ updated_at: number;
139
+ approved_at: number | null;
140
+ completed_at: number | null;
141
+ session_id: string | null;
142
+ overview: string;
143
+ approach: string | null;
144
+ complexity: number;
145
+ metadata: string | null;
146
+ created_by: string;
147
+ updated_by: string;
148
+ source_session_id: string | null;
149
+ source_message_id: string | null;
150
+ category: string | null;
151
+ archived_at: number | null;
152
+ original_plan_data: string | null;
153
+ created_by_agent: string | null;
154
+ executed_by_agent: string | null;
155
+ executed_by_session: string | null;
156
+ }
157
+
158
+ interface TaskRow {
159
+ id: string;
160
+ plan_id: string;
161
+ order_index: number;
162
+ description: string;
163
+ agent: string;
164
+ files: string;
165
+ complexity: number;
166
+ status: string;
167
+ started_at: number | null;
168
+ completed_at: number | null;
169
+ result: string | null;
170
+ error: string | null;
171
+ dependencies: string;
172
+ metadata: string | null;
173
+ created_by: string;
174
+ updated_by: string;
175
+ source_session_id: string | null;
176
+ source_message_id: string | null;
177
+ reviewed_by: string | null;
178
+ tokens_used: number | null;
179
+ duration_ms: number | null;
180
+ artifacts: string;
181
+ archived_at: number | null;
182
+ original_plan_data: string | null;
183
+ }
184
+
185
+ interface SessionRow {
186
+ id: string;
187
+ started_at: number;
188
+ ended_at: number | null;
189
+ last_checkpoint: number | null;
190
+ plan_id: string | null;
191
+ goal: string;
192
+ state: string;
193
+ agent_history: string;
194
+ key_decisions: string | null;
195
+ metadata: string | null;
196
+ created_by: string;
197
+ source_message_id: string | null;
198
+ parent_session_id: string | null;
199
+ outcome: string | null;
200
+ archived_at: number | null;
201
+ }
202
+
203
+ export function planFromRow(row: unknown): Plan {
204
+ const r = row as PlanRow;
205
+ return {
206
+ id: r.id,
207
+ slug: r.slug,
208
+ title: r.title,
209
+ status: r.status as PlanStatus,
210
+ priority: r.priority,
211
+ createdAt: r.created_at,
212
+ updatedAt: r.updated_at,
213
+ approvedAt: r.approved_at ?? null,
214
+ completedAt: r.completed_at ?? null,
215
+ sessionId: r.session_id ?? null,
216
+ overview: r.overview,
217
+ approach: r.approach ?? null,
218
+ complexity: r.complexity,
219
+ createdBy: r.created_by ?? "unknown",
220
+ updatedBy: r.updated_by ?? "unknown",
221
+ sourceSessionId: r.source_session_id ?? null,
222
+ sourceMessageId: r.source_message_id ?? null,
223
+ category: (r.category ?? null) as PlanCategory | null,
224
+ metadata: (r.metadata != null ? JSON.parse(r.metadata) : {}) as PlanMetadata,
225
+ archivedAt: r.archived_at ?? null,
226
+ originalPlanData: r.original_plan_data ?? null,
227
+ createdByAgent: r.created_by_agent ?? null,
228
+ executedByAgent: r.executed_by_agent ?? null,
229
+ executedBySession: r.executed_by_session ?? null,
230
+ };
231
+ }
232
+
233
+ interface PlanFileRow {
234
+ file_path: string;
235
+ role: string;
236
+ }
237
+
238
+ export function planWithFilesFromRow(planRow: unknown, fileRows: unknown[]): Plan {
239
+ const plan = planFromRow(planRow);
240
+ const files = (fileRows as PlanFileRow[]).map((f) => ({
241
+ filePath: f.file_path,
242
+ role: f.role,
243
+ }));
244
+ return { ...plan, files };
245
+ }
246
+
247
+ export function taskFromRow(row: unknown): PlanTask {
248
+ const r = row as TaskRow;
249
+ return {
250
+ id: r.id,
251
+ planId: r.plan_id,
252
+ orderIndex: r.order_index,
253
+ description: r.description,
254
+ agent: r.agent,
255
+ files: (JSON.parse(r.files) as string[]) ?? [],
256
+ complexity: r.complexity,
257
+ status: r.status as TaskStatus,
258
+ startedAt: r.started_at ?? null,
259
+ completedAt: r.completed_at ?? null,
260
+ result: r.result ?? null,
261
+ error: r.error ?? null,
262
+ dependencies: (JSON.parse(r.dependencies) as string[]) ?? [],
263
+ createdBy: r.created_by ?? "unknown",
264
+ updatedBy: r.updated_by ?? "unknown",
265
+ sourceSessionId: r.source_session_id ?? null,
266
+ sourceMessageId: r.source_message_id ?? null,
267
+ reviewedBy: r.reviewed_by ?? null,
268
+ tokensUsed: r.tokens_used ?? null,
269
+ durationMs: r.duration_ms ?? null,
270
+ artifacts: (JSON.parse(r.artifacts) as string[]) ?? [],
271
+ metadata: (r.metadata != null ? JSON.parse(r.metadata) : {}) as TaskMetadata,
272
+ archivedAt: r.archived_at ?? null,
273
+ originalPlanData: r.original_plan_data ?? null,
274
+ };
275
+ }
276
+
277
+ export function sessionFromRow(row: unknown): Session {
278
+ const r = row as SessionRow;
279
+ return {
280
+ id: r.id,
281
+ startedAt: r.started_at,
282
+ endedAt: r.ended_at ?? null,
283
+ lastCheckpoint: r.last_checkpoint ?? null,
284
+ planId: r.plan_id ?? null,
285
+ goal: r.goal,
286
+ state: (JSON.parse(r.state) as Record<string, unknown>) ?? {},
287
+ agentHistory: (JSON.parse(r.agent_history) as Session["agentHistory"]) ?? [],
288
+ keyDecisions: r.key_decisions ?? null,
289
+ createdBy: r.created_by ?? "unknown",
290
+ sourceMessageId: r.source_message_id ?? null,
291
+ parentSessionId: r.parent_session_id ?? null,
292
+ outcome: (r.outcome ?? null) as SessionOutcome | null,
293
+ metadata: (r.metadata != null ? JSON.parse(r.metadata) : {}) as SessionMetadata,
294
+ archivedAt: r.archived_at ?? null,
295
+ };
296
+ }
297
+
298
+ // ── v13: Ops types ──────────────────────────────────────────────
299
+
300
+ export type EnvironmentSlug = string
301
+ export type ReleaseVersion = string
302
+
303
+ export type DeploymentStatus = "planned" | "in_progress" | "succeeded" | "failed" | "rolled_back"
304
+ export type IncidentSeverity = "sev1" | "sev2" | "sev3" | "sev4"
305
+ export type IncidentStatus = "open" | "triaging" | "mitigated" | "resolved" | "postmortem"
306
+ export type RollbackStatus = "planned" | "approved" | "dry_run" | "executing" | "success" | "failed" | "cancelled"
307
+
308
+ export interface Environment {
309
+ id: string
310
+ name: string
311
+ slug: string
312
+ description: string | null
313
+ metadata: Record<string, unknown> | null
314
+ createdAt: number
315
+ updatedAt: number
316
+ archivedAt: number | null
317
+ }
318
+
319
+ export interface Release {
320
+ id: string
321
+ version: string
322
+ title: string
323
+ notes: string | null
324
+ metadata: Record<string, unknown> | null
325
+ createdAt: number
326
+ archivedAt: number | null
327
+ }
328
+
329
+ export interface Deployment {
330
+ id: string
331
+ releaseId: string
332
+ environmentId: string
333
+ status: DeploymentStatus
334
+ deployedAt: number | null
335
+ createdAt: number
336
+ metadata: Record<string, unknown> | null
337
+ }
338
+
339
+ export interface Incident {
340
+ id: string
341
+ title: string
342
+ severity: IncidentSeverity
343
+ status: IncidentStatus
344
+ summary: string | null
345
+ triggeredByDeploymentId: string | null
346
+ createdAt: number
347
+ updatedAt: number
348
+ resolvedAt: number | null
349
+ metadata: Record<string, unknown> | null
350
+ }
351
+
352
+ export interface RollbackExecution {
353
+ id: string
354
+ deploymentId: string
355
+ incidentId: string | null
356
+ newDeploymentId: string | null
357
+ status: RollbackStatus
358
+ plan: string
359
+ executedAt: number | null
360
+ createdAt: number
361
+ metadata: Record<string, unknown> | null
362
+ }
363
+
364
+ // Insert types (for createIncident/recordRollback helpers)
365
+ export interface InsertIncident {
366
+ title: string
367
+ severity: IncidentSeverity
368
+ summary?: string
369
+ triggeredByDeploymentId?: string
370
+ metadata?: Record<string, unknown>
371
+ }
372
+
373
+ export interface InsertRollback {
374
+ deploymentId: string
375
+ incidentId?: string
376
+ newDeploymentId?: string
377
+ status?: RollbackStatus
378
+ plan: string
379
+ metadata?: Record<string, unknown>
380
+ }
381
+
382
+ // Row types (internal, for mappers)
383
+ interface EnvironmentRow {
384
+ id: string
385
+ name: string
386
+ slug: string
387
+ description: string | null
388
+ metadata: string | null
389
+ created_at: number
390
+ updated_at: number
391
+ archived_at: number | null
392
+ }
393
+
394
+ interface ReleaseRow {
395
+ id: string
396
+ version: string
397
+ title: string
398
+ notes: string | null
399
+ metadata: string | null
400
+ created_at: number
401
+ archived_at: number | null
402
+ }
403
+
404
+ interface DeploymentRow {
405
+ id: string
406
+ release_id: string
407
+ environment_id: string
408
+ status: string
409
+ deployed_at: number | null
410
+ created_at: number
411
+ metadata: string | null
412
+ }
413
+
414
+ interface IncidentRow {
415
+ id: string
416
+ title: string
417
+ severity: string
418
+ status: string
419
+ summary: string | null
420
+ triggered_by_deployment_id: string | null
421
+ created_at: number
422
+ updated_at: number
423
+ resolved_at: number | null
424
+ metadata: string | null
425
+ }
426
+
427
+ interface RollbackRow {
428
+ id: string
429
+ deployment_id: string
430
+ incident_id: string | null
431
+ new_deployment_id: string | null
432
+ status: string
433
+ plan: string
434
+ executed_at: number | null
435
+ created_at: number
436
+ metadata: string | null
437
+ }
438
+
439
+ export function environmentFromRow(row: unknown): Environment {
440
+ const r = row as EnvironmentRow
441
+ return {
442
+ id: r.id,
443
+ name: r.name,
444
+ slug: r.slug,
445
+ description: r.description ?? null,
446
+ metadata: r.metadata != null ? JSON.parse(r.metadata) : null,
447
+ createdAt: r.created_at,
448
+ updatedAt: r.updated_at,
449
+ archivedAt: r.archived_at ?? null,
450
+ }
451
+ }
452
+
453
+ export function releaseFromRow(row: unknown): Release {
454
+ const r = row as ReleaseRow
455
+ return {
456
+ id: r.id,
457
+ version: r.version,
458
+ title: r.title,
459
+ notes: r.notes ?? null,
460
+ metadata: r.metadata != null ? JSON.parse(r.metadata) : null,
461
+ createdAt: r.created_at,
462
+ archivedAt: r.archived_at ?? null,
463
+ }
464
+ }
465
+
466
+ export function deploymentFromRow(row: unknown): Deployment {
467
+ const r = row as DeploymentRow
468
+ return {
469
+ id: r.id,
470
+ releaseId: r.release_id,
471
+ environmentId: r.environment_id,
472
+ status: r.status as DeploymentStatus,
473
+ deployedAt: r.deployed_at ?? null,
474
+ createdAt: r.created_at,
475
+ metadata: r.metadata != null ? JSON.parse(r.metadata) : null,
476
+ }
477
+ }
478
+
479
+ export function incidentFromRow(row: unknown): Incident {
480
+ const r = row as IncidentRow
481
+ return {
482
+ id: r.id,
483
+ title: r.title,
484
+ severity: r.severity as IncidentSeverity,
485
+ status: r.status as IncidentStatus,
486
+ summary: r.summary ?? null,
487
+ triggeredByDeploymentId: r.triggered_by_deployment_id ?? null,
488
+ createdAt: r.created_at,
489
+ updatedAt: r.updated_at,
490
+ resolvedAt: r.resolved_at ?? null,
491
+ metadata: r.metadata != null ? JSON.parse(r.metadata) : null,
492
+ }
493
+ }
494
+
495
+ export function rollbackFromRow(row: unknown): RollbackExecution {
496
+ const r = row as RollbackRow
497
+ return {
498
+ id: r.id,
499
+ deploymentId: r.deployment_id,
500
+ incidentId: r.incident_id ?? null,
501
+ newDeploymentId: r.new_deployment_id ?? null,
502
+ status: r.status as RollbackStatus,
503
+ plan: r.plan,
504
+ executedAt: r.executed_at ?? null,
505
+ createdAt: r.created_at,
506
+ metadata: r.metadata != null ? JSON.parse(r.metadata) : null,
507
+ }
508
+ }
509
+
510
+ // ── v14: Analyses ───────────────────────────────────────────────
511
+
512
+ /**
513
+ * Severity classification for analysis findings.
514
+ * Mirrors the existing observed convention in stored findings_json.
515
+ */
516
+ export type FindingSeverity = "high" | "medium" | "low" | "info";
517
+
518
+ /**
519
+ * Agent boundary contract for analysis findings (v15):
520
+ *
521
+ * - `observation` (REQUIRED): a factual, descriptive statement. Every
522
+ * agent — ranger, foreman, craftsman, smiths — MAY emit observation.
523
+ * Observations do not prescribe action; they describe what exists.
524
+ *
525
+ * - `proposedAction` (OPTIONAL): a prescriptive recommendation only
526
+ * decision-capable agents may emit. Concretely:
527
+ * • ranger is EXCLUDED. Validated at write time by
528
+ * `validateAnalysisFindings()` in src/db/analyses.ts.
529
+ * • foreman, craftsman, warden, smiths, sage, etc. MAY emit it
530
+ * when they have explicit decision/architectural authority.
531
+ *
532
+ * The boundary enforces ranger's "observation-only" role and keeps
533
+ * prescriptive planning in the hands of decision-capable agents.
534
+ */
535
+ export interface Finding {
536
+ severity: FindingSeverity;
537
+ /** Optional file:line or code location the finding refers to. */
538
+ location?: string;
539
+ /** Factual statement. Required for all agents. */
540
+ observation: string;
541
+ /**
542
+ * Prescriptive recommendation. OPTIONAL — and FORBIDDEN when agent === 'ranger'.
543
+ * See agent boundary contract above.
544
+ */
545
+ proposedAction?: string;
546
+ /** Optional effort estimate (e.g. 'small', 'medium', 'large'). */
547
+ effort?: string;
548
+ /** Optional impact estimate (e.g. 'low', 'medium', 'high'). */
549
+ impact?: string;
550
+ }
551
+
552
+ /**
553
+ * Analysis row. `findingsJson` is the serialized JSON array of {@link Finding}
554
+ * entries stored in the analyses table (TEXT column). The DB layer keeps it
555
+ * as a string for query/index flexibility; consumers parse on read.
556
+ */
557
+ export interface Analysis {
558
+ id: string;
559
+ slug: string;
560
+ title: string;
561
+ projectPath: string;
562
+ summary: string;
563
+ /** JSON array of {@link Finding}. Parse with JSON.parse(findingsJson) before use. */
564
+ findingsJson: string;
565
+ sourcePlanId: string | null;
566
+ agent: string;
567
+ sessionId: string | null;
568
+ createdBy: string | null;
569
+ createdAt: string;
570
+ updatedAt: string;
571
+ archivedAt: string | null;
572
+ }
573
+
574
+ export interface InsertAnalysis {
575
+ slug: string;
576
+ title: string;
577
+ projectPath: string;
578
+ summary?: string;
579
+ findingsJson?: string;
580
+ sourcePlanId?: string | null;
581
+ agent?: string;
582
+ sessionId?: string | null;
583
+ createdBy?: string | null;
584
+ }
585
+
586
+ export interface AnalysisRow {
587
+ id: string;
588
+ slug: string;
589
+ title: string;
590
+ project_path: string;
591
+ summary: string;
592
+ findings_json: string;
593
+ source_plan_id: string | null;
594
+ agent: string;
595
+ session_id: string | null;
596
+ created_by: string | null;
597
+ created_at: string;
598
+ updated_at: string;
599
+ archived_at: string | null;
600
+ }
601
+
602
+ export function analysisFromRow(row: unknown): Analysis {
603
+ const r = row as AnalysisRow;
604
+ return {
605
+ id: r.id,
606
+ slug: r.slug,
607
+ title: r.title,
608
+ projectPath: r.project_path,
609
+ summary: r.summary,
610
+ findingsJson: r.findings_json,
611
+ sourcePlanId: r.source_plan_id ?? null,
612
+ agent: r.agent,
613
+ sessionId: r.session_id ?? null,
614
+ createdBy: r.created_by ?? null,
615
+ createdAt: r.created_at,
616
+ updatedAt: r.updated_at,
617
+ archivedAt: r.archived_at ?? null,
618
+ };
619
+ }