spine-framework 0.4.4 → 0.5.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  15. package/.framework/functions/_shared/logging.ts +562 -224
  16. package/.framework/functions/_shared/middleware.ts +47 -44
  17. package/.framework/functions/admin-data.ts +39 -28
  18. package/.framework/functions/ai-agents.ts +10 -6
  19. package/.framework/functions/api-keys.ts +301 -122
  20. package/.framework/functions/apps.ts +43 -26
  21. package/.framework/functions/cil-ingest.ts +48 -15
  22. package/.framework/functions/debug-auth.ts +14 -8
  23. package/.framework/functions/deliveries.ts +39 -12
  24. package/.framework/functions/embeddings.ts +145 -76
  25. package/.framework/functions/integration-outbound.ts +67 -27
  26. package/.framework/functions/integrations.ts +254 -127
  27. package/.framework/functions/logs.ts +273 -133
  28. package/.framework/functions/observability.ts +277 -147
  29. package/.framework/functions/pipeline-executions.ts +1 -1
  30. package/.framework/functions/pipelines.ts +14 -9
  31. package/.framework/functions/prompt-configs.ts +7 -4
  32. package/.framework/functions/roles.ts +8 -5
  33. package/.framework/functions/security.ts +30 -17
  34. package/.framework/functions/system-cron.ts +240 -127
  35. package/.framework/functions/tests.ts +7 -6
  36. package/.framework/functions/timers.ts +310 -107
  37. package/.framework/functions/triggers.ts +28 -13
  38. package/.framework/functions/types.ts +13 -10
  39. package/.framework/migrations/069_log_stats_window.sql +54 -0
  40. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  41. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  42. package/.framework/migrations/072_slug_autofill.sql +77 -0
  43. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  44. package/.framework/src/apps/admin/index.tsx +1141 -775
  45. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  46. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  47. package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
  48. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  49. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  50. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  51. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  52. package/.framework/src/components/layout/Sidebar.tsx +300 -177
  53. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  54. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  55. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  56. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  57. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  58. package/.framework/src/components/ui/sheet.tsx +24 -26
  59. package/.framework/src/components/ui/textarea.tsx +74 -48
  60. package/.framework/src/hooks/useEntityList.ts +12 -9
  61. package/.framework/src/hooks/useListSchema.ts +108 -58
  62. package/.framework/src/instructions/README.md +57 -0
  63. package/.framework/src/instructions/ai-agents.md +69 -0
  64. package/.framework/src/instructions/alerts.md +80 -0
  65. package/.framework/src/instructions/api-keys.md +87 -0
  66. package/.framework/src/instructions/apps.md +77 -0
  67. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  68. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  69. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  70. package/.framework/src/instructions/concepts/automation.md +186 -0
  71. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  72. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  73. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  74. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  75. package/.framework/src/instructions/concepts/observability.md +116 -0
  76. package/.framework/src/instructions/concepts/permissions.md +150 -0
  77. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  78. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  79. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  80. package/.framework/src/instructions/deliveries.md +59 -0
  81. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  82. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  83. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  84. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  85. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  86. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  87. package/.framework/src/instructions/integrations.md +96 -0
  88. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  89. package/.framework/src/instructions/invites.md +60 -0
  90. package/.framework/src/instructions/kb.md +80 -0
  91. package/.framework/src/instructions/logs.md +77 -0
  92. package/.framework/src/instructions/observability.md +61 -0
  93. package/.framework/src/instructions/operations/deployment.md +115 -0
  94. package/.framework/src/instructions/operations/migrations.md +125 -0
  95. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  96. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  97. package/.framework/src/instructions/operations/upgrade.md +116 -0
  98. package/.framework/src/instructions/pipeline-executions.md +62 -0
  99. package/.framework/src/instructions/pipelines.md +78 -0
  100. package/.framework/src/instructions/prompts.md +95 -0
  101. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  102. package/.framework/src/instructions/reference/api/auth.md +113 -0
  103. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  104. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  105. package/.framework/src/instructions/reference/cli.md +124 -0
  106. package/.framework/src/instructions/reference/database.md +132 -0
  107. package/.framework/src/instructions/reference/design-schema.md +280 -0
  108. package/.framework/src/instructions/reference/env-vars.md +133 -0
  109. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  110. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  111. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  112. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  113. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  114. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  115. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  116. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  117. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  118. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  119. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  120. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  121. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  122. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  123. package/.framework/src/instructions/reference/functions/index.md +103 -0
  124. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  125. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  126. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  127. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  128. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  129. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  130. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  131. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  132. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  133. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  134. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  135. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  136. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  137. package/.framework/src/instructions/reference/functions/security.md +140 -0
  138. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  139. package/.framework/src/instructions/reference/functions/system.md +106 -0
  140. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  141. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  142. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  143. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  144. package/.framework/src/instructions/reference/functions/types.md +146 -0
  145. package/.framework/src/instructions/reference/seeds.md +126 -0
  146. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  147. package/.framework/src/instructions/reference/spine-config.md +114 -0
  148. package/.framework/src/instructions/roles.md +80 -0
  149. package/.framework/src/instructions/runtime-detail.md +70 -0
  150. package/.framework/src/instructions/runtime-list.md +77 -0
  151. package/.framework/src/instructions/security.md +67 -0
  152. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  153. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  154. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  155. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  156. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  157. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  158. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  159. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  160. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  161. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  162. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  163. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  164. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  165. package/.framework/src/instructions/testing.md +59 -0
  166. package/.framework/src/instructions/timers.md +82 -0
  167. package/.framework/src/instructions/triggers.md +69 -0
  168. package/.framework/src/instructions/types.md +93 -0
  169. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  170. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  171. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  172. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  173. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  174. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  175. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  176. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  177. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  178. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  179. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  180. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  181. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  182. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  183. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  184. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  185. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  186. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  187. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  188. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  189. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  190. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  191. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  192. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  193. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  194. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  195. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  196. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  197. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  198. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  199. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  200. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  201. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  202. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  203. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  204. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  205. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  206. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  207. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  208. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  209. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  210. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  211. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  212. package/.framework/templates/agent/AGENTS.md +150 -0
  213. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  214. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  215. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  216. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  217. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  218. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  219. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  220. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  221. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  222. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  223. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  224. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  225. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  226. package/.framework/templates/project.json +4 -1
  227. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  228. package/.framework/tests/unit/agent-files.test.ts +355 -0
  229. package/.framework/tests/unit/agent-runner.test.ts +479 -44
  230. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  231. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  232. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  233. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  234. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  235. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  236. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  237. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  238. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  239. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  240. package/.framework/tests/unit/system-cron.test.ts +240 -0
  241. package/.framework/tests/unit/timers.test.ts +159 -0
  242. package/README.md +125 -166
  243. package/config/tsconfig.cli.json +2 -2
  244. package/package.json +15 -4
  245. package/templates/project/package-lock.json +2 -2
  246. package/.framework/docs/admin-routes.md +0 -76
  247. package/.framework/docs/api-docs-progress.md +0 -38
  248. package/.framework/docs/api-governance.md +0 -146
  249. package/.framework/docs/api-testing-results.md +0 -212
  250. package/.framework/docs/assembly-launch-guide.md +0 -138
  251. package/.framework/docs/audit-results.md +0 -590
  252. package/.framework/docs/db-api-inventory.md +0 -95
  253. package/.framework/docs/gap-remediation-backlog.md +0 -103
  254. package/.framework/docs/ui-adoption-verification.md +0 -111
  255. package/.framework/docs/ui-api-coverage.md +0 -84
  256. package/.framework/docs/v2-compatibility-audit.md +0 -228
  257. package/.framework/mcp/index.ts +0 -133
  258. package/.framework/mcp/load-env.ts +0 -60
  259. package/.framework/mcp/shared/app-manifest.ts +0 -275
  260. package/.framework/mcp/shared/db.ts +0 -477
  261. package/.framework/mcp/shared/kb.ts +0 -118
  262. package/.framework/mcp/tools/architect.ts +0 -431
  263. package/.framework/mcp/tools/cil/context.ts +0 -345
  264. package/.framework/mcp/tools/cil/graph.ts +0 -242
  265. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  266. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  267. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  268. package/.framework/mcp/tools/cil/project.ts +0 -791
  269. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  270. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  271. package/.framework/mcp/tools/cil/search.ts +0 -97
  272. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  273. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  274. package/.framework/mcp/tools/kb/delete.ts +0 -139
  275. package/.framework/mcp/tools/kb/index.ts +0 -52
  276. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  277. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  278. package/.framework/mcp/tools/kb/publish.ts +0 -100
  279. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  280. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  281. package/.framework/mcp/tools/kb/search.ts +0 -101
  282. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  283. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,107 @@
