spine-framework 0.3.144 → 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 +20 -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,358 @@
1
+ #!/usr/bin/env tsx
2
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/scripts/verify-install.ts"]
3
+ // CIL[DOC-MACRO purpose="Read-only post-install contract check for a standalone repository against a target Supabase project"]
4
+ // CIL[DOC-RESPONSIBILITY responsibility="Verify required tables exist, required RPCs are callable, the app row + registration config are present, and every seed-declared row (types, roles, link types, agents, prompt configs, pipelines, triggers) materialized with resolvable pipeline/stage references — plus optional emptiness assertions for fresh-database acceptance"]
5
+ // CIL[DOC-CONTEXT module="export" layer="tooling" related_files="scripts/export-standalone.ts,custom/apps/demo/manifest.json,scripts/check-db-schema.ts"]
6
+ // CIL[DOC-CONTRACT inputs="--app <slug> [--expect-empty-people] [--expect-empty-accounts] [--expect-empty-runtime] [--expected-project-ref <ref>]; .env with SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY" outputs="stdout check report; exit 0 all pass" errors="exit 2 usage/missing env; exit 1 any failed check" side_effects="READ-ONLY: table probes use HEAD selects; RPC probes use invalid argument values so the function signature resolves but execution fails before any write; auth admin listUsers reads only"]
7
+ // CIL[DOC-MICRO logic_summary="RPC existence is probed by passing a malformed value to a typed parameter — PG rejects coercion (22P02) before executing the body, distinguishing 'exists but errors' from PostgREST PGRST202 'function not found'. Seed inventory is derived from the app manifest + seed JSONs, nothing is hardcoded" notable_decisions="no repair mode — this tool never writes; failures are reported for the operator"]
8
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
9
+ // CIL[DOC-STABILITY stability="experimental"]
10
+ import { createClient } from '@supabase/supabase-js'
11
+ import { readFileSync, existsSync, readdirSync } from 'node:fs'
12
+ import { join, resolve, dirname } from 'node:path'
13
+ import { fileURLToPath } from 'node:url'
14
+ // CIL[END stable_id="~/scripts/verify-install.ts:module_header:module_header"]
15
+
16
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:setup:block" type="block" identity="setup" parent="~/scripts/verify-install.ts"]
17
+ // CIL[DOC-MACRO purpose="Env loading, arg parsing, result tracking"]
18
+ function loadEnv(): void {
19
+ const envPath = join(process.cwd(), '.env')
20
+ if (!existsSync(envPath)) return
21
+ for (const line of readFileSync(envPath, 'utf8').split('\n')) {
22
+ const t = line.trim()
23
+ if (!t || t.startsWith('#')) continue
24
+ const eq = t.indexOf('=')
25
+ if (eq === -1) continue
26
+ const key = t.slice(0, eq).trim()
27
+ const val = t.slice(eq + 1).trim()
28
+ if (key && !(key in process.env)) process.env[key] = val
29
+ }
30
+ }
31
+
32
+ const args = process.argv.slice(2)
33
+ const appIdx = args.indexOf('--app')
34
+ const appSlug = appIdx >= 0 ? args[appIdx + 1] : undefined
35
+ const expectEmptyPeople = args.includes('--expect-empty-people')
36
+ const expectEmptyAccounts = args.includes('--expect-empty-accounts')
37
+ const expectEmptyRuntime = args.includes('--expect-empty-runtime')
38
+ const refIdx = args.indexOf('--expected-project-ref')
39
+ const expectedProjectRef = refIdx >= 0 ? args[refIdx + 1] : undefined
40
+ if (!appSlug) {
41
+ console.error('usage: tsx scripts/verify-install.ts --app <slug> [--expect-empty-people] [--expect-empty-accounts] [--expect-empty-runtime] [--expected-project-ref <ref>]')
42
+ process.exit(2)
43
+ }
44
+
45
+ loadEnv()
46
+ const SUPABASE_URL = process.env.SUPABASE_URL
47
+ const SERVICE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY
48
+ if (!SUPABASE_URL || !SERVICE_KEY) {
49
+ console.error('❌ SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are required (.env or environment)')
50
+ process.exit(2)
51
+ }
52
+ const db = createClient(SUPABASE_URL, SERVICE_KEY, { auth: { persistSession: false } })
53
+
54
+ let passed = 0
55
+ const failures: string[] = []
56
+ function check(name: string, ok: boolean, detail = ''): void {
57
+ if (ok) { passed++; console.log(` ✓ ${name}`) }
58
+ else { failures.push(name); console.log(` ✗ ${name}${detail ? ` — ${detail}` : ''}`) }
59
+ }
60
+ // CIL[END stable_id="~/scripts/verify-install.ts:setup:block"]
61
+
62
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:required:block" type="block" identity="required" parent="~/scripts/verify-install.ts"]
63
+ // CIL[DOC-MACRO purpose="Required tables and RPC probes"]
64
+ const REQUIRED_TABLES = [
65
+ 'accounts', 'actions', 'ai_agents', 'apps', 'attachments',
66
+ 'document_leases', 'embeddings', 'invites', 'items', 'link_types',
67
+ 'links', 'messages', 'notifications', 'people', 'pipeline_executions',
68
+ 'pipelines', 'prompt_configs', 'roles', 'security_blocklist',
69
+ 'system_settings', 'threads', 'trigger_executions', 'triggers',
70
+ 'types', 'watchers',
71
+ ]
72
+
73
+ /** RPC name → args that resolve the signature but fail inside execution (no writes). */
74
+ const REQUIRED_RPCS: Record<string, Record<string, unknown>> = {
75
+ complete_registration_placement: {
76
+ p_person_id: '__probe__',
77
+ p_app_slug: '__probe__',
78
+ p_path: '__probe__',
79
+ p_account_name: '__probe__',
80
+ },
81
+ acquire_document_lease: {
82
+ p_document_id: '__probe__',
83
+ p_account_id: '__probe__',
84
+ p_content_hash: '__probe__',
85
+ p_ttl_seconds: 1,
86
+ },
87
+ release_document_lease: {
88
+ p_document_id: '__probe__',
89
+ p_lease_id: '__probe__',
90
+ p_status: '__probe__',
91
+ },
92
+ }
93
+ // CIL[END stable_id="~/scripts/verify-install.ts:required:block"]
94
+
95
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:probes:block" type="block" identity="probes" parent="~/scripts/verify-install.ts"]
96
+ // CIL[DOC-MACRO purpose="Read-only DB probes"]
97
+ async function tableExists(table: string): Promise<boolean> {
98
+ const { error } = await db.from(table).select('*', { count: 'exact', head: true })
99
+ return !error
100
+ }
101
+
102
+ async function rowCount(table: string): Promise<number> {
103
+ const { count } = await db.from(table).select('*', { count: 'exact', head: true })
104
+ return count ?? -1
105
+ }
106
+
107
+ async function rpcExists(name: string, probeArgs: Record<string, unknown>): Promise<boolean> {
108
+ const { error } = await db.rpc(name, probeArgs)
109
+ if (!error) return true
110
+ return error.code !== 'PGRST202'
111
+ }
112
+ // CIL[END stable_id="~/scripts/verify-install.ts:probes:block"]
113
+
114
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:seed_check:block" type="block" identity="seed_inventory" parent="~/scripts/verify-install.ts"]
115
+ // CIL[DOC-MACRO purpose="Compare the app's declared seed inventory against DB state"]
116
+ // CIL[DOC-MICRO logic_summary="Each seed table gets its own key extractor: types match kind+slug, most others match slug, prompt_configs match name (the table has no slug)" notable_decisions="DB is the oracle — seed rows are the contract the installer promised to materialize"]
117
+ interface SeedCheck { table: string; label: string; key: (row: any) => string }
118
+
119
+ const SEED_KEYS: Record<string, SeedCheck> = {
120
+ 'seed/roles.json': { table: 'roles', label: 'role', key: (r) => r.slug },
121
+ 'seed/types.json': { table: 'types', label: 'type', key: (r) => `${r.kind}/${r.slug}` },
122
+ 'seed/link-types.json': { table: 'link_types', label: 'link_type', key: (r) => r.slug },
123
+ 'seed/ai-agents.json': { table: 'ai_agents', label: 'ai_agent', key: (r) => r.slug ?? r.name },
124
+ 'seed/prompt-configs.json': { table: 'prompt_configs', label: 'prompt_config', key: (r) => r.name },
125
+ 'seed/pipelines.json': { table: 'pipelines', label: 'pipeline', key: (r) => r.slug },
126
+ 'seed/triggers.json': { table: 'triggers', label: 'trigger', key: (r) => r.slug },
127
+ }
128
+
129
+ async function checkSeeds(appDir: string, seedEntries: any[]): Promise<void> {
130
+ for (const entry of seedEntries) {
131
+ const spec = SEED_KEYS[entry.file]
132
+ if (!spec) { console.log(` ○ no verifier for seed file ${entry.file} — skipped`); continue }
133
+ const file = join(appDir, entry.file)
134
+ if (!existsSync(file)) { check(`seed file ${entry.file} exists`, false); continue }
135
+ const rows: any[] = JSON.parse(readFileSync(file, 'utf8'))
136
+ for (const row of rows) {
137
+ const key = spec.key(row)
138
+ let ok = false
139
+ if (spec.table === 'types') {
140
+ const [kind, slug] = key.split('/')
141
+ const { data } = await db.from('types').select('slug, validation_schema').eq('kind', kind).eq('slug', slug).limit(1)
142
+ ok = (data?.length ?? 0) > 0
143
+ check(`${spec.label} seeded: ${key}`, ok)
144
+ if (ok) {
145
+ const vs = data![0].validation_schema
146
+ check(`${spec.label} validation_schema generated: ${key}`,
147
+ !!vs && typeof vs === 'object' && !Array.isArray(vs) && Object.keys(vs).length > 0)
148
+ }
149
+ continue
150
+ } else if (spec.table === 'prompt_configs') {
151
+ const { data } = await db.from(spec.table).select('name').eq('name', key).limit(1)
152
+ ok = (data?.length ?? 0) > 0
153
+ } else if (spec.table === 'ai_agents') {
154
+ // Seed rows may identify by slug (canonical) or name — query whichever
155
+ // identity the seed row actually carries.
156
+ const col = row.slug ? 'slug' : 'name'
157
+ const { data } = await db.from(spec.table).select(col).eq(col, key).limit(1)
158
+ ok = (data?.length ?? 0) > 0
159
+ } else {
160
+ const { data } = await db.from(spec.table).select('slug').eq('slug', key).limit(1)
161
+ ok = (data?.length ?? 0) > 0
162
+ }
163
+ check(`${spec.label} seeded: ${key}`, ok)
164
+ }
165
+ }
166
+ }
167
+ // CIL[END stable_id="~/scripts/verify-install.ts:seed_check:block"]
168
+
169
+ // CIL[BEGIN stable_id="~/scripts/verify-install.ts:main:function" type="function" identity="main" parent="~/scripts/verify-install.ts"]
170
+ // CIL[DOC-MACRO purpose="CLI entry — run all contract checks and report"]
171
+ async function main(): Promise<void> {
172
+ const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
173
+ const appDir = join(repoRoot, 'custom/apps', appSlug)
174
+ const manifestPath = join(appDir, 'manifest.json')
175
+ if (!existsSync(manifestPath)) {
176
+ console.error(`❌ app manifest not found: ${manifestPath}`)
177
+ process.exit(2)
178
+ }
179
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
180
+
181
+ console.log(`\n🔍 verify-install: ${appSlug} → ${SUPABASE_URL}\n`)
182
+
183
+ // 1. Connectivity + target identity
184
+ const { error: pingErr } = await db.from('apps').select('slug', { head: true, count: 'exact' })
185
+ check('database connectivity', !pingErr, pingErr?.message ?? '')
186
+ if (pingErr) { summarize(); return }
187
+ if (expectedProjectRef) {
188
+ check(`target project is ${expectedProjectRef}`,
189
+ SUPABASE_URL === `https://${expectedProjectRef}.supabase.co`,
190
+ `SUPABASE_URL is ${SUPABASE_URL}`)
191
+ }
192
+
193
+ // 2. Required tables
194
+ for (const t of REQUIRED_TABLES) check(`table exists: ${t}`, await tableExists(t))
195
+
196
+ // 3. Required RPCs
197
+ for (const [name, probeArgs] of Object.entries(REQUIRED_RPCS)) {
198
+ check(`rpc exists: ${name}`, await rpcExists(name, probeArgs))
199
+ }
200
+
201
+ // 4. App row + registration config
202
+ const { data: appRows } = await db.from('apps').select('id, slug, config').eq('slug', appSlug).limit(1)
203
+ const app = appRows?.[0]
204
+ check(`app installed: ${appSlug}`, !!app)
205
+ check('app registration config present', !!app?.config?.registration,
206
+ app ? 'apps.config.registration missing' : '')
207
+
208
+ // 5. Seed inventory vs DB
209
+ if (Array.isArray(manifest.seed)) await checkSeeds(appDir, manifest.seed)
210
+
211
+ // 5b. App config materialization — the seeded apps.config must carry the
212
+ // manifest's declared knowledge envelope (runtime KB scope binding).
213
+ if (app && manifest.config?.knowledge !== undefined) {
214
+ const expected = JSON.stringify(manifest.config.knowledge)
215
+ const actual = JSON.stringify((app.config as any)?.knowledge)
216
+ check('app knowledge envelope materialized', actual === expected,
217
+ actual === undefined ? 'apps.config.knowledge missing' : '')
218
+ }
219
+
220
+ // 5c. Migration inventory — every filename the export shipped must appear
221
+ // in the _spine_migrations tracking table (no silent skips on a virgin DB).
222
+ const exportManifestPath = join(repoRoot, '.spine-export-manifest.json')
223
+ // npm-created projects have no export manifest — the inventory falls back to
224
+ // the migration files on disk (.framework + custom).
225
+ let expectedMigrations: string[] | null = null
226
+ if (existsSync(exportManifestPath)) {
227
+ try {
228
+ const em = JSON.parse(readFileSync(exportManifestPath, 'utf8'))
229
+ expectedMigrations = [
230
+ ...(em.migrations?.framework ?? []),
231
+ ...(em.migrations?.custom ?? []),
232
+ ]
233
+ } catch (e: any) {
234
+ check('export manifest readable', false, e?.message ?? '')
235
+ }
236
+ } else {
237
+ const listDir = (rel: string): string[] => {
238
+ const dir = join(repoRoot, rel)
239
+ return existsSync(dir)
240
+ ? readdirSync(dir).filter((n) => n.endsWith('.sql')).sort()
241
+ : []
242
+ }
243
+ expectedMigrations = [...listDir('.framework/migrations'), ...listDir('custom/migrations')]
244
+ }
245
+ if (expectedMigrations) {
246
+ const { data: applied, error: migErr } = await db
247
+ .from('_spine_migrations')
248
+ .select('filename')
249
+ if (migErr) {
250
+ check('migration tracking table readable', false, migErr.message)
251
+ } else {
252
+ const appliedSet = new Set((applied ?? []).map((r: any) => r.filename))
253
+ check('migration inventory complete', expectedMigrations.every((f) => appliedSet.has(f)),
254
+ `${expectedMigrations.filter((f) => !appliedSet.has(f)).length} of ${expectedMigrations.length} not applied`)
255
+ }
256
+ }
257
+
258
+ // 5d. No unexpected installed apps — builtin framework apps plus exactly
259
+ // this app; anything else is a seed/scope defect.
260
+ {
261
+ const { data: appRows2 } = await db.from('apps').select('slug, source')
262
+ const nonBuiltin = (appRows2 ?? []).filter((a: any) => a.source !== 'builtin').map((a: any) => a.slug)
263
+ check('only expected app installed', nonBuiltin.length === 1 && nonBuiltin[0] === appSlug,
264
+ `found: ${nonBuiltin.join(', ') || 'none'}`)
265
+ }
266
+
267
+ // 5e. openai integration credentials must be an env-var reference, never a
268
+ // materialized secret. Report the source field only — never the value.
269
+ {
270
+ const { data: integ } = await db.from('integrations').select('slug, credentials')
271
+ .eq('slug', 'openai').limit(1)
272
+ const creds = (integ?.[0]?.credentials ?? {}) as any
273
+ const src = creds?.api_token?.source ?? creds?.api_key?.source
274
+ check('openai integration uses env-var credential reference', src === 'env_var',
275
+ src ? `source=${src}` : 'integration or credential block missing')
276
+ }
277
+
278
+ // 6. Pipeline stage → action resolution (app pipelines only)
279
+ if (app) {
280
+ const { data: pipelineRows } = await db.from('pipelines')
281
+ .select('slug, stages').eq('app_id', app.id)
282
+ const { data: actionRows } = await db.from('actions').select('slug')
283
+ const actionSlugs = new Set((actionRows ?? []).map((a: any) => a.slug))
284
+ for (const p of pipelineRows ?? []) {
285
+ const stages = Array.isArray(p.stages) ? p.stages : []
286
+ check(`pipeline has stages: ${p.slug}`, stages.length > 0)
287
+ for (const [i, st] of stages.entries()) {
288
+ check(`pipeline ${p.slug} stage[${i}] action "${st.stage_type}" exists`, actionSlugs.has(st.stage_type))
289
+ }
290
+ }
291
+
292
+ // 7. Trigger → pipeline resolution
293
+ const { data: triggerRows } = await db.from('triggers')
294
+ .select('slug, pipeline_id').eq('app_id', app.id)
295
+ for (const tr of triggerRows ?? []) {
296
+ check(`trigger ${tr.slug} resolved to pipeline`, !!tr.pipeline_id)
297
+ }
298
+ }
299
+
300
+ // 8. Fresh-database assertions
301
+ if (expectEmptyPeople) check('people table empty (fresh install)', (await rowCount('people')) === 0)
302
+ if (expectEmptyAccounts) check('accounts table empty (fresh install)', (await rowCount('accounts')) === 0)
303
+
304
+ // 8b. Empty runtime — no records of this app's seeded types may exist on a
305
+ // virgin install (config seeds materialize types, not runtime content).
306
+ if (expectEmptyRuntime && app) {
307
+ const kinds: Record<string, string> = { item: 'items', thread: 'threads', message: 'messages' }
308
+ const typesFile = join(appDir, 'seed/types.json')
309
+ if (existsSync(typesFile)) {
310
+ const typeRows: any[] = JSON.parse(readFileSync(typesFile, 'utf8'))
311
+ for (const t of typeRows) {
312
+ const table = kinds[t.kind]
313
+ if (!table) continue
314
+ const { data: tRow } = await db.from('types').select('id')
315
+ .eq('kind', t.kind).eq('slug', t.slug).limit(1)
316
+ if (!tRow?.[0]?.id) continue // covered by the seed checks above
317
+ const { count } = await db.from(table).select('*', { count: 'exact', head: true })
318
+ .eq('type_id', tRow[0].id)
319
+ check(`no runtime records: ${t.kind}/${t.slug}`, (count ?? 0) === 0, `found ${count}`)
320
+ }
321
+ }
322
+ }
323
+
324
+ // 8c. Auth/people consistency — every auth user must resolve to a people
325
+ // row; a stale auth user on an empty people table is the classic
326
+ // post-purge "Loading…" failure. Read-only via the admin API.
327
+ if (expectEmptyPeople) {
328
+ const { data: authData, error: authErr } = await db.auth.admin.listUsers({ perPage: 1000 })
329
+ if (authErr) {
330
+ console.log(` ○ auth user check skipped: ${authErr.message}`)
331
+ } else {
332
+ const authIds = new Set((authData?.users ?? []).map((u: any) => u.id))
333
+ const { data: peopleRows } = await db.from('people').select('auth_uid')
334
+ const linked = new Set((peopleRows ?? []).map((p: any) => p.auth_uid).filter(Boolean))
335
+ const orphans = [...authIds].filter((id) => !linked.has(id))
336
+ check('no auth users without matching people', orphans.length === 0,
337
+ `${orphans.length} orphan auth user(s)`)
338
+ }
339
+ }
340
+
341
+ summarize()
342
+ }
343
+
344
+ function summarize(): void {
345
+ console.log('')
346
+ if (failures.length === 0) {
347
+ console.log(`✅ verify-install: all ${passed} checks passed`)
348
+ process.exit(0)
349
+ }
350
+ console.log(`❌ verify-install: ${failures.length} failed, ${passed} passed`)
351
+ process.exit(1)
352
+ }
353
+ // CIL[END stable_id="~/scripts/verify-install.ts:main:function"]
354
+
355
+ main().catch((err) => {
356
+ console.error('❌ verify-install crashed:', err)
357
+ process.exit(1)
358
+ })
@@ -1,69 +1,33 @@
1
1
  #!/usr/bin/env bash
