spine-framework 0.3.124 → 0.3.126

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 (319) hide show
  1. package/.framework/cli/commands/agents.ts +35 -37
  2. package/.framework/cli/commands/auth.ts +35 -34
  3. package/.framework/cli/commands/create-app.ts +104 -40
  4. package/.framework/cli/commands/dev.ts +104 -69
  5. package/.framework/cli/commands/doctor.ts +179 -69
  6. package/.framework/cli/commands/generate.ts +83 -143
  7. package/.framework/cli/commands/init.ts +72 -34
  8. package/.framework/cli/commands/install-app.ts +260 -125
  9. package/.framework/cli/commands/items.ts +33 -44
  10. package/.framework/cli/commands/migrate.ts +122 -40
  11. package/.framework/cli/commands/migrations.ts +33 -40
  12. package/.framework/cli/commands/pipelines.ts +31 -39
  13. package/.framework/cli/commands/status.ts +47 -23
  14. package/.framework/cli/commands/system.ts +47 -37
  15. package/.framework/cli/commands/test.ts +112 -68
  16. package/.framework/cli/commands/validate-app.ts +215 -121
  17. package/.framework/cli/context.ts +112 -130
  18. package/.framework/cli/env-loader.ts +47 -18
  19. package/.framework/cli/index.ts +81 -42
  20. package/.framework/docs/runtime-types.md +1024 -0
  21. package/.framework/functions/_shared/agent-runner.ts +1296 -961
  22. package/.framework/functions/_shared/app-manifest.ts +82 -68
  23. package/.framework/functions/_shared/audit.ts +135 -83
  24. package/.framework/functions/_shared/{cil-state.ts → cil.ts} +158 -56
  25. package/.framework/functions/_shared/db.ts +80 -126
  26. package/.framework/functions/_shared/hash-api-key.ts +63 -0
  27. package/.framework/functions/_shared/index.ts +224 -335
  28. package/.framework/functions/_shared/kb.ts +319 -0
  29. package/.framework/functions/_shared/llm.ts +92 -0
  30. package/.framework/functions/_shared/logging.ts +288 -0
  31. package/.framework/functions/_shared/middleware.ts +329 -365
  32. package/.framework/functions/_shared/outcomes.ts +36 -0
  33. package/.framework/functions/_shared/permissions.ts +359 -643
  34. package/.framework/functions/_shared/pipeline-runner.ts +243 -316
  35. package/.framework/functions/_shared/principal.ts +376 -468
  36. package/.framework/functions/_shared/resolve-credentials.ts +102 -0
  37. package/.framework/functions/_shared/resolve-ids.ts +188 -16
  38. package/.framework/functions/_shared/retry-deliveries.ts +104 -0
  39. package/.framework/functions/_shared/schema-utils.ts +611 -373
  40. package/.framework/functions/_shared/testing.ts +207 -137
  41. package/.framework/functions/_shared/tracing.ts +276 -0
  42. package/.framework/functions/_shared/trigger-engine.ts +159 -211
  43. package/.framework/functions/_shared/webhook-registration.ts +95 -81
  44. package/.framework/functions/_shared/webhook-registry.ts +153 -75
  45. package/.framework/functions/account-nodes.ts +31 -53
  46. package/.framework/functions/adapters/generic-webhook-inbound.ts +56 -0
  47. package/.framework/functions/adapters/generic-webhook-outbound.ts +43 -0
  48. package/.framework/functions/adapters/openai-outbound.ts +40 -0
  49. package/.framework/functions/admin-data.ts +685 -537
  50. package/.framework/functions/admin-funnel.ts +31 -20
  51. package/.framework/functions/admin-install-funnel.ts +47 -28
  52. package/.framework/functions/admin-signals.ts +47 -29
  53. package/.framework/functions/ai-agents.ts +122 -147
  54. package/.framework/functions/ai-interactions.ts +180 -90
  55. package/.framework/functions/api-keys.ts +250 -192
  56. package/.framework/functions/apps.ts +203 -197
  57. package/.framework/functions/auth.ts +98 -91
  58. package/.framework/functions/cil-agent-ingest.ts +773 -0
  59. package/.framework/functions/cil-agent.ts +592 -0
  60. package/.framework/functions/cil-callmap.ts +1227 -0
  61. package/.framework/functions/cil-config.ts +583 -0
  62. package/.framework/functions/cil-context.ts +445 -0
  63. package/.framework/functions/cil-doc.ts +1752 -0
  64. package/.framework/functions/cil-embeddings.ts +1202 -0
  65. package/.framework/functions/cil-graph.ts +585 -0
  66. package/.framework/functions/cil-ingest.ts +2416 -0
  67. package/.framework/functions/cil-pipeline.ts +318 -0
  68. package/.framework/functions/cil-project.ts +499 -0
  69. package/.framework/functions/cil-validate.ts +469 -0
  70. package/.framework/functions/debug-auth.ts +73 -53
  71. package/.framework/functions/deliveries.ts +117 -0
  72. package/.framework/functions/embeddings.ts +528 -516
  73. package/.framework/functions/eval-harness.ts +158 -106
  74. package/.framework/functions/integration-outbound.ts +311 -0
  75. package/.framework/functions/integration-routes.ts +240 -466
  76. package/.framework/functions/integrations.ts +132 -140
  77. package/.framework/functions/invites.ts +401 -201
  78. package/.framework/functions/item-progress.ts +113 -53
  79. package/.framework/functions/kb.ts +28 -0
  80. package/.framework/functions/logs.ts +239 -222
  81. package/.framework/functions/observability.ts +308 -259
  82. package/.framework/functions/pipeline-executions.ts +179 -235
  83. package/.framework/functions/pipelines.ts +163 -205
  84. package/.framework/functions/prompt-configs.ts +109 -156
  85. package/.framework/functions/roles.ts +97 -147
  86. package/.framework/functions/system-cron.ts +132 -218
  87. package/.framework/functions/system.ts +112 -52
  88. package/.framework/functions/tests.ts +32 -28
  89. package/.framework/functions/timers.ts +162 -210
  90. package/.framework/functions/triggers.ts +282 -346
  91. package/.framework/functions/types.ts +283 -400
  92. package/.framework/migrations/001_core_schema.sql +630 -0
  93. package/.framework/migrations/002_core_rls_and_functions.sql +532 -0
  94. package/.framework/migrations/{003_system_data.sql → 003_core_system_data.sql} +26 -153
  95. package/.framework/migrations/004_core_rpc_and_broker.sql +421 -0
  96. package/.framework/migrations/005_core_observability.sql +598 -0
  97. package/.framework/migrations/006_security_hardening_rls.sql +97 -0
  98. package/.framework/migrations/007_kb_article_list.sql +237 -0
  99. package/.framework/migrations/008_items_target.sql +8 -0
  100. package/.framework/migrations/009_roles_slug_unique.sql +11 -0
  101. package/.framework/public/spine.config.json +91 -0
  102. package/.framework/scripts/app-install-cli.ts +112 -98
  103. package/.framework/src/App.tsx +67 -20
  104. package/.framework/src/apps/admin/index.tsx +97 -27
  105. package/.framework/src/components/AppWrapper.tsx +50 -11
  106. package/.framework/src/components/CustomAppLoader.tsx +131 -22
  107. package/.framework/src/components/admin/AdminListPage.tsx +75 -8
  108. package/.framework/src/components/admin/AdminSidebar.tsx +148 -36
  109. package/.framework/src/components/admin/AdminStatsCard.tsx +48 -29
  110. package/.framework/src/components/admin/PageInstructions.tsx +49 -0
  111. package/.framework/src/components/admin/SortableTableHeader.tsx +49 -2
  112. package/.framework/src/components/app-shell/GenericAppShell.tsx +69 -11
  113. package/.framework/src/components/app-shell/GenericDetailPage.tsx +55 -10
  114. package/.framework/src/components/app-shell/GenericListPage.tsx +76 -9
  115. package/.framework/src/components/app-sidebar.tsx +50 -3
  116. package/.framework/src/components/auth/ProtectedRoute.tsx +53 -45
  117. package/.framework/src/components/kb/KBArticleEditor.tsx +409 -0
  118. package/.framework/src/components/kb/KBArticleList.tsx +557 -0
  119. package/.framework/src/components/layout/AppShell.tsx +54 -16
  120. package/.framework/src/components/layout/Header.tsx +50 -38
  121. package/.framework/src/components/layout/Layout.tsx +48 -32
  122. package/.framework/src/components/layout/Sidebar.tsx +167 -16
  123. package/.framework/src/components/runtime/DataDetailHeader.tsx +33 -1
  124. package/.framework/src/components/runtime/DataDetailPage.tsx +289 -4
  125. package/.framework/src/components/runtime/DataFilters.tsx +78 -1
  126. package/.framework/src/components/runtime/DataHeader.tsx +32 -29
  127. package/.framework/src/components/runtime/DataListPage.tsx +161 -2
  128. package/.framework/src/components/runtime/DataStats.tsx +48 -1
  129. package/.framework/src/components/runtime/DataTable.tsx +153 -1
  130. package/.framework/src/components/runtime/SchemaDetailForm.tsx +77 -46
  131. package/.framework/src/components/runtime/index.ts +16 -9
  132. package/.framework/src/components/search-form.tsx +33 -1
  133. package/.framework/src/components/shared/AgentView.tsx +48 -51
  134. package/.framework/src/components/shared/FieldRenderer.tsx +84 -67
  135. package/.framework/src/components/shared/InviteManager.tsx +113 -52
  136. package/.framework/src/components/shared/MemberList.tsx +182 -0
  137. package/.framework/src/components/shared/SchemaFields.tsx +96 -78
  138. package/.framework/src/components/ui/DataTable.tsx +86 -56
  139. package/.framework/src/components/ui/Form.tsx +188 -109
  140. package/.framework/src/components/ui/ItemCard.tsx +86 -58
  141. package/.framework/src/components/ui/ItemListView.tsx +88 -61
  142. package/.framework/src/components/ui/LoadingSpinner.tsx +52 -27
  143. package/.framework/src/components/ui/Modal.tsx +81 -4
  144. package/.framework/src/components/ui/RichTextEditor.tsx +101 -9
  145. package/.framework/src/components/ui/accordion.tsx +97 -5
  146. package/.framework/src/components/ui/alert-dialog.tsx +225 -13
  147. package/.framework/src/components/ui/alert.tsx +113 -6
  148. package/.framework/src/components/ui/aspect-ratio.tsx +49 -2
  149. package/.framework/src/components/ui/avatar.tsx +129 -7
  150. package/.framework/src/components/ui/badge.tsx +65 -3
  151. package/.framework/src/components/ui/breadcrumb.tsx +145 -8
  152. package/.framework/src/components/ui/button-group.tsx +81 -4
  153. package/.framework/src/components/ui/button.tsx +49 -2
  154. package/.framework/src/components/ui/calendar.tsx +65 -3
  155. package/.framework/src/components/ui/card.tsx +145 -8
  156. package/.framework/src/components/ui/carousel.tsx +129 -7
  157. package/.framework/src/components/ui/chart.tsx +161 -10
  158. package/.framework/src/components/ui/checkbox.tsx +49 -2
  159. package/.framework/src/components/ui/collapsible.tsx +81 -5
  160. package/.framework/src/components/ui/combobox.tsx +305 -20
  161. package/.framework/src/components/ui/command.tsx +177 -11
  162. package/.framework/src/components/ui/context-menu.tsx +273 -17
  163. package/.framework/src/components/ui/dialog.tsx +209 -27
  164. package/.framework/src/components/ui/direction.tsx +64 -4
  165. package/.framework/src/components/ui/drawer.tsx +193 -12
  166. package/.framework/src/components/ui/dropdown-menu.tsx +273 -18
  167. package/.framework/src/components/ui/empty.tsx +145 -9
  168. package/.framework/src/components/ui/field.tsx +209 -14
  169. package/.framework/src/components/ui/hover-card.tsx +81 -4
  170. package/.framework/src/components/ui/input-group.tsx +162 -10
  171. package/.framework/src/components/ui/input-otp.tsx +98 -5
  172. package/.framework/src/components/ui/input.tsx +49 -2
  173. package/.framework/src/components/ui/item.tsx +225 -13
  174. package/.framework/src/components/ui/kbd.tsx +65 -3
  175. package/.framework/src/components/ui/label.tsx +49 -2
  176. package/.framework/src/components/ui/menubar.tsx +289 -17
  177. package/.framework/src/components/ui/native-select.tsx +97 -5
  178. package/.framework/src/components/ui/navigation-menu.tsx +177 -10
  179. package/.framework/src/components/ui/pagination.tsx +161 -9
  180. package/.framework/src/components/ui/popover.tsx +145 -8
  181. package/.framework/src/components/ui/progress.tsx +49 -2
  182. package/.framework/src/components/ui/radio-group.tsx +65 -3
  183. package/.framework/src/components/ui/resizable.tsx +82 -4
  184. package/.framework/src/components/ui/scroll-area.tsx +65 -3
  185. package/.framework/src/components/ui/select.tsx +193 -11
  186. package/.framework/src/components/ui/separator.tsx +49 -2
  187. package/.framework/src/components/ui/sheet.tsx +193 -11
  188. package/.framework/src/components/ui/sidebar.tsx +481 -38
  189. package/.framework/src/components/ui/skeleton.tsx +49 -2
  190. package/.framework/src/components/ui/slider.tsx +49 -2
  191. package/.framework/src/components/ui/sonner.tsx +49 -2
  192. package/.framework/src/components/ui/spinner.tsx +49 -2
  193. package/.framework/src/components/ui/switch.tsx +49 -2
  194. package/.framework/src/components/ui/table-primitives.tsx +160 -33
  195. package/.framework/src/components/ui/table.tsx +161 -9
  196. package/.framework/src/components/ui/tabs.tsx +113 -6
  197. package/.framework/src/components/ui/textarea.tsx +49 -2
  198. package/.framework/src/components/ui/toggle-group.tsx +81 -4
  199. package/.framework/src/components/ui/toggle.tsx +65 -3
  200. package/.framework/src/components/ui/tooltip.tsx +97 -5
  201. package/.framework/src/contexts/AppContext.tsx +129 -45
  202. package/.framework/src/contexts/AuthContext.tsx +187 -144
  203. package/.framework/src/hooks/use-mobile.ts +48 -2
  204. package/.framework/src/hooks/useApi.ts +208 -159
  205. package/.framework/src/hooks/useAppPath.ts +32 -23
  206. package/.framework/src/hooks/useApps.ts +51 -20
  207. package/.framework/src/hooks/useEntityList.ts +32 -78
  208. package/.framework/src/hooks/useEntityRecord.ts +35 -96
  209. package/.framework/src/hooks/useForm.ts +32 -92
  210. package/.framework/src/hooks/useListSchema.ts +49 -78
  211. package/.framework/src/hooks/useSchemaRecord.ts +114 -105
  212. package/.framework/src/lib/api.ts +195 -116
  213. package/.framework/src/lib/supabase.ts +98 -64
  214. package/.framework/src/lib/utils.ts +287 -151
  215. package/.framework/src/main.tsx +49 -3
  216. package/.framework/src/pages/DashboardPage.tsx +52 -29
  217. package/.framework/src/pages/NotFoundPage.tsx +32 -10
  218. package/.framework/src/pages/SetupPage.tsx +32 -12
  219. package/.framework/src/pages/admin/AIAgentDetailPage.tsx +119 -10
  220. package/.framework/src/pages/admin/AIAgentsPage.tsx +48 -19
  221. package/.framework/src/pages/admin/APIKeyDetailPage.tsx +187 -17
  222. package/.framework/src/pages/admin/APIKeysPage.tsx +98 -22
  223. package/.framework/src/pages/admin/AlertsConfigPage.tsx +113 -26
  224. package/.framework/src/pages/admin/AppDetailPage.tsx +125 -8
  225. package/.framework/src/pages/admin/AppsPage.tsx +49 -2
  226. package/.framework/src/pages/admin/DeliveriesPage.tsx +196 -0
  227. package/.framework/src/pages/admin/DesignedPage.tsx +32 -18
  228. package/.framework/src/pages/admin/EmbeddingDetailPage.tsx +215 -484
  229. package/.framework/src/pages/admin/EmbeddingsPage.tsx +116 -319
  230. package/.framework/src/pages/admin/ExtendedShadcnTestPage.tsx +34 -9
  231. package/.framework/src/pages/admin/IncrementalShadcnTestPage.tsx +34 -9
  232. package/.framework/src/pages/admin/IntegratedDashboard.tsx +32 -16
  233. package/.framework/src/pages/admin/IntegrationDetailPage.tsx +183 -26
  234. package/.framework/src/pages/admin/IntegrationsPage.tsx +48 -19
  235. package/.framework/src/pages/admin/InvitesPage.tsx +48 -14
  236. package/.framework/src/pages/admin/LogsPage.tsx +422 -75
  237. package/.framework/src/pages/admin/MinimalShadcnTestPage.tsx +34 -9
  238. package/.framework/src/pages/admin/ObservabilityDashboard.tsx +113 -24
  239. package/.framework/src/pages/admin/PipelineDetailPage.tsx +206 -34
  240. package/.framework/src/pages/admin/PipelineExecutionsPage.tsx +50 -17
  241. package/.framework/src/pages/admin/PipelinesPage.tsx +160 -21
  242. package/.framework/src/pages/admin/PromptConfigDetailPage.tsx +378 -88
  243. package/.framework/src/pages/admin/PromptConfigsPage.tsx +125 -16
  244. package/.framework/src/pages/admin/ProperlyDesignedPage.tsx +49 -10
  245. package/.framework/src/pages/admin/RoleDetailPage.tsx +130 -10
  246. package/.framework/src/pages/admin/RolesPage.tsx +171 -18
  247. package/.framework/src/pages/admin/SelectTestPage.tsx +34 -9
  248. package/.framework/src/pages/admin/ShadcnTestPage.tsx +49 -11
  249. package/.framework/src/pages/admin/SimpleDashboard.tsx +49 -10
  250. package/.framework/src/pages/admin/TestRunDetailPage.tsx +49 -4
  251. package/.framework/src/pages/admin/TestingDashboard.tsx +98 -28
  252. package/.framework/src/pages/admin/TimerDetailPage.tsx +143 -33
  253. package/.framework/src/pages/admin/TimersPage.tsx +48 -20
  254. package/.framework/src/pages/admin/TriggerDetailPage.tsx +118 -30
  255. package/.framework/src/pages/admin/TriggersPage.tsx +64 -19
  256. package/.framework/src/pages/admin/TypeDetailPage.tsx +138 -15
  257. package/.framework/src/pages/admin/TypesPage.tsx +48 -20
  258. package/.framework/src/pages/admin/cil/CILAgentPage.tsx +465 -0
  259. package/.framework/src/pages/admin/cil/CILChunkDetailPage.tsx +787 -0
  260. package/.framework/src/pages/admin/cil/CILDashboardPage.tsx +699 -0
  261. package/.framework/src/pages/admin/cil/CILFileListPage.tsx +647 -0
  262. package/.framework/src/pages/admin/cil/CILGraphPage.tsx +616 -0
  263. package/.framework/src/pages/admin/cil/CILIngestionPage.tsx +1402 -0
  264. package/.framework/src/pages/admin/cil/CILProjectsPage.tsx +614 -0
  265. package/.framework/src/pages/admin/cil/CILSearchPage.tsx +571 -0
  266. package/.framework/src/pages/admin/cil/FlexiblePipelinePage.tsx +731 -0
  267. package/.framework/src/pages/admin/kb/KBAdminPage.tsx +64 -0
  268. package/.framework/src/pages/auth/AuthConfirmPage.tsx +52 -51
  269. package/.framework/src/pages/auth/ForgotPasswordPage.tsx +31 -22
  270. package/.framework/src/pages/auth/LoginPage.tsx +50 -23
  271. package/.framework/src/pages/auth/PendingApprovalPage.tsx +31 -15
  272. package/.framework/src/pages/auth/RegisterPage.tsx +281 -317
  273. package/.framework/src/pages/spine-framework/APIPage.tsx +33 -0
  274. package/.framework/src/pages/spine-framework/CLIPage.tsx +33 -0
  275. package/.framework/src/types/auth.ts +128 -71
  276. package/.framework/src/types/types.ts +373 -157
  277. package/custom/.devin/skills/spine-workbench-discovery/SKILL.md +39 -0
  278. package/custom/.devin/skills/spine-workbench-idea/SKILL.md +36 -0
  279. package/custom/.devin/skills/spine-workbench-implement/SKILL.md +46 -0
  280. package/custom/.devin/skills/spine-workbench-initiative/SKILL.md +28 -0
  281. package/custom/.devin/skills/spine-workbench-plan/SKILL.md +38 -0
  282. package/docs/README.md +9 -130
  283. package/netlify.toml +5 -2
  284. package/package.json +13 -25
  285. package/scripts/kb-ingest-docs.ts +203 -0
  286. package/spine.config.json +65 -4
  287. package/.framework/migrations/001_schema.sql +0 -425
  288. package/.framework/migrations/002_rls_and_functions.sql +0 -763
  289. package/.framework/migrations/004_enhancements.sql +0 -43
  290. package/.framework/migrations/005_auth_user_trigger.sql +0 -105
  291. package/.framework/migrations/006_fix_current_actor_id.sql +0 -21
  292. package/.framework/migrations/016_invites.sql +0 -27
  293. package/.framework/migrations/017_fix_get_account_apps.sql +0 -78
  294. package/.framework/migrations/018_apps_required_roles.sql +0 -17
  295. package/.framework/migrations/019_triggers_unique_constraint.sql +0 -13
  296. package/.framework/migrations/020_get_account_apps_role_filter.sql +0 -117
  297. package/.framework/migrations/021_app_domains.sql +0 -112
  298. package/.framework/migrations/022_route_prefix_domain_unique.sql +0 -21
  299. package/.framework/migrations/023_match_embeddings_rpc.sql +0 -78
  300. package/.framework/migrations/024_messages_account_id.sql +0 -9
  301. package/.framework/migrations/025_retype_threads_messages.sql +0 -240
  302. package/.framework/migrations/026_fix_rls_policies.sql +0 -47
  303. package/.framework/migrations/027_embedding_lifecycle.sql +0 -176
  304. package/.framework/migrations/028_fix_message_data_permissions.sql +0 -49
  305. package/.framework/migrations/029_tighten_support_triage_prompt.sql +0 -41
  306. package/.framework/migrations/030_messages_updated_by.sql +0 -6
  307. package/.framework/migrations/031_seed_upsert_constraints.sql +0 -35
  308. package/docs/APPS.md +0 -232
  309. package/docs/ARCHITECTURE.md +0 -246
  310. package/docs/INSTALL.md +0 -155
  311. package/docs/design-schema-spec.md +0 -1837
  312. package/docs/dev-guides/01-create-an-app.md +0 -183
  313. package/docs/dev-guides/02-create-a-function.md +0 -178
  314. package/docs/dev-guides/03-create-a-component.md +0 -178
  315. package/docs/dev-guides/04-webhook-handlers.md +0 -129
  316. package/docs/dev-guides/05-testing.md +0 -168
  317. package/docs/dev-guides/README.md +0 -40
  318. package/docs/npm-packages-plan.md +0 -288
  319. package/docs/spine-core--api-reference.md +0 -822
