@cat-factory/app 0.302.2 → 0.303.0

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 (328) hide show
  1. package/README.md +16 -0
  2. package/app/app.config.ts +19 -10
  3. package/app/assets/css/main.css +13 -13
  4. package/app/assets/css/prose.css +22 -22
  5. package/app/assets/css/tokens.css +298 -0
  6. package/app/components/assistant/AssistantModal.vue +22 -22
  7. package/app/components/auth/AuthGate.vue +1 -1
  8. package/app/components/auth/BackendMisconfiguredScreen.vue +10 -10
  9. package/app/components/auth/LoginScreen.vue +34 -36
  10. package/app/components/auth/ResetPasswordScreen.vue +12 -12
  11. package/app/components/auth/UserMenu.vue +4 -4
  12. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +27 -25
  13. package/app/components/binaryOutput/BinaryOutputReport.vue +15 -15
  14. package/app/components/binaryOutput/StoredAssetView.vue +5 -5
  15. package/app/components/board/AddTaskModal.vue +17 -20
  16. package/app/components/board/AgentFailureCard.vue +11 -11
  17. package/app/components/board/AgentFailureHistory.vue +2 -2
  18. package/app/components/board/BoardCanvas.vue +6 -5
  19. package/app/components/board/CreateInitiativeModal.vue +8 -10
  20. package/app/components/board/DependencyConnectOverlay.vue +1 -1
  21. package/app/components/board/FailureHistoryList.vue +7 -7
  22. package/app/components/board/RecurringPipelineModal.vue +17 -17
  23. package/app/components/board/ReviewFrictionDialog.vue +6 -6
  24. package/app/components/board/StepExecutionHistory.vue +13 -13
  25. package/app/components/board/TaskDependencyEdges.vue +8 -8
  26. package/app/components/board/nodes/AgentChip.vue +10 -10
  27. package/app/components/board/nodes/BlockNode.vue +29 -29
  28. package/app/components/board/nodes/DecisionBadge.vue +2 -2
  29. package/app/components/board/nodes/EpicNode.vue +14 -10
  30. package/app/components/board/nodes/FrameSwimlanes.vue +7 -9
  31. package/app/components/board/nodes/InitiativeCard.vue +8 -8
  32. package/app/components/board/nodes/LaneGroup.vue +1 -1
  33. package/app/components/board/nodes/LaneTask.vue +2 -2
  34. package/app/components/board/nodes/ResizeGrips.vue +4 -4
  35. package/app/components/board/nodes/TaskCard.vue +18 -18
  36. package/app/components/board/nodes/TaskLane.vue +5 -5
  37. package/app/components/board/nodes/TaskPipelineMini.vue +18 -18
  38. package/app/components/bootstrap/AdoptionReviewModal.vue +29 -26
  39. package/app/components/bootstrap/BootstrapModal.vue +21 -23
  40. package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
  41. package/app/components/brainstorm/BrainstormWindow.vue +30 -33
  42. package/app/components/bugFishing/BugFishingWindow.vue +49 -49
  43. package/app/components/clarity/ClarityReviewWindow.vue +30 -30
  44. package/app/components/common/AsyncViewError.vue +5 -5
  45. package/app/components/common/ClarificationItem.vue +8 -13
  46. package/app/components/common/ConfirmDialog.vue +4 -2
  47. package/app/components/common/DescriptorFields.vue +1 -1
  48. package/app/components/common/EmptyState.vue +3 -3
  49. package/app/components/common/InterviewGateNotice.vue +7 -7
  50. package/app/components/common/KeyboardShortcutsHelp.vue +1 -1
  51. package/app/components/common/MarkdownProse.vue +14 -13
  52. package/app/components/consensus/ConsensusSessionWindow.vue +26 -26
  53. package/app/components/context/ContextAttachmentFields.vue +17 -17
  54. package/app/components/docs/DocInterviewWindow.vue +14 -14
  55. package/app/components/documents/ContextDocumentPicker.vue +17 -17
  56. package/app/components/documents/DocumentImportModal.vue +7 -7
  57. package/app/components/documents/DocumentSourceConnectModal.vue +3 -3
  58. package/app/components/documents/DocumentSyncState.vue +5 -5
  59. package/app/components/documents/DocumentTemplatesModal.vue +15 -15
  60. package/app/components/documents/RepoContextDocPicker.vue +5 -5
  61. package/app/components/documents/SpawnPreviewModal.vue +12 -12
  62. package/app/components/documents/StartFromDesignModal.vue +11 -11
  63. package/app/components/documents/TaskContextDocs.vue +5 -5
  64. package/app/components/environments/EnvSetupStepper.vue +6 -6
  65. package/app/components/environments/EnvironmentStatusPanel.vue +17 -17
  66. package/app/components/environments/steps/EnvPickStep.vue +3 -3
  67. package/app/components/environments/steps/EnvPreflightStep.vue +9 -9
  68. package/app/components/environments/steps/EnvReviewStep.vue +25 -22
  69. package/app/components/environments/steps/EnvSaveStep.vue +7 -5
  70. package/app/components/environments/steps/JourneyStepNav.vue +1 -1
  71. package/app/components/focus/BlockFocusView.vue +17 -19
  72. package/app/components/followUp/FollowUpWindow.vue +23 -23
  73. package/app/components/forkDecision/ForkDecisionWindow.vue +37 -39
  74. package/app/components/foundational/FoundationalContractSummary.vue +7 -7
  75. package/app/components/foundational/FoundationalServiceCatalogList.vue +11 -11
  76. package/app/components/foundational/FoundationalServiceManager.vue +1 -1
  77. package/app/components/foundational/FoundationalServiceRegistry.vue +13 -13
  78. package/app/components/foundational/FoundationalServiceSources.vue +13 -13
  79. package/app/components/foundational/FoundationalSuppressions.vue +7 -7
  80. package/app/components/foundational/ServiceCatalogConnection.vue +10 -10
  81. package/app/components/fragments/FragmentLibraryManager.vue +41 -44
  82. package/app/components/fragments/FragmentSelector.vue +13 -13
  83. package/app/components/fragments/GitHubDocUrlImport.vue +1 -1
  84. package/app/components/gates/GateFailingCheckList.vue +5 -5
  85. package/app/components/gates/GateResultView.vue +50 -47
  86. package/app/components/github/AddServiceFromRepoModal.vue +14 -14
  87. package/app/components/github/BranchProtectionPreflight.vue +13 -13
  88. package/app/components/github/GitHubConnect.vue +7 -7
  89. package/app/components/github/GitHubOnboarding.vue +7 -7
  90. package/app/components/github/GitHubPanel.vue +37 -40
  91. package/app/components/github/RepoTreeBrowser.vue +18 -18
  92. package/app/components/humanTest/HumanTestWindow.vue +30 -33
  93. package/app/components/initiative/InitiativePlanNotice.vue +5 -5
  94. package/app/components/initiative/InitiativePlanReview.vue +27 -29
  95. package/app/components/initiative/InitiativePlanningWindow.vue +10 -10
  96. package/app/components/initiative/InitiativeTrackerWindow.vue +71 -65
  97. package/app/components/inputGate/InputGateNotice.vue +2 -4
  98. package/app/components/judge/JudgeResultView.vue +30 -32
  99. package/app/components/kaizen/KaizenPanel.vue +40 -36
  100. package/app/components/kaizen/KaizenStepStatus.vue +21 -21
  101. package/app/components/layout/AccountAuditLog.vue +10 -10
  102. package/app/components/layout/AccountDeploymentSettings.vue +20 -20
  103. package/app/components/layout/AccountFailureKindRules.vue +13 -13
  104. package/app/components/layout/AccountFoundationalSettings.vue +1 -1
  105. package/app/components/layout/AccountFragmentSettings.vue +1 -1
  106. package/app/components/layout/AccountModelPolicySettings.vue +9 -9
  107. package/app/components/layout/AccountPlatformAlertSettings.vue +11 -11
  108. package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
  109. package/app/components/layout/AccountRunCredentialSettings.vue +5 -5
  110. package/app/components/layout/AccountSkillSettings.vue +1 -1
  111. package/app/components/layout/AccountTeamSettings.vue +23 -15
  112. package/app/components/layout/AiProvidersBanner.vue +11 -8
  113. package/app/components/layout/AppearanceSwitcher.vue +108 -0
  114. package/app/components/layout/BoardSwitcher.vue +8 -11
  115. package/app/components/layout/BoardToolbar.vue +3 -3
  116. package/app/components/layout/CommandBar.vue +7 -9
  117. package/app/components/layout/ConnectionStatusBanner.vue +2 -2
  118. package/app/components/layout/DefaultTestEnvBanner.vue +5 -5
  119. package/app/components/layout/GitHubPatBanner.vue +5 -5
  120. package/app/components/layout/GitHubPatPermissionsBanner.vue +16 -10
  121. package/app/components/layout/InfraSetupBanner.vue +6 -6
  122. package/app/components/layout/IntegrationsHub.vue +15 -15
  123. package/app/components/layout/LanguageSwitcher.vue +5 -5
  124. package/app/components/layout/ModelProvidersHub.vue +13 -16
  125. package/app/components/layout/NotificationsInbox.vue +10 -10
  126. package/app/components/layout/PersonalSetupModal.vue +8 -8
  127. package/app/components/layout/ProviderConfigBanner.vue +4 -4
  128. package/app/components/layout/RolePrompt.vue +7 -7
  129. package/app/components/layout/SideBar.vue +6 -4
  130. package/app/components/layout/SpendWarningBanner.vue +17 -14
  131. package/app/components/layout/TranslationWarningBanner.vue +4 -4
  132. package/app/components/layout/UiModeSwitcher.vue +13 -13
  133. package/app/components/layout/UiRoleSwitcher.vue +8 -8
  134. package/app/components/layout/WorkspaceMembersSettings.vue +15 -11
  135. package/app/components/media/ArtifactLightbox.vue +13 -13
  136. package/app/components/media/ImageCompare.vue +31 -24
  137. package/app/components/merge/MergeEffortChips.vue +2 -2
  138. package/app/components/notifications/NotificationSettingsPanel.vue +14 -12
  139. package/app/components/observability/OutcomeFilterChips.vue +6 -6
  140. package/app/components/observability/RunFailureSummary.vue +28 -25
  141. package/app/components/observability/StepMetricsBar.vue +26 -28
  142. package/app/components/observability/StepModelActivity.vue +2 -2
  143. package/app/components/observability/ToolCallList.vue +24 -24
  144. package/app/components/outcome/OutcomeSummaryWindow.vue +47 -47
  145. package/app/components/palettes/AgentPalette.vue +8 -8
  146. package/app/components/palettes/AgentTierSelect.vue +1 -1
  147. package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
  148. package/app/components/panels/AgentStepDetail.vue +52 -51
  149. package/app/components/panels/AttemptEntryHeader.vue +4 -6
  150. package/app/components/panels/DecisionModal.vue +7 -7
  151. package/app/components/panels/GenericStructuredResultView.vue +7 -7
  152. package/app/components/panels/InspectorPanel.vue +17 -17
  153. package/app/components/panels/MergerResultView.vue +19 -19
  154. package/app/components/panels/ObservabilityPanel.vue +115 -123
  155. package/app/components/panels/OperatorDashboardPanel.vue +89 -97
  156. package/app/components/panels/ReportsPanel.vue +69 -66
  157. package/app/components/panels/ReportsSpendBreakdown.vue +9 -9
  158. package/app/components/panels/ResultWindowShell.vue +38 -38
  159. package/app/components/panels/RunDetailLoadState.vue +2 -2
  160. package/app/components/panels/StepContainerStatus.vue +9 -9
  161. package/app/components/panels/StepDelegatedStatus.logic.spec.ts +135 -0
  162. package/app/components/panels/StepDelegatedStatus.logic.ts +160 -0
  163. package/app/components/panels/StepDelegatedStatus.vue +181 -0
  164. package/app/components/panels/StepEffortReport.vue +16 -15
  165. package/app/components/panels/StepFragmentAdherence.vue +14 -10
  166. package/app/components/panels/StepMetadataCard.vue +74 -45
  167. package/app/components/panels/StepReproductionReport.vue +25 -21
  168. package/app/components/panels/StepRunMeta.vue +20 -20
  169. package/app/components/panels/StepTestReport.logic.spec.ts +8 -3
  170. package/app/components/panels/StepTestReport.logic.ts +4 -4
  171. package/app/components/panels/StepTestReport.vue +28 -28
  172. package/app/components/panels/StepToolServers.vue +14 -11
  173. package/app/components/panels/StepValidationReport.vue +7 -7
  174. package/app/components/panels/inspector/ContainerSummary.vue +9 -9
  175. package/app/components/panels/inspector/DocReferenceRepos.vue +3 -3
  176. package/app/components/panels/inspector/EnvProbeReport.vue +24 -27
  177. package/app/components/panels/inspector/EpicChildren.vue +8 -12
  178. package/app/components/panels/inspector/FrontendBindingsResolved.vue +11 -11
  179. package/app/components/panels/inspector/FrontendConfig.vue +37 -41
  180. package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
  181. package/app/components/panels/inspector/InspectorSection.vue +6 -6
  182. package/app/components/panels/inspector/RecurringScheduleSettings.vue +16 -19
  183. package/app/components/panels/inspector/ServiceConnections.vue +2 -2
  184. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
  185. package/app/components/panels/inspector/ServiceSelfTests.vue +21 -17
  186. package/app/components/panels/inspector/ServiceTestConfig.vue +51 -59
  187. package/app/components/panels/inspector/ServiceTestSecrets.vue +5 -5
  188. package/app/components/panels/inspector/ServiceTestingContext.vue +3 -3
  189. package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
  190. package/app/components/panels/inspector/TaskAgentConfig.vue +3 -3
  191. package/app/components/panels/inspector/TaskAprioriBranches.vue +8 -5
  192. package/app/components/panels/inspector/TaskDependencies.vue +1 -1
  193. package/app/components/panels/inspector/TaskEstimateBadge.vue +11 -11
  194. package/app/components/panels/inspector/TaskExecution.vue +17 -17
  195. package/app/components/panels/inspector/TaskReviewTarget.vue +2 -2
  196. package/app/components/panels/inspector/TaskRunSettings.vue +35 -37
  197. package/app/components/panels/inspector/TaskStructure.vue +3 -3
  198. package/app/components/pipeline/AgentKindIcon.vue +1 -1
  199. package/app/components/pipeline/AgentPromptEditor.vue +17 -17
  200. package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -56
  201. package/app/components/pipeline/EstimateThresholdFields.vue +4 -4
  202. package/app/components/pipeline/GateConfigFields.vue +13 -10
  203. package/app/components/pipeline/IterationCapPrompt.vue +4 -2
  204. package/app/components/pipeline/PipelineBuilder.vue +47 -55
  205. package/app/components/pipeline/PipelineHealthModal.vue +27 -29
  206. package/app/components/pipeline/PipelinePicker.vue +10 -10
  207. package/app/components/pipeline/PipelinePreview.vue +10 -10
  208. package/app/components/pipeline/PipelineProgress.vue +64 -67
  209. package/app/components/prReview/PrReviewPhaseBadge.vue +7 -7
  210. package/app/components/prReview/PrReviewWindow.vue +75 -77
  211. package/app/components/providers/AiPresetMismatchDialog.vue +5 -5
  212. package/app/components/providers/AiProviderOnboardingModal.vue +9 -9
  213. package/app/components/providers/ApiKeysSection.vue +13 -13
  214. package/app/components/providers/PersonalCredentialModal.vue +2 -2
  215. package/app/components/providers/PersonalSubscriptionSection.vue +10 -10
  216. package/app/components/providers/SignInRequiredNotice.vue +1 -1
  217. package/app/components/providers/VendorCredentialsModal.vue +10 -10
  218. package/app/components/provisioning/ProvisioningLogsDrawer.vue +13 -13
  219. package/app/components/ralph/RalphLoopResultView.vue +28 -30
  220. package/app/components/recurring/RecurrenceEditor.vue +2 -2
  221. package/app/components/requirements/RequirementsReviewWindow.vue +51 -58
  222. package/app/components/riskPolicy/RiskPolicyPicker.vue +12 -15
  223. package/app/components/riskPolicy/RiskPolicyPreview.vue +14 -14
  224. package/app/components/sandbox/SandboxPanel.vue +58 -59
  225. package/app/components/settings/AccountSettingsPanel.vue +1 -1
  226. package/app/components/settings/ApiTokensPanel.vue +10 -10
  227. package/app/components/settings/BudgetSettings.vue +18 -22
  228. package/app/components/settings/CapabilityCredentialsPanel.vue +15 -15
  229. package/app/components/settings/CloudflareHandlerSection.vue +20 -20
  230. package/app/components/settings/ComposeEnvironmentSetupSection.vue +13 -13
  231. package/app/components/settings/ConnectionTestVerdict.vue +3 -3
  232. package/app/components/settings/ConnectionWarnings.vue +1 -1
  233. package/app/components/settings/ConsensusGroupsSection.vue +27 -35
  234. package/app/components/settings/CustomManifestTypeEditor.vue +10 -10
  235. package/app/components/settings/DefaultProvisionTypeSection.vue +6 -6
  236. package/app/components/settings/InfraHandlersConfigurator.vue +24 -22
  237. package/app/components/settings/InfrastructureBackendPicker.vue +6 -6
  238. package/app/components/settings/InfrastructureWindow.vue +5 -5
  239. package/app/components/settings/IssueTrackerPanel.vue +29 -23
  240. package/app/components/settings/KubernetesEngineForm.vue +11 -11
  241. package/app/components/settings/KubernetesEnvironmentForm.vue +4 -4
  242. package/app/components/settings/LocalContainerPoolSettings.vue +6 -11
  243. package/app/components/settings/LocalModelEndpointsPanel.vue +20 -18
  244. package/app/components/settings/McpAuthorizeScreen.vue +13 -15
  245. package/app/components/settings/McpOAuthCallbackScreen.vue +16 -10
  246. package/app/components/settings/MergeClassRulesEditor.vue +6 -9
  247. package/app/components/settings/MergeRolePolicyEditor.vue +16 -16
  248. package/app/components/settings/ModelConfigurationPanel.vue +23 -30
  249. package/app/components/settings/ModelPresetHealthModal.vue +15 -13
  250. package/app/components/settings/ObservabilityConnectionPanel.vue +4 -4
  251. package/app/components/settings/OpenRouterCatalogPanel.vue +23 -19
  252. package/app/components/settings/PackageRegistriesPanel.vue +8 -8
  253. package/app/components/settings/ProviderConnectionTab.vue +8 -11
  254. package/app/components/settings/ProviderManifestEditor.vue +9 -9
  255. package/app/components/settings/ProviderPreferenceEditor.vue +8 -8
  256. package/app/components/settings/RiskPolicyCreateForm.vue +10 -10
  257. package/app/components/settings/RiskPolicyEditorRow.vue +14 -14
  258. package/app/components/settings/RiskPolicyHealthModal.vue +15 -13
  259. package/app/components/settings/RiskPolicyInheritedRow.vue +1 -1
  260. package/app/components/settings/RiskPolicyPanel.vue +10 -15
  261. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +7 -7
  262. package/app/components/settings/SharedStacksPanel.vue +9 -12
  263. package/app/components/settings/TaskSourceCard.vue +4 -4
  264. package/app/components/settings/TaskTypeSuppressionsPanel.vue +6 -6
  265. package/app/components/settings/ToolServerChecklist.vue +36 -29
  266. package/app/components/settings/UsageSettings.vue +15 -15
  267. package/app/components/settings/UserSecretsSection.vue +9 -12
  268. package/app/components/settings/WorkspaceMetadataSettings.vue +5 -5
  269. package/app/components/settings/WorkspaceSettingsPanel.vue +47 -47
  270. package/app/components/skills/ReviewSkillQueue.vue +10 -10
  271. package/app/components/skills/SkillLibraryManager.vue +23 -20
  272. package/app/components/slack/SlackPanel.vue +13 -17
  273. package/app/components/spec/ServiceSpecWindow.vue +45 -42
  274. package/app/components/tasks/BugHuntModal.vue +24 -27
  275. package/app/components/tasks/ContextIssuePicker.vue +11 -11
  276. package/app/components/tasks/TaskContextIssues.vue +3 -3
  277. package/app/components/tasks/TaskImportModal.vue +7 -7
  278. package/app/components/tasks/TaskSourceConnectModal.vue +10 -8
  279. package/app/components/testing/TestReportWindow.vue +99 -82
  280. package/app/components/theme/ThemeImportModal.vue +115 -0
  281. package/app/components/tutorial/TutorialCatalogue.vue +9 -9
  282. package/app/components/tutorial/TutorialNudge.vue +5 -5
  283. package/app/components/tutorial/TutorialOverlay.vue +12 -12
  284. package/app/components/tutorial/TutorialPrompt.vue +6 -9
  285. package/app/components/vcs/GitLabConnect.vue +4 -4
  286. package/app/components/vcs/VcsConnectSurfaces.vue +2 -2
  287. package/app/components/visualConfirm/VisualConfirmationWindow.vue +36 -36
  288. package/app/composables/api/client.ts +6 -1
  289. package/app/composables/usePipelineErrorToast.ts +17 -0
  290. package/app/modular/agent-kinds.ts +24 -0
  291. package/app/pages/index.vue +5 -5
  292. package/app/plugins/theme.client.ts +125 -0
  293. package/app/spa-loading-template.html +52 -14
  294. package/app/stores/agents.ts +1 -1
  295. package/app/stores/theme.spec.ts +56 -0
  296. package/app/stores/theme.ts +87 -0
  297. package/app/types/domain.ts +19 -0
  298. package/app/types/execution.ts +4 -0
  299. package/app/utils/agentOutput.ts +1 -1
  300. package/app/utils/binaryOutput.ts +6 -6
  301. package/app/utils/bootstrapSteps.ts +14 -14
  302. package/app/utils/catalog.ts +87 -83
  303. package/app/utils/colorTint.ts +10 -0
  304. package/app/utils/failureKinds.ts +1 -0
  305. package/app/utils/observability.ts +3 -3
  306. package/app/utils/pipelineRender.ts +13 -17
  307. package/app/utils/swimlanes.ts +4 -4
  308. package/app/utils/theme/bootPalette.spec.ts +54 -0
  309. package/app/utils/theme/bootPalette.ts +108 -0
  310. package/app/utils/theme/builtins.spec.ts +38 -0
  311. package/app/utils/theme/builtins.ts +55 -0
  312. package/app/utils/theme/css.ts +162 -0
  313. package/app/utils/theme/doc.ts +353 -0
  314. package/app/utils/theme/link.ts +93 -0
  315. package/app/utils/theme/presets.ts +509 -0
  316. package/app/utils/theme/theme.spec.ts +224 -0
  317. package/i18n/locales/de.json +61 -4
  318. package/i18n/locales/en.json +70 -4
  319. package/i18n/locales/es.json +61 -4
  320. package/i18n/locales/fr.json +61 -4
  321. package/i18n/locales/he.json +61 -4
  322. package/i18n/locales/it.json +61 -4
  323. package/i18n/locales/ja.json +61 -4
  324. package/i18n/locales/pl.json +61 -4
  325. package/i18n/locales/tr.json +61 -4
  326. package/i18n/locales/uk.json +61 -4
  327. package/nuxt.config.ts +37 -11
  328. package/package.json +9 -8