2
- #
3
- # Core Integrity Verifier - Verify core files against the build manifest.
4
- # SPINE_INTEGRITY=warn → log warnings (default, dev-friendly)
5
- # SPINE_INTEGRITY=enforce → fail build on mismatch
2
+ # CIL[BEGIN stable_id="~/scripts/verify-integrity.sh:module:script" type="script" identity="verify-integrity" parent="~/scripts/verify-integrity.sh"]
3
+ # CIL[DOC-MACRO purpose="Thin wrapper that verifies protected trees against .spine-manifest.json via scripts/verify-manifest.mts"]
4
+ # CIL[DOC-RESPONSIBILITY responsibility="Map SPINE_INTEGRITY to verifier flags and invoke the Node verifier; fail closed by default"]
5
+ # CIL[DOC-CONTEXT module="spine-integrity" layer="build" related_files="scripts/verify-manifest.mts,scripts/build-manifest.sh,scripts/check-core-integrity.sh,package.json"]
6
+ # CIL[DOC-CONTRACT inputs="SPINE_INTEGRITY=warn|enforce (default enforce); passthrough args" outputs="exit 0 pass / exit 1 integrity failure" errors="exit 1 on missing tsx or verifier failure" side_effects="stdout/stderr report"]
7
+ # CIL[DOC-MICRO logic_summary="SPINE_INTEGRITY=warn passes --warn; the verifier itself ignores --warn when the manifest is frozen" notable_decisions="default is enforce — fail-closed — unlike the legacy warn-by-default script"]
8
+ # CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
9
+ # CIL[DOC-STABILITY stability="stable"]
10
+ # CIL[DOC-AUDIENCE audience="system-admin"]
11
+ # CIL[DOC-VECTOR type="context" content="Invoked by npm run verify and the retired check-core-integrity.sh shim"]
12
+ # CIL[DOC-VECTOR type="implementation_pattern" content="Sibling thin wrapper of build-manifest.sh; real logic in scripts/verify-manifest.mts" status="ready"]
13
+ # CIL[END stable_id="~/scripts/verify-integrity.sh:module:script"]
6
14
 
