selftune 0.2.22 → 0.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +95 -15
  3. package/apps/local-dashboard/dist/assets/index-DgY2KGP-.css +1 -0
  4. package/apps/local-dashboard/dist/assets/index-Dmx7LPVX.js +15 -0
  5. package/apps/local-dashboard/dist/assets/vendor-react-C5oyHiV1.js +11 -0
  6. package/apps/local-dashboard/dist/assets/{vendor-table-BIiI3YhS.js → vendor-table-Bc_bbKd8.js} +1 -1
  7. package/apps/local-dashboard/dist/assets/vendor-ui-B3BPIYy7.js +1 -0
  8. package/apps/local-dashboard/dist/index.html +5 -5
  9. package/cli/selftune/adapters/codex/install.ts +310 -78
  10. package/cli/selftune/adapters/opencode/install.ts +3 -4
  11. package/cli/selftune/adapters/pi/hook.ts +273 -0
  12. package/cli/selftune/adapters/pi/install.ts +207 -0
  13. package/cli/selftune/alpha-upload/build-payloads.ts +3 -3
  14. package/cli/selftune/alpha-upload/stage-canonical.ts +17 -11
  15. package/cli/selftune/auto-update.ts +200 -8
  16. package/cli/selftune/canonical-export.ts +55 -25
  17. package/cli/selftune/command-surface.ts +397 -0
  18. package/cli/selftune/constants.ts +10 -1
  19. package/cli/selftune/contribute/contribute.ts +64 -13
  20. package/cli/selftune/contribution-config.ts +57 -3
  21. package/cli/selftune/contribution-preferences.ts +117 -0
  22. package/cli/selftune/contribution-signals.ts +8 -4
  23. package/cli/selftune/contribution-staging.ts +13 -2
  24. package/cli/selftune/contributions.ts +55 -121
  25. package/cli/selftune/creator-contributions.ts +29 -10
  26. package/cli/selftune/cron/setup.ts +7 -3
  27. package/cli/selftune/dashboard-contract.ts +87 -0
  28. package/cli/selftune/dashboard-server.ts +168 -17
  29. package/cli/selftune/dashboard.ts +350 -17
  30. package/cli/selftune/eval/baseline.ts +21 -5
  31. package/cli/selftune/eval/execution-eval.ts +170 -0
  32. package/cli/selftune/eval/family-overlap.ts +2 -2
  33. package/cli/selftune/eval/hooks-to-evals.ts +228 -82
  34. package/cli/selftune/eval/import-skillsbench.ts +2 -2
  35. package/cli/selftune/eval/invocation-classifier.ts +56 -0
  36. package/cli/selftune/eval/synthetic-evals.ts +5 -3
  37. package/cli/selftune/eval/unit-test-cli.ts +7 -4
  38. package/cli/selftune/evolution/apply-proposal.ts +295 -0
  39. package/cli/selftune/evolution/engines/judge-engine.ts +96 -0
  40. package/cli/selftune/evolution/engines/replay-engine.ts +180 -0
  41. package/cli/selftune/evolution/evidence.ts +2 -6
  42. package/cli/selftune/evolution/evolve-body.ts +152 -38
  43. package/cli/selftune/evolution/evolve.ts +244 -52
  44. package/cli/selftune/evolution/rollback.ts +0 -1
  45. package/cli/selftune/evolution/validate-body.ts +111 -49
  46. package/cli/selftune/evolution/validate-host-replay.ts +510 -60
  47. package/cli/selftune/evolution/validate-proposal.ts +11 -150
  48. package/cli/selftune/evolution/validate-routing.ts +51 -108
  49. package/cli/selftune/evolution/validation-contract.ts +91 -0
  50. package/cli/selftune/grading/auto-grade.ts +11 -7
  51. package/cli/selftune/grading/grade-session.ts +10 -16
  52. package/cli/selftune/hooks/skill-eval.ts +2 -1
  53. package/cli/selftune/hooks-shared/types.ts +1 -0
  54. package/cli/selftune/index.ts +58 -15
  55. package/cli/selftune/ingestors/claude-replay.ts +15 -10
  56. package/cli/selftune/ingestors/codex-wrapper.ts +3 -3
  57. package/cli/selftune/ingestors/opencode-ingest.ts +2 -2
  58. package/cli/selftune/ingestors/pi-ingest.ts +727 -0
  59. package/cli/selftune/init.ts +38 -4
  60. package/cli/selftune/localdb/direct-write.ts +120 -1
  61. package/cli/selftune/localdb/materialize.ts +6 -7
  62. package/cli/selftune/localdb/queries/cron.ts +34 -0
  63. package/cli/selftune/localdb/queries/dashboard.ts +834 -0
  64. package/cli/selftune/localdb/queries/evolution.ts +158 -0
  65. package/cli/selftune/localdb/queries/execution.ts +133 -0
  66. package/cli/selftune/localdb/queries/json.ts +18 -0
  67. package/cli/selftune/localdb/queries/monitoring.ts +263 -0
  68. package/cli/selftune/localdb/queries/raw.ts +95 -0
  69. package/cli/selftune/localdb/queries/staging.ts +270 -0
  70. package/cli/selftune/localdb/queries/trust.ts +392 -0
  71. package/cli/selftune/localdb/queries.ts +60 -2162
  72. package/cli/selftune/localdb/schema.ts +59 -0
  73. package/cli/selftune/monitoring/watch.ts +96 -29
  74. package/cli/selftune/normalization.ts +3 -0
  75. package/cli/selftune/observability.ts +12 -3
  76. package/cli/selftune/orchestrate/cli.ts +161 -0
  77. package/cli/selftune/orchestrate/execute.ts +295 -0
  78. package/cli/selftune/orchestrate/finalize.ts +157 -0
  79. package/cli/selftune/orchestrate/locks.ts +40 -0
  80. package/cli/selftune/orchestrate/plan.ts +131 -0
  81. package/cli/selftune/orchestrate/post-run.ts +59 -0
  82. package/cli/selftune/orchestrate/prepare.ts +334 -0
  83. package/cli/selftune/orchestrate/report.ts +182 -0
  84. package/cli/selftune/orchestrate/runtime.ts +120 -0
  85. package/cli/selftune/orchestrate/signals.ts +48 -0
  86. package/cli/selftune/orchestrate.ts +162 -1142
  87. package/cli/selftune/registry/client.ts +74 -0
  88. package/cli/selftune/registry/history.ts +54 -0
  89. package/cli/selftune/registry/index.ts +90 -0
  90. package/cli/selftune/registry/install.ts +141 -0
  91. package/cli/selftune/registry/list.ts +44 -0
  92. package/cli/selftune/registry/push.ts +171 -0
  93. package/cli/selftune/registry/rollback.ts +49 -0
  94. package/cli/selftune/registry/status.ts +62 -0
  95. package/cli/selftune/registry/sync.ts +125 -0
  96. package/cli/selftune/repair/skill-usage.ts +9 -3
  97. package/cli/selftune/routes/overview.ts +5 -2
  98. package/cli/selftune/routes/skill-report.ts +15 -2
  99. package/cli/selftune/schedule.ts +5 -5
  100. package/cli/selftune/status.ts +70 -2
  101. package/cli/selftune/sync.ts +127 -23
  102. package/cli/selftune/testing-readiness.ts +597 -0
  103. package/cli/selftune/types.ts +46 -5
  104. package/cli/selftune/uninstall.ts +2 -1
  105. package/cli/selftune/utils/canonical-log.ts +1 -9
  106. package/cli/selftune/utils/cli-error.ts +9 -0
  107. package/cli/selftune/utils/jsonl.ts +1 -30
  108. package/cli/selftune/utils/llm-call.ts +126 -6
  109. package/cli/selftune/utils/skill-discovery.ts +24 -0
  110. package/cli/selftune/workflows/proposals.ts +184 -0
  111. package/cli/selftune/workflows/skill-scaffold.ts +241 -0
  112. package/cli/selftune/workflows/workflows.ts +100 -26
  113. package/node_modules/@selftune/telemetry-contract/fixtures/complete-push.ts +1 -1
  114. package/node_modules/@selftune/telemetry-contract/fixtures/evidence-only-push.ts +2 -2
  115. package/node_modules/@selftune/telemetry-contract/fixtures/golden.test.ts +0 -1
  116. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-no-sessions.ts +1 -1
  117. package/node_modules/@selftune/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +2 -2
  118. package/node_modules/@selftune/telemetry-contract/package.json +1 -1
  119. package/node_modules/@selftune/telemetry-contract/src/index.ts +1 -0
  120. package/node_modules/@selftune/telemetry-contract/src/schemas.ts +63 -5
  121. package/node_modules/@selftune/telemetry-contract/src/types.ts +97 -7
  122. package/node_modules/@selftune/telemetry-contract/tests/compatibility.test.ts +0 -1
  123. package/package.json +25 -9
  124. package/packages/dashboard-core/AGENTS.md +18 -0
  125. package/packages/dashboard-core/README.md +30 -0
  126. package/packages/dashboard-core/index.ts +3 -0
  127. package/packages/dashboard-core/package.json +39 -0
  128. package/packages/dashboard-core/src/chrome/DashboardChrome.tsx +74 -0
  129. package/packages/dashboard-core/src/chrome/DashboardHeader.tsx +200 -0
  130. package/packages/dashboard-core/src/chrome/DashboardSidebar.tsx +219 -0
  131. package/packages/dashboard-core/src/chrome/RuntimeBadge.tsx +46 -0
  132. package/packages/dashboard-core/src/chrome/index.ts +14 -0
  133. package/packages/dashboard-core/src/chrome/types.ts +81 -0
  134. package/packages/dashboard-core/src/chrome/utils.ts +23 -0
  135. package/packages/dashboard-core/src/gates/FeatureGate.tsx +11 -0
  136. package/packages/dashboard-core/src/gates/LockedRoute.tsx +29 -0
  137. package/packages/dashboard-core/src/gates/UpgradeCard.tsx +89 -0
  138. package/packages/dashboard-core/src/gates/index.ts +3 -0
  139. package/packages/dashboard-core/src/host/DashboardHostProvider.tsx +62 -0
  140. package/packages/dashboard-core/src/host/adapter.ts +47 -0
  141. package/packages/dashboard-core/src/host/capabilities.ts +55 -0
  142. package/packages/dashboard-core/src/host/index.ts +3 -0
  143. package/packages/dashboard-core/src/models/analytics.ts +39 -0
  144. package/packages/dashboard-core/src/models/index.ts +4 -0
  145. package/packages/dashboard-core/src/models/overview.ts +98 -0
  146. package/packages/dashboard-core/src/models/runtime.ts +7 -0
  147. package/packages/dashboard-core/src/models/skills.ts +34 -0
  148. package/packages/dashboard-core/src/routes/index.ts +2 -0
  149. package/packages/dashboard-core/src/routes/manifest.test.ts +70 -0
  150. package/packages/dashboard-core/src/routes/manifest.ts +451 -0
  151. package/packages/dashboard-core/src/routes/types.ts +39 -0
  152. package/packages/dashboard-core/src/screens/analytics/AnalyticsScreen.tsx +278 -0
  153. package/packages/dashboard-core/src/screens/analytics/index.ts +1 -0
  154. package/packages/dashboard-core/src/screens/index.ts +37 -0
  155. package/packages/dashboard-core/src/screens/overview/OverviewComparisonSurface.test.ts +101 -0
  156. package/packages/dashboard-core/src/screens/overview/OverviewComparisonSurface.tsx +393 -0
  157. package/packages/dashboard-core/src/screens/overview/OverviewCompositionSurface.test.tsx +113 -0
  158. package/packages/dashboard-core/src/screens/overview/OverviewCompositionSurface.tsx +72 -0
  159. package/packages/dashboard-core/src/screens/overview/OverviewCoreSurface.tsx +71 -0
  160. package/packages/dashboard-core/src/screens/overview/OverviewOnboardingBanner.tsx +90 -0
  161. package/packages/dashboard-core/src/screens/overview/OverviewRunSummary.tsx +40 -0
  162. package/packages/dashboard-core/src/screens/overview/index.ts +16 -0
  163. package/packages/dashboard-core/src/screens/overview/types.ts +13 -0
  164. package/packages/dashboard-core/src/screens/skill-report/SkillReportDailyBreakdownSection.tsx +99 -0
  165. package/packages/dashboard-core/src/screens/skill-report/SkillReportDataQualityTabContent.tsx +35 -0
  166. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceRail.tsx +71 -0
  167. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceSection.tsx +63 -0
  168. package/packages/dashboard-core/src/screens/skill-report/SkillReportEvidenceTabContent.tsx +25 -0
  169. package/packages/dashboard-core/src/screens/skill-report/SkillReportInvocationsSection.tsx +24 -0
  170. package/packages/dashboard-core/src/screens/skill-report/SkillReportMissedQueriesSection.tsx +79 -0
  171. package/packages/dashboard-core/src/screens/skill-report/SkillReportScaffold.tsx +150 -0
  172. package/packages/dashboard-core/src/screens/skill-report/SkillReportSections.test.tsx +224 -0
  173. package/packages/dashboard-core/src/screens/skill-report/SkillReportTabs.test.tsx +76 -0
  174. package/packages/dashboard-core/src/screens/skill-report/SkillReportTabs.tsx +88 -0
  175. package/packages/dashboard-core/src/screens/skill-report/SkillReportTrendSection.tsx +33 -0
  176. package/packages/dashboard-core/src/screens/skill-report/SkillReportTrustBadge.tsx +67 -0
  177. package/packages/dashboard-core/src/screens/skill-report/index.ts +45 -0
  178. package/packages/dashboard-core/src/screens/skills/SkillsLibraryScreen.tsx +162 -0
  179. package/packages/dashboard-core/src/screens/skills/index.ts +6 -0
  180. package/packages/telemetry-contract/fixtures/complete-push.ts +1 -1
  181. package/packages/telemetry-contract/fixtures/evidence-only-push.ts +2 -2
  182. package/packages/telemetry-contract/fixtures/golden.test.ts +0 -1
  183. package/packages/telemetry-contract/fixtures/partial-push-no-sessions.ts +1 -1
  184. package/packages/telemetry-contract/fixtures/partial-push-unresolved-parents.ts +2 -2
  185. package/packages/telemetry-contract/package.json +1 -1
  186. package/packages/telemetry-contract/src/index.ts +1 -0
  187. package/packages/telemetry-contract/src/schemas.ts +63 -5
  188. package/packages/telemetry-contract/src/types.ts +97 -7
  189. package/packages/telemetry-contract/tests/compatibility.test.ts +0 -1
  190. package/packages/ui/AGENTS.md +16 -0
  191. package/packages/ui/README.md +1 -1
  192. package/packages/ui/package.json +1 -1
  193. package/packages/ui/src/components/ActivityTimeline.tsx +152 -168
  194. package/packages/ui/src/components/AnalyticsCharts.tsx +344 -0
  195. package/packages/ui/src/components/EvidenceViewer.tsx +229 -464
  196. package/packages/ui/src/components/EvolutionTimeline.tsx +34 -87
  197. package/packages/ui/src/components/InfoTip.tsx +1 -2
  198. package/packages/ui/src/components/InvocationsPanel.tsx +413 -0
  199. package/packages/ui/src/components/JobHistoryTimeline.tsx +156 -0
  200. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +18 -36
  201. package/packages/ui/src/components/OverviewPanels.tsx +693 -0
  202. package/packages/ui/src/components/PipelineStatusBar.tsx +65 -0
  203. package/packages/ui/src/components/SkillReportGuide.tsx +215 -0
  204. package/packages/ui/src/components/SkillReportPanels.tsx +919 -0
  205. package/packages/ui/src/components/SkillsLibrary.tsx +437 -0
  206. package/packages/ui/src/components/index.ts +56 -1
  207. package/packages/ui/src/components/section-cards.tsx +18 -35
  208. package/packages/ui/src/components/skill-health-grid.tsx +47 -37
  209. package/packages/ui/src/lib/constants.tsx +0 -1
  210. package/packages/ui/src/primitives/card.tsx +1 -1
  211. package/packages/ui/src/primitives/checkbox.tsx +1 -1
  212. package/packages/ui/src/primitives/dropdown-menu.tsx +2 -2
  213. package/packages/ui/src/primitives/select.tsx +2 -2
  214. package/packages/ui/src/primitives/tabs.tsx +7 -6
  215. package/packages/ui/src/types.ts +182 -4
  216. package/skill/SKILL.md +130 -318
  217. package/skill/agents/diagnosis-analyst.md +3 -3
  218. package/skill/agents/evolution-reviewer.md +3 -3
  219. package/skill/agents/integration-guide.md +3 -3
  220. package/skill/agents/pattern-analyst.md +2 -2
  221. package/skill/references/cli-quick-reference.md +89 -0
  222. package/skill/references/creator-playbook.md +131 -0
  223. package/skill/references/examples.md +48 -0
  224. package/skill/references/troubleshooting.md +47 -0
  225. package/skill/references/version-history.md +1 -1
  226. package/skill/selftune.contribute.json +11 -0
  227. package/skill/{Workflows → workflows}/Baseline.md +20 -1
  228. package/skill/{Workflows → workflows}/Contribute.md +23 -10
  229. package/skill/{Workflows → workflows}/Contributions.md +13 -5
  230. package/skill/workflows/CreateTestDeploy.md +170 -0
  231. package/skill/{Workflows → workflows}/CreatorContributions.md +18 -6
  232. package/skill/{Workflows → workflows}/Cron.md +1 -1
  233. package/skill/{Workflows → workflows}/Dashboard.md +20 -0
  234. package/skill/{Workflows → workflows}/Doctor.md +1 -1
  235. package/skill/{Workflows → workflows}/Evals.md +67 -2
  236. package/skill/{Workflows → workflows}/Evolve.md +119 -30
  237. package/skill/{Workflows → workflows}/EvolveBody.md +41 -1
  238. package/skill/{Workflows → workflows}/Grade.md +1 -1
  239. package/skill/{Workflows → workflows}/Ingest.md +60 -2
  240. package/skill/{Workflows → workflows}/Initialize.md +16 -9
  241. package/skill/{Workflows → workflows}/Orchestrate.md +13 -3
  242. package/skill/{Workflows → workflows}/PlatformHooks.md +19 -3
  243. package/skill/workflows/Registry.md +99 -0
  244. package/skill/{Workflows → workflows}/Schedule.md +3 -3
  245. package/skill/workflows/SignalsDashboard.md +87 -0
  246. package/skill/{Workflows → workflows}/Sync.md +3 -1
  247. package/skill/{Workflows → workflows}/UnitTest.md +19 -0
  248. package/skill/{Workflows → workflows}/Watch.md +42 -2
  249. package/skill/{Workflows → workflows}/Workflows.md +39 -2
  250. package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +0 -60
  251. package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +0 -1
  252. package/apps/local-dashboard/dist/assets/vendor-react-CKkiCskZ.js +0 -11
  253. package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +0 -12
  254. package/cli/selftune/utils/html.ts +0 -27
  255. package/packages/ui/src/components/RecentActivityFeed.tsx +0 -117
  256. /package/skill/{Workflows → workflows}/AlphaUpload.md +0 -0
  257. /package/skill/{Workflows → workflows}/AutoActivation.md +0 -0
  258. /package/skill/{Workflows → workflows}/Badge.md +0 -0
  259. /package/skill/{Workflows → workflows}/Composability.md +0 -0
  260. /package/skill/{Workflows → workflows}/EvolutionMemory.md +0 -0
  261. /package/skill/{Workflows → workflows}/ExportCanonical.md +0 -0
  262. /package/skill/{Workflows → workflows}/Hook.md +0 -0
  263. /package/skill/{Workflows → workflows}/ImportSkillsBench.md +0 -0
  264. /package/skill/{Workflows → workflows}/Quickstart.md +0 -0
  265. /package/skill/{Workflows → workflows}/Recover.md +0 -0
  266. /package/skill/{Workflows → workflows}/RepairSkillUsage.md +0 -0
  267. /package/skill/{Workflows → workflows}/Replay.md +0 -0
  268. /package/skill/{Workflows → workflows}/Rollback.md +0 -0
  269. /package/skill/{Workflows → workflows}/Telemetry.md +0 -0
  270. /package/skill/{Workflows → workflows}/Uninstall.md +0 -0