@@ -0,0 +1,181 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import {
4
+ delegatedUsageUnreported,
5
+ delegationStatusView,
6
+ externalRunHref,
7
+ } from './StepDelegatedStatus.logic'
8
+ import type { PipelineStep } from '~/types/execution'
9
+
10
+ // The EXTERNAL work a delegated step dispatched: which registered executor is running it, what it
11
+ // is doing, and the link to that system's own logs, which is the affordance everything else here
12
+ // exists to frame.
13
+ //
14
+ // The sibling of `StepContainerStatus`, and deliberately not the same component. A container step
15
+ // reports a phase, an id and a reachable address, all of which the platform observes directly; a
16
+ // delegated step reports whatever an external system chose to tell us, which is usually a status
17
+ // and a URL. Rendering the second through the first would put empty rows labelled "Container" and
18
+ // "Address" on every delegated step, and would claim a container the platform never started.
19
+ //
20
+ // The second thing it says is what the platform CANNOT see. A delegated step's model calls never
21
+ // touch this deployment's proxy or recorder, so its tokens are in no total on this page, and a
22
+ // missing number is invisible. See `delegatedUsageUnreported`, which answers a settled step from
23
+ // contracts' own rule, the one the backend's gap fold reads, so the card and the debug overview
24
+ // cannot disagree about one step.
25
+ const props = defineProps<{
26
+ step: PipelineStep
27
+ /**
28
+ * The registered executor's presentation, when this build still registers it. ABSENT is a real
29
+ * state rather than a loading one: a deployment can stop registering an executor while runs that
30
+ * used it are still on the board, and the card then names the id instead of pretending to a
31
+ * label it does not have.
32
+ */
33
+ executor?: { label: string; description: string; telemetry: 'not-reported' | 'self-reported' }
34
+ }>()
35
+
36
+ const { t } = useI18n()
37
+
38
+ const record = computed(() => props.step.delegated ?? null)
39
+
40
+ /**
41
+ * Everything the card renders for this record's status, narrowed against the vocabulary this build
42
+ * ships. See `StepDelegatedStatus.logic` for why a bare `Record` lookup cannot be used here.
43
+ */
44
+ const statusView = computed(() => delegationStatusView(record.value?.status))
45
+
46
+ /** The executor's own label when this build registers it, else the persisted id. */
47
+ const executorName = computed(() => props.executor?.label || record.value?.executor || '')
48
+
49
+ /**
50
+ * The branch the work LANDED on, shown only when there is no pull request to show instead.
51
+ *
52
+ * An executor that pushes and lets a later step open the PR is a case the port names as
53
+ * legitimate, and then this is the run's entire product: without it the card reports a completed
54
+ * external run with nothing to point at.
55
+ */
56
+ const landedBranch = computed(() => record.value?.branch ?? null)
57
+
58
+ /**
59
+ * The run link, only when it is one this SPA may follow. The value comes from an external
60
+ * system's API, and this is the card's primary affordance; see {@link externalRunHref}.
61
+ */
62
+ const runHref = computed(() => externalRunHref(record.value?.url))
63
+
64
+ /** Whether to say the platform is not measuring this step's spend; see the logic sibling. */
65
+ const usageUnreported = computed(() => delegatedUsageUnreported(props.step, props.executor))
66
+
67
+ /** Earlier attempts, newest last, shown only once there is more than the current one. */
68
+ const priorAttempts = computed(() => {
69
+ const attempts = record.value?.attempts ?? []
70
+ return attempts.length > 1 ? attempts.slice(0, -1) : []
71
+ })
72
+
73
+ const { copy: copyText } = useCopyToClipboard()
74
+ </script>
75
+
76
+ <template>
77
+ <!-- Single conditional root so a passed-through `class` (e.g. layout margin) applies cleanly.
78
+ Renders nothing for a step that dispatched nowhere external. -->
79
+ <div v-if="record" data-testid="step-delegated-status">
80
+ <div class="rounded-lg border px-3 py-2 text-[12px]" :class="statusView.meta.cls">
81
+ <div class="flex items-center gap-2">
82
+ <UIcon
83
+ :name="statusView.meta.icon"
84
+ class="h-4 w-4 shrink-0"
85
+ :class="statusView.meta.spin ? 'animate-spin' : ''"
86
+ />
87
+ <!-- A status this build does not know is NAMED as unrecognised, carrying the stored value,
88
+ rather than guessed onto a current one or silently dropped. -->
89
+ <span class="font-medium">{{ t(statusView.labelKey, statusView.labelParams) }}</span>
90
+ <span class="text-dimmed">·</span>
91
+ <span class="truncate" :title="executor?.description">{{ executorName }}</span>
92
+ <template v-if="record.phase && statusView.status === 'running'">
93
+ <span class="text-dimmed">·</span>
94
+ <span>{{ record.phase }}</span>
95
+ </template>
96
+ </div>
97
+
98
+ <!-- The PRIMARY affordance: the executor's own logs. It is the only place the detail of
99
+ what happened exists, which is why a failed delegated step without it is a dead end. -->
100
+ <div v-if="record.url" class="mt-2 flex items-center gap-2">
101
+ <dt class="shrink-0 text-[11px] uppercase tracking-wide text-dimmed">
102
+ {{ t('panels.stepMeta.delegated.run') }}
103
+ </dt>
104
+ <!-- Linked only when the executor's URL is `http(s)`. Anything else is shown as the text
105
+ it is: refusing to follow it is right, and hiding it would report a run that named
106
+ no link at all. -->
107
+ <dd class="truncate font-mono text-[11px] text-toned">
108
+ <a
109
+ v-if="runHref"
110
+ :href="runHref"
111
+ target="_blank"
112
+ rel="noopener noreferrer"
113
+ class="hover:underline"
114
+ >
115
+ {{ record.url }}
116
+ </a>
117
+ <span v-else>{{ record.url }}</span>
118
+ </dd>
119
+ <UButton
120
+ icon="i-lucide-copy"
121
+ color="neutral"
122
+ variant="ghost"
123
+ size="xs"
124
+ class="ms-auto shrink-0"
125
+ :title="t('panels.stepMeta.delegated.copyUrl')"
126
+ :aria-label="t('panels.stepMeta.delegated.copyUrl')"
127
+ @click="copyText(record.url)"
128
+ />
129
+ </div>
130
+
131
+ <!-- The branch the work landed on, for an executor that pushed without opening a pull
132
+ request. Rendered here because the platform holds nothing else about that run's
133
+ product. -->
134
+ <div v-if="landedBranch" class="mt-2 flex items-center gap-2">
135
+ <dt class="shrink-0 text-[11px] uppercase tracking-wide text-dimmed">
136
+ {{ t('panels.stepMeta.delegated.branch') }}
137
+ </dt>
138
+ <dd class="truncate font-mono text-[11px] text-toned">{{ landedBranch }}</dd>
139
+ </div>
140
+
141
+ <!-- What the platform could not do, stated rather than left to read as a clean outcome:
142
+ a cancelled run whose executor declares no cancel is still going. -->
143
+ <p v-if="record.note" class="mt-2 text-[11px] text-toned">{{ record.note }}</p>
144
+ </div>
145
+
146
+ <!-- "Absent" and "zero" must never render the same. Without this line a delegated step shows
147
+ no tokens beside a container step that shows some, and the only available reading is that
148
+ it was free. -->
149
+ <p v-if="usageUnreported" class="mt-2 text-[11px] text-dimmed">
150
+ {{ t('panels.stepMeta.delegated.usageNotReported', { executor: executorName }) }}
151
+ </p>
152
+
153
+ <!-- Earlier attempts. Kept across a re-run on purpose: the previous run's logs are the
154
+ evidence for why this step is being run again, and the platform holds nothing else. -->
155
+ <div v-if="priorAttempts.length" class="mt-3">
156
+ <div class="text-[11px] uppercase tracking-wide text-dimmed">
157
+ {{ t('panels.stepMeta.delegated.earlierAttempts') }}
158
+ </div>
159
+ <ul class="mt-1 space-y-1">
160
+ <li
161
+ v-for="(attempt, index) in priorAttempts"
162
+ :key="`${attempt.startedAt}-${index}`"
163
+ class="flex items-center gap-2 text-[11px] text-muted"
164
+ >
165
+ <span class="truncate">{{
166
+ attempt.outcome || t('panels.stepMeta.delegated.noOutcome')
167
+ }}</span>
168
+ <a
169
+ v-if="externalRunHref(attempt.url)"
170
+ :href="externalRunHref(attempt.url)!"
171
+ target="_blank"
172
+ rel="noopener noreferrer"
173
+ class="ms-auto shrink-0 font-mono hover:underline"
174
+ >
175
+ {{ t('panels.stepMeta.delegated.openRun') }}
176
+ </a>
177
+ </li>
178
+ </ul>
179
+ </div>
180
+ </div>
181
+ </template>
@@ -22,7 +22,11 @@ const difficultyPct = computed(() =>
22
22
  Math.min(100, Math.max(0, (props.report.difficulty / 10) * 100)),
23
23
  )
