spine-framework 0.3.145 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/.env.example +76 -0
  2. package/.framework/.spine-manifest.json +19 -0
  3. package/.framework/cli/commands/agents.ts +61 -33
  4. package/.framework/cli/commands/auth.ts +60 -28
  5. package/.framework/cli/commands/create-app.ts +212 -80
  6. package/.framework/cli/commands/dev.ts +184 -68
  7. package/.framework/cli/commands/doctor.ts +324 -152
  8. package/.framework/cli/commands/generate.ts +119 -68
  9. package/.framework/cli/commands/init.ts +141 -242
  10. package/.framework/cli/commands/install-app.ts +1336 -314
  11. package/.framework/cli/commands/items.ts +31 -29
  12. package/.framework/cli/commands/migrate.ts +299 -156
  13. package/.framework/cli/commands/migrations.ts +62 -30
  14. package/.framework/cli/commands/pipelines.ts +31 -29
  15. package/.framework/cli/commands/status.ts +105 -45
  16. package/.framework/cli/commands/system.ts +47 -42
  17. package/.framework/cli/commands/test.ts +161 -102
  18. package/.framework/cli/commands/upgrade.ts +260 -0
  19. package/.framework/cli/commands/validate-app.ts +517 -161
  20. package/.framework/cli/context.ts +117 -85
  21. package/.framework/cli/env-loader.ts +74 -43
  22. package/.framework/cli/index.ts +70 -46
  23. package/.framework/cli/lib/project-files.ts +404 -0
  24. package/.framework/docs/guides/testing-guide.md +1 -1
  25. package/.framework/docs/runtime-types.md +72 -72
  26. package/.framework/functions/_shared/access.ts +84 -0
  27. package/.framework/functions/_shared/agent-runner.ts +5051 -1026
  28. package/.framework/functions/_shared/app-manifest.ts +214 -94
  29. package/.framework/functions/_shared/ast.ts +214 -0
  30. package/.framework/functions/_shared/audit.ts +91 -55
  31. package/.framework/functions/_shared/cil.ts +368 -127
  32. package/.framework/functions/_shared/db.ts +337 -68
  33. package/.framework/functions/_shared/funnel.ts +228 -0
  34. package/.framework/functions/_shared/geo.ts +105 -0
  35. package/.framework/functions/_shared/hash-api-key.ts +94 -17
  36. package/.framework/functions/_shared/index.ts +22 -224
  37. package/.framework/functions/_shared/kb.ts +832 -58
  38. package/.framework/functions/_shared/llm.ts +118 -18
  39. package/.framework/functions/_shared/logging.ts +317 -19
  40. package/.framework/functions/_shared/middleware.ts +701 -131
  41. package/.framework/functions/_shared/notify.ts +70 -0
  42. package/.framework/functions/_shared/outcomes.ts +34 -19
  43. package/.framework/functions/_shared/permissions.ts +501 -315
  44. package/.framework/functions/_shared/pipeline-runner.ts +704 -155
  45. package/.framework/functions/_shared/principal.ts +436 -263
  46. package/.framework/functions/_shared/rate-limit.ts +233 -0
  47. package/.framework/functions/_shared/resolve-credentials.ts +86 -19
  48. package/.framework/functions/_shared/resolve-ids.ts +203 -139
  49. package/.framework/functions/_shared/retry-deliveries.ts +51 -16
  50. package/.framework/functions/_shared/schema-utils.ts +602 -521
  51. package/.framework/functions/_shared/security.ts +345 -0
  52. package/.framework/functions/_shared/testing.ts +197 -172
  53. package/.framework/functions/_shared/tracing.ts +351 -21
  54. package/.framework/functions/_shared/trigger-engine.ts +425 -136
  55. package/.framework/functions/_shared/webhook-registration.ts +71 -70
  56. package/.framework/functions/_shared/webhook-registry.ts +126 -83
  57. package/.framework/functions/account-nodes.ts +30 -30
  58. package/.framework/functions/adapters/generic-webhook-inbound.ts +49 -16
  59. package/.framework/functions/adapters/generic-webhook-outbound.ts +17 -17
  60. package/.framework/functions/adapters/openai-outbound.ts +15 -16
  61. package/.framework/functions/admin-data.ts +596 -173
  62. package/.framework/functions/admin-funnel.ts +64 -60
  63. package/.framework/functions/admin-install-funnel.ts +84 -66
  64. package/.framework/functions/admin-signals.ts +69 -51
  65. package/.framework/functions/ai-agents.ts +431 -106
  66. package/.framework/functions/ai-interactions.ts +186 -112
  67. package/.framework/functions/api-keys.ts +107 -107
  68. package/.framework/functions/apps.ts +154 -136
  69. package/.framework/functions/auth.ts +83 -65
  70. package/.framework/functions/cil-agent-ingest.ts +189 -232
  71. package/.framework/functions/cil-agent.ts +433 -177
  72. package/.framework/functions/cil-callmap.ts +642 -430
  73. package/.framework/functions/cil-config.ts +139 -135
  74. package/.framework/functions/cil-context.ts +214 -127
  75. package/.framework/functions/cil-doc.ts +587 -458
  76. package/.framework/functions/cil-embeddings.ts +453 -364
  77. package/.framework/functions/cil-graph.ts +427 -142
  78. package/.framework/functions/cil-ingest.ts +1418 -1154
  79. package/.framework/functions/cil-pipeline.ts +276 -21
  80. package/.framework/functions/cil-project.ts +659 -132
  81. package/.framework/functions/cil-validate.ts +198 -164
  82. package/.framework/functions/debug-auth.ts +57 -55
  83. package/.framework/functions/deliveries.ts +80 -21
  84. package/.framework/functions/embeddings.ts +1023 -318
  85. package/.framework/functions/eval-harness.ts +146 -108
  86. package/.framework/functions/integration-outbound.ts +205 -75
  87. package/.framework/functions/integration-routes.ts +128 -23
  88. package/.framework/functions/integrations.ts +125 -100
  89. package/.framework/functions/invites.ts +1095 -460
  90. package/.framework/functions/item-progress.ts +158 -102
  91. package/.framework/functions/kb.ts +34 -10
  92. package/.framework/functions/logs.ts +133 -133
  93. package/.framework/functions/observability.ts +152 -122
  94. package/.framework/functions/pipeline-executions.ts +147 -132
  95. package/.framework/functions/pipelines.ts +134 -119
  96. package/.framework/functions/prompt-configs.ts +96 -80
  97. package/.framework/functions/roles.ts +112 -82
  98. package/.framework/functions/security.ts +450 -0
  99. package/.framework/functions/system-cron.ts +117 -80
  100. package/.framework/functions/system.ts +158 -98
  101. package/.framework/functions/tests.ts +29 -29
  102. package/.framework/functions/timers.ts +94 -95
  103. package/.framework/functions/trigger-scheduler.ts +10 -0
  104. package/.framework/functions/triggers.ts +179 -149
  105. package/.framework/functions/types.ts +137 -125
  106. package/.framework/mcp/index.ts +133 -0
  107. package/.framework/mcp/load-env.ts +60 -0
  108. package/.framework/mcp/shared/app-manifest.ts +275 -0
  109. package/.framework/mcp/shared/db.ts +477 -0
  110. package/.framework/mcp/shared/kb.ts +118 -0
  111. package/.framework/mcp/tools/architect.ts +431 -0
  112. package/.framework/mcp/tools/cil/context.ts +345 -0
  113. package/.framework/mcp/tools/cil/graph.ts +242 -0
  114. package/.framework/mcp/tools/cil/ingest.ts +1217 -0
  115. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +693 -0
  116. package/.framework/mcp/tools/cil/pipeline.ts +767 -0
  117. package/.framework/mcp/tools/cil/project.ts +791 -0
  118. package/.framework/mcp/tools/cil/retrieval.ts +336 -0
  119. package/.framework/mcp/tools/cil/search-core.ts +454 -0
  120. package/.framework/mcp/tools/cil/search.ts +97 -0
  121. package/.framework/mcp/tools/cil/suggest.ts +186 -0
  122. package/.framework/mcp/tools/cil/validate.ts +1369 -0
  123. package/.framework/mcp/tools/kb/delete.ts +139 -0
  124. package/.framework/mcp/tools/kb/index.ts +52 -0
  125. package/.framework/mcp/tools/kb/ingest.ts +84 -0
  126. package/.framework/mcp/tools/kb/list-types.ts +102 -0
  127. package/.framework/mcp/tools/kb/publish.ts +100 -0
  128. package/.framework/mcp/tools/kb/reindex.ts +86 -0
  129. package/.framework/mcp/tools/kb/revoke.ts +85 -0
  130. package/.framework/mcp/tools/kb/search.ts +101 -0
  131. package/.framework/mcp/tools/kb/upsert.ts +125 -0
  132. package/.framework/mcp/tools/observability.ts +510 -0
  133. package/.framework/migrations/001_core_schema.sql +15 -0
  134. package/.framework/migrations/002_core_rls_and_functions.sql +15 -0
  135. package/.framework/migrations/003_core_system_data.sql +16 -1
  136. package/.framework/migrations/004_core_rpc_and_broker.sql +17 -1
  137. package/.framework/migrations/005_core_observability.sql +17 -1
  138. package/.framework/migrations/006_security_hardening_rls.sql +15 -0
  139. package/.framework/migrations/007_kb_article_list.sql +17 -1
  140. package/.framework/migrations/008_items_target.sql +15 -0
  141. package/.framework/migrations/009_roles_slug_unique.sql +15 -0
  142. package/.framework/migrations/010_auth_user_trigger.sql +15 -0
  143. package/.framework/migrations/011_spine_system_account.sql +16 -0
  144. package/.framework/migrations/012_service_role_grants.sql +16 -0
  145. package/.framework/migrations/013_get_account_apps_role_filter.sql +15 -0
  146. package/.framework/migrations/014_apps_required_roles.sql +16 -0
  147. package/.framework/migrations/015_types_kind_slug_unique.sql +15 -0
  148. package/.framework/migrations/016_cil_types.sql +281 -0
  149. package/.framework/migrations/017_cil_stable_ids.sql +37 -0
  150. package/.framework/migrations/018_runtime_tables.sql +129 -0
  151. package/.framework/migrations/019_agent_prompt_columns.sql +56 -0
  152. package/.framework/migrations/020_schema_reconciliation.sql +67 -0
  153. package/.framework/migrations/021_kb_embedding_lifecycle.sql +31 -0
  154. package/.framework/migrations/032_cil_status_migration.sql +73 -0
  155. package/.framework/migrations/033_cil_versioning.sql +73 -0
  156. package/.framework/migrations/034_match_embeddings_knn.sql +115 -0
  157. package/.framework/migrations/035_rate_limits.sql +55 -0
  158. package/.framework/migrations/036_demo_record_flags.sql +72 -0
  159. package/.framework/migrations/037_kb_types_canonical.sql +244 -0
  160. package/.framework/migrations/038_drop_demo_record_flags.sql +38 -0
  161. package/.framework/migrations/042_registration_hierarchy.sql +309 -0
  162. package/.framework/migrations/043_policy_actor_cleanup.sql +39 -0
  163. package/.framework/migrations/044_match_embeddings_filtered_knn.sql +151 -0
  164. package/.framework/migrations/045_security_critical_rls.sql +139 -0
  165. package/.framework/migrations/046_security_missing_rls.sql +79 -0
  166. package/.framework/migrations/047_people_updated_by.sql +27 -0
  167. package/.framework/migrations/048_watcher_last_seen_at.sql +24 -0
  168. package/.framework/migrations/049_security_blocklist.sql +148 -0
  169. package/.framework/migrations/050_notifications.sql +49 -0
  170. package/.framework/migrations/051_message_agent_fields.sql +74 -0
  171. package/.framework/migrations/052_registration_security.sql +421 -0
  172. package/.framework/migrations/053_builtin_action_seeds.sql +66 -0
  173. package/.framework/migrations/054_kb_ingest_lease.sql +157 -0
  174. package/.framework/migrations/055_type_id_drift_reconciliation.sql +33 -0
  175. package/.framework/migrations/056_slug_column_reconciliation.sql +38 -0
  176. package/.framework/migrations/057_pipelines_slug_identity.sql +14 -0
  177. package/.framework/migrations/058_account_data_field_declarations.sql +53 -0
  178. package/.framework/migrations/059_registration_placement_fixes.sql +269 -0
  179. package/.framework/migrations/060_account_person_record_permissions.sql +38 -0
  180. package/.framework/migrations/061_fk_dedup.sql +32 -0
  181. package/.framework/public/spine-logo.svg +10 -3
  182. package/.framework/scripts/app-install-cli.ts +190 -93
  183. package/.framework/scripts/assemble-frontend.sh +127 -44
  184. package/.framework/scripts/assemble-functions.sh +128 -30
  185. package/.framework/scripts/boundary-check.sh +6 -2
  186. package/.framework/scripts/build-manifest.sh +24 -68
  187. package/.framework/scripts/check-core-integrity.sh +15 -76
  188. package/.framework/scripts/checksum-manifest.mts +343 -0
  189. package/.framework/scripts/cil-migrate-markers.ts +403 -0
  190. package/.framework/scripts/fix-cil-markers.mjs +176 -0
  191. package/.framework/scripts/verify-integrity.sh +21 -57
  192. package/.framework/scripts/verify-manifest.mts +305 -0
  193. package/.framework/seeds/types/funnel_signal_use.json +74 -21
  194. package/.framework/seeds/types/spine_instance.json +73 -21
  195. package/.framework/src/App.tsx +118 -72
  196. package/.framework/src/apps/admin/index.tsx +855 -58
  197. package/.framework/src/components/AppWrapper.tsx +43 -45
  198. package/.framework/src/components/CustomAppLoader.tsx +160 -116
  199. package/.framework/src/components/admin/AdminListPage.tsx +46 -44
  200. package/.framework/src/components/admin/AdminSidebar.tsx +57 -47
  201. package/.framework/src/components/admin/AdminStatsCard.tsx +46 -44
  202. package/.framework/src/components/admin/PageInstructions.tsx +98 -2
  203. package/.framework/src/components/admin/SortableTableHeader.tsx +44 -45
  204. package/.framework/src/components/app-shell/GenericAppShell.tsx +64 -65
  205. package/.framework/src/components/app-shell/GenericDetailPage.tsx +44 -47
  206. package/.framework/src/components/app-shell/GenericListPage.tsx +45 -49
  207. package/.framework/src/components/app-sidebar.tsx +42 -45
  208. package/.framework/src/components/auth/ProtectedRoute.tsx +48 -41
  209. package/.framework/src/components/auth/TurnstileWidget.tsx +27 -0
  210. package/.framework/src/components/kb/KBArticleEditor.tsx +231 -22
  211. package/.framework/src/components/kb/KBArticleList.tsx +251 -10
  212. package/.framework/src/components/layout/AppShell.tsx +51 -45
  213. package/.framework/src/components/layout/Header.tsx +42 -43
  214. package/.framework/src/components/layout/Layout.tsx +46 -46
  215. package/.framework/src/components/layout/Sidebar.tsx +196 -96
  216. package/.framework/src/components/runtime/DataDetailHeader.tsx +55 -30
  217. package/.framework/src/components/runtime/DataDetailPage.tsx +31 -269
  218. package/.framework/src/components/runtime/DataFilters.tsx +49 -74
  219. package/.framework/src/components/runtime/DataHeader.tsx +53 -32
  220. package/.framework/src/components/runtime/DataListPage.tsx +51 -155
  221. package/.framework/src/components/runtime/DataStats.tsx +66 -44
  222. package/.framework/src/components/runtime/DataTable.tsx +49 -149
  223. package/.framework/src/components/runtime/SchemaDetailForm.tsx +45 -75
  224. package/.framework/src/components/runtime/index.ts +119 -16
  225. package/.framework/src/components/search-form.tsx +46 -29
  226. package/.framework/src/components/shared/AgentView.tsx +65 -48
  227. package/.framework/src/components/shared/FieldRenderer.tsx +144 -50
  228. package/.framework/src/components/shared/InviteManager.tsx +207 -97
  229. package/.framework/src/components/shared/MemberList.tsx +97 -0
  230. package/.framework/src/components/shared/SchemaFields.tsx +107 -86
  231. package/.framework/src/components/ui/DataTable.tsx +80 -68
  232. package/.framework/src/components/ui/Form.tsx +157 -146
  233. package/.framework/src/components/ui/ItemCard.tsx +74 -67
  234. package/.framework/src/components/ui/ItemListView.tsx +73 -68
  235. package/.framework/src/components/ui/LoadingSpinner.tsx +43 -48
  236. package/.framework/src/components/ui/Modal.tsx +78 -68
  237. package/.framework/src/components/ui/RichTextEditor.tsx +89 -69
  238. package/.framework/src/components/ui/accordion.tsx +85 -86
  239. package/.framework/src/components/ui/alert-dialog.tsx +197 -184
  240. package/.framework/src/components/ui/alert.tsx +105 -94
  241. package/.framework/src/components/ui/aspect-ratio.tsx +57 -43
  242. package/.framework/src/components/ui/avatar.tsx +122 -107
  243. package/.framework/src/components/ui/badge.tsx +60 -55
  244. package/.framework/src/components/ui/breadcrumb.tsx +127 -128
  245. package/.framework/src/components/ui/button-group.tsx +86 -72
  246. package/.framework/src/components/ui/button.tsx +71 -50
  247. package/.framework/src/components/ui/calendar.tsx +72 -57
  248. package/.framework/src/components/ui/card.tsx +136 -129
  249. package/.framework/src/components/ui/carousel.tsx +226 -107
  250. package/.framework/src/components/ui/chart.tsx +252 -147
  251. package/.framework/src/components/ui/checkbox.tsx +43 -44
  252. package/.framework/src/components/ui/collapsible.tsx +86 -71
  253. package/.framework/src/components/ui/combobox.tsx +282 -249
  254. package/.framework/src/components/ui/command.tsx +158 -146
  255. package/.framework/src/components/ui/context-menu.tsx +239 -240
  256. package/.framework/src/components/ui/dialog.tsx +174 -157
  257. package/.framework/src/components/ui/direction.tsx +72 -56
  258. package/.framework/src/components/ui/drawer.tsx +168 -169
  259. package/.framework/src/components/ui/dropdown-menu.tsx +254 -223
  260. package/.framework/src/components/ui/empty.tsx +131 -120
  261. package/.framework/src/components/ui/field.tsx +205 -173
  262. package/.framework/src/components/ui/hover-card.tsx +71 -72
  263. package/.framework/src/components/ui/input-group.tsx +148 -133
  264. package/.framework/src/components/ui/input-otp.tsx +99 -86
  265. package/.framework/src/components/ui/input.tsx +43 -41
  266. package/.framework/src/components/ui/item.tsx +207 -185
  267. package/.framework/src/components/ui/kbd.tsx +57 -56
  268. package/.framework/src/components/ui/label.tsx +45 -41
  269. package/.framework/src/components/ui/menubar.tsx +268 -236
  270. package/.framework/src/components/ui/native-select.tsx +85 -86
  271. package/.framework/src/components/ui/navigation-menu.tsx +166 -156
  272. package/.framework/src/components/ui/pagination.tsx +131 -141
  273. package/.framework/src/components/ui/popover.tsx +125 -128
  274. package/.framework/src/components/ui/progress.tsx +47 -44
  275. package/.framework/src/components/ui/radio-group.tsx +60 -55
  276. package/.framework/src/components/ui/resizable.tsx +85 -72
  277. package/.framework/src/components/ui/scroll-area.tsx +57 -58
  278. package/.framework/src/components/ui/select.tsx +182 -169
  279. package/.framework/src/components/ui/separator.tsx +42 -43
  280. package/.framework/src/components/ui/sheet.tsx +170 -159
  281. package/.framework/src/components/ui/sidebar.tsx +395 -398
  282. package/.framework/src/components/ui/skeleton.tsx +42 -43
  283. package/.framework/src/components/ui/slider.tsx +61 -44
  284. package/.framework/src/components/ui/sonner.tsx +44 -44
  285. package/.framework/src/components/ui/spinner.tsx +47 -42
  286. package/.framework/src/components/ui/switch.tsx +56 -42
  287. package/.framework/src/components/ui/table-primitives.tsx +272 -132
  288. package/.framework/src/components/ui/table.tsx +140 -142
  289. package/.framework/src/components/ui/tabs.tsx +110 -93
  290. package/.framework/src/components/ui/textarea.tsx +43 -44
  291. package/.framework/src/components/ui/toggle-group.tsx +87 -71
  292. package/.framework/src/components/ui/toggle.tsx +56 -56
  293. package/.framework/src/components/ui/tooltip.tsx +99 -85
  294. package/.framework/src/contexts/AppContext.tsx +166 -114
  295. package/.framework/src/contexts/AuthContext.tsx +232 -137
  296. package/.framework/src/hooks/use-mobile.ts +43 -44
  297. package/.framework/src/hooks/useApi.ts +185 -172
  298. package/.framework/src/hooks/useAppPath.ts +31 -29
  299. package/.framework/src/hooks/useApps.ts +77 -42
  300. package/.framework/src/hooks/useEntityList.ts +66 -29
  301. package/.framework/src/hooks/useEntityRecord.ts +76 -32
  302. package/.framework/src/hooks/useForm.ts +62 -30
  303. package/.framework/src/hooks/useListSchema.ts +60 -43
  304. package/.framework/src/hooks/useSchemaRecord.ts +115 -95
  305. package/.framework/src/lib/api.ts +258 -134
  306. package/.framework/src/lib/registration.ts +181 -0
  307. package/.framework/src/lib/supabase.ts +118 -81
  308. package/.framework/src/lib/utils.ts +266 -266
  309. package/.framework/src/main.tsx +43 -44
  310. package/.framework/src/pages/DashboardPage.tsx +44 -43
  311. package/.framework/src/pages/NoAppPage.tsx +50 -0
  312. package/.framework/src/pages/NotFoundPage.tsx +32 -29
  313. package/.framework/src/pages/SetupPage.tsx +34 -33
  314. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +69 -58
  315. package/.framework/src/pages/admin/AIAgentsPage.tsx +43 -40
  316. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +63 -55
  317. package/.framework/src/pages/admin/APIKeysPage.tsx +44 -42
  318. package/.framework/src/pages/admin/AlertsConfigPage.tsx +117 -94
  319. package/.framework/src/pages/admin/AppDetailPage.tsx +48 -42
  320. package/.framework/src/pages/admin/AppsPage.tsx +45 -42
  321. package/.framework/src/pages/admin/DeliveriesPage.tsx +49 -17
  322. package/.framework/src/pages/admin/DesignedPage.tsx +31 -30
  323. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +58 -0
  324. package/.framework/src/pages/admin/EmbeddingsPage.tsx +49 -0
  325. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +31 -29
  326. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +46 -29
  327. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -29
  328. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +68 -55
  329. package/.framework/src/pages/admin/IntegrationsPage.tsx +45 -42
  330. package/.framework/src/pages/admin/InvitesPage.tsx +60 -44
  331. package/.framework/src/pages/admin/LogsPage.tsx +87 -137
  332. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +31 -29
  333. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +150 -89
  334. package/.framework/src/pages/admin/PipelineDetailPage.tsx +77 -104
  335. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +44 -43
  336. package/.framework/src/pages/admin/PipelinesPage.tsx +59 -145
  337. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +53 -42
  338. package/.framework/src/pages/admin/PromptConfigsPage.tsx +47 -117
  339. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +31 -44
  340. package/.framework/src/pages/admin/RoleDetailPage.tsx +49 -92
  341. package/.framework/src/pages/admin/RolesPage.tsx +47 -164
  342. package/.framework/src/pages/admin/SecurityPage.tsx +310 -0
  343. package/.framework/src/pages/admin/SelectTestPage.tsx +30 -32
  344. package/.framework/src/pages/admin/ShadcnTestPage.tsx +37 -44
  345. package/.framework/src/pages/admin/SimpleDashboard.tsx +32 -44
  346. package/.framework/src/pages/admin/TestRunDetailPage.tsx +97 -44
  347. package/.framework/src/pages/admin/TestingDashboard.tsx +126 -71
  348. package/.framework/src/pages/admin/TimerDetailPage.tsx +67 -55
  349. package/.framework/src/pages/admin/TimersPage.tsx +44 -42
  350. package/.framework/src/pages/admin/TriggerDetailPage.tsx +47 -42
  351. package/.framework/src/pages/admin/TriggersPage.tsx +58 -57
  352. package/.framework/src/pages/admin/TypeDetailPage.tsx +91 -55
  353. package/.framework/src/pages/admin/TypesPage.tsx +45 -42
  354. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +258 -128
  355. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +127 -123
  356. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +308 -235
  357. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +159 -110
  358. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +432 -0
  359. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +200 -178
  360. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +286 -239
  361. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +211 -90
  362. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +156 -143
  363. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +379 -0
  364. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +337 -0
  365. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +219 -214
  366. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  367. package/.framework/src/pages/auth/AuthConfirmPage.tsx +135 -65
  368. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +39 -31
  369. package/.framework/src/pages/auth/LoginPage.tsx +38 -32
  370. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -28
  371. package/.framework/src/pages/auth/RegisterPage.tsx +429 -155
  372. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -30
  373. package/.framework/src/pages/spine-framework/CLIPage.tsx +32 -30
  374. package/.framework/src/types/auth.ts +101 -93
  375. package/.framework/src/types/types.ts +283 -262
  376. package/.framework/templates/project.json +87 -0
  377. package/.framework/templates/spine.config.neutral.json +7 -0
  378. package/.framework/tests/api/api-surface.test.ts +381 -0
  379. package/.framework/tests/fixtures/seed.ts +427 -0
  380. package/.framework/tests/fixtures/teardown.ts +228 -0
  381. package/.framework/tests/reporter.ts +351 -0
  382. package/.framework/tests/setup.ts +135 -0
  383. package/.framework/tests/ui/ui-sweep.ts +278 -0
  384. package/.framework/tests/unit/admin-data-logger.test.ts +310 -0
  385. package/.framework/tests/unit/agent-runner.test.ts +2389 -0
  386. package/.framework/tests/unit/ai-interactions.test.ts +81 -0
  387. package/.framework/tests/unit/app-manifest.test.ts +359 -0
  388. package/.framework/tests/unit/assembly-scripts.test.ts +172 -0
  389. package/.framework/tests/unit/cil-determinism.test.ts +85 -0
  390. package/.framework/tests/unit/core-isolation.test.ts +153 -0
  391. package/.framework/tests/unit/funnel-signals.test.ts +135 -0
  392. package/.framework/tests/unit/install-app.test.ts +1759 -0
  393. package/.framework/tests/unit/invites-security.test.ts +572 -0
  394. package/.framework/tests/unit/kb-ingest-lease.test.ts +287 -0
  395. package/.framework/tests/unit/kb-search.test.ts +119 -0
  396. package/.framework/tests/unit/migrate.test.ts +509 -0
  397. package/.framework/tests/unit/notify.test.ts +136 -0
  398. package/.framework/tests/unit/permissions.test.ts +269 -0
  399. package/.framework/tests/unit/pipeline-runner.test.ts +755 -0
  400. package/.framework/tests/unit/principal-cache.test.ts +138 -0
  401. package/.framework/tests/unit/retrieval-golden.test.ts +520 -0
  402. package/.framework/tests/unit/runtime-isolation.test.ts +642 -0
  403. package/.framework/tests/unit/schema-utils.test.ts +225 -0
  404. package/.framework/tests/unit/trigger-engine.test.ts +513 -0
  405. package/.framework/tests/unit/validate-app.test.ts +527 -0
  406. package/README.md +34 -3
  407. package/bin/spine-framework.cjs +1 -0
  408. package/config/tailwind.config.cjs +7 -0
  409. package/config/tsconfig.functions.json +42 -0
  410. package/config/tsconfig.json +37 -9
  411. package/config/vite.config.ts +31 -2
  412. package/netlify.toml +31 -3
  413. package/package.json +46 -29
  414. package/scripts/assemble-frontend.sh +127 -44
  415. package/scripts/assemble-functions.sh +127 -29
  416. package/scripts/boundary-check.sh +6 -2
  417. package/scripts/build-manifest.sh +24 -68
  418. package/scripts/checksum-manifest.mts +345 -0
  419. package/scripts/dev-clean.sh +86 -0
  420. package/scripts/dev-netlify.sh +49 -0
  421. package/scripts/netlify-dev-wrapper.sh +2 -0
  422. package/scripts/verify-install.ts +358 -0
  423. package/scripts/verify-integrity.sh +21 -57
  424. package/scripts/verify-manifest.mts +305 -0
  425. package/templates/project/gitignore +45 -0
  426. package/{config → templates/project}/package-lock.json +22335 -9366
  427. package/vitest.config.ts +46 -0
  428. package/.framework/public/spine.config.json +0 -91
  429. package/config/deno.lock +0 -108
  430. package/config/tsconfig.node.tsbuildinfo +0 -1
  431. package/config/tsconfig.tsbuildinfo +0 -1
  432. package/config/vite.config.d.ts +0 -2
  433. package/custom/.devin/AGENT.md +0 -357
  434. package/custom/.devin/skills/install-app/SKILL.md +0 -39
  435. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +0 -39
  436. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +0 -36
  437. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +0 -46
  438. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +0 -28
  439. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +0 -38
  440. package/custom/.devin/workflows/app-install.yaml +0 -83
  441. package/custom/README.md +0 -79
  442. package/docs/README.md +0 -15
  443. package/scripts/app-install-cli.ts +0 -286
  444. package/scripts/check-core-integrity.sh +0 -82
  445. package/scripts/ingest-chunks.cjs +0 -202
  446. package/scripts/kb-chunk-parser.cjs +0 -312
  447. package/scripts/kb-chunk-parser.ts +0 -330
  448. package/scripts/kb-ingest-docs.ts +0 -203
  449. package/scripts/load-test-app-install.ts +0 -484
  450. package/scripts/test-login.js +0 -33
  451. package/spine.config.json +0 -91