@@ -0,0 +1,693 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import { useMemo, useState } from "react";
5
+ import { CheckCircleIcon, ChevronDownIcon } from "lucide-react";
6
+
7
+ import { Badge } from "../primitives/badge";
8
+ import {
9
+ Card,
10
+ CardAction,
11
+ CardContent,
12
+ CardDescription,
13
+ CardHeader,
14
+ CardTitle,
15
+ } from "../primitives/card";
16
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../primitives/tabs";
17
+ import { timeAgo } from "../lib/format";
18
+ import type {
19
+ AttentionItem,
20
+ AttentionSeverity,
21
+ AutonomousDecision,
22
+ AutonomyStatus,
23
+ AutonomyStatusLevel,
24
+ DecisionKind,
25
+ EvolutionEntry,
26
+ TrustBucket,
27
+ TrustWatchlistEntry,
28
+ } from "../types";
29
+
30
+ // ---------------------------------------------------------------------------
31
+ // Shared constants
32
+ // ---------------------------------------------------------------------------
33
+
34
+ const STATUS_DOT: Record<AutonomyStatusLevel, { color: string; glow: string }> = {
35
+ healthy: {
36
+ color: "bg-emerald-400",
37
+ glow: "shadow-[0_0_12px_rgba(52,211,153,0.6)]",
38
+ },
39
+ watching: {
40
+ color: "bg-cyan-400",
41
+ glow: "shadow-[0_0_12px_rgba(79,242,255,0.6)]",
42
+ },
43
+ needs_review: {
44
+ color: "bg-amber-400",
45
+ glow: "shadow-[0_0_12px_rgba(251,191,36,0.6)]",
46
+ },
47
+ blocked: {
48
+ color: "bg-red-400",
49
+ glow: "shadow-[0_0_12px_rgba(248,113,113,0.6)]",
50
+ },
51
+ };
52
+
53
+ const STATUS_LABELS: Record<AutonomyStatusLevel, string> = {
54
+ healthy: "Healthy",
55
+ watching: "Watching",
56
+ needs_review: "Needs Review",
57
+ blocked: "Blocked",
58
+ };
59
+
60
+ const SEVERITY: Record<AttentionSeverity, { dot: string; text: string; bg: string }> = {
61
+ critical: {
62
+ dot: "bg-red-400",
63
+ text: "text-red-400",
64
+ bg: "bg-red-500/10",
65
+ },
66
+ warning: {
67
+ dot: "bg-amber-400",
68
+ text: "text-amber-400",
69
+ bg: "bg-amber-500/10",
70
+ },
71
+ info: {
72
+ dot: "bg-cyan-400",
73
+ text: "text-primary",
74
+ bg: "bg-cyan-400/10",
75
+ },
76
+ };
77
+
78
+ const DECISION_MARKERS: Record<DecisionKind, string> = {
79
+ proposal_created: "bg-cyan-400",
80
+ proposal_rejected: "bg-red-400",
81
+ validation_failed: "bg-amber-400",
82
+ proposal_deployed: "bg-emerald-400",
83
+ rollback_triggered: "bg-red-400",
84
+ regression_found: "bg-amber-400",
85
+ };
86
+
87
+ const BUCKET_CFG: Record<TrustBucket, { label: string; accent: string; dot: string }> = {
88
+ at_risk: { label: "At Risk", accent: "text-red-400", dot: "bg-red-400" },
89
+ improving: { label: "Improving", accent: "text-primary", dot: "bg-cyan-400" },
90
+ uncertain: { label: "Uncertain", accent: "text-amber-400", dot: "bg-amber-400" },
91
+ stable: {
92
+ label: "Stable",
93
+ accent: "text-muted-foreground",
94
+ dot: "bg-muted-foreground/60",
95
+ },
96
+ };
97
+
98
+ // Ambient bar heights for hero background
99
+ const BARS = [
100
+ { id: "autonomy-bar-1", height: 35 },
101
+ { id: "autonomy-bar-2", height: 55 },
102
+ { id: "autonomy-bar-3", height: 40 },
103
+ { id: "autonomy-bar-4", height: 70 },
104
+ { id: "autonomy-bar-5", height: 45 },
105
+ { id: "autonomy-bar-6", height: 80 },
106
+ { id: "autonomy-bar-7", height: 30 },
107
+ { id: "autonomy-bar-8", height: 65 },
108
+ { id: "autonomy-bar-9", height: 50 },
109
+ { id: "autonomy-bar-10", height: 75 },
110
+ { id: "autonomy-bar-11", height: 38 },
111
+ { id: "autonomy-bar-12", height: 60 },
112
+ { id: "autonomy-bar-13", height: 42 },
113
+ { id: "autonomy-bar-14", height: 72 },
114
+ ] as const;
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // AutonomyHeroCard
118
+ // ---------------------------------------------------------------------------
119
+
120
+ export interface AutonomyHeroCardProps {
121
+ status: AutonomyStatus;
122
+ lastRun: string | null;
123
+ actions?: ReactNode;
124
+ }
125
+
126
+ export function AutonomyHeroCard({ status, lastRun, actions }: AutonomyHeroCardProps) {
127
+ const dot = STATUS_DOT[status.level];
128
+ const primaryStat =
129
+ status.attention_required > 0
130
+ ? { value: status.attention_required, label: "Attention Required" }
131
+ : { value: status.skills_observed, label: "Skills Observed" };
132
+
133
+ return (
134
+ <Card className="relative min-h-[332px] border-none bg-gradient-to-br from-muted via-muted to-primary/5 shadow-none py-0 ring-0">
135
+ {/* Ambient bars */}
136
+ <div className="absolute inset-0 flex items-end justify-around px-8 pb-24 pt-20 opacity-[0.08] pointer-events-none">
137
+ {BARS.map((bar) => (
138
+ <div
139
+ key={bar.id}
140
+ className="flex-1 rounded-t-sm min-w-[12px]"
141
+ style={{
142
+ height: `${bar.height}%`,
143
+ backgroundColor: `rgba(79, 242, 255, ${0.15 + (bar.height / 100) * 0.3})`,
144
+ }}
145
+ />
146
+ ))}
147
+ </div>
148
+
149
+ {/* Top: status + primary stat */}
150
+ <CardHeader className="relative z-10 px-8 pt-8 pb-0">
151
+ <div className="flex items-start gap-3">
152
+ <span
153
+ className={`mt-2 size-3.5 shrink-0 rounded-full animate-pulse ${dot.color} ${dot.glow}`}
154
+ />
155
+ <div>
156
+ <p className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
157
+ Autonomy Status
158
+ </p>
159
+ <CardTitle className="text-2xl font-extrabold tracking-tight text-foreground">
160
+ {STATUS_LABELS[status.level]}
161
+ </CardTitle>
162
+ <CardDescription className="mt-1.5 max-w-xl text-[13px] leading-relaxed text-muted-foreground">
163
+ {status.summary}
164
+ </CardDescription>
165
+ </div>
166
+ </div>
167
+ <CardAction>
168
+ <div className="text-right shrink-0">
169
+ <p
170
+ className="text-5xl font-extrabold text-primary leading-none"
171
+ style={{ filter: "drop-shadow(0 0 8px rgba(79,242,255,0.3))" }}
172
+ >
173
+ {primaryStat.value}
174
+ </p>
175
+ <p className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground mt-1.5">
176
+ {primaryStat.label}
177
+ </p>
178
+ </div>
179
+ </CardAction>
180
+ </CardHeader>
181
+
182
+ {/* Spacer */}
183
+ <div className="flex-1 min-h-6" />
184
+
185
+ {/* Bottom: compact stat chips */}
186
+ <CardContent className="relative z-10 flex flex-col gap-5 px-8 pb-8 pt-0">
187
+ <div className="flex flex-wrap items-center gap-2.5 text-xs">
188
+ <div className="rounded-full border border-border/40 bg-background/60 px-3 py-1.5 backdrop-blur-sm shadow-[inset_0_1px_0_rgba(255,255,255,0.02)]">
189
+ <span className="text-[10px] uppercase tracking-[0.18em] text-muted-foreground/70">
190
+ Last Run
191
+ </span>
192
+ <span className="ml-2 font-medium text-foreground">
193
+ {lastRun ? timeAgo(lastRun) : "Never"}
194
+ </span>
195
+ </div>
196
+ <div className="rounded-full border border-border/40 bg-background/60 px-3 py-1.5 backdrop-blur-sm shadow-[inset_0_1px_0_rgba(255,255,255,0.02)]">
197
+ <span className="text-[10px] uppercase tracking-[0.18em] text-muted-foreground/70">
198
+ Skills
199
+ </span>
200
+ <span className="ml-2 font-medium text-foreground">{status.skills_observed}</span>
201
+ </div>
202
+ <div className="rounded-full border border-primary/20 bg-primary/10 px-3 py-1.5 backdrop-blur-sm">
203
+ <span className="text-[10px] uppercase tracking-[0.18em] text-primary/80">Pending</span>
204
+ <span className="ml-2 font-semibold text-primary">{status.pending_reviews}</span>
205
+ </div>
206
+ </div>
207
+
208
+ {actions ??
209
+ (status.attention_required > 0 ? (
210
+ <a
211
+ href="#supervision-feed"
212
+ className="inline-flex w-fit items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/80"
213
+ >
214
+ Review Attention Queue
215
+ </a>
216
+ ) : (
217
+ <span className="text-sm text-muted-foreground/70">No action needed</span>
218
+ ))}
219
+ </CardContent>
220
+ </Card>
221
+ );
222
+ }
223
+
224
+ // ---------------------------------------------------------------------------
225
+ // TrustWatchlistRail
226
+ // ---------------------------------------------------------------------------
227
+
228
+ export interface TrustWatchlistRailProps {
229
+ entries: TrustWatchlistEntry[];
230
+ /** Optional render prop for skill name links */
231
+ renderSkillLink?: (skillName: string) => ReactNode;
232
+ footer?: ReactNode;
233
+ }
234
+
235
+ export function TrustWatchlistRail({ entries, renderSkillLink, footer }: TrustWatchlistRailProps) {
236
+ const buckets = useMemo(() => {
237
+ const order: TrustBucket[] = ["at_risk", "improving", "uncertain", "stable"];
238
+ const grouped: Record<TrustBucket, TrustWatchlistEntry[]> = {
239
+ at_risk: [],
240
+ improving: [],
241
+ uncertain: [],
242
+ stable: [],
243
+ };
244
+ for (const e of entries) grouped[e.bucket].push(e);
245
+ return order
246
+ .filter((b) => grouped[b].length > 0)
247
+ .map((b) => ({ bucket: b, items: grouped[b] }));
248
+ }, [entries]);
249
+
250
+ return (
251
+ <Card
252
+ data-parity-root="overview-trust-watchlist"
253
+ className="border-none bg-muted shadow-none py-0 max-h-[360px] ring-0"
254
+ >
255
+ <CardHeader className="px-5 pt-5 pb-0">
256
+ <div>
257
+ <p className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
258
+ Trust Watchlist
259
+ </p>
260
+ <CardDescription className="mt-1 text-[11px] text-muted-foreground/70">
261
+ Highest-risk skills worth checking next.
262
+ </CardDescription>
263
+ </div>
264
+ <CardAction>
265
+ <span className="text-[10px] text-muted-foreground/70 shrink-0">
266
+ {entries.length} skills
267
+ </span>
268
+ </CardAction>
269
+ </CardHeader>
270
+
271
+ {buckets.length === 0 ? (
272
+ <CardContent className="flex flex-1 items-center justify-center px-5 py-4">
273
+ <p className="text-xs text-muted-foreground/70">No skills tracked yet.</p>
274
+ </CardContent>
275
+ ) : (
276
+ <CardContent className="space-y-3 overflow-y-auto min-h-0 flex-1 px-5 py-4">
277
+ {buckets.map(({ bucket, items }) => (
278
+ <RailBucket
279
+ key={bucket}
280
+ bucket={bucket}
281
+ items={items}
282
+ renderSkillLink={renderSkillLink}
283
+ />
284
+ ))}
285
+ </CardContent>
286
+ )}
287
+
288
+ {footer ? (
289
+ <CardContent className="mt-auto px-5 pb-5 pt-1 shrink-0">{footer}</CardContent>
290
+ ) : null}
291
+ </Card>
292
+ );
293
+ }
294
+
295
+ function RailBucket({
296
+ bucket,
297
+ items,
298
+ renderSkillLink,
299
+ }: {
300
+ bucket: TrustBucket;
301
+ items: TrustWatchlistEntry[];
302
+ renderSkillLink?: (skillName: string) => ReactNode;
303
+ }) {
304
+ const cfg = BUCKET_CFG[bucket];
305
+ const [open, setOpen] = useState(false);
306
+ const MAX = 5;
307
+ const [showAll, setShowAll] = useState(false);
308
+ const visible = open ? (showAll ? items : items.slice(0, MAX)) : [];
309
+
310
+ return (
311
+ <div className="rounded-xl bg-background/40 px-3 py-2.5">
312
+ <button
313
+ type="button"
314
+ onClick={() => setOpen(!open)}
315
+ className="flex w-full items-center gap-1.5 text-left"
316
+ >
317
+ <span className={`size-1.5 shrink-0 rounded-full ${cfg.dot}`} />
318
+ <ChevronDownIcon
319
+ className={`size-3 text-muted-foreground transition-transform ${open ? "" : "-rotate-90"}`}
320
+ />
321
+ <span className={`text-xs font-medium ${cfg.accent}`}>{cfg.label}</span>
322
+ <span className="text-[10px] text-muted-foreground/70">({items.length})</span>
323
+ </button>
324
+ {open && (
325
+ <div className="mt-2 space-y-1">
326
+ {visible.map((e) => (
327
+ <div
328
+ key={e.skill_name}
329
+ className="rounded-lg px-2 py-1.5 transition-colors hover:bg-background/55"
330
+ >
331
+ <div className="flex items-baseline justify-between gap-2">
332
+ {renderSkillLink ? (
333
+ renderSkillLink(e.skill_name)
334
+ ) : (
335
+ <span className="text-[11px] font-medium text-foreground truncate">
336
+ {e.skill_name}
337
+ </span>
338
+ )}
339
+ {e.pass_rate != null && (
340
+ <span className="text-[10px] text-muted-foreground shrink-0">
341
+ {Math.round(e.pass_rate * 100)}%
342
+ </span>
343
+ )}
344
+ </div>
345
+ <p className="mt-0.5 line-clamp-1 text-[10px] text-muted-foreground/70">{e.reason}</p>
346
+ </div>
347
+ ))}
348
+ {items.length > MAX && !showAll && (
349
+ <button
350
+ type="button"
351
+ onClick={() => setShowAll(true)}
352
+ className="pl-2 text-[10px] text-primary hover:underline"
353
+ >
354
+ +{items.length - MAX} more
355
+ </button>
356
+ )}
357
+ </div>
358
+ )}
359
+ </div>
360
+ );
361
+ }
362
+
363
+ // ---------------------------------------------------------------------------
364
+ // SupervisionFeed
365
+ // ---------------------------------------------------------------------------
366
+
367
+ export interface SupervisionFeedProps {
368
+ attention: AttentionItem[];
369
+ decisions: AutonomousDecision[];
370
+ /** Optional render prop for skill name links */
371
+ renderSkillLink?: (skillName: string) => ReactNode;
372
+ }
373
+
374
+ export function SupervisionFeed({ attention, decisions, renderSkillLink }: SupervisionFeedProps) {
375
+ return (
376
+ <Card
377
+ id="supervision-feed"
378
+ data-parity-root="overview-supervision-feed"
379
+ className="relative overflow-hidden border-none bg-muted shadow-none py-0 scroll-mt-6 ring-0"
380
+ >
381
+ <div className="pointer-events-none absolute inset-x-0 top-0 h-20 bg-gradient-to-b from-primary/5 via-transparent to-transparent" />
382
+ <Tabs defaultValue="attention" className="gap-0">
383
+ <CardHeader className="relative px-5 pt-4 pb-0">
384
+ <div>
385
+ <p className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
386
+ Supervision Feed
387
+ </p>
388
+ <CardDescription className="mt-1 text-[13px] text-muted-foreground/70">
389
+ What needs review and what selftune just decided.
390
+ </CardDescription>
391
+ </div>
392
+ <TabsList variant="line" className="mt-3">
393
+ <TabsTrigger value="attention" className="text-xs uppercase tracking-[0.15em]">
394
+ Attention Required
395
+ {attention.length > 0 && (
396
+ <Badge variant="secondary" className="ml-1.5 text-[10px] py-0 px-1.5">
397
+ {attention.length}
398
+ </Badge>
399
+ )}
400
+ </TabsTrigger>
401
+ <TabsTrigger value="decisions" className="text-xs uppercase tracking-[0.15em]">
402
+ Recent Decisions
403
+ {decisions.length > 0 && (
404
+ <span className="ml-1.5 text-[10px] text-muted-foreground">{decisions.length}</span>
405
+ )}
406
+ </TabsTrigger>
407
+ </TabsList>
408
+ </CardHeader>
409
+
410
+ <CardContent className="max-h-[440px] overflow-y-auto px-5 py-5">
411
+ <TabsContent value="attention">
412
+ <AttentionContent attention={attention} renderSkillLink={renderSkillLink} />
413
+ </TabsContent>
414
+ <TabsContent value="decisions">
415
+ <DecisionsContent decisions={decisions} renderSkillLink={renderSkillLink} />
416
+ </TabsContent>
417
+ </CardContent>
418
+ </Tabs>
419
+ </Card>
420
+ );
421
+ }
422
+
423
+ function AttentionContent({
424
+ attention,
425
+ renderSkillLink,
426
+ }: {
427
+ attention: AttentionItem[];
428
+ renderSkillLink?: (skillName: string) => ReactNode;
429
+ }) {
430
+ const [showAll, setShowAll] = useState(false);
431
+
432
+ if (attention.length === 0) {
433
+ return (
434
+ <div className="flex items-center gap-3 py-4">
435
+ <CheckCircleIcon className="size-5 text-emerald-400" />
436
+ <p className="text-sm text-muted-foreground">Nothing needs your attention</p>
437
+ </div>
438
+ );
439
+ }
440
+
441
+ const visible = showAll ? attention : attention.slice(0, 6);
442
+
443
+ return (
444
+ <div className="space-y-2">
445
+ {visible.map((item) => {
446
+ const sev = SEVERITY[item.severity];
447
+ return (
448
+ <div
449
+ key={`${item.skill_name}-${item.category}`}
450
+ className="flex items-start gap-3 rounded-xl bg-background/40 px-3 py-3 transition-colors hover:bg-background/55"
451
+ >
452
+ <span className={`mt-1.5 size-2 shrink-0 rounded-full ${sev.dot}`} />
453
+ <div className="flex-1 min-w-0">
454
+ <div className="flex items-center gap-2 flex-wrap">
455
+ {renderSkillLink ? (
456
+ renderSkillLink(item.skill_name)
457
+ ) : (
458
+ <span className="text-sm font-medium text-foreground">{item.skill_name}</span>
459
+ )}
460
+ <Badge
461
+ variant="outline"
462
+ className={`text-[10px] font-normal ${sev.text} ${sev.bg} border-transparent`}
463
+ >
464
+ {item.category.replace(/_/g, " ")}
465
+ </Badge>
466
+ </div>
467
+ <p className="mt-0.5 line-clamp-1 text-sm text-muted-foreground">{item.reason}</p>
468
+ <p className="mt-0.5 line-clamp-1 text-xs text-muted-foreground/70">
469
+ {item.recommended_action}
470
+ </p>
471
+ </div>
472
+ <span className="text-[10px] text-muted-foreground/70 shrink-0 mt-0.5">
473
+ {item.timestamp ? timeAgo(item.timestamp) : ""}
474
+ </span>
475
+ </div>
476
+ );
477
+ })}
478
+ {attention.length > 6 && !showAll && (
479
+ <div className="pt-3">
480
+ <button
481
+ type="button"
482
+ onClick={() => setShowAll(true)}
483
+ className="text-xs text-primary hover:underline"
484
+ >
485
+ Show all {attention.length} attention items
486
+ </button>
487
+ </div>
488
+ )}
489
+ </div>
490
+ );
491
+ }
492
+
493
+ function DecisionsContent({
494
+ decisions,
495
+ renderSkillLink,
496
+ }: {
497
+ decisions: AutonomousDecision[];
498
+ renderSkillLink?: (skillName: string) => ReactNode;
499
+ }) {
500
+ const [showAll, setShowAll] = useState(false);
501
+ const visible = showAll ? decisions : decisions.slice(0, 10);
502
+ const keyedVisible = useMemo(() => {
503
+ const seen = new Map<string, number>();
504
+
505
+ return visible.map((decision) => {
506
+ const baseKey = `${decision.timestamp}-${decision.skill_name}-${decision.kind}`;
507
+ const occurrence = seen.get(baseKey) ?? 0;
508
+ seen.set(baseKey, occurrence + 1);
509
+
510
+ return {
511
+ decision,
512
+ key: `${baseKey}-${occurrence}`,
513
+ };
514
+ });
515
+ }, [visible]);
516
+
517
+ if (decisions.length === 0) {
518
+ return <p className="text-xs text-muted-foreground/70 py-4">No autonomous decisions yet.</p>;
519
+ }
520
+
521
+ return (
522
+ <div className="space-y-1">
523
+ {keyedVisible.map(({ decision: d, key }) => {
524
+ const marker = DECISION_MARKERS[d.kind];
525
+ return (
526
+ <div
527
+ key={key}
528
+ className="flex items-start gap-2.5 rounded-xl bg-background/30 px-3 py-2 transition-colors hover:bg-background/45"
529
+ >
530
+ <span className={`mt-1.5 size-2 shrink-0 rounded-full ${marker}`} />
531
+ <div className="flex-1 min-w-0">
532
+ {renderSkillLink ? (
533
+ renderSkillLink(d.skill_name)
534
+ ) : (
535
+ <span className="text-xs font-medium text-foreground truncate block">
536
+ {d.skill_name}
537
+ </span>
538
+ )}
539
+ <p className="line-clamp-2 text-xs text-muted-foreground">{d.summary}</p>
540
+ </div>
541
+ <span className="text-[10px] text-muted-foreground/70 shrink-0 mt-0.5">
542
+ {timeAgo(d.timestamp)}
543
+ </span>
544
+ </div>
545
+ );
546
+ })}
547
+ {decisions.length > 10 && !showAll && (
548
+ <button
549
+ type="button"
550
+ onClick={() => setShowAll(true)}
551
+ className="text-xs text-primary hover:underline mt-1 pl-2"
552
+ >
553
+ Show all ({decisions.length})
554
+ </button>
555
+ )}
556
+ </div>
557
+ );
558
+ }
559
+
560
+ // ---------------------------------------------------------------------------
561
+ // SkillComparisonGrid
562
+ // ---------------------------------------------------------------------------
563
+
564
+ export interface SkillComparisonRow {
565
+ skillId?: string;
566
+ skillName: string;
567
+ platforms: string[];
568
+ triggerRate: number | null;
569
+ routingConfidence: number | null;
570
+ confidenceCoverage: number;
571
+ sessions: number;
572
+ lastEvolution: EvolutionEntry | null;
573
+ bucket: TrustBucket;
574
+ }
575
+
576
+ export interface SkillComparisonGridProps {
577
+ rows: SkillComparisonRow[];
578
+ /** Optional render prop for skill name links */
579
+ renderSkillLink?: (skillName: string) => ReactNode;
580
+ }
581
+
582
+ function formatEvolutionAction(action: string): string {
583
+ switch (action) {
584
+ case "created":
585
+ return "Proposal created";
586
+ case "validated":
587
+ return "Validated";
588
+ case "deployed":
589
+ return "Deployed";
590
+ case "rolled_back":
591
+ return "Rolled back";
592
+ case "watch":
593
+ return "Watching";
594
+ case "rejected":
595
+ return "Rejected";
596
+ default:
597
+ return action.replace(/_/g, " ");
598
+ }
599
+ }
600
+
601
+ export function SkillComparisonGrid({ rows, renderSkillLink }: SkillComparisonGridProps) {
602
+ if (rows.length === 0) return null;
603
+
604
+ return (
605
+ <Card className="border-none bg-muted shadow-none py-0 ring-0">
606
+ <CardHeader className="px-5 pt-5 pb-0">
607
+ <div>
608
+ <p className="text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
609
+ Skill Comparison
610
+ </p>
611
+ <CardDescription className="mt-1 text-[13px] text-muted-foreground/70">
612
+ Compare skill performance before drilling into the details.
613
+ </CardDescription>
614
+ </div>
615
+ </CardHeader>
616
+
617
+ <CardContent className="overflow-x-auto px-5 py-5">
618
+ <div className="min-w-[680px]">
619
+ <div className="grid grid-cols-[minmax(180px,2fr)_1fr_1fr_0.8fr_1.2fr_1fr] gap-3 px-3 pb-2 text-[10px] uppercase tracking-[0.18em] text-muted-foreground/70">
620
+ <span>Skill</span>
621
+ <span>Trigger Rate</span>
622
+ <span>Routing Conf.</span>
623
+ <span>Sessions</span>
624
+ <span>Last Evolution</span>
625
+ <span>Status</span>
626
+ </div>
627
+ <div className="space-y-1.5">
628
+ {rows.map((row) => {
629
+ const bucketCfg = BUCKET_CFG[row.bucket];
630
+ return (
631
+ <div
632
+ key={row.skillId ?? row.skillName}
633
+ className="grid grid-cols-[minmax(180px,2fr)_1fr_1fr_0.8fr_1.2fr_1fr] items-center gap-3 rounded-xl bg-background/35 px-3 py-3 text-sm transition-colors hover:bg-background/50"
634
+ >
635
+ <div className="min-w-0">
636
+ {renderSkillLink ? (
637
+ renderSkillLink(row.skillName)
638
+ ) : (
639
+ <p className="truncate font-medium text-foreground">{row.skillName}</p>
640
+ )}
641
+ </div>
642
+ <div className="font-medium text-foreground">
643
+ {row.triggerRate != null ? `${Math.round(row.triggerRate * 100)}%` : "--"}
644
+ </div>
645
+ <div className="min-w-0">
646
+ {row.routingConfidence != null && row.confidenceCoverage >= 0.5 ? (
647
+ <>
648
+ <p className="text-sm font-medium text-foreground">
649
+ {Math.round(row.routingConfidence * 100)}%
650
+ </p>
651
+ <p className="truncate text-xs text-muted-foreground/70">
652
+ {Math.round(row.confidenceCoverage * 100)}% coverage
653
+ </p>
654
+ </>
655
+ ) : (
656
+ <p className="text-sm font-medium text-muted-foreground/70">--</p>
657
+ )}
658
+ </div>
659
+ <div className="text-muted-foreground">{row.sessions}</div>
660
+ <div className="min-w-0">
661
+ {row.lastEvolution ? (
662
+ <>
663
+ <p className="truncate text-sm text-foreground">
664
+ {formatEvolutionAction(row.lastEvolution.action)}
665
+ </p>
666
+ <p className="text-xs text-muted-foreground/70">
667
+ {timeAgo(row.lastEvolution.timestamp)}
668
+ </p>
669
+ </>
670
+ ) : (
671
+ <span className="text-xs text-muted-foreground/70">No evolutions yet</span>
672
+ )}
673
+ </div>
674
+ <div>
675
+ <Badge
676
+ variant="outline"
677
+ className={`border-transparent ${bucketCfg.accent} bg-background/55`}
678
+ >
679
+ <span
680
+ className={`mr-1.5 inline-block size-1.5 rounded-full ${bucketCfg.dot}`}
681
+ />
682
+ {bucketCfg.label}
683
+ </Badge>
684
+ </div>
685
+ </div>
686
+ );
687
+ })}
688
+ </div>
689
+ </div>
690
+ </CardContent>
691
+ </Card>
692
+ );
693
+ }