selftune 0.2.18 → 0.2.20

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 (77) hide show
  1. package/README.md +9 -4
  2. package/apps/local-dashboard/dist/assets/index-D8O-RG1I.js +60 -0
  3. package/apps/local-dashboard/dist/assets/index-_EcLywDg.css +1 -0
  4. package/apps/local-dashboard/dist/assets/vendor-table-BIiI3YhS.js +1 -0
  5. package/apps/local-dashboard/dist/assets/vendor-ui-CGEmUayx.js +12 -0
  6. package/apps/local-dashboard/dist/index.html +5 -5
  7. package/cli/selftune/alpha-upload/stage-canonical.ts +7 -6
  8. package/cli/selftune/constants.ts +10 -0
  9. package/cli/selftune/contribute/contribute.ts +30 -2
  10. package/cli/selftune/contribution-config.ts +249 -0
  11. package/cli/selftune/contribution-relay.ts +177 -0
  12. package/cli/selftune/contribution-signals.ts +219 -0
  13. package/cli/selftune/contribution-staging.ts +147 -0
  14. package/cli/selftune/contributions.ts +532 -0
  15. package/cli/selftune/creator-contributions.ts +333 -0
  16. package/cli/selftune/dashboard-contract.ts +209 -1
  17. package/cli/selftune/dashboard-server.ts +45 -11
  18. package/cli/selftune/eval/family-overlap.ts +714 -0
  19. package/cli/selftune/eval/hooks-to-evals.ts +182 -28
  20. package/cli/selftune/eval/synthetic-evals.ts +298 -11
  21. package/cli/selftune/evolution/evidence.ts +5 -0
  22. package/cli/selftune/evolution/evolve-body.ts +62 -2
  23. package/cli/selftune/evolution/evolve.ts +58 -1
  24. package/cli/selftune/evolution/validate-body.ts +10 -0
  25. package/cli/selftune/evolution/validate-host-replay.ts +236 -0
  26. package/cli/selftune/evolution/validate-proposal.ts +10 -0
  27. package/cli/selftune/evolution/validate-routing.ts +112 -5
  28. package/cli/selftune/export.ts +2 -2
  29. package/cli/selftune/index.ts +41 -5
  30. package/cli/selftune/ingestors/codex-rollout.ts +31 -35
  31. package/cli/selftune/ingestors/codex-wrapper.ts +32 -24
  32. package/cli/selftune/localdb/db.ts +2 -2
  33. package/cli/selftune/localdb/direct-write.ts +8 -3
  34. package/cli/selftune/localdb/materialize.ts +7 -2
  35. package/cli/selftune/localdb/queries.ts +712 -31
  36. package/cli/selftune/localdb/schema.ts +30 -1
  37. package/cli/selftune/recover.ts +153 -0
  38. package/cli/selftune/repair/skill-usage.ts +363 -4
  39. package/cli/selftune/routes/actions.ts +35 -1
  40. package/cli/selftune/routes/analytics.ts +14 -0
  41. package/cli/selftune/routes/index.ts +1 -0
  42. package/cli/selftune/routes/overview.ts +112 -4
  43. package/cli/selftune/routes/skill-report.ts +575 -11
  44. package/cli/selftune/status.ts +81 -2
  45. package/cli/selftune/sync.ts +56 -2
  46. package/cli/selftune/trust-model.ts +66 -0
  47. package/cli/selftune/types.ts +103 -0
  48. package/cli/selftune/utils/skill-detection.ts +43 -0
  49. package/cli/selftune/utils/text-similarity.ts +73 -0
  50. package/cli/selftune/watchlist.ts +65 -0
  51. package/package.json +1 -1
  52. package/packages/ui/src/components/ActivityTimeline.tsx +165 -150
  53. package/packages/ui/src/components/EvidenceViewer.tsx +419 -145
  54. package/packages/ui/src/components/EvolutionTimeline.tsx +81 -29
  55. package/packages/ui/src/components/OrchestrateRunsPanel.tsx +33 -16
  56. package/packages/ui/src/components/RecentActivityFeed.tsx +72 -41
  57. package/packages/ui/src/components/section-cards.tsx +12 -9
  58. package/packages/ui/src/primitives/card.tsx +1 -1
  59. package/packages/ui/src/types.ts +4 -0
  60. package/skill/SKILL.md +11 -1
  61. package/skill/Workflows/AlphaUpload.md +4 -0
  62. package/skill/Workflows/Composability.md +78 -0
  63. package/skill/Workflows/Contribute.md +6 -3
  64. package/skill/Workflows/Contributions.md +97 -0
  65. package/skill/Workflows/CreatorContributions.md +74 -0
  66. package/skill/Workflows/Dashboard.md +31 -0
  67. package/skill/Workflows/Evals.md +57 -8
  68. package/skill/Workflows/Evolve.md +23 -0
  69. package/skill/Workflows/Ingest.md +7 -0
  70. package/skill/Workflows/Initialize.md +20 -1
  71. package/skill/Workflows/Recover.md +84 -0
  72. package/skill/Workflows/RepairSkillUsage.md +12 -4
  73. package/skill/Workflows/Sync.md +18 -12
  74. package/apps/local-dashboard/dist/assets/index-BMIS6uUh.css +0 -2
  75. package/apps/local-dashboard/dist/assets/index-DOu3iLD9.js +0 -16
  76. package/apps/local-dashboard/dist/assets/vendor-table-pHbDxq36.js +0 -8
  77. package/apps/local-dashboard/dist/assets/vendor-ui-DIwlrGlb.js +0 -12
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { parseArgs } from "node:util";
4
+
5
+ import { readAlphaIdentity } from "./alpha-identity.js";
6
+ import { SELFTUNE_CONFIG_PATH } from "./constants.js";
7
+ import {
8
+ type CreatorContributionConfig,
9
+ discoverCreatorContributionConfigs,
10
+ findCreatorContributionConfig,
11
+ getContributionConfigSearchRoots,
12
+ removeCreatorContributionConfig,
13
+ resolveContributionSkillPath,
14
+ writeCreatorContributionConfig,
15
+ } from "./contribution-config.js";
16
+ import { CLIError } from "./utils/cli-error.js";
17
+ import { handleCLIError } from "./utils/cli-error.js";
18
+ import { findInstalledSkillNames } from "./utils/skill-discovery.js";
19
+
20
+ function inferCreatorId(explicitCreatorId?: string): string | null {
21
+ if (explicitCreatorId?.trim()) return explicitCreatorId.trim();
22
+ const alpha = readAlphaIdentity(SELFTUNE_CONFIG_PATH);
23
+ return alpha?.cloud_user_id?.trim() || null;
24
+ }
25
+
26
+ function printConfig(config: CreatorContributionConfig): void {
27
+ console.log(`${config.skill_name}`);
28
+ console.log(` creator_id: ${config.creator_id}`);
29
+ console.log(` skill_path: ${config.skill_path}`);
30
+ console.log(` signals: ${config.contribution.signals.join(", ")}`);
31
+ if (config.contribution.message) {
32
+ console.log(` message: ${config.contribution.message}`);
33
+ }
34
+ if (config.contribution.privacy_url) {
35
+ console.log(` privacy_url: ${config.contribution.privacy_url}`);
36
+ }
37
+ }
38
+
39
+ function printStatus(skillName?: string): void {
40
+ const searchRoots = getContributionConfigSearchRoots();
41
+ const installedSkills = [...findInstalledSkillNames(searchRoots)].sort();
42
+ const configuredSkillNames = new Set(
43
+ discoverCreatorContributionConfigs(searchRoots).map((c) => c.skill_name),
44
+ );
45
+ if (skillName) {
46
+ const config = findCreatorContributionConfig(skillName);
47
+ if (!config) {
48
+ console.log(`No creator contribution config found for "${skillName}".`);
49
+ return;
50
+ }
51
+ console.log("Creator contribution config:");
52
+ printConfig(config);
53
+ return;
54
+ }
55
+
56
+ const configs = discoverCreatorContributionConfigs();
57
+ if (configs.length === 0) {
58
+ console.log("No creator contribution configs discovered.");
59
+ console.log("Use `selftune creator-contributions enable --skill <name>` to add one.");
60
+ } else {
61
+ console.log("Discovered creator contribution configs:");
62
+ for (const config of configs) {
63
+ printConfig(config);
64
+ }
65
+ }
66
+
67
+ const missingInstalled = installedSkills.filter((skill) => !configuredSkillNames.has(skill));
68
+ if (missingInstalled.length > 0) {
69
+ console.log("Installed skills without creator contribution config:");
70
+ for (const skill of missingInstalled) {
71
+ console.log(` ${skill}`);
72
+ }
73
+ }
74
+ }
75
+
76
+ interface BulkEnableSkip {
77
+ skill_name: string;
78
+ reason: "already_configured" | "skill_path_not_found";
79
+ }
80
+
81
+ interface BulkEnableResult {
82
+ written: string[];
83
+ skipped: BulkEnableSkip[];
84
+ }
85
+
86
+ function enableCreatorContributionConfigs(options: {
87
+ skillName?: string;
88
+ all?: boolean;
89
+ prefix?: string;
90
+ explicitSkillPath?: string;
91
+ explicitCreatorId?: string;
92
+ signals: string[];
93
+ message?: string;
94
+ privacyUrl?: string;
95
+ }): BulkEnableResult {
96
+ const creatorId = inferCreatorId(options.explicitCreatorId);
97
+ if (!creatorId) {
98
+ throw new CLIError(
99
+ "Creator ID is required.",
100
+ "MISSING_FLAG",
101
+ "Pass --creator-id <id> or enroll alpha so cloud_user_id is available.",
102
+ );
103
+ }
104
+
105
+ const searchRoots = getContributionConfigSearchRoots();
106
+ const targetSkills = options.all
107
+ ? [...findInstalledSkillNames(searchRoots)]
108
+ .filter((name) => !options.prefix || name.startsWith(options.prefix))
109
+ .sort()
110
+ : options.skillName
111
+ ? [options.skillName]
112
+ : [];
113
+
114
+ if (targetSkills.length === 0) {
115
+ throw new CLIError(
116
+ options.all
117
+ ? `No installed skills found${options.prefix ? ` with prefix "${options.prefix}"` : ""}.`
118
+ : "Skill name is required.",
119
+ options.all ? "FILE_NOT_FOUND" : "MISSING_FLAG",
120
+ options.all
121
+ ? "selftune creator-contributions status"
122
+ : "selftune creator-contributions enable --skill <name>",
123
+ );
124
+ }
125
+
126
+ const result: BulkEnableResult = { written: [], skipped: [] };
127
+ for (const skillName of targetSkills) {
128
+ if (findCreatorContributionConfig(skillName, searchRoots)) {
129
+ result.skipped.push({ skill_name: skillName, reason: "already_configured" });
130
+ continue;
131
+ }
132
+ const skillPath = resolveContributionSkillPath(
133
+ skillName,
134
+ options.all ? undefined : options.explicitSkillPath,
135
+ searchRoots,
136
+ );
137
+ if (!skillPath) {
138
+ result.skipped.push({ skill_name: skillName, reason: "skill_path_not_found" });
139
+ continue;
140
+ }
141
+
142
+ writeCreatorContributionConfig({
143
+ creator_id: creatorId,
144
+ skill_name: skillName,
145
+ skill_path: skillPath,
146
+ signals: options.signals,
147
+ message: options.message,
148
+ privacy_url: options.privacyUrl,
149
+ });
150
+ result.written.push(skillName);
151
+ }
152
+
153
+ return result;
154
+ }
155
+
156
+ export async function cliMain(): Promise<void> {
157
+ const sub = process.argv[2];
158
+ const rest = process.argv.slice(3);
159
+
160
+ if (sub === "--help" || sub === "-h") {
161
+ console.log(`selftune creator-contributions — Manage creator-side contribution configs
162
+
163
+ Usage:
164
+ selftune creator-contributions
165
+ selftune creator-contributions status [--skill <name>]
166
+ selftune creator-contributions enable --skill <name> [--skill-path <path>] [--creator-id <id>] [--signals a,b,c]
167
+ selftune creator-contributions enable --all [--prefix <prefix>] [--creator-id <id>] [--signals a,b,c]
168
+ selftune creator-contributions disable --skill <name> [--skill-path <path>]
169
+
170
+ 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
174
+ selftune contribute Community export bundle`);
175
+ return;
176
+ }
177
+
178
+ const normalizedSub = sub ?? "status";
179
+
180
+ switch (normalizedSub) {
181
+ case "status": {
182
+ const { values } = parseArgs({
183
+ args: rest,
184
+ options: {
185
+ skill: { type: "string" },
186
+ help: { type: "boolean", short: "h", default: false },
187
+ },
188
+ strict: true,
189
+ });
190
+ if (values.help) {
191
+ console.log("Usage: selftune creator-contributions status [--skill <name>]");
192
+ return;
193
+ }
194
+ printStatus(values.skill);
195
+ return;
196
+ }
197
+ case "enable": {
198
+ const { values } = parseArgs({
199
+ args: rest,
200
+ options: {
201
+ skill: { type: "string" },
202
+ all: { type: "boolean", default: false },
203
+ prefix: { type: "string" },
204
+ "skill-path": { type: "string" },
205
+ "creator-id": { type: "string" },
206
+ signals: { type: "string", default: "trigger,grade,miss_category" },
207
+ message: { type: "string" },
208
+ "privacy-url": { type: "string" },
209
+ help: { type: "boolean", short: "h", default: false },
210
+ },
211
+ strict: true,
212
+ });
213
+ if (values.help) {
214
+ console.log(
215
+ "Usage: selftune creator-contributions enable (--skill <name> [--skill-path <path>] | --all [--prefix <prefix>]) [--creator-id <id>]",
216
+ );
217
+ return;
218
+ }
219
+
220
+ if (!values.all && !values.skill?.trim()) {
221
+ throw new CLIError(
222
+ "Pass either --skill <name> or --all.",
223
+ "MISSING_FLAG",
224
+ "selftune creator-contributions enable --skill <name>",
225
+ );
226
+ }
227
+
228
+ const signals = (values.signals ?? "trigger,grade,miss_category")
229
+ .split(",")
230
+ .map((signal) => signal.trim())
231
+ .filter(Boolean);
232
+ const outcome = enableCreatorContributionConfigs({
233
+ skillName: values.skill?.trim(),
234
+ all: values.all,
235
+ prefix: values.prefix?.trim(),
236
+ explicitSkillPath: values["skill-path"],
237
+ explicitCreatorId: values["creator-id"],
238
+ signals,
239
+ message: values.message,
240
+ privacyUrl: values["privacy-url"],
241
+ });
242
+ if (values.all) {
243
+ console.log(
244
+ `Enabled creator contribution config for ${outcome.written.length} skills${values.prefix ? ` with prefix "${values.prefix}"` : ""}.`,
245
+ );
246
+ if (outcome.written.length > 0) {
247
+ for (const skill of outcome.written) {
248
+ const config = findCreatorContributionConfig(skill);
249
+ if (config) printConfig(config);
250
+ }
251
+ }
252
+ if (outcome.skipped.length > 0) {
253
+ console.log(
254
+ `Skipped ${outcome.skipped.length} skills: ${outcome.skipped.map((entry) => entry.skill_name).join(", ")}`,
255
+ );
256
+ }
257
+ return;
258
+ }
259
+ const skillName = values.skill!.trim();
260
+ if (outcome.written.length === 0) {
261
+ const skip = outcome.skipped[0];
262
+ if (skip?.reason === "already_configured") {
263
+ throw new CLIError(
264
+ `A creator contribution config already exists for "${skillName}".`,
265
+ "FILE_EXISTS",
266
+ "Run `selftune creator-contributions status --skill <name>` to inspect it.",
267
+ );
268
+ }
269
+ throw new CLIError(
270
+ `Could not resolve SKILL.md for "${skillName}".`,
271
+ "FILE_NOT_FOUND",
272
+ "Pass --skill-path /path/to/SKILL.md",
273
+ );
274
+ }
275
+ const config = findCreatorContributionConfig(skillName);
276
+ console.log(`Enabled creator contribution config for "${skillName}".`);
277
+ if (config) printConfig(config);
278
+ return;
279
+ }
280
+ case "disable": {
281
+ const { values } = parseArgs({
282
+ args: rest,
283
+ options: {
284
+ skill: { type: "string" },
285
+ "skill-path": { type: "string" },
286
+ help: { type: "boolean", short: "h", default: false },
287
+ },
288
+ strict: true,
289
+ });
290
+ if (values.help) {
291
+ console.log(
292
+ "Usage: selftune creator-contributions disable --skill <name> [--skill-path <path>]",
293
+ );
294
+ return;
295
+ }
296
+
297
+ const skillName = values.skill?.trim();
298
+ if (!skillName) {
299
+ throw new CLIError(
300
+ "Skill name is required.",
301
+ "MISSING_FLAG",
302
+ "selftune creator-contributions disable --skill <name>",
303
+ );
304
+ }
305
+ const skillPath = resolveContributionSkillPath(skillName, values["skill-path"]);
306
+ if (!skillPath) {
307
+ throw new CLIError(
308
+ `Could not resolve SKILL.md for "${skillName}".`,
309
+ "FILE_NOT_FOUND",
310
+ "Pass --skill-path /path/to/SKILL.md",
311
+ );
312
+ }
313
+
314
+ const removed = removeCreatorContributionConfig(skillPath);
315
+ if (!removed) {
316
+ console.log(`No creator contribution config found for "${skillName}".`);
317
+ return;
318
+ }
319
+ console.log(`Disabled creator contribution config for "${skillName}".`);
320
+ return;
321
+ }
322
+ default:
323
+ throw new CLIError(
324
+ `Unknown creator-contributions subcommand: ${normalizedSub}`,
325
+ "UNKNOWN_COMMAND",
326
+ "selftune creator-contributions --help",
327
+ );
328
+ }
329
+ }
330
+
331
+ if (import.meta.main) {
332
+ cliMain().catch(handleCLIError);
333
+ }
@@ -100,6 +100,10 @@ export interface EvolutionEntry {
100
100
  action: string;
101
101
  details: string;
102
102
  eval_snapshot?: EvalSnapshot | null;
103
+ validation_mode?: "structural_guard" | "host_replay" | "llm_judge" | null;
104
+ validation_agent?: string | null;
105
+ validation_fixture_id?: string | null;
106
+ validation_evidence_ref?: string | null;
103
107
  }