@@ -0,0 +1,33 @@
1
+ -- 055_type_id_drift_reconciliation.sql
2
+ -- CIL[BEGIN stable_id="~/.framework/migrations/055_type_id_drift_reconciliation.sql:type_id_drift_reconciliation:file" type="file" identity="type_id_drift_reconciliation" parent="~/.framework/migrations"]
3
+ -- CIL[DOC-MACRO purpose="Reconcile archive-era databases with the consolidated baseline's type_id columns/indexes on meta tables"]
4
+ -- CIL[DOC-RESPONSIBILITY responsibility="Add nullable type_id columns and matching indexes to triggers, timers, prompt_configs, integrations, test_runs, ai_agents, and api_keys where absent"]
5
+ -- CIL[DOC-CONTEXT module="migrations" layer="data" related_files=".framework/migrations/001_core_schema.sql"]
6
+ -- CIL[DOC-CONTRACT inputs="tables predating consolidated 001" outputs="type_id columns + idx_*_type_id indexes present" errors="none" side_effects="ADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS only; nullable columns leave existing rows null — matches baseline where type_id is optional on these tables"]
7
+ -- CIL[DOC-MICRO logic_summary="The consolidated baseline declares type_id on meta config tables; archive-era databases never received it, which made replaying 001 fail at CREATE INDEX ... (type_id). Nothing in the runtime writes these columns today — they exist for baseline convergence" notable_decisions="columns added nullable rather than NOT NULL to avoid backfill semantics on tables the baseline leaves optional"]
8
+ -- CIL[DOC-STABILITY stability="stable"]
9
+ -- CIL[END stable_id="~/.framework/migrations/055_type_id_drift_reconciliation.sql:type_id_drift_reconciliation:file"]
10
+ -- Reconciles pre-consolidation databases that were built from the archive-era
11
+ -- migration set (tracked in _spine_migrations under the old filenames) with the
12
+ -- consolidated baseline in 001_core_schema.sql. The baseline declares a nullable
13
+ -- type_id column plus an index on these tables; archive-era databases never
14
+ -- received them, which makes replaying 001 fail on
15
+ -- CREATE INDEX ... (type_id) -> "column type_id does not exist".
16
+ -- All statements are additive and idempotent: no-op on fresh installs where the
17
+ -- consolidated baseline already created the columns and indexes.
18
+
19
+ ALTER TABLE public.triggers ADD COLUMN IF NOT EXISTS type_id uuid;
20
+ ALTER TABLE public.timers ADD COLUMN IF NOT EXISTS type_id uuid;
21
+ ALTER TABLE public.prompt_configs ADD COLUMN IF NOT EXISTS type_id uuid;
22
+ ALTER TABLE public.integrations ADD COLUMN IF NOT EXISTS type_id uuid;
23
+ ALTER TABLE public.test_runs ADD COLUMN IF NOT EXISTS type_id uuid;
24
+ ALTER TABLE public.ai_agents ADD COLUMN IF NOT EXISTS type_id uuid;
25
+ ALTER TABLE public.api_keys ADD COLUMN IF NOT EXISTS type_id uuid;
26
+
27
+ CREATE INDEX IF NOT EXISTS idx_triggers_type_id ON public.triggers(type_id);
28
+ CREATE INDEX IF NOT EXISTS idx_timers_type_id ON public.timers(type_id);
29
+ CREATE INDEX IF NOT EXISTS idx_prompt_configs_type_id ON public.prompt_configs(type_id);
30
+ CREATE INDEX IF NOT EXISTS idx_integrations_type_id ON public.integrations(type_id);
31
+ CREATE INDEX IF NOT EXISTS idx_test_runs_type_id ON public.test_runs(type_id);
32
+ CREATE INDEX IF NOT EXISTS idx_ai_agents_type_id ON public.ai_agents(type_id);
33
+ CREATE INDEX IF NOT EXISTS idx_api_keys_type_id ON public.api_keys(type_id);
@@ -0,0 +1,38 @@
1
+ -- 056_slug_column_reconciliation.sql
2
+ -- CIL[BEGIN stable_id="~/.framework/migrations/056_slug_column_reconciliation.sql:slug_column_reconciliation:file" type="file" identity="slug_column_reconciliation" parent="~/.framework/migrations"]
3
+ -- CIL[DOC-MACRO purpose="Add baseline slug identity columns to archive-era config tables so manifest seed upserts resolve"]
4
+ -- CIL[DOC-RESPONSIBILITY responsibility="Add slug to triggers/timers/test_runs/ai_agents/item_progress, add missing app_id to test_runs, backfill slug from name, and create UNIQUE(app_id, slug) indexes"]
5
+ -- CIL[DOC-CONTEXT module="migrations" layer="data" related_files=".framework/migrations/001_core_schema.sql,.framework/cli/commands/install-app.ts"]
6
+ -- CIL[DOC-CONTRACT inputs="archive-era table shapes" outputs="slug columns present, backfilled, uniquely indexed per app" errors="unique-index creation fails if name-derived slugs collide per app (checked: 17 triggers + 13 agents produce no collisions)" side_effects="Existing rows gain deterministic slug = lower(name) with non-alphanumerics folded to underscores — matches demo seed slugs so upserts update in place instead of duplicating"]
7
+ -- CIL[DOC-MICRO logic_summary="Demo ai_agents seeds upsert on slug; live rows existed name-only from pre-E1 seeding — regex backfill makes 'Demo Triage Agent' → 'demo_triage_agent' exactly matching the seed. slug left nullable (unlike baseline NOT NULL) to avoid breaking legacy inserts that omit it; uniqueness enforced by index" notable_decisions="test_runs also lacked app_id — added nullable since the identity index requires it"]
8
+ -- CIL[DOC-STABILITY stability="stable"]
9
+ -- CIL[END stable_id="~/.framework/migrations/056_slug_column_reconciliation.sql:slug_column_reconciliation:file"]
10
+ -- Adds the consolidated-baseline `slug` identity column to config tables that
11
+ -- predate it on archive-era databases, backfills existing rows from `name`,
12
+ -- and adds the baseline UNIQUE(app_id, slug) index so seed upserts resolve.
13
+ -- All statements are additive and idempotent — no-op on fresh installs where
14
+ -- 001_core_schema.sql already created the columns.
15
+
16
+ ALTER TABLE public.triggers ADD COLUMN IF NOT EXISTS slug text;
17
+ ALTER TABLE public.timers ADD COLUMN IF NOT EXISTS slug text;
18
+ ALTER TABLE public.test_runs ADD COLUMN IF NOT EXISTS slug text;
19
+ ALTER TABLE public.ai_agents ADD COLUMN IF NOT EXISTS slug text;
20
+ ALTER TABLE public.item_progress ADD COLUMN IF NOT EXISTS slug text;
21
+
22
+ -- Archive-era test_runs also predates app_id, which the (app_id, slug)
23
+ -- identity index requires.
24
+ ALTER TABLE public.test_runs ADD COLUMN IF NOT EXISTS app_id uuid;
25
+
26
+ UPDATE public.triggers
27
+ SET slug = regexp_replace(lower(name), '[^a-z0-9]+', '_', 'g')
28
+ WHERE slug IS NULL;
29
+
30
+ UPDATE public.ai_agents
31
+ SET slug = regexp_replace(lower(name), '[^a-z0-9]+', '_', 'g')
32
+ WHERE slug IS NULL;
33
+
34
+ CREATE UNIQUE INDEX IF NOT EXISTS triggers_app_slug_key ON public.triggers(app_id, slug);
35
+ CREATE UNIQUE INDEX IF NOT EXISTS timers_app_slug_key ON public.timers(app_id, slug);
36
+ CREATE UNIQUE INDEX IF NOT EXISTS test_runs_app_slug_key ON public.test_runs(app_id, slug);
37
+ CREATE UNIQUE INDEX IF NOT EXISTS ai_agents_app_slug_key ON public.ai_agents(app_id, slug);
38
+ CREATE UNIQUE INDEX IF NOT EXISTS item_progress_app_slug_key ON public.item_progress(app_id, slug);
@@ -0,0 +1,14 @@
1
+ -- 057_pipelines_slug_identity.sql
2
+ -- CIL[BEGIN stable_id="~/.framework/migrations/057_pipelines_slug_identity.sql:pipelines_slug_identity:file" type="file" identity="pipelines_slug_identity" parent="~/.framework/migrations"]
3
+ -- CIL[DOC-MACRO purpose="Add the baseline UNIQUE(app_id, slug) identity to pipelines on archive-era databases"]
4
+ -- CIL[DOC-RESPONSIBILITY responsibility="Create pipelines_app_slug_key unique index so manifest seed upserts with conflict 'app_id,slug' resolve"]
5
+ -- CIL[DOC-CONTEXT module="migrations" layer="data" related_files=".framework/migrations/001_core_schema.sql,.framework/migrations/056_slug_column_reconciliation.sql"]
6
+ -- CIL[DOC-CONTRACT inputs="pipelines table with app_id + slug columns (both already present)" outputs="unique index exists" errors="fails if duplicate (app_id,slug) pairs exist (verified none: 5 rows, 5 unique)" side_effects="none — index only"]
7
+ -- CIL[DOC-STABILITY stability="stable"]
8
+ -- CIL[END stable_id="~/.framework/migrations/057_pipelines_slug_identity.sql:pipelines_slug_identity:file"]
9
+ -- Archive-era databases lack the baseline UNIQUE(app_id, slug) identity on
10
+ -- pipelines, so manifest seed upserts with conflict "app_id,slug" fail with
11
+ -- "no unique or exclusion constraint matching the ON CONFLICT specification".
12
+ -- Idempotent — no-op where the constraint already exists.
13
+
14
+ CREATE UNIQUE INDEX IF NOT EXISTS pipelines_app_slug_key ON public.pipelines(app_id, slug);
@@ -0,0 +1,53 @@
1
+ -- 058_account_data_field_declarations.sql
2
+ -- CIL[BEGIN stable_id="~/.framework/migrations/058_account_data_field_declarations.sql:account_data_field_declarations:file" type="file" identity="account_data_field_declarations" parent="~/.framework/migrations"]
3
+ -- CIL[DOC-MACRO purpose="Declare the account.data extension fields that runtime code already writes"]
4
+ -- CIL[DOC-RESPONSIBILITY responsibility="Merge is_demo, demo_provisioned_at, geo_country, rate_limits, lifecycle_stage, segment, temperature into the account type design_schema.fields and re-stamp existing accounts"]
5
+ -- CIL[DOC-CONTEXT module="migrations" layer="data" related_files=".framework/functions/admin-data.ts,.framework/functions/_shared/permissions.ts,custom/apps/demo/functions/custom_demo-journey.ts,.framework/migrations/037_kb_types_canonical.sql"]
6
+ -- CIL[DOC-CONTRACT inputs="types row kind='account' slug='account'; accounts rows with stamped design_schema" outputs="declared fields present on type schema and every account stamp" errors="none — merges are idempotent" side_effects="member/member_admin can now adminDataUpdate these account.data keys"]
7
+ -- CIL[DOC-MICRO logic_summary="admin-data field gate requires every key in an update's data object to be declared in the record's stamped design_schema.fields; these keys were historically written via raw adminDb so they were never declared. jsonb merge adds declarations without touching existing fields or record_permissions — declared fields inherit the record-level update grant." notable_decisions="no field-level permissions — member/member_admin already hold record-level update; no validation_schema regeneration needed (baseline is '{}' and sanitization is optional per-field); legacy demo_run_id/expires_at/demo_expired/expired_at deliberately NOT re-declared — provisioning scrubs them"]
8
+ -- CIL[DOC-STABILITY stability="stable"]
9
+ -- CIL[END stable_id="~/.framework/migrations/058_account_data_field_declarations.sql:account_data_field_declarations:file"]
10
+ -- Account data fields historically written through raw adminDb were never
11
+ -- declared on the account type. admin-data's field gate (permissions.ts)
12
+ -- denies writes to undeclared fields, so provisioning a demo workspace as
13
+ -- member_admin 403s on accounts.data.is_demo. Declaring the fields is the
14
+ -- schema-correct fix: record_permissions already grant member/member_admin
15
+ -- update, and declared fields inherit it. Follows the 037 jsonb merge pattern.
16
+
17
+ -- 1. Declare the fields on the account type (covers accounts created later,
18
+ -- since account creation stamps the type's design_schema).
19
+ UPDATE public.types
20
+ SET design_schema = jsonb_set(
21
+ design_schema,
22
+ '{fields}',
23
+ COALESCE(design_schema->'fields', '{}'::jsonb) || $F${
24
+ "is_demo": { "data_type": "boolean", "label": "Demo Account", "required": false },
25
+ "demo_provisioned_at": { "data_type": "datetime", "label": "Demo Provisioned At", "required": false },
26
+ "geo_country": { "data_type": "text", "label": "Geo Country", "required": false },
27
+ "rate_limits": { "data_type": "json", "label": "Rate Limits", "required": false },
28
+ "lifecycle_stage": { "data_type": "text", "label": "Lifecycle Stage", "required": false },
29
+ "segment": { "data_type": "text", "label": "Segment", "required": false },
30
+ "temperature": { "data_type": "text", "label": "Temperature", "required": false }
31
+ }$F$::jsonb,
32
+ true
33
+ )
34
+ WHERE kind = 'account' AND slug = 'account';
35
+
36
+ -- 2. Re-stamp existing accounts — the field gate reads the schema stamped on
37
+ -- the record at creation, so the types update alone only helps new rows.
38
+ UPDATE public.accounts
39
+ SET design_schema = jsonb_set(
40
+ design_schema,
41
+ '{fields}',
42
+ COALESCE(design_schema->'fields', '{}'::jsonb) || $F${
43
+ "is_demo": { "data_type": "boolean", "label": "Demo Account", "required": false },
44
+ "demo_provisioned_at": { "data_type": "datetime", "label": "Demo Provisioned At", "required": false },
45
+ "geo_country": { "data_type": "text", "label": "Geo Country", "required": false },
46
+ "rate_limits": { "data_type": "json", "label": "Rate Limits", "required": false },
47
+ "lifecycle_stage": { "data_type": "text", "label": "Lifecycle Stage", "required": false },
48
+ "segment": { "data_type": "text", "label": "Segment", "required": false },
49
+ "temperature": { "data_type": "text", "label": "Temperature", "required": false }
50
+ }$F$::jsonb,
51
+ true
52
+ )
53
+ WHERE design_schema IS NOT NULL;
@@ -0,0 +1,269 @@
1
+ -- Migration 059: complete_registration_placement — stamp account schema + fix slug
2
+ -- CIL[DOC-RESPONSIBILITY responsibility="Fix two defects in complete_registration_placement's new_account path discovered by fresh standalone install: (1) the accounts INSERT never stamped design_schema/validation_schema, so every registered account hit admin-data's 'No permissions defined on this record type' gate; (2) slug generation ran regexp_replace before lower(), stripping capitals ('Test Company' -> 'est-ompany-*')"]
3
+ -- CIL[DOC-MICRO logic_summary="Recreates the RPC identical to 052 except: the new_account branch loads the ACCOUNT type's design/validation schemas alongside account type_id and stamps them into the new accounts row; slug base lowercases the name BEFORE the non-alphanumeric replace, matching generateAccountSlug. Repair of already-broken account stamps is handled by 060's blanket restamp." notable_decisions="CREATE OR REPLACE preserves signature and grants; dev DB never showed this because its accounts predate 052 (older creation paths stamped schemas)"]
4
+ -- ============================================
5
+
6
+ CREATE OR REPLACE FUNCTION public.complete_registration_placement(
7
+ p_person_id uuid,
8
+ p_app_slug text,
9
+ p_path text,
10
+ p_account_name text
11
+ )
12
+ RETURNS jsonb
13
+ LANGUAGE plpgsql
14
+ SECURITY DEFINER
15
+ SET search_path = public
16
+ AS $$
17
+ DECLARE
18
+ v_person public.people%ROWTYPE;
19
+ v_app_reg jsonb;
20
+ v_role_slug text;
21
+ v_role public.roles%ROWTYPE;
22
+ v_account_id uuid;
23
+ v_account_slug text;
24
+ v_account_type_id uuid;
25
+ v_account_design_schema jsonb;
26
+ v_account_validation_schema jsonb;
27
+ v_parent_id uuid;
28
+ v_type_id uuid;
29
+ v_design_schema jsonb;
30
+ v_validation_schema jsonb;
31
+ v_base text;
32
+ v_domain text;
33
+ v_status text;
34
+ v_target record;
35
+ v_personal_domains text[] := ARRAY[
36
+ 'gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'icloud.com',
37
+ 'aol.com', 'protonmail.com', 'me.com', 'mac.com', 'live.com',
38
+ 'msn.com', 'ymail.com', 'googlemail.com', 'fastmail.com'
39
+ ];
40
+ BEGIN
41
+ IF p_person_id IS NULL OR p_app_slug IS NULL OR p_path IS NULL THEN
42
+ RAISE EXCEPTION 'person, app, and path are required';
43
+ END IF;
44
+
45
+ -- Per-person lock serializes concurrent/duplicate placement calls.
46
+ PERFORM pg_advisory_xact_lock(hashtext('spine.placement.' || p_person_id::text));
47
+
48
+ SELECT * INTO v_person FROM public.people WHERE id = p_person_id FOR UPDATE;
49
+ IF NOT FOUND THEN
50
+ RAISE EXCEPTION 'Person not found';
51
+ END IF;
52
+
53
+ -- Idempotent: a retry after a lost response returns the placement that
54
+ -- already committed instead of creating a second account or failing.
55
+ IF v_person.account_id IS NOT NULL AND v_person.role_id IS NOT NULL THEN
56
+ RETURN (
57
+ SELECT jsonb_build_object(
58
+ 'status', v_person.status,
59
+ 'account_slug', a.slug,
60
+ 'role_slug', r.slug
61
+ )
62
+ FROM public.accounts a, public.roles r
63
+ WHERE a.id = v_person.account_id AND r.id = v_person.role_id
64
+ );
65
+ END IF;
66
+
67
+ -- Resolve the installed registration policy server-side.
68
+ SELECT config->'registration' INTO v_app_reg
69
+ FROM public.apps
70
+ WHERE slug = p_app_slug AND is_active = true
71
+ LIMIT 1;
72
+
73
+ IF v_app_reg IS NULL THEN
74
+ RAISE EXCEPTION 'Unknown or uninstalled app ''%'' or no registration policy configured', p_app_slug;
75
+ END IF;
76
+ IF COALESCE((v_app_reg->>'enabled')::boolean, false) = false THEN
77
+ RAISE EXCEPTION 'Registration is not enabled for app ''%''', p_app_slug;
78
+ END IF;
79
+
80
+ -- Role comes from the app's installed config — NEVER the request.
81
+ -- join_request defaults to 'member' when the app permits it.
82
+ v_role_slug := CASE
83
+ WHEN p_path = 'join_request' AND (v_app_reg->'roles' ? 'member') THEN 'member'
84
+ ELSE v_app_reg->>'default_role'
85
+ END;
86
+
87
+ IF v_role_slug IS NULL THEN
88
+ RAISE EXCEPTION 'App ''%'' registration has no default_role configured', p_app_slug;
89
+ END IF;
90
+ IF v_app_reg->'roles' IS NOT NULL AND NOT (v_app_reg->'roles' ? v_role_slug) THEN
91
+ RAISE EXCEPTION 'Role ''%'' is not permitted in this context', v_role_slug;
92
+ END IF;
93
+ IF v_role_slug = 'system_admin' THEN
94
+ RAISE EXCEPTION 'Role ''system_admin'' cannot be assigned through registration';
95
+ END IF;
96
+
97
+ SELECT * INTO v_role FROM public.roles WHERE slug = v_role_slug LIMIT 1;
98
+ IF NOT FOUND THEN
99
+ RAISE EXCEPTION 'Role ''%'' not found', v_role_slug;
100
+ END IF;
101
+ IF v_role.is_system = true THEN
102
+ RAISE EXCEPTION 'Role ''%'' is not permitted in this context', v_role_slug;
103
+ END IF;
104
+
105
+ -- Post-lock precondition: another writer already assigned a role. Return
106
+ -- the existing placement rather than overwrite (lost-response resume).
107
+ IF v_person.role_id IS NOT NULL THEN
108
+ RETURN (
109
+ SELECT jsonb_build_object(
110
+ 'status', v_person.status,
111
+ 'account_slug', a.slug,
112
+ 'role_slug', r.slug
113
+ )
114
+ FROM public.roles r
115
+ LEFT JOIN public.accounts a ON a.id = v_person.account_id
116
+ WHERE r.id = v_person.role_id
117
+ );
118
+ END IF;
119
+
120
+ SELECT id, design_schema, validation_schema
121
+ INTO v_type_id, v_design_schema, v_validation_schema
122
+ FROM public.types WHERE kind = 'person' AND slug = 'person' LIMIT 1;
123
+ IF v_type_id IS NULL THEN
124
+ RAISE EXCEPTION 'Person type (kind=person, slug=person) not found — run framework type seeds';
125
+ END IF;
126
+
127
+ IF p_path = 'new_account' THEN
128
+ IF p_account_name IS NULL OR btrim(p_account_name) = '' THEN
129
+ RAISE EXCEPTION 'account_name is required for new_account path';
130
+ END IF;
131
+
132
+ v_parent_id := NULL;
133
+ IF v_app_reg->>'parent_account' IS NOT NULL THEN
134
+ SELECT id INTO v_parent_id FROM public.accounts
135
+ WHERE slug = v_app_reg->>'parent_account' LIMIT 1;
136
+ IF v_parent_id IS NULL THEN
137
+ RAISE EXCEPTION 'Parent account ''%'' not found', v_app_reg->>'parent_account';
138
+ END IF;
139
+ END IF;
140
+
141
+ SELECT id, design_schema, validation_schema
142
+ INTO v_account_type_id, v_account_design_schema, v_account_validation_schema
143
+ FROM public.types
144
+ WHERE kind = 'account' AND slug = 'account' LIMIT 1;
145
+ IF v_account_type_id IS NULL THEN
146
+ RAISE EXCEPTION 'Account type (kind=account, slug=account) not found — run framework type seeds';
147
+ END IF;
148
+
149
+ -- Slug pattern matches generateAccountSlug: lower FIRST so capitals are
150
+ -- preserved, non-alphanumeric runs -> '-', trimmed, 40-char base,
151
+ -- '-xxxxxx' random suffix, retried on collision until unique.
152
+ v_base := left(btrim(regexp_replace(lower(p_account_name), '[^a-z0-9]+', '-', 'g'), '-'), 40);
153
+ IF v_base = '' THEN v_base := 'account'; END IF;
154
+ LOOP
155
+ v_account_slug := v_base || '-' || substr(md5(random()::text), 1, 6);
156
+ EXIT WHEN NOT EXISTS (SELECT 1 FROM public.accounts WHERE slug = v_account_slug);
157
+ END LOOP;
158
+
159
+ INSERT INTO public.accounts (
160
+ slug, display_name, type_id, parent_id, is_active,
161
+ design_schema, validation_schema
162
+ )
163
+ VALUES (
164
+ v_account_slug, btrim(p_account_name), v_account_type_id, v_parent_id, true,
165
+ v_account_design_schema, COALESCE(v_account_validation_schema, '{}'::jsonb)
166
+ )
167
+ RETURNING id INTO v_account_id;
168
+
169
+ v_status := 'active';
170
+
171
+ UPDATE public.people SET
172
+ account_id = v_account_id,
173
+ role_id = v_role.id,
174
+ status = 'active',
175
+ type_id = v_type_id,
176
+ design_schema = v_design_schema,
177
+ validation_schema = COALESCE(v_validation_schema, '{}'::jsonb),
178
+ updated_at = now()
179
+ WHERE id = v_person.id
180
+ AND role_id IS NULL;
181
+
182
+ ELSIF p_path = 'existing_account' THEN
183
+ IF COALESCE(v_app_reg->>'account_strategy', '') <> 'existing' THEN
184
+ RAISE EXCEPTION 'App ''%'' does not support joining an existing account', p_app_slug;
185
+ END IF;
186
+ IF v_app_reg->>'target_account' IS NULL THEN
187
+ RAISE EXCEPTION 'No target_account configured for this app registration';
188
+ END IF;
189
+
190
+ SELECT id, slug INTO v_target FROM public.accounts
191
+ WHERE slug = v_app_reg->>'target_account' LIMIT 1;
192
+ IF NOT FOUND THEN
193
+ RAISE EXCEPTION 'Target account ''%'' not found', v_app_reg->>'target_account';
194
+ END IF;
195
+ v_account_id := v_target.id;
196
+ v_account_slug := v_target.slug;
197
+ v_status := 'active';
198
+
199
+ UPDATE public.people SET
200
+ account_id = v_account_id,
201
+ role_id = v_role.id,
202
+ status = 'active',
203
+ type_id = v_type_id,
204
+ design_schema = v_design_schema,
205
+ validation_schema = COALESCE(v_validation_schema, '{}'::jsonb),
206
+ data = '{}'::jsonb,
207
+ updated_at = now()
208
+ WHERE id = v_person.id
209
+ AND role_id IS NULL;
210
+
211
+ ELSIF p_path = 'join_request' THEN
212
+ -- Opt-in only: apps must declare allow_domain_join. The target account
213
+ -- is derived server-side from the person's email domain — the client
214
+ -- never supplies target_account_id.
215
+ IF COALESCE((v_app_reg->>'allow_domain_join')::boolean, false) = false THEN
216
+ RAISE EXCEPTION 'App ''%'' does not allow domain-based join requests', p_app_slug;
217
+ END IF;
218
+
219
+ v_domain := lower(split_part(v_person.email, '@', 2));
220
+ IF v_domain IS NULL OR v_domain = '' OR v_domain = ANY(v_personal_domains) THEN
221
+ RAISE EXCEPTION 'No account found for this email domain';
222
+ END IF;
223
+
224
+ SELECT p2.account_id INTO v_account_id
225
+ FROM public.people p2
226
+ WHERE lower(split_part(p2.email, '@', 2)) = v_domain
227
+ AND p2.status = 'active'
228
+ AND p2.account_id IS NOT NULL
229
+ ORDER BY p2.created_at
230
+ LIMIT 1;
231
+
232
+ IF v_account_id IS NULL THEN
233
+ RAISE EXCEPTION 'No account found for this email domain';
234
+ END IF;
235
+ SELECT slug INTO v_account_slug FROM public.accounts WHERE id = v_account_id;
236
+
237
+ v_status := 'pending';
238
+
239
+ UPDATE public.people SET
240
+ account_id = v_account_id,
241
+ role_id = v_role.id,
242
+ status = 'pending',
243
+ type_id = v_type_id,
244
+ design_schema = v_design_schema,
245
+ validation_schema = COALESCE(v_validation_schema, '{}'::jsonb),
246
+ data = COALESCE(data, '{}'::jsonb) || jsonb_build_object(
247
+ 'invite_kind', 'access_request',
248
+ 'invite_invited_by', null,
249
+ 'invite_token', null),
250
+ updated_at = now()
251
+ WHERE id = v_person.id
252
+ AND role_id IS NULL;
253
+
254
+ ELSE
255
+ RAISE EXCEPTION 'Unknown registration path: %', p_path;
256
+ END IF;
257
+
258
+ RETURN jsonb_build_object(
259
+ 'status', v_status,
260
+ 'account_slug', v_account_slug,
261
+ 'role_slug', v_role.slug
262
+ );
263
+ END;
264
+ $$;
265
+
266
+ REVOKE ALL ON FUNCTION public.complete_registration_placement(uuid, text, text, text) FROM PUBLIC;
267
+ REVOKE ALL ON FUNCTION public.complete_registration_placement(uuid, text, text, text) FROM anon;
268
+ REVOKE ALL ON FUNCTION public.complete_registration_placement(uuid, text, text, text) FROM authenticated;
269
+ GRANT EXECUTE ON FUNCTION public.complete_registration_placement(uuid, text, text, text) TO service_role;
@@ -0,0 +1,38 @@
1
+ -- Migration 060: account + person type record_permissions convergence
2
+ -- CIL[DOC-RESPONSIBILITY responsibility="Grant member/member_admin record-level CRU on the account and person types. The 003 seed grants system_admin only; member_admin provisioning (account data writes via admin-data) and team management (people record updates) fail the record-level gate without these grants. Also restamps every accounts/people row so stamped schemas carry the new grants."]
3
+ -- CIL[DOC-MICRO logic_summary="jsonb_set replaces design_schema.record_permissions on the two type rows, then blanket restamps accounts and people from their type rows — covering records stamped with the old schema AND accounts left unstamped (design_schema='{}') by the pre-059 placement RPC." notable_decisions="Grant set = least privilege proven by the standalone demo install (system_admin CRUD, member_admin/member CRU) — deliberately narrower than the dev DB's drifted superset (support/support_admin/engineering_admin/sales/csm/tam); widen with a follow-up migration if another app declares a need"]
4
+ -- ============================================
5
+
6
+ UPDATE public.types
7
+ SET design_schema = jsonb_set(
8
+ design_schema,
9
+ '{record_permissions}',
10
+ '{
11
+ "system_admin": ["create","read","update","delete"],
12
+ "member_admin": ["create","read","update"],
13
+ "member": ["create","read","update"]
14
+ }'::jsonb
15
+ )
16
+ WHERE (kind = 'account' AND slug = 'account')
17
+ OR (kind = 'person' AND slug = 'person');
18
+
19
+ -- Restamp all account rows with the updated type schema (also repairs
20
+ -- accounts stamped '{}' by the pre-059 registration RPC).
21
+ UPDATE public.accounts a
22
+ SET type_id = t.id,
23
+ design_schema = t.design_schema,
24
+ validation_schema = COALESCE(t.validation_schema, '{}'::jsonb),
25
+ updated_at = now()
26
+ FROM public.types t
27
+ WHERE t.kind = 'account' AND t.slug = 'account'
28
+ AND (a.type_id = t.id OR a.type_id IS NULL);
29
+
30
+ -- Restamp all people rows with the updated type schema.
31
+ UPDATE public.people p
32
+ SET type_id = t.id,
33
+ design_schema = t.design_schema,
34
+ validation_schema = COALESCE(t.validation_schema, '{}'::jsonb),
35
+ updated_at = now()
36
+ FROM public.types t
37
+ WHERE t.kind = 'person' AND t.slug = 'person'
38
+ AND (p.type_id = t.id OR p.type_id IS NULL);
@@ -0,0 +1,32 @@
1
+ -- Migration 061: drop duplicate and non-canonical foreign keys
2
+ -- CIL[DOC-RESPONSIBILITY responsibility="Remove 26 auto-named *_fkey foreign key constraints that shadow identically-scoped fk_* named constraints (001's inline REFERENCES clauses created them; 002/004/018 re-added the same FKs under fk_* names with DROP CONSTRAINT IF EXISTS fk_* which never matched the auto names). Also drop both roles.created_by->people FKs so people<->roles has a single relationship path."]
3
+ -- CIL[DOC-MICRO logic_summary="PostgREST counts every FK between two tables as a candidate relationship for embeds — the duplicates made every unhinted select on these pairs ambiguous ('more than one relationship was found'). Named fk_* constraints are kept as canonical; roles loses BOTH created_by FKs (dev DB has neither; a roles->people path would keep people<->roles ambiguous for role embeds). Legitimate multi-column pairs (items.created_by/updated_by, apps.owner_account_id/account_id) are preserved — queries on those still require !fkey hints." notable_decisions="IF EXISTS everywhere makes this a no-op on schemas that lack any listed constraint; verified against a fresh migration replay on a standalone-export database"]
4
+ -- ============================================
5
+
6
+ ALTER TABLE public.accounts DROP CONSTRAINT IF EXISTS accounts_app_id_fkey;
7
+ ALTER TABLE public.accounts DROP CONSTRAINT IF EXISTS accounts_type_id_fkey;
8
+ ALTER TABLE public.ai_agents DROP CONSTRAINT IF EXISTS ai_agents_created_by_fkey;
9
+ ALTER TABLE public.api_keys DROP CONSTRAINT IF EXISTS api_keys_created_by_fkey;
10
+ ALTER TABLE public.apps DROP CONSTRAINT IF EXISTS apps_account_id_fkey;
11
+ ALTER TABLE public.apps DROP CONSTRAINT IF EXISTS apps_owner_account_id_fkey;
12
+ ALTER TABLE public.integrations DROP CONSTRAINT IF EXISTS integrations_created_by_fkey;
13
+ ALTER TABLE public.invites DROP CONSTRAINT IF EXISTS invites_created_by_fkey;
14
+ ALTER TABLE public.item_progress DROP CONSTRAINT IF EXISTS item_progress_created_by_fkey;
15
+ ALTER TABLE public.items DROP CONSTRAINT IF EXISTS items_created_by_fkey;
16
+ ALTER TABLE public.items DROP CONSTRAINT IF EXISTS items_updated_by_fkey;
17
+ ALTER TABLE public.logs DROP CONSTRAINT IF EXISTS logs_created_by_fkey;
18
+ ALTER TABLE public.messages DROP CONSTRAINT IF EXISTS messages_account_id_fkey;
19
+ ALTER TABLE public.messages DROP CONSTRAINT IF EXISTS messages_created_by_fkey;
20
+ ALTER TABLE public.observability DROP CONSTRAINT IF EXISTS observability_created_by_fkey;
21
+ ALTER TABLE public.pipeline_executions DROP CONSTRAINT IF EXISTS pipeline_executions_created_by_fkey;
22
+ ALTER TABLE public.pipelines DROP CONSTRAINT IF EXISTS pipelines_created_by_fkey;
23
+ ALTER TABLE public.prompt_configs DROP CONSTRAINT IF EXISTS prompt_configs_created_by_fkey;
24
+ ALTER TABLE public.prompt_configs DROP CONSTRAINT IF EXISTS prompt_configs_updated_by_fkey;
25
+ ALTER TABLE public.roles DROP CONSTRAINT IF EXISTS roles_created_by_fkey;
26
+ ALTER TABLE public.roles DROP CONSTRAINT IF EXISTS fk_roles_created_by;
27
+ ALTER TABLE public.test_runs DROP CONSTRAINT IF EXISTS test_runs_created_by_fkey;
28
+ ALTER TABLE public.threads DROP CONSTRAINT IF EXISTS threads_created_by_fkey;
29
+ ALTER TABLE public.threads DROP CONSTRAINT IF EXISTS threads_updated_by_fkey;
30
+ ALTER TABLE public.timers DROP CONSTRAINT IF EXISTS timers_created_by_fkey;
31
+ ALTER TABLE public.triggers DROP CONSTRAINT IF EXISTS triggers_created_by_fkey;
32
+ ALTER TABLE public.types DROP CONSTRAINT IF EXISTS types_app_id_fkey;
@@ -1,3 +1,10 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
2
- <rect width="32" height="32" rx="5" fill="#1e2a4a"/>
3
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 210" role="img" aria-label="Spine">
2
+ <rect x="10" y="20" width="130" height="40" rx="6" fill="#0071e3"/>
3
+ <rect x="150" y="30" width="40" height="20" rx="3" fill="#0071e3"/>
4
+
5
+ <rect x="0" y="95" width="40" height="20" rx="3" fill="#0071e3"/>
6
+ <rect x="60" y="85" width="130" height="40" rx="6" fill="#0071e3"/>
7
+
8
+ <rect x="10" y="150" width="130" height="40" rx="6" fill="#0071e3"/>
9
+ <rect x="150" y="160" width="40" height="20" rx="3" fill="#0071e3"/>
10
+ </svg>