7
15
  set -euo pipefail
8
16
 
9
17
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
10
18
  PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
11
- MANIFEST_FILE="$PROJECT_ROOT/.spine-manifest.json"
12
19
 
13
- INTEGRITY_MODE="${SPINE_INTEGRITY:-warn}"
20
+ INTEGRITY_MODE="${SPINE_INTEGRITY:-enforce}"
21
+ echo "🔒 Verifying spine integrity (mode: $INTEGRITY_MODE)"
14
22
 
15
- if [ ! -f "$MANIFEST_FILE" ]; then
16
- echo "⚠️ No manifest found at $MANIFEST_FILE — run build-manifest.sh first"
17
- if [ "$INTEGRITY_MODE" = "enforce" ]; then exit 1; fi
18
- exit 0
19
- fi
20
-
21
- echo "🔒 Verifying core integrity (mode: $INTEGRITY_MODE)"
22
-
23
- MISMATCHES=0
24
- MISSING=0
25
-
26
- # Parse each file:hash pair from manifest
27
- while IFS=: read -r file_path expected_hash; do
28
- # Strip quotes and whitespace
29
- file_path=$(echo "$file_path" | tr -d '"' | xargs)
30
- expected_hash=$(echo "$expected_hash" | tr -d '"' | xargs)
31
-
32
- FULL_PATH="$PROJECT_ROOT/$file_path"
33
-
34
- if [ ! -f "$FULL_PATH" ]; then
35
- echo " ✗ MISSING: $file_path"
36
- MISSING=$((MISSING + 1))
37
- continue
38
- fi
39
-
40
- ACTUAL_HASH=$(shasum -a 256 "$FULL_PATH" | cut -d' ' -f1)
41
-
42
- if [ "$ACTUAL_HASH" != "$expected_hash" ]; then
43
- echo " ✗ MODIFIED: $file_path"
44
- MISMATCHES=$((MISMATCHES + 1))
45
- fi
46
- done < <(cat "$MANIFEST_FILE" | python3 -c "
47
- import json, sys
48
- data = json.load(sys.stdin)
49
- for k, v in data.get('hashes', {}).items():
50
- print(f'{k}:{v}')
51
- " 2>/dev/null || echo "")
52
-
53
- TOTAL=$((MISMATCHES + MISSING))
54
-
55
- if [ "$TOTAL" -eq 0 ]; then
56
- echo "✅ All core files match manifest"
57
- exit 0
23
+ TSX="$PROJECT_ROOT/node_modules/.bin/tsx"
24
+ if [ ! -x "$TSX" ]; then
25
+ echo "❌ tsx not found at $TSX — run 'npm install' first" >&2
26
+ exit 1
58
27
  fi
59
28
 
60
- echo ""
61
- echo "⚠️ $MISMATCHES modified, $MISSING missing ($TOTAL total issues)"
62
-
63
- if [ "$INTEGRITY_MODE" = "enforce" ]; then
64
- echo "❌ Build failed: core integrity check failed (SPINE_INTEGRITY=enforce)"
65
- exit 1
29
+ if [ "$INTEGRITY_MODE" = "warn" ]; then
30
+ "$TSX" "$SCRIPT_DIR/verify-manifest.mts" "$PROJECT_ROOT" --warn "$@"
66
31
  else
67
- echo "⚠️ Continuing with warnings (SPINE_INTEGRITY=warn)"
68
- exit 0
32
+ "$TSX" "$SCRIPT_DIR/verify-manifest.mts" "$PROJECT_ROOT" "$@"
69
33
  fi