104
108
 
105
109
  export interface UnmatchedQuery {
@@ -134,6 +138,65 @@ export interface SkillSummary {
134
138
  unique_sessions: number;
135
139
  last_seen: string | null;
136
140
  has_evidence: boolean;
141
+ routing_confidence: number | null;
142
+ confidence_coverage: number;
143
+ }
144
+
145
+ // -- Autonomy-first overview types -------------------------------------------
146
+
147
+ export type AutonomyStatusLevel = "healthy" | "watching" | "needs_review" | "blocked";
148
+
149
+ export interface AutonomyStatus {
150
+ level: AutonomyStatusLevel;
151
+ summary: string;
152
+ last_run: string | null;
153
+ skills_observed: number;
154
+ pending_reviews: number;
155
+ attention_required: number;
156
+ }
157
+
158
+ export type AttentionCategory =
159
+ | "needs_review"
160
+ | "regression"
161
+ | "low_trust"
162
+ | "polluted"
163
+ | "blocked";
164
+
165
+ export interface AttentionItem {
166
+ skill_name: string;
167
+ category: AttentionCategory;
168
+ severity: "critical" | "warning" | "info";
169
+ reason: string;
170
+ recommended_action: string;
171
+ timestamp: string;
172
+ }
173
+
174
+ export type TrustBucket = "at_risk" | "improving" | "uncertain" | "stable";
175
+
176
+ export interface TrustWatchlistEntry {
177
+ skill_name: string;
178
+ bucket: TrustBucket;
179
+ trust_state: TrustState;
180
+ reason: string;
181
+ pass_rate: number | null;
182
+ checks: number;
183
+ last_seen: string | null;
184
+ }
185
+
186
+ export type DecisionKind =
187
+ | "proposal_created"
188
+ | "proposal_rejected"
189
+ | "validation_failed"
190
+ | "proposal_deployed"
191
+ | "rollback_triggered"
192
+ | "regression_found";
193
+
194
+ export interface AutonomousDecision {
195
+ timestamp: string;
196
+ kind: DecisionKind;
197
+ skill_name: string;
198
+ proposal_id?: string;
199
+ summary: string;
137
200
  }
138
201
 
139
202
  export interface OverviewPayload {
@@ -158,6 +221,11 @@ export interface OverviewResponse {
158
221
  overview: OverviewPayload;
159
222
  skills: SkillSummary[];
160
223
  version?: string;
224
+ watched_skills: string[];
225
+ autonomy_status: AutonomyStatus;
226
+ attention_queue: AttentionItem[];
227
+ trust_watchlist: TrustWatchlistEntry[];
228
+ recent_decisions: AutonomousDecision[];
161
229
  }
162
230
 
163
231
  export interface EvidenceEntry {
@@ -188,6 +256,8 @@ export interface CanonicalInvocation {
188
256
  source?: string | null;
189
257
  skill_path?: string | null;
190
258
  skill_scope?: string | null;
259
+ observation_kind?: ObservationKind;
260
+ historical_context?: HistoricalContext | null;
191
261
  }
192
262
 
193
263
  export interface PromptSample {
@@ -266,6 +336,48 @@ export interface OrchestrateRunsResponse {
266
336
  runs: OrchestrateRunReport[];
267
337
  }
268
338
 
339
+ // -- Performance analytics response -------------------------------------------
340
+
341
+ export interface AnalyticsResponse {
342
+ /** Daily pass rate trend (last 90 days, bucketed by day) */
343
+ pass_rate_trend: Array<{
344
+ date: string;
345
+ pass_rate: number;
346
+ total_checks: number;
347
+ }>;
348
+
349
+ /** Skills ranked by pass rate with trend direction */
350
+ skill_rankings: Array<{
351
+ skill_name: string;
352
+ pass_rate: number;
353
+ total_checks: number;
354
+ triggered_count: number;
355
+ }>;
356
+
357
+ /** Daily check counts for heatmap (last 84 days / 12 weeks) */
358
+ daily_activity: Array<{
359
+ date: string;
360
+ checks: number;
361
+ }>;
362
+
363
+ /** Evolution impact — before/after pass rates for deployed evolutions */
364
+ evolution_impact: Array<{
365
+ skill_name: string;
366
+ proposal_id: string;
367
+ deployed_at: string;
368
+ pass_rate_before: number;
369
+ pass_rate_after: number;
370
+ }>;
371
+
372
+ /** Aggregate summary */
373
+ summary: {
374
+ total_evolutions: number;
375
+ avg_improvement: number;
376
+ total_checks_30d: number;
377
+ active_skills: number;
378
+ };
379
+ }
380
+
269
381
  // -- Health endpoint response -------------------------------------------------
270
382
 
271
383
  export interface HealthResponse {
@@ -318,7 +430,103 @@ export interface CommitSummary {
318
430
  recent_commits: Array<{ sha: string; title: string; branch: string; timestamp: string }>;
319
431
  }
320
432
 
321
- export interface SkillReportResponse extends SkillReportPayload {
433
+ // -- Trust-oriented types for skill report ------------------------------------
434
+
435
+ export type TrustState =
436
+ | "low_sample"
437
+ | "observed"
438
+ | "watch"
439
+ | "validated"
440
+ | "deployed"
441
+ | "rolled_back";
442
+
443
+ export type ObservationKind =
444
+ | "canonical"
445
+ | "repaired_trigger"
446
+ | "repaired_contextual_miss"
447
+ | "legacy_materialized";
448
+
449
+ export type HistoricalContext = "previously_missed";
450
+
451
+ export interface ExampleRow {
452
+ timestamp: string | null;
453
+ session_id: string;
454
+ query_text: string;
455
+ triggered: boolean;
456
+ confidence: number | null;
457
+ invocation_mode: string | null;
458
+ prompt_kind: string | null;
459
+ source: string | null;
460
+ platform: string | null;
461
+ workspace_path: string | null;
462
+ query_origin: "inline_query" | "matched_prompt" | "missing";
463
+ is_system_like: boolean;
464
+ observation_kind: ObservationKind;
465
+ historical_context?: HistoricalContext | null;
466
+ }
467
+
468
+ export interface TrustFields {
469
+ trust: {
470
+ state: TrustState;
471
+ summary: string;
472
+ };
473
+ coverage: {
474
+ checks: number;
475
+ sessions: number;
476
+ workspaces: number;
477
+ first_seen: string | null;
478
+ last_seen: string | null;
479
+ };
480
+ evidence_quality: {
481
+ prompt_link_rate: number;
482
+ inline_query_rate: number;
483
+ user_prompt_rate: number;
484
+ meta_prompt_rate: number;
485
+ internal_prompt_rate: number;
486
+ no_prompt_rate: number;
487
+ system_like_rate: number;
488
+ invocation_mode_coverage: number;
489
+ confidence_coverage: number;
490
+ source_coverage: number;
491
+ scope_coverage: number;
492
+ };
493
+ routing_quality: {
494
+ missed_triggers: number;
495
+ miss_rate: number;
496
+ avg_confidence: number | null;
497
+ confidence_coverage: number;
498
+ low_confidence_rate: number | null;
499
+ };
500
+ evolution_state: {
501
+ has_evidence: boolean;
502
+ has_pending_proposals: boolean;
503
+ latest_action: string | null;
504
+ latest_timestamp: string | null;
505
+ evidence_rows: number;
506
+ evolution_rows: number;
507
+ };
508
+ data_hygiene: {
509
+ naming_variants: string[];
510
+ source_breakdown: Array<{ source: string; count: number }>;
511
+ prompt_kind_breakdown: Array<{ kind: string; count: number }>;
512
+ observation_breakdown: Array<{ kind: ObservationKind; count: number }>;
513
+ raw_checks: number;
514
+ operational_checks: number;
515
+ internal_prompt_rows: number;
516
+ internal_prompt_rate: number;
517
+ legacy_rows: number;
518
+ legacy_rate: number;
519
+ repaired_rows: number;
520
+ repaired_rate: number;
521
+ };
522
+ examples: {
523
+ good: ExampleRow[];
524
+ missed: ExampleRow[];
525
+ noisy: ExampleRow[];
526
+ };
527
+ }
528
+
529
+ export interface SkillReportResponse extends SkillReportPayload, TrustFields {
322
530
  evolution: EvolutionEntry[];
323
531
  pending_proposals: PendingProposal[];
324
532
  token_usage: {