spine-framework 0.4.4 → 0.5.0-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/.env.example +13 -3
  2. package/.framework/cli/commands/bootstrap.ts +392 -0
  3. package/.framework/cli/commands/dev.ts +91 -218
  4. package/.framework/cli/commands/doctor.ts +132 -26
  5. package/.framework/cli/commands/generate.ts +38 -298
  6. package/.framework/cli/commands/init.ts +20 -1
  7. package/.framework/cli/commands/install-app.ts +183 -13
  8. package/.framework/cli/commands/migrate.ts +36 -30
  9. package/.framework/cli/commands/migrations.ts +16 -56
  10. package/.framework/cli/commands/upgrade.ts +35 -0
  11. package/.framework/cli/commands/validate-app.ts +127 -1
  12. package/.framework/cli/commands/validate-schema.ts +147 -0
  13. package/.framework/cli/context.ts +223 -110
  14. package/.framework/cli/index.ts +6 -0
  15. package/.framework/cli/lib/project-files.ts +241 -1
  16. package/.framework/functions/_shared/app-manifest.ts +2 -0
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/logging.ts +562 -224
  19. package/.framework/functions/_shared/middleware.ts +47 -44
  20. package/.framework/functions/_shared/permissions.ts +4 -0
  21. package/.framework/functions/admin-data.ts +51 -28
  22. package/.framework/functions/ai-agents.ts +10 -6
  23. package/.framework/functions/api-keys.ts +301 -122
  24. package/.framework/functions/apps.ts +63 -29
  25. package/.framework/functions/cil-ingest.ts +48 -15
  26. package/.framework/functions/debug-auth.ts +14 -8
  27. package/.framework/functions/deliveries.ts +39 -12
  28. package/.framework/functions/embeddings.ts +149 -78
  29. package/.framework/functions/integration-outbound.ts +67 -27
  30. package/.framework/functions/integrations.ts +254 -127
  31. package/.framework/functions/logs.ts +273 -133
  32. package/.framework/functions/observability.ts +278 -148
  33. package/.framework/functions/pipeline-executions.ts +1 -1
  34. package/.framework/functions/pipelines.ts +14 -9
  35. package/.framework/functions/prompt-configs.ts +7 -4
  36. package/.framework/functions/roles.ts +8 -5
  37. package/.framework/functions/security.ts +30 -17
  38. package/.framework/functions/system-cron.ts +240 -127
  39. package/.framework/functions/tests.ts +7 -6
  40. package/.framework/functions/timers.ts +310 -107
  41. package/.framework/functions/triggers.ts +28 -13
  42. package/.framework/functions/types.ts +13 -10
  43. package/.framework/migrations/069_log_stats_window.sql +54 -0
  44. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  45. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  46. package/.framework/migrations/072_slug_autofill.sql +77 -0
  47. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  48. package/.framework/migrations/074_app_route_prefix_domains.sql +168 -0
  49. package/.framework/migrations/075_security_definer_grants.sql +157 -0
  50. package/.framework/src/apps/admin/index.tsx +1141 -775
  51. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  52. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  53. package/.framework/src/components/admin/AdminSidebar.tsx +88 -79
  54. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  55. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  56. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  57. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  58. package/.framework/src/components/layout/Sidebar.tsx +300 -177
  59. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  60. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  61. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  62. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  63. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  64. package/.framework/src/components/ui/sheet.tsx +24 -26
  65. package/.framework/src/components/ui/textarea.tsx +74 -48
  66. package/.framework/src/contexts/AppContext.tsx +5 -12
  67. package/.framework/src/contexts/AuthContext.tsx +17 -9
  68. package/.framework/src/hooks/useApps.ts +18 -22
  69. package/.framework/src/hooks/useEntityList.ts +12 -9
  70. package/.framework/src/hooks/useListSchema.ts +108 -58
  71. package/.framework/src/instructions/README.md +74 -0
  72. package/.framework/src/instructions/accounts.md +97 -0
  73. package/.framework/src/instructions/ai-agents.md +69 -0
  74. package/.framework/src/instructions/alerts.md +80 -0
  75. package/.framework/src/instructions/api-keys.md +87 -0
  76. package/.framework/src/instructions/apps.md +77 -0
  77. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  78. package/.framework/src/instructions/concepts/apps-and-renderers.md +228 -0
  79. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  80. package/.framework/src/instructions/concepts/automation.md +186 -0
  81. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  82. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  83. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  84. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  85. package/.framework/src/instructions/concepts/observability.md +116 -0
  86. package/.framework/src/instructions/concepts/permissions.md +150 -0
  87. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  88. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  89. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  90. package/.framework/src/instructions/deliveries.md +59 -0
  91. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  92. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  93. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  94. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  95. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  96. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  97. package/.framework/src/instructions/integrations.md +96 -0
  98. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  99. package/.framework/src/instructions/internal/docs-contract.md +158 -0
  100. package/.framework/src/instructions/invites.md +60 -0
  101. package/.framework/src/instructions/kb.md +80 -0
  102. package/.framework/src/instructions/logs.md +77 -0
  103. package/.framework/src/instructions/notifications.md +77 -0
  104. package/.framework/src/instructions/observability.md +61 -0
  105. package/.framework/src/instructions/operations/deployment.md +115 -0
  106. package/.framework/src/instructions/operations/migrations.md +125 -0
  107. package/.framework/src/instructions/operations/security-checklist.md +170 -0
  108. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  109. package/.framework/src/instructions/operations/upgrade.md +116 -0
  110. package/.framework/src/instructions/people.md +91 -0
  111. package/.framework/src/instructions/pipeline-executions.md +62 -0
  112. package/.framework/src/instructions/pipelines.md +78 -0
  113. package/.framework/src/instructions/prompts.md +95 -0
  114. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  115. package/.framework/src/instructions/reference/api/auth.md +113 -0
  116. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  117. package/.framework/src/instructions/reference/app-manifest.md +184 -0
  118. package/.framework/src/instructions/reference/cli.md +124 -0
  119. package/.framework/src/instructions/reference/database.md +132 -0
  120. package/.framework/src/instructions/reference/design-schema.md +280 -0
  121. package/.framework/src/instructions/reference/env-vars.md +133 -0
  122. package/.framework/src/instructions/reference/frontend-sdk.md +207 -0
  123. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  124. package/.framework/src/instructions/reference/functions/admin-data.md +197 -0
  125. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  126. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  127. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  128. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  129. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  130. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  131. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  132. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  133. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  134. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  135. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  136. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  137. package/.framework/src/instructions/reference/functions/index.md +103 -0
  138. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  139. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  140. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  141. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  142. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  143. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  144. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  145. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  146. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  147. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  148. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  149. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  150. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  151. package/.framework/src/instructions/reference/functions/security.md +140 -0
  152. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  153. package/.framework/src/instructions/reference/functions/system.md +106 -0
  154. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  155. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  156. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  157. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  158. package/.framework/src/instructions/reference/functions/types.md +146 -0
  159. package/.framework/src/instructions/reference/seeds.md +126 -0
  160. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  161. package/.framework/src/instructions/reference/spine-config.md +114 -0
  162. package/.framework/src/instructions/roles.md +80 -0
  163. package/.framework/src/instructions/runtime-collaboration.md +106 -0
  164. package/.framework/src/instructions/runtime-detail.md +70 -0
  165. package/.framework/src/instructions/runtime-list.md +77 -0
  166. package/.framework/src/instructions/security.md +67 -0
  167. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  168. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  169. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  170. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  171. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  172. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  173. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  174. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  175. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  176. package/.framework/src/instructions/tasks/document-your-app.md +117 -0
  177. package/.framework/src/instructions/tasks/package-and-install-an-app.md +136 -0
  178. package/.framework/src/instructions/tasks/register-an-app.md +137 -0
  179. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  180. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  181. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  182. package/.framework/src/instructions/testing.md +59 -0
  183. package/.framework/src/instructions/timers.md +84 -0
  184. package/.framework/src/instructions/triggers.md +69 -0
  185. package/.framework/src/instructions/types.md +93 -0
  186. package/.framework/src/lib/app-mount.ts +88 -0
  187. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  188. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  189. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  190. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  191. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  192. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  193. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  194. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  195. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  196. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  197. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  198. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  199. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  200. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  201. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  202. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  203. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  204. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  205. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  206. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  207. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  208. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  209. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  210. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  211. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  212. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  213. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  214. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  215. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  216. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  217. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  218. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  219. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  220. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  221. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  222. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  223. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  224. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  225. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  226. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  227. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  228. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  229. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  230. package/.framework/src/pages/auth/LoginPage.tsx +3 -2
  231. package/.framework/src/pages/auth/RegisterPage.tsx +16 -8
  232. package/.framework/templates/agent/AGENTS.md +167 -0
  233. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  234. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  235. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  236. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  237. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +79 -0
  238. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  239. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +79 -0
  240. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +71 -0
  241. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +72 -0
  242. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  243. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  244. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  245. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  246. package/.framework/templates/project.json +4 -1
  247. package/.framework/tests/unit/account-scoping-gates.test.ts +394 -0
  248. package/.framework/tests/unit/agent-files.test.ts +355 -0
  249. package/.framework/tests/unit/agent-runner.test.ts +479 -44
  250. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  251. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  252. package/.framework/tests/unit/app-mount.test.ts +80 -0
  253. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  254. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  255. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  256. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  257. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  258. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  259. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  260. package/.framework/tests/unit/security-definer-grants.test.ts +154 -0
  261. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  262. package/.framework/tests/unit/system-cron.test.ts +240 -0
  263. package/.framework/tests/unit/timers.test.ts +159 -0
  264. package/README.md +125 -166
  265. package/config/tsconfig.cli.json +2 -2
  266. package/package.json +15 -4
  267. package/templates/project/package-lock.json +2 -2
  268. package/.framework/docs/admin-routes.md +0 -76
  269. package/.framework/docs/api-docs-progress.md +0 -38
  270. package/.framework/docs/api-governance.md +0 -146
  271. package/.framework/docs/api-testing-results.md +0 -212
  272. package/.framework/docs/assembly-launch-guide.md +0 -138
  273. package/.framework/docs/audit-results.md +0 -590
  274. package/.framework/docs/db-api-inventory.md +0 -95
  275. package/.framework/docs/gap-remediation-backlog.md +0 -103
  276. package/.framework/docs/ui-adoption-verification.md +0 -111
  277. package/.framework/docs/ui-api-coverage.md +0 -84
  278. package/.framework/docs/v2-compatibility-audit.md +0 -228
  279. package/.framework/mcp/index.ts +0 -133
  280. package/.framework/mcp/load-env.ts +0 -60
  281. package/.framework/mcp/shared/app-manifest.ts +0 -275
  282. package/.framework/mcp/shared/db.ts +0 -477
  283. package/.framework/mcp/shared/kb.ts +0 -118
  284. package/.framework/mcp/tools/architect.ts +0 -431
  285. package/.framework/mcp/tools/cil/context.ts +0 -345
  286. package/.framework/mcp/tools/cil/graph.ts +0 -242
  287. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  288. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  289. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  290. package/.framework/mcp/tools/cil/project.ts +0 -791
  291. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  292. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  293. package/.framework/mcp/tools/cil/search.ts +0 -97
  294. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  295. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  296. package/.framework/mcp/tools/kb/delete.ts +0 -139
  297. package/.framework/mcp/tools/kb/index.ts +0 -52
  298. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  299. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  300. package/.framework/mcp/tools/kb/publish.ts +0 -100
  301. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  302. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  303. package/.framework/mcp/tools/kb/search.ts +0 -101
  304. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  305. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: Add an integration
