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,117 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { CONTRIBUTION_PREFERENCES_PATH, SELFTUNE_CONFIG_DIR } from "./constants.js";
5
+ import type { ContributionSignal } from "./contribution-signals.js";
6
+
7
+ export type ContributionGlobalDefault = "ask" | "always" | "never";
8
+ export type ContributionSkillStatus = "opted_in" | "opted_out";
9
+
10
+ export interface ContributionSkillPreference {
11
+ status: ContributionSkillStatus;
12
+ opted_in_at?: string;
13
+ opted_out_at?: string;
14
+ creator_id?: string;
15
+ signals?: ContributionSignal[];
16
+ }
17
+
18
+ export interface ContributionPreferences {
19
+ version: 1;
20
+ global_default: ContributionGlobalDefault;
21
+ skills: Record<string, ContributionSkillPreference>;
22
+ }
23
+
24
+ const DEFAULT_PREFERENCES: ContributionPreferences = {
25
+ version: 1,
26
+ global_default: "ask",
27
+ skills: {},
28
+ };
29
+
30
+ let cachedPreferences: ContributionPreferences | undefined;
31
+
32
+ function getSelftuneConfigDir(): string {
33
+ return process.env.SELFTUNE_CONFIG_DIR || SELFTUNE_CONFIG_DIR;
34
+ }
35
+
36
+ function getContributionPreferencesPath(): string {
37
+ return process.env.SELFTUNE_CONFIG_DIR
38
+ ? join(process.env.SELFTUNE_CONFIG_DIR, "contribution-preferences.json")
39
+ : CONTRIBUTION_PREFERENCES_PATH;
40
+ }
41
+
42
+ export function cloneDefaultContributionPreferences(): ContributionPreferences {
43
+ return {
44
+ version: 1,
45
+ global_default: "ask",
46
+ skills: {},
47
+ };
48
+ }
49
+
50
+ export function isValidGlobalDefault(value: unknown): value is ContributionGlobalDefault {
51
+ return value === "ask" || value === "always" || value === "never";
52
+ }
53
+
54
+ function normalizePreferences(raw: unknown): ContributionPreferences {
55
+ if (!raw || typeof raw !== "object") return cloneDefaultContributionPreferences();
56
+ const candidate = raw as Partial<ContributionPreferences>;
57
+ const globalDefault = isValidGlobalDefault(candidate.global_default)
58
+ ? candidate.global_default
59
+ : DEFAULT_PREFERENCES.global_default;
60
+ const skills: Record<string, ContributionSkillPreference> = {};
61
+
62
+ if (candidate.skills && typeof candidate.skills === "object") {
63
+ for (const [skill, pref] of Object.entries(candidate.skills)) {
64
+ if (!pref || typeof pref !== "object") continue;
65
+ const status = (pref as Partial<ContributionSkillPreference>).status;
66
+ if (status !== "opted_in" && status !== "opted_out") continue;
67
+ skills[skill] = {
68
+ status,
69
+ opted_in_at: (pref as Partial<ContributionSkillPreference>).opted_in_at,
70
+ opted_out_at: (pref as Partial<ContributionSkillPreference>).opted_out_at,
71
+ creator_id:
72
+ typeof (pref as Partial<ContributionSkillPreference>).creator_id === "string"
73
+ ? (pref as Partial<ContributionSkillPreference>).creator_id
74
+ : undefined,
75
+ signals: Array.isArray((pref as Partial<ContributionSkillPreference>).signals)
76
+ ? (pref as Partial<ContributionSkillPreference>).signals?.filter(
77
+ (signal): signal is ContributionSignal =>
78
+ signal === "trigger" || signal === "grade" || signal === "miss_category",
79
+ )
80
+ : undefined,
81
+ };
82
+ }
83
+ }
84
+
85
+ return {
86
+ version: 1,
87
+ global_default: globalDefault,
88
+ skills,
89
+ };
90
+ }
91
+
92
+ export function loadContributionPreferences(): ContributionPreferences {
93
+ if (cachedPreferences) return cachedPreferences;
94
+ const preferencesPath = getContributionPreferencesPath();
95
+ try {
96
+ if (!existsSync(preferencesPath)) {
97
+ cachedPreferences = cloneDefaultContributionPreferences();
98
+ return cachedPreferences;
99
+ }
100
+ const parsed = JSON.parse(readFileSync(preferencesPath, "utf-8")) as unknown;
101
+ cachedPreferences = normalizePreferences(parsed);
102
+ return cachedPreferences;
103
+ } catch {
104
+ cachedPreferences = cloneDefaultContributionPreferences();
105
+ return cachedPreferences;
106
+ }
107
+ }
108
+
109
+ export function saveContributionPreferences(preferences: ContributionPreferences): void {
110
+ mkdirSync(getSelftuneConfigDir(), { recursive: true });
111
+ writeFileSync(getContributionPreferencesPath(), JSON.stringify(preferences, null, 2), "utf-8");
112
+ cachedPreferences = preferences;
113
+ }
114
+
115
+ export function resetContributionPreferencesState(): void {
116
+ cachedPreferences = undefined;
117
+ }
@@ -12,6 +12,7 @@ export type ContributionSignal = "trigger" | "grade" | "miss_category";
12
12
  export interface CreatorContributionRelayPayload {
13
13
  version: 1;
14
14
  signal_type: "skill_session";
15
+ skill_name?: string;
15
16
  relay_destination: string;
16
17
  skill_hash: string;
17
18
  user_cohort: string;
@@ -114,11 +115,12 @@ export function buildCreatorDirectedContributionSignals(
114
115
  options: ContributionSignalBuildOptions = {},
115
116
  ): CreatorContributionSignalRecord[] {
116
117
  const bySkill = new Map(configs.map((config) => [config.skill_name, config]));
117
- const gradingBySession = new Map<string, "A" | "B" | "C" | "F">();
118
+ const gradingBySkillSession = new Map<string, "A" | "B" | "C" | "F">();
118
119
  for (const row of queryGradingResults(db)) {
119
120
  const source = typeof row.mean_score === "number" ? row.mean_score : row.pass_rate;
120
- if (typeof source === "number" && !gradingBySession.has(row.session_id)) {
121
- gradingBySession.set(row.session_id, gradeBucket(source));
121
+ const key = `${row.skill_name}::${row.session_id}`;
122
+ if (typeof source === "number" && !gradingBySkillSession.has(key)) {
123
+ gradingBySkillSession.set(key, gradeBucket(source));
122
124
  }
123
125
  }
124
126
 
@@ -136,7 +138,7 @@ export function buildCreatorDirectedContributionSignals(
136
138
  signals.miss_detected = row.triggered === 0;
137
139
  }
138
140
  if (config.contribution.signals.includes("grade")) {
139
- const grade = gradingBySession.get(row.session_id);
141
+ const grade = gradingBySkillSession.get(`${row.skill_name}::${row.session_id}`);
140
142
  if (grade) signals.execution_grade = grade;
141
143
  }
142
144
  if (config.contribution.signals.includes("miss_category")) {
@@ -162,6 +164,7 @@ export function buildCreatorDirectedContributionSignals(
162
164
  payload: {
163
165
  version: 1 as const,
164
166
  signal_type: "skill_session" as const,
167
+ skill_name: config.skill_name,
165
168
  relay_destination: config.creator_id,
166
169
  skill_hash: buildContributionSkillHash(config.skill_name),
167
170
  user_cohort: cohort,
@@ -206,6 +209,7 @@ export function buildContributionPreview(
206
209
  samplePayload: payloads[0]?.payload ?? {
207
210
  version: 1,
208
211
  signal_type: "skill_session",
212
+ skill_name: config.skill_name,
209
213
  relay_destination: config.creator_id,
210
214
  skill_hash: buildContributionSkillHash(config.skill_name),
211
215
  user_cohort: buildContributionUserCohort(options.now ?? new Date(), options.cohortSeed),
@@ -1,9 +1,16 @@
1
1
  import type { Database } from "bun:sqlite";
2
2
 
3
3
  import type { CreatorContributionConfig } from "./contribution-config.js";
4
- import { discoverCreatorContributionConfigs } from "./contribution-config.js";
4
+ import {
5
+ discoverCreatorContributionConfigs,
6
+ isSupportedContributionSignal,
7
+ isValidCreatorUUID,
8
+ } from "./contribution-config.js";
9
+ import {
10
+ loadContributionPreferences,
11
+ type ContributionPreferences,
12
+ } from "./contribution-preferences.js";
5
13
  import { buildCreatorDirectedContributionSignals } from "./contribution-signals.js";
6
- import { loadContributionPreferences, type ContributionPreferences } from "./contributions.js";
7
14
 
8
15
  export interface CreatorContributionStagingResult {
9
16
  eligible_skills: number;
@@ -34,6 +41,10 @@ export function resolveEligibleContributionConfigs(
34
41
  configs: CreatorContributionConfig[] = discoverCreatorContributionConfigs(),
35
42
  ): CreatorContributionConfig[] {
36
43
  return configs.filter((config) => {
44
+ if (!isValidCreatorUUID(config.creator_id)) return false;
45
+ if (!config.contribution.signals.some((signal) => isSupportedContributionSignal(signal))) {
46
+ return false;
47
+ }
37
48
  const pref = preferences.skills[config.skill_name];
38
49
  if (pref?.status === "opted_out") return false;
39
50
  if (pref?.status === "opted_in") return true;
@@ -1,12 +1,20 @@
1
1
  #!/usr/bin/env bun
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
2
 
5
- import { CONTRIBUTION_PREFERENCES_PATH, SELFTUNE_CONFIG_DIR } from "./constants.js";
6
3
  import {
7
4
  discoverCreatorContributionConfigs,
8
5
  findCreatorContributionConfig,
6
+ isSupportedContributionSignal,
7
+ isValidCreatorUUID,
9
8
  } from "./contribution-config.js";
9
+ import {
10
+ cloneDefaultContributionPreferences,
11
+ isValidGlobalDefault,
12
+ loadContributionPreferences,
13
+ resetContributionPreferencesState,
14
+ saveContributionPreferences,
15
+ type ContributionPreferences,
16
+ type ContributionSkillStatus,
17
+ } from "./contribution-preferences.js";
10
18
  import {
11
19
  flushCreatorContributionSignals,
12
20
  resolveContributionRelayEndpoint,
@@ -17,6 +25,7 @@ import {
17
25
  type ContributionSignalBuildOptions,
18
26
  type CreatorContributionRelayPayload,
19
27
  } from "./contribution-signals.js";
28
+ import type { CreatorContributionConfig } from "./contribution-config.js";
20
29
  import { getDb } from "./localdb/db.js";
21
30
  import {
22
31
  getCreatorContributionRelayStats,
@@ -25,22 +34,16 @@ import {
25
34
  } from "./localdb/queries.js";
26
35
  import { CLIError } from "./utils/cli-error.js";
27
36
 
28
- export type ContributionGlobalDefault = "ask" | "always" | "never";
29
- export type ContributionSkillStatus = "opted_in" | "opted_out";
30
-
31
- export interface ContributionSkillPreference {
32
- status: ContributionSkillStatus;
33
- opted_in_at?: string;
34
- opted_out_at?: string;
35
- creator_id?: string;
36
- signals?: ContributionSignal[];
37
- }
38
-
39
- export interface ContributionPreferences {
40
- version: 1;
41
- global_default: ContributionGlobalDefault;
42
- skills: Record<string, ContributionSkillPreference>;
43
- }
37
+ export {
38
+ cloneDefaultContributionPreferences,
39
+ loadContributionPreferences,
40
+ resetContributionPreferencesState,
41
+ saveContributionPreferences,
42
+ type ContributionGlobalDefault,
43
+ type ContributionPreferences,
44
+ type ContributionSkillPreference,
45
+ type ContributionSkillStatus,
46
+ } from "./contribution-preferences.js";
44
47
 
45
48
  export interface ContributionPromptCandidate {
46
49
  skill_name: string;
@@ -48,101 +51,6 @@ export interface ContributionPromptCandidate {
48
51
  successful_triggers: number;
49
52
  }
50
53
 
51
- const DEFAULT_PREFERENCES: ContributionPreferences = {
52
- version: 1,
53
- global_default: "ask",
54
- skills: {},
55
- };
56
-
57
- let cachedPreferences: ContributionPreferences | undefined;
58
-
59
- function getSelftuneConfigDir(): string {
60
- return process.env.SELFTUNE_CONFIG_DIR || SELFTUNE_CONFIG_DIR;
61
- }
62
-
63
- function getContributionPreferencesPath(): string {
64
- return process.env.SELFTUNE_CONFIG_DIR
65
- ? join(process.env.SELFTUNE_CONFIG_DIR, "contribution-preferences.json")
66
- : CONTRIBUTION_PREFERENCES_PATH;
67
- }
68
-
69
- function cloneDefaultPreferences(): ContributionPreferences {
70
- return {
71
- version: 1,
72
- global_default: "ask",
73
- skills: {},
74
- };
75
- }
76
-
77
- function isValidGlobalDefault(value: unknown): value is ContributionGlobalDefault {
78
- return value === "ask" || value === "always" || value === "never";
79
- }
80
-
81
- function normalizePreferences(raw: unknown): ContributionPreferences {
82
- if (!raw || typeof raw !== "object") return cloneDefaultPreferences();
83
- const candidate = raw as Partial<ContributionPreferences>;
84
- const globalDefault = isValidGlobalDefault(candidate.global_default)
85
- ? candidate.global_default
86
- : DEFAULT_PREFERENCES.global_default;
87
- const skills: Record<string, ContributionSkillPreference> = {};
88
-
89
- if (candidate.skills && typeof candidate.skills === "object") {
90
- for (const [skill, pref] of Object.entries(candidate.skills)) {
91
- if (!pref || typeof pref !== "object") continue;
92
- const status = (pref as Partial<ContributionSkillPreference>).status;
93
- if (status !== "opted_in" && status !== "opted_out") continue;
94
- skills[skill] = {
95
- status,
96
- opted_in_at: (pref as Partial<ContributionSkillPreference>).opted_in_at,
97
- opted_out_at: (pref as Partial<ContributionSkillPreference>).opted_out_at,
98
- creator_id:
99
- typeof (pref as Partial<ContributionSkillPreference>).creator_id === "string"
100
- ? (pref as Partial<ContributionSkillPreference>).creator_id
101
- : undefined,
102
- signals: Array.isArray((pref as Partial<ContributionSkillPreference>).signals)
103
- ? (pref as Partial<ContributionSkillPreference>).signals?.filter(
104
- (signal): signal is ContributionSignal =>
105
- signal === "trigger" || signal === "grade" || signal === "miss_category",
106
- )
107
- : undefined,
108
- };
109
- }
110
- }
111
-
112
- return {
113
- version: 1,
114
- global_default: globalDefault,
115
- skills,
116
- };
117
- }
118
-
119
- export function loadContributionPreferences(): ContributionPreferences {
120
- if (cachedPreferences) return cachedPreferences;
121
- const preferencesPath = getContributionPreferencesPath();
122
- try {
123
- if (!existsSync(preferencesPath)) {
124
- cachedPreferences = cloneDefaultPreferences();
125
- return cachedPreferences;
126
- }
127
- const parsed = JSON.parse(readFileSync(preferencesPath, "utf-8")) as unknown;
128
- cachedPreferences = normalizePreferences(parsed);
129
- return cachedPreferences;
130
- } catch {
131
- cachedPreferences = cloneDefaultPreferences();
132
- return cachedPreferences;
133
- }
134
- }
135
-
136
- export function saveContributionPreferences(preferences: ContributionPreferences): void {
137
- mkdirSync(getSelftuneConfigDir(), { recursive: true });
138
- writeFileSync(getContributionPreferencesPath(), JSON.stringify(preferences, null, 2), "utf-8");
139
- cachedPreferences = preferences;
140
- }
141
-
142
- export function resetContributionPreferencesState(): void {
143
- cachedPreferences = undefined;
144
- }
145
-
146
54
  function printStatus(preferences: ContributionPreferences): void {
147
55
  const discovered = discoverCreatorContributionConfigs();
148
56
  const promptCandidates = listContributionPromptCandidates(preferences);
@@ -223,6 +131,11 @@ export function listContributionPromptCandidates(
223
131
 
224
132
  const bySkill = new Map(getSkillTrustSummaries(getDb()).map((row) => [row.skill_name, row]));
225
133
  return discoverCreatorContributionConfigs()
134
+ .filter(
135
+ (config) =>
136
+ isValidCreatorUUID(config.creator_id) &&
137
+ config.contribution.signals.some((signal) => isSupportedContributionSignal(signal)),
138
+ )
226
139
  .filter((config) => !preferences.skills[config.skill_name])
227
140
  .map((config) => {
228
141
  const summary = bySkill.get(config.skill_name);
@@ -247,19 +160,40 @@ function upsertSkillPreference(skill: string, status: ContributionSkillStatus):
247
160
  const preferences = loadContributionPreferences();
248
161
  const timestamp = new Date().toISOString();
249
162
  const discovered = findCreatorContributionConfig(normalizedSkill);
163
+ if (!discovered) {
164
+ throw new CLIError(
165
+ `No creator contribution request found for "${normalizedSkill}".`,
166
+ "FILE_NOT_FOUND",
167
+ "Run `selftune contributions` to see installed skill requests.",
168
+ );
169
+ }
170
+ if (!isValidCreatorUUID(discovered.creator_id)) {
171
+ throw new CLIError(
172
+ `Creator contribution request for "${normalizedSkill}" has an invalid creator_id.`,
173
+ "INVALID_FLAG",
174
+ "Ask the skill creator to ship a valid selftune.contribute.json or choose another skill.",
175
+ );
176
+ }
177
+ const validSignals = discovered.contribution.signals.filter(
178
+ (signal): signal is ContributionSignal => isSupportedContributionSignal(signal),
179
+ );
180
+ if (validSignals.length === 0) {
181
+ throw new CLIError(
182
+ `Creator contribution request for "${normalizedSkill}" does not declare any supported signals.`,
183
+ "INVALID_FLAG",
184
+ "Ask the skill creator to ship a valid selftune.contribute.json or choose another skill.",
185
+ );
186
+ }
250
187
  preferences.skills[normalizedSkill] =
251
188
  status === "opted_in"
252
189
  ? { status, opted_in_at: timestamp }
253
190
  : { status, opted_out_at: timestamp };
254
- if (status === "opted_in" && discovered) {
191
+ if (status === "opted_in") {
255
192
  preferences.skills[normalizedSkill] = {
256
193
  status,
257
194
  opted_in_at: timestamp,
258
195
  creator_id: discovered.creator_id,
259
- signals: discovered.contribution.signals.filter(
260
- (signal): signal is ContributionSignal =>
261
- signal === "trigger" || signal === "grade" || signal === "miss_category",
262
- ),
196
+ signals: validSignals,
263
197
  };
264
198
  }
265
199
  saveContributionPreferences(preferences);
@@ -273,7 +207,7 @@ function buildPreviewPayload(
273
207
  skill: string,
274
208
  options: ContributionSignalBuildOptions = {},
275
209
  ): {
276
- config: ReturnType<typeof findCreatorContributionConfig>;
210
+ config: CreatorContributionConfig;
277
211
  observedCount: number;
278
212
  triggerRate: number | null;
279
213
  missRate: number | null;
@@ -345,7 +279,7 @@ function setGlobalDefault(value: string | undefined): void {
345
279
  }
346
280
 
347
281
  function resetPreferences(): void {
348
- saveContributionPreferences(cloneDefaultPreferences());
282
+ saveContributionPreferences(cloneDefaultContributionPreferences());
349
283
  console.log("Creator-directed contribution preferences reset to defaults.");
350
284
  }
351
285
 
@@ -9,6 +9,8 @@ import {
9
9
  discoverCreatorContributionConfigs,
10
10
  findCreatorContributionConfig,
11
11
  getContributionConfigSearchRoots,
12
+ isValidCreatorUUID,
13
+ normalizeSupportedContributionSignals,
12
14
  removeCreatorContributionConfig,
13
15
  resolveContributionSkillPath,
14
16
  writeCreatorContributionConfig,
@@ -96,9 +98,16 @@ function enableCreatorContributionConfigs(options: {
96
98
  const creatorId = inferCreatorId(options.explicitCreatorId);
97
99
  if (!creatorId) {
98
100
  throw new CLIError(
99
- "Creator ID is required.",
101
+ "Creator ID is required. Must be the creator's cloud user UUID.",
100
102
  "MISSING_FLAG",
101
- "Pass --creator-id <id> or enroll alpha so cloud_user_id is available.",
103
+ "Pass --creator-id <uuid> or enroll alpha so cloud_user_id is available.",
104
+ );
105
+ }
106
+ if (!isValidCreatorUUID(creatorId)) {
107
+ throw new CLIError(
108
+ `Creator ID must be a cloud user UUID. Received "${creatorId}".`,
109
+ "INVALID_FLAG",
110
+ "Pass --creator-id <uuid> or enroll alpha so cloud_user_id is available.",
102
111
  );
103
112
  }
104
113
 
@@ -158,7 +167,7 @@ export async function cliMain(): Promise<void> {
158
167
  const rest = process.argv.slice(3);
159
168
 
160
169
  if (sub === "--help" || sub === "-h") {
161
- console.log(`selftune creator-contributions — Manage creator-side contribution configs
170
+ console.log(`selftune creator-contributions — Manage creator sharing setup configs
162
171
 
163
172
  Usage:
164
173
  selftune creator-contributions
@@ -168,9 +177,11 @@ Usage:
168
177
  selftune creator-contributions disable --skill <name> [--skill-path <path>]
169
178
 
170
179
  Purpose:
171
- Manage the local selftune.contribute.json file that a skill creator bundles
172
- with a skill package. This is separate from:
173
- selftune contributions End-user sharing preferences
180
+ Manage the local selftune.contribute.json creator sharing setup file that
181
+ a skill creator bundles with a skill package. The --creator-id must be the
182
+ creator's cloud user UUID (the cloud_user_id from alpha enrollment).
183
+ This is separate from:
184
+ selftune contributions Sharing preferences (end-user opt-in/out)
174
185
  selftune contribute Community export bundle`);
175
186
  return;
176
187
  }
@@ -225,10 +236,18 @@ Purpose:
225
236
  );
226
237
  }
227
238
 
228
- const signals = (values.signals ?? "trigger,grade,miss_category")
229
- .split(",")
230
- .map((signal) => signal.trim())
231
- .filter(Boolean);
239
+ let signals: string[];
240
+ try {
241
+ signals = normalizeSupportedContributionSignals(
242
+ (values.signals ?? "trigger,grade,miss_category").split(","),
243
+ );
244
+ } catch (error) {
245
+ throw new CLIError(
246
+ error instanceof Error ? error.message : String(error),
247
+ "INVALID_FLAG",
248
+ "selftune creator-contributions enable --help",
249
+ );
250
+ }
232
251
  const outcome = enableCreatorContributionConfigs({
233
252
  skillName: values.skill?.trim(),
234
253
  all: values.all,
@@ -231,17 +231,21 @@ export async function cliMain(): Promise<void> {
231
231
  });
232
232
 
233
233
  // Get timezone: flag > env > system default
234
- const tz = values.tz ?? process.env.TZ ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
234
+ const tz =
235
+ (typeof values.tz === "string" ? values.tz : undefined) ??
236
+ process.env.TZ ??
237
+ Intl.DateTimeFormat().resolvedOptions().timeZone;
238
+ const dryRun = values["dry-run"] === true;
235
239
 
236
240
  switch (subcommand) {
237
241
  case "setup":
238
- await setupCronJobs(tz, values["dry-run"] ?? false);
242
+ await setupCronJobs(tz, dryRun);
239
243
  break;
240
244
  case "list":
241
245
  listCronJobs();
242
246
  break;
243
247
  case "remove":
244
- await removeCronJobs(values["dry-run"] ?? false);
248
+ await removeCronJobs(dryRun);
245
249
  break;
246
250
  default:
247
251
  console.log(`selftune cron — OpenClaw cron integration
@@ -91,6 +91,16 @@ export interface EvalSnapshot {
91
91
  improved?: boolean;
92
92
  regressions?: Array<Record<string, unknown>>;
93
93
  new_passes?: Array<Record<string, unknown>>;
94
+ per_entry_results?: Array<Record<string, unknown>>;
95
+ before_entry_results?: Array<Record<string, unknown>>;
96
+ gates_passed?: number;
97
+ gates_total?: number;
98
+ gate_results?: Array<Record<string, unknown>>;
99
+ validation_mode?: string;
100
+ validation_agent?: string;
101
+ validation_fixture_id?: string;
102
+ validation_fallback_reason?: string;
103
+ validation_evidence_ref?: string;
94
104
  }
95
105
 
96
106
  export interface EvolutionEntry {
@@ -140,6 +150,7 @@ export interface SkillSummary {
140
150
  has_evidence: boolean;
141
151
  routing_confidence: number | null;
142
152
  confidence_coverage: number;
153
+ testing_readiness?: SkillTestingReadiness;
143
154
  }
144
155
 
145
156
  // -- Autonomy-first overview types -------------------------------------------
@@ -226,6 +237,7 @@ export interface OverviewResponse {
226
237
  attention_queue: AttentionItem[];
227
238
  trust_watchlist: TrustWatchlistEntry[];
228
239
  recent_decisions: AutonomousDecision[];
240
+ creator_testing?: CreatorTestingOverview;
229
241
  }
230
242
 
231
243
  export interface EvidenceEntry {
@@ -303,6 +315,62 @@ export interface SkillReportPayload {
303
315
  sessions_with_skill: number;
304
316
  }
305
317
 
318
+ export type SkillEvalReadiness = "log_ready" | "cold_start_ready" | "telemetry_only";
319
+
320
+ export type CreatorLoopNextStep =
321
+ | "generate_evals"
322
+ | "run_unit_tests"
323
+ | "run_replay_dry_run"
324
+ | "measure_baseline"
325
+ | "deploy_candidate"
326
+ | "watch_deployment";
327
+
328
+ export type DeploymentReadiness = "blocked" | "ready_to_deploy" | "watching" | "rolled_back";
329
+
330
+ export interface SkillTestingReadiness {
331
+ skill_name: string;
332
+ eval_readiness: SkillEvalReadiness;
333
+ next_step: CreatorLoopNextStep;
334
+ summary: string;
335
+ recommended_command: string;
336
+ skill_path: string | null;
337
+ trusted_trigger_count: number;
338
+ trusted_session_count: number;
339
+ eval_set_entries: number;
340
+ latest_eval_at: string | null;
341
+ unit_test_cases: number;
342
+ unit_test_pass_rate: number | null;
343
+ unit_test_ran_at: string | null;
344
+ replay_check_count: number;
345
+ latest_validation_mode: "structural_guard" | "host_replay" | "llm_judge" | null;
346
+ baseline_sample_size: number;
347
+ baseline_pass_rate: number | null;
348
+ latest_baseline_at: string | null;
349
+ deployment_readiness: DeploymentReadiness;
350
+ deployment_summary: string;
351
+ deployment_command: string | null;
352
+ latest_evolution_action: string | null;
353
+ latest_evolution_at: string | null;
354
+ }
355
+
356
+ export interface CreatorTestingOverview {
357
+ summary: string;
358
+ counts: {
359
+ generate_evals: number;
360
+ run_unit_tests: number;
361
+ run_replay_dry_run: number;
362
+ measure_baseline: number;
363
+ deploy_candidate: number;
364
+ watch_deployment: number;
365
+ };
366
+ priorities: Array<{
367
+ skill_name: string;
368
+ next_step: CreatorLoopNextStep;
369
+ summary: string;
370
+ recommended_command: string;
371
+ }>;
372
+ }
373
+
306
374
  // -- Orchestrate run report types --------------------------------------------
307
375
 
308
376
  export interface OrchestrateRunSkillAction {
@@ -384,7 +452,11 @@ export interface HealthResponse {
384
452
  ok: boolean;
385
453
  service: string;
386
454
  version: string;
455
+ pid: number;
387
456
  spa: boolean;
457
+ spa_mode?: "dist" | "proxy" | "missing";
458
+ spa_build_id?: string | null;
459
+ spa_proxy_url?: string | null;
388
460
  v2_data_available: boolean;
389
461
  workspace_root: string;
390
462
  git_sha: string;
@@ -397,6 +469,20 @@ export interface HealthResponse {
397
469
  port: number;
398
470
  }
399
471
 
472
+ // -- Replay entry result types ------------------------------------------------
473
+
474
+ export interface ReplayEntryResult {
475
+ proposal_id: string;
476
+ skill_name: string;
477
+ validation_mode: string;
478
+ phase: string;
479
+ query: string;
480
+ should_trigger: boolean;
481
+ triggered: boolean;
482
+ passed: boolean;
483
+ evidence: string | null;
484
+ }
485
+
400
486
  // -- Doctor / health check types ----------------------------------------------
401
487
  export type { DoctorResult, HealthCheck, HealthStatus } from "./types.js";
402
488
 
@@ -560,4 +646,5 @@ export interface SkillReportResponse extends SkillReportPayload, TrustFields {
560
646
  not_just_name: number;
561
647
  };
562
648
  } | null;
649
+ testing_readiness?: SkillTestingReadiness;
563
650
  }