spine-framework 0.4.3 → 0.5.0-rc.4

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 (320) 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/migrate.ts +36 -30
  8. package/.framework/cli/commands/migrations.ts +16 -56
  9. package/.framework/cli/commands/upgrade.ts +35 -0
  10. package/.framework/cli/commands/validate-schema.ts +147 -0
  11. package/.framework/cli/context.ts +223 -110
  12. package/.framework/cli/index.ts +6 -0
  13. package/.framework/cli/lib/project-files.ts +241 -1
  14. package/.framework/functions/_shared/agent-runner.ts +1000 -81
  15. package/.framework/functions/_shared/cil.ts +2 -1
  16. package/.framework/functions/_shared/db.ts +3 -6
  17. package/.framework/functions/_shared/hash-api-key.ts +183 -79
  18. package/.framework/functions/_shared/kb.ts +61 -7
  19. package/.framework/functions/_shared/logging.ts +562 -224
  20. package/.framework/functions/_shared/message-sequence.ts +53 -0
  21. package/.framework/functions/_shared/middleware.ts +47 -44
  22. package/.framework/functions/_shared/pipeline-runner.ts +150 -37
  23. package/.framework/functions/_shared/principal.ts +9 -6
  24. package/.framework/functions/_shared/trigger-engine.ts +149 -40
  25. package/.framework/functions/admin-data.ts +48 -31
  26. package/.framework/functions/ai-agents.ts +103 -33
  27. package/.framework/functions/api-keys.ts +301 -122
  28. package/.framework/functions/apps.ts +43 -26
  29. package/.framework/functions/cil-ingest.ts +48 -15
  30. package/.framework/functions/debug-auth.ts +14 -8
  31. package/.framework/functions/deliveries.ts +39 -12
  32. package/.framework/functions/embeddings.ts +145 -76
  33. package/.framework/functions/integration-outbound.ts +67 -27
  34. package/.framework/functions/integrations.ts +254 -127
  35. package/.framework/functions/logs.ts +273 -133
  36. package/.framework/functions/observability.ts +277 -147
  37. package/.framework/functions/pipeline-executions.ts +1 -1
  38. package/.framework/functions/pipeline-runner-background.ts +120 -0
  39. package/.framework/functions/pipelines.ts +14 -9
  40. package/.framework/functions/prompt-configs.ts +7 -4
  41. package/.framework/functions/roles.ts +8 -5
  42. package/.framework/functions/security.ts +30 -17
  43. package/.framework/functions/system-cron.ts +250 -128
  44. package/.framework/functions/tests.ts +7 -6
  45. package/.framework/functions/timers.ts +310 -107
  46. package/.framework/functions/trigger-scheduler.ts +65 -5
  47. package/.framework/functions/triggers.ts +28 -13
  48. package/.framework/functions/types.ts +13 -10
  49. package/.framework/migrations/064_episode_leases.sql +196 -0
  50. package/.framework/migrations/065_execution_tables_updated_at.sql +20 -0
  51. package/.framework/migrations/066_episode_steal_thread_merge.sql +148 -0
  52. package/.framework/migrations/067_episode_lease_stale_steal.sql +93 -0
  53. package/.framework/migrations/068_cron_schedule_timer_rpcs.sql +251 -0
  54. package/.framework/migrations/069_log_stats_window.sql +54 -0
  55. package/.framework/migrations/070_app_type_schema_rpcs.sql +166 -0
  56. package/.framework/migrations/071_timer_interval_keys.sql +96 -0
  57. package/.framework/migrations/072_slug_autofill.sql +77 -0
  58. package/.framework/migrations/073_seed_member_roles.sql +26 -0
  59. package/.framework/schemas/spine.config.schema.json +62 -0
  60. package/.framework/src/App.tsx +3 -1
  61. package/.framework/src/apps/admin/index.tsx +1141 -775
  62. package/.framework/src/components/ErrorBoundary.tsx +136 -0
  63. package/.framework/src/components/admin/AdminListPage.tsx +81 -73
  64. package/.framework/src/components/admin/AdminSidebar.tsx +92 -81
  65. package/.framework/src/components/admin/EntitySelect.tsx +181 -0
  66. package/.framework/src/components/admin/PageInstructions.tsx +86 -127
  67. package/.framework/src/components/app-shell/GenericAppShell.tsx +3 -1
  68. package/.framework/src/components/app-shell/GenericDetailPage.tsx +37 -13
  69. package/.framework/src/components/app-shell/GenericListPage.tsx +28 -32
  70. package/.framework/src/components/auth/TurnstileWidget.tsx +9 -6
  71. package/.framework/src/components/kb/KBArticleEditor.tsx +49 -6
  72. package/.framework/src/components/layout/Layout.tsx +4 -2
  73. package/.framework/src/components/layout/Sidebar.tsx +304 -179
  74. package/.framework/src/components/runtime/DataDetailPage.tsx +67 -35
  75. package/.framework/src/components/runtime/DataHeader.tsx +96 -48
  76. package/.framework/src/components/runtime/DataListPage.tsx +3 -26
  77. package/.framework/src/components/runtime/DataStats.tsx +10 -10
  78. package/.framework/src/components/runtime/DataTable.tsx +12 -10
  79. package/.framework/src/components/ui/sheet.tsx +24 -26
  80. package/.framework/src/components/ui/textarea.tsx +74 -48
  81. package/.framework/src/hooks/useBranding.ts +47 -0
  82. package/.framework/src/hooks/useEntityList.ts +12 -9
  83. package/.framework/src/hooks/useListSchema.ts +108 -58
  84. package/.framework/src/instructions/README.md +57 -0
  85. package/.framework/src/instructions/ai-agents.md +69 -0
  86. package/.framework/src/instructions/alerts.md +80 -0
  87. package/.framework/src/instructions/api-keys.md +87 -0
  88. package/.framework/src/instructions/apps.md +77 -0
  89. package/.framework/src/instructions/concepts/ai-and-prompts.md +150 -0
  90. package/.framework/src/instructions/concepts/apps-and-renderers.md +162 -0
  91. package/.framework/src/instructions/concepts/auth-and-api-keys.md +133 -0
  92. package/.framework/src/instructions/concepts/automation.md +186 -0
  93. package/.framework/src/instructions/concepts/concurrency-and-idempotency.md +125 -0
  94. package/.framework/src/instructions/concepts/core-vs-custom.md +124 -0
  95. package/.framework/src/instructions/concepts/design-schema.md +152 -0
  96. package/.framework/src/instructions/concepts/integrations-and-webhooks.md +127 -0
  97. package/.framework/src/instructions/concepts/observability.md +116 -0
  98. package/.framework/src/instructions/concepts/permissions.md +150 -0
  99. package/.framework/src/instructions/concepts/runtime-records.md +111 -0
  100. package/.framework/src/instructions/concepts/scope-and-accounts.md +112 -0
  101. package/.framework/src/instructions/concepts/spine-way.md +172 -0
  102. package/.framework/src/instructions/deliveries.md +59 -0
  103. package/.framework/src/instructions/guide/00-orientation.md +106 -0
  104. package/.framework/src/instructions/guide/01-install-and-first-run.md +253 -0
  105. package/.framework/src/instructions/guide/02-project-map.md +147 -0
  106. package/.framework/src/instructions/guide/03-domain-model.md +223 -0
  107. package/.framework/src/instructions/guide/04-first-app.md +227 -0
  108. package/.framework/src/instructions/guide/05-glossary.md +183 -0
  109. package/.framework/src/instructions/integrations.md +96 -0
  110. package/.framework/src/instructions/internal/cil-markers.md +83 -0
  111. package/.framework/src/instructions/invites.md +60 -0
  112. package/.framework/src/instructions/kb.md +80 -0
  113. package/.framework/src/instructions/logs.md +77 -0
  114. package/.framework/src/instructions/observability.md +61 -0
  115. package/.framework/src/instructions/operations/deployment.md +115 -0
  116. package/.framework/src/instructions/operations/migrations.md +125 -0
  117. package/.framework/src/instructions/operations/security-checklist.md +148 -0
  118. package/.framework/src/instructions/operations/troubleshooting.md +170 -0
  119. package/.framework/src/instructions/operations/upgrade.md +116 -0
  120. package/.framework/src/instructions/pipeline-executions.md +62 -0
  121. package/.framework/src/instructions/pipelines.md +78 -0
  122. package/.framework/src/instructions/prompts.md +95 -0
  123. package/.framework/src/instructions/reference/api/admin-data.md +162 -0
  124. package/.framework/src/instructions/reference/api/auth.md +113 -0
  125. package/.framework/src/instructions/reference/api/errors-and-envelopes.md +113 -0
  126. package/.framework/src/instructions/reference/app-manifest.md +150 -0
  127. package/.framework/src/instructions/reference/cli.md +124 -0
  128. package/.framework/src/instructions/reference/database.md +132 -0
  129. package/.framework/src/instructions/reference/design-schema.md +280 -0
  130. package/.framework/src/instructions/reference/env-vars.md +133 -0
  131. package/.framework/src/instructions/reference/functions/account-nodes.md +75 -0
  132. package/.framework/src/instructions/reference/functions/admin-data.md +195 -0
  133. package/.framework/src/instructions/reference/functions/admin-funnel.md +90 -0
  134. package/.framework/src/instructions/reference/functions/admin-install-funnel.md +107 -0
  135. package/.framework/src/instructions/reference/functions/admin-signals.md +102 -0
  136. package/.framework/src/instructions/reference/functions/ai-agents.md +148 -0
  137. package/.framework/src/instructions/reference/functions/ai-interactions.md +156 -0
  138. package/.framework/src/instructions/reference/functions/api-keys.md +130 -0
  139. package/.framework/src/instructions/reference/functions/apps.md +140 -0
  140. package/.framework/src/instructions/reference/functions/auth.md +99 -0
  141. package/.framework/src/instructions/reference/functions/debug-auth.md +91 -0
  142. package/.framework/src/instructions/reference/functions/deliveries.md +110 -0
  143. package/.framework/src/instructions/reference/functions/embeddings.md +166 -0
  144. package/.framework/src/instructions/reference/functions/eval-harness.md +149 -0
  145. package/.framework/src/instructions/reference/functions/index.md +103 -0
  146. package/.framework/src/instructions/reference/functions/integration-outbound.md +200 -0
  147. package/.framework/src/instructions/reference/functions/integration-routes.md +151 -0
  148. package/.framework/src/instructions/reference/functions/integrations.md +130 -0
  149. package/.framework/src/instructions/reference/functions/invites.md +182 -0
  150. package/.framework/src/instructions/reference/functions/item-progress.md +107 -0
  151. package/.framework/src/instructions/reference/functions/kb.md +166 -0
  152. package/.framework/src/instructions/reference/functions/logs.md +141 -0
  153. package/.framework/src/instructions/reference/functions/observability.md +105 -0
  154. package/.framework/src/instructions/reference/functions/pipeline-executions.md +132 -0
  155. package/.framework/src/instructions/reference/functions/pipeline-runner-background.md +121 -0
  156. package/.framework/src/instructions/reference/functions/pipelines.md +136 -0
  157. package/.framework/src/instructions/reference/functions/prompt-configs.md +121 -0
  158. package/.framework/src/instructions/reference/functions/roles.md +121 -0
  159. package/.framework/src/instructions/reference/functions/security.md +140 -0
  160. package/.framework/src/instructions/reference/functions/system-cron.md +136 -0
  161. package/.framework/src/instructions/reference/functions/system.md +106 -0
  162. package/.framework/src/instructions/reference/functions/tests.md +97 -0
  163. package/.framework/src/instructions/reference/functions/timers.md +117 -0
  164. package/.framework/src/instructions/reference/functions/trigger-scheduler.md +102 -0
  165. package/.framework/src/instructions/reference/functions/triggers.md +140 -0
  166. package/.framework/src/instructions/reference/functions/types.md +146 -0
  167. package/.framework/src/instructions/reference/seeds.md +126 -0
  168. package/.framework/src/instructions/reference/shared-modules.md +127 -0
  169. package/.framework/src/instructions/reference/spine-config.md +114 -0
  170. package/.framework/src/instructions/roles.md +80 -0
  171. package/.framework/src/instructions/runtime-detail.md +70 -0
  172. package/.framework/src/instructions/runtime-list.md +77 -0
  173. package/.framework/src/instructions/security.md +67 -0
  174. package/.framework/src/instructions/tasks/add-a-pipeline.md +110 -0
  175. package/.framework/src/instructions/tasks/add-a-role.md +96 -0
  176. package/.framework/src/instructions/tasks/add-a-timer.md +114 -0
  177. package/.framework/src/instructions/tasks/add-a-trigger.md +106 -0
  178. package/.framework/src/instructions/tasks/add-a-type.md +151 -0
  179. package/.framework/src/instructions/tasks/add-a-webhook.md +108 -0
  180. package/.framework/src/instructions/tasks/add-an-ai-agent.md +108 -0
  181. package/.framework/src/instructions/tasks/add-an-integration.md +124 -0
  182. package/.framework/src/instructions/tasks/build-a-custom-page.md +118 -0
  183. package/.framework/src/instructions/tasks/package-and-install-an-app.md +127 -0
  184. package/.framework/src/instructions/tasks/test-your-code.md +108 -0
  185. package/.framework/src/instructions/tasks/write-a-function.md +132 -0
  186. package/.framework/src/instructions/tasks/write-a-migration.md +110 -0
  187. package/.framework/src/instructions/testing.md +59 -0
  188. package/.framework/src/instructions/timers.md +82 -0
  189. package/.framework/src/instructions/triggers.md +69 -0
  190. package/.framework/src/instructions/types.md +93 -0
  191. package/.framework/src/lib/branding.ts +128 -0
  192. package/.framework/src/pages/NoAppPage.tsx +3 -1
  193. package/.framework/src/pages/NotFoundPage.tsx +62 -31
  194. package/.framework/src/pages/SetupPage.tsx +5 -2
  195. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +117 -106
  196. package/.framework/src/pages/admin/AIAgentsPage.tsx +86 -48
  197. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +153 -152
  198. package/.framework/src/pages/admin/APIKeysPage.tsx +91 -90
  199. package/.framework/src/pages/admin/AlertsConfigPage.tsx +29 -18
  200. package/.framework/src/pages/admin/AppDetailPage.tsx +6 -60
  201. package/.framework/src/pages/admin/AppsPage.tsx +104 -66
  202. package/.framework/src/pages/admin/DeliveriesPage.tsx +5 -5
  203. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +5 -23
  204. package/.framework/src/pages/admin/EmbeddingsPage.tsx +5 -26
  205. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +7 -7
  206. package/.framework/src/pages/admin/IntegrationsPage.tsx +85 -43
  207. package/.framework/src/pages/admin/LogsPage.tsx +144 -76
  208. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +223 -140
  209. package/.framework/src/pages/admin/PipelineDetailPage.tsx +134 -113
  210. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +108 -45
  211. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -115
  212. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +96 -98
  213. package/.framework/src/pages/admin/PromptConfigsPage.tsx +101 -59
  214. package/.framework/src/pages/admin/RoleDetailPage.tsx +90 -79
  215. package/.framework/src/pages/admin/RolesPage.tsx +86 -43
  216. package/.framework/src/pages/admin/SecurityPage.tsx +1 -1
  217. package/.framework/src/pages/admin/TestingDashboard.tsx +16 -28
  218. package/.framework/src/pages/admin/TimerDetailPage.tsx +117 -100
  219. package/.framework/src/pages/admin/TimersPage.tsx +90 -62
  220. package/.framework/src/pages/admin/TriggerDetailPage.tsx +90 -83
  221. package/.framework/src/pages/admin/TriggersPage.tsx +124 -99
  222. package/.framework/src/pages/admin/TypeDetailPage.tsx +152 -138
  223. package/.framework/src/pages/admin/TypesPage.tsx +90 -48
  224. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +17 -25
  225. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +17 -27
  226. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +72 -40
  227. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +321 -129
  228. package/.framework/src/pages/admin/cil/CILFileSelectPage.tsx +18 -29
  229. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +26 -38
  230. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +34 -46
  231. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +189 -103
  232. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +246 -136
  233. package/.framework/src/pages/admin/cil/CILSuggestionsPage.tsx +11 -22
  234. package/.framework/src/pages/admin/cil/CILVersionsPage.tsx +9 -20
  235. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +26 -38
  236. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +7 -26
  237. package/.framework/src/pages/auth/AuthConfirmPage.tsx +3 -1
  238. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +3 -1
  239. package/.framework/src/pages/auth/LoginPage.tsx +4 -2
  240. package/.framework/src/pages/auth/RegisterPage.tsx +3 -1
  241. package/.framework/templates/agent/AGENTS.md +150 -0
  242. package/.framework/templates/agent/skills/spine-add-ai-agent/SKILL.md +58 -0
  243. package/.framework/templates/agent/skills/spine-add-automation/SKILL.md +62 -0
  244. package/.framework/templates/agent/skills/spine-add-integration/SKILL.md +63 -0
  245. package/.framework/templates/agent/skills/spine-architect/SKILL.md +86 -0
  246. package/.framework/templates/agent/skills/spine-install-app/SKILL.md +72 -0
  247. package/.framework/templates/agent/skills/spine-migration/SKILL.md +75 -0
  248. package/.framework/templates/agent/skills/spine-new-app/SKILL.md +67 -0
  249. package/.framework/templates/agent/skills/spine-new-function/SKILL.md +65 -0
  250. package/.framework/templates/agent/skills/spine-new-type/SKILL.md +65 -0
  251. package/.framework/templates/agent/skills/spine-schema/SKILL.md +77 -0
  252. package/.framework/templates/agent/skills/spine-test/SKILL.md +67 -0
  253. package/.framework/templates/agent/skills/spine-troubleshoot/SKILL.md +74 -0
  254. package/.framework/templates/agent/skills/spine-upgrade/SKILL.md +69 -0
  255. package/.framework/templates/project.json +5 -1
  256. package/.framework/tests/unit/account-scoping-gates.test.ts +392 -0
  257. package/.framework/tests/unit/agent-files.test.ts +355 -0
  258. package/.framework/tests/unit/agent-runner.test.ts +496 -47
  259. package/.framework/tests/unit/api-keys-create-scope-gate.test.ts +160 -0
  260. package/.framework/tests/unit/api-keys-omit-secret-fields.test.ts +38 -0
  261. package/.framework/tests/unit/bootstrap.test.ts +242 -0
  262. package/.framework/tests/unit/cli-context-api-key.test.ts +114 -0
  263. package/.framework/tests/unit/debug-auth.test.ts +38 -0
  264. package/.framework/tests/unit/deliveries-account-scoping.test.ts +185 -0
  265. package/.framework/tests/unit/episode-lease.test.ts +695 -0
  266. package/.framework/tests/unit/integration-outbound-credentials.test.ts +167 -0
  267. package/.framework/tests/unit/integrations-audit-redaction.test.ts +130 -0
  268. package/.framework/tests/unit/kb-ingest-lease.test.ts +19 -0
  269. package/.framework/tests/unit/logging-app-id.test.ts +75 -0
  270. package/.framework/tests/unit/pipeline-runner-background.test.ts +120 -0
  271. package/.framework/tests/unit/pipeline-runner.test.ts +153 -1
  272. package/.framework/tests/unit/principal-cache.test.ts +15 -0
  273. package/.framework/tests/unit/system-context-denial.test.ts +145 -0
  274. package/.framework/tests/unit/system-cron.test.ts +240 -0
  275. package/.framework/tests/unit/timers.test.ts +159 -0
  276. package/.framework/tests/unit/trigger-engine.test.ts +278 -91
  277. package/.framework/tests/unit/trigger-scheduler.test.ts +78 -0
  278. package/README.md +125 -166
  279. package/config/tsconfig.cli.json +2 -2
  280. package/netlify.toml +5 -0
  281. package/package.json +15 -4
  282. package/templates/project/package-lock.json +2 -2
  283. package/.framework/docs/admin-routes.md +0 -76
  284. package/.framework/docs/api-docs-progress.md +0 -38
  285. package/.framework/docs/api-governance.md +0 -146
  286. package/.framework/docs/api-testing-results.md +0 -212
  287. package/.framework/docs/assembly-launch-guide.md +0 -138
  288. package/.framework/docs/audit-results.md +0 -590
  289. package/.framework/docs/db-api-inventory.md +0 -95
  290. package/.framework/docs/gap-remediation-backlog.md +0 -103
  291. package/.framework/docs/ui-adoption-verification.md +0 -111
  292. package/.framework/docs/ui-api-coverage.md +0 -84
  293. package/.framework/docs/v2-compatibility-audit.md +0 -228
  294. package/.framework/mcp/index.ts +0 -133
  295. package/.framework/mcp/load-env.ts +0 -60
  296. package/.framework/mcp/shared/app-manifest.ts +0 -275
  297. package/.framework/mcp/shared/db.ts +0 -477
  298. package/.framework/mcp/shared/kb.ts +0 -118
  299. package/.framework/mcp/tools/architect.ts +0 -431
  300. package/.framework/mcp/tools/cil/context.ts +0 -345
  301. package/.framework/mcp/tools/cil/graph.ts +0 -242
  302. package/.framework/mcp/tools/cil/ingest.ts +0 -1217
  303. package/.framework/mcp/tools/cil/pipeline-orchestrator.ts +0 -693
  304. package/.framework/mcp/tools/cil/pipeline.ts +0 -767
  305. package/.framework/mcp/tools/cil/project.ts +0 -791
  306. package/.framework/mcp/tools/cil/retrieval.ts +0 -336
  307. package/.framework/mcp/tools/cil/search-core.ts +0 -454
  308. package/.framework/mcp/tools/cil/search.ts +0 -97
  309. package/.framework/mcp/tools/cil/suggest.ts +0 -186
  310. package/.framework/mcp/tools/cil/validate.ts +0 -1369
  311. package/.framework/mcp/tools/kb/delete.ts +0 -139
  312. package/.framework/mcp/tools/kb/index.ts +0 -52
  313. package/.framework/mcp/tools/kb/ingest.ts +0 -84
  314. package/.framework/mcp/tools/kb/list-types.ts +0 -102
  315. package/.framework/mcp/tools/kb/publish.ts +0 -100
  316. package/.framework/mcp/tools/kb/reindex.ts +0 -86
  317. package/.framework/mcp/tools/kb/revoke.ts +0 -85
  318. package/.framework/mcp/tools/kb/search.ts +0 -101
  319. package/.framework/mcp/tools/kb/upsert.ts +0 -125
  320. package/.framework/mcp/tools/observability.ts +0 -510