3
+ audience: developers, agents
4
+ kind: task
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/integrations.ts
9
+ - .framework/functions/integration-outbound.ts
10
+ - .framework/functions/_shared/resolve-credentials.ts
11
+ - .framework/functions/_shared/permissions.ts
12
+ - .framework/migrations/004_core_rpc_and_broker.sql
13
+ ---
14
+
15
+ # Add an integration
16
+
17
+ ## Goal
18
+
19
+ Create a `public.integrations` row — the config + credentials handle for
20
+ outbound HTTP calls (`integration-outbound`) and inbound webhook routing
21
+ (`integration-routes`). See
22
+ [Integrations and webhooks](../concepts/integrations-and-webhooks.md) for
23
+ the full model.
24
+
25
+ ## Prerequisites
26
+
27
+ - A `system_admin` login — the integrations function enforces it
28
+ (`.framework/functions/integrations.ts`, `isSystemAdmin` signal).
29
+
30
+ ## Steps
31
+
32
+ 1. **Choose the integration shape.**
33
+
34
+ - `integration_type`: semantic category — `webhook`, `oauth`, `api`,
35
+ `scheduled_sync` (column comment,
36
+ `.framework/migrations/004_core_rpc_and_broker.sql`).
37
+ - `provider`: the vendor name — `stripe`, `openai`, `generic`, …
38
+ (used as the fallback inbound adapter name).
39
+ - `slug`: unique per `(app_id, account_id)` —
40
+ `integrations_app_account_slug_key`
41
+ (`.framework/migrations/004_core_rpc_and_broker.sql`).
42
+
43
+ 2. **Configure `config` and `credentials`.**
44
+
45
+ - `config.outbound_adapter` selects the adapter module for outbound
46
+ calls — built-ins: `adapters/openai-outbound`,
47
+ `adapters/generic-webhook-outbound`, `adapters/resend-outbound`
48
+ (`.framework/functions/integration-outbound.ts`). `action_config.adapter`
49
+ can override it per call.
50
+ - `credentials` is a map `{ field: { source, value } }`:
51
+ `source: "value"` stores a literal secret; `source: "env_var"` resolves
52
+ `value` as a `process.env` name server-side at call time
53
+ (`.framework/functions/_shared/resolve-credentials.ts`).
54
+
55
+ ```json
56
+ {
57
+ "slug": "acme-api", "name": "Acme API",
58
+ "integration_type": "api", "provider": "acme",
59
+ "config": {
60
+ "base_url": "https://api.acme.test",
61
+ "outbound_adapter": "adapters/generic-webhook-outbound"
62
+ },
63
+ "credentials": {
64
+ "api_token": { "source": "env_var", "value": "ACME_API_TOKEN" }
65
+ }
66
+ }
67
+ ```
68
+
69
+ `is_configured` is recomputed on every save — `true` only when every
70
+ credential entry is present and non-empty
71
+ (`.framework/functions/integrations.ts`).
72
+
73
+ 3. **Create the integration.** Admin UI → **Configs → Integrations → New**,
74
+ or `POST /api/integrations` — `name`, `integration_type`, and `provider`
75
+ required (`.framework/functions/integrations.ts`).
76
+
77
+ 4. **Call it outbound.** `POST /api/integration-outbound` (or a pipeline
78
+ `http_request`/webhook stage) with `{ integration_slug, action_config }`.
79
+ `action_config` carries `endpoint`/`url`, `body`, `credential_field`
80
+ (default `api_token`), `action_slug`, `max_attempts`, and optional
81
+ `adapter` (`.framework/functions/integration-outbound.ts`).
82
+ Deliveries land in `webhook_deliveries` with retry bookkeeping — see
83
+ [integration-outbound reference](../reference/functions/integration-outbound.md).
84
+
85
+ 5. **For inbound**, point senders at `integration-routes` and pick an
86
+ adapter — see [Add a webhook](./add-a-webhook.md).
87
+
88
+ ## Verify
89
+
90
+ - `GET /api/integrations?action=list` — `is_configured: true` once every
91
+ credential resolves.
92
+ - A `webhook_deliveries` row is written per outbound call with
93
+ `response_status` and retry fields.
94
+
95
+ ## Common mistakes
96
+
97
+ - **An empty or absent credential means "no auth", not an error.**
98
+ `execute` skips resolution for an absent entry or an empty
99
+ `source: "value"` literal and the call goes out without a credential —
100
+ the stock `generic-http` seed (`{"api_token":{"source":"value","value":""}}`,
101
+ `.framework/migrations/004_core_rpc_and_broker.sql`) therefore works
102
+ unauthenticated out of the box. Whether a credential is required is the
103
+ *adapter's* call: `generic-webhook-outbound` only attaches
104
+ `Authorization: Bearer` when a credential resolved, while
105
+ `openai-outbound`/`resend-outbound` throw in `buildRequest`. What still
106
+ fails hard: an `env_var` entry naming an unset variable
107
+ (`credential_env_missing`).
108
+ - **Expecting credentials in reads.** Integrations are a protected
109
+ second-surface table — non-system-admin/machine reads are sanitized to
110
+ minimal fields; secrets are only resolved server-side
111
+ (`.framework/functions/_shared/permissions.ts`).
112
+ - **`env_var` pointing at a missing env** — `resolveCredential` throws
113
+ before the request is sent and emits `integration.credential_*` audit
114
+ rows.
115
+ - **Slug not unique within `(app_id, account_id)`** — the DB unique
116
+ constraint rejects it; account-scoped and global rows share the key.
117
+
118
+ ## Reference
119
+
120
+ - [integrations entity page](../integrations.md) ·
121
+ [integrations function reference](../reference/functions/integrations.md) ·
122
+ [integration-outbound reference](../reference/functions/integration-outbound.md) ·
123
+ [integration-routes reference](../reference/functions/integration-routes.md)
124
+ - [Integrations and webhooks](../concepts/integrations-and-webhooks.md)
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: Build a custom page
3
+ audience: developers, agents
4
+ kind: task
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/src/components/CustomAppLoader.tsx
9
+ - .framework/src/components/AppWrapper.tsx
10
+ - .framework/src/components/app-shell/GenericAppShell.tsx
11
+ - .framework/src/lib/api.ts
12
+ - .framework/cli/commands/create-app.ts
13
+ - scripts/assemble.sh
14
+ ---
15
+
16
+ # Build a custom page
17
+
18
+ ## Goal
19
+
20
+ Ship a React page inside a custom app (`renderer: 'custom'`) instead of the
21
+ generic list/detail renderer. See
22
+ [Apps and renderers](../concepts/apps-and-renderers.md) for the model.
23
+
24
+ ## When to go custom
25
+
26
+ Stay on `renderer: 'generic'` when the app is CRUD over typed records — the
27
+ generic renderer gives you list/detail/create/sort/permission-aware editing
28
+ for free from `views.default_list` / `views.default_detail`. Go custom for
29
+ dashboards, wizards, bespoke layouts, or anything not expressible as
30
+ schema-driven list/detail — `GenericAppShell` only generates routes for
31
+ nav items carrying `type_slug`
32
+ (`.framework/src/components/app-shell/GenericAppShell.tsx`).
33
+
34
+ ## Steps
35
+
36
+ 1. **Scaffold the app** (or reuse one):
37
+
38
+ ```bash
39
+ npx spine-framework create-app <slug> --role <role-slug>
40
+ ```
41
+
42
+ Writes `custom/apps/<slug>/` with `manifest.json`, `index.tsx`, and
43
+ `components/`, and inserts the `apps` row with `renderer: 'custom'`
44
+ (`.framework/cli/commands/create-app.ts`). Pass `--role` a slug
45
+ that exists in `roles` — the default `member` is seeded by
46
+ `073_seed_member_roles.sql`.
47
+
48
+ 2. **Add pages under `custom/apps/<slug>/pages/`.** `CustomAppLoader`
49
+ lazy-imports only `custom/apps/<slug>/index.tsx`
50
+ (`import.meta.glob('../../../custom/apps/*/index.tsx')`,
51
+ `.framework/src/components/CustomAppLoader.tsx`) — `index.tsx`
52
+ default-exports the component (usually its own `<Routes>`) and imports
53
+ the page components itself.
54
+
55
+ ```tsx
56
+ // custom/apps/<slug>/pages/DashboardPage.tsx
57
+ export default function DashboardPage() { /* ... */ }
58
+ ```
59
+
60
+ 3. **Set the app row.** `renderer: 'custom'`, `route_prefix: '/<slug>'`,
61
+ `min_role` / `required_roles`, `nav_items` for the sidebar. `AppWrapper`
62
+ enforces auth + role before mounting your app
63
+ (`.framework/src/components/AppWrapper.tsx`).
64
+
65
+ 4. **Fetch data through `/api/admin-data`** — never write a bespoke CRUD
66
+ function:
67
+
68
+ ```ts
69
+ import { apiFetch } from '@core/lib/api'
70
+
71
+ const res = await apiFetch(
72
+ '/api/admin-data?action=list&entity=items&type_slug=ticket&limit=50')
73
+ const { data } = await res.json()
74
+ ```
75
+
76
+ `apiFetch` attaches the Supabase Bearer token and the `X-Account-Id`
77
+ header (`.framework/src/lib/api.ts`). Find-or-create related records
78
+ (e.g. a thread for a work item) through the same endpoint — see
79
+ [Build your first app — Step 4](../guide/04-first-app.md) for the full
80
+ call shapes.
81
+
82
+ 5. **Rebuild and run.** `npm run assemble` (or `npm run dev` / `dev:netlify`,
83
+ which run assembly automatically). Custom functions go in
84
+ `custom/functions/` prefixed `custom_` — assembly hard-fails on a
85
+ basename collision with a framework function
86
+ (`scripts/assemble-functions.sh`).
87
+
88
+ ## Verify
89
+
90
+ - Navigate to `/<slug>` — `CustomAppLoader` resolves your `index.tsx`
91
+ (a missing entry shows the `App "<slug>" not found` error naming the
92
+ expected path).
93
+ - `GET /api/apps?action=get&slug=<slug>` shows `renderer: 'custom'` and the
94
+ right `route_prefix`.
95
+ - `npm run typecheck` compiles the page.
96
+
97
+ ## Common mistakes
98
+
99
+ - **A bespoke CRUD endpoint.** Custom Netlify functions that
100
+ create/update/list runtime records bypass `admin-data`'s permission,
101
+ type-resolution, audit, and trigger logic — call `/api/admin-data` from
102
+ the client instead (see [Write a function](./write-a-function.md) for the
103
+ cases where a custom function *is* allowed).
104
+ - **Forgetting `index.tsx` is the only entry.** New pages in `pages/` are
105
+ invisible until `index.tsx` routes to them.
106
+ - **`type_slug` nav items on a custom app** — they only do something under
107
+ the generic renderer; use plain `path` nav items and route them yourself.
108
+ - **Editing before re-assembling** — `CustomAppLoader`'s glob is evaluated
109
+ at build time; new files need an assemble/dev restart.
110
+
111
+ ## Reference
112
+
113
+ - [Apps and renderers](../concepts/apps-and-renderers.md) ·
114
+ [Runtime records](../concepts/runtime-records.md)
115
+ - [App manifest reference](../reference/app-manifest.md) ·
116
+ [admin-data function reference](../reference/functions/admin-data.md)
117
+ - [Build your first app](../guide/04-first-app.md) ·
118
+ [Anatomy of an installed project](../guide/02-project-map.md)
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Document your app
3
+ audience: developers, agents
4
+ kind: task
5
+ verified_against: b3b3ae0
6
+ verified_on: 2026-11-01
7
+ source_files:
8
+ - .framework/cli/commands/install-app.ts
9
+ - .framework/cli/commands/validate-app.ts
10
+ - .framework/src/instructions/internal/docs-contract.md
11
+ - .framework/templates/agent/AGENTS.md
12
+ ---
13
+
14
+ # Document your app
15
+
16
+ ## Goal
17
+
18
+ Give your app the same agent-legible documentation the framework ships —
19
+ so a cold agent (or a future teammate) can install it, understand it, and
20
+ work in it without reading every file. The contract: a small instruction
21
+ book inside the app plus namespaced Devin skills, kept in sync by the same
22
+ capture rule as the core book.
23
+
24
+ ## Steps
25
+
26
+ 1. **Create the app's instruction book** at
27
+ `custom/apps/<slug>/src/instructions/` — "book-lite": the same layer
28
+ structure, shrunk to app scale:
29
+
30
+ ```
31
+ src/instructions/
32
+ ├── README.md # what the app is + the doc map (kind: index)
33
+ ├── entities/ # one page per seeded type (or per type family)
34
+ │ └── ticketing.md
35
+ ├── tasks/ # the jobs someone does in/with this app
36
+ │ └── install.md
37
+ └── reference/
38
+ └── functions.md # the app's custom_* functions
39
+ ```
40
+
41
+ Every page carries the same frontmatter as the core book — `title`,
42
+ `audience`, `kind`, `verified_against`, `verified_on`, and
43
+ `source_files` pointing at **the app's own** functions/seeds/pages. That
44
+ frontmatter is what makes your docs drift-checkable later.
45
+
46
+ 2. **Author skills where they live.** App skills go in
47
+ `custom/.devin/skills/<prefix>_<kebab-name>/SKILL.md` — the discovered,
48
+ invocable location. Directory name = SKILL.md `name:` field = the skill's
49
+ identity. The `<prefix>_` namespace keeps your skills separate from
50
+ framework `spine-*` skills and makes uninstall deterministic.
51
+
52
+ 3. **Declare the prefix in `manifest.json`:**
53
+
54
+ ```json
55
+ "agent": { "skill_prefix": "ao" }
56
+ ```
57
+
58
+ `^[a-z][a-z0-9]{0,7}$`, unique across installed apps (the installer
59
+ checks). Convention: initials of the slug — `answer-once` → `ao`,
60
+ `demo` → `d`.
61
+
62
+ 4. **Stage assets for packaging** under `agent/` in the app directory —
63
+ the installer maps them to their destinations:
64
+
65
+ | Package path | Installed to |
66
+ |---|---|
67
+ | `agent/skills/<prefix>_*/` | `custom/.devin/skills/<prefix>_*/` |
68
+ | `agent/instructions/**` | `custom/apps/<slug>/src/instructions/` |
69
+
70
+ The package's `files` array in `package.json` must list `agent/`
71
+ (npm only packs declared entries) — plus everything else the installer
72
+ copies (`seed/`, `standalone.config.json`, `docs/`, …).
73
+
74
+ For in-project development you edit the live copies directly
75
+ (`custom/.devin/skills/`, `custom/apps/<slug>/src/instructions/`); copy
76
+ them into `agent/` when packaging. `validate-app` warns if the staged
77
+ and live copies drift.
78
+
79
+ 5. **Validate** with `npx spine-framework validate-app <dir>` — checks the
80
+ `agent` block, prefix rules, and SKILL.md name/dir equality.
81
+
82
+ ## Verify
83
+
84
+ - `custom/.devin/skills/<prefix>_*/SKILL.md` files are invocable in a fresh
85
+ agent session.
86
+ - `custom/apps/<slug>/src/instructions/README.md` exists and links the
87
+ rest.
88
+ - A fresh agent session asked "work in this app" finds the book via the
89
+ AGENTS.md pointer line without being told the path.
90
+
91
+ ## Common mistakes
92
+
93
+ - **Skills inside the app dir** — `custom/apps/<slug>/.devin/` is *not*
94
+ discovered by agent tooling; `custom/.devin/skills/` is. Authored in the
95
+ app dir they become docs, not skills.
96
+ - **Missing `agent.skill_prefix`** — the installer fails closed when
97
+ `agent/skills/` exists without it; declare it even with one skill.
98
+ - **Prefix collisions** — two installed apps sharing a prefix fails at
99
+ install. Keep prefixes short and slug-derived.
100
+ - **`agent/` missing from `files`** — the tarball silently drops your
101
+ staged assets and the installer has nothing to sync. List `agent/` (and
102
+ `seed/`, `standalone.config.json`, …) in `package.json`'s `files`.
103
+ - **`src/` for code** — `src/` inside an app is *instructions-only*; the
104
+ loader only globs `custom/apps/*/index.tsx`. App code stays in
105
+ `pages/`, `components/`, `hooks/`, `utils/`, `lib/`.
106
+ - **Docs written once and abandoned** — the capture contract applies to
107
+ your app too: every change to the app updates its `src/instructions/`
108
+ in the same commit.
109
+
110
+ ## Reference
111
+
112
+ - [Docs contract](../internal/docs-contract.md) — the doctrine and the
113
+ capture rule this recipe implements
114
+ - [App manifest reference](../reference/app-manifest.md) — the `agent`
115
+ block
116
+ - [Package and install an app](package-and-install-an-app.md) — the
117
+ install/update/uninstall mechanics
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: Package and install an app
3
+ audience: developers, agents
4
+ kind: task
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/cli/commands/install-app.ts
9
+ - .framework/cli/commands/validate-app.ts
10
+ - .framework/functions/_shared/app-manifest.ts
11
+ - .framework/cli/commands/migrate.ts
12
+ ---
13
+
14
+ # Package and install an app
15
+
16
+ ## Goal
17
+
18
+ Package a `custom/apps/<slug>/` directory as an npm package and install it
19
+ into a Spine project — files copied, `apps` row upserted, migrations run,
20
+ seeds applied. Field-level manifest semantics are in
21
+ [App manifest reference](../reference/app-manifest.md) and
22
+ [Seeds reference](../reference/seeds.md).
23
+
24
+ ## Steps
25
+
26
+ 1. **Author `manifest.json`.** Required fields the installer validates:
27
+ `slug` (must equal the directory name and match
28
+ `^[a-z][a-z0-9-]*$`), plus `name`, `route_prefix`, `renderer`,
29
+ `min_role` / `required_roles`, `nav_items`, `config`, `registration`.
30
+ See [App manifest reference](../reference/app-manifest.md) for every
31
+ field.
32
+
33
+ 2. **Author seed files.** The `seed` block is a list of
34
+ `{ file, table, conflict, inject_app_id?, ignore_duplicates?, permissions_patch? }`
35
+ entries, each pointing at a JSON file of rows
36
+ (`.framework/cli/commands/install-app.ts`):
37
+
38
+ - `conflict` — the `onConflict` column list for the upsert. Types use
39
+ `conflict: "kind,slug"` with `inject_app_id: false` — types are
40
+ **global**, not duplicated per install.
41
+ - `inject_app_id` — stamps `app_id` on each seeded row (default `true`);
42
+ set `false` for global tables like `types`.
43
+ - `ignore_duplicates` — `ignoreDuplicates` on the upsert; `types` rows
44
+ always get it (`.framework/cli/commands/install-app.ts`).
45
+ - `permissions_patch` — a types-table entry that merges
46
+ `record_permissions` into existing design schemas instead of
47
+ overwriting; ranks just after `types` in apply order.
48
+ - If no `seed` array is declared, the conventional files
49
+ `seed/roles.json`, `seed/types.json`, `seed/link-types.json` are used
50
+ (`.framework/cli/commands/install-app.ts`).
51
+
52
+ Seeds apply in dependency order — `roles`, `types`, `link_types`,
53
+ `actions`, `ai_agents`, `prompt_configs`, `pipelines`, `triggers` —
54
+ regardless of declaration order
55
+ (`.framework/cli/commands/install-app.ts`). Pipeline seeds are
56
+ validated against `public.actions` stage slugs, and trigger seeds
57
+ resolve `pipeline_slug` → `pipeline_id`.
58
+
59
+ 3. **Package it.** Ship the app directory as an npm package — declare
60
+ every shippable entry in `package.json`'s `files` array (npm packs
61
+ nothing else). Include `index.tsx`, `manifest.json`, and optionally `pages/`,
62
+ `components/`, `hooks/`, `utils/`, `functions/`, `seed/`, `public/` —
63
+ the installer copies a fixed allowlist (`APP_COPY_ENTRIES` in
64
+ `.framework/cli/commands/install-app.ts`: those dirs plus `lib/`,
65
+ `views/`, `docs/`, `api/`, `installer/`, `mcp/`, `migrations*`,
66
+ `tests/`, `standalone.config.json`, `agent/`). `agent/` is staged —
67
+ `agent/skills/` lands at `custom/.devin/skills/` (requires
68
+ `manifest.agent.skill_prefix`) and `agent/instructions/` lands at
69
+ `custom/apps/<slug>/src/instructions/`; see
70
+ [App manifest reference](../reference/app-manifest.md). Validate locally
71
+ with `npx spine-framework validate-app`.
72
+
73
+ 4. **Install.**
74
+
75
+ ```bash
76
+ npx spine-framework install-app <package>
77
+ ```
78
+
79
+ Flow: `npm install <package>` → resolve slug → copy files to
80
+ `custom/apps/<slug>/` (a reinstall preserves the local `manifest.json`)
81
+ → `runMigrations` → `seedApp` → sync the `registration` block into
82
+ `apps.config` and `spine.config.json`
83
+ (`.framework/cli/commands/install-app.ts`). Requires
84
+ `SUPABASE_URL` + `SUPABASE_DB_PASSWORD` + `SUPABASE_SERVICE_ROLE_KEY`;
85
+ `--no-seed` skips all DB work.
86
+
87
+ 5. **Update paths.**
88
+
89
+ - `update-app <package>` — `npm install @latest` + copy files + sync
90
+ registration. **It does not re-run migrations or seeds.**
91
+ - `update-db-app <slug>` — upserts the `apps` row from the local
92
+ `manifest.json` and syncs registration; no npm, no file changes.
93
+ - `seed-app <slug>` — re-runs just the seed block for a local app;
94
+ `--reconcile-owned-types` interactively updates `manifest.owned_types`
95
+ slugs whose DB `design_schema` drifted from the seed.
96
+
97
+ 6. **Uninstall.** `uninstall-app <package>` removes
98
+ `custom/apps/<slug>/`, removes the app's skills
99
+ (`custom/.devin/skills/<prefix>_*` when `manifest.agent.skill_prefix`
100
+ is declared), and runs `npm uninstall` — and nothing else. The
101
+ `--keep-data` flag is parsed but never read: seeded rows are **not**
102
+ deleted either way (`.framework/cli/commands/install-app.ts`).
103
+
104
+ ## Verify
105
+
106
+ - `npx spine-framework status` lists the installed app and version.
107
+ - `GET /api/apps?action=get&slug=<slug>` shows the upserted row;
108
+ `migrations status` shows migration state.
109
+ - Seeded rows exist: `GET /api/types?action=list` (or the seeded table's
110
+ endpoint) includes the app's types/roles.
111
+
112
+ ## Common mistakes
113
+
114
+ - **Types not global.** Forgetting `inject_app_id: false` on a types seed
115
+ injects `app_id` into global type rows — and `conflict: "kind,slug"`
116
+ means a second app with the same slug silently skips or errors on
117
+ schema drift. Prefix slugs.
118
+ - **Expecting `uninstall --keep-data` to matter** — it is a no-op; seeded
119
+ rows always survive uninstall.
120
+ - **`update-app` doesn't re-seed** — new seed rows in the package never
121
+ reach the DB; run `seed-app <slug>` or `update-db-app` after.
122
+ - **Seed rows referencing unseeded slugs** — `owned_types`,
123
+ `pipeline_slug`, and `send_notification` recipients are validated
124
+ fail-closed before any DB writes; fix the manifest, don't retry.
125
+ - **`SUPABASE_URL` must be a hosted `https://<ref>.supabase.co` URL** —
126
+ the migrator rejects anything else before connecting
127
+ (`.framework/cli/commands/migrate.ts`).
128
+
129
+ ## Reference
130
+
131
+ - [App manifest reference](../reference/app-manifest.md) ·
132
+ [Seeds reference](../reference/seeds.md) ·
133
+ [CLI reference](../reference/cli.md) ·
134
+ [spine.config.json reference](../reference/spine-config.md)
135
+ - [Apps and renderers](../concepts/apps-and-renderers.md) ·
136
+ [Write a migration](./write-a-migration.md)
@@ -0,0 +1,137 @@
1
+ ---
2
+ title: Register an app
3
+ audience: developers, agents
4
+ kind: task
5
+ verified_against: b3b3ae0
6
+ verified_on: 2026-11-01
7
+ source_files:
8
+ - .framework/cli/commands/create-app.ts
9
+ - .framework/cli/commands/install-app.ts
10
+ - .framework/functions/apps.ts
11
+ - .framework/functions/_shared/app-manifest.ts
12
+ - .framework/src/App.tsx
13
+ - .framework/src/components/app-shell/GenericAppShell.tsx
14
+ ---
15
+
16
+ # Register an app
17
+
18
+ ## Goal
19
+
20
+ Put an app in the `public.apps` registry so it appears in navigation, mounts
21
+ at a route, and renders — either by the generic renderer (schema-driven) or
22
+ by your own code (`custom/apps/<slug>/`).
23
+
24
+ Two decisions up front:
25
+
26
+ 1. **Renderer** — `generic` (platform draws list/detail pages from each
27
+ type's `design_schema.views`) or `custom` (your `index.tsx` draws
28
+ everything). `none` registers data/types with no UI.
29
+ 2. **Mount point (`route_prefix`)** — where the app lives in the URL space:
30
+ `/my-app` (default), `/` (site root), or a nested prefix. See
31
+ [Mounting](../concepts/apps-and-renderers.md#mounting-directory-or-root)
32
+ for the full rules — the short version:
33
+
34
+ | Mount | Effect |
35
+ |---|---|
36
+ | `/my-app` (default) | App owns `/my-app/*`. Safe default. |
37
+ | `/` | App owns `/*`; renders at `/` for non-admins. **One app only** — must render its own not-found for unknown paths. |
38
+ | `/crm/sales` | Nested; longest-prefix-first. Works, rarely needed. |
39
+
40
+ ## Steps
41
+
42
+ ### Option A — scaffold a custom app
43
+
44
+ ```bash
45
+ npx spine-framework create-app <slug>
46
+ ```
47
+
48
+ Creates `custom/apps/<slug>/` (`manifest.json` + `index.tsx` +
49
+ `components/`) and inserts the `apps` row with `renderer: 'custom'` and
50
+ `route_prefix: /<slug>` (`.framework/cli/commands/create-app.ts`).
51
+
52
+ Then edit `custom/apps/<slug>/manifest.json` — set `route_prefix`,
53
+ `required_roles`, `nav_items`, and `routes`. Sync to the DB:
54
+
55
+ ```bash
56
+ npx spine-framework update-db-app <slug>
57
+ ```
58
+
59
+ ### Option B — register a generic app
60
+
61
+ Generic apps are registry rows plus nav items — no app directory is needed
62
+ (the shell renders from type schemas):
63
+
64
+ 1. Ensure every type the app will serve has complete `design_schema` —
65
+ `fields`, `views.default_list`, `views.default_detail` (see
66
+ [Design schema](../concepts/design-schema.md)).
67
+ 2. Create the `apps` row — via the admin UI (Admin → Apps → New) or the API:
68
+
69
+ ```ts
70
+ await apiFetch('/api/apps?action=create', {
71
+ method: 'POST',
72
+ body: JSON.stringify({
73
+ slug: 'taskboard', name: 'Taskboard',
74
+ renderer: 'generic',
75
+ route_prefix: '/taskboard',
76
+ min_role: 'member',
77
+ is_active: true,
78
+ nav_items: [
79
+ { title: 'Tasks', path: '/taskboard/tasks', type_slug: 'task', order: 1 }
80
+ ]
81
+ })
82
+ })
83
+ ```
84
+
85
+ `nav_items` entries carrying `type_slug` (and optional `view`) get
86
+ generated list/detail/new routes. Omit `view` to use `default_list` +
87
+ `default_detail` — a nav item cannot pick list and detail views
88
+ independently ([manifest reference](../reference/app-manifest.md)).
89
+ 3. Log out/in (or refresh) so the registry reloads.
90
+
91
+ ### Option C — install a packaged app
92
+
93
+ ```bash
94
+ npx spine-framework install-app <package>
95
+ ```
96
+
97
+ The package's `manifest.json` declares `route_prefix` and everything else —
98
+ see [Package and install an app](package-and-install-an-app.md).
99
+
100
+ ## Verify
101
+
102
+ - `GET /api/apps?action=get&slug=<slug>` returns the row with your
103
+ `route_prefix`, `renderer`, `nav_items`.
104
+ - Navigating to `<route_prefix>` renders the app (generic: list page;
105
+ custom: your `index.tsx`). For `/`, members land on it directly;
106
+ `system_admin` still lands on `/spine-framework/admin`.
107
+ - `useAppPath`-built links inside the app resolve correctly at the chosen
108
+ mount point.
109
+
110
+ ## Common mistakes
111
+
112
+ - **Two apps mounted at `/`** — both compile to `/*`; ordering silently
113
+ decides which renders. The registry won't stop you; don't do it.
114
+ - **Root app with no internal not-found** — a `/`-mounted `/*` shadows the
115
+ global `*` route; unmatched paths inside the app show blank/error instead
116
+ of your 404. Add `<Route path="*" element={...}>` in the app's routes.
117
+ - **Hardcoded paths** — `navigate('/tasks')` or `to="/tasks"` escapes a
118
+ directory-mounted app. Use `useAppPath()` from `@core/hooks/useAppPath`
119
+ or relative routes.
120
+ - **`nav_items` absolute paths missing the prefix** — `/tasks` navigates
121
+ outside the app and 404s. Use either `/taskboard/tasks` (absolute
122
+ incl. prefix) or `tasks` (relative — the shell prefixes it).
123
+ - **Expecting public access** — `is_public`/`auth_required` exist on the
124
+ manifest but are not wired into the unauthenticated router; every app is
125
+ behind login.
126
+ - **Generic app, thin schema** — `renderer: 'generic'` with types lacking
127
+ `views.default_list`/`default_detail` renders "No schema available."
128
+
129
+ ## Reference
130
+
131
+ - [Apps and renderers](../concepts/apps-and-renderers.md) — mounting model,
132
+ role gating, renderer semantics
133
+ - [Apps](../apps.md) — the registry entity page
134
+ - [App manifest reference](../reference/app-manifest.md) — every manifest
135
+ field incl. `route_prefix`, `nav_items`, `registration`
136
+ - [Build a custom page](build-a-custom-page.md) ·
137
+ [Frontend SDK](../reference/frontend-sdk.md) — `useAppPath`, `apiFetch`