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,527 @@
1
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/tests/unit/validate-app.test.ts"]
2
+ // CIL[DOC-MACRO purpose="Import Vitest, filesystem fixture utilities, Commander, and validator entry points."]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Import Vitest, filesystem fixture utilities, Commander, and validator entry points."]
4
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
5
+ // CIL[DOC-CONTRACT inputs="none" outputs="none" errors="none" side_effects="none"]
6
+ // CIL[DOC-MICRO logic_summary="Import Vitest, filesystem fixture utilities, Commander, and validator entry points." notable_decisions=""]
7
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
8
+ // CIL[DOC-STABILITY stability="experimental"]
9
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
10
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
11
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, symlinkSync } from 'node:fs'
12
+ import { tmpdir } from 'node:os'
13
+ import { join, dirname } from 'node:path'
14
+ import { Command } from 'commander'
15
+ import { validateAppStructure, registerValidateAppCommands } from '../../cli/commands/validate-app.ts'
16
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:module_header:module_header"]
17
+
18
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:root:variable" type="variable" identity="root" parent="none"]
19
+ // CIL[DOC-MACRO purpose="Declare the temporary repository root used by validator fixtures."]
20
+ // CIL[DOC-RESPONSIBILITY responsibility="Declare the temporary repository root used by validator fixtures."]
21
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
22
+ // CIL[DOC-CONTRACT inputs="none" outputs="Mutable root path" errors="Assertion or fixture failures propagate to Vitest" side_effects="Declares module-local fixture state"]
23
+ // CIL[DOC-MICRO logic_summary="Declare the temporary repository root used by validator fixtures." notable_decisions=""]
24
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
25
+ // CIL[DOC-STABILITY stability="experimental"]
26
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
27
+ let root: string
28
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:root:variable"]
29
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:app:variable" type="variable" identity="app" parent="none"]
30
+ // CIL[DOC-MACRO purpose="Declare the current temporary app directory."]
31
+ // CIL[DOC-RESPONSIBILITY responsibility="Declare the current temporary app directory."]
32
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
33
+ // CIL[DOC-CONTRACT inputs="none" outputs="Mutable app path" errors="Assertion or fixture failures propagate to Vitest" side_effects="Declares module-local fixture state"]
34
+ // CIL[DOC-MICRO logic_summary="Declare the current temporary app directory." notable_decisions=""]
35
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
36
+ // CIL[DOC-STABILITY stability="experimental"]
37
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
38
+ let app: string
39
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:app:variable"]
40
+
41
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_setup:block" type="block" identity="fixture_setup" parent="none"]
42
+ // CIL[DOC-MACRO purpose="Register fixture setup that creates a temporary root and mocks console output."]
43
+ // CIL[DOC-RESPONSIBILITY responsibility="Register fixture setup that creates a temporary root and mocks console output."]
44
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
45
+ // CIL[DOC-CONTRACT inputs="Vitest beforeEach lifecycle" outputs="Fixture paths and console spies" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a hook that creates a temporary directory and installs console spies"]
46
+ // CIL[DOC-MICRO logic_summary="Register fixture setup that creates a temporary root and mocks console output." notable_decisions=""]
47
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
48
+ // CIL[DOC-STABILITY stability="experimental"]
49
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
50
+ beforeEach(() => {
51
+ root = mkdtempSync(join(tmpdir(), 'spine-validate-app-'))
52
+ app = join(root, 'test-app')
53
+ vi.spyOn(console, 'log').mockImplementation(() => {})
54
+ vi.spyOn(console, 'warn').mockImplementation(() => {})
55
+ })
56
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_setup:block"]
57
+
58
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_cleanup:block" type="block" identity="fixture_cleanup" parent="none"]
59
+ // CIL[DOC-MACRO purpose="Register fixture cleanup that removes the created root and restores spies."]
60
+ // CIL[DOC-RESPONSIBILITY responsibility="Register fixture cleanup that removes the created root and restores spies."]
61
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
62
+ // CIL[DOC-CONTRACT inputs="Fixture root and Vitest mocks" outputs="Cleaned fixture and restored mocks" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a cleanup hook"]
63
+ // CIL[DOC-MICRO logic_summary="Register fixture cleanup that removes the created root and restores spies." notable_decisions=""]
64
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
65
+ // CIL[DOC-STABILITY stability="experimental"]
66
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
67
+ afterEach(() => {
68
+ rmSync(root, { recursive: true, force: true })
69
+ vi.restoreAllMocks()
70
+ })
71
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixture_cleanup:block"]
72
+
73
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:fixtureoptions:interface" type="interface" identity="FixtureOptions" parent="none"]
74
+ // CIL[DOC-MACRO purpose="Describe optional manifest, directory, file, and seed overrides for a fixture."]
75
+ // CIL[DOC-RESPONSIBILITY responsibility="Describe optional manifest, directory, file, and seed overrides for a fixture."]
76
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
77
+ // CIL[DOC-CONTRACT inputs="Fixture override values" outputs="FixtureOptions type" errors="Assertion or fixture failures propagate to Vitest" side_effects="none"]
78
+ // CIL[DOC-MICRO logic_summary="Describe optional manifest, directory, file, and seed overrides for a fixture." notable_decisions=""]
79
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
80
+ // CIL[DOC-STABILITY stability="experimental"]
81
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
82
+ interface FixtureOptions {
83
+ manifest?: Record<string, any>
84
+ manifestText?: string
85
+ dirs?: string[]
86
+ files?: Record<string, string>
87
+ seedJson?: Record<string, string>
88
+ }
89
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:fixtureoptions:interface"]
90
+
91
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:makeapp:function" type="function" identity="makeApp" parent="none"]
92
+ // CIL[DOC-MACRO purpose="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides."]
93
+ // CIL[DOC-RESPONSIBILITY responsibility="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides."]
94
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
95
+ // CIL[DOC-CONTRACT inputs="opts: FixtureOptions default empty object" outputs="void" errors="Assertion or fixture failures propagate to Vitest" side_effects="Creates temporary app directories and writes fixture files"]
96
+ // CIL[DOC-MICRO logic_summary="Write a minimal valid temporary app with optional manifest, directory, file, and seed overrides." notable_decisions=""]
97
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
98
+ // CIL[DOC-STABILITY stability="experimental"]
99
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
100
+ function makeApp(opts: FixtureOptions = {}): void {
101
+ mkdirSync(app, { recursive: true })
102
+ const manifest = opts.manifest ?? {
103
+ name: 'Test App',
104
+ slug: 'test-app',
105
+ version: '0.1.0',
106
+ app_type: 'full',
107
+ route_prefix: '/test-app',
108
+ entry_point: 'index.tsx',
109
+ seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }],
110
+ config: { knowledge: { account: true } },
111
+ registration: { enabled: true },
112
+ }
113
+ writeFileSync(join(app, 'manifest.json'), opts.manifestText ?? JSON.stringify(manifest))
114
+ writeFileSync(join(app, 'package.json'), JSON.stringify({ name: 'test-app', version: '0.1.0', private: true }))
115
+ writeFileSync(join(app, 'index.tsx'), 'export default () => null\n')
116
+ for (const d of ['pages', 'functions', 'seed', ...(opts.dirs ?? [])]) {
117
+ mkdirSync(join(app, d), { recursive: true })
118
+ }
119
+ writeFileSync(join(app, 'seed/roles.json'), JSON.stringify([{ slug: 'member' }]))
120
+ for (const [rel, content] of Object.entries(opts.files ?? {})) {
121
+ mkdirSync(dirname(join(app, rel)), { recursive: true })
122
+ writeFileSync(join(app, rel), content)
123
+ }
124
+ for (const [rel, content] of Object.entries(opts.seedJson ?? {})) {
125
+ writeFileSync(join(app, rel), content)
126
+ }
127
+ }
128
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:makeapp:function"]
129
+
130
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:basemanifest:function" type="function" identity="baseManifest" parent="none"]
131
+ // CIL[DOC-MACRO purpose="Build the standard validator fixture manifest with supplied overrides."]
132
+ // CIL[DOC-RESPONSIBILITY responsibility="Build the standard validator fixture manifest with supplied overrides."]
133
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
134
+ // CIL[DOC-CONTRACT inputs="overrides: Record<string, any> default empty object" outputs="Manifest fixture object" errors="Assertion or fixture failures propagate to Vitest" side_effects="none"]
135
+ // CIL[DOC-MICRO logic_summary="Build the standard validator fixture manifest with supplied overrides." notable_decisions=""]
136
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
137
+ // CIL[DOC-STABILITY stability="experimental"]
138
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
139
+ function baseManifest(overrides: Record<string, any> = {}): Record<string, any> {
140
+ return {
141
+ name: 'Test App',
142
+ slug: 'test-app',
143
+ version: '0.1.0',
144
+ app_type: 'full',
145
+ route_prefix: '/test-app',
146
+ entry_point: 'index.tsx',
147
+ seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }],
148
+ config: { knowledge: { account: true } },
149
+ registration: { enabled: true },
150
+ ...overrides,
151
+ }
152
+ }
153
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:basemanifest:function"]
154
+
155
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:acceptance_suite:block" type="block" identity="acceptance_suite" parent="none"]
156
+ // CIL[DOC-MACRO purpose="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds."]
157
+ // CIL[DOC-RESPONSIBILITY responsibility="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds."]
158
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
159
+ // CIL[DOC-CONTRACT inputs="makeApp, baseManifest, validateAppStructure" outputs="Vitest acceptance assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers test cases using temporary fixture files"]
160
+ // CIL[DOC-MICRO logic_summary="Register successful validation cases for current JSON seed paths, private packages, legacy app_type, source directories, and empty seeds." notable_decisions=""]
161
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
162
+ // CIL[DOC-STABILITY stability="experimental"]
163
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
164
+ describe('validateAppStructure — acceptance', () => {
165
+ it('accepts a valid app fixture using current seed/ JSON paths', () => {
166
+ makeApp()
167
+ expect(() => validateAppStructure(app)).not.toThrow()
168
+ })
169
+
170
+ it('accepts a private internal package', () => {
171
+ makeApp()
172
+ expect(() => validateAppStructure(app)).not.toThrow()
173
+ })
174
+
175
+ it('normalizes nested registration.app_type when root app_type is absent', () => {
176
+ const m = baseManifest()
177
+ delete m.app_type
178
+ m.registration = { enabled: true, app_type: 'full' }
179
+ makeApp({ manifest: m })
180
+ expect(() => validateAppStructure(app)).not.toThrow()
181
+ })
182
+
183
+ it('accepts the expanded allowed source directories', () => {
184
+ makeApp({ dirs: ['lib', 'views', 'docs', 'mcp', 'migrations', 'migrations-archive', 'tests'] })
185
+ expect(() => validateAppStructure(app)).not.toThrow()
186
+ })
187
+
188
+ it('accepts an empty JSON array seed file', () => {
189
+ makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/empty.json', table: 'roles', conflict: 'slug' }] }), seedJson: { 'seed/empty.json': '[]' } })
190
+ expect(() => validateAppStructure(app)).not.toThrow()
191
+ })
192
+ })
193
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:acceptance_suite:block"]
194
+
195
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:manifest_rules_suite:block" type="block" identity="manifest_rules_suite" parent="none"]
196
+ // CIL[DOC-MACRO purpose="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases."]
197
+ // CIL[DOC-RESPONSIBILITY responsibility="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases."]
198
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
199
+ // CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest manifest validation assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that mutate or remove their own fixture files"]
200
+ // CIL[DOC-MICRO logic_summary="Register malformed and missing manifest, app_type precedence, invalid roots, and whitespace-name rejection cases." notable_decisions=""]
201
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
202
+ // CIL[DOC-STABILITY stability="experimental"]
203
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
204
+ describe('validateAppStructure — manifest rules', () => {
205
+ it('rejects a missing manifest.json', () => {
206
+ mkdirSync(app, { recursive: true })
207
+ expect(() => validateAppStructure(app)).toThrowError(/manifest\.json is required/)
208
+ })
209
+
210
+ it('rejects malformed manifest.json', () => {
211
+ makeApp({ manifestText: '{nope' })
212
+ expect(() => validateAppStructure(app)).toThrowError(/not valid JSON/)
213
+ })
214
+
215
+ it('rejects an invalid explicit app_type', () => {
216
+ makeApp({ manifest: baseManifest({ app_type: 'banana' }) })
217
+ expect(() => validateAppStructure(app)).toThrowError(/app_type/)
218
+ })
219
+
220
+ it('rejects an invalid nested registration.app_type', () => {
221
+ const m = baseManifest()
222
+ delete m.app_type
223
+ m.registration = { app_type: 'banana' }
224
+ makeApp({ manifest: m })
225
+ expect(() => validateAppStructure(app)).toThrowError(/app_type/)
226
+ })
227
+
228
+ it('still requires app_type when neither root nor nested value is present', () => {
229
+ const m = baseManifest()
230
+ delete m.app_type
231
+ makeApp({ manifest: m })
232
+ expect(() => validateAppStructure(app)).toThrowError(/app_type/)
233
+ })
234
+
235
+ it('lets a root app_type take precedence over the nested registration value', () => {
236
+ const m = baseManifest({ app_type: 'backend', entry_point: undefined })
237
+ delete m.entry_point
238
+ m.registration = { app_type: 'full' }
239
+ makeApp({ manifest: m })
240
+ rmSync(join(app, 'index.tsx'))
241
+ rmSync(join(app, 'pages'), { recursive: true })
242
+ expect(() => validateAppStructure(app)).not.toThrow()
243
+ })
244
+
245
+ it('rejects a manifest.json root that is null, an array, or a primitive', () => {
246
+ for (const body of ['null', '[1]', '"x"', '7']) {
247
+ makeApp({ manifestText: body })
248
+ expect(() => validateAppStructure(app), body).toThrowError(/JSON object/)
249
+ rmSync(app, { recursive: true, force: true })
250
+ }
251
+ })
252
+
253
+ it('rejects a whitespace-only manifest name', () => {
254
+ makeApp({ manifest: baseManifest({ name: ' ' }) })
255
+ expect(() => validateAppStructure(app)).toThrowError(/name/)
256
+ })
257
+ })
258
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:manifest_rules_suite:block"]
259
+
260
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:directory_rules_suite:block" type="block" identity="directory_rules_suite" parent="none"]
261
+ // CIL[DOC-MACRO purpose="Register rejection of an unknown app source directory."]
262
+ // CIL[DOC-RESPONSIBILITY responsibility="Register rejection of an unknown app source directory."]
263
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
264
+ // CIL[DOC-CONTRACT inputs="makeApp and validateAppStructure" outputs="Vitest directory validation assertion" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a temporary fixture test"]
265
+ // CIL[DOC-MICRO logic_summary="Register rejection of an unknown app source directory." notable_decisions=""]
266
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
267
+ // CIL[DOC-STABILITY stability="experimental"]
268
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
269
+ describe('validateAppStructure — directory rules', () => {
270
+ it('rejects unknown directories', () => {
271
+ makeApp({ dirs: ['junk-drawer'] })
272
+ expect(() => validateAppStructure(app)).toThrowError(/Unknown directories/)
273
+ })
274
+ })
275
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:directory_rules_suite:block"]
276
+
277
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:seed_rules_suite:block" type="block" identity="seed_rules_suite" parent="none"]
278
+ // CIL[DOC-MACRO purpose="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases."]
279
+ // CIL[DOC-RESPONSIBILITY responsibility="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases."]
280
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
281
+ // CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest seed validation assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that create, rewrite, and remove temporary seed fixtures"]
282
+ // CIL[DOC-MICRO logic_summary="Register missing, malformed, non-object, absolute, escaping, and externally linked JSON seed rejection cases." notable_decisions=""]
283
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
284
+ // CIL[DOC-STABILITY stability="experimental"]
285
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
286
+ describe('validateAppStructure — seed rules', () => {
287
+ it('rejects a missing seed file', () => {
288
+ makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/nope.json', table: 'roles', conflict: 'slug' }] }) })
289
+ expect(() => validateAppStructure(app)).toThrowError(/not found/)
290
+ })
291
+
292
+ it('rejects a non-.json seed file', () => {
293
+ makeApp({
294
+ manifest: baseManifest({ seed: [{ file: 'seed/x.sql', table: 'roles', conflict: 'slug' }] }),
295
+ seedJson: { 'seed/x.sql': 'INSERT INTO roles VALUES (1);' },
296
+ })
297
+ expect(() => validateAppStructure(app)).toThrowError(/\.json/)
298
+ })
299
+
300
+ it('rejects a seed file that is not valid JSON', () => {
301
+ makeApp({ seedJson: { 'seed/roles.json': '{oops' } })
302
+ expect(() => validateAppStructure(app)).toThrowError(/valid JSON/)
303
+ })
304
+
305
+ it('rejects a seed JSON root that is an object not an array', () => {
306
+ makeApp({ seedJson: { 'seed/roles.json': '{"a":1}' } })
307
+ expect(() => validateAppStructure(app)).toThrowError(/array/)
308
+ })
309
+
310
+ it('rejects null, primitive, or array seed rows', () => {
311
+ for (const body of ['[null]', '[1]', '["x"]', '[[1]]']) {
312
+ makeApp({ seedJson: { 'seed/roles.json': body } })
313
+ expect(() => validateAppStructure(app), body).toThrowError(/non-null object/)
314
+ rmSync(app, { recursive: true, force: true })
315
+ }
316
+ })
317
+
318
+ it('rejects a seed entry with missing file/table/conflict', () => {
319
+ makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/roles.json', conflict: 'slug' }] }) })
320
+ expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
321
+ })
322
+
323
+ it('rejects a whitespace-only file/table/conflict', () => {
324
+ makeApp({ manifest: baseManifest({ seed: [{ file: ' ', table: 'roles', conflict: 'slug' }] }) })
325
+ expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
326
+ rmSync(app, { recursive: true, force: true })
327
+ makeApp({ manifest: baseManifest({ seed: [{ file: 'seed/roles.json', table: ' ', conflict: 'slug' }] }) })
328
+ expect(() => validateAppStructure(app)).toThrowError(/file, table, and conflict/)
329
+ })
330
+
331
+ it('rejects null, array, or primitive seed entries', () => {
332
+ for (const entry of [null, 7, 'x', ['seed/roles.json']]) {
333
+ makeApp({ manifest: baseManifest({ seed: [entry] }) })
334
+ expect(() => validateAppStructure(app), JSON.stringify(entry)).toThrowError(/seed entries must be objects/)
335
+ rmSync(app, { recursive: true, force: true })
336
+ }
337
+ })
338
+
339
+ it('rejects an absolute seed path', () => {
340
+ makeApp({ manifest: baseManifest({ seed: [{ file: join(app, 'seed/roles.json'), table: 'roles', conflict: 'slug' }] }) })
341
+ expect(() => validateAppStructure(app)).toThrowError()
342
+ })
343
+
344
+ it('rejects a traversal seed path escaping the app root', () => {
345
+ writeFileSync(join(root, 'outside.json'), '[]')
346
+ makeApp({ manifest: baseManifest({ seed: [{ file: '../outside.json', table: 'roles', conflict: 'slug' }] }) })
347
+ expect(() => validateAppStructure(app)).toThrowError()
348
+ })
349
+
350
+ it('rejects a seed file that symlinks outside the app root', () => {
351
+ const external = join(root, 'external-seed.json')
352
+ writeFileSync(external, '[]')
353
+ makeApp({
354
+ manifest: baseManifest({ seed: [{ file: 'seed/external.json', table: 'roles', conflict: 'slug' }] }),
355
+ })
356
+ rmSync(join(app, 'seed/external.json'), { force: true })
357
+ symlinkSync(external, join(app, 'seed/external.json'))
358
+ expect(() => validateAppStructure(app)).toThrowError()
359
+ })
360
+ })
361
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:seed_rules_suite:block"]
362
+
363
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:component_paths_suite:block" type="block" identity="component_paths_suite" parent="none"]
364
+ // CIL[DOC-MACRO purpose="Register entry and sidebar path confinement checks plus successful contained paths."]
365
+ // CIL[DOC-RESPONSIBILITY responsibility="Register entry and sidebar path confinement checks plus successful contained paths."]
366
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
367
+ // CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest component path assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers temporary file and symlink fixture tests"]
368
+ // CIL[DOC-MICRO logic_summary="Register entry and sidebar path confinement checks plus successful contained paths." notable_decisions=""]
369
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
370
+ // CIL[DOC-STABILITY stability="experimental"]
371
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
372
+ describe('validateAppStructure — component path confinement', () => {
373
+ it('rejects an entry_point escaping the app root', () => {
374
+ writeFileSync(join(root, 'escape.tsx'), 'export default () => null\n')
375
+ makeApp({ manifest: baseManifest({ entry_point: '../escape.tsx' }) })
376
+ expect(() => validateAppStructure(app)).toThrowError(/entry_point/)
377
+ })
378
+
379
+ it('rejects an absolute entry_point outside the app root', () => {
380
+ makeApp({ manifest: baseManifest({ entry_point: join(root, 'escape.tsx') }) })
381
+ expect(() => validateAppStructure(app)).toThrowError(/entry_point/)
382
+ })
383
+
384
+ it('rejects a sidebar_component that symlinks outside the app root', () => {
385
+ const external = join(root, 'external-sidebar.tsx')
386
+ writeFileSync(external, 'export default () => null\n')
387
+ makeApp({ manifest: baseManifest({ sidebar_component: 'components/Sidebar.tsx' }) })
388
+ mkdirSync(join(app, 'components'), { recursive: true })
389
+ symlinkSync(external, join(app, 'components/Sidebar.tsx'))
390
+ expect(() => validateAppStructure(app)).toThrowError(/sidebar_component/)
391
+ })
392
+
393
+ it('accepts entry_point and sidebar_component inside the app', () => {
394
+ makeApp({
395
+ manifest: baseManifest({ entry_point: './index.tsx', sidebar_component: 'components/Sidebar.tsx' }),
396
+ files: { 'components/Sidebar.tsx': 'export default () => null\n' },
397
+ })
398
+ expect(() => validateAppStructure(app)).not.toThrow()
399
+ })
400
+ })
401
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:component_paths_suite:block"]
402
+
403
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:isolation_suite:block" type="block" identity="isolation_suite" parent="none"]
404
+ // CIL[DOC-MACRO purpose="Register successful validation while cwd differs from the repository and restore cwd afterward."]
405
+ // CIL[DOC-RESPONSIBILITY responsibility="Register successful validation while cwd differs from the repository and restore cwd afterward."]
406
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
407
+ // CIL[DOC-CONTRACT inputs="Fixture app and validateAppStructure" outputs="Vitest cwd-independence assertion" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers a test that temporarily changes and restores process cwd"]
408
+ // CIL[DOC-MICRO logic_summary="Register successful validation while cwd differs from the repository and restore cwd afterward." notable_decisions=""]
409
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
410
+ // CIL[DOC-STABILITY stability="experimental"]
411
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
412
+ describe('validateAppStructure — isolation', () => {
413
+ it('validates without repo .env or cwd dependence and no network use', () => {
414
+ makeApp()
415
+ const cwd = process.cwd()
416
+ try {
417
+ process.chdir(root)
418
+ expect(() => validateAppStructure(app)).not.toThrow()
419
+ } finally {
420
+ process.chdir(cwd)
421
+ }
422
+ })
423
+ })
424
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:isolation_suite:block"]
425
+
426
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:command_registration_suite:block" type="block" identity="command_registration_suite" parent="none"]
427
+ // CIL[DOC-MACRO purpose="Register a Commander assertion that validate-app appears in the command list and help."]
428
+ // CIL[DOC-RESPONSIBILITY responsibility="Register a Commander assertion that validate-app appears in the command list and help."]
429
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
430
+ // CIL[DOC-CONTRACT inputs="Command and registerValidateAppCommands" outputs="Vitest command registration assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers an in-memory Commander test"]
431
+ // CIL[DOC-MICRO logic_summary="Register a Commander assertion that validate-app appears in the command list and help." notable_decisions=""]
432
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
433
+ // CIL[DOC-STABILITY stability="experimental"]
434
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
435
+ describe('registerValidateAppCommands', () => {
436
+ it('registers a validate-app command that appears in CLI help', () => {
437
+ const program = new Command()
438
+ registerValidateAppCommands(program)
439
+ expect(program.commands.map((c) => c.name())).toContain('validate-app')
440
+ expect(program.helpInformation()).toContain('validate-app')
441
+ })
442
+ })
443
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:command_registration_suite:block"]
444
+
445
+ // CIL[BEGIN stable_id="~/.framework/tests/unit/validate-app.test.ts:owned_types_suite:block" type="block" identity="owned_types_suite" parent="none"]
446
+ // CIL[DOC-MACRO purpose="Register owned_types shape and type-seed resolution validation cases."]
447
+ // CIL[DOC-RESPONSIBILITY responsibility="Register owned_types shape and type-seed resolution validation cases."]
448
+ // CIL[DOC-CONTEXT module="tests/unit" layer="test-unit" related_files=".framework/cli/commands/validate-app.ts"]
449
+ // CIL[DOC-CONTRACT inputs="Fixture helpers and validateAppStructure" outputs="Vitest owned_types assertions" errors="Assertion or fixture failures propagate to Vitest" side_effects="Registers tests that write temporary seed fixtures"]
450
+ // CIL[DOC-MICRO logic_summary="Register owned_types shape and type-seed resolution validation cases." notable_decisions=""]
451
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
452
+ // CIL[DOC-STABILITY stability="experimental"]
453
+ // CIL[DOC-AUDIENCE audience="framework contributors"]
454
+ describe('validateAppStructure — owned_types', () => {
455
+ const typesSeedEntry = { file: 'seed/types.json', table: 'types', conflict: 'kind,slug', inject_app_id: false }
456
+
457
+ it('treats an absent owned_types as a valid optional field', () => {
458
+ makeApp()
459
+ expect(() => validateAppStructure(app)).not.toThrow()
460
+ })
461
+
462
+ it('accepts owned_types slugs that resolve to the types seed', () => {
463
+ makeApp({
464
+ manifest: baseManifest({
465
+ owned_types: ['demo_thing', 'demo_other'],
466
+ seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
467
+ }),
468
+ seedJson: {
469
+ 'seed/types.json': JSON.stringify([
470
+ { kind: 'item', slug: 'demo_thing' },
471
+ { kind: 'item', slug: 'demo_other' },
472
+ ]),
473
+ },
474
+ })
475
+ expect(() => validateAppStructure(app)).not.toThrow()
476
+ })
477
+
478
+ it('accepts owned_types resolved by a declared non-patch types seed file', () => {
479
+ makeApp({
480
+ manifest: baseManifest({
481
+ owned_types: ['demo_thing'],
482
+ seed: [
483
+ { file: 'seed/roles.json', table: 'roles', conflict: 'slug' },
484
+ { file: 'seed/more-types.json', table: 'types', conflict: 'kind,slug', inject_app_id: false },
485
+ ],
486
+ }),
487
+ seedJson: { 'seed/more-types.json': JSON.stringify([{ kind: 'item', slug: 'demo_thing' }]) },
488
+ })
489
+ expect(() => validateAppStructure(app)).not.toThrow()
490
+ })
491
+
492
+ it.each([
493
+ 'not-an-array',
494
+ 42,
495
+ { slug: 'x' },
496
+ ['ok', ''],
497
+ ['ok', ' '],
498
+ ['ok', 7],
499
+ [null],
500
+ ])('rejects malformed owned_types %j', (owned_types) => {
501
+ makeApp({
502
+ manifest: baseManifest({
503
+ owned_types,
504
+ seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
505
+ }),
506
+ seedJson: { 'seed/types.json': JSON.stringify([{ kind: 'item', slug: 'ok' }]) },
507
+ })
508
+ expect(() => validateAppStructure(app)).toThrowError(/owned_types/)
509
+ })
510
+
511
+ it('rejects an owned slug absent from the types seed', () => {
512
+ makeApp({
513
+ manifest: baseManifest({
514
+ owned_types: ['ghost'],
515
+ seed: [{ file: 'seed/roles.json', table: 'roles', conflict: 'slug' }, typesSeedEntry],
516
+ }),
517
+ seedJson: { 'seed/types.json': JSON.stringify([{ kind: 'item', slug: 'real' }]) },
518
+ })
519
+ expect(() => validateAppStructure(app)).toThrowError(/ghost/)
520
+ })
521
+
522
+ it('rejects owned_types when the app ships no types seed', () => {
523
+ makeApp({ manifest: baseManifest({ owned_types: ['anything'] }) })
524
+ expect(() => validateAppStructure(app)).toThrowError(/owned_types.*anything|anything.*types seed/)
525
+ })
526
+ })
527
+ // CIL[END stable_id="~/.framework/tests/unit/validate-app.test.ts:owned_types_suite:block"]
package/README.md CHANGED
@@ -8,11 +8,19 @@ A complete enterprise application platform built on Supabase + Netlify + React f
8
8
 
