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