spine-framework 0.3.17 → 0.3.19

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 (270) hide show
  1. package/.framework/cli/bin.mjs +16 -0
  2. package/.framework/cli/commands/create-app.ts +1 -1
  3. package/.framework/cli/commands/dev.ts +215 -33
  4. package/.framework/cli/index.ts +37 -75
  5. package/.framework/functions/_shared/app-manifest.ts +8 -15
  6. package/.framework/functions/_shared/db.ts +11 -15
  7. package/.framework/functions/_shared/index.ts +7 -16
  8. package/.framework/functions/_shared/principal.ts +4 -62
  9. package/.framework/functions/_shared/resolve-ids.ts +6 -6
  10. package/.framework/functions/_shared/webhook-registry.ts +43 -1
  11. package/.framework/functions/admin-funnel.ts +104 -0
  12. package/.framework/functions/admin-install-funnel.ts +225 -0
  13. package/.framework/functions/admin-signals.ts +241 -0
  14. package/.framework/functions/integration-routes.ts +4 -8
  15. package/.framework/migrations/001_schema.sql +406 -0
  16. package/.framework/migrations/002_rls_and_functions.sql +710 -0
  17. package/.framework/migrations/003_system_data.sql +520 -0
  18. package/.framework/migrations/004_enhancements.sql +43 -0
  19. package/.framework/public/_redirects +3 -0
  20. package/.framework/public/spine-logo.jpg +0 -0
  21. package/.framework/public/spine-logo.svg +3 -0
  22. package/{scripts → .framework/scripts}/assemble-frontend.sh +8 -5
  23. package/{scripts → .framework/scripts}/assemble-functions.sh +4 -47
  24. package/{scripts → .framework/scripts}/assemble.sh +2 -8
  25. package/{scripts → .framework/scripts}/boundary-check.sh +5 -9
  26. package/{scripts → .framework/scripts}/check-core-integrity.sh +0 -0
  27. package/.framework/seeds/link_types/account_cli_instances.json +9 -0
  28. package/.framework/seeds/types/funnel_signal_use.json +98 -0
  29. package/.framework/seeds/types/spine_instance.json +109 -0
  30. package/.framework/src/App.tsx +11 -21
  31. package/.framework/src/components/CustomAppLoader.tsx +2 -19
  32. package/.framework/src/components/ui/accordion.tsx +1 -1
  33. package/.framework/src/components/ui/alert-dialog.tsx +1 -1
  34. package/.framework/src/components/ui/aspect-ratio.tsx +1 -1
  35. package/.framework/src/components/ui/avatar.tsx +1 -1
  36. package/.framework/src/components/ui/badge.tsx +2 -2
  37. package/.framework/src/components/ui/breadcrumb.tsx +2 -2
  38. package/.framework/src/components/ui/button-group.tsx +2 -2
  39. package/.framework/src/components/ui/button.tsx +2 -2
  40. package/.framework/src/components/ui/chart.tsx +6 -1
  41. package/.framework/src/components/ui/checkbox.tsx +1 -1
  42. package/.framework/src/components/ui/collapsible.tsx +1 -1
  43. package/.framework/src/components/ui/context-menu.tsx +1 -1
  44. package/.framework/src/components/ui/dialog.tsx +1 -1
  45. package/.framework/src/components/ui/direction.tsx +17 -1
  46. package/.framework/src/components/ui/dropdown-menu.tsx +1 -1
  47. package/.framework/src/components/ui/hover-card.tsx +1 -1
  48. package/.framework/src/components/ui/item.tsx +2 -2
  49. package/.framework/src/components/ui/label.tsx +1 -1
  50. package/.framework/src/components/ui/menubar.tsx +1 -1
  51. package/.framework/src/components/ui/navigation-menu.tsx +1 -1
  52. package/.framework/src/components/ui/popover.tsx +1 -1
  53. package/.framework/src/components/ui/progress.tsx +1 -1
  54. package/.framework/src/components/ui/radio-group.tsx +1 -1
  55. package/.framework/src/components/ui/scroll-area.tsx +1 -1
  56. package/.framework/src/components/ui/select.tsx +1 -1
  57. package/.framework/src/components/ui/separator.tsx +1 -1
  58. package/.framework/src/components/ui/sheet.tsx +1 -1
  59. package/.framework/src/components/ui/sidebar.tsx +6 -6
  60. package/.framework/src/components/ui/slider.tsx +1 -1
  61. package/.framework/src/components/ui/switch.tsx +1 -1
  62. package/.framework/src/components/ui/tabs.tsx +1 -1
  63. package/.framework/src/components/ui/toggle-group.tsx +1 -1
  64. package/.framework/src/components/ui/toggle.tsx +1 -1
  65. package/.framework/src/components/ui/tooltip.tsx +1 -1
  66. package/.framework/src/pages/auth/LoginPage.tsx +10 -9
  67. package/LICENSE.md +63 -93
  68. package/README.md +254 -129
  69. package/custom/.devin/skills/install-app/SKILL.md +39 -0
  70. package/custom/.devin/workflows/app-install.yaml +83 -0
  71. package/custom/README.md +79 -0
  72. package/docs/APPS.md +232 -0
  73. package/docs/ARCHITECTURE.md +246 -0
  74. package/docs/INSTALL.md +155 -0
  75. package/docs/README.md +170 -0
  76. package/docs/design-schema-spec.md +1837 -0
  77. package/docs/dev-guides/01-create-an-app.md +183 -0
  78. package/docs/dev-guides/02-create-a-function.md +178 -0
  79. package/docs/dev-guides/03-create-a-component.md +178 -0
  80. package/docs/dev-guides/04-webhook-handlers.md +129 -0
  81. package/docs/dev-guides/05-testing.md +168 -0
  82. package/docs/dev-guides/README.md +40 -0
  83. package/docs/npm-packages-plan.md +288 -0
  84. package/docs/spine-core--api-reference.md +822 -0
  85. package/package.json +79 -150
  86. package/.framework/cli/bin.cjs +0 -14
  87. package/.framework/cli/commands/claim.ts +0 -430
  88. package/.framework/cli/commands/init.ts +0 -219
  89. package/.framework/cli/commands/install-app.ts +0 -685
  90. package/.framework/cli/commands/migrate.ts +0 -178
  91. package/.framework/cli/commands/publish.ts +0 -191
  92. package/.framework/cli/commands/status.ts +0 -268
  93. package/.framework/cli/commands/uninstall-app.ts +0 -166
  94. package/.framework/cli/commands/update-app.ts +0 -397
  95. package/.framework/cli/welcome.cjs +0 -45
  96. package/.framework/config/signal-client.ts +0 -147
  97. package/.framework/config/signals.ts +0 -70
  98. package/.framework/docs/STABILITY.md +0 -52
  99. package/.framework/docs/examples/custom-app/README.md +0 -77
  100. package/.framework/docs/examples/custom-function/README.md +0 -27
  101. package/.framework/docs/examples/custom-function/handler.ts +0 -48
  102. package/.framework/docs/examples/custom-webhook/README.md +0 -68
  103. package/.framework/functions/handler-registry.ts +0 -22
  104. package/.framework/functions/signal.ts +0 -168
  105. package/.framework/migrations/000_foundation.sql +0 -1302
  106. package/.framework/migrations/001_seed.sql +0 -163
  107. package/.framework/migrations/002_seed_constraints.sql +0 -19
  108. package/.framework/migrations/003_auth_user_trigger.sql +0 -67
  109. package/.framework/migrations/004_app_version_tracking.sql +0 -8
  110. package/.framework/migrations/005_pipelines_slug_stages.sql +0 -21
  111. package/.framework/src/pages/auth/RegisterPage.tsx +0 -163
  112. package/CHANGELOG.md +0 -71
  113. package/STRUCTURE.md +0 -150
  114. package/bin/spine-framework.cjs +0 -62
  115. package/bin/welcome.cjs +0 -58
  116. package/bin/ws-shim.ts +0 -10
  117. package/config/components.json +0 -25
  118. package/config/deno.lock +0 -108
  119. package/config/package-lock.json +0 -17183
  120. package/config/postcss.config.cjs +0 -10
  121. package/config/tailwind.config.cjs +0 -78
  122. package/config/tsconfig.build.json +0 -32
  123. package/config/tsconfig.cli.json +0 -18
  124. package/config/tsconfig.json +0 -41
  125. package/config/tsconfig.node.json +0 -17
  126. package/config/tsconfig.node.tsbuildinfo +0 -1
  127. package/config/tsconfig.tsbuildinfo +0 -1
  128. package/config/typedoc.json +0 -16
  129. package/config/vite.config.d.ts +0 -2
  130. package/config/vite.config.ts +0 -71
  131. package/dist/cli/commands/agents.d.ts +0 -39
  132. package/dist/cli/commands/agents.d.ts.map +0 -1
  133. package/dist/cli/commands/auth.d.ts +0 -36
  134. package/dist/cli/commands/auth.d.ts.map +0 -1
  135. package/dist/cli/commands/claim.d.ts +0 -38
  136. package/dist/cli/commands/claim.d.ts.map +0 -1
  137. package/dist/cli/commands/create-app.d.ts +0 -23
  138. package/dist/cli/commands/create-app.d.ts.map +0 -1
  139. package/dist/cli/commands/dev.d.ts +0 -24
  140. package/dist/cli/commands/dev.d.ts.map +0 -1
  141. package/dist/cli/commands/doctor.d.ts +0 -42
  142. package/dist/cli/commands/doctor.d.ts.map +0 -1
  143. package/dist/cli/commands/generate.d.ts +0 -36
  144. package/dist/cli/commands/generate.d.ts.map +0 -1
  145. package/dist/cli/commands/init.d.ts +0 -20
  146. package/dist/cli/commands/init.d.ts.map +0 -1
  147. package/dist/cli/commands/install-app.d.ts +0 -30
  148. package/dist/cli/commands/install-app.d.ts.map +0 -1
  149. package/dist/cli/commands/items.d.ts +0 -45
  150. package/dist/cli/commands/items.d.ts.map +0 -1
  151. package/dist/cli/commands/migrate.d.ts +0 -21
  152. package/dist/cli/commands/migrate.d.ts.map +0 -1
  153. package/dist/cli/commands/migrations.d.ts +0 -41
  154. package/dist/cli/commands/migrations.d.ts.map +0 -1
  155. package/dist/cli/commands/pipelines.d.ts +0 -40
  156. package/dist/cli/commands/pipelines.d.ts.map +0 -1
  157. package/dist/cli/commands/publish.d.ts +0 -23
  158. package/dist/cli/commands/publish.d.ts.map +0 -1
  159. package/dist/cli/commands/status.d.ts +0 -23
  160. package/dist/cli/commands/status.d.ts.map +0 -1
  161. package/dist/cli/commands/system.d.ts +0 -29
  162. package/dist/cli/commands/system.d.ts.map +0 -1
  163. package/dist/cli/commands/test.d.ts +0 -46
  164. package/dist/cli/commands/test.d.ts.map +0 -1
  165. package/dist/cli/commands/uninstall-app.d.ts +0 -23
  166. package/dist/cli/commands/uninstall-app.d.ts.map +0 -1
  167. package/dist/cli/commands/update-app.d.ts +0 -24
  168. package/dist/cli/commands/update-app.d.ts.map +0 -1
  169. package/dist/cli/context.d.ts +0 -88
  170. package/dist/cli/context.d.ts.map +0 -1
  171. package/dist/cli/env-loader.d.ts +0 -14
  172. package/dist/cli/env-loader.d.ts.map +0 -1
  173. package/dist/cli/index.d.ts +0 -44
  174. package/dist/cli/index.d.ts.map +0 -1
  175. package/dist/config/signals.d.ts +0 -82
  176. package/dist/config/signals.d.ts.map +0 -1
  177. package/dist/functions/_shared/agent-runner.d.ts +0 -156
  178. package/dist/functions/_shared/agent-runner.d.ts.map +0 -1
  179. package/dist/functions/_shared/app-manifest.d.ts +0 -68
  180. package/dist/functions/_shared/app-manifest.d.ts.map +0 -1
  181. package/dist/functions/_shared/audit.d.ts +0 -91
  182. package/dist/functions/_shared/audit.d.ts.map +0 -1
  183. package/dist/functions/_shared/db.d.ts +0 -125
  184. package/dist/functions/_shared/db.d.ts.map +0 -1
  185. package/dist/functions/_shared/index.d.ts +0 -299
  186. package/dist/functions/_shared/index.d.ts.map +0 -1
  187. package/dist/functions/_shared/middleware.d.ts +0 -315
  188. package/dist/functions/_shared/middleware.d.ts.map +0 -1
  189. package/dist/functions/_shared/permissions.d.ts +0 -626
  190. package/dist/functions/_shared/permissions.d.ts.map +0 -1
  191. package/dist/functions/_shared/pipeline-runner.d.ts +0 -124
  192. package/dist/functions/_shared/pipeline-runner.d.ts.map +0 -1
  193. package/dist/functions/_shared/principal.d.ts +0 -284
  194. package/dist/functions/_shared/principal.d.ts.map +0 -1
  195. package/dist/functions/_shared/resolve-ids.d.ts +0 -10
  196. package/dist/functions/_shared/resolve-ids.d.ts.map +0 -1
  197. package/dist/functions/_shared/schema-utils.d.ts +0 -181
  198. package/dist/functions/_shared/schema-utils.d.ts.map +0 -1
  199. package/dist/functions/_shared/testing.d.ts +0 -172
  200. package/dist/functions/_shared/testing.d.ts.map +0 -1
  201. package/dist/functions/_shared/trigger-engine.d.ts +0 -140
  202. package/dist/functions/_shared/trigger-engine.d.ts.map +0 -1
  203. package/dist/functions/_shared/webhook-registration.d.ts +0 -81
  204. package/dist/functions/_shared/webhook-registration.d.ts.map +0 -1
  205. package/dist/functions/_shared/webhook-registry.d.ts +0 -41
  206. package/dist/functions/_shared/webhook-registry.d.ts.map +0 -1
  207. package/dist/functions/account-nodes.d.ts +0 -48
  208. package/dist/functions/account-nodes.d.ts.map +0 -1
  209. package/dist/functions/admin-data.d.ts +0 -178
  210. package/dist/functions/admin-data.d.ts.map +0 -1
  211. package/dist/functions/ai-agents.d.ts +0 -125
  212. package/dist/functions/ai-agents.d.ts.map +0 -1
  213. package/dist/functions/api-keys.d.ts +0 -140
  214. package/dist/functions/api-keys.d.ts.map +0 -1
  215. package/dist/functions/apps.d.ts +0 -163
  216. package/dist/functions/apps.d.ts.map +0 -1
  217. package/dist/functions/auth.d.ts +0 -74
  218. package/dist/functions/auth.d.ts.map +0 -1
  219. package/dist/functions/debug-auth.d.ts +0 -33
  220. package/dist/functions/debug-auth.d.ts.map +0 -1
  221. package/dist/functions/embeddings.d.ts +0 -205
  222. package/dist/functions/embeddings.d.ts.map +0 -1
  223. package/dist/functions/handler-registry.d.ts +0 -22
  224. package/dist/functions/handler-registry.d.ts.map +0 -1
  225. package/dist/functions/integration-routes.d.ts +0 -45
  226. package/dist/functions/integration-routes.d.ts.map +0 -1
  227. package/dist/functions/integrations.d.ts +0 -124
  228. package/dist/functions/integrations.d.ts.map +0 -1
  229. package/dist/functions/item-progress.d.ts +0 -41
  230. package/dist/functions/item-progress.d.ts.map +0 -1
  231. package/dist/functions/logs.d.ts +0 -162
  232. package/dist/functions/logs.d.ts.map +0 -1
  233. package/dist/functions/observability.d.ts +0 -123
  234. package/dist/functions/observability.d.ts.map +0 -1
  235. package/dist/functions/pipeline-executions.d.ts +0 -190
  236. package/dist/functions/pipeline-executions.d.ts.map +0 -1
  237. package/dist/functions/pipelines.d.ts +0 -171
  238. package/dist/functions/pipelines.d.ts.map +0 -1
  239. package/dist/functions/prompt-configs.d.ts +0 -125
  240. package/dist/functions/prompt-configs.d.ts.map +0 -1
  241. package/dist/functions/roles.d.ts +0 -118
  242. package/dist/functions/roles.d.ts.map +0 -1
  243. package/dist/functions/signal.d.ts +0 -73
  244. package/dist/functions/signal.d.ts.map +0 -1
  245. package/dist/functions/system-cron.d.ts +0 -65
  246. package/dist/functions/system-cron.d.ts.map +0 -1
  247. package/dist/functions/system.d.ts +0 -29
  248. package/dist/functions/system.d.ts.map +0 -1
  249. package/dist/functions/tests.d.ts +0 -28
  250. package/dist/functions/tests.d.ts.map +0 -1
  251. package/dist/functions/timers.d.ts +0 -139
  252. package/dist/functions/timers.d.ts.map +0 -1
  253. package/dist/functions/triggers.d.ts +0 -203
  254. package/dist/functions/triggers.d.ts.map +0 -1
  255. package/dist/functions/types.d.ts +0 -151
  256. package/dist/functions/types.d.ts.map +0 -1
  257. package/dist/src/types/types.d.ts +0 -364
  258. package/dist/src/types/types.d.ts.map +0 -1
  259. package/index.html +0 -13
  260. package/netlify.toml +0 -36
  261. package/package-project.json +0 -71
  262. package/scripts/ingest-chunks.cjs +0 -202
  263. package/scripts/kb-chunk-parser.cjs +0 -312
  264. package/scripts/kb-chunk-parser.ts +0 -330
  265. package/scripts/load-test-app-install.ts +0 -484
  266. package/scripts/netlify-dev-wrapper.sh +0 -22
  267. package/vitest.config.ts +0 -45
  268. /package/{scripts → .framework/scripts}/app-install-cli.ts +0 -0
  269. /package/{scripts → .framework/scripts}/build-manifest.sh +0 -0
  270. /package/{scripts → .framework/scripts}/verify-integrity.sh +0 -0