9
9
  ### Prerequisites
10
10
 
11
- - Node.js 18+
11
+ - Node.js 22+
12
12
  - A [Supabase](https://supabase.com) project
13
13
  - A [Netlify](https://netlify.com) account
14
14
 
15
- ### 1. Install and scaffold
15
+ ### Start with an app
16
+
17
+ The fastest way to a running system is an app package that scaffolds the framework plus itself:
18
+
19
+ ```bash
20
+ npm create @spine-framework/answer-once my-app
21
+ ```
22
+
23
+ ### Or scaffold the framework alone
16
24
 
17
25
  ```bash
18
26
  mkdir my-app && cd my-app
@@ -20,7 +28,7 @@ npm install spine-framework@latest
20
28
  npx spine-framework init
21
29
  ```
22
30
 
23
- This scaffolds the full project structure, copies all framework files, and writes a `.env` template.
31
+ This copies the framework files, writes `package.json` with an exact `spine-framework` pin, a verified `package-lock.json`, a neutral `spine.config.json`, `.env.example`, and a frozen `.spine-manifest.json` integrity manifest.
24
32
 
25
33
  ### 2. Configure your environment
26
34
 
@@ -65,6 +73,29 @@ All commands run via `npx spine-framework <command>`.
65
73
  | `migrate --dry-run` | Preview pending migrations without applying |
66
74
  | `migrate --db-password <pw>` | Pass DB password directly instead of reading from `.env` |
67
75
  | `status` | Show installed apps and framework version |
76
+ | `upgrade` | Sync framework-owned files to the installed `spine-framework` version |
77
+
78
+ ---
79
+
80
+ ## Upgrading
81
+
82
+ `.framework/`, `config/`, `scripts/` and `bin/` are a managed copy of the
83
+ `spine-framework` package — don't edit them (the frozen `.spine-manifest.json`
84
+ integrity check will fail). To move to a newer release:
85
+
86
+ ```bash
87
+ npm install spine-framework@latest
88
+ npx spine-framework upgrade --dry-run # review what would change
89
+ npx spine-framework upgrade
90
+ npm install
91
+ npm run verify
92
+ npx spine-framework migrate # apply any new migrations
93
+ ```
94
+
95
+ Upgrade refuses to overwrite locally modified framework files (rerun with
96
+ `--force` to accept the overwrite), preserves your `custom/` code and app-owned
97
+ `netlify.toml` sections, and lists new migrations before you apply them.
98
+ App packages upgrade themselves the same way — see the app's own docs.
68
99
 
69
100
  ---
70
101
 
@@ -14,6 +14,7 @@ if (process.argv.length === 2) {
14
14
  console.log('Usage: spine-framework <command>\n')
15
15
  console.log('Commands:')
16
16
  console.log(' init Scaffold a new Spine Framework project')
17
+ console.log(' upgrade Sync framework files to the installed spine-framework version')
17
18
  console.log(' migrate Apply database migrations')
18
19
  console.log(' install-app Install a Spine Framework app from npm')
19
20
  console.log(' status Show installed apps and framework version')
@@ -63,6 +63,12 @@ module.exports = {
63
63
  '4': 'hsl(var(--chart-4))',
64
64
  '5': 'hsl(var(--chart-5))',
65
65
  },
66
+ brand: {
67
+ navy: '#0B1F44',
68
+ DEFAULT: '#0066FF',
69
+ light: '#33A1FF',
70
+ gray: '#F4F7FB',
71
+ },
66
72
  },
67
73
  borderRadius: {
68
74
  lg: 'var(--radius)',
@@ -73,6 +79,7 @@ module.exports = {
73
79
  },
74
80
  plugins: [
75
81
  require('tailwindcss-animate'),
82
+ require('@tailwindcss/typography'),
76
83
  ],
77
84
  }
78
85