spine-framework 0.4.4 → 0.5.0-rc.5

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 (305) 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/install-app.ts +183 -13
  8. package/.framework/cli/commands/migrate.ts +36 -30
  9. package/.framework/cli/commands/migrations.ts +16 -56
  10. package/.framework/cli/commands/upgrade.ts +35 -0
  11. package/.framework/cli/commands/validate-app.ts +127 -1
  12. package/.framework/cli/commands/validate-schema.ts +147 -0
  13. package/.framework/cli/context.ts +223 -110
  14. package/.framework/cli/index.ts +6 -0
  15. package/.framework/cli/lib/project-files.ts +241 -1
  16. package/.framework/functions/_shared/app-manifest.ts +2 -0
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/logging.ts +562 -224
  19. package/.framework/functions/_shared/middleware.ts +47 -44
  20. package/.framework/functions/_shared/permissions.ts +4 -0
  21. package/.framework/functions/admin-data.ts +51 -28
  22. package/.framework/functions/ai-agents.ts +10 -6
  23. package/.framework/functions/api-keys.ts +301 -122
  24. package/.framework/functions/apps.ts +63 -29
  25. package/.framework/functions/cil-ingest.ts +48 -15
  26. package/.framework/functions/debug-auth.ts +14 -8
  27. package/.framework/functions/deliveries.ts +39 -12
  28. package/.framework/functions/embeddings.ts +149 -78
  29. package/.framework/functions/integration-outbound.ts +67 -27
  30. package/.framework/functions/integrations.ts +254 -127
  31. package/.framework/functions/logs.ts +273 -133
  32. package/.framework/functions/observability.ts +278 -148
  33. package/.framework/functions/pipeline-executions.ts +1 -1
  34. package/.framework/functions/pipelines.ts +14 -9
  35. package/.framework/functions/prompt-configs.ts +7 -4
  36. package/.framework/functions/roles.ts +8 -5
  37. package/.framework/functions/security.ts +30 -17
  38. package/.framework/functions/system-cron.ts +240 -127
  39. package/.framework/functions/tests.ts +7 -6
  40. package/.framework/functions/timers.ts +310 -107
  41. package/.framework/functions/triggers.ts +28 -13
  42. package/.framework/functions/types.ts +13 -10
  43. package/.framework/migrations/069_log_stats_window.sql +54 -0
  44. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  45. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  46. package/.framework/migrations/072_slug_autofill.sql +77 -0
  47. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  48. package/.framework/migrations/074_app_route_prefix_domains.sql +168 -0
  49. package/.framework/migrations/075_security_definer_grants.sql +157 -0
  50. package/.framework/src/apps/admin/index.tsx +1141 -775
  51. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  52. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  53. package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
  54. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  55. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  56. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  57. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  58. package/.framework/src/components/layout/Sidebar.tsx +300 -177
  59. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  60. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  61. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  62. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  63. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  64. package/.framework/src/components/ui/sheet.tsx +24 -26
  65. package/.framework/src/components/ui/textarea.tsx +74 -48
  66. package/.framework/src/contexts/AppContext.tsx +5 -12
  67. package/.framework/src/contexts/AuthContext.tsx +17 -9
  68. package/.framework/src/hooks/useApps.ts +18 -22
  69. package/.framework/src/hooks/useEntityList.ts +12 -9
  70. package/.framework/src/hooks/useListSchema.ts +108 -58
  71. package/.framework/src/instructions/README.md +74 -0
  72. package/.framework/src/instructions/accounts.md +97 -0
  73. package/.framework/src/instructions/ai-agents.md +69 -0
  74. package/.framework/src/instructions/alerts.md +80 -0
  75. package/.framework/src/instructions/api-keys.md +87 -0
  76. package/.framework/src/instructions/apps.md +77 -0
  77. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  78. package/.framework/src/instructions/concepts/apps-and-renderers.md +228 -0
  79. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  80. package/.framework/src/instructions/concepts/automation.md +186 -0
  81. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  82. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  83. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  84. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  85. package/.framework/src/instructions/concepts/observability.md +116 -0
  86. package/.framework/src/instructions/concepts/permissions.md +150 -0
  87. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  88. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  89. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  90. package/.framework/src/instructions/deliveries.md +59 -0
  91. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  92. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  93. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  94. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  95. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  96. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  97. package/.framework/src/instructions/integrations.md +96 -0
  98. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  99. package/.framework/src/instructions/internal/docs-contract.md +158 -0
  100. package/.framework/src/instructions/invites.md +60 -0
  101. package/.framework/src/instructions/kb.md +80 -0
  102. package/.framework/src/instructions/logs.md +77 -0
  103. package/.framework/src/instructions/notifications.md +77 -0
  104. package/.framework/src/instructions/observability.md +61 -0
  105. package/.framework/src/instructions/operations/deployment.md +115 -0
  106. package/.framework/src/instructions/operations/migrations.md +125 -0
  107. package/.framework/src/instructions/operations/security-checklist.md +170 -0
  108. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  109. package/.framework/src/instructions/operations/upgrade.md +116 -0
  110. package/.framework/src/instructions/people.md +91 -0
  111. package/.framework/src/instructions/pipeline-executions.md +62 -0
  112. package/.framework/src/instructions/pipelines.md +78 -0
  113. package/.framework/src/instructions/prompts.md +95 -0
  114. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  115. package/.framework/src/instructions/reference/api/auth.md +113 -0
  116. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  117. package/.framework/src/instructions/reference/app-manifest.md +184 -0
  118. package/.framework/src/instructions/reference/cli.md +124 -0
  119. package/.framework/src/instructions/reference/database.md +132 -0
  120. package/.framework/src/instructions/reference/design-schema.md +280 -0
  121. package/.framework/src/instructions/reference/env-vars.md +133 -0
  122. package/.framework/src/instructions/reference/frontend-sdk.md +207 -0
  123. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  124. package/.framework/src/instructions/reference/functions/admin-data.md +197 -0
  125. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  126. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  127. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  128. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  129. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  130. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  131. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  132. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  133. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  134. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  135. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  136. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  137. package/.framework/src/instructions/reference/functions/index.md +103 -0
  138. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  139. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  140. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  141. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  142. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  143. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  144. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  145. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  146. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  147. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  148. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  149. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  150. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  151. package/.framework/src/instructions/reference/functions/security.md +140 -0
  152. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  153. package/.framework/src/instructions/reference/functions/system.md +106 -0
  154. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  155. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  156. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  157. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  158. package/.framework/src/instructions/reference/functions/types.md +146 -0
  159. package/.framework/src/instructions/reference/seeds.md +126 -0
  160. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  161. package/.framework/src/instructions/reference/spine-config.md +114 -0
  162. package/.framework/src/instructions/roles.md +80 -0
  163. package/.framework/src/instructions/runtime-collaboration.md +106 -0
  164. package/.framework/src/instructions/runtime-detail.md +70 -0
  165. package/.framework/src/instructions/runtime-list.md +77 -0
  166. package/.framework/src/instructions/security.md +67 -0
  167. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  168. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  169. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  170. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  171. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  172. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  173. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  174. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  175. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  176. package/.framework/src/instructions/tasks/document-your-app.md +117 -0
  177. package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
  178. package/.framework/src/instructions/tasks/register-an-app.md +137 -0
  179. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  180. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  181. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  182. package/.framework/src/instructions/testing.md +59 -0
  183. package/.framework/src/instructions/timers.md +84 -0
  184. package/.framework/src/instructions/triggers.md +69 -0
  185. package/.framework/src/instructions/types.md +93 -0
  186. package/.framework/src/lib/app-mount.ts +88 -0
  187. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  188. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  189. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  190. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  191. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  192. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  193. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  194. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  195. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  196. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  197. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  198. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  199. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  200. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  201. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  202. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  203. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  204. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  205. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  206. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  207. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  208. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  209. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  210. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  211. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  212. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  213. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  214. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  215. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  216. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  217. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  218. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  219. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  220. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  221. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  222. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  223. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  224. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  225. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  226. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  227. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  228. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  229. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  230. package/.framework/src/pages/auth/LoginPage.tsx +3 -2
  231. package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
  232. package/.framework/templates/agent/AGENTS.md +167 -0
  233. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  234. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  235. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  236. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  237. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +79 -0
  238. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  239. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
  240. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
  241. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -0
  242. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  243. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  244. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  245. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  246. package/.framework/templates/project.json +4 -1
  247. package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
  248. package/.framework/tests/unit/agent-files.test.ts +355 -0
  249. package/.framework/tests/unit/agent-runner.test.ts +479 -44
  250. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  251. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  252. package/.framework/tests/unit/app-mount.test.ts +80 -0
  253. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  254. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  255. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  256. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  257. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  258. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  259. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  260. package/.framework/tests/unit/security-definer-grants.test.ts +154 -0
  261. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  262. package/.framework/tests/unit/system-cron.test.ts +240 -0
  263. package/.framework/tests/unit/timers.test.ts +159 -0
  264. package/README.md +125 -166
  265. package/config/tsconfig.cli.json +2 -2
  266. package/package.json +15 -4
  267. package/templates/project/package-lock.json +2 -2
  268. package/.framework/docs/admin-routes.md +0 -76
  269. package/.framework/docs/api-docs-progress.md +0 -38
  270. package/.framework/docs/api-governance.md +0 -146
  271. package/.framework/docs/api-testing-results.md +0 -212
  272. package/.framework/docs/assembly-launch-guide.md +0 -138
  273. package/.framework/docs/audit-results.md +0 -590
  274. package/.framework/docs/db-api-inventory.md +0 -95
  275. package/.framework/docs/gap-remediation-backlog.md +0 -103
  276. package/.framework/docs/ui-adoption-verification.md +0 -111
  277. package/.framework/docs/ui-api-coverage.md +0 -84
  278. package/.framework/docs/v2-compatibility-audit.md +0 -228
  279. package/.framework/mcp/index.ts +0 -133
  280. package/.framework/mcp/load-env.ts +0 -60
  281. package/.framework/mcp/shared/app-manifest.ts +0 -275
  282. package/.framework/mcp/shared/db.ts +0 -477
  283. package/.framework/mcp/shared/kb.ts +0 -118
  284. package/.framework/mcp/tools/architect.ts +0 -431
  285. package/.framework/mcp/tools/cil/context.ts +0 -345
  286. package/.framework/mcp/tools/cil/graph.ts +0 -242
  287. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  288. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  289. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  290. package/.framework/mcp/tools/cil/project.ts +0 -791
  291. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  292. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  293. package/.framework/mcp/tools/cil/search.ts +0 -97
  294. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  295. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  296. package/.framework/mcp/tools/kb/delete.ts +0 -139
  297. package/.framework/mcp/tools/kb/index.ts +0 -52
  298. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  299. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  300. package/.framework/mcp/tools/kb/publish.ts +0 -100
  301. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  302. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  303. package/.framework/mcp/tools/kb/search.ts +0 -101
  304. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  305. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: account-nodes
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/account-nodes.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/public/_redirects
11
+ ---
12
+
13
+ # account-nodes
14
+
15
+ ## What it does
16
+
17
+ Returns one account ("node") plus its position in the account hierarchy:
18
+ ancestors, direct children, and full descendants. Used to render account
19
+ trees and breadcrumb navigation.
20
+
21
+ ## Route and methods
22
+
23
+ `/.netlify/functions/account-nodes`, reachable as
24
+ `GET /api/account-nodes?node_id=<uuid>` (`/api/*` →
25
+ `/.netlify/functions/:splat`). The handler performs no method check — any
26
+ HTTP method with the query parameter works.
27
+
28
+ ## Authentication
29
+
30
+ Standard `createHandler`: `x-api-key`, internal secret, or
31
+ `Bearer <jwt>` required; anonymous is rejected with 401. Queries run on
32
+ `ctx.db`, so human callers see only what row-level security allows; machine
33
+ and system principals use the service role.
34
+
35
+ ## Request contract
36
+
37
+ One query parameter:
38
+
39
+ - `node_id` — required, the `accounts.id` to center the lookup on.
40
+
41
+ ## Response contract
42
+
43
+ `{ node, ancestors, children, descendants }` inside the standard
44
+ `{ data, error, meta }` envelope:
45
+
46
+ - `node` — the account row's embedded `type` (`{ type: { id, slug, name,
47
+ icon, color } }`); the account's own columns are not selected.
48
+ - `ancestors` — rows returned by the `get_account_ancestors` RPC, else `[]`.
49
+ - `children` — active `accounts` rows with `parent_id = node_id`, each
50
+ shaped `{ type: {...} }`, ordered by `display_name`.
51
+ - `descendants` — rows returned by the `get_account_descendants` RPC, else `[]`.
52
+
53
+ ## Side effects
54
+
55
+ None — read-only. A `request.get` span is emitted by the middleware like
56
+ every `createHandler` call.
57
+
58
+ ## Errors
59
+
60
+ | Condition | HTTP | Message |
61
+ |---|---|---|
62
+ | Missing `node_id` | 500 | `node_id is required` |
63
+ | No active account with that id | 500 | `Account node not found` |
64
+ | RPC/query failure | 500 | underlying PostgREST message |
65
+
66
+ ## Agent notes
67
+
68
+ - The `select` on `node` and `children` contains only the embedded `type`
69
+ relation — do not expect `display_name`, `slug`, or other account
70
+ columns on those payloads; fetch the account itself via
71
+ `/api/admin-data?entity=accounts&id=…` when you need them.
72
+ - `ancestors`/`descendants` come from database functions; their row shape
73
+ is whatever the RPC returns, not the `{ type }` shape used for `node`.
74
+ - Inactive accounts are filtered from `node` and `children`
75
+ (`is_active = true`) but RPC results are whatever the functions return.
@@ -0,0 +1,197 @@
1
+ ---
2
+ title: admin-data
3
+ audience: both
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-11-01
7
+ source_files:
8
+ - .framework/functions/admin-data.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/permissions.ts
11
+ - .framework/functions/_shared/access.ts
12
+ - .framework/functions/_shared/security.ts
13
+ - .framework/public/_redirects
14
+ ---
15
+
16
+ # admin-data
17
+
18
+ ## What it does
19
+
20
+ `admin-data` is the generic CRUD endpoint for Spine's runtime and config
21
+ tables. One function exposes list, get, stats, create, update, and remove
22
+ across a fixed allowlist of entities, with record-permission evaluation,
23
+ field-level sanitization, type-schema stamping, and trigger firing built in.
24
+ Custom apps are expected to use this endpoint for all runtime-record CRUD
25
+ rather than wrapping it.
26
+
27
+ ## Route and methods
28
+
29
+ `/.netlify/functions/admin-data`, reachable publicly as `/api/admin-data`
30
+ (`/api/*` is redirected to `/.netlify/functions/:splat` in
31
+ `.framework/public/_redirects`).
32
+
33
+ Dispatch is by `?action=` plus HTTP method (`.framework/functions/admin-data.ts`
34
+ handler):
35
+
36
+ | Call | Action |
37
+ |---|---|
38
+ | `GET /api/admin-data?action=list&entity=…` | `list` |
39
+ | `GET /api/admin-data?action=get&entity=…&id=…` | `get` |
40
+ | `GET /api/admin-data?action=stats&entity=…` | `stats` |
41
+ | `POST /api/admin-data?action=create` | `create` |
42
+ | `PATCH /api/admin-data?action=update&entity=…&id=…` | `update` |
43
+ | `DELETE /api/admin-data?action=remove&entity=…&id=…` | `remove` |
44
+
45
+ Without a recognized `action` (absent or unmatched), the HTTP method alone
46
+ dispatches: `GET` → `get` when `id` is present else `list`, `POST` →
47
+ `create`, `PATCH` → `update`, `DELETE` → `remove`. A `?method=` query
48
+ parameter overrides the HTTP method for dispatch (the middleware copies
49
+ `event.httpMethod` into `query.method` only when the query did not set
50
+ it). A matched action with the wrong method (e.g. `action=list` on `POST`)
51
+ falls through to `Invalid action or method`.
52
+
53
+ ## Authentication
54
+
55
+ Standard `createHandler` authentication: `x-api-key` (machine), internal
56
+ invoke secret (system), or `Authorization: Bearer <jwt>` (human). Anonymous
57
+ requests are rejected with 401 before the handler runs.
58
+
59
+ - Humans query through a JWT-scoped client (`ctx.db`), so row-level security
60
+ applies. Machines, anonymous, and system principals get the service-role
61
+ client — endpoint-level checks carry the weight.
62
+ - `list` switches to `adminDb` when `principal.isInternal` or the principal
63
+ is a system admin; otherwise it uses `ctx.db`.
64
+ - `create` calls `resolveFirstSurfacePermissions(personId, accountId,
65
+ typeSlug, 'create', designSchema)` — a machine whose scopes contain `*:*`
66
+ bypasses (`isMachineGlobal`); a `*:*` machine also resolves as
67
+ `system_admin`.
68
+ - `update` runs `validateUpdatePermissions` and lets system admins through
69
+ regardless of the result.
70
+ - `remove` calls `resolveFirstSurfacePermissions(..., 'delete',
71
+ existing.design_schema)` with a system-admin bypass.
72
+ - Non-internal audiences never see `visibility: 'internal'` threads, and
73
+ internal messages on external threads are projected to label-only
74
+ skeletons via `projectMessageForCustomer`.
75
+
76
+ ## Request contract
77
+
78
+ `entity` is required on every call and must be one of `VALID_ENTITIES`:
79
+ `accounts`, `people`, `items`, `threads`, `messages`, `links`,
80
+ `attachments`, `watchers`, `item_progress`, `logs`, `test_runs`,
81
+ `observability`, `test_cases`, `types`, `link_types`, `ai_agents`,
82
+ `prompt_configs`.
83
+
84
+ **list** (query params): `entity`, `search` (ILIKE against a per-entity
85
+ field — e.g. `title` for items, `full_name` for people, `message` for
86
+ logs), `sort_field` (default `created_at`), `sort_direction` (`desc`
87
+ default), `limit` (default 50), `offset` (default 0), `type_slug`,
88
+ `type_slugs` (comma-separated, resolved through `ENTITY_TO_KIND` to
89
+ `type_id` filters), `view`, `flag_only` / `exclude_flag` (filter on
90
+ `data->><flag>` for `items`, `threads`, `messages`, `links`, `attachments`,
91
+ `watchers`). Every other query parameter becomes an `.eq(key, value)`
92
+ filter, with boolean coercion for `is_active`, `is_verified`, `is_primary`.
93
+ Soft-deleted rows are hidden by default — pass `is_active=false` or
94
+ `include_inactive=true` to include them. The default `is_active` filter
95
+ only applies to tables that have the column; `links`, `attachments`,
96
+ `watchers`, `logs`, and `observability` skip it.
97
+
98
+ **get** (query params): `entity`, `id`, `view`, `lineage` — for `items`,
99
+ parent/child lineage is attached unless `lineage=0`.
100
+
101
+ **stats** (query params): `entity` only.
102
+
103
+ **create** (body): `entity` (or `?entity=`), `type_slug` or `type_id` —
104
+ required for every non-raw entity; `account_id` (optional override);
105
+ arbitrary record fields. `design_schema`, `validation_schema`, and `id` in
106
+ the body are stripped. `thread_id`, `target_id`, or `parent_id` in the body
107
+ pull account and `scope` from the parent record.
108
+
109
+ **update** (query `entity` + `id`; body): record fields; `design_schema`,
110
+ `validation_schema`, `type_id`, `type_slug`, `id`, `entity` are stripped.
111
+ `account_id` is honored only for `entity=people`.
112
+
113
+ **remove** (query params): `entity`, `id`, `soft` (default `'true'`).
114
+
115
+ ## Response contract
116
+
117
+ All responses use the middleware envelope:
118
+ `{ "data": <result>, "error": null, "meta": { "requestId", "duration" } }`
119
+ with HTTP 200. Errors return `{ "data": null, "error": "<message>" }` with
120
+ HTTP `err.statusCode || 500`.
121
+
122
+ - `list` → array of sanitized records; when both `view` and `type_slug`
123
+ are given and the type resolves, `{ data, schema, view }` (schema = the
124
+ type's `design_schema`, `view` = `schema.views[view]` or `null`). The
125
+ exact-count probe runs but the count is not returned.
126
+ - `get` → one sanitized record; `items` records additionally carry
127
+ `parents`, `children`, and `type_slug` fields from
128
+ `design_schema.functionality.lineage`. With `view`, returns
129
+ `{ data, schema, view }`.
130
+ - `stats` → `{ entity, count }`.
131
+ - `create` / `update` → the sanitized row.
132
+ - `remove` → `{ deleted: true, soft: true, data }` on soft delete, or
133
+ `{ deleted: true, soft: false }` on hard delete.
134
+
135
+ ## Side effects
136
+
137
+ - Writes to the target table. On create, `design_schema` and
138
+ `validation_schema` are stamped onto the record from the resolved type
139
+ (missing `record_permissions` defaults to `PERMISSIONS_ALL` — all
140
+ actions for everyone), plus `created_by`, `created_at`, `updated_at`,
141
+ and `account_id` (except `accounts`, which gets `parent_id` instead).
142
+ `types` and `link_types` skip `created_by`/`updated_by` (`UNAUDITED_ENTITIES`).
143
+ - `scope: 'platform'` schemas force `account_id` to the `spine-system`
144
+ account; a `thread_id`/`target_id`/`parent_id` parent overrides both
145
+ account and scope.
146
+ - `fireCreateTriggers` runs after every create (including raw entities) and
147
+ `fireUpdateTriggers` after every update; trigger errors are logged, not
148
+ raised. **`fireDeleteTriggers` is imported but never called** — removing
149
+ a record fires no triggers.
150
+ - `logs`, `test_runs`, `observability`, `test_cases` (`RAW_ENTITIES`)
151
+ bypass type resolution entirely: create inserts a fixed column whitelist
152
+ (e.g. `level`+`message` required for `logs`, `suite` for `test_runs`,
153
+ `metric_name`+`metric_value` for `observability`, `slug`+`name` for
154
+ `test_cases`) and update skips the `updated_by`/`updated_at` stamps.
155
+ - `checkTypeSlugRateLimit` enforces the app's `rate_limits.<type_slug>_create`
156
+ policies on create.
157
+ - Every action is wrapped in `withAdminLogging`, which writes
158
+ admin-operation log rows (entity, action, method, duration, row count).
159
+
160
+ ## Errors
161
+
162
+ | Condition | HTTP | Message |
163
+ |---|---|---|
164
+ | No/invalid `entity` | 400 (create) / 500 (others) | `Valid entity parameter is required` |
165
+ | Missing `id` on get/update/remove | 500 | `ID parameter is required` / `ID is required for update|delete` |
166
+ | Missing type on create | 400 | `type_id or type_slug is required — every runtime record must reference a type` |
167
+ | Bad `type_id` / inactive type | 500 | `type_id not found: …` / `type_id references an inactive type: …` |
168
+ | `type_slug` on an unmappable entity | 400 | `type_slug is not supported for entity: …` |
169
+ | Missing parent | 403 | `Parent record not found or not accessible` |
170
+ | Permission denied | 403 | `Insufficient permissions to create|update|delete this record` |
171
+ | Record missing or RLS-hidden | 500 | `Record not found or access denied` |
172
+ | Bad flag name | 400 | `Invalid flag field name` |
173
+ | Unknown action/method | 500 | `Invalid action or method` |
174
+
175
+ ## Agent notes
176
+
177
+ - Always pass `type_slug` (not `type_id`) when creating runtime records —
178
+ the endpoint resolves it per `kind` via `ENTITY_TO_KIND`
179
+ (`items`→`item`, `threads`→`thread`, `messages`→`message`, `links`→`link`,
180
+ `attachments`→`attachment`, `watchers`→`watcher`, `accounts`→`account`,
181
+ `people`→`person`, `item_progress`→`item_progress`).
182
+ - Soft delete is the default and only applies to `accounts`, `people`,
183
+ `items`, `threads`, `messages`, `watchers`, `item_progress`,
184
+ `test_cases` — it sets `is_active=false`. Everything else hard-deletes
185
+ even without `soft=false`; pass `soft=false` explicitly when you mean it.
186
+ - Delete triggers do not exist in practice: `remove` never calls
187
+ `fireDeleteTriggers`, so automations keyed on delete events will not run.
188
+ - `?id=` is mirrored into the JSON body by the middleware before the
189
+ handler runs.
190
+ - Record reads are sanitized per principal: second/third-surface tables and
191
+ field-level `permissions` in `design_schema` shape what comes back, and a
192
+ principal with no account context gets only `id`/`created_at`/`updated_at`.
193
+ - A machine API key without `*:*` scope cannot create (the permission
194
+ lookup is by `people.id`, which machine keys don't have) — use `*:*`
195
+ keys for server-side writes, or a human JWT.
196
+ - `update` does not re-stamp `design_schema`; changing a record's type
197
+ is not possible through this endpoint (`type_id` is stripped).
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: admin-funnel
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-funnel.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-funnel
14
+
15
+ ## What it does
16
+
17
+ Computes anonymous-visitor → identified-user → installed-instance
18
+ conversion metrics for the platform funnel. It reads `items` rows under
19
+ three app-seeded item types — `funnel_signal`, `anonymous_session`, and
20
+ `spine_instance` — and returns counts, conversion rates, and a
21
+ stage distribution over the last 30 days.
22
+
23
+ This is framework-internal telemetry for the platform operator (growth
24
+ and install funnel for Spine itself), not per-customer app analytics.
25
+
26
+ ## Route and methods
27
+
28
+ **None — this file exports no `handler`.** It exports a single
29
+ `createHandler`-wrapped const, `metrics`, which nothing else imports or
30
+ dispatches. As shipped there is no HTTP entrypoint: the file is deployed
31
+ to the functions directory by the assembly script like every other
32
+ `.framework/functions` file, but Netlify has no `handler` export to
33
+ invoke. Treat it as a dormant module pending a dispatch handler (or a
34
+ wiring bug); do not cite it as a live endpoint.
35
+
36
+ ## Authentication
37
+
38
+ If `metrics` were wired: `createHandler` requires any non-anonymous
39
+ principal — no role check inside the function. All queries run on
40
+ `adminDb` across **all accounts** (see Agent notes).
41
+
42
+ ## Request contract
43
+
44
+ `metrics(ctx, body)` takes no parameters — the 30-day window is hardcoded.
45
+
46
+ ## Response contract
47
+
48
+ `{ anonymous_visitors, identified_users, installed_instances,
49
+ conversion_rates: { anonymous_to_identified, identified_to_installed },
50
+ stage_distribution: [{ stage, count, percentage }] }`
51
+
52
+ - `anonymous_visitors` — `anonymous_session` items in the last 30 days
53
+ whose `data->lifecycle->>stitched_to_account_id` is null.
54
+ - `identified_users` — distinct `account_id`s on `funnel_signal` items in
55
+ the last 30 days.
56
+ - `installed_instances` — `spine_instance` items with a non-null
57
+ `data->>claimed_by_person_id` (all time).
58
+ - `stage_distribution` — counts of `funnel_signal.data->>stage` over the
59
+ last 30 days, only counting rows that have an `account_id`.
60
+
61
+ ## Side effects
62
+
63
+ None — read-only aggregation.
64
+
65
+ ## Errors
66
+
67
+ No explicit error paths. Missing signal types are expected on a clean
68
+ install (the types are seeded by installed apps): when none of the three
69
+ type slugs resolves, `metrics` returns a zeroed payload instead of
70
+ failing. Database errors would surface as thrown errors through
71
+ `createHandler`'s 500 path.
72
+
73
+ ## Agent notes
74
+
75
+ - **Verify before use.** The missing `handler` export means there is no
76
+ routed endpoint; check the current tree before assuming
77
+ `GET /api/admin-funnel` does anything.
78
+ - **Unscoped platform-wide reads.** If wired as-is, every query runs on
79
+ `adminDb` with no `account_id` filter and no `isSystemAdmin` gate — any
80
+ authenticated principal would see cross-account funnel data. A
81
+ `system_admin` check belongs in any dispatch handler that exposes it.
82
+ - Signal data is written elsewhere — `emitSignal` in
83
+ `.framework/functions/_shared/funnel.ts` inserts typed signal items
84
+ (`funnel_signal` among them) and fires `item_created` triggers; these
85
+ admin modules only read the results.
86
+ - The `funnel_signal_use` type seed exists in the framework
87
+ (`.framework/seeds/types/funnel_signal_use.json`); the
88
+ `funnel_signal`/`anonymous_session`/`spine_instance` slugs the queries
89
+ look for are seeded by installed apps, hence the degrade-to-zeros
90
+ behavior.
@@ -0,0 +1,107 @@
1
+ ---
2
+ title: admin-install-funnel
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-install-funnel.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-install-funnel
14
+
15
+ ## What it does
16
+
17
+ Install-funnel analytics for the platform operator: download/install/
18
+ first-use/claimed counts, per-day install trends over 30 days, recent
19
+ install records with contact and account joins, a version breakdown, and
20
+ a full export of install-instance records. All data comes from `items`
21
+ rows under the `funnel_signal` type where `data->>source = 'inst'`, plus
22
+ `spine_instance` rows for the version split.
23
+
24
+ Framework-internal telemetry about Spine installations (serials, app
25
+ versions, environments), not customer app data.
26
+
27
+ ## Route and methods
28
+
29
+ **None — this file exports no `handler`.** It exports two
30
+ `createHandler`-wrapped consts, `metrics` and `exportData`, which nothing
31
+ imports or dispatches. As shipped there is no HTTP entrypoint; treat it
32
+ as a dormant module pending a dispatch handler (or a wiring bug), not a
33
+ live endpoint.
34
+
35
+ ## Authentication
36
+
37
+ If wired: `createHandler` requires any non-anonymous principal — no role
38
+ check inside either export. All queries run on `adminDb` across **all
39
+ accounts** (see Agent notes).
40
+
41
+ ## Request contract
42
+
43
+ Neither export reads parameters — `metrics` uses fixed windows and
44
+ `exportData` returns the full matching set.
45
+
46
+ ## Response contract
47
+
48
+ `metrics` →
49
+
50
+ ```json
51
+ {
52
+ "total_downloads": 0,
53
+ "unique_installers": 0,
54
+ "active_instances": 0,
55
+ "conversion_funnel": { "downloads": 0, "installations": 0, "first_use": 0, "claimed": 0 },
56
+ "install_trends": [{ "date": "YYYY-MM-DD", "downloads": 0, "installations": 0 }],
57
+ "recent_installs": [{ "id": "…", "serial": "…", "app_version": "…", "environment": "…",
58
+ "first_seen_at": "…", "last_signal_at": "…",
59
+ "claimed_by": "…", "claimed_at": "…",
60
+ "account_name": "…", "person_email": "…" }],
61
+ "top_versions": [{ "version": "…", "count": 0, "percentage": 0 }]
62
+ }
63
+ ```
64
+
65
+ Field semantics:
66
+
67
+ - `total_downloads` / `installations` / `first_use` — `funnel_signal`
68
+ rows with `action_type` `download` / `install` / `first_command`.
69
+ - `unique_installers` — distinct `data->>serial` values.
70
+ - `active_instances` — serialized signals seen in the last 7 days.
71
+ - `claimed` — signals with non-null `data->>claimed_by_person_id`.
72
+ - `install_trends` — 30 daily buckets of download + install counts.
73
+ - `recent_installs` — latest 20 serialized signals joined to
74
+ `people(email)` and `accounts(name)`.
75
+ - `top_versions` — up to 10 versions by `spine_instance` count share.
76
+
77
+ `exportData` → `{ instances, exported_at, total_count }` — every
78
+ `funnel_signal` row with `source='inst'` and a serial, full `data` bag,
79
+ `account_id`, and the same people/accounts joins, newest first.
80
+
81
+ ## Side effects
82
+
83
+ None — read-only.
84
+
85
+ ## Errors
86
+
87
+ No explicit error paths. When the `funnel_signal` type is absent
88
+ (app-seeded; missing on a clean install) `metrics` returns a zeroed
89
+ payload and `exportData` returns `{ instances: [], …, total_count: 0 }`.
90
+ The 30-day trend loop issues two count queries per day (60 sequential
91
+ queries) — slow but not an error.
92
+
93
+ ## Agent notes
94
+
95
+ - **Verify before use** — no `handler` export means no routed endpoint;
96
+ `?action=export`-style dispatch does not exist in the file.
97
+ - **Platform-wide PII.** `exportData` and the `recent_installs` join
98
+ return `people.email` and `accounts.name` across all accounts via
99
+ `adminDb`, gated only by authentication if this is ever wired. Any
100
+ dispatch handler added later must gate on `system_admin` (or narrower)
101
+ before exposing these exports.
102
+ - The daily-trend loop is 60 round trips per call — do not put it on a
103
+ hot path; if the endpoint is wired for real use, fold the loop into a
104
+ single grouped query or RPC.
105
+ - `serial` is the installer-instance identity; `claimed_by_person_id`
106
+ links an install to a registered person — that join is what makes the
107
+ export PII-bearing.
@@ -0,0 +1,102 @@
1
+ ---
2
+ title: admin-signals
3
+ audience: developers, agents
4
+ kind: reference
5
+ verified_against: f69c37d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/admin-signals.ts
9
+ - .framework/functions/_shared/middleware.ts
10
+ - .framework/functions/_shared/funnel.ts
11
+ ---
12
+
13
+ # admin-signals
14
+
15
+ ## What it does
16
+
17
+ Two read surfaces over `funnel_signal` items for the platform operator:
18
+ `metrics` aggregates signal totals (all-time, today, this week), a
19
+ conversion rate from high-value signals, top sources, and the 20 most
20
+ recent signals; `audit` lists individual signal records with filters,
21
+ pagination, attribution fields (UTM/referrer/anonymous identity), and an
22
+ unpaginated export mode.
23
+
24
+ Signals record product-journey events (`action_type`, `stage`, `source`,
25
+ `rating`) — framework-internal telemetry for the platform, not customer
26
+ app analytics.
27
+
28
+ ## Route and methods
29
+
30
+ **None — this file exports no `handler`.** It exports `metrics` and
31
+ `audit`, both `createHandler`-wrapped, which nothing imports or
32
+ dispatches. As shipped there is no HTTP entrypoint; treat it as a dormant
33
+ module pending a dispatch handler, not a live endpoint.
34
+
35
+ ## Authentication
36
+
37
+ If wired: `createHandler` requires any non-anonymous principal — no role
38
+ check in either export. All queries run on `adminDb` across **all
39
+ accounts** (see Agent notes).
40
+
41
+ ## Request contract
42
+
43
+ `metrics` — no parameters; windows are hardcoded (today, trailing 7
44
+ days).
45
+
46
+ `audit` — query parameters:
47
+
48
+ | Param | Effect |
49
+ |---|---|
50
+ | `page` (default 1), `limit` (default 50) | Pagination (ignored in export mode) |
51
+ | `date_from`, `date_to` | `created_at` bounds |
52
+ | `source`, `stage`, `action_type` | Exact match on `data->>` fields |
53
+ | `min_rating` | `data->>rating >= value` |
54
+ | `account_id` | Caller-supplied account filter |
55
+ | `search` | OR-match on `accounts.name`, `people.email`, `data->attribution->>first_touch_referrer_domain` (ILIKE) |
56
+ | `export` (any value) | Returns the full formatted array with no range limit |
57
+
58
+ ## Response contract
59
+
60
+ `metrics` → `{ total_signals, signals_today, signals_this_week,
61
+ conversion_rate, top_sources: [{source, count}], recent_signals: [{id,
62
+ action_type, source, stage, created_at, account_name, rating}] }`.
63
+ `conversion_rate` is the share of this week's signals with
64
+ `data->>rating >= '4'` — a "high-value signal" rate, not a funnel
65
+ conversion.
66
+
67
+ `audit` → `{ signals, total, page, limit }`, or in export mode the bare
68
+ formatted array. Each signal row: `{ id, action_type, action_value,
69
+ stage, source, created_at, account_name, person_email, anonymous_id,
70
+ session_id, rating, raw_score, referrer_domain, utm_source, utm_medium,
71
+ utm_campaign }` — joined to `people(email)` and `accounts(name)`, with
72
+ identity fields from `data->identity` and attribution from
73
+ `data->attribution`.
74
+
75
+ ## Side effects
76
+
77
+ None — read-only.
78
+
79
+ ## Errors
80
+
81
+ - `Failed to fetch signals: <message>` — the one thrown error, on query
82
+ failure inside `audit`.
83
+ - Missing `funnel_signal` type (app-seeded; absent on clean installs)
84
+ degrades gracefully: `metrics` returns zeroed metrics, `audit` returns
85
+ an empty page or `[]` in export mode.
86
+
87
+ ## Agent notes
88
+
89
+ - **Verify before use** — no `handler` export means no routed endpoint.
90
+ - **Cross-account PII, caller-steered.** `audit` accepts an arbitrary
91
+ `account_id` filter and returns `people.email`, `accounts.name`,
92
+ anonymous IDs, session IDs, and UTM attribution across all accounts via
93
+ `adminDb`. If a dispatch handler is ever added, it needs a
94
+ `system_admin` gate before this surface is exposed.
95
+ - `export` mode drops the range limit — it returns the entire filtered
96
+ set, so combined with the joins it is the heaviest call in the file.
97
+ - Rating comparisons are string comparisons on `data->>rating`
98
+ (`gte '4'`) — works for single digits, but `"10" >= "4"` is false
99
+ lexicographically; values above 9 would misclassify.
100
+ - Signals are produced by `emitSignal` in
101
+ `.framework/functions/_shared/funnel.ts` (deduped once per account +
102
+ `action_type` by default) — this module only reads them.