24
24
  // Colour the difficulty by band: easy (emerald) → moderate (amber) → hard (rose).
25
- const BAR_CLASS = { easy: 'bg-emerald-400', moderate: 'bg-amber-400', hard: 'bg-rose-400' } as const
25
+ const BAR_CLASS = {
26
+ easy: 'bg-app-success-400',
27
+ moderate: 'bg-app-warning-400',
28
+ hard: 'bg-app-error-400',
29
+ } as const
26
30
  const difficultyClass = computed(() => BAR_CLASS[effortBand(props.report.difficulty)])
27
31
  </script>
28
32
 
@@ -30,25 +34,22 @@ const difficultyClass = computed(() => BAR_CLASS[effortBand(props.report.difficu
30
34
  <section
31
35
  data-testid="step-effort-report"
32
36
  :class="
33
- variant === 'card' ? 'scroll-mt-4 rounded-xl border border-slate-800 bg-slate-900/50 p-4' : ''
37
+ variant === 'card' ? 'scroll-mt-4 rounded-xl border border-default bg-default/50 p-4' : ''
34
38
  "
35
39
  >
36
40
  <div
37
41
  v-if="variant === 'card'"
38
- class="mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-slate-400"
42
+ class="mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted"
39
43
  >
40
44
  <UIcon name="i-lucide-gauge" class="h-3.5 w-3.5" />
41
45
  <span>{{ t('panels.stepDetail.effort.heading') }}</span>
42
46
  </div>
43
47
 
44
48
  <div class="flex items-center gap-2">
45
- <span
46
- class="text-[12px] text-slate-300"
47
- :title="t('panels.stepDetail.effort.difficultyHint')"
48
- >
49
+ <span class="text-[12px] text-toned" :title="t('panels.stepDetail.effort.difficultyHint')">
49
50
  {{ t('panels.stepDetail.effort.difficulty') }}
50
51
  </span>
51
- <div class="h-1.5 flex-1 overflow-hidden rounded-full bg-slate-700/60">
52
+ <div class="h-1.5 flex-1 overflow-hidden rounded-full bg-accented/60">
52
53
  <div
53
54
  class="h-full rounded-full"
54
55
  :class="difficultyClass"
@@ -57,7 +58,7 @@ const difficultyClass = computed(() => BAR_CLASS[effortBand(props.report.difficu
57
58
  </div>
58
59
  <span
59
60
  data-testid="step-effort-difficulty"
60
- class="text-[12px] font-medium text-slate-200"
61
+ class="text-[12px] font-medium text-default"
61
62
  :title="t('panels.stepDetail.effort.difficultyHint')"
62
63
  >
63
64
  {{ t('panels.stepDetail.effort.outOfTen', { value: report.difficulty }) }}
@@ -66,33 +67,33 @@ const difficultyClass = computed(() => BAR_CLASS[effortBand(props.report.difficu
66
67
 
67
68
  <p
68
69
  v-if="report.summary"
69
- class="mt-2 whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300"
70
+ class="mt-2 whitespace-pre-wrap text-[13px] leading-relaxed text-toned"
70
71
  >
71
72
  {{ report.summary }}
72
73
  </p>
73
74
 
74
75
  <div v-if="report.reducedEffectiveness" class="mt-3">
75
- <p class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
76
+ <p class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
76
77
  {{ t('panels.stepDetail.effort.reduced') }}
77
78
  </p>
78
- <p class="mt-0.5 whitespace-pre-wrap text-[12px] text-slate-300">
79
+ <p class="mt-0.5 whitespace-pre-wrap text-[12px] text-toned">
79
80
  {{ report.reducedEffectiveness }}
80
81
  </p>
81
82
  </div>
82
83
 
83
84
  <div v-if="report.obstacles?.length" class="mt-3">
84
- <p class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
85
+ <p class="text-[11px] font-semibold uppercase tracking-wide text-dimmed">
85
86
  {{ t('panels.stepDetail.effort.obstacles') }}
86
87
  </p>
87
88
  <ul class="mt-0.5 space-y-1">
88
89
  <li
89
90
  v-for="(obstacle, i) in report.obstacles"
90
91
  :key="i"
91
- class="flex items-start gap-1.5 text-[12px] text-slate-300"
92
+ class="flex items-start gap-1.5 text-[12px] text-toned"
92
93
  >
93
94
  <UIcon
94
95
  name="i-lucide-alert-triangle"
95
- class="mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-400/80"
96
+ class="mt-0.5 h-3.5 w-3.5 shrink-0 text-app-warning-400/80"
96
97
  />
97
98
  <span>{{ obstacle }}</span>
98
99
  </li>
@@ -13,7 +13,11 @@ const { t } = useI18n()
13
13
 
14
14
  /** Rating band → bar colour: poor adherence (rose) → partial (amber) → strong (emerald). */
15
15
  function ratingClass(rating: number): string {
16
- return rating >= 8 ? 'bg-emerald-400' : rating >= 5 ? 'bg-amber-400' : 'bg-rose-400'
16
+ return rating >= 8
17
+ ? 'bg-app-success-400'
18
+ : rating >= 5
19
+ ? 'bg-app-warning-400'
20
+ : 'bg-app-error-400'
17
21
  }
18
22
  function ratingPct(rating: number): number {
19
23
  return Math.min(100, Math.max(0, (rating / 10) * 100))
@@ -28,10 +32,10 @@ function label(item: FragmentAdherence[number]): string {
28
32
  <section
29
33
  v-if="items.length"
30
34
  data-testid="step-fragment-adherence"
31
- class="scroll-mt-4 rounded-xl border border-slate-800 bg-slate-900/50 p-4"
35
+ class="scroll-mt-4 rounded-xl border border-default bg-default/50 p-4"
32
36
  >
33
37
  <div
34
- class="mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-slate-400"
38
+ class="mb-2 flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wide text-muted"
35
39
  >
36
40
  <UIcon name="i-lucide-clipboard-check" class="h-3.5 w-3.5" />
37
41
  <span :title="t('panels.stepDetail.adherence.headingHint')">
@@ -44,13 +48,13 @@ function label(item: FragmentAdherence[number]): string {
44
48
  v-for="(item, i) in items"
45
49
  :key="item.fragmentId ?? i"
46
50
  data-testid="step-fragment-adherence-item"
47
- class="rounded-lg border border-slate-800 bg-slate-900/60 px-3 py-2"
51
+ class="rounded-lg border border-default bg-default/60 px-3 py-2"
48
52
  >
49
53
  <div class="flex items-center gap-2">
50
- <span class="min-w-0 flex-1 truncate text-[13px] font-medium text-slate-100">{{
54
+ <span class="min-w-0 flex-1 truncate text-[13px] font-medium text-app-100">{{
51
55
  label(item)
52
56
  }}</span>
53
- <div class="h-1.5 w-16 shrink-0 overflow-hidden rounded-full bg-slate-700/60">
57
+ <div class="h-1.5 w-16 shrink-0 overflow-hidden rounded-full bg-accented/60">
54
58
  <div
55
59
  class="h-full rounded-full"
56
60
  :class="ratingClass(item.rating)"
@@ -58,7 +62,7 @@ function label(item: FragmentAdherence[number]): string {
58
62
  />
59
63
  </div>
60
64
  <span
61
- class="shrink-0 text-[12px] font-medium text-slate-200"
65
+ class="shrink-0 text-[12px] font-medium text-default"
62
66
  :title="t('panels.stepDetail.adherence.ratingHint')"
63
67
  >
64
68
  {{ t('panels.stepDetail.adherence.outOfTen', { value: item.rating }) }}
@@ -67,17 +71,17 @@ function label(item: FragmentAdherence[number]): string {
67
71
  <MarkdownProse
68
72
  v-if="item.assessment"
69
73
  :text="item.assessment"
70
- class="mt-1 text-[12px] leading-relaxed text-slate-300"
74
+ class="mt-1 text-[12px] leading-relaxed text-toned"
71
75
  />
72
76
  <div v-if="item.relatedFindings.length" class="mt-1.5">
73
- <p class="text-[10px] font-semibold uppercase tracking-wide text-slate-500">
77
+ <p class="text-[10px] font-semibold uppercase tracking-wide text-dimmed">
74
78
  {{ t('panels.stepDetail.adherence.relatedFindings') }}
75
79
  </p>
76
80
  <ul class="mt-0.5 space-y-0.5">
77
81
  <li
78
82
  v-for="(finding, fi) in item.relatedFindings"
79
83
  :key="fi"
80
- class="flex items-start gap-1.5 text-[11px] text-slate-400"
84
+ class="flex items-start gap-1.5 text-[11px] text-muted"
81
85
  >
82
86
  <UIcon name="i-lucide-dot" class="mt-0.5 h-3.5 w-3.5 shrink-0" />
83
87
  <span>{{ finding }}</span>
@@ -10,6 +10,7 @@ import type { BadgeColor } from '~/utils/badge'
10
10
  import { subtaskIconClass } from '~/utils/pipelineRender'
11
11
  import StepModelActivity from '~/components/observability/StepModelActivity.vue'
12
12
  import StepContainerStatus from '~/components/panels/StepContainerStatus.vue'
13
+ import StepDelegatedStatus from '~/components/panels/StepDelegatedStatus.vue'
13
14
  import CopyButton from '~/components/common/CopyButton.vue'
14
15
  import MarkdownProse from '~/components/common/MarkdownProse.vue'
15
16
 
@@ -32,8 +33,28 @@ const props = defineProps<{
32
33
  }>()
33
34
 
34
35
  const models = useModelsStore()
36
+ const agents = useAgentsStore()
35
37
  const { t, d } = useI18n()
36
38
 
39
+ /**
40
+ * The registered executor behind a DELEGATED step, when this build still registers it.
41
+ *
42
+ * Read off the agent catalog rather than the step, because the step persists only the executor's
43
+ * ID: a card that named that id would show `acme:executor` where a person wants the label the
44
+ * deployment gave it. Undefined for every other step, and the card renders nothing.
45
+ */
46
+ const delegatedExecutor = computed(() => {
47
+ const meta = agents.get(props.step.agentKind)?.delegatedExecutor
48
+ if (!meta) return undefined
49
+ return {
50
+ // `||`, not `??`: an entry that carried an empty label would otherwise satisfy the fallback and
51
+ // leave the card with a blank name, which is the one thing naming the id exists to prevent.
52
+ label: meta.label || meta.id,
53
+ description: meta.description ?? '',
54
+ telemetry: meta.telemetry ?? ('not-reported' as const),
55
+ }
56
+ })
57
+
37
58
  const STATE_LABEL_KEYS: Record<AgentState, string> = {
38
59
  pending: 'panels.stepMeta.state.pending',
39
60
  working: 'panels.stepMeta.state.working',
@@ -41,10 +62,10 @@ const STATE_LABEL_KEYS: Record<AgentState, string> = {
41
62
  done: 'panels.stepMeta.state.done',
42
63
  }
43
64
  const STATE_COLOR: Record<AgentState, string> = {
44
- pending: '#64748b',
45
- working: '#6366f1',
46
- waiting_decision: '#f59e0b',
47
- done: '#22c55e',
65
+ pending: 'var(--ui-text-muted)',
66
+ working: 'var(--ui-primary)',
67
+ waiting_decision: 'var(--ui-warning)',
68
+ done: 'var(--ui-success)',
48
69
  }
49
70
 
50
71
  // The state badge: a step left mid-flight on a failed run keeps `state: 'working'`,
@@ -52,7 +73,7 @@ const STATE_COLOR: Record<AgentState, string> = {
52
73
  const stateMeta = computed(() => {
53
74
  const s = props.step
54
75
  if (props.runFailed && s.state === 'working')
55
- return { label: t('panels.stepMeta.state.failed'), color: '#ef4444' }
76
+ return { label: t('panels.stepMeta.state.failed'), color: 'var(--ui-error)' }
56
77
  return { label: t(STATE_LABEL_KEYS[s.state]), color: STATE_COLOR[s.state] }
57
78
  })
58
79
 
@@ -163,10 +184,10 @@ async function copyRunId() {
163
184
  <div>
164
185
  <dl class="grid grid-cols-2 gap-x-6 gap-y-3 text-[13px] sm:grid-cols-3">
165
186
  <div>
166
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
187
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
167
188
  {{ t('panels.stepMeta.stateLabel') }}
168
189
  </dt>
169
- <dd class="mt-0.5 flex items-center gap-1.5 text-slate-200">
190
+ <dd class="mt-0.5 flex items-center gap-1.5 text-default">
170
191
  <UIcon
171
192
  v-if="runFailed && step.state === 'working'"
172
193
  name="i-lucide-circle-x"
@@ -178,66 +199,66 @@ async function copyRunId() {
178
199
  </dd>
179
200
  </div>
180
201
  <div>
181
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
202
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
182
203
  {{ t('panels.stepMeta.duration') }}
183
204
  </dt>
184
- <dd class="mt-0.5 flex items-center gap-1.5 tabular-nums text-slate-200">
205
+ <dd class="mt-0.5 flex items-center gap-1.5 tabular-nums text-default">
185
206
  <UIcon
186
207
  v-if="isRunning"
187
208
  name="i-lucide-loader-circle"
188
- class="h-3 w-3 animate-spin text-indigo-400"
209
+ class="h-3 w-3 animate-spin text-primary"
189
210
  />
190
211
  <span v-if="durationLabel">{{ durationLabel }}</span>
191
- <span v-else class="text-slate-500">—</span>
192
- <span v-if="isRunning" class="text-[11px] text-slate-500">{{
212
+ <span v-else class="text-dimmed">—</span>
213
+ <span v-if="isRunning" class="text-[11px] text-dimmed">{{
193
214
  t('panels.stepMeta.elapsed')
194
215
  }}</span>
195
216
  </dd>
196
217
  </div>
197
218
  <div>
198
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
219
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
199
220
  {{ t('panels.stepMeta.step') }}
200
221
  </dt>
201
- <dd class="mt-0.5 text-slate-200">
222
+ <dd class="mt-0.5 text-default">
202
223
  {{ t('panels.stepMeta.stepOf', { number: stepNumber, total: totalSteps }) }}
203
224
  </dd>
204
225
  </div>
205
226
  <div>
206
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
227
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
207
228
  {{ t('panels.stepMeta.started') }}
208
229
  </dt>
209
- <dd class="mt-0.5 text-slate-300">{{ formatClock(step.startedAt) ?? '—' }}</dd>
230
+ <dd class="mt-0.5 text-toned">{{ formatClock(step.startedAt) ?? '—' }}</dd>
210
231
  </div>
211
232
  <div>
212
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
233
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
213
234
  {{ t('panels.stepMeta.finished') }}
214
235
  </dt>
215
- <dd class="mt-0.5 text-slate-300">{{ formatClock(step.finishedAt) ?? '—' }}</dd>
236
+ <dd class="mt-0.5 text-toned">{{ formatClock(step.finishedAt) ?? '—' }}</dd>
216
237
  </div>
217
238
  <div>
218
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
239
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
219
240
  {{ t('panels.stepMeta.model') }}
220
241
  </dt>
221
- <dd class="mt-0.5 truncate text-slate-300" :title="step.model">
242
+ <dd class="mt-0.5 truncate text-toned" :title="step.model">
222
243
  {{ modelLabel ?? t('panels.stepMeta.notRecorded') }}
223
244
  </dd>
224
245
  </div>
225
246
  <div v-if="promptVariant">
226
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
247
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
227
248
  {{ t('panels.stepMeta.promptVariant') }}
228
249
  </dt>
229
- <dd class="mt-0.5 truncate text-slate-300">{{ promptVariant.label }}</dd>
230
- <dd v-if="promptVariant.note" class="mt-0.5 text-[11px] text-amber-400/80">
250
+ <dd class="mt-0.5 truncate text-toned">{{ promptVariant.label }}</dd>
251
+ <dd v-if="promptVariant.note" class="mt-0.5 text-[11px] text-app-warning-400/80">
231
252
  {{ promptVariant.note }}
232
253
  </dd>
233
254
  </div>
234
255
  <!-- The run id this step belongs to, surfaced for debugging (copyable). -->
235
256
  <div class="col-span-2 sm:col-span-3">
236
- <dt class="text-[11px] uppercase tracking-wide text-slate-500">
257
+ <dt class="text-[11px] uppercase tracking-wide text-dimmed">
237
258
  {{ t('panels.stepMeta.run') }}
238
259
  </dt>
239
260
  <dd
240
- class="mt-0.5 cursor-pointer truncate font-mono text-[12px] text-slate-400 hover:text-slate-200"
261
+ class="mt-0.5 cursor-pointer truncate font-mono text-[12px] text-muted hover:text-default"
241
262
  :title="t('panels.stepMeta.clickToCopy', { id: step.runId ?? instanceId ?? '' })"
242
263
  @click="copyRunId"
243
264
  >
@@ -255,9 +276,15 @@ async function copyRunId() {
255
276
  class="mt-4"
256
277
  />
257
278
 
279
+ <!-- the EXTERNAL work a delegated step dispatched: which executor, what it is doing, the link
280
+ to its own logs, and what this platform therefore cannot measure. Its own component
281
+ because the two records share no field: a delegated step has no container to report a
282
+ phase, an id or an address for. -->
283
+ <StepDelegatedStatus :step="step" :executor="delegatedExecutor" class="mt-4" />
284
+
258
285
  <!-- live subtask breakdown -->
259
286
  <div v-if="step.subtasks && step.subtasks.total > 0" class="mt-4">
260
- <div class="text-[11px] uppercase tracking-wide text-slate-500">
287
+ <div class="text-[11px] uppercase tracking-wide text-dimmed">
261
288
  {{
262
289
  t('panels.stepMeta.subtasks', {
263
290
  completed: step.subtasks.completed,
@@ -265,9 +292,9 @@ async function copyRunId() {
265
292
  })
266
293
  }}
267
294
  </div>
268
- <div class="mt-1 h-1 overflow-hidden rounded-full bg-slate-700/60">
295
+ <div class="mt-1 h-1 overflow-hidden rounded-full bg-accented/60">
269
296
  <div
270
- class="h-full rounded-full bg-indigo-400 transition-all duration-500"
297
+ class="h-full rounded-full bg-primary transition-all duration-500"
271
298
  :style="{
272
299
  width: `${(step.subtasks.completed / step.subtasks.total) * 100}%`,
273
300
  }"
@@ -280,10 +307,10 @@ async function copyRunId() {
280
307
  class="flex items-start gap-1.5 text-[12px]"
281
308
  :class="
282
309
  item.status === 'completed'
283
- ? 'text-slate-500 line-through'
310
+ ? 'text-dimmed line-through'
284
311
  : item.status === 'in_progress'
285
- ? 'text-slate-100'
286
- : 'text-slate-400'
312
+ ? 'text-app-100'
313
+ : 'text-muted'
287
314
  "
288
315
  >
289
316
  <UIcon
@@ -309,7 +336,7 @@ async function copyRunId() {
309
336
  <!-- standards (prompt fragments) folded into this step -->
310
337
  <div v-if="step.selectedFragmentIds && step.selectedFragmentIds.length" class="mt-4">
311
338
  <div
312
- class="text-[11px] uppercase tracking-wide text-slate-500"
339
+ class="text-[11px] uppercase tracking-wide text-dimmed"
313
340
  :title="t('panels.stepMeta.standardsAppliedHint')"
314
341
  >
315
342
  {{ t('panels.stepMeta.standardsApplied') }}
@@ -329,28 +356,28 @@ async function copyRunId() {
329
356
 
330
357
  <!-- decision raised on this step -->
331
358
  <div v-if="step.decision" class="mt-4">
332
- <div class="text-[11px] uppercase tracking-wide text-slate-500">
359
+ <div class="text-[11px] uppercase tracking-wide text-dimmed">
333
360
  {{ t('panels.stepMeta.decision') }}
334
361
  </div>
335
- <p class="mt-0.5 text-[13px] text-slate-200">{{ step.decision.question }}</p>
362
+ <p class="mt-0.5 text-[13px] text-default">{{ step.decision.question }}</p>
336
363
  <p
337
364
  v-if="step.decision.chosen"
338
- class="mt-0.5 flex items-center gap-1 text-[12px] text-emerald-400"
365
+ class="mt-0.5 flex items-center gap-1 text-[12px] text-app-success-400"
339
366
  >
340
367
  <UIcon name="i-lucide-check" class="h-3 w-3 shrink-0" />
341
368
  {{ step.decision.chosen }}
342
369
  </p>
343
- <p v-else class="mt-0.5 text-[12px] text-amber-400">
370
+ <p v-else class="mt-0.5 text-[12px] text-app-warning-400">
344
371
  {{ t('panels.stepMeta.awaitingChoice') }}
345
372
  </p>
346
373
  </div>
347
374
 
348
375
  <!-- approval gate state -->
349
376
  <div v-if="step.approval" class="mt-4">
350
- <div class="text-[11px] uppercase tracking-wide text-slate-500">
377
+ <div class="text-[11px] uppercase tracking-wide text-dimmed">
351
378
  {{ t('panels.stepMeta.approvalGate') }}
352
379
  </div>
353
- <p class="mt-0.5 text-[13px] text-slate-200">
380
+ <p class="mt-0.5 text-[13px] text-default">
354
381
  {{ approvalStatusLabel }}
355
382
  </p>
356
383
  </div>
@@ -358,7 +385,7 @@ async function copyRunId() {
358
385
  <!-- companion verdict + full correction sequence -->
359
386
  <div v-if="companionVerdicts.length" class="mt-4">
360
387
  <div class="flex items-center justify-between">
361
- <span class="text-[11px] uppercase tracking-wide text-slate-500">
388
+ <span class="text-[11px] uppercase tracking-wide text-dimmed">
362
389
  {{ t('panels.stepMeta.companionReview') }}
363
390
  </span>
364
391
  <!-- The COLOUR is the verdict (`passed`) and the GLYPH is the arithmetic, which are no
@@ -380,19 +407,21 @@ async function copyRunId() {
380
407
  v-for="({ verdict: v, findings }, i) in verdictRounds"
381
408
  :key="i"
382
409
  data-testid="companion-verdict"
383
- class="relative rounded-lg border border-slate-800 bg-slate-900/60 px-3 py-2"
410
+ class="relative rounded-lg border border-default bg-default/60 px-3 py-2"
384
411
  >
385
412
  <CopyButton v-if="v.feedback" :text="v.feedback" class="absolute end-1 top-1" />
386
413
  <div class="flex items-center gap-2 text-[12px]">
387
414
  <span
388
415
  class="inline-flex h-4 shrink-0 items-center rounded px-1 font-mono text-[11px] tabular-nums"
389
416
  :class="
390
- v.passed ? 'bg-emerald-500/15 text-emerald-300' : 'bg-amber-500/15 text-amber-300'
417
+ v.passed
418
+ ? 'bg-app-success-500/15 text-app-success-300'
419
+ : 'bg-app-warning-500/15 text-app-warning-300'
391
420
  "
392
421
  >
393
422
  {{ i + 1 }}
394
423
  </span>
395
- <span :class="v.passed ? 'text-emerald-300' : 'text-amber-300'">
424
+ <span :class="v.passed ? 'text-app-success-300' : 'text-app-warning-300'">
396
425
  {{ pctOf(v.rating) }} {{ ratingMeetsBar(v) ? '≥' : '<' }} {{ pctOf(v.threshold) }}
397
426
  </span>
398
427
  </div>
@@ -400,7 +429,7 @@ async function copyRunId() {
400
429
  v-if="v.feedback"
401
430
  :text="v.feedback"
402
431
  data-testid="companion-verdict-summary"
403
- class="mt-1.5 pe-6 text-[12px] leading-relaxed text-slate-300"
432
+ class="mt-1.5 pe-6 text-[12px] leading-relaxed text-toned"
404
433
  />
405
434
  <ul v-if="findings.length" class="mt-2 space-y-1.5">
406
435
  <li
@@ -419,13 +448,13 @@ async function copyRunId() {
419
448
  </UBadge>
420
449
  <MarkdownProse
421
450
  :text="finding.body"
422
- class="min-w-0 text-[12px] leading-relaxed text-slate-300"
451
+ class="min-w-0 text-[12px] leading-relaxed text-toned"
423
452
  />
424
453
  </li>
425
454
  </ul>
426
455
  </li>
427
456
  </ol>
428
- <p v-if="companionVerdicts.length > 1" class="mt-1 text-[11px] text-slate-500">
457
+ <p v-if="companionVerdicts.length > 1" class="mt-1 text-[11px] text-dimmed">
429
458
  {{
430
459
  t(
431
460
  'panels.stepMeta.correctionIterations',