@@ -0,0 +1,82 @@
1
+ ---
2
+ title: Timers
3
+ audience: both
4
+ kind: entity
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/timers.ts
9
+ - .framework/functions/system-cron.ts
10
+ - .framework/functions/trigger-scheduler.ts
11
+ - .framework/src/pages/admin/TimersPage.tsx
12
+ - .framework/src/pages/admin/TimerDetailPage.tsx
13
+ - .framework/migrations/068_cron_schedule_timer_rpcs.sql
14
+ - .framework/migrations/020_schema_reconciliation.sql
15
+ ---
16
+
17
+ ## What this is for
18
+
19
+ A **timer** is a scheduled job that invokes a pipeline when it comes due — Spine's recurring/one-off background task primitive. `system-cron` (invoked every minute by the `trigger-scheduler` Netlify scheduled function) asks the `get_due_timers` RPC for due timers and runs each one's pipeline.
20
+
21
+ ## Fields and settings
22
+
23
+ - **Name** — string. Display name; required (`name` and `timer_type` are the API's required fields).
24
+ - **Type** — select: `interval`, `cron`, `once`. **Stored but not read by the scheduler** — it labels intent only; see Downstream effects.
25
+ - **App ID** — select, optional. Owning app; blank = account-scoped.
26
+ - **Pipeline ID** — select. The pipeline invoked when the timer fires. `get_due_timers` skips rows where `pipeline_id IS NULL`, so a blank pipeline means the timer never runs.
27
+ - **Active** — checkbox. Inactive timers are skipped by `get_due_timers`.
28
+ - **Description** — string, optional.
29
+ - **Config (JSON)** — the schedule. The only key the rescheduler reads today is `interval_minutes` (minutes between runs). The form seeds `{ "schedule": "", "interval_seconds": 3600 }` — neither key is honored by the current RPCs.
30
+ - **Metadata (JSON)** — optional metadata.
31
+
32
+ **Downstream effects**
33
+
34
+ - `get_due_timers` returns active timers with `pipeline_id IS NOT NULL` and `next_execution <= now()`. The timers API arms `next_execution` automatically on create/activate when `config.interval_minutes` (or `interval_seconds`) resolves to a positive interval — or pass `next_execution` explicitly.
35
+ - `update_timer_after_run` reschedules `next_execution = now() + interval` only when `config.interval_minutes` is a positive number; otherwise it sets `next_execution = NULL` and the timer becomes one-shot/dead. `timer_type`, `interval_seconds`, `schedule`, and `run_at` are ignored by the RPC.
36
+ - Each firing also stamps `last_execution` and increments `execution_count` / `success_count` / `failure_count` — the source of the list page's Executions and Success Rate columns.
37
+ - True cron expressions live on the separate `public.schedules` table (read by `get_due_schedules`), not on `timers` — a `cron`-type timer is a label, not a working cron.
38
+
39
+ ## Minimal configuration
40
+
41
+ ```json
42
+ {
43
+ "name": "Hourly sync",
44
+ "timer_type": "interval",
45
+ "pipeline_id": "<pipeline uuid>",
46
+ "is_active": true,
47
+ "config": { "interval_minutes": 60 }
48
+ }
49
+ ```
50
+
51
+ ## Full configuration
52
+
53
+ ```json
54
+ {
55
+ "name": "Nightly-ish rollup",
56
+ "timer_type": "interval",
57
+ "pipeline_id": "<pipeline uuid>",
58
+ "is_active": true,
59
+ "config": { "interval_minutes": 1440 },
60
+ "metadata": { "owner": "platform", "notes": "fires ~daily once next_execution is seeded" }
61
+ }
62
+ ```
63
+
64
+ ## Example
65
+
66
+ A support app keeps an `interval` timer with `interval_minutes: 360` to re-check stale tickets every six hours, and a second timer whose `interval_minutes` was left blank to run a one-off backfill — after its first fire, `update_timer_after_run` writes `next_execution = NULL` so it never re-fires.
67
+
68
+ ## Core files
69
+
70
+ - `.framework/functions/timers.ts` — API (list/get/create/update/delete; does not set `next_execution`)
71
+ - `.framework/functions/system-cron.ts` — per-tick driver: calls `get_due_timers`, runs `runPipeline` as a `timer:<id>` principal, then `update_timer_after_run`
72
+ - `.framework/functions/trigger-scheduler.ts` — Netlify scheduled entry point that delegates to `system-cron`
73
+ - `.framework/migrations/068_cron_schedule_timer_rpcs.sql` — the `get_due_timers` / `update_timer_after_run` RPCs
74
+ - `.framework/src/pages/admin/TimersPage.tsx` / `TimerDetailPage.tsx` — admin UI
75
+ - Table: `public.timers` (`timer_type`, `pipeline_id`, `next_execution`, `execution_count` added by migration 020)
76
+
77
+ ## Agent notes
78
+
79
+ - The list's "Next Execution" column shows `Not scheduled` when `next_execution` is NULL — an active timer in that state cannot fire until it is re-armed (edit its interval or toggle it off/on).
80
+ - A timer with a blank `pipeline_id` saves fine but is filtered out by `get_due_timers` — always pick the pipeline.
81
+ - To arm a new timer, set `next_execution` in the DB (e.g. `now() + interval`); after the first successful run, `interval_minutes` keeps it rescheduling.
82
+ - "Success Rate" is `success_count / execution_count`; failures still reschedule — a broken pipeline does not stop the cadence.
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Triggers
3
+ audience: both
4
+ kind: entity
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/triggers.ts
9
+ - .framework/functions/_shared/trigger-engine.ts
10
+ - .framework/functions/trigger-scheduler.ts
11
+ - .framework/src/pages/admin/TriggersPage.tsx
12
+ - .framework/src/pages/admin/TriggerDetailPage.tsx
13
+ - .framework/migrations/064_episode_leases.sql
14
+ ---
15
+
16
+ ## What this is for
17
+
18
+ A **trigger** listens for a Spine event and, when active, hands the event payload to a pipeline for processing. It is the link between emitted platform events (e.g. `item_created`) and the workflows that react to them.
19
+
20
+ ## Fields and settings
21
+
22
+ - **Name** — string. Display name; missing name prevents saving.
23
+ - **Event Type** — string. The event to listen to, e.g. `item_created`. Must match the emitted event name **exactly** — a typo means the trigger never fires.
24
+ - **App ID** — select, optional. Owning app; blank = account-scoped.
25
+ - **Pipeline ID** — select, required at create (`name`, `event_type`, `pipeline_id` are all required by the API). The pipeline invoked when the trigger fires; a stale ID means the queued execution fails instead of running.
26
+ - **Active** — checkbox. Inactive triggers are skipped at fire time — matching events produce no run.
27
+ - **Description** — string, optional.
28
+ - **Config (JSON)** — trigger-engine configuration, e.g. filters or payload mapping.
29
+ - **Metadata (JSON)** — optional metadata.
30
+
31
+ **Downstream effects**
32
+
33
+ - The trigger engine (`admin-data` writes → `_shared/trigger-engine`) matches emitted events to active triggers by `event_type`; real event names use underscores — `item_created`, `item_updated`, `item_deleted`, `thread_created`, `message_created`, etc. (see the `EventType` union in `_shared/trigger-engine.ts`).
34
+ - `config` filters narrow which matching events actually invoke the pipeline.
35
+ - A wrong or stale `pipeline_id` means the trigger fires, queues an execution row, and the run fails — check Pipeline Executions, not just the trigger, when "nothing happened".
36
+
37
+ ## Minimal configuration
38
+
39
+ ```json
40
+ {
41
+ "name": "On item created",
42
+ "event_type": "item_created",
43
+ "pipeline_id": "<pipeline uuid>",
44
+ "is_active": true
45
+ }
46
+ ```
47
+
48
+ ## Full configuration
49
+
50
+ Adds `config` with payload filters (e.g. match only `item_created` where `type_slug = "ticket"`), field mappings into the pipeline input, and `metadata` documenting the contract the event producer is expected to emit.
51
+
52
+ ## Example
53
+
54
+ A support app fires `item_created` filtered to `type_slug = "ticket"`; the trigger invokes its triage pipeline, which assigns the ticket and posts a thread message. A second trigger on `item_updated` handles SLA warnings — both share the same pipeline pattern but different filters.
55
+
56
+ ## Core files
57
+
58
+ - `.framework/functions/triggers.ts` — API
59
+ - `.framework/functions/_shared/trigger-engine.ts` — event matching and dispatch to `pipeline-runner-background`
60
+ - `.framework/functions/trigger-scheduler.ts` — Netlify scheduled entry point (delegates to `system-cron`)
61
+ - `.framework/src/pages/admin/TriggersPage.tsx` / `TriggerDetailPage.tsx` — admin UI
62
+ - Table: `public.triggers`
63
+
64
+ ## Agent notes
65
+
66
+ - Event names are `<entity>_<past tense>` (`item_created`, `message_deleted`, ...) — the Event Type input's own placeholder still shows the stale `item.created` form; use underscores.
67
+ - `config` filters (`entity_type`, `type_slug`, `filters` with `$eq`/`$ne`/`$gt`/etc.) narrow which matching events actually invoke the pipeline.
68
+ - The list's "Total Fires" stat and "Fires"/"Last Fired" columns count trigger firings (the `record_trigger_fire` RPC), not successful pipeline runs — a trigger can fire while its pipeline fails.
69
+ - `config` must be a JSON **object**, not a string — the second most common save failure after event-type typos.
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Types
3
+ audience: both
4
+ kind: entity
5
+ verified_against: e13127d
6
+ verified_on: 2026-10-30
7
+ source_files:
8
+ - .framework/functions/types.ts
9
+ - .framework/src/pages/admin/TypesPage.tsx
10
+ - .framework/src/pages/admin/TypeDetailPage.tsx
11
+ - .framework/functions/_shared/permissions.ts
12
+ - .framework/functions/_shared/resolve-ids.ts
13
+ - .framework/functions/_shared/schema-utils.ts
14
+ - .framework/cli/commands/install-app.ts
15
+ - .framework/migrations/015_types_kind_slug_unique.sql
16
+ ---
17
+
18
+ ## What this is for
19
+
20
+ A **type** is the design schema for a runtime record kind. It defines the fields, default list/detail views, record permissions, icon, and color that the generic admin and runtime UIs use to create, validate, and display records. Every `items`/`threads`/`messages`/etc. record is stamped with its type's `design_schema` at creation.
21
+
22
+ ## Fields and settings
23
+
24
+ - **Name** — string. Display label for the type. Empty name blocks save.
25
+ - **Slug** — string. URL-friendly identifier, unique per `kind`. Callers resolve types by slug; duplicates break `resolveTypeId`/`.single()` lookups. Rarely change after records exist.
26
+ - **Description** — string, optional. Human-readable context.
27
+ - **Kind** — select: `item`, `account`, `person`, `thread`, `message`, `link`, `attachment`, `watcher`. Determines which runtime table the type governs.
28
+ - **Ownership** — select: `system` (platform types, protected from deletion), `app` (owned by an app), `tenant` (account-specific).
29
+ - **Is Active** — checkbox. Inactive types are hidden from lists and cannot have records created.
30
+ - **App ID** — select. Required whenever ownership is not `system` — the form rejects save otherwise.
31
+ - **Icon / Color** — UI presentation stored on the `types` row.
32
+ - **Design Schema (JSON)** — the full schema: `fields`, `views` (`default_list`, `default_detail`), `record_permissions`, `scope`, `functionality`.
33
+ - **Validation Schema** — read-only, regenerated from `design_schema` on every save by the API.
34
+
35
+ **Downstream effects**
36
+
37
+ - Generic list/detail pages render directly from `views`. A missing `default_list`/`default_detail` or unknown `display_type` breaks those pages.
38
+ - `record_permissions` is the **base, most restrictive grant**: if a role can read/update a record, all fields default to read/write. `fields.*.permissions` only *loosen* access per field or restrict `system: true` fields.
39
+ - `system: true` fields are read from `record.<field>`; non-system fields from `record.data.<field>`.
40
+ - Changing `data_type` on a field with existing records does not migrate data — old values remain until rewritten.
41
+ - Type seeds use `conflict: "kind,slug"` with `inject_app_id: false` — types are global, not duplicated per app install.
42
+
43
+ ## Minimal configuration
44
+
45
+ ```json
46
+ {
47
+ "scope": "account",
48
+ "fields": {
49
+ "title": { "data_type": "text", "label": "Title", "required": true, "system": true }
50
+ },
51
+ "views": {
52
+ "default_list": {
53
+ "type": "list", "display": "table", "label": "List",
54
+ "fields": {
55
+ "title": { "sortable": true, "display_type": "text" },
56
+ "created_at": { "sortable": true, "display_type": "timestamp" }
57
+ },
58
+ "default_sort": { "field": "created_at", "direction": "desc" }
59
+ },
60
+ "default_detail": {
61
+ "type": "detail", "label": "Detail",
62
+ "sections": [{ "title": "Details", "fields": { "title": { "display_type": "input" } } }]
63
+ }
64
+ },
65
+ "record_permissions": { "system_admin": ["create", "read", "update", "delete"] },
66
+ "functionality": null
67
+ }
68
+ ```
69
+
70
+ ## Full configuration
71
+
72
+ Adds typed fields, select `options`, per-field `permissions` overrides, multiple views with sorting/badges, and per-role record grants (`admin`, `member`). See the [design schema reference](reference/design-schema.md) for the full `data_type` list (`text`, `number`, `select`, `multiselect`, `reference`, `json`, `date`, `boolean`, ...) and `display_type` list (`badge`, `timestamp`, `select`, `currency`, ...).
73
+
74
+ ## Example
75
+
76
+ A support app defines a `ticket` type (kind `item`, ownership `app`): `title` (system text), `status` (select: open/pending/closed), `priority` (select), `assignee_id` (reference). `record_permissions` grants `system_admin` all actions, `admin` create/read/update, `member` read. `fields.assignee_id.permissions` loosens `write` to `member` so agents can self-assign.
77
+
78
+ ## Core files
79
+
80
+ - `.framework/functions/types.ts` — API (list/get/create/update/delete; regenerates `validation_schema`)
81
+ - `.framework/src/pages/admin/TypesPage.tsx` / `TypeDetailPage.tsx` — admin UI
82
+ - `.framework/src/components/app-shell/GenericListPage.tsx` / `GenericDetailPage.tsx` — generic renderer
83
+ - `.framework/functions/_shared/permissions.ts` — record/field permission evaluation
84
+ - [Design schema reference](reference/design-schema.md) — full field/view schema reference in this book
85
+ - Table: `public.types`
86
+
87
+ ## Agent notes
88
+
89
+ - Never hand-edit `validation_schema`; change `design_schema` and the API regenerates it.
90
+ - Slug uniqueness is per `(kind, slug)` — reuse across kinds is fine, reuse within a kind breaks lookups.
91
+ - Every field needs `data_type`, `label`, `required`; `select`/`multiselect`/`radio` also need `options`.
92
+ - Don't add `fields.*.permissions` unless overriding the base grant — the common mistake is re-stating record-level permissions per field.
93
+ - Custom apps CRUD runtime records through `/api/admin-data` — never custom wrappers.
@@ -0,0 +1,128 @@
1
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/lib/branding.ts"]
2
+ // CIL[DOC-MACRO purpose="Instance branding resolved from spine.config.json — app name, logo, favicon, document title"]
3
+ // CIL[DOC-RESPONSIBILITY responsibility="Load the optional branding block from /spine.config.json once per session, merge it over Spine defaults, and apply favicon/title to the document"]
4
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/hooks/useBranding.ts,spine.config.json,.framework/index.html"]
5
+ // CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding resolved object; applyBrandingToDocument performs DOM mutation" errors="fetch or parse failures fall back to DEFAULT_BRANDING" side_effects="HTTP GET /spine.config.json (single cached request); document.title and link[rel=icon] mutation"]
6
+ // CIL[DOC-MICRO logic_summary="Module-level cache plus single inflight fetch; resolveBranding fills missing keys with defaults; applyBrandingToDocument sets document.title and swaps the favicon link href" notable_decisions="Branding lives in instance-owned spine.config.json so framework upgrades can never overwrite app branding; index.html keeps the Spine default and the runtime swap handles app-branded instances"]
7
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
8
+ // CIL[DOC-STABILITY stability="experimental"]
9
+ // CIL[DOC-AUDIENCE audience="framework developers"]
10
+ // CIL[DOC-NOTE type="contract" content="branding is a top-level sibling of registration in spine.config.json. All keys optional; absent block yields DEFAULT_BRANDING so bare framework installs stay Spine-branded."]
11
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:module_header:module_header"]
12
+
13
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:spinebranding:interface" type="interface" identity="SpineBranding" parent="~/.framework/src/lib/branding.ts"]
14
+ // CIL[DOC-MACRO purpose="Resolved instance branding shape"]
15
+ // CIL[DOC-RESPONSIBILITY responsibility="Type for the branding block after defaults are applied"]
16
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
17
+ // CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding object" errors="none" side_effects="none"]
18
+ // CIL[DOC-MICRO logic_summary="app_name drives headings and logo alt; title drives document.title and wordmark text; logo_url and favicon_url are public/ asset paths" notable_decisions="title falls back to app_name, then to 'Spine Framework' when no branding block exists"]
19
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
20
+ // CIL[DOC-STABILITY stability="experimental"]
21
+ export interface SpineBranding {
22
+ app_name: string
23
+ title: string
24
+ logo_url: string
25
+ favicon_url: string
26
+ }
27
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:spinebranding:interface"]
28
+
29
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:default_branding:constant" type="constant" identity="DEFAULT_BRANDING" parent="~/.framework/src/lib/branding.ts"]
30
+ // CIL[DOC-MACRO purpose="Spine Framework default branding used when spine.config.json has no branding block"]
31
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide the fallback brand so the framework is self-branded out of the box"]
32
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/index.html"]
33
+ // CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding constant" errors="none" side_effects="none"]
34
+ // CIL[DOC-MICRO logic_summary="Matches the static values in .framework/index.html (title 'Spine Framework', favicon /spine-logo.svg)" notable_decisions="[]"]
35
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
36
+ // CIL[DOC-STABILITY stability="experimental"]
37
+ export const DEFAULT_BRANDING: SpineBranding = {
38
+ app_name: 'Spine',
39
+ title: 'Spine Framework',
40
+ logo_url: '/spine-logo.svg',
41
+ favicon_url: '/spine-logo.svg',
42
+ }
43
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:default_branding:constant"]
44
+
45
+ let cached: SpineBranding | null = null
46
+ let inflight: Promise<SpineBranding> | null = null
47
+
48
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:resolvebranding:function" type="function" identity="resolveBranding" parent="~/.framework/src/lib/branding.ts"]
49
+ // CIL[DOC-MACRO purpose="Merge a raw branding block over defaults"]
50
+ // CIL[DOC-RESPONSIBILITY responsibility="Produce a complete SpineBranding from a possibly partial branding config value"]
51
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
52
+ // CIL[DOC-CONTRACT inputs="raw: unknown (config.branding)" outputs="SpineBranding" errors="none" side_effects="none"]
53
+ // CIL[DOC-MICRO logic_summary="Each key falls back independently; title falls back to the resolved app_name so app-branded instances get a sane tab title without duplicating config" notable_decisions="non-string values treated as absent"]
54
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
55
+ // CIL[DOC-STABILITY stability="experimental"]
56
+ function resolveBranding(raw: unknown): SpineBranding {
57
+ const b = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>
58
+ const appName = typeof b.app_name === 'string' && b.app_name ? b.app_name : DEFAULT_BRANDING.app_name
59
+ const configuredTitle = typeof b.title === 'string' && b.title ? b.title : null
60
+ return {
61
+ app_name: appName,
62
+ title: configuredTitle ?? (b.app_name ? appName : DEFAULT_BRANDING.title),
63
+ logo_url: typeof b.logo_url === 'string' && b.logo_url ? b.logo_url : DEFAULT_BRANDING.logo_url,
64
+ favicon_url:
65
+ typeof b.favicon_url === 'string' && b.favicon_url ? b.favicon_url : DEFAULT_BRANDING.favicon_url,
66
+ }
67
+ }
68
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:resolvebranding:function"]
69
+
70
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:loadbranding:function" type="function" identity="loadBranding" parent="~/.framework/src/lib/branding.ts"]
71
+ // CIL[DOC-MACRO purpose="Fetch and cache the instance branding from /spine.config.json"]
72
+ // CIL[DOC-RESPONSIBILITY responsibility="Perform a single fetch per session and memoize the resolved branding"]
73
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/src/hooks/useBranding.ts"]
74
+ // CIL[DOC-CONTRACT inputs="none" outputs="Promise<SpineBranding>" errors="network/parse failures resolve to DEFAULT_BRANDING" side_effects="one HTTP GET /spine.config.json per session"]
75
+ // CIL[DOC-MICRO logic_summary="Returns cached value if present; otherwise shares one inflight promise across callers; any failure caches defaults so a missing config never retries per component" notable_decisions="[]"]
76
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
77
+ // CIL[DOC-STABILITY stability="experimental"]
78
+ export function loadBranding(): Promise<SpineBranding> {
79
+ if (cached) return Promise.resolve(cached)
80
+ if (!inflight) {
81
+ inflight = fetch('/spine.config.json')
82
+ .then(res => (res.ok ? res.json() : null))
83
+ .then(config => {
84
+ cached = resolveBranding(config?.branding)
85
+ return cached
86
+ })
87
+ .catch(() => {
88
+ cached = DEFAULT_BRANDING
89
+ return cached
90
+ })
91
+ }
92
+ return inflight
93
+ }
94
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:loadbranding:function"]
95
+
96
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:getcachedbranding:function" type="function" identity="getCachedBranding" parent="~/.framework/src/lib/branding.ts"]
97
+ // CIL[DOC-MACRO purpose="Synchronous read of resolved branding, or defaults before the fetch lands"]
98
+ // CIL[DOC-RESPONSIBILITY responsibility="Give render paths a branding value without awaiting"]
99
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts"]
100
+ // CIL[DOC-CONTRACT inputs="none" outputs="SpineBranding" errors="none" side_effects="none"]
101
+ // CIL[DOC-MICRO logic_summary="cached ?? DEFAULT_BRANDING" notable_decisions="[]"]
102
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
103
+ // CIL[DOC-STABILITY stability="experimental"]
104
+ export function getCachedBranding(): SpineBranding {
105
+ return cached ?? DEFAULT_BRANDING
106
+ }
107
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:getcachedbranding:function"]
108
+
109
+ // CIL[BEGIN stable_id="~/.framework/src/lib/branding.ts:applybrandingtodocument:function" type="function" identity="applyBrandingToDocument" parent="~/.framework/src/lib/branding.ts"]
110
+ // CIL[DOC-MACRO purpose="Apply resolved branding to document.title and the favicon link"]
111
+ // CIL[DOC-RESPONSIBILITY responsibility="Swap the framework-shipped favicon/title for instance branding at runtime"]
112
+ // CIL[DOC-CONTEXT module="framework-api" layer="lib" related_files=".framework/src/lib/branding.ts,.framework/index.html"]
113
+ // CIL[DOC-CONTRACT inputs="branding: SpineBranding" outputs="void" errors="none" side_effects="sets document.title; rewrites or creates link[rel~=icon] in document.head"]
114
+ // CIL[DOC-MICRO logic_summary="Idempotent DOM mutation — safe to call on every branding state change from every consumer" notable_decisions="runtime swap keeps .framework/index.html a static framework-owned file that upgrades can replace freely"]
115
+ // CIL[DOC-CONFIDENCE pass="first" score="0.9" flags="none"]
116
+ // CIL[DOC-STABILITY stability="experimental"]
117
+ export function applyBrandingToDocument(branding: SpineBranding): void {
118
+ if (branding.title) document.title = branding.title
119
+ if (!branding.favicon_url) return
120
+ let link = document.querySelector<HTMLLinkElement>('link[rel~="icon"]')
121
+ if (!link) {
122
+ link = document.createElement('link')
123
+ link.rel = 'icon'
124
+ document.head.appendChild(link)
125
+ }
126
+ link.href = branding.favicon_url
127
+ }
128
+ // CIL[END stable_id="~/.framework/src/lib/branding.ts:applybrandingtodocument:function"]
@@ -10,6 +10,7 @@
10
10
 
11
11
  import React from 'react'
12
12
  import { useAuth } from '../contexts/AuthContext'
13
+ import { useBranding } from '../hooks/useBranding'
13
14
  // CIL[END stable_id="~/.framework/src/pages/NoAppPage.tsx:module_header:module_header"]
14
15
 
15
16
  // CIL[BEGIN stable_id="~/.framework/src/pages/NoAppPage.tsx:noapppage:component" type="component" identity="NoAppPage" parent="~/.framework/src/pages/NoAppPage.tsx"]
@@ -22,12 +23,13 @@ import { useAuth } from '../contexts/AuthContext'
22
23
  // CIL[DOC-STABILITY stability="experimental"]
23
24
  export function NoAppPage() {
24
25
  const { logout } = useAuth()
26
+ const branding = useBranding()
25
27
 
26
28
  return (
27
29
  <div className="min-h-screen flex items-center justify-center bg-slate-50 px-4">
28
30
  <div className="max-w-md w-full text-center space-y-6">
29
31
  <div className="mx-auto flex items-center justify-center">
30
- <img src="/spine-logo.svg" alt="Spine Framework" className="h-14 w-auto" />
32
+ <img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
31
33
  </div>
32
34
  <div className="space-y-2">
33
35
  <h1 className="text-2xl font-semibold text-slate-900">No apps yet</h1>
@@ -1,35 +1,65 @@
1
1
  // CIL[BEGIN stable_id="~/.framework/src/pages/NotFoundPage.tsx:module_header:module_header" type="module_header" identity="module_header" parent="~/.framework/src/pages/NotFoundPage.tsx"]
2
- // CIL[DOC-MACRO purpose="Import dependencies used by the NotFoundPage 404 component"]
3
- // CIL[DOC-RESPONSIBILITY responsibility="Provide React, react-router-dom Link, and lucide-react Home icon for the page"]
4
- // CIL[DOC-CONTEXT module=".framework/src/pages" layer="page" related_files=".framework/src/pages/NotFoundPage.tsx"]
5
- // CIL[DOC-CONTRACT inputs="N/A" outputs="N/A" errors="N/A" side_effects="N/A"]
6
- // CIL[DOC-MICRO logic_summary="Imports React, Link, and Home icon; no runtime logic" notable_decisions="[]"]
7
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
8
- // CIL[DOC-STABILITY stability="stable"]
9
- // CIL[DOC-AUDIENCE audience="Spine framework page authors"]
2
+ /**
3
+ * @module NotFoundPage
4
+ * @audience both
5
+ * @layer shared-core
6
+ * @stability stable
7
+ *
8
+ * This module renders a Not Found page for the application, providing users with a friendly message when they navigate to an undefined route. It serves as a fallback UI component, enhancing user experience by guiding them back to the home page. INVARIANT: The Not Found page should always be accessible for any invalid routes.
9
+ *
10
+ * @seeAlso react-router-dom (provides routing capabilities).
11
+ * @seeAlso lucide-react (used for icons).
12
+ *
13
+ * @cil-generated
14
+ */
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
10
25
 
11
26
 
12
27
 
13
28
  import React from 'react'
14
29
  import { Link } from 'react-router-dom'
15
30
  import { Home } from 'lucide-react';
16
- // CIL[DOC-VECTOR type="context" content="purpose='Import dependencies used by the NotFoundPage 404 component'; No verified external callers; imports: none"]
17
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
18
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
19
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
20
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
21
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
22
31
  // CIL[END stable_id="~/.framework/src/pages/NotFoundPage.tsx:module_header:module_header"]
23
32
 
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
24
51
  // CIL[BEGIN stable_id="~/.framework/src/pages/NotFoundPage.tsx:NotFoundPage:component" type="component" identity="NotFoundPage" parent="~/.framework/src/pages/NotFoundPage.tsx"]
25
- // CIL[DOC-MACRO purpose="Render a friendly 404 not found page with a link back to sign in"]
26
- // CIL[DOC-RESPONSIBILITY responsibility="Display a centered 404 message and a Home icon link that navigates to /login"]
27
- // CIL[DOC-CONTEXT module=".framework/src/pages" layer="page" related_files=".framework/src/pages/NotFoundPage.tsx"]
28
- // CIL[DOC-CONTRACT inputs="None" outputs="JSX 404 page" errors="None" side_effects="None"]
29
- // CIL[DOC-MICRO logic_summary="Renders a full-screen 404 page with a heading, description, and a Link to /login" notable_decisions="[]"]
30
- // CIL[DOC-CONFIDENCE pass="second" score="0.90" flags="none"]
31
- // CIL[DOC-STABILITY stability="stable"]
32
- // CIL[DOC-AUDIENCE audience="End users who reach an unknown route"]
52
+ /**
53
+ * The NotFoundPage component displays a 404 error message when a page is not found, providing a user-friendly interface to guide users back to the home page.
54
+ *
55
+ * @macro React functional component responsible for rendering a 404 error page.
56
+ * @micro Utilizes JSX to structure the layout and styling of the error message and navigation link.
57
+ * @outputSpec JSX.Element
58
+ * @sideEffects none
59
+ * @stability internal
60
+ * @calls Link
61
+ * @cil-generated
62
+ */
33
63
  export function NotFoundPage() {
34
64
  return (
35
65
  <div className="min-h-screen bg-slate-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
@@ -43,11 +73,11 @@ export function NotFoundPage() {
43
73
 
44
74
  <div className="mt-6">
45
75
  <Link
46
- to="/login"
76
+ to="/"
47
77
  className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
48
78
  >
49
79
  <Home className="w-4 h-4 mr-2" />
50
- Go to sign in
80
+ Go home
51
81
  </Link>
52
82
  </div>
53
83
  </div>
@@ -55,10 +85,11 @@ export function NotFoundPage() {
55
85
  </div>
56
86
  )
57
87
  }
58
- // CIL[DOC-VECTOR type="context" content="purpose='Render a friendly 404 not found page with a link back to sign in'; No verified external callers; imports: none"]
59
- // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
60
- // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
61
- // CIL[DOC-VECTOR type="constraint" content="Standard runtime; no special invariants documented"]
62
- // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
63
- // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
64
- // CIL[END stable_id="~/.framework/src/pages/NotFoundPage.tsx:NotFoundPage:component"]
88
+ // CIL[END stable_id="~/.framework/src/pages/NotFoundPage.tsx:NotFoundPage:component"]
89
+
90
+
91
+
92
+
93
+
94
+
95
+
@@ -10,6 +10,7 @@
10
10
 
11
11
  import React from 'react'
12
12
  import { Link } from 'react-router-dom'
13
+ import { useBranding } from '../hooks/useBranding'
13
14
  // CIL[DOC-VECTOR type="context" content="purpose='Declare module-level imports for the Setup page'; No verified external callers; imports: none"]
14
15
  // CIL[DOC-VECTOR type="blast_radius" content="No direct caller data from CIL graph; local impact only"]
15
16
  // CIL[DOC-VECTOR type="configuration" content="No environment or build configuration dependencies identified"]
@@ -28,15 +29,17 @@ import { Link } from 'react-router-dom'
28
29
  // CIL[DOC-STABILITY stable]
29
30
 
30
31
  export function SetupPage() {
32
+ const branding = useBranding()
33
+
31
34
  return (
32
35
  <div className="min-h-screen flex items-center justify-center bg-slate-50 py-12 px-4 sm:px-6 lg:px-8">
33
36
  <div className="max-w-lg w-full space-y-8">
34
37
  <div className="text-center">
35
38
  <div className="mx-auto flex items-center justify-center">
36
- <img src="/spine-logo.svg" alt="Spine Framework" className="h-14 w-auto" />
39
+ <img src={branding.logo_url} alt={branding.app_name} className="h-14 w-auto" />
37
40
  </div>
38
41
  <h2 className="mt-6 text-3xl font-extrabold text-slate-900">
39
- Welcome to Spine
42
+ Welcome to {branding.app_name}
40
43
  </h2>
41
44
  <p className="mt-2 text-sm text-slate-600">
42
45
  Your project is running but no apps are installed yet.