@@ -0,0 +1,1837 @@
1
+ # Spine v2 — Design Schema & Validation Schema Specification
2
+
3
+ **Version:** 1.0 — Generated 2026-04-26
4
+ **Source of truth:** All definitions in this document are derived directly from the live codebase.
5
+ **Primary sources:** `v2-core/src/types/types.ts`, `v2-core/src/components/shared/FieldRenderer.tsx`, `v2-core/functions/_shared/schema-utils.ts`, `v2-core/src/components/runtime/SchemaDetailForm.tsx`, `v2-core/src/components/runtime/DataListPage.tsx`
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ 1. [Overview & Key Concepts](#1-overview--key-concepts)
12
+ 2. [Top-Level Shape](#2-top-level-shape)
13
+ 3. [fields — Field Definitions](#3-fields--field-definitions)
14
+ 4. [views — List & Detail Views](#4-views--list--detail-views)
15
+ 5. [record_permissions](#5-record_permissions)
16
+ 6. [functionality — Reserved](#6-functionality--reserved)
17
+ 7. [Field-Level permissions](#7-field-level-permissions)
18
+ 8. [validation_schema](#8-validation_schema)
19
+ 9. [system Field Flag](#9-system-field-flag)
20
+ 10. [Complete Examples](#10-complete-examples)
21
+ 11. [Rules & Constraints](#11-rules--constraints)
22
+
23
+ ---
24
+
25
+ ## 1. Overview & Key Concepts
26
+
27
+ ### What is `design_schema`?
28
+
29
+ `design_schema` is a JSONB column on the `types` table. It is the complete configuration contract for a type — defining its fields, how those fields are rendered in list and detail views, who can access which fields, and what automation is bound to the type.
30
+
31
+ ### What is `validation_schema`?
32
+
33
+ `validation_schema` is a separate JSONB column on the `types` table. It is **auto-generated** from `design_schema` by `generateValidationSchema()` in `schema-utils.ts`. It strips display/permission information and retains only structural validation rules. The backend uses it to sanitize and validate incoming record data.
34
+
35
+ > **Do not hand-author `validation_schema`.** It is always derived from `design_schema`.
36
+
37
+ ### `data_type` vs `display_type`
38
+
39
+ These are two different concepts used in two different places:
40
+
41
+ | Concept | Where it lives | What it controls |
42
+ |---|---|---|
43
+ | `data_type` | `design_schema.fields[key].data_type` | Storage format, backend sanitization, default form input widget |
44
+ | `display_type` | `design_schema.views[viewKey].fields[key].display_type` | View-level render override — changes how the field is presented without changing the data contract |
45
+
46
+ A field with `data_type: "text"` can be rendered as `display_type: "textarea"` in a specific view. The data contract doesn't change.
47
+
48
+ ### `system` vs custom fields
49
+
50
+ | `system: true` | `system: false` / absent |
51
+ |---|---|
52
+ | Value lives at `record[fieldName]` — a real DB column | Value lives at `record.data[fieldName]` — stored in JSONB |
53
+ | Set by Spine core — not writable by non-`system-admin` roles | Defined by type author — writable per field permissions |
54
+ | Examples: `id`, `title`, `is_active`, `created_at` | Examples: `priority`, `category`, `content` |
55
+
56
+ Source: `SchemaFields.tsx` line 45:
57
+ ```ts
58
+ field.system ? data[name] : (data.data?.[name] ?? data[name])
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 2. Top-Level Shape
64
+
65
+ ```json
66
+ {
67
+ "fields": { },
68
+ "views": { },
69
+ "record_permissions": { },
70
+ "functionality": null
71
+ }
72
+ ```
73
+
74
+ | Key | Type | Required | Description |
75
+ |---|---|---|---|
76
+ | `fields` | `Record<string, FieldDefinition>` | ✅ | All field definitions, keyed by field name |
77
+ | `views` | `Record<string, ListView \| DetailView>` | ✅ | Named view configurations |
78
+ | `record_permissions` | `Record<string, string[]>` | ✅ | Record-level CRUD permissions per role |
79
+ | `functionality` | `FunctionalityBindings \| null` | — | Pipeline, agent, embedding, integration bindings |
80
+
81
+ ---
82
+
83
+ ## 3. `fields` — Field Definitions
84
+
85
+ The key is the **field name** in `snake_case`. This key is the canonical identifier used everywhere — in views, validation schemas, API payloads, and permissions.
86
+
87
+ ### 3.1 Field Definition Properties
88
+
89
+ | Property | Type | Required | Notes |
90
+ |---|---|---|---|
91
+ | `data_type` | enum | ✅ | See full list below |
92
+ | `label` | string | ✅ | Human-readable display name |
93
+ | `required` | boolean | ✅ | Drives form validation and backend enforcement |
94
+ | `system` | boolean | — | `true` = DB column; `false`/absent = in `record.data` |
95
+ | `placeholder` | string | — | Input placeholder text — UI hint only |
96
+ | `description` | string | — | Help text rendered below the field |
97
+ | `rows` | number | — | Textarea/JSON editor row height hint |
98
+ | `min` | number | — | Minimum value for number/range fields |
99
+ | `max` | number | — | Maximum value for number/range fields |
100
+ | `step` | number | — | Step increment for number/range fields |
101
+ | `readonly` | boolean | — | Render as read-only regardless of edit mode |
102
+ | `disabled` | boolean | — | Render as disabled |
103
+ | `options` | `string[] \| {value: string, label: string}[]` | — | Required for `select`, `multiselect`, `radio` |
104
+ | `permissions` | `Record<string, string[]>` | ✅ | Field-level read/write per role |
105
+ | `validation` | object | — | Structural validation constraints (see §3.3) |
106
+
107
+ ### 3.2 `data_type` — Full Enum (22 values)
108
+
109
+ These are the exact values accepted by `FieldDefinition.data_type` in `types.ts` and handled by `FieldRenderer.tsx` and `schema-utils.ts`:
110
+
111
+ | `data_type` | Input rendered | Backend sanitization | Notes |
112
+ |---|---|---|---|
113
+ | `text` | `<input type="text">` | HTML-escaped, length-constrained | Default fallback type |
114
+ | `textarea` | `<textarea>` | HTML-escaped, length-constrained | Multi-line text |
115
+ | `rich_text` | `<textarea>` (future: rich editor) | HTML sanitized (safe tags only) | Allows basic HTML formatting |
116
+ | `email` | `<input type="email">` | Lowercased, validated format | Rendered as `mailto:` link in readonly |
117
+ | `phone` | `<input type="tel">` | Stripped to digits + `+` | Pattern validation if provided |
118
+ | `url` | `<input type="url">` | Validated, http/https only | Rendered as external link in readonly |
119
+ | `number` | `<input type="number">` | Parsed to `Number`, min/max enforced | Also used for `currency`, `range` base |
120
+ | `currency` | `<input type="number">` | Rounded to 2 decimal places | Use `validation.currency_code` for display |
121
+ | `range` | `<input type="range">` + value display | Same as `number` | Requires `min` and `max` |
122
+ | `date` | `<input type="date">` | ISO date string `YYYY-MM-DD` | |
123
+ | `datetime` | `<input type="datetime-local">` | ISO datetime string | |
124
+ | `boolean` | Checkbox | Coerced from string/int if needed | Readonly: "Yes" / "No" |
125
+ | `checkbox` | Checkbox | Same as `boolean` | Alias for `boolean` |
126
+ | `select` | `<select>` | Validated against `options` | Requires `options` |
127
+ | `multiselect` | Checkbox group | Array of strings, validated against `options` | Requires `options` |
128
+ | `radio` | Radio group | Validated against `options` | Requires `options` |
129
+ | `color` | Color picker + hex text input | Validated as `#RRGGBB` or `#RGB`, normalized to uppercase 6-digit | |
130
+ | `file` | `<input type="file">` | Filename sanitized, size/type checked | |
131
+ | `image` | `<input type="file">` | Same as `file` + image dimension hints | |
132
+ | `json` | Monospace textarea | Validated as parseable JSON, script injection blocked | Readonly: formatted `<pre>` |
133
+ | `reference` | Text input (future: picker) | UUID format validated | Use `validation.reference_kind` + `reference_type` |
134
+ | `address` | Text inputs per component | Each component sanitized as text | Stored as object: `{street, city, state, postal_code, country}` |
135
+
136
+ ### 3.3 `validation` Sub-Properties
137
+
138
+ | Property | Applies to | Type | Description |
139
+ |---|---|---|---|
140
+ | `minLength` | `text`, `textarea`, `rich_text` | number | Minimum character count |
141
+ | `maxLength` | `text`, `textarea`, `rich_text`, `url` | number | Maximum character count |
142
+ | `pattern` | `text`, `phone` | string | Regex pattern (JavaScript syntax) |
143
+ | `min` | `number`, `currency`, `range`, `date`, `datetime` | number / ISO string | Minimum value or date |
144
+ | `max` | `number`, `currency`, `range`, `date`, `datetime` | number / ISO string | Maximum value or date |
145
+ | `step` | `number`, `range` | number | Step increment |
146
+ | `integer` | `number` | boolean | Enforce integer-only values |
147
+ | `precision` | `currency` | number | Decimal precision |
148
+ | `maxSize` | `file`, `image` | number | Max file size in bytes |
149
+ | `allowedTypes` | `file`, `image` | string[] | Allowed MIME types (e.g. `["image/png", "image/jpeg"]`) |
150
+ | `maxWidth` | `image` | number | Max image width in pixels |
151
+ | `maxHeight` | `image` | number | Max image height in pixels |
152
+ | `currency_code` | `currency` | string | ISO 4217 code (e.g. `"USD"`) — used for display formatting |
153
+ | `reference_kind` | `reference` | string | Entity kind to reference (e.g. `"item"`, `"person"`) |
154
+ | `reference_type` | `reference` | string | Type slug to reference (e.g. `"task"`) |
155
+ | `max` (count) | `multiselect` | number | Maximum number of selections allowed |
156
+
157
+ ### 3.4 Field Examples
158
+
159
+ **Minimal field** — `data_type`, `label`, `required` are always required:
160
+ ```json
161
+ {
162
+ "title": {
163
+ "data_type": "text",
164
+ "label": "Title",
165
+ "required": true,
166
+ "permissions": {
167
+ "system-admin": ["read", "write"]
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ **Minimal custom field** — non-system fields always include tenant roles:
174
+ ```json
175
+ {
176
+ "notes": {
177
+ "data_type": "textarea",
178
+ "label": "Notes",
179
+ "required": false,
180
+ "permissions": {
181
+ "system-admin": ["read", "write"],
182
+ "admin": ["read", "write"],
183
+ "member": ["read", "write"],
184
+ "guest": ["read"]
185
+ }
186
+ }
187
+ }
188
+ ```
189
+
190
+ **Maximal field — text:**
191
+ ```json
192
+ {
193
+ "summary": {
194
+ "data_type": "text",
195
+ "label": "Summary",
196
+ "required": false,
197
+ "system": false,
198
+ "placeholder": "Enter a brief summary",
199
+ "description": "A short description shown in list views",
200
+ "readonly": false,
201
+ "disabled": false,
202
+ "validation": {
203
+ "minLength": 10,
204
+ "maxLength": 500,
205
+ "pattern": "^[A-Za-z0-9 .,!?-]+$"
206
+ },
207
+ "permissions": {
208
+ "system-admin": ["read", "write"],
209
+ "admin": ["read", "write"],
210
+ "member": ["read"],
211
+ "guest": []
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ **Select field with options:**
218
+ ```json
219
+ {
220
+ "status": {
221
+ "data_type": "select",
222
+ "label": "Status",
223
+ "required": true,
224
+ "system": false,
225
+ "options": ["todo", "in_progress", "done", "blocked"],
226
+ "permissions": {
227
+ "admin": ["read", "write"],
228
+ "member": ["read", "write"],
229
+ "guest": ["read"]
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ **Select with labeled options:**
236
+ ```json
237
+ {
238
+ "priority": {
239
+ "data_type": "select",
240
+ "label": "Priority",
241
+ "required": true,
242
+ "options": [
243
+ { "value": "low", "label": "Low" },
244
+ { "value": "medium", "label": "Medium" },
245
+ { "value": "high", "label": "High" },
246
+ { "value": "urgent", "label": "Urgent 🔥" }
247
+ ],
248
+ "permissions": {
249
+ "admin": ["read", "write"],
250
+ "member": ["read", "write"]
251
+ }
252
+ }
253
+ }
254
+ ```
255
+
256
+ **Reference field:**
257
+ ```json
258
+ {
259
+ "assignee_id": {
260
+ "data_type": "reference",
261
+ "label": "Assignee",
262
+ "required": false,
263
+ "system": false,
264
+ "validation": {
265
+ "reference_kind": "person",
266
+ "reference_type": "employee"
267
+ },
268
+ "permissions": {
269
+ "admin": ["read", "write"],
270
+ "member": ["read"]
271
+ }
272
+ }
273
+ }
274
+ ```
275
+
276
+ **Number field with constraints:**
277
+ ```json
278
+ {
279
+ "score": {
280
+ "data_type": "range",
281
+ "label": "Score",
282
+ "required": false,
283
+ "min": 0,
284
+ "max": 100,
285
+ "step": 5,
286
+ "validation": {
287
+ "min": 0,
288
+ "max": 100,
289
+ "step": 5,
290
+ "integer": true
291
+ },
292
+ "permissions": {
293
+ "admin": ["read", "write"],
294
+ "member": ["read", "write"]
295
+ }
296
+ }
297
+ }
298
+ ```
299
+
300
+ ---
301
+
302
+ ## 4. `views` — List & Detail Views
303
+
304
+ Views are named configurations that control how fields are presented. The key is the view slug (e.g. `"default_list"`, `"default_detail"`). Multiple views of the same type can exist.
305
+
306
+ ### 4.1 List View
307
+
308
+ Used by `DataListPage` → `DataTable`. Drives table columns, sorting, filtering, and stats.
309
+
310
+ ```ts
311
+ type ListView = {
312
+ type: "list"
313
+ display: "table" | "card" | "board"
314
+ label: string
315
+ fields: Record<string, ViewFieldConfig>
316
+ default_sort?: { field: string; direction: "asc" | "desc" }
317
+ filters?: string[]
318
+ stats?: Array<{ title: string; type: "count" | "filter_count"; icon?: string; color?: string; filter?: Record<string, any> }>
319
+ group_by?: string
320
+ }
321
+ ```
322
+
323
+ | Property | Required | Notes |
324
+ |---|---|---|
325
+ | `type` | ✅ | Must be `"list"` |
326
+ | `display` | ✅ | `"table"` is fully implemented; `"card"` and `"board"` are reserved |
327
+ | `label` | ✅ | Human-readable view name |
328
+ | `fields` | ✅ | Field keys + `ViewFieldConfig` per field |
329
+ | `default_sort` | — | Defaults to `created_at desc` if absent |
330
+ | `filters` | — | Array of field names to expose as filter controls |
331
+ | `stats` | — | Summary stat cards above the table |
332
+ | `group_by` | — | Field name for board grouping (board display only) |
333
+
334
+ **Minimal list view:**
335
+ ```json
336
+ {
337
+ "default_list": {
338
+ "type": "list",
339
+ "display": "table",
340
+ "label": "Default List",
341
+ "fields": {
342
+ "title": { "sortable": true, "display_type": "text" },
343
+ "created_at": { "sortable": true, "display_type": "timestamp" }
344
+ }
345
+ }
346
+ }
347
+ ```
348
+
349
+ **Maximal list view:**
350
+ ```json
351
+ {
352
+ "default_list": {
353
+ "type": "list",
354
+ "display": "table",
355
+ "label": "Tasks",
356
+ "fields": {
357
+ "title": { "sortable": true, "searchable": true, "display_type": "text" },
358
+ "status": { "sortable": true, "display_type": "badge" },
359
+ "priority": { "sortable": true, "display_type": "badge" },
360
+ "due_date": { "sortable": true, "display_type": "timestamp" },
361
+ "is_active": { "sortable": true, "display_type": "boolean" },
362
+ "created_at": { "sortable": true, "display_type": "timestamp" }
363
+ },
364
+ "default_sort": { "field": "due_date", "direction": "asc" },
365
+ "filters": ["status", "priority", "is_active"],
366
+ "stats": [
367
+ { "title": "Total", "type": "count", "icon": "ClipboardIcon", "color": "blue" },
368
+ { "title": "Open", "type": "filter_count", "icon": "ClockIcon", "color": "yellow", "filter": { "status": "todo" } },
369
+ { "title": "Done", "type": "filter_count", "icon": "CheckCircleIcon", "color": "green", "filter": { "status": "done" } }
370
+ ]
371
+ }
372
+ }
373
+ ```
374
+
375
+ ### 4.2 Detail View
376
+
377
+ Used by `SchemaDetailForm` → `SchemaFields` → `FieldRenderer`. Drives the record detail page form/display.
378
+
379
+ ```ts
380
+ type DetailView = {
381
+ type: "detail"
382
+ label: string
383
+ sections: Array<{
384
+ title: string
385
+ fields: Record<string, ViewFieldConfig>
386
+ }>
387
+ }
388
+ ```
389
+
390
+ | Property | Required | Notes |
391
+ |---|---|---|
392
+ | `type` | ✅ | Must be `"detail"` |
393
+ | `label` | ✅ | Human-readable view name |
394
+ | `sections` | ✅ | Array of sections; each has a `title` and `fields` map |
395
+
396
+ Sections render as separate cards on the detail page. Fields within a section render in two-column grid by default.
397
+
398
+ **Minimal detail view:**
399
+ ```json
400
+ {
401
+ "default_detail": {
402
+ "type": "detail",
403
+ "label": "Default Detail",
404
+ "sections": [
405
+ {
406
+ "title": "Details",
407
+ "fields": {
408
+ "title": { "display_type": "text" }
409
+ }
410
+ }
411
+ ]
412
+ }
413
+ }
414
+ ```
415
+
416
+ **Maximal detail view:**
417
+ ```json
418
+ {
419
+ "default_detail": {
420
+ "type": "detail",
421
+ "label": "Task Detail",
422
+ "sections": [
423
+ {
424
+ "title": "Core Info",
425
+ "fields": {
426
+ "title": { "display_type": "input" },
427
+ "status": { "display_type": "select" },
428
+ "priority": { "display_type": "radio" },
429
+ "due_date": { "display_type": "date_picker" }
430
+ }
431
+ },
432
+ {
433
+ "title": "Content",
434
+ "fields": {
435
+ "description": { "display_type": "textarea" },
436
+ "notes": { "display_type": "rich_text" }
437
+ }
438
+ },
439
+ {
440
+ "title": "System",
441
+ "fields": {
442
+ "is_active": { "display_type": "checkbox" },
443
+ "created_at": { "display_type": "timestamp" }
444
+ }
445
+ }
446
+ ]
447
+ }
448
+ }
449
+ ```
450
+
451
+ ### 4.3 `ViewFieldConfig` — `display_type` Enum
452
+
453
+ The `display_type` in a view overrides the default input widget without changing the field's data contract.
454
+
455
+ | `display_type` | Renders as | Notes |
456
+ |---|---|---|
457
+ | `text` | Plain text display | Read-only display in list views |
458
+ | `input` | `<input type="text">` | Editable text in detail |
459
+ | `textarea` | `<textarea>` | Multi-line editable |
460
+ | `rich_text` | Sanitized HTML textarea | Basic formatting |
461
+ | `select` | `<select>` dropdown | Requires field `options` |
462
+ | `multiselect` | Checkbox group | Requires field `options` |
463
+ | `radio` | Radio button group | Requires field `options` |
464
+ | `checkbox` | Single checkbox | Boolean toggle |
465
+ | `switch` | Toggle switch | Alias for `checkbox` |
466
+ | `date_picker` | `<input type="date">` | |
467
+ | `datetime_picker` | `<input type="datetime-local">` | |
468
+ | `color_picker` | Color input + hex field | |
469
+ | `file_upload` | File input | |
470
+ | `image_upload` | File input (image) | |
471
+ | `range_slider` | Range slider + value | Requires `min`/`max` on field |
472
+ | `badge` | Colored badge pill | Good for status/priority in list views |
473
+ | `timestamp` | Formatted date/time string | `formatDateTime()` from `lib/utils` |
474
+ | `currency` | Formatted currency string | Uses `validation.currency_code` |
475
+ | `number` | Numeric display | |
476
+ | `rating` | Reserved | Not yet implemented |
477
+ | `autocomplete` | Reserved | Not yet implemented |
478
+ | `address_form` | Reserved | Not yet implemented |
479
+ | `reference_picker` | Reserved | Not yet implemented |
480
+
481
+ ---
482
+
483
+ ## 5. `record_permissions`
484
+
485
+ Controls who can perform CRUD operations on records of this type.
486
+
487
+ ```json
488
+ {
489
+ "record_permissions": {
490
+ "system-admin": ["create", "read", "update", "delete"],
491
+ "admin": ["create", "read", "update"],
492
+ "member": ["read", "update"],
493
+ "guest": ["read"]
494
+ }
495
+ }
496
+ ```
497
+
498
+ | Property | Notes |
499
+ |---|---|
500
+ | Keys | Role slugs — must match slugs in the `roles` table |
501
+ | Values | Array of actions. Valid values: `"create"`, `"read"`, `"update"`, `"delete"` |
502
+ | Absent role | No access — not inherited from other roles |
503
+ | Empty array `[]` | Explicitly no access |
504
+
505
+ **Minimum (system-only type):**
506
+ ```json
507
+ {
508
+ "record_permissions": {
509
+ "system-admin": ["create", "read", "update", "delete"]
510
+ }
511
+ }
512
+ ```
513
+
514
+ **Full tenant type:**
515
+ ```json
516
+ {
517
+ "record_permissions": {
518
+ "system-admin": ["create", "read", "update", "delete"],
519
+ "admin": ["create", "read", "update", "delete"],
520
+ "member": ["create", "read", "update"],
521
+ "guest": ["read"]
522
+ }
523
+ }
524
+ ```
525
+
526
+ ---
527
+
528
+ ## 6. `functionality` — Reserved
529
+
530
+ `functionality` is currently `null` in all system seeds. The shape is fully defined in `types.ts` (`FunctionalityBindings`) and reserved for future automation bindings. Document the structure here for forward compatibility.
531
+
532
+ ```json
533
+ {
534
+ "functionality": {
535
+ "pipelines": [
536
+ {
537
+ "pipeline_id": "uuid",
538
+ "trigger": "on_create",
539
+ "roles": ["admin", "member"]
540
+ }
541
+ ],
542
+ "ai_agents": [
543
+ {
544
+ "agent_id": "uuid",
545
+ "capabilities": ["read", "summarize", "suggest"],
546
+ "trigger": "manual",
547
+ "roles": ["admin"]
548
+ }
549
+ ],
550
+ "embeddings": [
551
+ {
552
+ "slug": "task-embeddings",
553
+ "fields": ["title", "description"],
554
+ "model": "text-embedding-3-small",
555
+ "vector_column": "embedding",
556
+ "trigger": "on_create_or_update"
557
+ }
558
+ ],
559
+ "integrations": [
560
+ {
561
+ "integration_id": "uuid",
562
+ "sync": "bidirectional",
563
+ "field_map": {
564
+ "title": { "external_field": "name", "direction": "both" }
565
+ },
566
+ "trigger": "on_create_or_update"
567
+ }
568
+ ],
569
+ "constraints": [
570
+ {
571
+ "type": "unique",
572
+ "fields": ["title", "account_id"],
573
+ "message": "A task with this title already exists"
574
+ },
575
+ {
576
+ "type": "conditional_required",
577
+ "field": "due_date",
578
+ "condition": "priority === 'urgent'",
579
+ "message": "Due date is required for urgent tasks"
580
+ },
581
+ {
582
+ "type": "immutable",
583
+ "field": "created_by",
584
+ "after": "create",
585
+ "message": "Creator cannot be changed after creation"
586
+ }
587
+ ]
588
+ }
589
+ }
590
+ ```
591
+
592
+ **`pipeline` trigger values:** `"manual"` | `"on_create"` | `"on_update"` | `"on_field_change"` | `"on_delete"` | `"scheduled"`
593
+ **`ai_agent` capability values:** `"read"` | `"summarize"` | `"suggest"` | `"update"`
594
+ **`embedding` trigger values:** `"on_create"` | `"on_update"` | `"on_create_or_update"`
595
+ **`constraint` type values:** `"unique"` | `"conditional_required"` | `"immutable"`
596
+
597
+ > **Current status:** `functionality` is not yet enforced by the backend. Set to `null` until needed.
598
+
599
+ ---
600
+
601
+ ## 7. Field-Level `permissions`
602
+
603
+ Nested inside each field definition. Controls who can read or write individual field values.
604
+
605
+ ```json
606
+ {
607
+ "permissions": {
608
+ "system-admin": ["read", "write"],
609
+ "admin": ["read", "write"],
610
+ "member": ["read"],
611
+ "guest": []
612
+ }
613
+ }
614
+ ```
615
+
616
+ | Valid action values | Meaning |
617
+ |---|---|
618
+ | `"read"` | Role can see this field's value |
619
+ | `"write"` | Role can modify this field's value |
620
+
621
+ **Rules:**
622
+ - Actions for field permissions are `read` and `write` only — not `create`/`delete` (those are record-level)
623
+ - Empty array `[]` = explicitly no access to this field
624
+ - Absent role key = no access
625
+ - `record_permissions` gates first — if a role can't read a record, field permissions are irrelevant
626
+ - `system: true` fields should always restrict `write` to `system-admin` by convention
627
+
628
+ ---
629
+
630
+ ## 8. `validation_schema`
631
+
632
+ ### What it is
633
+
634
+ `validation_schema` is stored alongside `design_schema` on the `types` table. It is the server-side enforcement contract. **It is always generated by `generateValidationSchema()` in `schema-utils.ts` — never hand-authored.**
635
+
636
+ ### Shape
637
+
638
+ ```json
639
+ {
640
+ "fields": {
641
+ "field_name": {
642
+ "data_type": "text",
643
+ "required": true,
644
+ "minLength": 1,
645
+ "maxLength": 255
646
+ }
647
+ }
648
+ }
649
+ ```
650
+
651
+ Per-field properties are: `data_type`, `required`, plus any `validation` sub-properties flattened from the field definition, plus `options` if present.
652
+
653
+ ### What the backend enforces per `data_type`
654
+
655
+ | `data_type` | Sanitization applied |
656
+ |---|---|
657
+ | `text` | HTML entity escape, control chars stripped, `minLength`/`maxLength`/`pattern` enforced |
658
+ | `textarea` | Same as `text`, preserves line breaks |
659
+ | `rich_text` | Safe HTML tag allowlist, script/onX stripped, `minLength`/`maxLength` enforced |
660
+ | `email` | Lowercased, regex format validated |
661
+ | `phone` | Stripped to digits + `+`, `pattern` validated if set |
662
+ | `url` | URL parsed, http/https only enforced |
663
+ | `number` | Coerced to `Number`, `min`/`max`/`step` enforced |
664
+ | `currency` | Coerced to `Number`, rounded to 2 decimal places, `min`/`max` enforced |
665
+ | `range` | Same as `number` |
666
+ | `date` | Parsed to ISO `YYYY-MM-DD`, `min`/`max` date enforced |
667
+ | `datetime` | Parsed to ISO datetime, `min`/`max` enforced |
668
+ | `boolean` / `checkbox` | Coerced from string/int (`"true"`, `"1"`, `"yes"` → `true`) |
669
+ | `select` / `radio` | Validated against `options` array |
670
+ | `multiselect` | Array of strings, each validated against `options`, deduped, `max` count enforced |
671
+ | `color` | Validated as `#RRGGBB` or `#RGB`, normalized to uppercase 6-digit hex |
672
+ | `file` / `image` | Filename sanitized, `maxSize` and `allowedTypes` enforced |
673
+ | `json` | Parsed as JSON, script injection blocked |
674
+ | `reference` | UUID format validated |
675
+ | `address` | Each component sanitized as `text` |
676
+
677
+ ### Example generated `validation_schema`
678
+
679
+ Given this `design_schema.fields`:
680
+ ```json
681
+ {
682
+ "title": { "data_type": "text", "label": "Title", "required": true, "system": true, "validation": { "minLength": 1, "maxLength": 255 } },
683
+ "status": { "data_type": "select", "label": "Status", "required": true, "options": ["todo", "in_progress", "done"] },
684
+ "due_date": { "data_type": "date", "label": "Due Date", "required": false }
685
+ }
686
+ ```
687
+
688
+ `generateValidationSchema()` produces:
689
+ ```json
690
+ {
691
+ "fields": {
692
+ "title": { "data_type": "text", "required": true, "minLength": 1, "maxLength": 255 },
693
+ "status": { "data_type": "select", "required": true, "options": ["todo", "in_progress", "done"] },
694
+ "due_date": { "data_type": "date", "required": false }
695
+ }
696
+ }
697
+ ```
698
+
699
+ ---
700
+
701
+ ## 9. `system` Field Flag
702
+
703
+ ### Behavior
704
+
705
+ | Flag | Where value is stored | Who can write |
706
+ |---|---|---|
707
+ | `system: true` | `record[fieldName]` — real DB column | `system-admin` only (by convention) |
708
+ | `system: false` or absent | `record.data[fieldName]` — JSONB column | Per field `permissions` |
709
+
710
+ ### System fields that always exist on every record
711
+
712
+ These are DB columns present on all entity tables and do not need to be defined in `design_schema.fields` to appear in views:
713
+
714
+ | Field | Type | Notes |
715
+ |---|---|---|
716
+ | `id` | `uuid` | Auto-generated |
717
+ | `title` | `text` | Primary display field |
718
+ | `is_active` | `boolean` | Soft delete flag |
719
+ | `account_id` | `uuid` | Owning account |
720
+ | `created_at` | `timestamp` | Auto-set |
721
+ | `updated_at` | `timestamp` | Auto-updated |
722
+ | `created_by` | `uuid` | FK to `people.id` |
723
+
724
+ These can be referenced in `views.fields` without being in `design_schema.fields`.
725
+
726
+ ### Including system fields in `design_schema.fields`
727
+
728
+ You should include system fields you want to be editable in forms. Example:
729
+
730
+ ```json
731
+ {
732
+ "title": {
733
+ "data_type": "text",
734
+ "label": "Title",
735
+ "required": true,
736
+ "system": true,
737
+ "validation": { "minLength": 1, "maxLength": 255 },
738
+ "permissions": {
739
+ "system-admin": ["read", "write"],
740
+ "admin": ["read", "write"],
741
+ "member": ["read", "write"]
742
+ }
743
+ }
744
+ }
745
+ ```
746
+
747
+ ---
748
+
749
+ ## 10. Complete Examples
750
+
751
+ ### 10.1 Minimal `design_schema`
752
+
753
+ One system field, one custom field, minimal list and detail views, system-admin only:
754
+
755
+ ```json
756
+ {
757
+ "fields": {
758
+ "title": {
759
+ "data_type": "text",
760
+ "label": "Title",
761
+ "required": true,
762
+ "system": true,
763
+ "permissions": {
764
+ "system-admin": ["read", "write"]
765
+ }
766
+ },
767
+ "notes": {
768
+ "data_type": "textarea",
769
+ "label": "Notes",
770
+ "required": false,
771
+ "permissions": {
772
+ "system-admin": ["read", "write"]
773
+ }
774
+ }
775
+ },
776
+ "views": {
777
+ "default_list": {
778
+ "type": "list",
779
+ "display": "table",
780
+ "label": "Default List",
781
+ "fields": {
782
+ "title": { "sortable": true, "display_type": "text" },
783
+ "created_at": { "sortable": true, "display_type": "timestamp" }
784
+ },
785
+ "default_sort": { "field": "created_at", "direction": "desc" }
786
+ },
787
+ "default_detail": {
788
+ "type": "detail",
789
+ "label": "Default Detail",
790
+ "sections": [
791
+ {
792
+ "title": "Details",
793
+ "fields": {
794
+ "title": { "display_type": "input" },
795
+ "notes": { "display_type": "textarea" }
796
+ }
797
+ }
798
+ ]
799
+ }
800
+ },
801
+ "record_permissions": {
802
+ "system-admin": ["create", "read", "update", "delete"]
803
+ },
804
+ "functionality": null
805
+ }
806
+ ```
807
+
808
+ ### 10.2 Maximal `design_schema` — Task Type
809
+
810
+ Full field set, multi-section detail view, stats, filters, role permissions, functionality reserved:
811
+
812
+ ```json
813
+ {
814
+ "fields": {
815
+ "title": {
816
+ "data_type": "text",
817
+ "label": "Title",
818
+ "required": true,
819
+ "system": true,
820
+ "validation": { "minLength": 1, "maxLength": 255 },
821
+ "permissions": {
822
+ "system-admin": ["read", "write"],
823
+ "admin": ["read", "write"],
824
+ "member": ["read", "write"],
825
+ "guest": ["read"]
826
+ }
827
+ },
828
+ "status": {
829
+ "data_type": "select",
830
+ "label": "Status",
831
+ "required": true,
832
+ "system": false,
833
+ "options": ["todo", "in_progress", "done", "blocked"],
834
+ "permissions": {
835
+ "admin": ["read", "write"],
836
+ "member": ["read", "write"],
837
+ "guest": ["read"]
838
+ }
839
+ },
840
+ "priority": {
841
+ "data_type": "select",
842
+ "label": "Priority",
843
+ "required": true,
844
+ "system": false,
845
+ "options": [
846
+ { "value": "low", "label": "Low" },
847
+ { "value": "medium", "label": "Medium" },
848
+ { "value": "high", "label": "High" },
849
+ { "value": "urgent", "label": "Urgent" }
850
+ ],
851
+ "permissions": {
852
+ "admin": ["read", "write"],
853
+ "member": ["read", "write"],
854
+ "guest": ["read"]
855
+ }
856
+ },
857
+ "due_date": {
858
+ "data_type": "date",
859
+ "label": "Due Date",
860
+ "required": false,
861
+ "system": false,
862
+ "permissions": {
863
+ "admin": ["read", "write"],
864
+ "member": ["read", "write"],
865
+ "guest": ["read"]
866
+ }
867
+ },
868
+ "description": {
869
+ "data_type": "textarea",
870
+ "label": "Description",
871
+ "required": false,
872
+ "system": false,
873
+ "validation": { "maxLength": 5000 },
874
+ "permissions": {
875
+ "admin": ["read", "write"],
876
+ "member": ["read", "write"],
877
+ "guest": ["read"]
878
+ }
879
+ },
880
+ "internal_notes": {
881
+ "data_type": "textarea",
882
+ "label": "Internal Notes",
883
+ "required": false,
884
+ "system": false,
885
+ "validation": { "maxLength": 5000 },
886
+ "permissions": {
887
+ "admin": ["read", "write"],
888
+ "member": [],
889
+ "guest": []
890
+ }
891
+ },
892
+ "score": {
893
+ "data_type": "range",
894
+ "label": "Completion Score",
895
+ "required": false,
896
+ "system": false,
897
+ "min": 0,
898
+ "max": 100,
899
+ "step": 5,
900
+ "validation": { "min": 0, "max": 100, "step": 5, "integer": true },
901
+ "permissions": {
902
+ "admin": ["read", "write"],
903
+ "member": ["read", "write"]
904
+ }
905
+ }
906
+ },
907
+ "views": {
908
+ "default_list": {
909
+ "type": "list",
910
+ "display": "table",
911
+ "label": "Tasks",
912
+ "fields": {
913
+ "title": { "sortable": true, "searchable": true, "display_type": "text" },
914
+ "status": { "sortable": true, "display_type": "badge" },
915
+ "priority": { "sortable": true, "display_type": "badge" },
916
+ "due_date": { "sortable": true, "display_type": "timestamp" },
917
+ "created_at": { "sortable": true, "display_type": "timestamp" }
918
+ },
919
+ "default_sort": { "field": "due_date", "direction": "asc" },
920
+ "filters": ["status", "priority", "is_active"],
921
+ "stats": [
922
+ { "title": "Total", "type": "count", "icon": "ClipboardIcon", "color": "blue" },
923
+ { "title": "Open", "type": "filter_count", "icon": "ClockIcon", "color": "yellow", "filter": { "status": "todo" } },
924
+ { "title": "Done", "type": "filter_count", "icon": "CheckCircleIcon", "color": "green", "filter": { "status": "done" } }
925
+ ]
926
+ },
927
+ "default_detail": {
928
+ "type": "detail",
929
+ "label": "Task Detail",
930
+ "sections": [
931
+ {
932
+ "title": "Core",
933
+ "fields": {
934
+ "title": { "display_type": "input" },
935
+ "status": { "display_type": "select" },
936
+ "priority": { "display_type": "radio" },
937
+ "due_date": { "display_type": "date_picker" }
938
+ }
939
+ },
940
+ {
941
+ "title": "Details",
942
+ "fields": {
943
+ "description": { "display_type": "textarea" },
944
+ "score": { "display_type": "range_slider" }
945
+ }
946
+ },
947
+ {
948
+ "title": "Internal",
949
+ "fields": {
950
+ "internal_notes": { "display_type": "textarea" }
951
+ }
952
+ }
953
+ ]
954
+ }
955
+ },
956
+ "record_permissions": {
957
+ "system-admin": ["create", "read", "update", "delete"],
958
+ "admin": ["create", "read", "update", "delete"],
959
+ "member": ["create", "read", "update"],
960
+ "guest": ["read"]
961
+ },
962
+ "functionality": null
963
+ }
964
+ ```
965
+
966
+ ### 10.3 Corresponding `validation_schema` (auto-generated)
967
+
968
+ ```json
969
+ {
970
+ "fields": {
971
+ "title": { "data_type": "text", "required": true, "minLength": 1, "maxLength": 255 },
972
+ "status": { "data_type": "select", "required": true, "options": ["todo", "in_progress", "done", "blocked"] },
973
+ "priority": { "data_type": "select", "required": true, "options": [{"value":"low","label":"Low"},{"value":"medium","label":"Medium"},{"value":"high","label":"High"},{"value":"urgent","label":"Urgent"}] },
974
+ "due_date": { "data_type": "date", "required": false },
975
+ "description": { "data_type": "textarea", "required": false, "maxLength": 5000 },
976
+ "internal_notes": { "data_type": "textarea", "required": false, "maxLength": 5000 },
977
+ "score": { "data_type": "range", "required": false, "min": 0, "max": 100, "step": 5, "integer": true }
978
+ }
979
+ }
980
+ ```
981
+
982
+ ---
983
+
984
+ ## 11. Rules & Constraints
985
+
986
+ ### Field-level `permissions` — when to set them
987
+ - Field `permissions` are **only needed to restrict access below the record-level grant**
988
+ - If `record_permissions` already grants a role full access, repeating that grant at the field level is redundant — omit it
989
+ - System-only types (all fields `system: true`, `record_permissions` grants `system-admin` only) need **no field-level permissions at all**
990
+ - Custom fields (`system: false`) that should be visible to tenant roles (`admin`, `member`, `guest`) **must** declare those roles in field `permissions`, since the record-level grant alone does not grant field visibility to those roles
991
+ - A field with no matching role entry (and no record-level field override) is invisible and unwritable to that role
992
+ - `validation: null` is acceptable (used in all system seeds) — omitting `validation` entirely is also fine
993
+
994
+ ### Field names
995
+ - Must be `snake_case`
996
+ - Must be unique within a type
997
+ - The key in `design_schema.fields` IS the field name — it is used as the data key in API payloads and `record.data`
998
+
999
+ ### Views
1000
+ - `views.fields` keys should reference fields defined in `design_schema.fields` OR known system columns (`id`, `title`, `is_active`, `created_at`, `updated_at`, `created_by`)
1001
+ - Referencing an undefined field key in a view is non-fatal (field is silently skipped) but should be avoided
1002
+ - A type must have at least one view named `"default_list"` and one named `"default_detail"` for the runtime UI to work
1003
+
1004
+ ### `options` requirement
1005
+ - Required for `data_type: "select"`, `"multiselect"`, `"radio"` — field will not render correctly without it
1006
+ - Options can be plain strings `["a", "b"]` or labeled objects `[{value, label}]` — not mixed
1007
+
1008
+ ### `reference` fields
1009
+ - `validation.reference_kind` and `validation.reference_type` should be set to enable future reference picker UI
1010
+ - Value stored is a UUID string
1011
+
1012
+ ### `validation_schema`
1013
+ - Always auto-generated by `generateValidationSchema()` — do not hand-edit
1014
+ - When `design_schema` is updated, `validation_schema` must be regenerated and saved together
1015
+
1016
+ ### `system: true` fields
1017
+ - By convention, `write` permission should be restricted to `system-admin` only
1018
+ - Do not include system fields in `design_schema.fields` unless you want them editable in forms
1019
+
1020
+ ### `record_permissions`
1021
+ - At minimum, include `"system-admin": ["create", "read", "update", "delete"]`
1022
+ - Valid actions: `"create"`, `"read"`, `"update"`, `"delete"` — no others
1023
+
1024
+ ### `functionality`
1025
+ - Set to `null` until automation bindings are needed
1026
+ - Do not set non-null values unless the runtime enforcement is confirmed active
1027
+
1028
+ ---
1029
+
1030
+ ## 12. Runtime Entity — Minimal `design_schema` Examples
1031
+
1032
+ Each example below includes **only the user-facing system columns** for that entity's DB table — all marked `system: true`. Since `record_permissions` grants `system-admin` full access, **no field-level `permissions` are set** — they are only needed when restricting access below the record-level grant.
1033
+
1034
+ These serve as the starting point for any custom type built on that entity. Add custom fields (with `system: false`) and their field-level `permissions` on top of these.
1035
+
1036
+ > Excluded from all examples: `id`, `type_id`, `account_id`, `app_id`, `data`, `design_schema`, `validation_schema`, `created_by`, `updated_by`, `auth_uid`, `role_id`, `owner_account_id` — these are internal FK/system columns not exposed in UI forms.
1037
+
1038
+ ---
1039
+
1040
+ ### 12.1 `items`
1041
+
1042
+ DB columns: `title`, `description`, `status`, `is_active`, `item_type`, `created_at`, `updated_at`
1043
+
1044
+ ```json
1045
+ {
1046
+ "fields": {
1047
+ "title": {
1048
+ "data_type": "text",
1049
+ "label": "Title",
1050
+ "required": true,
1051
+ "system": true,
1052
+ "validation": null
1053
+ },
1054
+ "description": {
1055
+ "data_type": "textarea",
1056
+ "label": "Description",
1057
+ "required": false,
1058
+ "system": true,
1059
+ "validation": null
1060
+ },
1061
+ "status": {
1062
+ "data_type": "text",
1063
+ "label": "Status",
1064
+ "required": true,
1065
+ "system": true,
1066
+ "validation": null
1067
+ },
1068
+ "is_active": {
1069
+ "data_type": "boolean",
1070
+ "label": "Active",
1071
+ "required": true,
1072
+ "system": true,
1073
+ "validation": null
1074
+ },
1075
+ "item_type": {
1076
+ "data_type": "text",
1077
+ "label": "Item Type",
1078
+ "required": true,
1079
+ "system": true,
1080
+ "validation": null
1081
+ },
1082
+ "created_at": {
1083
+ "data_type": "datetime",
1084
+ "label": "Created",
1085
+ "required": false,
1086
+ "system": true,
1087
+ "readonly": true,
1088
+ "validation": null
1089
+ },
1090
+ "updated_at": {
1091
+ "data_type": "datetime",
1092
+ "label": "Updated",
1093
+ "required": false,
1094
+ "system": true,
1095
+ "readonly": true,
1096
+ "validation": null
1097
+ }
1098
+ },
1099
+ "views": {
1100
+ "default_list": {
1101
+ "type": "list",
1102
+ "display": "table",
1103
+ "label": "Items",
1104
+ "fields": {
1105
+ "title": { "sortable": true, "display_type": "text" },
1106
+ "status": { "sortable": true, "display_type": "badge" },
1107
+ "item_type": { "sortable": true, "display_type": "text" },
1108
+ "is_active": { "sortable": true, "display_type": "badge" },
1109
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1110
+ },
1111
+ "default_sort": { "field": "created_at", "direction": "desc" }
1112
+ },
1113
+ "default_detail": {
1114
+ "type": "detail",
1115
+ "label": "Item Detail",
1116
+ "sections": [
1117
+ {
1118
+ "title": "Core",
1119
+ "fields": {
1120
+ "title": { "display_type": "input" },
1121
+ "description": { "display_type": "textarea" },
1122
+ "status": { "display_type": "input" },
1123
+ "item_type": { "display_type": "text" },
1124
+ "is_active": { "display_type": "checkbox" }
1125
+ }
1126
+ },
1127
+ {
1128
+ "title": "System",
1129
+ "fields": {
1130
+ "created_at": { "display_type": "timestamp" },
1131
+ "updated_at": { "display_type": "timestamp" }
1132
+ }
1133
+ }
1134
+ ]
1135
+ }
1136
+ },
1137
+ "record_permissions": {
1138
+ "system-admin": ["create", "read", "update", "delete"]
1139
+ },
1140
+ "functionality": null
1141
+ }
1142
+ ```
1143
+
1144
+ ---
1145
+
1146
+ ### 12.2 `people`
1147
+
1148
+ DB columns: `full_name`, `email`, `phone`, `avatar_url`, `status`, `is_active`, `created_at`, `updated_at`
1149
+
1150
+ ```json
1151
+ {
1152
+ "fields": {
1153
+ "full_name": {
1154
+ "data_type": "text",
1155
+ "label": "Full Name",
1156
+ "required": true,
1157
+ "system": true,
1158
+ "validation": null
1159
+ },
1160
+ "email": {
1161
+ "data_type": "email",
1162
+ "label": "Email",
1163
+ "required": true,
1164
+ "system": true,
1165
+ "validation": null
1166
+ },
1167
+ "phone": {
1168
+ "data_type": "phone",
1169
+ "label": "Phone",
1170
+ "required": false,
1171
+ "system": true,
1172
+ "validation": null
1173
+ },
1174
+ "avatar_url": {
1175
+ "data_type": "url",
1176
+ "label": "Avatar URL",
1177
+ "required": false,
1178
+ "system": true,
1179
+ "validation": null
1180
+ },
1181
+ "status": {
1182
+ "data_type": "text",
1183
+ "label": "Status",
1184
+ "required": true,
1185
+ "system": true,
1186
+ "validation": null
1187
+ },
1188
+ "is_active": {
1189
+ "data_type": "boolean",
1190
+ "label": "Active",
1191
+ "required": true,
1192
+ "system": true,
1193
+ "validation": null
1194
+ },
1195
+ "created_at": {
1196
+ "data_type": "datetime",
1197
+ "label": "Created",
1198
+ "required": false,
1199
+ "system": true,
1200
+ "readonly": true,
1201
+ "validation": null
1202
+ },
1203
+ "updated_at": {
1204
+ "data_type": "datetime",
1205
+ "label": "Updated",
1206
+ "required": false,
1207
+ "system": true,
1208
+ "readonly": true,
1209
+ "validation": null
1210
+ }
1211
+ },
1212
+ "views": {
1213
+ "default_list": {
1214
+ "type": "list",
1215
+ "display": "table",
1216
+ "label": "People",
1217
+ "fields": {
1218
+ "full_name": { "sortable": true, "display_type": "text" },
1219
+ "email": { "sortable": true, "display_type": "text" },
1220
+ "status": { "sortable": true, "display_type": "badge" },
1221
+ "is_active": { "sortable": true, "display_type": "badge" },
1222
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1223
+ },
1224
+ "default_sort": { "field": "created_at", "direction": "desc" }
1225
+ },
1226
+ "default_detail": {
1227
+ "type": "detail",
1228
+ "label": "Person Detail",
1229
+ "sections": [
1230
+ {
1231
+ "title": "Identity",
1232
+ "fields": {
1233
+ "full_name": { "display_type": "input" },
1234
+ "email": { "display_type": "input" },
1235
+ "phone": { "display_type": "input" },
1236
+ "avatar_url": { "display_type": "input" }
1237
+ }
1238
+ },
1239
+ {
1240
+ "title": "Status",
1241
+ "fields": {
1242
+ "status": { "display_type": "input" },
1243
+ "is_active": { "display_type": "checkbox" }
1244
+ }
1245
+ },
1246
+ {
1247
+ "title": "System",
1248
+ "fields": {
1249
+ "created_at": { "display_type": "timestamp" },
1250
+ "updated_at": { "display_type": "timestamp" }
1251
+ }
1252
+ }
1253
+ ]
1254
+ }
1255
+ },
1256
+ "record_permissions": {
1257
+ "system-admin": ["create", "read", "update", "delete"]
1258
+ },
1259
+ "functionality": null
1260
+ }
1261
+ ```
1262
+
1263
+ ---
1264
+
1265
+ ### 12.3 `accounts`
1266
+
1267
+ DB columns: `slug`, `display_name`, `description`, `is_active`, `created_at`, `updated_at`
1268
+
1269
+ ```json
1270
+ {
1271
+ "fields": {
1272
+ "slug": {
1273
+ "data_type": "text",
1274
+ "label": "Slug",
1275
+ "required": true,
1276
+ "system": true,
1277
+ "validation": null
1278
+ },
1279
+ "display_name": {
1280
+ "data_type": "text",
1281
+ "label": "Display Name",
1282
+ "required": true,
1283
+ "system": true,
1284
+ "validation": null
1285
+ },
1286
+ "description": {
1287
+ "data_type": "textarea",
1288
+ "label": "Description",
1289
+ "required": false,
1290
+ "system": true,
1291
+ "validation": null
1292
+ },
1293
+ "is_active": {
1294
+ "data_type": "boolean",
1295
+ "label": "Active",
1296
+ "required": true,
1297
+ "system": true,
1298
+ "validation": null
1299
+ },
1300
+ "created_at": {
1301
+ "data_type": "datetime",
1302
+ "label": "Created",
1303
+ "required": false,
1304
+ "system": true,
1305
+ "readonly": true,
1306
+ "validation": null
1307
+ },
1308
+ "updated_at": {
1309
+ "data_type": "datetime",
1310
+ "label": "Updated",
1311
+ "required": false,
1312
+ "system": true,
1313
+ "readonly": true,
1314
+ "validation": null
1315
+ }
1316
+ },
1317
+ "views": {
1318
+ "default_list": {
1319
+ "type": "list",
1320
+ "display": "table",
1321
+ "label": "Accounts",
1322
+ "fields": {
1323
+ "display_name": { "sortable": true, "display_type": "text" },
1324
+ "slug": { "sortable": true, "display_type": "text" },
1325
+ "is_active": { "sortable": true, "display_type": "badge" },
1326
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1327
+ },
1328
+ "default_sort": { "field": "created_at", "direction": "desc" }
1329
+ },
1330
+ "default_detail": {
1331
+ "type": "detail",
1332
+ "label": "Account Detail",
1333
+ "sections": [
1334
+ {
1335
+ "title": "Identity",
1336
+ "fields": {
1337
+ "display_name": { "display_type": "input" },
1338
+ "slug": { "display_type": "input" },
1339
+ "description": { "display_type": "textarea" },
1340
+ "is_active": { "display_type": "checkbox" }
1341
+ }
1342
+ },
1343
+ {
1344
+ "title": "System",
1345
+ "fields": {
1346
+ "created_at": { "display_type": "timestamp" },
1347
+ "updated_at": { "display_type": "timestamp" }
1348
+ }
1349
+ }
1350
+ ]
1351
+ }
1352
+ },
1353
+ "record_permissions": {
1354
+ "system-admin": ["create", "read", "update", "delete"]
1355
+ },
1356
+ "functionality": null
1357
+ }
1358
+ ```
1359
+
1360
+ ---
1361
+
1362
+ ### 12.4 `threads`
1363
+
1364
+ DB columns: `title`, `target_type`, `visibility`, `status`, `created_at`, `updated_at`
1365
+
1366
+ ```json
1367
+ {
1368
+ "fields": {
1369
+ "title": {
1370
+ "data_type": "text",
1371
+ "label": "Title",
1372
+ "required": false,
1373
+ "system": true,
1374
+ "validation": null
1375
+ },
1376
+ "target_type": {
1377
+ "data_type": "text",
1378
+ "label": "Target Type",
1379
+ "required": true,
1380
+ "system": true,
1381
+ "readonly": true,
1382
+ "validation": null
1383
+ },
1384
+ "visibility": {
1385
+ "data_type": "text",
1386
+ "label": "Visibility",
1387
+ "required": true,
1388
+ "system": true,
1389
+ "validation": null
1390
+ },
1391
+ "status": {
1392
+ "data_type": "text",
1393
+ "label": "Status",
1394
+ "required": true,
1395
+ "system": true,
1396
+ "validation": null
1397
+ },
1398
+ "created_at": {
1399
+ "data_type": "datetime",
1400
+ "label": "Created",
1401
+ "required": false,
1402
+ "system": true,
1403
+ "readonly": true,
1404
+ "validation": null
1405
+ },
1406
+ "updated_at": {
1407
+ "data_type": "datetime",
1408
+ "label": "Updated",
1409
+ "required": false,
1410
+ "system": true,
1411
+ "readonly": true,
1412
+ "validation": null
1413
+ }
1414
+ },
1415
+ "views": {
1416
+ "default_list": {
1417
+ "type": "list",
1418
+ "display": "table",
1419
+ "label": "Threads",
1420
+ "fields": {
1421
+ "title": { "sortable": true, "display_type": "text" },
1422
+ "target_type": { "sortable": true, "display_type": "text" },
1423
+ "visibility": { "sortable": true, "display_type": "badge" },
1424
+ "status": { "sortable": true, "display_type": "badge" },
1425
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1426
+ },
1427
+ "default_sort": { "field": "created_at", "direction": "desc" }
1428
+ },
1429
+ "default_detail": {
1430
+ "type": "detail",
1431
+ "label": "Thread Detail",
1432
+ "sections": [
1433
+ {
1434
+ "title": "Core",
1435
+ "fields": {
1436
+ "title": { "display_type": "input" },
1437
+ "target_type": { "display_type": "text" },
1438
+ "visibility": { "display_type": "input" },
1439
+ "status": { "display_type": "input" }
1440
+ }
1441
+ },
1442
+ {
1443
+ "title": "System",
1444
+ "fields": {
1445
+ "created_at": { "display_type": "timestamp" },
1446
+ "updated_at": { "display_type": "timestamp" }
1447
+ }
1448
+ }
1449
+ ]
1450
+ }
1451
+ },
1452
+ "record_permissions": {
1453
+ "system-admin": ["create", "read", "update", "delete"]
1454
+ },
1455
+ "functionality": null
1456
+ }
1457
+ ```
1458
+
1459
+ ---
1460
+
1461
+ ### 12.5 `messages`
1462
+
1463
+ DB columns: `content`, `direction`, `sequence`, `visibility`, `created_at`
1464
+
1465
+ ```json
1466
+ {
1467
+ "fields": {
1468
+ "content": {
1469
+ "data_type": "textarea",
1470
+ "label": "Content",
1471
+ "required": true,
1472
+ "system": true,
1473
+ "validation": null
1474
+ },
1475
+ "direction": {
1476
+ "data_type": "text",
1477
+ "label": "Direction",
1478
+ "required": true,
1479
+ "system": true,
1480
+ "readonly": true,
1481
+ "validation": null
1482
+ },
1483
+ "sequence": {
1484
+ "data_type": "number",
1485
+ "label": "Sequence",
1486
+ "required": true,
1487
+ "system": true,
1488
+ "readonly": true,
1489
+ "validation": null
1490
+ },
1491
+ "visibility": {
1492
+ "data_type": "text",
1493
+ "label": "Visibility",
1494
+ "required": true,
1495
+ "system": true,
1496
+ "validation": null
1497
+ },
1498
+ "created_at": {
1499
+ "data_type": "datetime",
1500
+ "label": "Created",
1501
+ "required": false,
1502
+ "system": true,
1503
+ "readonly": true,
1504
+ "validation": null
1505
+ }
1506
+ },
1507
+ "views": {
1508
+ "default_list": {
1509
+ "type": "list",
1510
+ "display": "table",
1511
+ "label": "Messages",
1512
+ "fields": {
1513
+ "content": { "sortable": false, "display_type": "text" },
1514
+ "direction": { "sortable": true, "display_type": "badge" },
1515
+ "sequence": { "sortable": true, "display_type": "number" },
1516
+ "visibility": { "sortable": true, "display_type": "badge" },
1517
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1518
+ },
1519
+ "default_sort": { "field": "sequence", "direction": "asc" }
1520
+ },
1521
+ "default_detail": {
1522
+ "type": "detail",
1523
+ "label": "Message Detail",
1524
+ "sections": [
1525
+ {
1526
+ "title": "Content",
1527
+ "fields": {
1528
+ "content": { "display_type": "textarea" },
1529
+ "direction": { "display_type": "text" },
1530
+ "sequence": { "display_type": "number" },
1531
+ "visibility": { "display_type": "input" }
1532
+ }
1533
+ },
1534
+ {
1535
+ "title": "System",
1536
+ "fields": {
1537
+ "created_at": { "display_type": "timestamp" }
1538
+ }
1539
+ }
1540
+ ]
1541
+ }
1542
+ },
1543
+ "record_permissions": {
1544
+ "system-admin": ["create", "read", "update", "delete"]
1545
+ },
1546
+ "functionality": null
1547
+ }
1548
+ ```
1549
+
1550
+ ---
1551
+
1552
+ ### 12.6 `roles`
1553
+
1554
+ DB columns: `slug`, `name`, `description`, `is_system`, `is_active`, `is_protected`, `created_at`, `updated_at`
1555
+
1556
+ ```json
1557
+ {
1558
+ "fields": {
1559
+ "slug": {
1560
+ "data_type": "text",
1561
+ "label": "Slug",
1562
+ "required": true,
1563
+ "system": true,
1564
+ "validation": null
1565
+ },
1566
+ "name": {
1567
+ "data_type": "text",
1568
+ "label": "Name",
1569
+ "required": true,
1570
+ "system": true,
1571
+ "validation": null
1572
+ },
1573
+ "description": {
1574
+ "data_type": "textarea",
1575
+ "label": "Description",
1576
+ "required": false,
1577
+ "system": true,
1578
+ "validation": null
1579
+ },
1580
+ "is_system": {
1581
+ "data_type": "boolean",
1582
+ "label": "System Role",
1583
+ "required": true,
1584
+ "system": true,
1585
+ "readonly": true,
1586
+ "validation": null
1587
+ },
1588
+ "is_active": {
1589
+ "data_type": "boolean",
1590
+ "label": "Active",
1591
+ "required": true,
1592
+ "system": true,
1593
+ "validation": null
1594
+ },
1595
+ "is_protected": {
1596
+ "data_type": "boolean",
1597
+ "label": "Protected",
1598
+ "required": false,
1599
+ "system": true,
1600
+ "readonly": true,
1601
+ "validation": null
1602
+ },
1603
+ "created_at": {
1604
+ "data_type": "datetime",
1605
+ "label": "Created",
1606
+ "required": false,
1607
+ "system": true,
1608
+ "readonly": true,
1609
+ "validation": null
1610
+ },
1611
+ "updated_at": {
1612
+ "data_type": "datetime",
1613
+ "label": "Updated",
1614
+ "required": false,
1615
+ "system": true,
1616
+ "readonly": true,
1617
+ "validation": null
1618
+ }
1619
+ },
1620
+ "views": {
1621
+ "default_list": {
1622
+ "type": "list",
1623
+ "display": "table",
1624
+ "label": "Roles",
1625
+ "fields": {
1626
+ "name": { "sortable": true, "display_type": "text" },
1627
+ "slug": { "sortable": true, "display_type": "text" },
1628
+ "is_system": { "sortable": true, "display_type": "badge" },
1629
+ "is_active": { "sortable": true, "display_type": "badge" },
1630
+ "created_at": { "sortable": true, "display_type": "timestamp" }
1631
+ },
1632
+ "default_sort": { "field": "name", "direction": "asc" }
1633
+ },
1634
+ "default_detail": {
1635
+ "type": "detail",
1636
+ "label": "Role Detail",
1637
+ "sections": [
1638
+ {
1639
+ "title": "Identity",
1640
+ "fields": {
1641
+ "name": { "display_type": "input" },
1642
+ "slug": { "display_type": "input" },
1643
+ "description": { "display_type": "textarea" }
1644
+ }
1645
+ },
1646
+ {
1647
+ "title": "Flags",
1648
+ "fields": {
1649
+ "is_system": { "display_type": "checkbox" },
1650
+ "is_active": { "display_type": "checkbox" },
1651
+ "is_protected": { "display_type": "checkbox" }
1652
+ }
1653
+ },
1654
+ {
1655
+ "title": "System",
1656
+ "fields": {
1657
+ "created_at": { "display_type": "timestamp" },
1658
+ "updated_at": { "display_type": "timestamp" }
1659
+ }
1660
+ }
1661
+ ]
1662
+ }
1663
+ },
1664
+ "record_permissions": {
1665
+ "system-admin": ["create", "read", "update", "delete"]
1666
+ },
1667
+ "functionality": null
1668
+ }
1669
+ ```
1670
+
1671
+ ---
1672
+
1673
+ ### 12.7 `apps`
1674
+
1675
+ DB columns: `slug`, `name`, `description`, `icon`, `color`, `version`, `app_type`, `source`, `is_active`, `is_system`, `min_role`, `created_at`, `updated_at`
1676
+
1677
+ ```json
1678
+ {
1679
+ "fields": {
1680
+ "slug": {
1681
+ "data_type": "text",
1682
+ "label": "Slug",
1683
+ "required": true,
1684
+ "system": true,
1685
+ "validation": null
1686
+ },
1687
+ "name": {
1688
+ "data_type": "text",
1689
+ "label": "Name",
1690
+ "required": true,
1691
+ "system": true,
1692
+ "validation": null
1693
+ },
1694
+ "description": {
1695
+ "data_type": "textarea",
1696
+ "label": "Description",
1697
+ "required": false,
1698
+ "system": true,
1699
+ "validation": null
1700
+ },
1701
+ "icon": {
1702
+ "data_type": "text",
1703
+ "label": "Icon",
1704
+ "required": false,
1705
+ "system": true,
1706
+ "validation": null
1707
+ },
1708
+ "color": {
1709
+ "data_type": "text",
1710
+ "label": "Color",
1711
+ "required": false,
1712
+ "system": true,
1713
+ "validation": null
1714
+ },
1715
+ "version": {
1716
+ "data_type": "text",
1717
+ "label": "Version",
1718
+ "required": true,
1719
+ "system": true,
1720
+ "validation": null
1721
+ },
1722
+ "app_type": {
1723
+ "data_type": "text",
1724
+ "label": "App Type",
1725
+ "required": true,
1726
+ "system": true,
1727
+ "readonly": true,
1728
+ "validation": null
1729
+ },
1730
+ "source": {
1731
+ "data_type": "text",
1732
+ "label": "Source",
1733
+ "required": true,
1734
+ "system": true,
1735
+ "readonly": true,
1736
+ "validation": null
1737
+ },
1738
+ "is_active": {
1739
+ "data_type": "boolean",
1740
+ "label": "Active",
1741
+ "required": true,
1742
+ "system": true,
1743
+ "validation": null
1744
+ },
1745
+ "is_system": {
1746
+ "data_type": "boolean",
1747
+ "label": "System App",
1748
+ "required": true,
1749
+ "system": true,
1750
+ "readonly": true,
1751
+ "validation": null
1752
+ },
1753
+ "min_role": {
1754
+ "data_type": "text",
1755
+ "label": "Minimum Role",
1756
+ "required": false,
1757
+ "system": true,
1758
+ "validation": null
1759
+ },
1760
+ "created_at": {
1761
+ "data_type": "datetime",
1762
+ "label": "Created",
1763
+ "required": false,
1764
+ "system": true,
1765
+ "readonly": true,
1766
+ "validation": null
1767
+ },
1768
+ "updated_at": {
1769
+ "data_type": "datetime",
1770
+ "label": "Updated",
1771
+ "required": false,
1772
+ "system": true,
1773
+ "readonly": true,
1774
+ "validation": null
1775
+ }
1776
+ },
1777
+ "views": {
1778
+ "default_list": {
1779
+ "type": "list",
1780
+ "display": "table",
1781
+ "label": "Apps",
1782
+ "fields": {
1783
+ "name": { "sortable": true, "display_type": "text" },
1784
+ "slug": { "sortable": true, "display_type": "text" },
1785
+ "version": { "sortable": true, "display_type": "text" },
1786
+ "app_type": { "sortable": true, "display_type": "badge" },
1787
+ "is_active": { "sortable": true, "display_type": "badge" },
1788
+ "created_at":{ "sortable": true, "display_type": "timestamp" }
1789
+ },
1790
+ "default_sort": { "field": "name", "direction": "asc" }
1791
+ },
1792
+ "default_detail": {
1793
+ "type": "detail",
1794
+ "label": "App Detail",
1795
+ "sections": [
1796
+ {
1797
+ "title": "Identity",
1798
+ "fields": {
1799
+ "name": { "display_type": "input" },
1800
+ "slug": { "display_type": "input" },
1801
+ "description": { "display_type": "textarea" },
1802
+ "icon": { "display_type": "input" },
1803
+ "color": { "display_type": "input" }
1804
+ }
1805
+ },
1806
+ {
1807
+ "title": "Configuration",
1808
+ "fields": {
1809
+ "version": { "display_type": "input" },
1810
+ "app_type": { "display_type": "text" },
1811
+ "source": { "display_type": "text" },
1812
+ "min_role": { "display_type": "input" }
1813
+ }
1814
+ },
1815
+ {
1816
+ "title": "Flags",
1817
+ "fields": {
1818
+ "is_active": { "display_type": "checkbox" },
1819
+ "is_system": { "display_type": "checkbox" }
1820
+ }
1821
+ },
1822
+ {
1823
+ "title": "System",
1824
+ "fields": {
1825
+ "created_at": { "display_type": "timestamp" },
1826
+ "updated_at": { "display_type": "timestamp" }
1827
+ }
1828
+ }
1829
+ ]
1830
+ }
1831
+ },
1832
+ "record_permissions": {
1833
+ "system-admin": ["create", "read", "update", "delete"]
1834
+ },
1835
+ "functionality": null
1836
+ }
1837
+ ```