@sanity/ailf-studio 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -301,7 +301,19 @@ interface ContentImpactItem {
301
301
  /** Provenance data as stored in Sanity */
302
302
  interface ProvenanceData {
303
303
  areas: string[];
304
+ /** How this run is treated for reporting and trend tracking (D0037). */
305
+ classification?: string;
304
306
  contextHash?: string;
307
+ /** Who/what actually invoked the run (D0037). */
308
+ executor?: {
309
+ email?: string;
310
+ githubActor?: string;
311
+ name?: string;
312
+ runId?: string;
313
+ surface?: string;
314
+ type?: string;
315
+ workflow?: string;
316
+ };
305
317
  git?: {
306
318
  branch: string;
307
319
  prNumber?: number;
@@ -309,8 +321,17 @@ interface ProvenanceData {
309
321
  sha: string;
310
322
  };
311
323
  graderModel: string;
324
+ /** Platform and CI-provider metadata (D0037). */
325
+ host?: {
326
+ arch?: string;
327
+ ci?: string;
328
+ platform?: string;
329
+ };
330
+ /** Free-form searchable tags (D0037). */
331
+ labels?: string[];
312
332
  lineage?: {
313
333
  comparedAgainst?: string;
334
+ parentJobId?: string;
314
335
  rerunOf?: string;
315
336
  };
316
337
  mode: string;
@@ -318,6 +339,11 @@ interface ProvenanceData {
318
339
  id: string;
319
340
  label: string;
320
341
  }[];
342
+ /** Team and (optionally) individual this run is attributable to (D0037). */
343
+ owner?: {
344
+ individual?: string;
345
+ team?: string;
346
+ };
321
347
  /** Identity of the pipeline run that produced this report (D0032) */
322
348
  runId: string;
323
349
  /** @deprecated Use `promptfooUrls` when available */
@@ -327,6 +353,8 @@ interface ProvenanceData {
327
353
  mode: string;
328
354
  url: string;
329
355
  }[];
356
+ /** Human-authored "why I ran this" (D0037). */
357
+ purpose?: string;
330
358
  source: {
331
359
  baseUrl: string;
332
360
  dataset?: string;
@@ -336,6 +364,11 @@ interface ProvenanceData {
336
364
  };
337
365
  targetDocuments?: string[];
338
366
  taskIds?: string[];
367
+ /** AILF/Node version metadata (D0037). */
368
+ tool?: {
369
+ ailfVersion?: string;
370
+ nodeVersion?: string;
371
+ };
339
372
  trigger: {
340
373
  callerRef?: string;
341
374
  callerRepo?: string;
@@ -364,11 +397,19 @@ interface ReportListItem {
364
397
  _id: string;
365
398
  actualScore?: number | null;
366
399
  areas: string[];
400
+ /** Run classification (D0037) — projected from provenance.classification. */
401
+ classification?: null | string;
367
402
  comparisonDelta: null | number;
368
403
  completedAt: string;
369
404
  docLift: number;
370
405
  durationMs: number;
371
406
  evaluationMode?: string | null;
407
+ /** Executor identity name (D0037). */
408
+ executorName?: null | string;
409
+ /** Origin surface of the executor (cli/studio/api) — D0037. */
410
+ executorSurface?: null | string;
411
+ /** Executor discriminator (user/system) — D0037. */
412
+ executorType?: null | string;
372
413
  git: null | {
373
414
  branch: string;
374
415
  prNumber?: number;
@@ -376,9 +417,15 @@ interface ReportListItem {
376
417
  sha: string;
377
418
  };
378
419
  improved: null | string[];
420
+ /** Free-form labels (D0037). */
421
+ labels?: null | string[];
379
422
  mode: string;
380
423
  models: string[];
381
424
  overall: number;
425
+ /** Individual attributable for the run (D0037). */
426
+ ownerIndividual?: null | string;
427
+ /** Owner team slug (D0037). */
428
+ ownerTeam?: null | string;
382
429
  /** Content release perspective ID (when evaluated with --sanity-perspective) */
383
430
  perspective?: null | string;
384
431
  promptfooUrl: null | string;
package/dist/index.js CHANGED
@@ -156,6 +156,30 @@ var RAW_EVAL_MODES = [
156
156
  ...LEGACY_EVAL_MODE_ALIASES
157
157
  ];
158
158
 
159
+ // ../shared/dist/owner-teams.js
160
+ var KNOWN_OWNER_TEAMS = [
161
+ "content-lake",
162
+ "core-docs",
163
+ "growth",
164
+ "media",
165
+ "platform",
166
+ "studio"
167
+ ];
168
+
169
+ // ../shared/dist/run-classification.js
170
+ var RUN_CLASSIFICATIONS = [
171
+ "official",
172
+ "ad-hoc",
173
+ "experimental",
174
+ "test",
175
+ "external"
176
+ ];
177
+ var RUN_EXECUTOR_SURFACES = [
178
+ "cli",
179
+ "studio",
180
+ "api"
181
+ ];
182
+
159
183
  // src/types.ts
160
184
  function formatPercent(n) {
161
185
  if (n == null) return "\u2014";
@@ -1012,11 +1036,142 @@ var reportSchema = defineType4({
1012
1036
  name: "comparedAgainst",
1013
1037
  title: "Compared Against",
1014
1038
  type: "string"
1039
+ }),
1040
+ defineField4({
1041
+ description: "API-gateway job ID that dispatched this run",
1042
+ name: "parentJobId",
1043
+ title: "Parent Job ID",
1044
+ type: "string"
1015
1045
  })
1016
1046
  ],
1017
1047
  name: "lineage",
1018
1048
  title: "Lineage",
1019
1049
  type: "object"
1050
+ }),
1051
+ defineField4({
1052
+ description: "How this run should be treated for reporting and trend tracking (D0037). Orthogonal to trigger.type \u2014 intent vs mechanism.",
1053
+ name: "classification",
1054
+ options: { list: [...RUN_CLASSIFICATIONS] },
1055
+ title: "Classification",
1056
+ type: "string"
1057
+ }),
1058
+ defineField4({
1059
+ description: "Team and (optionally) individual this run is attributable to.",
1060
+ fields: [
1061
+ defineField4({
1062
+ description: "Free-form team slug. Known values suggested but external teams may add their own.",
1063
+ name: "team",
1064
+ options: { list: [...KNOWN_OWNER_TEAMS, "unknown"] },
1065
+ title: "Team",
1066
+ type: "string"
1067
+ }),
1068
+ defineField4({
1069
+ description: "GH actor, Sanity user ID, or similar.",
1070
+ name: "individual",
1071
+ title: "Individual",
1072
+ type: "string"
1073
+ })
1074
+ ],
1075
+ name: "owner",
1076
+ title: "Owner",
1077
+ type: "object"
1078
+ }),
1079
+ defineField4({
1080
+ description: "Who or what actually invoked the run. May or may not match owner.",
1081
+ fields: [
1082
+ defineField4({
1083
+ name: "type",
1084
+ options: { list: ["user", "system"] },
1085
+ title: "Type",
1086
+ type: "string"
1087
+ }),
1088
+ defineField4({
1089
+ description: 'For system executors (e.g. "github-actions"), the system name. For user executors, the resolved name.',
1090
+ name: "name",
1091
+ title: "Name",
1092
+ type: "string"
1093
+ }),
1094
+ defineField4({
1095
+ description: "For user executors \u2014 captured from git config; omitted when AILF_CAPTURE_EMAIL=0.",
1096
+ name: "email",
1097
+ title: "Email",
1098
+ type: "string"
1099
+ }),
1100
+ defineField4({
1101
+ description: "For user executors \u2014 where the invocation originated.",
1102
+ name: "surface",
1103
+ options: { list: [...RUN_EXECUTOR_SURFACES] },
1104
+ title: "Surface",
1105
+ type: "string"
1106
+ }),
1107
+ defineField4({
1108
+ description: "For user executors \u2014 GH actor when available.",
1109
+ name: "githubActor",
1110
+ title: "GitHub Actor",
1111
+ type: "string"
1112
+ }),
1113
+ defineField4({
1114
+ description: "For system executors \u2014 workflow name.",
1115
+ name: "workflow",
1116
+ title: "Workflow",
1117
+ type: "string"
1118
+ }),
1119
+ defineField4({
1120
+ description: "For system executors \u2014 external run ID.",
1121
+ name: "runId",
1122
+ title: "Run ID",
1123
+ type: "string"
1124
+ })
1125
+ ],
1126
+ name: "executor",
1127
+ title: "Executor",
1128
+ type: "object"
1129
+ }),
1130
+ defineField4({
1131
+ description: "Human-authored reason for running. Set via AILF_PURPOSE or --purpose flag.",
1132
+ name: "purpose",
1133
+ title: "Purpose",
1134
+ type: "text"
1135
+ }),
1136
+ defineField4({
1137
+ description: "Free-form searchable tags (release IDs, regression hunts, experiments).",
1138
+ name: "labels",
1139
+ of: [{ type: "string" }],
1140
+ title: "Labels",
1141
+ type: "array"
1142
+ }),
1143
+ defineField4({
1144
+ description: "Reproducibility metadata: AILF and Node version.",
1145
+ fields: [
1146
+ defineField4({
1147
+ name: "ailfVersion",
1148
+ title: "AILF Version",
1149
+ type: "string"
1150
+ }),
1151
+ defineField4({
1152
+ name: "nodeVersion",
1153
+ title: "Node Version",
1154
+ type: "string"
1155
+ })
1156
+ ],
1157
+ name: "tool",
1158
+ title: "Tool",
1159
+ type: "object"
1160
+ }),
1161
+ defineField4({
1162
+ description: "Platform and CI-provider metadata. Hostname intentionally excluded.",
1163
+ fields: [
1164
+ defineField4({
1165
+ name: "platform",
1166
+ title: "Platform",
1167
+ type: "string"
1168
+ }),
1169
+ defineField4({ name: "arch", title: "Arch", type: "string" }),
1170
+ defineField4({ name: "ci", title: "CI Provider", type: "string" })
1171
+ ],
1172
+ name: "host",
1173
+ title: "Host",
1174
+ type: "object"
1020
1175
  })
1021
1176
  ],
1022
1177
  group: ["main", "all-fields"],
@@ -3909,6 +4064,10 @@ var latestReportsQuery = (
3909
4064
  *[_type == "${REPORT_TYPE}"
3910
4065
  ${filterSourceClause("$source")}
3911
4066
  ${filterModeClause("$mode")}
4067
+ ${filterClassificationClause("$classification")}
4068
+ ${filterOwnerTeamClause("$ownerTeam")}
4069
+ ${filterExecutorSurfaceClause("$executorSurface")}
4070
+ ${filterLabelsClause("$labels")}
3912
4071
  ] | order(completedAt desc) [0...$limit] {
3913
4072
  _id,
3914
4073
  reportId,
@@ -3921,6 +4080,13 @@ var latestReportsQuery = (
3921
4080
  "areas": provenance.areas,
3922
4081
  "models": provenance.models[].label,
3923
4082
  "trigger": provenance.trigger.type,
4083
+ "classification": provenance.classification,
4084
+ "ownerTeam": provenance.owner.team,
4085
+ "ownerIndividual": provenance.owner.individual,
4086
+ "executorType": provenance.executor.type,
4087
+ "executorName": provenance.executor.name,
4088
+ "executorSurface": provenance.executor.surface,
4089
+ "labels": provenance.labels,
3924
4090
  "perspective": provenance.source.perspective,
3925
4091
  "targetDocuments": provenance.targetDocuments,
3926
4092
  "git": provenance.git,
@@ -4160,12 +4326,48 @@ var distinctTriggersQuery = (
4160
4326
  array::unique(*[_type == "${REPORT_TYPE}"].provenance.trigger.type)
4161
4327
  `
4162
4328
  );
4329
+ var distinctClassificationsQuery = (
4330
+ /* groq */
4331
+ `
4332
+ array::unique(*[_type == "${REPORT_TYPE}" && defined(provenance.classification)].provenance.classification)
4333
+ `
4334
+ );
4335
+ var distinctOwnerTeamsQuery = (
4336
+ /* groq */
4337
+ `
4338
+ array::unique(*[_type == "${REPORT_TYPE}" && defined(provenance.owner.team)].provenance.owner.team)
4339
+ `
4340
+ );
4341
+ var distinctExecutorSurfacesQuery = (
4342
+ /* groq */
4343
+ `
4344
+ array::unique(*[_type == "${REPORT_TYPE}" && defined(provenance.executor.surface)].provenance.executor.surface)
4345
+ `
4346
+ );
4347
+ var distinctLabelsQuery = (
4348
+ /* groq */
4349
+ `
4350
+ array::unique(*[_type == "${REPORT_TYPE}" && defined(provenance.labels)].provenance.labels[])
4351
+ `
4352
+ );
4163
4353
  function filterModeClause(param) {
4164
4354
  return `&& (${param} == null || provenance.mode == ${param})`;
4165
4355
  }
4166
4356
  function filterSourceClause(param) {
4167
4357
  return `&& (${param} == null || provenance.source.name == ${param})`;
4168
4358
  }
4359
+ function filterClassificationClause(param) {
4360
+ return `&& (${param} == null || provenance.classification == ${param})`;
4361
+ }
4362
+ function filterOwnerTeamClause(param) {
4363
+ return `&& (${param} == null || provenance.owner.team == ${param})`;
4364
+ }
4365
+ function filterExecutorSurfaceClause(param) {
4366
+ return `&& (${param} == null || provenance.executor.surface == ${param})`;
4367
+ }
4368
+ function filterLabelsClause(param) {
4369
+ return `&& (${param} == null || count(${param}) == 0 || count((provenance.labels[])[@ in ${param}]) > 0)`;
4370
+ }
4169
4371
 
4170
4372
  // src/components/PageBlurb.tsx
4171
4373
  import { Card as Card5, Stack as Stack7, Text as Text8 } from "@sanity/ui";
@@ -5058,78 +5260,146 @@ function FilterBar({
5058
5260
  trigger,
5059
5261
  triggers,
5060
5262
  onTriggerChange,
5263
+ classification,
5264
+ classifications,
5265
+ onClassificationChange,
5266
+ ownerTeam,
5267
+ ownerTeams,
5268
+ onOwnerTeamChange,
5269
+ executorSurface,
5270
+ executorSurfaces,
5271
+ onExecutorSurfaceChange,
5272
+ presets,
5273
+ activePreset,
5274
+ onPresetSelect,
5061
5275
  onReset,
5062
5276
  filteredCount,
5063
5277
  totalCount
5064
5278
  }) {
5065
- const hasActiveFilters = query.trim() !== "" || mode !== null || source !== null || trigger !== null;
5279
+ const hasActiveFilters = query.trim() !== "" || mode !== null || source !== null || trigger !== null || classification !== null || ownerTeam !== null || executorSurface !== null;
5066
5280
  const handleQueryChange = useCallback11(
5067
5281
  (e) => {
5068
5282
  onQueryChange(e.currentTarget.value);
5069
5283
  },
5070
5284
  [onQueryChange]
5071
5285
  );
5072
- return /* @__PURE__ */ jsxs13("div", { style: BAR_STYLE, children: [
5073
- /* @__PURE__ */ jsx17("div", { style: { maxWidth: 320, minWidth: 140, flex: "1 1 200px" }, children: /* @__PURE__ */ jsx17(
5074
- TextInput2,
5075
- {
5076
- fontSize: 2,
5077
- icon: SearchIcon3,
5078
- onChange: handleQueryChange,
5079
- placeholder: "Search reports...",
5080
- value: query
5081
- }
5082
- ) }),
5083
- modes.length > 0 && /* @__PURE__ */ jsx17(
5084
- PillGroup,
5085
- {
5086
- label: "Mode",
5087
- onChange: onModeChange,
5088
- options: modes,
5089
- value: mode
5090
- }
5091
- ),
5092
- sources.length > 0 && /* @__PURE__ */ jsx17(
5093
- PillGroup,
5094
- {
5095
- label: "Source",
5096
- onChange: onSourceChange,
5097
- options: sources,
5098
- value: source
5099
- }
5100
- ),
5101
- triggers.length > 0 && /* @__PURE__ */ jsx17(
5102
- PillGroup,
5103
- {
5104
- label: "Trigger",
5105
- onChange: onTriggerChange,
5106
- options: triggers,
5107
- value: trigger
5108
- }
5109
- ),
5110
- /* @__PURE__ */ jsx17("div", { style: { flex: "1 0 0px" } }),
5111
- hasActiveFilters && /* @__PURE__ */ jsxs13(
5112
- "button",
5113
- {
5114
- onClick: onReset,
5115
- style: {
5116
- ...PILL_BASE,
5117
- alignItems: "center",
5118
- background: "transparent",
5119
- color: "var(--card-muted-fg-color)",
5120
- display: "inline-flex",
5121
- gap: 4
5286
+ return /* @__PURE__ */ jsxs13(Flex9, { direction: "column", children: [
5287
+ presets.length > 0 && /* @__PURE__ */ jsxs13("div", { style: { ...BAR_STYLE, paddingBottom: 4 }, children: [
5288
+ /* @__PURE__ */ jsx17(
5289
+ Text15,
5290
+ {
5291
+ muted: true,
5292
+ size: 1,
5293
+ style: {
5294
+ letterSpacing: "0.05em",
5295
+ textTransform: "uppercase",
5296
+ whiteSpace: "nowrap"
5297
+ },
5298
+ weight: "semibold",
5299
+ children: "Presets"
5300
+ }
5301
+ ),
5302
+ /* @__PURE__ */ jsx17("div", { style: PILL_GROUP_STYLE, children: presets.map((preset) => /* @__PURE__ */ jsx17(
5303
+ "button",
5304
+ {
5305
+ onClick: () => onPresetSelect(preset.id === activePreset ? null : preset.id),
5306
+ style: activePreset === preset.id ? PILL_ACTIVE : PILL_INACTIVE,
5307
+ title: preset.description,
5308
+ type: "button",
5309
+ children: preset.label
5122
5310
  },
5123
- type: "button",
5124
- children: [
5125
- /* @__PURE__ */ jsx17(ResetIcon, { style: { fontSize: 14 } }),
5126
- "Reset"
5127
- ]
5128
- }
5129
- ),
5130
- /* @__PURE__ */ jsxs13(Flex9, { align: "center", gap: 2, shrink: 0, children: [
5131
- /* @__PURE__ */ jsx17(ThListIcon, { style: { opacity: 0.5 } }),
5132
- /* @__PURE__ */ jsx17(Text15, { muted: true, size: 2, children: filteredCount === totalCount ? `${totalCount} of ${totalCount}` : `${filteredCount} of ${totalCount}` })
5311
+ preset.id
5312
+ )) })
5313
+ ] }),
5314
+ /* @__PURE__ */ jsxs13("div", { style: BAR_STYLE, children: [
5315
+ /* @__PURE__ */ jsx17("div", { style: { maxWidth: 320, minWidth: 140, flex: "1 1 200px" }, children: /* @__PURE__ */ jsx17(
5316
+ TextInput2,
5317
+ {
5318
+ fontSize: 2,
5319
+ icon: SearchIcon3,
5320
+ onChange: handleQueryChange,
5321
+ placeholder: "Search reports...",
5322
+ value: query
5323
+ }
5324
+ ) }),
5325
+ classifications.length > 0 && /* @__PURE__ */ jsx17(
5326
+ PillGroup,
5327
+ {
5328
+ label: "Class",
5329
+ onChange: onClassificationChange,
5330
+ options: classifications,
5331
+ value: classification
5332
+ }
5333
+ ),
5334
+ ownerTeams.length > 0 && /* @__PURE__ */ jsx17(
5335
+ PillGroup,
5336
+ {
5337
+ label: "Team",
5338
+ onChange: onOwnerTeamChange,
5339
+ options: ownerTeams,
5340
+ value: ownerTeam
5341
+ }
5342
+ ),
5343
+ modes.length > 0 && /* @__PURE__ */ jsx17(
5344
+ PillGroup,
5345
+ {
5346
+ label: "Mode",
5347
+ onChange: onModeChange,
5348
+ options: modes,
5349
+ value: mode
5350
+ }
5351
+ ),
5352
+ sources.length > 0 && /* @__PURE__ */ jsx17(
5353
+ PillGroup,
5354
+ {
5355
+ label: "Source",
5356
+ onChange: onSourceChange,
5357
+ options: sources,
5358
+ value: source
5359
+ }
5360
+ ),
5361
+ triggers.length > 0 && /* @__PURE__ */ jsx17(
5362
+ PillGroup,
5363
+ {
5364
+ label: "Trigger",
5365
+ onChange: onTriggerChange,
5366
+ options: triggers,
5367
+ value: trigger
5368
+ }
5369
+ ),
5370
+ executorSurfaces.length > 0 && /* @__PURE__ */ jsx17(
5371
+ PillGroup,
5372
+ {
5373
+ label: "Surface",
5374
+ onChange: onExecutorSurfaceChange,
5375
+ options: executorSurfaces,
5376
+ value: executorSurface
5377
+ }
5378
+ ),
5379
+ /* @__PURE__ */ jsx17("div", { style: { flex: "1 0 0px" } }),
5380
+ hasActiveFilters && /* @__PURE__ */ jsxs13(
5381
+ "button",
5382
+ {
5383
+ onClick: onReset,
5384
+ style: {
5385
+ ...PILL_BASE,
5386
+ alignItems: "center",
5387
+ background: "transparent",
5388
+ color: "var(--card-muted-fg-color)",
5389
+ display: "inline-flex",
5390
+ gap: 4
5391
+ },
5392
+ type: "button",
5393
+ children: [
5394
+ /* @__PURE__ */ jsx17(ResetIcon, { style: { fontSize: 14 } }),
5395
+ "Reset"
5396
+ ]
5397
+ }
5398
+ ),
5399
+ /* @__PURE__ */ jsxs13(Flex9, { align: "center", gap: 2, shrink: 0, children: [
5400
+ /* @__PURE__ */ jsx17(ThListIcon, { style: { opacity: 0.5 } }),
5401
+ /* @__PURE__ */ jsx17(Text15, { muted: true, size: 2, children: filteredCount === totalCount ? `${totalCount} of ${totalCount}` : `${filteredCount} of ${totalCount}` })
5402
+ ] })
5133
5403
  ] })
5134
5404
  ] });
5135
5405
  }
@@ -5861,6 +6131,32 @@ function ColHeader({
5861
6131
 
5862
6132
  // src/components/LatestReports.tsx
5863
6133
  import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
6134
+ var MY_TEAM_STORAGE_KEY = "ailf:studio:myTeam";
6135
+ var PRESETS = [
6136
+ {
6137
+ description: "classification=official & trigger=scheduled & owner.team=core-docs",
6138
+ id: "core-docs-scheduled",
6139
+ label: "Core docs scheduled series"
6140
+ },
6141
+ {
6142
+ description: "owner.team equals your remembered team (first use will prompt for the slug)",
6143
+ id: "my-team",
6144
+ label: "My team's runs"
6145
+ }
6146
+ ];
6147
+ function presetFilters(preset, myTeam) {
6148
+ if (preset === "core-docs-scheduled") {
6149
+ return {
6150
+ classification: "official",
6151
+ ownerTeam: "core-docs",
6152
+ trigger: "scheduled"
6153
+ };
6154
+ }
6155
+ if (preset === "my-team" && myTeam) {
6156
+ return { classification: null, ownerTeam: myTeam, trigger: null };
6157
+ }
6158
+ return null;
6159
+ }
5864
6160
  function readParam(params, key) {
5865
6161
  if (!params) return null;
5866
6162
  const pair = params.find(([k]) => k === key);
@@ -5872,8 +6168,29 @@ function buildSearchParams(filters) {
5872
6168
  if (filters.mode) params.push(["mode", filters.mode]);
5873
6169
  if (filters.source) params.push(["source", filters.source]);
5874
6170
  if (filters.trigger) params.push(["trigger", filters.trigger]);
6171
+ if (filters.classification)
6172
+ params.push(["classification", filters.classification]);
6173
+ if (filters.ownerTeam) params.push(["ownerTeam", filters.ownerTeam]);
6174
+ if (filters.executorSurface)
6175
+ params.push(["executorSurface", filters.executorSurface]);
6176
+ if (filters.preset) params.push(["preset", filters.preset]);
5875
6177
  return params;
5876
6178
  }
6179
+ function readStoredTeam() {
6180
+ if (typeof window === "undefined") return null;
6181
+ try {
6182
+ return window.localStorage.getItem(MY_TEAM_STORAGE_KEY);
6183
+ } catch {
6184
+ return null;
6185
+ }
6186
+ }
6187
+ function writeStoredTeam(team) {
6188
+ if (typeof window === "undefined") return;
6189
+ try {
6190
+ window.localStorage.setItem(MY_TEAM_STORAGE_KEY, team);
6191
+ } catch {
6192
+ }
6193
+ }
5877
6194
  function LatestReports({
5878
6195
  onSelectReport,
5879
6196
  pageSize = 20
@@ -5894,15 +6211,40 @@ function LatestReports({
5894
6211
  const [trigger, setTrigger] = useState10(
5895
6212
  () => readParam(urlParams, "trigger")
5896
6213
  );
6214
+ const [classification, setClassification] = useState10(
6215
+ () => readParam(urlParams, "classification")
6216
+ );
6217
+ const [ownerTeam, setOwnerTeam] = useState10(
6218
+ () => readParam(urlParams, "ownerTeam")
6219
+ );
6220
+ const [executorSurface, setExecutorSurface] = useState10(
6221
+ () => readParam(urlParams, "executorSurface")
6222
+ );
6223
+ const [activePreset, setActivePreset] = useState10(
6224
+ () => readParam(urlParams, "preset")
6225
+ );
5897
6226
  useEffect6(() => {
5898
6227
  const params = buildSearchParams({
5899
- q: searchQuery,
6228
+ classification,
6229
+ executorSurface,
5900
6230
  mode,
6231
+ ownerTeam,
6232
+ preset: activePreset,
6233
+ q: searchQuery,
5901
6234
  source,
5902
6235
  trigger
5903
6236
  });
5904
6237
  router.navigate({ ...router.state, _searchParams: params });
5905
- }, [searchQuery, mode, source, trigger]);
6238
+ }, [
6239
+ searchQuery,
6240
+ mode,
6241
+ source,
6242
+ trigger,
6243
+ classification,
6244
+ ownerTeam,
6245
+ executorSurface,
6246
+ activePreset
6247
+ ]);
5906
6248
  const [initialLoading, setInitialLoading] = useState10(true);
5907
6249
  const [loadingMore, setLoadingMore] = useState10(false);
5908
6250
  const [reports, setReports] = useState10([]);
@@ -5910,6 +6252,9 @@ function LatestReports({
5910
6252
  const [modes, setModes] = useState10([]);
5911
6253
  const [sources, setSources] = useState10([]);
5912
6254
  const [triggers, setTriggers] = useState10([]);
6255
+ const [classifications, setClassifications] = useState10([]);
6256
+ const [ownerTeams, setOwnerTeams] = useState10([]);
6257
+ const [executorSurfaces, setExecutorSurfaces] = useState10([]);
5913
6258
  const [sort, setSort] = useState10({
5914
6259
  direction: "desc",
5915
6260
  field: "date"
@@ -5917,8 +6262,12 @@ function LatestReports({
5917
6262
  useEffect6(() => {
5918
6263
  setHasMore(true);
5919
6264
  client.fetch(latestReportsQuery, {
6265
+ classification,
6266
+ executorSurface,
6267
+ labels: null,
5920
6268
  limit: pageSize,
5921
6269
  mode,
6270
+ ownerTeam,
5922
6271
  source
5923
6272
  }).then((data) => {
5924
6273
  const items = data ?? [];
@@ -5930,14 +6279,26 @@ function LatestReports({
5930
6279
  setHasMore(false);
5931
6280
  setInitialLoading(false);
5932
6281
  });
5933
- }, [client, pageSize, mode, source]);
6282
+ }, [
6283
+ client,
6284
+ pageSize,
6285
+ mode,
6286
+ source,
6287
+ classification,
6288
+ ownerTeam,
6289
+ executorSurface
6290
+ ]);
5934
6291
  const handleLoadMore = useCallback14(() => {
5935
6292
  if (loadingMore || !hasMore) return;
5936
6293
  setLoadingMore(true);
5937
6294
  const nextLimit = reports.length + pageSize;
5938
6295
  client.fetch(latestReportsQuery, {
6296
+ classification,
6297
+ executorSurface,
6298
+ labels: null,
5939
6299
  limit: nextLimit,
5940
6300
  mode,
6301
+ ownerTeam,
5941
6302
  source
5942
6303
  }).then((data) => {
5943
6304
  const items = data ?? [];
@@ -5947,11 +6308,25 @@ function LatestReports({
5947
6308
  }).catch(() => {
5948
6309
  setLoadingMore(false);
5949
6310
  });
5950
- }, [client, hasMore, loadingMore, mode, pageSize, reports.length, source]);
6311
+ }, [
6312
+ client,
6313
+ hasMore,
6314
+ loadingMore,
6315
+ mode,
6316
+ pageSize,
6317
+ reports.length,
6318
+ source,
6319
+ classification,
6320
+ ownerTeam,
6321
+ executorSurface
6322
+ ]);
5951
6323
  useEffect6(() => {
5952
6324
  client.fetch(distinctModesQuery).then((data) => setModes((data ?? []).sort())).catch(() => setModes([]));
5953
6325
  client.fetch(distinctSourcesQuery).then((data) => setSources((data ?? []).sort())).catch(() => setSources([]));
5954
6326
  client.fetch(distinctTriggersQuery).then((data) => setTriggers((data ?? []).sort())).catch(() => setTriggers([]));
6327
+ client.fetch(distinctClassificationsQuery).then((data) => setClassifications((data ?? []).sort())).catch(() => setClassifications([]));
6328
+ client.fetch(distinctOwnerTeamsQuery).then((data) => setOwnerTeams((data ?? []).sort())).catch(() => setOwnerTeams([]));
6329
+ client.fetch(distinctExecutorSurfacesQuery).then((data) => setExecutorSurfaces((data ?? []).sort())).catch(() => setExecutorSurfaces([]));
5955
6330
  }, [client]);
5956
6331
  const filteredReports = useMemo5(() => {
5957
6332
  let result = reports;
@@ -6000,25 +6375,65 @@ function LatestReports({
6000
6375
  setMode(null);
6001
6376
  setSource(null);
6002
6377
  setTrigger(null);
6378
+ setClassification(null);
6379
+ setOwnerTeam(null);
6380
+ setExecutorSurface(null);
6381
+ setActivePreset(null);
6382
+ }, []);
6383
+ const handlePresetSelect = useCallback14((presetId) => {
6384
+ if (!presetId) {
6385
+ setActivePreset(null);
6386
+ return;
6387
+ }
6388
+ let team = readStoredTeam();
6389
+ if (presetId === "my-team" && !team) {
6390
+ if (typeof window === "undefined") return;
6391
+ const prompted = window.prompt(
6392
+ 'Which team slug do you want to filter by? (e.g. "core-docs", "studio") \u2014 saved to this browser only.',
6393
+ ""
6394
+ );
6395
+ const trimmed = prompted?.trim();
6396
+ if (!trimmed) return;
6397
+ team = trimmed;
6398
+ writeStoredTeam(team);
6399
+ }
6400
+ const filters = presetFilters(presetId, team);
6401
+ if (!filters) return;
6402
+ setActivePreset(presetId);
6403
+ setClassification(filters.classification);
6404
+ setOwnerTeam(filters.ownerTeam);
6405
+ setTrigger(filters.trigger);
6003
6406
  }, []);
6004
6407
  if (initialLoading) {
6005
6408
  return /* @__PURE__ */ jsx23(Card10, { padding: 4, children: /* @__PURE__ */ jsx23(Text20, { muted: true, children: "Loading reports\u2026" }) });
6006
6409
  }
6007
- if (reports.length === 0 && !mode && !source) {
6410
+ if (reports.length === 0 && !mode && !source && !classification && !ownerTeam && !executorSurface) {
6008
6411
  return /* @__PURE__ */ jsx23(Card10, { padding: 4, children: /* @__PURE__ */ jsx23(Text20, { muted: true, children: "No reports found. Run the pipeline with --publish to create reports." }) });
6009
6412
  }
6010
6413
  return /* @__PURE__ */ jsxs18(Stack15, { space: 0, children: [
6011
6414
  /* @__PURE__ */ jsx23(
6012
6415
  FilterBar,
6013
6416
  {
6417
+ activePreset,
6418
+ classification,
6419
+ classifications,
6420
+ executorSurface,
6421
+ executorSurfaces,
6014
6422
  filteredCount: filteredReports.length,
6015
6423
  mode,
6016
6424
  modes,
6425
+ onClassificationChange: setClassification,
6426
+ onExecutorSurfaceChange: setExecutorSurface,
6017
6427
  onModeChange: setMode,
6428
+ onOwnerTeamChange: setOwnerTeam,
6429
+ onPresetSelect: handlePresetSelect,
6018
6430
  onQueryChange: setSearchQuery,
6019
6431
  onReset: handleReset,
6020
6432
  onSourceChange: setSource,
6021
6433
  onTriggerChange: setTrigger,
6434
+ ownerTeam,
6435
+ ownerTeams,
6436
+ presets: PRESETS,
6022
6437
  query: searchQuery,
6023
6438
  source,
6024
6439
  sources,
@@ -6063,7 +6478,7 @@ function LatestReports({
6063
6478
  // src/components/report-detail/ReportDetail.tsx
6064
6479
  import { ArrowLeftIcon as ArrowLeftIcon3 } from "@sanity/icons";
6065
6480
  import {
6066
- Badge as Badge9,
6481
+ Badge as Badge10,
6067
6482
  Box as Box27,
6068
6483
  Button as Button10,
6069
6484
  Flex as Flex30,
@@ -9669,13 +10084,47 @@ function SectionTitle({ children }) {
9669
10084
  }
9670
10085
 
9671
10086
  // src/components/report-detail/ProvenanceCard.tsx
9672
- import { Button as Button5, Card as Card16, Flex as Flex22, Grid as Grid4, Stack as Stack25, Text as Text33 } from "@sanity/ui";
10087
+ import {
10088
+ Badge as Badge9,
10089
+ Button as Button5,
10090
+ Card as Card16,
10091
+ Flex as Flex22,
10092
+ Grid as Grid4,
10093
+ Inline as Inline2,
10094
+ Stack as Stack25,
10095
+ Text as Text33
10096
+ } from "@sanity/ui";
9673
10097
  import { useState as useState18 } from "react";
9674
10098
  import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
9675
10099
  var TASK_IDS_INLINE_THRESHOLD = 3;
9676
10100
  function ProvenanceCard({ provenance }) {
9677
10101
  return /* @__PURE__ */ jsx37(Card16, { padding: 4, radius: 2, shadow: 1, children: /* @__PURE__ */ jsxs29(Stack25, { space: 4, children: [
9678
- /* @__PURE__ */ jsx37(Text33, { size: 3, weight: "semibold", children: "Provenance" }),
10102
+ /* @__PURE__ */ jsxs29(Flex22, { align: "center", gap: 3, wrap: "wrap", children: [
10103
+ /* @__PURE__ */ jsx37(Text33, { size: 3, weight: "semibold", children: "Provenance" }),
10104
+ provenance.classification && /* @__PURE__ */ jsx37(ClassificationBadge, { value: provenance.classification }),
10105
+ provenance.owner?.team && provenance.owner.team !== "unknown" && /* @__PURE__ */ jsxs29(Badge9, { fontSize: 1, mode: "outline", tone: "primary", children: [
10106
+ "Team: ",
10107
+ provenance.owner.team
10108
+ ] }),
10109
+ provenance.executor && /* @__PURE__ */ jsx37(ExecutorBadge, { executor: provenance.executor })
10110
+ ] }),
10111
+ provenance.purpose && /* @__PURE__ */ jsx37(Card16, { border: true, padding: 3, radius: 2, tone: "transparent", children: /* @__PURE__ */ jsxs29(Stack25, { space: 2, children: [
10112
+ /* @__PURE__ */ jsx37(
10113
+ Text33,
10114
+ {
10115
+ muted: true,
10116
+ size: 1,
10117
+ style: {
10118
+ letterSpacing: "0.05em",
10119
+ textTransform: "uppercase"
10120
+ },
10121
+ weight: "semibold",
10122
+ children: "Purpose"
10123
+ }
10124
+ ),
10125
+ /* @__PURE__ */ jsx37(Text33, { size: 2, children: provenance.purpose })
10126
+ ] }) }),
10127
+ provenance.labels && provenance.labels.length > 0 && /* @__PURE__ */ jsx37(Inline2, { space: 2, children: provenance.labels.map((label) => /* @__PURE__ */ jsx37(Badge9, { fontSize: 1, mode: "outline", tone: "default", children: label }, label)) }),
9679
10128
  /* @__PURE__ */ jsxs29(Grid4, { columns: [1, 2, 3], gap: 4, children: [
9680
10129
  /* @__PURE__ */ jsx37(Field, { label: "Mode", value: provenance.mode }),
9681
10130
  /* @__PURE__ */ jsx37(Field, { label: "Source", value: provenance.source.name }),
@@ -9702,13 +10151,71 @@ function ProvenanceCard({ provenance }) {
9702
10151
  value: provenance.contextHash.slice(0, 16) + "\u2026"
9703
10152
  }
9704
10153
  ),
10154
+ provenance.owner?.individual && /* @__PURE__ */ jsx37(
10155
+ Field,
10156
+ {
10157
+ label: "Owner (individual)",
10158
+ value: provenance.owner.individual
10159
+ }
10160
+ ),
9705
10161
  provenance.areas.length > 0 && /* @__PURE__ */ jsx37(Field, { label: "Areas", value: provenance.areas.join(", ") }),
9706
10162
  provenance.taskIds && provenance.taskIds.length > 0 && /* @__PURE__ */ jsx37(TaskIdsField, { taskIds: provenance.taskIds })
9707
10163
  ] }),
9708
10164
  provenance.git && /* @__PURE__ */ jsx37(GitInfo, { git: provenance.git }),
10165
+ /* @__PURE__ */ jsx37(ToolHostFooter, { tool: provenance.tool, host: provenance.host }),
9709
10166
  /* @__PURE__ */ jsx37(PromptfooLinks, { provenance })
9710
10167
  ] }) });
9711
10168
  }
10169
+ function ClassificationBadge({ value }) {
10170
+ const tone = classificationTone(value);
10171
+ return /* @__PURE__ */ jsx37(Badge9, { fontSize: 1, tone, children: value });
10172
+ }
10173
+ function classificationTone(value) {
10174
+ switch (value) {
10175
+ case "official":
10176
+ return "positive";
10177
+ case "external":
10178
+ return "primary";
10179
+ case "experimental":
10180
+ return "caution";
10181
+ case "test":
10182
+ return "critical";
10183
+ case "ad-hoc":
10184
+ default:
10185
+ return "default";
10186
+ }
10187
+ }
10188
+ function ExecutorBadge({
10189
+ executor
10190
+ }) {
10191
+ if (executor.type === "system") {
10192
+ const workflowSuffix = executor.workflow ? ` \xB7 ${executor.workflow}` : "";
10193
+ return /* @__PURE__ */ jsxs29(Badge9, { fontSize: 1, mode: "outline", children: [
10194
+ executor.name ?? "system",
10195
+ workflowSuffix
10196
+ ] });
10197
+ }
10198
+ const name = executor.name ?? executor.githubActor ?? "anonymous";
10199
+ const surface = executor.surface ? ` \xB7 ${executor.surface}` : "";
10200
+ return /* @__PURE__ */ jsxs29(Badge9, { fontSize: 1, mode: "outline", children: [
10201
+ name,
10202
+ surface
10203
+ ] });
10204
+ }
10205
+ function ToolHostFooter({
10206
+ tool,
10207
+ host
10208
+ }) {
10209
+ const parts = [];
10210
+ if (tool?.ailfVersion) parts.push(`ailf ${tool.ailfVersion}`);
10211
+ if (tool?.nodeVersion) parts.push(`node ${tool.nodeVersion}`);
10212
+ if (host?.platform) {
10213
+ parts.push(host.arch ? `${host.platform}/${host.arch}` : host.platform);
10214
+ }
10215
+ if (host?.ci) parts.push(`ci:${host.ci}`);
10216
+ if (parts.length === 0) return null;
10217
+ return /* @__PURE__ */ jsx37(Text33, { muted: true, size: 1, style: { fontFamily: "monospace" }, children: parts.join(" \xB7 ") });
10218
+ }
9712
10219
  function Field({
9713
10220
  label,
9714
10221
  mono,
@@ -11733,10 +12240,10 @@ function ReportDetail({
11733
12240
  HoverTip,
11734
12241
  {
11735
12242
  text: SOURCE_TIP[provenance.source.name] ?? GLOSSARY.reportMode,
11736
- children: /* @__PURE__ */ jsx54(Badge9, { mode: "outline", tone: "default", children: provenance.source.name })
12243
+ children: /* @__PURE__ */ jsx54(Badge10, { mode: "outline", tone: "default", children: provenance.source.name })
11737
12244
  }
11738
12245
  ),
11739
- /* @__PURE__ */ jsx54(HoverTip, { text: MODE_TIP2[provenance.mode] ?? GLOSSARY.reportMode, children: /* @__PURE__ */ jsx54(Badge9, { tone: "primary", children: provenance.mode }) }),
12246
+ /* @__PURE__ */ jsx54(HoverTip, { text: MODE_TIP2[provenance.mode] ?? GLOSSARY.reportMode, children: /* @__PURE__ */ jsx54(Badge10, { tone: "primary", children: provenance.mode }) }),
11740
12247
  /* @__PURE__ */ jsx54(
11741
12248
  ReportActions,
11742
12249
  {
@@ -11993,7 +12500,7 @@ import { jsx as jsx56 } from "react/jsx-runtime";
11993
12500
  import { jsx as jsx57, jsxs as jsxs41 } from "react/jsx-runtime";
11994
12501
 
11995
12502
  // src/components/report-detail/AutoComparisonCard.tsx
11996
- import { Badge as Badge10, Box as Box29, Card as Card20, Flex as Flex32, Grid as Grid5, Stack as Stack34, Text as Text44, Tooltip as Tooltip11 } from "@sanity/ui";
12503
+ import { Badge as Badge11, Box as Box29, Card as Card20, Flex as Flex32, Grid as Grid5, Stack as Stack34, Text as Text44, Tooltip as Tooltip11 } from "@sanity/ui";
11997
12504
  import { jsx as jsx58, jsxs as jsxs42 } from "react/jsx-runtime";
11998
12505
 
11999
12506
  // src/components/report-detail/OverviewStats.tsx
@@ -12007,7 +12514,7 @@ import { jsx as jsx60, jsxs as jsxs44 } from "react/jsx-runtime";
12007
12514
 
12008
12515
  // src/components/report-detail/ThreeLayerTable.tsx
12009
12516
  import React5 from "react";
12010
- import { Badge as Badge11, Card as Card21, Flex as Flex34, Stack as Stack36, Text as Text46 } from "@sanity/ui";
12517
+ import { Badge as Badge12, Card as Card21, Flex as Flex34, Stack as Stack36, Text as Text46 } from "@sanity/ui";
12011
12518
  import { jsx as jsx61, jsxs as jsxs45 } from "react/jsx-runtime";
12012
12519
 
12013
12520
  // src/components/report-detail/JudgmentDetailDrawerOutlet.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf-studio",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "AI Literacy Framework — Sanity Studio dashboard plugin",
5
5
  "type": "module",
6
6
  "license": "MIT",