@@ -1,20 +1,30 @@
1
- /**
2
- * @module src/pages/admin/TestingDashboard
3
- * @audience installer
4
- * @layer frontend-page
5
- * @stability stable
6
- *
7
- * Admin Testing dashboard. Shows per-suite health cards (unit, integration,
8
- * api, ui) and a table of the 20 most recent test runs. Read-only — tests
9
- * are triggered by the agentic IDE, not from this UI.
10
- *
11
- * Route: /admin/testing
12
- *
13
- * @seeAlso src/pages/admin/TestRunDetailPage.tsx
14
- * @seeAlso functions/tests.ts (data source)
15
- * @seeAlso migrations_dayzero/008_test_runs.sql
16
- */
17
1
 
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+ // CIL[BEGIN chunk_id="testingdashboard_module_header_1783665503" type="module_header" identity="module_header" parent="none"]
14
+ // CIL[DOC-MACRO purpose="Import React, router, API hooks and define testing interfaces"]
15
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide React components and test data types"]
16
+ // CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
17
+ // CIL[DOC-CONTRACT inputs="none" outputs="React components and test interfaces" errors="none" side_effects="none"]
18
+ // CIL[DOC-MICRO logic_summary="Imports testing dependencies and defines SuiteStat and TestRun types" notable_decisions="[]"]
19
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
20
+ // CIL[DOC-STABILITY stability="stable"]
21
+ // CIL[DOC-AUDIENCE audience="developer"]
22
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, \."]
23
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
24
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
25
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
26
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
27
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
18
28
  import React from 'react'