1
+ ---
2
+ title: item-progress
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/item-progress.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/audit.ts
11
+ - .framework/public/_redirects
12
+ ---
13
+
14
+ # item-progress
15
+
16
+ ## What it does
17
+
18
+ Tracks a person's progress through a pipeline on a specific item —
19
+ one `item_progress` row per `(person_id, item_id)` pair. Enforces
20
+ forward-only status transitions defined by the row's type
21
+ (`design_schema.pipeline`, default `['not_started', 'in_progress',
22
+ 'completed']`), stamps `started_at`/`completed_at`, and counts attempts.
23
+
24
+ ## Route and methods
25
+
26
+ `/.netlify/functions/item-progress`, reachable as `/api/item-progress`
27
+ (`/api/*` → `/.netlify/functions/:splat`). Dispatch is by method
28
+ (`ctx.query.method`; `?method=` can override):
29
+
30
+ | Call | Action |
31
+ |---|---|
32
+ | `GET /api/item-progress` | `list` |
33
+ | `GET /api/item-progress?id=<uuid>` | `get` |
34
+ | `POST /api/item-progress` | `upsert` |
35
+ | `PATCH /api/item-progress?id=<uuid>` | `update` |
36
+
37
+ Any other method → `Method <method> not supported`.
38
+
39
+ ## Authentication
40
+
41
+ Standard `createHandler`: `x-api-key`, internal secret, or
42
+ `Bearer <jwt>`; anonymous is rejected with 401. All table access goes
43
+ through `ctx.db` (RLS-scoped for humans); type lookups and the
44
+ item/person title fetches use the service role. There are no additional
45
+ role checks.
46
+
47
+ ## Request contract
48
+
49
+ **list** (query): `person_id`, `item_id`, `item_ids` (comma-separated),
50
+ `status`, `limit` (default `100`), `offset` (default `0`). Only
51
+ `is_active = true` rows, ordered `updated_at` desc.
52
+
53
+ **get** (query): `id`.
54
+
55
+ **upsert** (body): `person_id`, `item_id`, `account_id`, and `type_id`
56
+ *or* `type_slug` are required. Optional: `status`, `score`, `data`
57
+ (merged into `record.data`), `title`, `description`, `app_id`, `force`.
58
+
59
+ **update** (body `id` or query `id`): partial row fields plus `force`.
60
+
61
+ Transition rules: `status` may only move to the same or a later stage of
62
+ the type's pipeline (`toIdx >= fromIdx`). Statuses not present in the
63
+ pipeline pass unchecked. `force: true` bypasses the check. Backward moves
64
+ without `force` throw.
65
+
66
+ ## Response contract
67
+
68
+ Standard `{ data, error, meta }` envelope.
69
+
70
+ - `list` → array of `item_progress` rows.
71
+ - `get` → one row.
72
+ - `upsert` → the upserted row (Postgres `ON CONFLICT (person_id, item_id)`
73
+ — repeat calls update the same row). Auto-fills `title`
74
+ (`"<item title> — <person name>"`) and `description`
75
+ (`"In Progress · score 90 · 2 attempts"`) when not given.
76
+ - `update` → the updated row.
77
+
78
+ ## Side effects
79
+
80
+ - Writes `item_progress` rows; `data.attempts` increments on each status
81
+ change, `data.started_at`/`data.completed_at` stamp on first entry to
82
+ `in_progress`/`completed`. `created_by`/`updated_by` track the principal.
83
+ - Emits `item_progress.upserted` / `item_progress.updated` log entries via
84
+ `emitLog` with before/after payloads.
85
+ - Reads `items`, `people`, `types` for title composition and pipeline
86
+ resolution.
87
+
88
+ ## Errors
89
+
90
+ | Condition | HTTP | Message |
91
+ |---|---|---|
92
+ | Missing `id` | 500 | `Progress record ID is required` |
93
+ | Missing required upsert fields | 500 | `person_id, item_id, type_id (or type_slug), and account_id are required` |
94
+ | Backward/invalid transition | 500 | `Invalid status transition: <from> → <to>. Use force: true to override.` |
95
+ | Row not found | 500 | `Progress record not found` |
96
+ | Unsupported method | 500 | `Method <method> not supported` |
97
+
98
+ ## Agent notes
99
+
100
+ - There is no delete action; to retire a progress row, `PATCH`
101
+ `{ "is_active": false }` (list filters it out).
102
+ - `type_slug` is resolved with kind `item_progress` — use the progress
103
+ type's slug, not the item's type slug.
104
+ - Because `data` is merged (not replaced) on both upsert and update,
105
+ remove a key by writing an explicit `null`, not by omitting it.
106
+ - The unique key is `(person_id, item_id)` — one progress row per person
107
+ per item; concurrent upserts conflict-resolve onto the same row.
@@ -0,0 +1,166 @@
1
+ ---
2
+ title: kb
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/kb.ts
9
+ - .framework/functions/_shared/kb.ts
10
+ - .framework/functions/_shared/llm.ts
11
+ - .framework/functions/_shared/middleware.ts
12
+ - .framework/functions/_shared/permissions.ts
13
+ - .framework/functions/_shared/pipeline-runner.ts
14
+ - .framework/migrations/054_kb_ingest_lease.sql
15
+ - netlify.toml
16
+ - public/_redirects
17
+ ---
18
+
19
+ # kb
20
+
21
+ ## What it does
22
+
23
+ `kb` (`.framework/functions/kb.ts`) is the knowledge-base surface: a thin
24
+ `?action=` dispatcher over the shared implementation in
25
+ `.framework/functions/_shared/kb.ts`. It maintains `embeddings` vector rows
26
+ for published knowledge items and serves semantic search over them, gated by
27
+ the calling app's knowledge envelope.
28
+
29
+ | Action | Handler | Purpose |
30
+ |---|---|---|
31
+ | `ingest` | `kbIngest` | Embed one `items` record's declared vector fields into `embeddings`. |
32
+ | `revoke` | `kbRevoke` | Revoke + delete a document's embeddings and clear its ingest lease. |
33
+ | `reindex` | `kbReindex` | Re-ingest every `published` item of a type (batched, 10 in flight). |
34
+ | `search` | `kbSearch` | Vector search via `match_embeddings`, best-match-per-document. Default action. |
35
+ | `publish` | `kbPublish` | PATCH `status: 'published'` through admin-data, then ingest. |
36
+
37
+ ## Route and methods
38
+
39
+ - `/api/kb?action=<action>` — `public/_redirects` maps `/api/*` to
40
+ `/.netlify/functions/:splat`. The action comes from `ctx.query.action`
41
+ only — it must be a query parameter; the HTTP method itself is not
42
+ checked, and the body carries the parameters.
43
+ - The function is configured with a 120s timeout in `netlify.toml` (reindex
44
+ sweeps a whole corpus).
45
+
46
+ ## Authentication
47
+
48
+ `createHandler` resolves a principal and rejects anonymous requests with 401.
49
+ There is no role gate on the actions themselves; authorization is enforced
50
+ downstream:
51
+
52
+ - `kbSearch` runs `resolveKnowledgeScope` — deny-by-default unless the
53
+ caller's app declares `config.knowledge` — and each result is passed
54
+ through `sanitizeRecordData` before returning.
55
+ - `kbPublish` goes through admin-data `update` (a synthetic PATCH), so
56
+ field-level permissions and triggers apply.
57
+ - `kbIngest`/`kbRevoke`/`kbReindex` operate via `adminDb` — they rely on the
58
+ caller being trusted to name a document; `body.account_id` on ingest lets
59
+ an operator ingest a record owned by another account.
60
+
61
+ ## Request contract
62
+
63
+ `ingest`: `{ id (required, items.id), account_id? }`
64
+
65
+ `revoke`: `{ id (required) }`
66
+
67
+ `reindex`: `{ type_slug (required), app_id? }` — `app_id` narrows the items
68
+ re-ingested.
69
+
70
+ `search`:
71
+
72
+ | Field | Default | Notes |
73
+ |---|---|---|
74
+ | `query` | — | Required. |
75
+ | `type_slugs` | `[]` | The caller's pick — intersected with the app envelope; picks outside it deny the call. |
76
+ | `sources` | `['kb']` | `'kb'` for knowledge items, `'code'` for code-chunk vectors (the `cil_*` item-type namespace). |
77
+ | `app_id` | `null` | Post-filter on `metadata.app_id`. |
78
+ | `vector_fields` | `[]` | Restrict to specific `metadata.vector_type` values. |
79
+ | `limit` / `offset` | `10` / `0` | Page over best-match documents. |
80
+ | `similarity_threshold` | `0.15` | Minimum cosine similarity. |
81
+ | `filters` | `{}` | Post-filter on the resolved `items` row. |
82
+
83
+ `publish`: `{ id (required) }`
84
+
85
+ ## Response contract
86
+
87
+ - `ingest` → `{ ok, message, count?, in_progress? }`. `message` is one of
88
+ `embeddings generated`, `embeddings reused (unchanged content)`,
89
+ `record is not published; embeddings revoked if any`,
90
+ `embedding already in progress` (with `in_progress: true` — **not** a
91
+ success signal for stampers), or `no content to embed`.
92
+ - `revoke` → `{ ok, message: 'embeddings revoked', count }`, or
93
+ `{ ok, skipped: true, message: 'document is published — embeddings
94
+ retained', count: 0 }` — revoke re-checks current status at execution time
95
+ and refuses to strip a now-published document.
96
+ - `reindex` → `{ ok, count, errors?: [{ id, error }] }` — per-item failures
97
+ are collected, not fatal.
98
+ - `search` → `{ results, count, error? }`; results are
99
+ `{ id, document_id, type_slug, title, snippet, app_id, similarity, status,
100
+ created_at, source, data }` sorted by similarity. `error` carries
101
+ `knowledge_denied: <reason>` when the envelope refuses.
102
+ - `publish` → `{ record, ingest }` — the admin-data update result plus the
103
+ ingest result.
104
+
105
+ ## Side effects
106
+
107
+ - `ingest`: reads the record's stamped `design_schema.functionality.kb` —
108
+ `vector_fields` (default `['title', 'content']`), `chunk_field` (default
109
+ `content`), `chunk_size` (800), `chunk_overlap` (100), `security_level`
110
+ (deny-by-default `internal`), `supersede_keys`. Field text is normalized
111
+ (rich-text HTML stripped, bare UUID tokens dropped) and hashed — a
112
+ `document_leases` row (migration `054`) dedupes unchanged re-embeds
113
+ (`reused`) and fences concurrent workers (`processing`). Prior embeddings
114
+ are cleared via `revoke_embeddings_for_item` + delete, chunks are embedded
115
+ in **one batched** `callOpenAIEmbeddings` call (`text-embedding-3-small`),
116
+ and new rows are inserted with `metadata.{type_slug, item_type, app_id,
117
+ vector_type, security_level, status}` and `approved_at` set. Older
118
+ `published` items sharing a `supersede_keys` value are archived with
119
+ `superseded_by`/`superseded_at` and their embeddings revoked.
120
+ - `revoke`: `revoke_embeddings_for_item` RPC + embeddings row delete +
121
+ unconditional `document_leases` delete (warn-only on failure).
122
+ - `reindex`: sweeps `published` items of the type in batches of 10, calling
123
+ `kbIngest` each — sequential ingest would exceed the function budget.
124
+ - `publish`: admin-data PATCH sets `status: 'published'` (permissions,
125
+ audit, triggers run), then `kbIngest` runs immediately in the same
126
+ request.
127
+ - `search`: one `callOpenAIEmbeddings` call for the query embedding plus
128
+ `match_embeddings` RPC per requested vector type.
129
+
130
+ ## Errors
131
+
132
+ | Condition | Error |
133
+ |---|---|
134
+ | Missing inputs | `id is required` · `type_slug is required` · `query is required` |
135
+ | Unknown record | `record not found` |
136
+ | Unknown type | `type not found: <type_slug>` |
137
+ | Bad action | `unknown kb action: <action>` |
138
+ | RPC/insert failures | thrown — the lease is released with `failed` status |
139
+
140
+ ## Agent notes
141
+
142
+ - **Ingest outcomes are tri-state.** `in_progress: true` means another
143
+ worker owns this content's embed — callers that stamp embedding state on
144
+ the record must not write success *or* failure; the owning worker's
145
+ completion reconciles the corpus. A fenced lease release (superseded
146
+ mid-flight) warns without throwing.
147
+ - **Search is deny-by-default.** No `config.knowledge` envelope on the app →
148
+ `knowledge_denied`. The envelope declares `account` (self, or
149
+ `self_and_descendants` via `get_accessible_accounts`), `platform`
150
+ (spine-system account, own `sources` allowlist), and project-scoped code
151
+ chunks — the last only when an explicit project id list survives the
152
+ envelope∩pick intersection. When the app declares `required_roles`, the
153
+ principal must hold one (system_admin bypasses).
154
+ - **`document_id` is polymorphic.** Regular records key `embeddings` by
155
+ their items UUID; code-chunk vectors key it by a `stable_id` string —
156
+ `kbSearch` splits the `items` lookup (`id` vs `data->>stable_id`) to
157
+ handle both.
158
+ - **Pipelines don't call this endpoint.** The `kb_ingest`/`kb_revoke` stage
159
+ handlers lazy-import `kbIngest`/`kbRevoke` directly and resolve `item_id`
160
+ from trigger data; `search_knowledge` uses its own text-search path over
161
+ `embeddings`, not `kbSearch`.
162
+ - `adminUpdate` is imported lazily to avoid a circular-import deadlock
163
+ through `pipeline-runner` — preserve that pattern if adding admin-data
164
+ calls.
165
+ - See [AI agents, prompt configs, and embeddings](../../concepts/ai-and-prompts.md)
166
+ for how these vectors feed agent retrieval.
@@ -0,0 +1,141 @@
1
+ ---
2
+ title: logs
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/logs.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/principal.ts
11
+ - .framework/migrations/069_log_stats_window.sql
12
+ ---
13
+
14
+ # logs
15
+
16
+ ## What it does
17
+
18
+ Read/write API over the `public.logs` table — the shared audit, span, and
19
+ event log every function writes into. Read actions cover account feeds,
20
+ per-target and per-person histories, aggregate stats, and message search;
21
+ the write path appends a single log row; `cleanup` prunes old rows.
22
+
23
+ ## Route and methods
24
+
25
+ `/.netlify/functions/logs` — also `/api/logs` (`/api/*` →
26
+ `/.netlify/functions/:splat` in `.framework/public/_redirects`; same
27
+ rewrite in the dev proxy, `config/vite.config.ts`).
28
+
29
+ Dispatch by `?action=` + HTTP method:
30
+
31
+ | Call | Handler |
32
+ |---|---|
33
+ | `GET?action=account` | `listAccount` — account-scoped feed |
34
+ | `GET?action=target` | `listTarget` — rows for one record |
35
+ | `GET?action=person` | `listPerson` — rows for one person |
36
+ | `GET?action=stats` | `getStats` — `get_log_stats` RPC |
37
+ | `GET?action=search` | `search` — `message ILIKE` match |
38
+ | `POST?action=cleanup` | `cleanup` — delete old rows |
39
+ | `POST` (no action) | `log` — insert one row |
40
+
41
+ Anything else throws `Invalid action or method`.
42
+
43
+ ## Authentication
44
+
45
+ `createHandler` — any non-anonymous principal; anonymous → 401.
46
+
47
+ Scope varies per action:
48
+
49
+ - `listAccount` — `system_admin` principals read via `adminDb`
50
+ (cross-account); everyone else reads via `ctx.db` filtered to
51
+ `account_id`, and needs an account context.
52
+ - `listTarget`, `listPerson`, `search`, `getStats` — always `ctx.db`
53
+ pinned to `ctx.accountId`; all throw `Account context required` without
54
+ one. Note `ctx.db` is `adminDb` for machine principals, so the
55
+ `account_id` filter is the only scope guard there.
56
+ - `log`, `cleanup` — `ctx.db`, no role check inside the function.
57
+
58
+ ## Request contract
59
+
60
+ Query parameters:
61
+
62
+ - `account`: `event` (exact `span_name`), `event_type` (exact `level`),
63
+ `target_type` (`source_type`), `source`, `source_id`, `actor_id`
64
+ (`created_by`), `query` (`message ILIKE %…%`), `date_from`, `date_to`,
65
+ `trace_id`, `limit` (100), `offset` (0).
66
+ - Special case: `event=threshold.breached` returns alert-shaped rows
67
+ `{ id, metric, threshold_value, actual_value, created_at }` taken from
68
+ `metadata`, plus `total` — not the standard row mapping.
69
+ - `target`: `target_type` + `target_id` (both required), `event_type`,
70
+ `limit` (100), `offset` (0).
71
+ - `person`: `person_id` (defaults to the caller's principal id),
72
+ `include_system` (`'true'` to include `level='system'` rows, excluded by
73
+ default), `limit` (50), `offset` (0).
74
+ - `stats`: `event_type`, `target_type`, `source`, `source_id`, `actor_id`,
75
+ `query`, `date_from`, `date_to` — all optional, mapped to the
76
+ `get_log_stats` RPC parameters.
77
+ - `search`: `query` (required), `event_type`, `target_type`, `limit` (50),
78
+ `offset` (0).
79
+
80
+ Body (`log` action only): `event_type` (required — written to `level`),
81
+ `target_type`, `target_id`, `action` (written to `source`), `message`,
82
+ `details` (written to `context`), `metadata`.
83
+
84
+ Body (`cleanup`): `days_to_keep` (default 30).
85
+
86
+ ## Response contract
87
+
88
+ Standard envelope `{ data, error, meta }`.
89
+
90
+ - `account` → `{ data: [mapped rows], total }`.
91
+ - `target` / `person` / `search` → array of mapped rows.
92
+ - Rows are remapped by `mapLogRow`: `level → event_type`,
93
+ `person_id → actor_id`, `source_type → target_type`,
94
+ `source_id → target_id`, `source → action`, `context → details`, plus
95
+ `span_name`, `trace_id`, `span_id`, `parent_span_id`, `duration_ms`,
96
+ `outcome`, `event_category`, `kind`, `message`, `metadata`,
97
+ `created_at`.
98
+ - `stats` → first `get_log_stats` row `{ total, error_count,
99
+ user_action_count, event_type_count, levels, sources, source_types }`,
100
+ or a zeroed object when the RPC returns nothing.
101
+ - `log` → `{ log_id }`. `cleanup` → `{ deleted_count }`.
102
+
103
+ ## Side effects
104
+
105
+ - `log` inserts one `logs` row stamped with `person_id` (caller),
106
+ `account_id`, and the mapped fields.
107
+ - `cleanup` deletes `logs` rows with `created_at` older than
108
+ `days_to_keep` — permanently.
109
+ - Read actions are pure queries. (The `get_log_stats` RPC is a read.)
110
+
111
+ ## Errors
112
+
113
+ - `target_type and target_id are required`, `Person ID is required`,
114
+ `Search query is required`, `event_type is required` — validation.
115
+ - `Account context required` — non-admin callers without `ctx.accountId`.
116
+ - `Invalid action or method` — unmatched dispatch.
117
+ - `person`/`target` use caller-scoped `ctx.db` — a `person_id` outside the
118
+ caller's account simply returns an empty page, not an error.
119
+
120
+ ## Agent notes
121
+
122
+ - **`get_log_stats` defaults to a 7-day window.** When `date_from` is not
123
+ supplied the RPC uses `now() - interval '7 days'`
124
+ (`.framework/migrations/069_log_stats_window.sql`) — "all-time" stats
125
+ are really trailing-week stats. Pass `date_from` explicitly for longer
126
+ windows. (Audit finding F-037.)
127
+ - `cleanup` has no admin check: it runs on `ctx.db`. Human JWTs get an
128
+ RLS-scoped client, but machine principals resolve to `adminDb` — a
129
+ machine call to `POST ?action=cleanup` deletes matching rows
130
+ **platform-wide**. Treat this action as operator-only.
131
+ - The same `account_id`-vs-`adminDb` asymmetry applies to `listTarget`,
132
+ `listPerson`, `search`, and `getStats`: the `account_id` filter is a
133
+ query predicate, not RLS, for machine callers — but it is always
134
+ applied, so cross-account reads still require omitting account context,
135
+ which those handlers forbid.
136
+ - `listAccount`'s `event=threshold.breached` branch returns a different
137
+ row shape — clients parsing log rows should branch on it.
138
+ - Field names in the response are the *mapped* names (`event_type`,
139
+ `actor_id`, `target_type`), not the column names (`level`, `person_id`,
140
+ `source_type`) — a common source of confusion when correlating with the
141
+ table directly.
@@ -0,0 +1,105 @@
1
+ ---
2
+ title: observability
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/observability.ts
9
+ - .framework/functions/_shared/principal.ts
10
+ - .framework/functions/_shared/resolve-ids.ts
11
+ - .framework/functions/_shared/middleware.ts
12
+ ---
13
+
14
+ # observability
15
+
16
+ ## What it does
17
+
18
+ Aggregation API over `public.logs` and `public.pipeline_executions` — the
19
+ metrics surface behind the observability dashboards: event volume, error
20
+ rate, latency percentiles, pipeline stats, top actors, AI-support metrics,
21
+ and a log-retention cleanup action.
22
+
23
+ ## Route and methods
24
+
25
+ `/.netlify/functions/observability?action=<action>` — also `/api/…`
26
+ (`/api/*` → `/.netlify/functions/:splat` in
27
+ `.framework/public/_redirects`).
28
+
29
+ Dispatch is on `action` only — there are no HTTP-method checks. Valid
30
+ actions: `event_volume`, `error_rate`, `latency_percentiles`,
31
+ `pipeline_stats`, `top_actors`, `ai_support_metrics`, `cleanup`.
32
+
33
+ ## Authentication
34
+
35
+ `createHandler` — non-anonymous principal required (401 otherwise).
36
+
37
+ `system_admin` principals query via `adminDb` across all accounts;
38
+ everyone else uses `ctx.db` and the handlers additionally filter
39
+ `account_id = ctx.accountId` — non-admins without account context get
40
+ `Account context required`. `cleanup` separately requires
41
+ `principal.roles` to include `system_admin`.
42
+
43
+ ## Request contract
44
+
45
+ All inputs are query parameters:
46
+
47
+ | Action | Params | Returns |
48
+ |---|---|---|
49
+ | `event_volume` | `from`, `to` (required ISO dates); `bucket` = `hour`\|`day` (default `hour`) | `[{ bucket, count }]` sorted ascending — counts of `logs` rows bucketed by `created_at` |
50
+ | `error_rate` | `from`, `to` (required) | `{ total, errors, rate }` — rows with `outcome='failure'` or `level='error'` over total |
51
+ | `latency_percentiles` | `from`, `to` (required); `source` (default `request`, matched against `span_name`) | `{ p50, p90, p95, p99 }` over positive `duration_ms` values |
52
+ | `pipeline_stats` | `from`, `to` (required) | `[{ pipeline_id, success_count, failure_count, avg_duration_ms }]` over `pipeline_executions` (`completed`/`failed` statuses counted; avg over rows having both timestamps) |
53
+ | `top_actors` | `from`, `to` (required); `limit` (default 5) | `[{ principal_id, event_count }]` — counts of `logs.created_by`, `anonymous` for null |
54
+ | `ai_support_metrics` | `from`, `to` (optional; default last 30 days); requires account context | see below |
55
+ | `cleanup` | `days` (default 30) | `{ deleted_count }` via `cleanup_old_logs` RPC; system-admin only |
56
+
57
+ `ai_support_metrics` resolves the `ai_feedback_record` item type and
58
+ reads `items` for the context account: feedback rows (attempted count,
59
+ `customer_feedback='not_helpful'`, `kb_article_id`, `reuse_count`,
60
+ `kb_candidate`), resolved items (`status='resolved'` with
61
+ `data->>aim_escalation_reason='none'`), and escalations
62
+ (`status='human_assigned'`). Response: `{ period: {from,to},
63
+ ai_attempted, ai_resolved, human_escalations, thumbs_down_rate,
64
+ kb_articles_created, kb_reuse_count, knowledge_gaps }`. It uses `adminDb`
65
+ scoped to `ctx.accountId` and tolerates per-query failure via
66
+ `Promise.allSettled` — a failed sub-query contributes zero, not an error.
67
+
68
+ ## Response contract
69
+
70
+ Standard envelope `{ data, error, meta }`. Payloads per action are listed
71
+ above; aggregation happens in memory inside the function (there are no
72
+ SQL rollups for the metrics endpoints — full matching rows are selected
73
+ then counted).
74
+
75
+ ## Side effects
76
+
77
+ Read-only except `cleanup`, which deletes `logs` rows older than `days`
78
+ via the `cleanup_old_logs` RPC. (`system-cron` performs the same cleanup
79
+ automatically on the 00:00 UTC tick with a 30-day retention.)
80
+
81
+ ## Errors
82
+
83
+ - `Account context required` — non-admin callers without `ctx.accountId`
84
+ (all actions), and `ai_support_metrics` for any caller.
85
+ - `from and to dates are required` — all metric actions except
86
+ `ai_support_metrics` and `cleanup`.
87
+ - `System admin required` — `cleanup` without the `system_admin` role.
88
+ - `Unknown action: <x>. Valid actions: …` — unmatched `action`.
89
+
90
+ ## Agent notes
91
+
92
+ - These are in-memory aggregations, not RPC rollups: `event_volume`,
93
+ `error_rate`, `latency_percentiles`, and `top_actors` pull every
94
+ matching row's columns and count locally. Wide `from`/`to` windows over
95
+ a busy `logs` table are proportional in cost — keep ranges tight.
96
+ - `latency_percentiles` filters `span_name = source` (default `request`)
97
+ — the request spans emitted by `createHandler`. Passing `source=` for
98
+ other span names (e.g. `db.select`, `pipeline.run`) yields percentiles
99
+ for those operations.
100
+ - `top_actors` keys on `created_by`, which is null for machine-emitted
101
+ spans — those collapse into `anonymous`.
102
+ - `ai_support_metrics` depends on `ai_feedback_record` items and
103
+ `status='human_assigned'`/`'resolved'` conventions written by the AI
104
+ support flows; it returns zeros on installations without them rather
105
+ than erroring.
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: pipeline-executions
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/pipeline-executions.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/principal.ts
11
+ - .framework/functions/_shared/pipeline-runner.ts
12
+ ---
13
+
14
+ # pipeline-executions
15
+
16
+ ## What it does
17
+
18
+ CRUD and lifecycle surface for `public.pipeline_executions` — the durable
19
+ ledger of pipeline runs. One row per run carries `pipeline_id`, `status`,
20
+ `trigger_data`, `result`, `error_message`, `started_at`, `completed_at`, and
21
+ `duration_ms`. The pipeline runner writes most transitions itself; this
22
+ endpoint is how operators and the admin UI read run history and how scripts
23
+ create, start, complete, cancel, or prune rows.
24
+
25
+ ## Route and methods
26
+
27
+ `/.netlify/functions/pipeline-executions` — also reachable as
28
+ `/api/pipeline-executions` (`/api/*` → `/.netlify/functions/:splat` in
29
+ `.framework/public/_redirects`; same rewrite in the dev proxy,
30
+ `config/vite.config.ts`).
31
+
32
+ Dispatch is by `?action=` query parameter plus HTTP method (the middleware
33
+ copies `event.httpMethod` into `ctx.query.method`):
34
+
35
+ | Call | Handler |
36
+ |---|---|
37
+ | `GET` (no action, no `id`) | `list` |
38
+ | `GET&id=` | `get` — single row by id |
39
+ | `GET?action=list` | `list` |
40
+ | `GET?action=running` | `getRunning` — `status = 'running'` rows |
41
+ | `GET?action=stats` | `getStats` — status counts |
42
+ | `POST` (no action) | `create` — insert a `pending` execution |
43
+ | `POST?action=cleanup` | `cleanup` — delete old rows |
44
+ | `PATCH?action=start` | `start` — set `running` + `started_at` |
45
+ | `PATCH?action=complete` | `complete` — set terminal status |
46
+ | `PATCH?action=cancel` | `cancel` — set `cancelled` |
47
+
48
+ Any other action/method combination throws `Invalid action or method`.
49
+
50
+ ## Authentication
51
+
52
+ Wrapped in `createHandler`, so the request must resolve to a non-anonymous
53
+ principal — Bearer JWT (human), `x-api-key` (machine), or
54
+ `x-internal-secret` (internal) per `resolvePrincipal`. Anonymous requests
55
+ get 401 before the handler runs.
56
+
57
+ System admins (`roles` contains `system_admin`) read through `adminDb`
58
+ across all accounts. Everyone else reads through their scoped `ctx.db` and
59
+ must have an account context — `list`, `running`, and `stats` throw
60
+ `Account context required` without one.
61
+
62
+ ## Request contract
63
+
64
+ Query parameters:
65
+
66
+ - `id` — execution UUID. `get` requires it; the middleware also copies it
67
+ into `body.id` for write actions.
68
+ - `pipeline_id` — filter for `list`, `running`, `stats`.
69
+ - `status` — filter for `list`.
70
+ - `date_from`, `date_to` — `created_at` bounds for `stats`.
71
+ - `limit` (default 100), `offset` (default 0) — `list` pagination.
72
+
73
+ Body fields:
74
+
75
+ - `create`: `pipeline_id` (required), `trigger_data` (optional object).
76
+ - `start` / `cancel`: `id` (required).
77
+ - `complete`: `id` (required), `output_data`, `error_message`. A non-empty
78
+ `error_message` makes the final status `failed`; otherwise `completed`.
79
+ - `cleanup`: `days_to_keep` (default 30), `status_filter` (optional) —
80
+ deletes rows with `created_at` older than the cutoff.
81
+
82
+ ## Response contract
83
+
84
+ All successful returns are wrapped by the middleware in
85
+ `{ data, error: null, meta: { requestId, duration } }`.
86
+
87
+ - `list` / `running` — array of `pipeline_executions` rows, newest first
88
+ (`list` by `created_at`, `running` by `started_at`).
89
+ - `get` — one row.
90
+ - `stats` — `{ total, completed, failed, running }` counted in memory over
91
+ the filtered set.
92
+ - `create` — `{ execution_id }`.
93
+ - `start` / `complete` / `cancel` — `{ success: true }`.
94
+ - `cleanup` — `{ deleted_count }`.
95
+
96
+ ## Side effects
97
+
98
+ - `create` inserts a row with `status: 'pending'`, stamped with
99
+ `created_by` and `account_id`.
100
+ - `start` / `complete` / `cancel` update status and timestamps; `complete`
101
+ also writes `result`, `error_message`, and `duration_ms` computed from
102
+ `started_at`.
103
+ - `cleanup` deletes rows permanently.
104
+ - Each write emits an audit log: `pipeline_execution.created`,
105
+ `pipeline_execution.started`, `pipeline_execution.completed`,
106
+ `pipeline_execution.cancelled`, `pipeline_executions.cleaned`.
107
+
108
+ ## Errors
109
+
110
+ - `Execution ID is required` — `get`, `start`, `complete`, `cancel`
111
+ without `id`.
112
+ - `Pipeline ID is required` / `Account context required` — `create`
113
+ validation; account check on `list`, `running`, `stats` for non-admins.
114
+ - `Invalid action or method` — unmatched action/method pair.
115
+ - `get` uses `.single()` — a missing id surfaces as a database error,
116
+ not a clean 404.
117
+ - All thrown errors become structured 500 responses unless they carry a
118
+ `statusCode`.
119
+
120
+ ## Agent notes
121
+
122
+ - `create` here stamps `status: 'pending'` — a different value from the
123
+ `queued` rows the trigger engine creates before dispatching to
124
+ `pipeline-runner-background`. Both are non-terminal.
125
+ - Rows stuck in `queued` or `running` mean dispatch or the runner died;
126
+ `sweepStaleExecutions` in `.framework/functions/_shared/pipeline-runner.ts`
127
+ marks `running`/`pending`/`queued` rows untouched for over 10 minutes as
128
+ `failed`.
129
+ - `cancel` only updates the row — nothing in this function interrupts an
130
+ in-flight run.
131
+ - `stats` loads all matching `id, status` rows and counts in memory; it
132
+ has no `limit` guard, so very large histories are expensive.