19
29
  import { useNavigate } from 'react-router-dom'
20
30
  import { useApi } from '../../hooks/useApi'
@@ -24,8 +34,6 @@ import { FlaskConical, CheckCircle, XCircle, Clock, RefreshCw } from 'lucide-rea
24
34
  import { LoadingSpinner } from '../../components/ui/LoadingSpinner'
25
35
  import { formatDateTime } from '../../lib/utils'
26
36
 
27
- // ─── TYPES ───────────────────────────────────────────────────────────────────
28
-
29
37
  interface SuiteStat {
30
38
  suite: string
31
39
  total_runs: number
@@ -49,9 +57,22 @@ interface TestRun {
49
57
  skipped: number | null
50
58
  triggered_by: string | null
51
59
  }
52
-
53
- // ─── HELPERS ─────────────────────────────────────────────────────────────────
54
-
60
+ import { PageInstructions } from '../../components/admin/PageInstructions'
61
+ // CIL[END chunk_id="testingdashboard_module_header_1783665503"]
62
+ // CIL[BEGIN chunk_id="testingdashboard_helpers_1783665503" type="module" identity="helpers" parent="none"]
63
+ // CIL[DOC-MACRO purpose="Provide helper constants and utility functions for testing dashboard"]
64
+ // CIL[DOC-RESPONSIBILITY responsibility="Define suite labels, status badge, and duration formatting"]
65
+ // CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/functions/custom_cil-embeddings.ts\",\".framework/functions/_shared/principal.ts\",\".framework/functions/_shared/schema-utils.ts\",\"\\\"]"]
66
+ // CIL[DOC-CONTRACT inputs="none" outputs="formatted UI elements" errors="none" side_effects="none"]
67
+ // CIL[DOC-MICRO logic_summary="Creates status badges, duration labels, and suite name mappings" notable_decisions="[]"]
68
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
69
+ // CIL[DOC-STABILITY stability="stable"]
70
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/functions/custom_cil-embeddings.ts, .framework/functions/_shared/principal.ts, .framework/functions/_shared/schema-utils.ts, \."]
71
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
72
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
73
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
74
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
75
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
55
76
  const SUITE_LABELS: Record<string, string> = {
56
77
  unit: 'Unit',
57
78
  integration: 'Integration',
@@ -78,9 +99,21 @@ function durationLabel(ms: number | null): string {
78
99
  if (ms < 1000) return `${ms}ms`
79
100
  return `${(ms / 1000).toFixed(1)}s`
80
101
  }
81
-
82
- // ─── SUITE HEALTH CARD ───────────────────────────────────────────────────────
83
-
102
+ // CIL[END chunk_id="testingdashboard_helpers_1783665503"]
103
+ // CIL[BEGIN chunk_id="testingdashboard_suitecard_1783665503" type="function" identity="SuiteCard" parent="none"]
104
+ // CIL[DOC-MACRO purpose="Render test suite health card with status and statistics"]
105
+ // CIL[DOC-RESPONSIBILITY responsibility="Display suite stats in a formatted card component"]
106
+ // CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/IntegrationsPage.tsx\",\"\\\"]"]
107
+ // CIL[DOC-CONTRACT inputs="none" outputs="JSX card element" errors="none" side_effects="none"]
108
+ // CIL[DOC-MICRO logic_summary="Calculates pass rate and renders suite status with metrics" notable_decisions="[]"]
109
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
110
+ // CIL[DOC-STABILITY stability="stable"]
111
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/RolesPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/IntegrationsPage.tsx, \."]
112
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
113
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
114
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
115
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
116
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
84
117
  function SuiteCard({ stat }: { stat: SuiteStat }) {
85
118
  const label = SUITE_LABELS[stat.suite] ?? stat.suite
86
119
  const passRate = stat.last_total
@@ -113,9 +146,21 @@ function SuiteCard({ stat }: { stat: SuiteStat }) {
113
146
  </div>
114
147
  )
115
148
  }
116
-
117
- // ─── PAGE ────────────────────────────────────────────────────────────────────
118
-
149
+ // CIL[END chunk_id="testingdashboard_suitecard_1783665503"]
150
+ // CIL[BEGIN chunk_id="testingdashboard_testingdashboard_1783665503" type="function" identity="TestingDashboard" parent="none"]
151
+ // CIL[DOC-MACRO purpose="Render testing dashboard with suite health cards and recent runs table"]
152
+ // CIL[DOC-RESPONSIBILITY responsibility="Display suite stats, recent runs, and navigation to test details"]
153
+ // CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
154
+ // CIL[DOC-CONTRACT inputs="none" outputs="Complete dashboard UI" errors="none" side_effects="none"]
155
+ // CIL[DOC-MICRO logic_summary="Renders header, suite cards grid, and recent runs table with navigation" notable_decisions="[]"]
156
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
157
+ // CIL[DOC-STABILITY stability="stable"]
158
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, \."]
159
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
160
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
161
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
162
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
163
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
119
164
  export default function TestingDashboard() {
120
165
  const navigate = useNavigate()
121
166
 
@@ -136,10 +181,23 @@ export default function TestingDashboard() {
136
181
  refreshRuns()
137
182
  }
138
183
 
139
- // API returns { data: { data: [...], error: null }, error: null, meta: {...} }
140
184
  const stats: SuiteStat[] = (statsData as any)?.data?.data ?? []
141
185
  const runs: TestRun[] = (runsData as any)?.data?.data ?? []
142
186
 
187
+ // CIL[BEGIN chunk_id="testingdashboard_return_1783665503" type="jsx" identity="return" parent="testingdashboard_testingdashboard_1783665503"]
188
+ // CIL[DOC-MACRO purpose="Render testing dashboard with suite health cards and recent runs table"]
189
+ // CIL[DOC-RESPONSIBILITY responsibility="Handle return"]
190
+ // CIL[DOC-CONTEXT module="TestingDashboard" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\".framework/src/components/ui/dialog.tsx\",\".framework/functions/_shared/principal.ts\",\"\\\"]"]
191
+ // CIL[DOC-CONTRACT inputs="none" outputs="Complete dashboard UI" errors="none" side_effects="none"]
192
+ // CIL[DOC-MICRO logic_summary="Renders header, suite cards grid, and recent runs table with navigation" notable_decisions="[]"]
193
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
194
+ // CIL[DOC-STABILITY stability="stable"]
195
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, .framework/src/components/ui/dialog.tsx, .framework/functions/_shared/principal.ts, \."]
196
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
197
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
198
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
199
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
200
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
143
201
  return (
144
202
  <div className="p-6 max-w-6xl mx-auto">
145
203
  {/* Header */}
@@ -157,6 +215,16 @@ export default function TestingDashboard() {
157
215
  </button>
158
216
  </div>
159
217
 
218
+ <PageInstructions
219
+ description="Run and review Spine test suites."
220
+ sections={[
221
+ { title: 'What this page is for', content: 'This page displays the health and history of the unit, integration, API, and UI test suites.' },
222
+ { title: 'How to read the cards', content: 'The suite health cards show the most recent run status, pass rate, total runs, and last run time. Colors indicate passed, failed, running, or unknown states.' },
223
+ { title: 'How to use this page', content: 'Click the Refresh button to reload stats and runs. Click a row in the Recent Runs table to view individual test case results. Suites are triggered from the IDE or CI pipeline.' },
224
+ { title: 'Important notes', content: 'A pass rate below 100% is shown in red. Empty states mean no runs have been recorded yet for that suite.' },
225
+ ]}
226
+ />
227
+
160
228
  {/* Suite health cards */}
161
229
  {statsLoading ? (
162
230
  <div className="flex justify-center py-8"><LoadingSpinner /></div>
@@ -254,4 +322,6 @@ export default function TestingDashboard() {
254
322
  )}
255
323
  </div>
256
324
  )
325
+ // CIL[END chunk_id="testingdashboard_return_1783665503"]
257
326
  }
327
+ // CIL[END chunk_id="testingdashboard_testingdashboard_1783665503"]
@@ -1,6 +1,22 @@
1
- /**
2
- * @module src/pages/admin/TimerDetailPage
3
- */
1
+
2
+
3
+
4
+
5
+ // CIL[BEGIN chunk_id="timerdetailpage_moduleheader_1783664706" type="module_header" identity="module_header" parent="none"]
6
+ // CIL[DOC-MACRO purpose="Import dependencies for TimerDetailPage component"]
7
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide all external dependencies and UI components"]
8
+ // CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
9
+ // CIL[DOC-CONTRACT inputs="none" outputs="React components and utilities" errors="none" side_effects="none"]
10
+ // CIL[DOC-MICRO logic_summary="Imports React hooks, navigation, API utilities, and UI components" notable_decisions="[]"]
11
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
12
+ // CIL[DOC-STABILITY stability="stable"]
13
+ // CIL[DOC-AUDIENCE audience="developer"]
14
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
15
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
16
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
17
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
18
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
19
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
4
20
  import { useState, useEffect } from 'react'
5
21
  import { useParams, useNavigate } from 'react-router-dom'
6
22
  import { apiFetch } from '../../lib/api'
@@ -16,31 +32,70 @@ import { Alert, AlertDescription, AlertTitle } from '../../components/ui/alert'
16
32
  import { Badge } from '../../components/ui/badge'
17
33
  import { ArrowLeft, Clock, AlertCircle } from 'lucide-react'
18
34
  import { formatDateTime } from '../../lib/utils'
19
-
35
+ import { PageInstructions } from '../../components/admin/PageInstructions'
36
+ // CIL[END chunk_id="timerdetailpage_moduleheader_1783664706"]
37
+ // CIL[BEGIN chunk_id="timerdetailpage_timer_1783664706" type="interface" identity="Timer" parent="none"]
38
+ // CIL[DOC-MACRO purpose="Define the Timer data structure for timer management"]
39
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide type definition for scheduled/recurring timers"]
40
+ // CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
41
+ // CIL[DOC-CONTRACT inputs="none" outputs="Timer interface" errors="none" side_effects="none"]
42
+ // CIL[DOC-MICRO logic_summary="Defines timer structure with execution tracking and target configuration" notable_decisions="[]"]
43
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
44
+ // CIL[DOC-STABILITY stability="stable"]
45
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
46
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
47
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
48
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
49
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
50
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
20
51
  interface Timer {
21
52
  id: string
22
53
  name: string
23
54
  description?: string
24
- schedule: string
55
+ app_id?: string
25
56
  timer_type: 'interval' | 'cron' | 'once'
26
- interval_seconds?: number
27
- next_run?: string
28
- last_run?: string
29
- last_run_status?: 'success' | 'failed'
57
+ config?: Record<string, any>
58
+ pipeline_id?: string
59
+ metadata?: Record<string, any>
30
60
  is_active: boolean
31
- target_type: string
32
- target_action: string
33
- target_config: Record<string, any>
34
61
  created_at: string
35
62
  updated_at: string
36
63
  }
37
-
64
+ // CIL[END chunk_id="timerdetailpage_timer_1783664706"]
65
+ // CIL[BEGIN chunk_id="timerdetailpage_timertypes_1783664706" type="constant" identity="TIMER_TYPES" parent="none"]
66
+ // CIL[DOC-MACRO purpose="Define timer type options for form selection"]
67
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide timer type configuration options"]
68
+ // CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/functions/_shared/principal.ts\",\".framework/migrations/025_retype_threads_messages.sql\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"\\\"]"]
69
+ // CIL[DOC-CONTRACT inputs="none" outputs="TIMER_TYPES constant" errors="none" side_effects="none"]
70
+ // CIL[DOC-MICRO logic_summary="Defines available timer types: interval, cron, and one-time" notable_decisions="[]"]
71
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
72
+ // CIL[DOC-STABILITY stability="stable"]
73
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, .framework/functions/_shared/schema-utils.ts, .framework/functions/_shared/principal.ts, .framework/migrations/025_retype_threads_messages.sql, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, \."]
74
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
75
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
76
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
77
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
78
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
38
79
  const TIMER_TYPES = [
39
80
  { value: 'interval', label: 'Interval' },
40
81
  { value: 'cron', label: 'Cron Expression' },
41
82
  { value: 'once', label: 'One-time' }
42
83
  ]
43
-
84
+ // CIL[END chunk_id="timerdetailpage_timertypes_1783664706"]
85
+ // CIL[BEGIN chunk_id="timerdetailpage_timerdetailpage_1783664706" type="function" identity="TimerDetailPage" parent="none"]
86
+ // CIL[DOC-MACRO purpose="Create/view/edit/delete page for single timer records"]
87
+ // CIL[DOC-RESPONSIBILITY responsibility="Manage timer CRUD operations with form handling and validation"]
88
+ // CIL[DOC-CONTEXT module="TimerDetailPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
89
+ // CIL[DOC-CONTRACT inputs="none" outputs="React component with timer form" errors="API failures, validation errors" side_effects="API calls, navigation"]
90
+ // CIL[DOC-MICRO logic_summary="Form handling with timer configuration and execution tracking" notable_decisions="[]"]
91
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
92
+ // CIL[DOC-STABILITY stability="stable"]
93
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/portal/pages/ContentPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
94
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
95
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
96
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
97
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
98
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
44
99
  export function TimerDetailPage() {
45
100
  const { id } = useParams<{ id: string }>()
46
101
  const navigate = useNavigate()
@@ -49,7 +104,16 @@ export function TimerDetailPage() {
49
104
  const [loading, setLoading] = useState(true)
50
105
  const [error, setError] = useState<string | null>(null)
51
106
  const [isEditing, setIsEditing] = useState(isCreateMode)
52
- const [formData, setFormData] = useState({ name: '', description: '', schedule: '', timer_type: 'interval' as const, interval_seconds: 3600, is_active: true, target_type: '', target_action: '', target_config: '{}' })
107
+ const [formData, setFormData] = useState({
108
+ name: '',
109
+ description: '',
110
+ app_id: '',
111
+ timer_type: 'interval' as const,
112
+ config: JSON.stringify({ schedule: '', interval_seconds: 3600 }, null, 2),
113
+ pipeline_id: '',
114
+ metadata: '{}',
115
+ is_active: true
116
+ })
53
117
 
54
118
  useEffect(() => {
55
119
  if (isCreateMode) {
@@ -65,9 +129,14 @@ export function TimerDetailPage() {
65
129
  const data = result.data
66
130
  setTimer(data)
67
131
  setFormData({
68
- name: data.name, description: data.description || '', schedule: data.schedule, timer_type: data.timer_type,
69
- interval_seconds: data.interval_seconds || 3600, is_active: data.is_active, target_type: data.target_type,
70
- target_action: data.target_action, target_config: JSON.stringify(data.target_config || {}, null, 2)
132
+ name: data.name,
133
+ description: data.description || '',
134
+ app_id: data.app_id || '',
135
+ timer_type: data.timer_type,
136
+ config: JSON.stringify(data.config || {}, null, 2),
137
+ pipeline_id: data.pipeline_id || '',
138
+ metadata: JSON.stringify(data.metadata || {}, null, 2),
139
+ is_active: data.is_active ?? true
71
140
  })
72
141
  } catch (err) {
73
142
  setError(err instanceof Error ? err.message : 'Failed to load timer')
@@ -81,10 +150,22 @@ export function TimerDetailPage() {
81
150
  const handleSave = async () => {
82
151
  try {
83
152
  let parsedConfig = {}
84
- try { parsedConfig = JSON.parse(formData.target_config) } catch { throw new Error('Invalid target config JSON') }
153
+ let parsedMetadata = {}
154
+ try { parsedConfig = JSON.parse(formData.config) } catch { throw new Error('Invalid config JSON') }
155
+ try { parsedMetadata = JSON.parse(formData.metadata) } catch { throw new Error('Invalid metadata JSON') }
85
156
  const url = isCreateMode ? '/api/timers?action=create' : `/api/timers?action=update&id=${id}`
86
157
  const method = isCreateMode ? 'POST' : 'PATCH'
87
- const response = await apiFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...formData, target_config: parsedConfig }) })
158
+ const saveData = {
159
+ name: formData.name,
160
+ description: formData.description,
161
+ app_id: formData.app_id || null,
162
+ timer_type: formData.timer_type,
163
+ config: parsedConfig,
164
+ pipeline_id: formData.pipeline_id || null,
165
+ metadata: parsedMetadata,
166
+ is_active: formData.is_active
167
+ }
168
+ const response = await apiFetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(saveData) })
88
169
  if (!response.ok) throw new Error('Failed to save timer')
89
170
  if (isCreateMode) {
90
171
  const result = await response.json()
@@ -120,32 +201,61 @@ export function TimerDetailPage() {
120
201
  </div>
121
202
  </div>
122
203
 
123
- <Card><CardContent className="p-6">
204
+ <PageInstructions
205
+ description="Schedule a timer to run actions at intervals, via cron, or once."
206
+ sections={[
207
+ { title: 'What this timer controls', content: 'A timer is a scheduled job that calls a target function or pipeline on an interval, a cron schedule, or one time. It is the Spine equivalent of a recurring or one-off background task.' },
208
+ { title: 'Fields explained', content: <ul className="list-disc pl-4 space-y-1">
209
+ <li><strong>Name</strong> — string. The display name. Missing name prevents saving.</li>
210
+ <li><strong>Type</strong> — select. interval, cron, or once. Controls which schedule field in Config is used and how the next run is calculated.</li>
211
+ <li><strong>App ID</strong> — string. Optional owning app UUID; blank means account-scoped.</li>
212
+ <li><strong>Pipeline ID</strong> — string. The pipeline to invoke when the timer fires.</li>
213
+ <li><strong>Active</strong> — checkbox. Inactive timers are skipped by the scheduler.</li>
214
+ <li><strong>Description</strong> — string. Optional notes.</li>
215
+ <li><strong>Config (JSON)</strong> — JSON. The timer schedule, e.g. { `{ "schedule": "0 */6 * * *" }` } for cron, { `{ "interval_seconds": 3600 }` } for interval, or { `{ "run_at": "2026-01-01T00:00:00Z" }` } for once.</li>
216
+ <li><strong>Metadata (JSON)</strong> — JSON. Optional metadata about the timer.</li>
217
+ </ul> },
218
+ { title: 'Options and values', content: <ul className="list-disc pl-4 space-y-1">
219
+ <li><strong>interval</strong>: config should contain interval_seconds.</li>
220
+ <li><strong>cron</strong>: config should contain a schedule cron expression like 0 */6 * * *.</li>
221
+ <li><strong>once</strong>: config should contain a run_at ISO timestamp.</li>
222
+ </ul> },
223
+ { title: 'Downstream impact', content: <ul className="list-disc pl-4 space-y-1">
224
+ <li>The scheduler uses the active flag and the timer_type/config fields to determine the next run and to enqueue the pipeline.</li>
225
+ <li>Pipeline ID must refer to an existing pipeline; otherwise the timer will fail with a missing handler.</li>
226
+ </ul> },
227
+ { title: 'How to create, save, and validate', content: <ol className="list-decimal pl-4 space-y-1">
228
+ <li>Pick the timer type and fill the Config JSON with the matching schedule. Enter the Pipeline ID and optional Metadata JSON.</li>
229
+ <li>Click Create to save or Edit then Save for existing timers.</li>
230
+ <li>Validation checks the name, timer type, and that Config and Metadata are valid JSON.</li>
231
+ </ol> },
232
+ { title: 'Common mistakes', content: <ul className="list-disc pl-4 space-y-1">
233
+ <li>Selecting cron but filling interval_seconds instead of a cron schedule.</li>
234
+ <li>Using an invalid cron expression, which the scheduler will not run.</li>
235
+ <li>Leaving Pipeline ID blank when the scheduler expects a pipeline to invoke.</li>
236
+ </ul> },
237
+ ]}
238
+ />
239
+
240
+ <Card><CardContent className="p-6 space-y-6">
124
241
  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
125
242
  <div className="space-y-4">
126
243
  <h3 className="text-sm font-medium text-muted-foreground">Configuration</h3>
127
244
  <div className="space-y-2"><Label>Name</Label>{isEditing ? <Input value={formData.name} onChange={e => setFormData({...formData, name: e.target.value})} /> : <div className="text-sm">{timer?.name}</div>}</div>
245
+ <div className="space-y-2"><Label>App ID</Label>{isEditing ? <Input value={formData.app_id} onChange={e => setFormData({...formData, app_id: e.target.value})} placeholder="Optional app UUID" /> : <div className="text-sm font-mono">{timer?.app_id || '—'}</div>}</div>
128
246
  <div className="space-y-2"><Label>Type</Label>{isEditing ? <Select value={formData.timer_type} onValueChange={v => setFormData({...formData, timer_type: v as any})}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{TIMER_TYPES.map(t => <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>)}</SelectContent></Select> : <Badge>{timer?.timer_type}</Badge>}</div>
129
- {formData.timer_type === 'interval' && <div className="space-y-2"><Label>Interval (seconds)</Label>{isEditing ? <Input type="number" value={formData.interval_seconds} onChange={e => setFormData({...formData, interval_seconds: parseInt(e.target.value)})} /> : <div className="text-sm">{timer?.interval_seconds}s</div>}</div>}
130
- <div className="space-y-2"><Label>Schedule/Cron</Label>{isEditing ? <Input value={formData.schedule} onChange={e => setFormData({...formData, schedule: e.target.value})} placeholder={formData.timer_type === 'interval' ? '3600' : '0 */6 * * *'} /> : <div className="text-sm font-mono">{timer?.schedule}</div>}</div>
131
247
  <div className="space-y-2"><Label>Active</Label>{isEditing ? <div className="flex items-center gap-2"><Checkbox id="active" checked={formData.is_active} onCheckedChange={c => setFormData({...formData, is_active: c === true})} /><Label htmlFor="active">Enabled</Label></div> : <div className="text-sm">{timer?.is_active ? 'Yes' : 'No'}</div>}</div>
132
248
  </div>
133
249
  <div className="space-y-4">
134
250
  <h3 className="text-sm font-medium text-muted-foreground">Execution</h3>
135
- {!isCreateMode && (
136
- <>
137
- <div className="flex justify-between"><span className="text-sm text-muted-foreground">Next Run</span><span className="text-sm">{timer?.next_run ? formatDateTime(timer.next_run) : '—'}</span></div>
138
- <div className="flex justify-between"><span className="text-sm text-muted-foreground">Last Run</span><span className="text-sm">{timer?.last_run ? formatDateTime(timer.last_run) : '—'}</span></div>
139
- <div className="flex justify-between"><span className="text-sm text-muted-foreground">Last Status</span><Badge variant={timer?.last_run_status === 'success' ? 'default' : 'destructive'}>{timer?.last_run_status || '—'}</Badge></div>
140
- </>
141
- )}
142
- <div className="space-y-2"><Label>Target Type</Label>{isEditing ? <Input value={formData.target_type} onChange={e => setFormData({...formData, target_type: e.target.value})} placeholder="e.g., function, pipeline" /> : <div className="text-sm">{timer?.target_type}</div>}</div>
143
- <div className="space-y-2"><Label>Target Action</Label>{isEditing ? <Input value={formData.target_action} onChange={e => setFormData({...formData, target_action: e.target.value})} placeholder="e.g., execute" /> : <div className="text-sm">{timer?.target_action}</div>}</div>
251
+ <div className="space-y-2"><Label>Pipeline ID</Label>{isEditing ? <Input value={formData.pipeline_id} onChange={e => setFormData({...formData, pipeline_id: e.target.value})} placeholder="Pipeline to invoke" /> : <div className="text-sm font-mono">{timer?.pipeline_id || '—'}</div>}</div>
144
252
  </div>
145
253
  </div>
146
254
  <div className="mt-6 space-y-2"><Label>Description</Label>{isEditing ? <Textarea value={formData.description} onChange={e => setFormData({...formData, description: e.target.value})} rows={3} /> : <div className="text-sm">{timer?.description || '—'}</div>}</div>
147
- <div className="mt-6 space-y-2"><Label>Target Config (JSON)</Label>{isEditing ? <Textarea value={formData.target_config} onChange={e => setFormData({...formData, target_config: e.target.value})} rows={6} className="font-mono text-sm" /> : <pre className="text-sm bg-muted p-3 rounded">{JSON.stringify(timer?.target_config || {}, null, 2)}</pre>}</div>
255
+ <div className="mt-6 space-y-2"><Label>Config (JSON)</Label>{isEditing ? <Textarea value={formData.config} onChange={e => setFormData({...formData, config: e.target.value})} rows={6} className="font-mono text-sm" /> : <pre className="text-sm bg-muted p-3 rounded">{JSON.stringify(timer?.config || {}, null, 2)}</pre>}</div>
256
+ <div className="mt-6 space-y-2"><Label>Metadata (JSON)</Label>{isEditing ? <Textarea value={formData.metadata} onChange={e => setFormData({...formData, metadata: e.target.value})} rows={4} className="font-mono text-sm" /> : <pre className="text-sm bg-muted p-3 rounded">{JSON.stringify(timer?.metadata || {}, null, 2)}</pre>}</div>
148
257
  </CardContent></Card>
149
258
  </div>
150
259
  )
151
260
  }
261
+ // CIL[END chunk_id="timerdetailpage_timerdetailpage_1783664706"]
@@ -1,18 +1,21 @@
1
- /**
2
- * @module src/pages/admin/TimersPage
3
- * @audience installer
4
- * @layer frontend-page
5
- * @stability stable
6
- *
7
- * Admin list page for scheduled timers. Fetches all timers via
8
- * `/api/timers?action=list`, applies client-side search, type filter
9
- * (`schedule` | `delay` | `recurring` | `cron`), and sort. Renders
10
- * inside `AdminListPage` with stat cards and a sortable table. Row
11
- * clicks navigate to `/spine-framework/admin/configs/timers/:id`.
12
- *
13
- * @seeAlso src/pages/admin/TimerDetailPage.tsx
14
- */
15
1
 
2
+
3
+
4
+ // CIL[BEGIN chunk_id="timerspage_moduleheader_1783664706" type="module_header" identity="module_header" parent="none"]
5
+ // CIL[DOC-MACRO purpose="Import dependencies for TimersPage component"]
6
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide all external dependencies and UI components"]
7
+ // CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\".framework/src/components/layout/Sidebar.tsx\",\".framework/src/components/ui/dialog.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
8
+ // CIL[DOC-CONTRACT inputs="none" outputs="React components and utilities" errors="none" side_effects="none"]
9
+ // CIL[DOC-MICRO logic_summary="Imports React hooks, navigation, API utilities, icons, and UI components" notable_decisions="[]"]
10
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
11
+ // CIL[DOC-STABILITY stability="stable"]
12
+ // CIL[DOC-AUDIENCE audience="developer"]
13
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/components/layout/Sidebar.tsx, .framework/src/components/ui/dialog.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
14
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
15
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
16
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
17
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
18
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
16
19
  import React, { useState } from 'react'
17
20
  import { useNavigate } from 'react-router-dom'
18
21
  import { useApi } from '../../hooks/useApi'
@@ -24,7 +27,21 @@ import { Badge } from '../../components/ui/badge'
24
27
  import { formatDateTime } from '../../lib/utils'
25
28
  import { AdminListPage } from '../../components/admin/AdminListPage'
26
29
  import { SortableTableHeader } from '../../components/admin/SortableTableHeader'
27
-
30
+ // CIL[END chunk_id="timerspage_moduleheader_1783664706"]
31
+ // CIL[BEGIN chunk_id="timerspage_timer_1783664706" type="interface" identity="Timer" parent="none"]
32
+ // CIL[DOC-MACRO purpose="Define the Timer data structure for timer management"]
33
+ // CIL[DOC-RESPONSIBILITY responsibility="Provide type definition for scheduled/recurring timers"]
34
+ // CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\".framework/src/pages/admin/IntegrationsPage.tsx\",\".framework/functions/_shared/principal.ts\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/functions/_shared/schema-utils.ts\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"\\\"]"]
35
+ // CIL[DOC-CONTRACT inputs="none" outputs="Timer interface" errors="none" side_effects="none"]
36
+ // CIL[DOC-MICRO logic_summary="Defines comprehensive timer structure with execution statistics" notable_decisions="[]"]
37
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
38
+ // CIL[DOC-STABILITY stability="stable"]
39
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: .framework/src/pages/admin/IntegrationsPage.tsx, .framework/functions/_shared/principal.ts, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/functions/_shared/schema-utils.ts, .framework/src/pages/admin/TypeDetailPage.tsx, \."]
40
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
41
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
42
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
43
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
44
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
28
45
  interface Timer {
29
46
  id: string
30
47
  name: string
@@ -50,7 +67,21 @@ interface Timer {
50
67
  success_count: number
51
68
  failure_count: number
52
69
  }
53
-
70
+ // CIL[END chunk_id="timerspage_timer_1783664706"]
71
+ // CIL[BEGIN chunk_id="timerspage_timerspage_1783664706" type="function" identity="TimersPage" parent="none"]
72
+ // CIL[DOC-MACRO purpose="Admin list page for managing scheduled and recurring timers"]
73
+ // CIL[DOC-RESPONSIBILITY responsibility="Fetch, filter, sort, and display timers with navigation"]
74
+ // CIL[DOC-CONTEXT module="TimersPage" layer="frontend-page" related_files="[\"custom/apps/portal/pages/ContentPage.tsx\",\".framework/src/pages/admin/TypeDetailPage.tsx\",\"custom/apps/cortex/pages/operations/OperationsDashboard.tsx\",\".framework/src/pages/admin/RolesPage.tsx\",\"\\\"]"]
75
+ // CIL[DOC-CONTRACT inputs="none" outputs="React component with timer list" errors="API failures" side_effects="API calls, navigation"]
76
+ // CIL[DOC-MICRO logic_summary="Client-side filtering, sorting, and stats calculation for timers" notable_decisions="[]"]
77
+ // CIL[DOC-CONFIDENCE pass="second" score="0.95" flags="none"]
78
+ // CIL[DOC-STABILITY stability="stable"]
79
+ // CIL[DOC-VECTOR type="context" content="Layer: frontend-page. Direct callers: 0. Direct calls: 0. Related files: custom/apps/portal/pages/ContentPage.tsx, .framework/src/pages/admin/TypeDetailPage.tsx, custom/apps/cortex/pages/operations/OperationsDashboard.tsx, .framework/src/pages/admin/RolesPage.tsx, \."]
80
+ // CIL[DOC-VECTOR type="blast_radius" content="Direct callers: 0. Total affected: 0."]
81
+ // CIL[DOC-VECTOR type="configuration" content="No configuration dependencies."]
82
+ // CIL[DOC-VECTOR type="constraint" content="No test paths identified."]
83
+ // CIL[DOC-VECTOR type="implementation_pattern" content="" status="pending_human_input"]
84
+ // CIL[DOC-VECTOR type="user_journey" content="" status="pending_human_input"]
54
85
  export function TimersPage() {
55
86
  const navigate = useNavigate()
56
87
  const [searchTerm, setSearchTerm] = useState('')
@@ -59,7 +90,6 @@ export function TimersPage() {
59
90
  const [sortKey, setSortKey] = useState('name')
60
91
  const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc')
61
92
 
62
- // Fetch timers from API
63
93
  const { data: timers, loading, error, refetch } = useApi<Timer[]>(
64
94
  async ({ signal }: { signal?: AbortSignal }) => {
65
95
  try {
@@ -75,7 +105,6 @@ export function TimersPage() {
75
105
  const result = await response.json()
76
106
  console.log('Raw API result:', result)
77
107
 
78
- // Handle both nested and direct responses
79
108
  const timers = result.data || result
80
109
  console.log('Timers after processing:', timers)
81
110
 
@@ -151,7 +180,6 @@ export function TimersPage() {
151
180
  return Math.round((timer.success_count / timer.execution_count) * 100)
152
181
  }
153
182
 
154
- // Sorting logic
155
183
  const sortedTimers = [...filteredTimers].sort((a, b) => {
156
184
  const aValue = a[sortKey as keyof Timer]
157
185
  const bValue = b[sortKey as keyof Timer]
@@ -191,7 +219,6 @@ export function TimersPage() {
191
219
  navigate(`/spine-framework/admin/configs/timers/${timer.id}`)
192
220
  }
193
221
 
194
- // Stats cards
195
222
  const statsCards = [
196
223
  {
197
224
  title: 'Total Timers',
@@ -374,3 +401,4 @@ export function TimersPage() {
374
401
  </AdminListPage>
375
402
  )
376
403
  }
404
+ // CIL[END chunk_id="timerspage_timerspage_1783664706"]