@sanity/ailf 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/config/features.ts +23 -0
  2. package/config/models.ts +83 -0
  3. package/config/prompts.ts +16 -0
  4. package/config/rubrics.ts +225 -0
  5. package/config/schedules.ts +47 -0
  6. package/config/sinks.ts +37 -0
  7. package/config/sources.ts +21 -0
  8. package/config/thresholds.ts +61 -0
  9. package/dist/_vendor/ailf-core/config-helpers.d.ts +174 -0
  10. package/dist/_vendor/ailf-core/config-helpers.js +150 -0
  11. package/dist/_vendor/ailf-core/env-helper.d.ts +35 -0
  12. package/dist/_vendor/ailf-core/env-helper.js +45 -0
  13. package/dist/_vendor/ailf-core/index.d.ts +3 -0
  14. package/dist/_vendor/ailf-core/index.js +5 -0
  15. package/dist/_vendor/ailf-core/ports/context.d.ts +15 -2
  16. package/dist/_vendor/ailf-core/ports/doc-fetcher.d.ts +2 -2
  17. package/dist/_vendor/ailf-core/ports/index.d.ts +2 -1
  18. package/dist/_vendor/ailf-core/ports/mode-handler.d.ts +129 -0
  19. package/dist/_vendor/ailf-core/ports/mode-handler.js +19 -0
  20. package/dist/_vendor/ailf-core/ports/task-source.d.ts +16 -122
  21. package/dist/_vendor/ailf-core/ports/task-source.js +7 -7
  22. package/dist/_vendor/ailf-core/schemas/eval-config.d.ts +7 -2
  23. package/dist/_vendor/ailf-core/schemas/eval-config.js +7 -2
  24. package/dist/_vendor/ailf-core/schemas/pipeline-request.d.ts +8 -3
  25. package/dist/_vendor/ailf-core/schemas/pipeline-request.js +6 -1
  26. package/dist/_vendor/ailf-core/schemas/pipeline.d.ts +14 -29
  27. package/dist/_vendor/ailf-core/schemas/pipeline.js +17 -8
  28. package/dist/_vendor/ailf-core/schemas/schedules.d.ts +14 -4
  29. package/dist/_vendor/ailf-core/schemas/schedules.js +6 -2
  30. package/dist/_vendor/ailf-core/schemas/sinks.d.ts +1 -1
  31. package/dist/_vendor/ailf-core/services/comparison-formatters.js +57 -19
  32. package/dist/_vendor/ailf-core/services/index.d.ts +2 -1
  33. package/dist/_vendor/ailf-core/services/index.js +2 -1
  34. package/dist/_vendor/ailf-core/services/scoring-engine.d.ts +153 -0
  35. package/dist/_vendor/ailf-core/services/scoring-engine.js +237 -0
  36. package/dist/_vendor/ailf-core/services/scoring.d.ts +15 -2
  37. package/dist/_vendor/ailf-core/services/scoring.js +25 -15
  38. package/dist/_vendor/ailf-core/types/branded-ids.d.ts +137 -0
  39. package/dist/_vendor/ailf-core/types/branded-ids.js +136 -0
  40. package/dist/_vendor/ailf-core/types/eval-mode-config.d.ts +150 -0
  41. package/dist/_vendor/ailf-core/types/eval-mode-config.js +24 -0
  42. package/dist/_vendor/ailf-core/types/generalized-task.d.ts +319 -0
  43. package/dist/_vendor/ailf-core/types/generalized-task.js +13 -0
  44. package/dist/_vendor/ailf-core/types/index.d.ts +45 -81
  45. package/dist/_vendor/ailf-core/types/index.js +8 -1
  46. package/dist/_vendor/ailf-core/types/plugin-registry.d.ts +202 -0
  47. package/dist/_vendor/ailf-core/types/plugin-registry.js +132 -0
  48. package/dist/_vendor/ailf-core/types/storage-schema.d.ts +199 -0
  49. package/dist/_vendor/ailf-core/types/storage-schema.js +39 -0
  50. package/dist/_vendor/ailf-core/types/task-graph.d.ts +86 -0
  51. package/dist/_vendor/ailf-core/types/task-graph.js +20 -0
  52. package/dist/_vendor/ailf-core/types/trace.d.ts +118 -0
  53. package/dist/_vendor/ailf-core/types/trace.js +18 -0
  54. package/dist/_vendor/ailf-core/types/variable-envelope.d.ts +80 -0
  55. package/dist/_vendor/ailf-core/types/variable-envelope.js +16 -0
  56. package/dist/_vendor/ailf-shared/dimension-names.d.ts +5 -18
  57. package/dist/_vendor/ailf-shared/dimension-names.js +6 -24
  58. package/dist/_vendor/ailf-shared/eval-modes.d.ts +38 -6
  59. package/dist/_vendor/ailf-shared/eval-modes.js +26 -2
  60. package/dist/_vendor/ailf-shared/index.d.ts +0 -1
  61. package/dist/_vendor/ailf-shared/index.js +0 -1
  62. package/dist/adapters/api-client/build-request.js +14 -13
  63. package/dist/adapters/config-sources/file-config-adapter.d.ts +20 -11
  64. package/dist/adapters/config-sources/file-config-adapter.js +38 -12
  65. package/dist/adapters/config-sources/index.d.ts +2 -0
  66. package/dist/adapters/config-sources/index.js +1 -0
  67. package/dist/adapters/config-sources/ts-config-loader.d.ts +59 -0
  68. package/dist/adapters/config-sources/ts-config-loader.js +133 -0
  69. package/dist/adapters/doc-fetchers/sanity-doc-fetcher.d.ts +3 -2
  70. package/dist/adapters/doc-fetchers/sanity-doc-fetcher.js +7 -2
  71. package/dist/adapters/task-sources/composite-task-source.d.ts +3 -3
  72. package/dist/adapters/task-sources/composite-task-source.js +1 -1
  73. package/dist/adapters/task-sources/content-lake-task-source.d.ts +7 -6
  74. package/dist/adapters/task-sources/content-lake-task-source.js +22 -23
  75. package/dist/adapters/task-sources/index.d.ts +1 -0
  76. package/dist/adapters/task-sources/index.js +1 -0
  77. package/dist/adapters/task-sources/repo-task-source.d.ts +4 -4
  78. package/dist/adapters/task-sources/repo-task-source.js +69 -16
  79. package/dist/adapters/task-sources/task-file-loader.d.ts +64 -0
  80. package/dist/adapters/task-sources/task-file-loader.js +83 -0
  81. package/dist/adapters/task-sources/yaml-task-source.d.ts +6 -6
  82. package/dist/adapters/task-sources/yaml-task-source.js +19 -16
  83. package/dist/cli.js +0 -2
  84. package/dist/commands/baseline.js +4 -1
  85. package/dist/commands/calculate-scores.js +1 -1
  86. package/dist/commands/coverage-audit.js +7 -1
  87. package/dist/commands/explain-handler.js +25 -23
  88. package/dist/commands/fetch-docs.js +3 -2
  89. package/dist/commands/generate-configs.js +1 -1
  90. package/dist/commands/interactive.js +11 -7
  91. package/dist/commands/pipeline-action.d.ts +2 -0
  92. package/dist/commands/pipeline-action.js +16 -6
  93. package/dist/commands/pipeline.d.ts +1 -0
  94. package/dist/commands/pipeline.js +4 -2
  95. package/dist/commands/pr-comment.js +1 -1
  96. package/dist/commands/publish.js +2 -2
  97. package/dist/commands/readiness-report.js +13 -6
  98. package/dist/composition-root.d.ts +1 -1
  99. package/dist/composition-root.js +67 -4
  100. package/dist/orchestration/build-app-context.js +1 -0
  101. package/dist/orchestration/build-step-sequence.js +24 -6
  102. package/dist/orchestration/steps/calculate-scores-step.js +24 -11
  103. package/dist/orchestration/steps/fetch-docs-step.js +6 -4
  104. package/dist/orchestration/steps/gap-analysis-step.js +8 -7
  105. package/dist/orchestration/steps/generate-configs-step.d.ts +16 -3
  106. package/dist/orchestration/steps/generate-configs-step.js +245 -51
  107. package/dist/orchestration/steps/grader-consistency-step.js +7 -4
  108. package/dist/orchestration/steps/mirror-repo-tasks-step.js +1 -1
  109. package/dist/orchestration/steps/readiness-step.js +5 -6
  110. package/dist/orchestration/steps/run-eval-step.d.ts +1 -2
  111. package/dist/orchestration/steps/run-eval-step.js +8 -7
  112. package/dist/pipeline/cache.d.ts +1 -1
  113. package/dist/pipeline/cache.js +36 -8
  114. package/dist/pipeline/calculate-scores.d.ts +2 -4
  115. package/dist/pipeline/calculate-scores.js +43 -113
  116. package/dist/pipeline/checks.js +2 -2
  117. package/dist/pipeline/compare.js +8 -8
  118. package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.d.ts +10 -0
  119. package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.js +288 -0
  120. package/dist/pipeline/compiler/__tests__/assertion-mapper.test.d.ts +9 -0
  121. package/dist/pipeline/compiler/__tests__/assertion-mapper.test.js +145 -0
  122. package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.d.ts +10 -0
  123. package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.js +314 -0
  124. package/dist/pipeline/compiler/__tests__/literacy-handler.test.d.ts +10 -0
  125. package/dist/pipeline/compiler/__tests__/literacy-handler.test.js +486 -0
  126. package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.d.ts +10 -0
  127. package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.js +355 -0
  128. package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.d.ts +9 -0
  129. package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.js +333 -0
  130. package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.d.ts +12 -0
  131. package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.js +210 -0
  132. package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.d.ts +7 -0
  133. package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.js +471 -0
  134. package/dist/pipeline/compiler/__tests__/scoring-bridge.test.d.ts +10 -0
  135. package/dist/pipeline/compiler/__tests__/scoring-bridge.test.js +184 -0
  136. package/dist/pipeline/compiler/__tests__/task-graph-builder.test.d.ts +8 -0
  137. package/dist/pipeline/compiler/__tests__/task-graph-builder.test.js +301 -0
  138. package/dist/pipeline/compiler/__tests__/telemetry.test.d.ts +9 -0
  139. package/dist/pipeline/compiler/__tests__/telemetry.test.js +503 -0
  140. package/dist/pipeline/compiler/assertion-mapper.d.ts +58 -0
  141. package/dist/pipeline/compiler/assertion-mapper.js +175 -0
  142. package/dist/pipeline/compiler/compiler-to-yaml.d.ts +51 -0
  143. package/dist/pipeline/compiler/compiler-to-yaml.js +222 -0
  144. package/dist/pipeline/compiler/config-loader.d.ts +56 -0
  145. package/dist/pipeline/compiler/config-loader.js +111 -0
  146. package/dist/pipeline/compiler/fixture-resolver.d.ts +41 -0
  147. package/dist/pipeline/compiler/fixture-resolver.js +113 -0
  148. package/dist/pipeline/compiler/hash.d.ts +11 -0
  149. package/dist/pipeline/compiler/hash.js +18 -0
  150. package/dist/pipeline/compiler/ignore-fields.d.ts +53 -0
  151. package/dist/pipeline/compiler/ignore-fields.js +113 -0
  152. package/dist/pipeline/compiler/index.d.ts +29 -0
  153. package/dist/pipeline/compiler/index.js +45 -0
  154. package/dist/pipeline/compiler/literacy-bridge.d.ts +102 -0
  155. package/dist/pipeline/compiler/literacy-bridge.js +172 -0
  156. package/dist/pipeline/compiler/mode-handlers/__fixtures__/agent-harness-example-tasks.d.ts +14 -0
  157. package/dist/pipeline/compiler/mode-handlers/__fixtures__/agent-harness-example-tasks.js +152 -0
  158. package/dist/pipeline/compiler/mode-handlers/__fixtures__/knowledge-probe-example-tasks.d.ts +32 -0
  159. package/dist/pipeline/compiler/mode-handlers/__fixtures__/knowledge-probe-example-tasks.js +176 -0
  160. package/dist/pipeline/compiler/mode-handlers/__fixtures__/mcp-example-tasks.d.ts +49 -0
  161. package/dist/pipeline/compiler/mode-handlers/__fixtures__/mcp-example-tasks.js +259 -0
  162. package/dist/pipeline/compiler/mode-handlers/agent-harness-handler.d.ts +70 -0
  163. package/dist/pipeline/compiler/mode-handlers/agent-harness-handler.js +485 -0
  164. package/dist/pipeline/compiler/mode-handlers/index.d.ts +16 -0
  165. package/dist/pipeline/compiler/mode-handlers/index.js +21 -0
  166. package/dist/pipeline/compiler/mode-handlers/knowledge-probe-handler.d.ts +76 -0
  167. package/dist/pipeline/compiler/mode-handlers/knowledge-probe-handler.js +245 -0
  168. package/dist/pipeline/compiler/mode-handlers/literacy-handler.d.ts +89 -0
  169. package/dist/pipeline/compiler/mode-handlers/literacy-handler.js +379 -0
  170. package/dist/pipeline/compiler/mode-handlers/mcp-assertions.d.ts +50 -0
  171. package/dist/pipeline/compiler/mode-handlers/mcp-assertions.js +277 -0
  172. package/dist/pipeline/compiler/mode-handlers/mcp-server-handler.d.ts +67 -0
  173. package/dist/pipeline/compiler/mode-handlers/mcp-server-handler.js +309 -0
  174. package/dist/pipeline/compiler/presets/index.d.ts +9 -0
  175. package/dist/pipeline/compiler/presets/index.js +8 -0
  176. package/dist/pipeline/compiler/presets/sanity-literacy.d.ts +45 -0
  177. package/dist/pipeline/compiler/presets/sanity-literacy.js +354 -0
  178. package/dist/pipeline/compiler/promptfoo-compiler.d.ts +96 -0
  179. package/dist/pipeline/compiler/promptfoo-compiler.js +230 -0
  180. package/dist/pipeline/compiler/provider-assembler.d.ts +39 -0
  181. package/dist/pipeline/compiler/provider-assembler.js +137 -0
  182. package/dist/pipeline/compiler/sandbox/docker-sandbox.d.ts +21 -0
  183. package/dist/pipeline/compiler/sandbox/docker-sandbox.js +136 -0
  184. package/dist/pipeline/compiler/sandbox/fixture-provisioner.d.ts +69 -0
  185. package/dist/pipeline/compiler/sandbox/fixture-provisioner.js +189 -0
  186. package/dist/pipeline/compiler/sandbox/git-worktree-sandbox.d.ts +20 -0
  187. package/dist/pipeline/compiler/sandbox/git-worktree-sandbox.js +114 -0
  188. package/dist/pipeline/compiler/sandbox/index.d.ts +10 -0
  189. package/dist/pipeline/compiler/sandbox/index.js +11 -0
  190. package/dist/pipeline/compiler/sandbox/sandbox-selector.d.ts +35 -0
  191. package/dist/pipeline/compiler/sandbox/sandbox-selector.js +86 -0
  192. package/dist/pipeline/compiler/sandbox/sandbox-strategy.d.ts +81 -0
  193. package/dist/pipeline/compiler/sandbox/sandbox-strategy.js +15 -0
  194. package/dist/pipeline/compiler/sandbox/tempdir-sandbox.d.ts +20 -0
  195. package/dist/pipeline/compiler/sandbox/tempdir-sandbox.js +74 -0
  196. package/dist/pipeline/compiler/scoring-bridge.d.ts +49 -0
  197. package/dist/pipeline/compiler/scoring-bridge.js +114 -0
  198. package/dist/pipeline/compiler/task-graph-builder.d.ts +54 -0
  199. package/dist/pipeline/compiler/task-graph-builder.js +291 -0
  200. package/dist/pipeline/compiler/telemetry/cost-tracker.d.ts +90 -0
  201. package/dist/pipeline/compiler/telemetry/cost-tracker.js +146 -0
  202. package/dist/pipeline/compiler/telemetry/index.d.ts +14 -0
  203. package/dist/pipeline/compiler/telemetry/index.js +19 -0
  204. package/dist/pipeline/compiler/telemetry/redactor.d.ts +58 -0
  205. package/dist/pipeline/compiler/telemetry/redactor.js +222 -0
  206. package/dist/pipeline/compiler/telemetry/tool-classifier.d.ts +32 -0
  207. package/dist/pipeline/compiler/telemetry/tool-classifier.js +120 -0
  208. package/dist/pipeline/compiler/telemetry/trace-collector.d.ts +75 -0
  209. package/dist/pipeline/compiler/telemetry/trace-collector.js +297 -0
  210. package/dist/pipeline/compiler/telemetry/trace-store.d.ts +78 -0
  211. package/dist/pipeline/compiler/telemetry/trace-store.js +85 -0
  212. package/dist/pipeline/compiler/variable-resolver.d.ts +46 -0
  213. package/dist/pipeline/compiler/variable-resolver.js +115 -0
  214. package/dist/pipeline/coverage-audit.d.ts +15 -5
  215. package/dist/pipeline/coverage-audit.js +41 -22
  216. package/dist/pipeline/eval-constants.d.ts +16 -6
  217. package/dist/pipeline/eval-constants.js +25 -4
  218. package/dist/pipeline/eval-fingerprint.d.ts +2 -2
  219. package/dist/pipeline/eval-fingerprint.js +8 -9
  220. package/dist/pipeline/expand-tasks.d.ts +19 -10
  221. package/dist/pipeline/expand-tasks.js +34 -28
  222. package/dist/pipeline/gap-analysis.d.ts +1 -1
  223. package/dist/pipeline/gap-analysis.js +2 -2
  224. package/dist/pipeline/generate-configs.d.ts +22 -4
  225. package/dist/pipeline/generate-configs.js +53 -24
  226. package/dist/pipeline/grader-api.d.ts +3 -3
  227. package/dist/pipeline/grader-api.js +5 -12
  228. package/dist/pipeline/grader-compare-runner.js +20 -27
  229. package/dist/pipeline/grader-comparison.d.ts +4 -8
  230. package/dist/pipeline/grader-comparison.js +11 -17
  231. package/dist/pipeline/grader-consistency-runner.d.ts +2 -3
  232. package/dist/pipeline/grader-consistency-runner.js +16 -20
  233. package/dist/pipeline/grader-consistency.d.ts +6 -10
  234. package/dist/pipeline/grader-consistency.js +13 -32
  235. package/dist/pipeline/grader-sensitivity-runner.js +7 -5
  236. package/dist/pipeline/grader-sensitivity.d.ts +2 -6
  237. package/dist/pipeline/grader-sensitivity.js +10 -10
  238. package/dist/pipeline/grader-validate-runner.js +7 -5
  239. package/dist/pipeline/grader-validation.d.ts +2 -6
  240. package/dist/pipeline/grader-validation.js +14 -22
  241. package/dist/pipeline/map-request-to-config.js +6 -1
  242. package/dist/pipeline/mirror-repo-tasks.d.ts +6 -6
  243. package/dist/pipeline/mirror-repo-tasks.js +16 -15
  244. package/dist/pipeline/normalize-mode.d.ts +49 -0
  245. package/dist/pipeline/normalize-mode.js +64 -0
  246. package/dist/pipeline/plan.d.ts +5 -2
  247. package/dist/pipeline/plan.js +134 -78
  248. package/dist/pipeline/pr-comment.js +2 -0
  249. package/dist/pipeline/profile-resolution.d.ts +22 -14
  250. package/dist/pipeline/profile-resolution.js +41 -19
  251. package/dist/pipeline/provenance.d.ts +2 -2
  252. package/dist/pipeline/provenance.js +12 -17
  253. package/dist/pipeline/release-report.js +4 -4
  254. package/dist/pipeline/repo-threshold-evaluator.d.ts +1 -1
  255. package/dist/pipeline/repo-threshold-evaluator.js +1 -1
  256. package/dist/pipeline/rubric-loader.d.ts +20 -0
  257. package/dist/pipeline/rubric-loader.js +37 -0
  258. package/dist/pipeline/validate.d.ts +4 -4
  259. package/dist/pipeline/validate.js +64 -53
  260. package/dist/schedules/loader.js +18 -8
  261. package/dist/scripts/migrate-task-mode.d.ts +24 -0
  262. package/dist/scripts/migrate-task-mode.js +85 -0
  263. package/dist/scripts/migrate-tasks-to-content-lake.js +11 -10
  264. package/dist/scripts/validate-task-sources.d.ts +1 -1
  265. package/dist/scripts/validate-task-sources.js +15 -15
  266. package/dist/sinks/loader.js +5 -7
  267. package/dist/sources.d.ts +7 -7
  268. package/dist/sources.js +22 -24
  269. package/dist/webhook/dispatch.js +2 -1
  270. package/package.json +6 -3
  271. package/tasks/knowledge-probe/define-type-api.task.ts +55 -0
  272. package/tasks/knowledge-probe/groq-projections.task.ts +59 -0
  273. package/tasks/literacy/frameworks.task.ts +128 -0
  274. package/tasks/literacy/functions.task.ts +69 -0
  275. package/tasks/literacy/groq.task.ts +258 -0
  276. package/tasks/literacy/nextjs-live.task.ts +75 -0
  277. package/tasks/literacy/studio-setup.task.ts +131 -0
  278. package/tasks/literacy/visual-editing.task.ts +146 -0
  279. package/config/features.yaml +0 -116
  280. package/config/models.yaml +0 -116
  281. package/config/prompts.yaml +0 -75
  282. package/config/rubrics.yaml +0 -81
  283. package/config/schedules.yaml +0 -43
  284. package/config/sinks.yaml +0 -54
  285. package/config/sources.yaml +0 -51
  286. package/config/thresholds.yaml +0 -49
  287. package/dist/agent-observer/test-imports.d.ts +0 -7
  288. package/dist/agent-observer/test-imports.js +0 -185
@@ -22,7 +22,7 @@ export declare function validateConfiguration(rootDir: string): ValidationResult
22
22
  */
23
23
  export declare function validateContexts(rootDir: string): ValidationIssue[];
24
24
  /**
25
- * Check that config/features.yaml exists, parses, and conforms to the Zod schema.
25
+ * Check that config/features exists, parses, and conforms to the Zod schema.
26
26
  * Also cross-references covered features against actual task files for consistency.
27
27
  *
28
28
  * Returns warnings (not errors) if the file is missing — the feature registry
@@ -30,7 +30,7 @@ export declare function validateContexts(rootDir: string): ValidationIssue[];
30
30
  */
31
31
  export declare function validateFeaturesYaml(rootDir: string): ValidationIssue[];
32
32
  /**
33
- * Check that config/models.yaml exists, parses, has at least one model with an id
33
+ * Check that config/models exists, parses, has at least one model with an id
34
34
  * and label, and has a grader defined.
35
35
  */
36
36
  export declare function validateModelsYaml(rootDir: string): ValidationIssue[];
@@ -45,7 +45,7 @@ export declare function validateModelsYaml(rootDir: string): ValidationIssue[];
45
45
  */
46
46
  export declare function validateReferenceSolutions(rootDir: string): ValidationIssue[];
47
47
  /**
48
- * Check that config/rubrics.yaml exists, parses, and conforms to the Zod schema.
48
+ * Check that config/rubrics exists, parses, and conforms to the Zod schema.
49
49
  * Returns the set of valid template keys for cross-referencing by task
50
50
  * validation.
51
51
  */
@@ -58,7 +58,7 @@ export declare function validateRubricsYaml(rootDir: string): ValidationIssue[];
58
58
  */
59
59
  export declare function validateTaskFiles(rootDir: string): ValidationIssue[];
60
60
  /**
61
- * Check that config/thresholds.yaml exists, parses, and conforms to the Zod schema.
61
+ * Check that config/thresholds exists, parses, and conforms to the Zod schema.
62
62
  *
63
63
  * Returns warnings (not errors) if the file is missing — thresholds are
64
64
  * optional and don't block evaluation. They only activate when
@@ -10,6 +10,7 @@
10
10
  import fs from "fs";
11
11
  import path from "path";
12
12
  import { load } from "js-yaml";
13
+ import { tryLoadConfigFile } from "./compiler/config-loader.js";
13
14
  import { resolveMappings } from "./resolve-mappings.js";
14
15
  import { FeatureRegistrySchema, formatZodErrors, RubricConfigSchema, TaskFileSchema, ThresholdConfigSchema, } from "./schemas.js";
15
16
  // ---------------------------------------------------------------------------
@@ -64,7 +65,7 @@ export function validateContexts(rootDir) {
64
65
  return issues;
65
66
  }
66
67
  /**
67
- * Check that config/features.yaml exists, parses, and conforms to the Zod schema.
68
+ * Check that config/features exists, parses, and conforms to the Zod schema.
68
69
  * Also cross-references covered features against actual task files for consistency.
69
70
  *
70
71
  * Returns warnings (not errors) if the file is missing — the feature registry
@@ -73,20 +74,24 @@ export function validateContexts(rootDir) {
73
74
  export function validateFeaturesYaml(rootDir) {
74
75
  const source = "validateFeaturesYaml";
75
76
  const issues = [];
76
- const filePath = path.join(rootDir, "config", "features.yaml");
77
- if (!fs.existsSync(filePath)) {
77
+ const loaded = tryLoadConfigFile("features", rootDir);
78
+ if (!loaded) {
78
79
  // Feature registry is optional — warn, don't error
79
- issues.push(warning(source, "config/features.yaml not found — coverage audit unavailable", filePath));
80
+ issues.push(warning(source, "config/features not found — coverage audit unavailable", path.join(rootDir, "config")));
81
+ return issues;
82
+ }
83
+ // Empty features array is valid — it means features come from the preset.
84
+ // The Zod schema requires .min(1) for non-empty configs, but an empty
85
+ // stub is the expected state when the preset provides all features.
86
+ const raw = loaded.data;
87
+ if (Array.isArray(raw?.features) && raw.features.length === 0) {
80
88
  return issues;
81
89
  }
82
- const result = parseYamlFile(filePath, source);
83
- if (!result.ok)
84
- return [result.issue];
85
- const zodResult = FeatureRegistrySchema.safeParse(result.data);
90
+ const zodResult = FeatureRegistrySchema.safeParse(loaded.data);
86
91
  if (!zodResult.success) {
87
92
  const lines = formatZodErrors(zodResult.error);
88
93
  for (const line of lines) {
89
- issues.push(error(source, `config/features.yaml: ${line.trim()}`, filePath));
94
+ issues.push(error(source, `config/features: ${line.trim()}`, loaded.filePath));
90
95
  }
91
96
  return issues;
92
97
  }
@@ -95,18 +100,18 @@ export function validateFeaturesYaml(rootDir) {
95
100
  if (fs.existsSync(tasksDir)) {
96
101
  const taskFiles = new Set(fs
97
102
  .readdirSync(tasksDir)
98
- .filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"))
99
- .map((f) => f.replace(/\.(yaml|yml)$/, "")));
103
+ .filter((f) => /\.(yaml|yml|task\.ts|task\.js)$/.test(f))
104
+ .map((f) => f.replace(/\.(yaml|yml|task\.ts|task\.js)$/, "")));
100
105
  for (const feature of zodResult.data.features) {
101
106
  if (feature.status === "covered" && feature.area) {
102
107
  if (!taskFiles.has(feature.area)) {
103
- issues.push(warning(source, `Feature '${feature.id}' is status: covered with area: '${feature.area}', but tasks/${feature.area}.yaml does not exist`, filePath));
108
+ issues.push(warning(source, `Feature '${feature.id}' is status: covered with area: '${feature.area}', but tasks/${feature.area} does not exist`, loaded.filePath));
104
109
  }
105
110
  }
106
111
  // Warn if an "uncovered" feature has a matching task file
107
112
  if (feature.status === "uncovered" && feature.area) {
108
113
  if (taskFiles.has(feature.area)) {
109
- issues.push(warning(source, `Feature '${feature.id}' is status: uncovered but tasks/${feature.area}.yaml exists — consider updating status to 'covered'`, filePath));
114
+ issues.push(warning(source, `Feature '${feature.id}' is status: uncovered but tasks/${feature.area} task file exists — consider updating status to 'covered'`, loaded.filePath));
110
115
  }
111
116
  }
112
117
  }
@@ -114,7 +119,7 @@ export function validateFeaturesYaml(rootDir) {
114
119
  const ids = new Set();
115
120
  for (const feature of zodResult.data.features) {
116
121
  if (ids.has(feature.id)) {
117
- issues.push(error(source, `Duplicate feature id '${feature.id}' in config/features.yaml`, filePath));
122
+ issues.push(error(source, `Duplicate feature id '${feature.id}' in config/features`, loaded.filePath));
118
123
  }
119
124
  ids.add(feature.id);
120
125
  }
@@ -122,47 +127,57 @@ export function validateFeaturesYaml(rootDir) {
122
127
  return issues;
123
128
  }
124
129
  /**
125
- * Check that config/models.yaml exists, parses, has at least one model with an id
130
+ * Check that config/models exists, parses, has at least one model with an id
126
131
  * and label, and has a grader defined.
127
132
  */
128
133
  export function validateModelsYaml(rootDir) {
129
134
  const source = "validateModelsYaml";
130
135
  const issues = [];
131
- const filePath = path.join(rootDir, "config", "models.yaml");
132
- const result = parseYamlFile(filePath, source);
133
- if (!result.ok)
134
- return [result.issue];
135
- const { data } = result;
136
+ let data;
137
+ try {
138
+ data = loadConfigFileForValidation("models", rootDir);
139
+ }
140
+ catch {
141
+ issues.push(error(source, "config/models not found", path.join(rootDir, "config")));
142
+ return issues;
143
+ }
136
144
  if (!data || typeof data !== "object") {
137
- issues.push(error(source, "config/models.yaml did not parse to an object", filePath));
145
+ issues.push(error(source, "config/models did not parse to an object", path.join(rootDir, "config")));
138
146
  return issues;
139
147
  }
140
148
  // Check models array
141
149
  if (!Array.isArray(data.models)) {
142
- issues.push(error(source, "config/models.yaml is missing a 'models' array", filePath));
150
+ issues.push(error(source, "config/models is missing a 'models' array", path.join(rootDir, "config")));
143
151
  }
144
152
  else if (data.models.length === 0) {
145
- issues.push(error(source, "config/models.yaml has an empty 'models' array", filePath));
153
+ issues.push(error(source, "config/models has an empty 'models' array", path.join(rootDir, "config")));
146
154
  }
147
155
  else {
148
156
  for (const [i, model] of data.models.entries()) {
149
157
  if (!model.id) {
150
- issues.push(error(source, `models[${i}] is missing 'id'`, filePath));
158
+ issues.push(error(source, `models[${i}] is missing 'id'`, path.join(rootDir, "config")));
151
159
  }
152
160
  if (!model.label) {
153
- issues.push(error(source, `models[${i}] is missing 'label'`, filePath));
161
+ issues.push(error(source, `models[${i}] is missing 'label'`, path.join(rootDir, "config")));
154
162
  }
155
163
  }
156
164
  }
157
165
  // Check grader
158
166
  if (!data.grader) {
159
- issues.push(error(source, "config/models.yaml is missing a 'grader' section", filePath));
167
+ issues.push(error(source, "config/models is missing a 'grader' section", path.join(rootDir, "config")));
160
168
  }
161
169
  else if (!data.grader.id) {
162
- issues.push(error(source, "config/models.yaml grader is missing 'id'", filePath));
170
+ issues.push(error(source, "config/models grader is missing 'id'", path.join(rootDir, "config")));
163
171
  }
164
172
  return issues;
165
173
  }
174
+ /** Helper: load a config file for validation (throws on not found) */
175
+ function loadConfigFileForValidation(name, rootDir) {
176
+ const result = tryLoadConfigFile(name, rootDir);
177
+ if (!result)
178
+ throw new Error(`Config "${name}" not found`);
179
+ return result.data;
180
+ }
166
181
  // ---------------------------------------------------------------------------
167
182
  // Validators
168
183
  // ---------------------------------------------------------------------------
@@ -198,22 +213,23 @@ export function validateReferenceSolutions(rootDir) {
198
213
  return issues;
199
214
  }
200
215
  /**
201
- * Check that config/rubrics.yaml exists, parses, and conforms to the Zod schema.
216
+ * Check that config/rubrics exists, parses, and conforms to the Zod schema.
202
217
  * Returns the set of valid template keys for cross-referencing by task
203
218
  * validation.
204
219
  */
205
220
  export function validateRubricsYaml(rootDir) {
206
221
  const source = "validateRubricsYaml";
207
222
  const issues = [];
208
- const filePath = path.join(rootDir, "config", "rubrics.yaml");
209
- const result = parseYamlFile(filePath, source);
210
- if (!result.ok)
211
- return [result.issue];
212
- const zodResult = RubricConfigSchema.safeParse(result.data);
223
+ const loaded = tryLoadConfigFile("rubrics", rootDir);
224
+ if (!loaded) {
225
+ issues.push(error(source, "config/rubrics not found", path.join(rootDir, "config")));
226
+ return issues;
227
+ }
228
+ const zodResult = RubricConfigSchema.safeParse(loaded.data);
213
229
  if (!zodResult.success) {
214
230
  const lines = formatZodErrors(zodResult.error);
215
231
  for (const line of lines) {
216
- issues.push(error(source, `config/rubrics.yaml: ${line.trim()}`, filePath));
232
+ issues.push(error(source, `config/rubrics: ${line.trim()}`, loaded.filePath));
217
233
  }
218
234
  }
219
235
  return issues;
@@ -281,7 +297,7 @@ export function validateTaskFiles(rootDir) {
281
297
  issues.push(warning(source, `${file}: id is '${entry.id}' but docs path is '${vars.docs}' (expected '${expectedPath}')`, filePath));
282
298
  }
283
299
  }
284
- // Check that llm-rubric template references exist in config/rubrics.yaml
300
+ // Check that llm-rubric template references exist in config/rubrics
285
301
  const asserts = entry.assert;
286
302
  if (Array.isArray(asserts) && templateKeys.size > 0) {
287
303
  for (const a of asserts) {
@@ -300,7 +316,7 @@ export function validateTaskFiles(rootDir) {
300
316
  return issues;
301
317
  }
302
318
  /**
303
- * Check that config/thresholds.yaml exists, parses, and conforms to the Zod schema.
319
+ * Check that config/thresholds exists, parses, and conforms to the Zod schema.
304
320
  *
305
321
  * Returns warnings (not errors) if the file is missing — thresholds are
306
322
  * optional and don't block evaluation. They only activate when
@@ -309,20 +325,17 @@ export function validateTaskFiles(rootDir) {
309
325
  export function validateThresholdsYaml(rootDir) {
310
326
  const source = "validateThresholdsYaml";
311
327
  const issues = [];
312
- const filePath = path.join(rootDir, "config", "thresholds.yaml");
313
- if (!fs.existsSync(filePath)) {
328
+ const loaded = tryLoadConfigFile("thresholds", rootDir);
329
+ if (!loaded) {
314
330
  // Thresholds are optional — warn, don't error
315
- issues.push(warning(source, "config/thresholds.yaml not found — readiness gates and threshold alerts unavailable", filePath));
331
+ issues.push(warning(source, "config/thresholds not found — readiness gates and threshold alerts unavailable", path.join(rootDir, "config")));
316
332
  return issues;
317
333
  }
318
- const result = parseYamlFile(filePath, source);
319
- if (!result.ok)
320
- return [result.issue];
321
- const zodResult = ThresholdConfigSchema.safeParse(result.data);
334
+ const zodResult = ThresholdConfigSchema.safeParse(loaded.data);
322
335
  if (!zodResult.success) {
323
336
  const lines = formatZodErrors(zodResult.error);
324
337
  for (const line of lines) {
325
- issues.push(error(source, `config/thresholds.yaml: ${line.trim()}`, filePath));
338
+ issues.push(error(source, `config/thresholds: ${line.trim()}`, loaded.filePath));
326
339
  }
327
340
  return issues;
328
341
  }
@@ -332,11 +345,11 @@ export function validateThresholdsYaml(rootDir) {
332
345
  if (fs.existsSync(tasksDir)) {
333
346
  const taskFiles = new Set(fs
334
347
  .readdirSync(tasksDir)
335
- .filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"))
336
- .map((f) => f.replace(/\.(yaml|yml)$/, "")));
348
+ .filter((f) => /\.(yaml|yml|task\.ts|task\.js)$/.test(f))
349
+ .map((f) => f.replace(/\.(yaml|yml|task\.ts|task\.js)$/, "")));
337
350
  for (const areaName of Object.keys(zodResult.data.areas)) {
338
351
  if (!taskFiles.has(areaName)) {
339
- issues.push(warning(source, `config/thresholds.yaml: area override '${areaName}' has no matching tasks/${areaName}.yaml`, filePath));
352
+ issues.push(warning(source, `config/thresholds: area override '${areaName}' has no matching tasks/${areaName}`, loaded.filePath));
340
353
  }
341
354
  }
342
355
  }
@@ -352,17 +365,15 @@ function error(source, message, filePath) {
352
365
  };
353
366
  }
354
367
  /**
355
- * Load the set of valid rubric template keys from config/rubrics.yaml.
368
+ * Load the set of valid rubric template keys from config/rubrics.
356
369
  * Returns an empty set if the file is missing or invalid.
357
370
  */
358
371
  function loadTemplateKeys(rootDir) {
359
- const filePath = path.join(rootDir, "config", "rubrics.yaml");
360
- if (!fs.existsSync(filePath))
372
+ const loaded = tryLoadConfigFile("rubrics", rootDir);
373
+ if (!loaded)
361
374
  return new Set();
362
375
  try {
363
- const raw = fs.readFileSync(filePath, "utf-8");
364
- const parsed = load(raw);
365
- const templates = parsed?.templates;
376
+ const templates = loaded.data?.templates;
366
377
  if (templates && typeof templates === "object") {
367
378
  return new Set(Object.keys(templates));
368
379
  }
@@ -14,12 +14,13 @@ import { dirname, resolve } from "path";
14
14
  import { fileURLToPath } from "url";
15
15
  import { load } from "js-yaml";
16
16
  import { interpolate } from "../interpolate.js";
17
+ import { tryLoadConfigFile } from "../pipeline/compiler/config-loader.js";
17
18
  import { SchedulesFileSchema, } from "./schema.js";
18
19
  // ---------------------------------------------------------------------------
19
20
  // Paths
20
21
  // ---------------------------------------------------------------------------
21
22
  const __dirname = dirname(fileURLToPath(import.meta.url));
22
- const DEFAULT_CONFIG_PATH = resolve(__dirname, "..", "..", "config", "schedules.yaml");
23
+ const PACKAGE_ROOT = resolve(__dirname, "..", "..");
23
24
  // ---------------------------------------------------------------------------
24
25
  // Public API
25
26
  // ---------------------------------------------------------------------------
@@ -56,14 +57,23 @@ export function getEnabledSchedules(configPath) {
56
57
  * @param configPath - Override the default config file location
57
58
  * @returns Parsed and validated schedules file, or null if not found/invalid
58
59
  */
59
- export function loadSchedules(configPath = DEFAULT_CONFIG_PATH) {
60
- if (!existsSync(configPath)) {
61
- return null;
62
- }
60
+ export function loadSchedules(configPath) {
63
61
  try {
64
- const raw = readFileSync(configPath, "utf-8");
65
- const parsed = load(raw);
66
- const interpolated = interpolate(parsed);
62
+ let data;
63
+ if (configPath) {
64
+ // Explicit path override (used by tests) — read directly
65
+ if (!existsSync(configPath))
66
+ return null;
67
+ data = load(readFileSync(configPath, "utf-8"));
68
+ }
69
+ else {
70
+ // Default: use unified config loader
71
+ const loaded = tryLoadConfigFile("schedules", PACKAGE_ROOT);
72
+ if (!loaded)
73
+ return null;
74
+ data = loaded.data;
75
+ }
76
+ const interpolated = interpolate(data);
67
77
  return SchedulesFileSchema.parse(interpolated);
68
78
  }
69
79
  catch (error) {
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Migration script: Add mode: "literacy" to Content Lake task documents.
4
+ *
5
+ * Phase 7f of the Architecture Overhaul. Queries all ailf.task documents
6
+ * that lack an explicit `mode` field and patches them with
7
+ * `mode: "literacy"` (the default mode for existing tasks).
8
+ *
9
+ * Idempotent — the GROQ query only matches documents where
10
+ * `!defined(mode)`, so re-running has no effect on already-patched
11
+ * documents.
12
+ *
13
+ * Usage:
14
+ * cd packages/eval
15
+ * npx tsx src/scripts/migrate-task-mode.ts --dry-run
16
+ * npx tsx src/scripts/migrate-task-mode.ts
17
+ *
18
+ * Prerequisites:
19
+ * - SANITY_API_TOKEN with write access to the project
20
+ * - SANITY_PROJECT_ID and SANITY_DATASET configured (or defaults used)
21
+ *
22
+ * @see docs/exec-plans/architecture-overhaul/phase-7-migrate-literacy.md
23
+ */
24
+ export {};
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Migration script: Add mode: "literacy" to Content Lake task documents.
4
+ *
5
+ * Phase 7f of the Architecture Overhaul. Queries all ailf.task documents
6
+ * that lack an explicit `mode` field and patches them with
7
+ * `mode: "literacy"` (the default mode for existing tasks).
8
+ *
9
+ * Idempotent — the GROQ query only matches documents where
10
+ * `!defined(mode)`, so re-running has no effect on already-patched
11
+ * documents.
12
+ *
13
+ * Usage:
14
+ * cd packages/eval
15
+ * npx tsx src/scripts/migrate-task-mode.ts --dry-run
16
+ * npx tsx src/scripts/migrate-task-mode.ts
17
+ *
18
+ * Prerequisites:
19
+ * - SANITY_API_TOKEN with write access to the project
20
+ * - SANITY_PROJECT_ID and SANITY_DATASET configured (or defaults used)
21
+ *
22
+ * @see docs/exec-plans/architecture-overhaul/phase-7-migrate-literacy.md
23
+ */
24
+ import { config as dotenvConfig } from "dotenv";
25
+ import { existsSync } from "fs";
26
+ import { dirname, resolve } from "path";
27
+ import { fileURLToPath } from "url";
28
+ import { getSanityClient } from "../sanity/client.js";
29
+ const __dirname = dirname(fileURLToPath(import.meta.url));
30
+ const ROOT = resolve(__dirname, "..", "..");
31
+ // Load .env from repository root (same as CLI entry point)
32
+ const envPath = resolve(ROOT, "..", "..", ".env");
33
+ if (existsSync(envPath)) {
34
+ dotenvConfig({ override: true, path: envPath });
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Configuration
38
+ // ---------------------------------------------------------------------------
39
+ const DRY_RUN = process.argv.includes("--dry-run");
40
+ // ---------------------------------------------------------------------------
41
+ // Main
42
+ // ---------------------------------------------------------------------------
43
+ async function main() {
44
+ console.log("╭──────────────────────────────────────────────╮");
45
+ console.log('│ Phase 7f: Migrate task mode → "literacy" │');
46
+ console.log("╰──────────────────────────────────────────────╯");
47
+ console.log();
48
+ if (DRY_RUN) {
49
+ console.log(" 🏷️ DRY RUN — no documents will be modified.\n");
50
+ }
51
+ const client = getSanityClient({
52
+ token: process.env.AILF_REPORT_SANITY_API_TOKEN || process.env.SANITY_API_TOKEN,
53
+ });
54
+ // Step 1: Find all ailf.task documents without an explicit mode
55
+ const query = `*[_type == "ailf.task" && !defined(mode)]{ _id, title }`;
56
+ const tasksWithoutMode = await client.fetch(query);
57
+ console.log(` Found ${tasksWithoutMode.length} task(s) without an explicit mode.\n`);
58
+ if (tasksWithoutMode.length === 0) {
59
+ console.log(" ✅ Nothing to migrate — all tasks already have a mode.");
60
+ return;
61
+ }
62
+ // Step 2: Show what would be patched
63
+ for (const task of tasksWithoutMode) {
64
+ const label = task.title ?? task._id;
65
+ console.log(` • ${label} (${task._id})`);
66
+ }
67
+ console.log();
68
+ if (DRY_RUN) {
69
+ console.log(` Would patch ${tasksWithoutMode.length} document(s) with mode: "literacy".`);
70
+ console.log(" Re-run without --dry-run to apply.\n");
71
+ return;
72
+ }
73
+ // Step 3: Patch all matching documents in a transaction
74
+ const transaction = client.transaction();
75
+ for (const task of tasksWithoutMode) {
76
+ transaction.patch(task._id, (patch) => patch.set({ mode: "literacy" }));
77
+ }
78
+ const result = await transaction.commit();
79
+ console.log(` ✅ Patched ${tasksWithoutMode.length} document(s) with mode: "literacy".`);
80
+ console.log(` Transaction ID: ${result.transactionId}\n`);
81
+ }
82
+ main().catch((err) => {
83
+ console.error("Migration failed:", err);
84
+ process.exit(1);
85
+ });
@@ -138,12 +138,12 @@ function buildReferenceSolutionDoc(task, code, language) {
138
138
  _type: "ailf.referenceSolution",
139
139
  content: sourceToPortableText(code, language),
140
140
  language,
141
- title: `${task.description} — Reference Solution`,
141
+ title: `${task.title} — Reference Solution`,
142
142
  };
143
143
  }
144
144
  function buildTaskDoc(task, slugToDocId, hasReferenceSolution) {
145
145
  // Build canonical docs array with resolved references (slug refs only)
146
- const canonicalDocs = task.canonicalDocs.map((ref) => {
146
+ const canonicalDocs = (task.context?.docs ?? []).map((ref) => {
147
147
  const resolvedId = isSlugRef(ref) ? slugToDocId.get(ref.slug) : undefined;
148
148
  return {
149
149
  _key: ptKey(),
@@ -152,7 +152,7 @@ function buildTaskDoc(task, slugToDocId, hasReferenceSolution) {
152
152
  };
153
153
  });
154
154
  // Build assertions array
155
- const assertArray = task.assertions.map((a) => {
155
+ const assertArray = (task.assertions ?? []).map((a) => {
156
156
  const entry = {
157
157
  _key: ptKey(),
158
158
  type: a.type,
@@ -176,19 +176,20 @@ function buildTaskDoc(task, slugToDocId, hasReferenceSolution) {
176
176
  }
177
177
  return entry;
178
178
  });
179
+ const area = task.area ?? "";
179
180
  const doc = {
180
181
  _id: taskDocId(task.id),
181
182
  _type: "ailf.task",
182
183
  assert: assertArray,
183
184
  canonicalDocs,
184
- description: task.description,
185
- docCoverage: task.docCoverage,
185
+ description: task.title,
186
+ docCoverage: task.docCoverage ?? false,
186
187
  featureArea: {
187
- _ref: featureAreaDocId(task.featureArea),
188
+ _ref: featureAreaDocId(area),
188
189
  _type: "reference",
189
190
  },
190
191
  id: { _type: "slug", current: task.id },
191
- taskPrompt: task.taskPrompt,
192
+ taskPrompt: task.prompt?.text ?? "",
192
193
  };
193
194
  // Optional reference solution
194
195
  if (hasReferenceSolution) {
@@ -220,14 +221,14 @@ async function migrate() {
220
221
  // 1. Load all tasks from YAML
221
222
  console.log("\n1️⃣ Loading tasks from YAML...");
222
223
  const taskSource = new YamlTaskSource(ROOT);
223
- const tasks = await taskSource.loadTasks();
224
+ const tasks = (await taskSource.loadTasks()).filter((t) => t.mode === "literacy");
224
225
  console.log(` Loaded ${tasks.length} tasks`);
225
226
  // 2. Extract unique feature areas
226
- const areas = [...new Set(tasks.map((t) => t.featureArea))].sort();
227
+ const areas = [...new Set(tasks.map((t) => t.area ?? ""))].sort();
227
228
  console.log(` Found ${areas.length} feature areas: ${areas.join(", ")}`);
228
229
  // 3. Collect all canonical doc slugs for batch resolution (slug refs only)
229
230
  const allSlugs = [
230
- ...new Set(tasks.flatMap((t) => t.canonicalDocs.filter(isSlugRef).map((d) => d.slug))),
231
+ ...new Set(tasks.flatMap((t) => (t.context?.docs ?? []).filter(isSlugRef).map((d) => d.slug))),
231
232
  ];
232
233
  console.log(` Found ${allSlugs.length} unique canonical doc slugs`);
233
234
  // 4. Create Sanity client with write token
@@ -3,7 +3,7 @@
3
3
  * Validation script: Compare YamlTaskSource vs ContentLakeTaskSource
4
4
  *
5
5
  * Loads tasks from both sources and compares them field-by-field to verify
6
- * that the Content Lake migration produced identical TaskDefinition[] output.
6
+ * that the Content Lake migration produced identical LiteracyTaskDefinition[] output.
7
7
  *
8
8
  * This is Phase 3b of the tasks-as-content exec plan — parallel validation
9
9
  * before deleting YAML files.
@@ -3,7 +3,7 @@
3
3
  * Validation script: Compare YamlTaskSource vs ContentLakeTaskSource
4
4
  *
5
5
  * Loads tasks from both sources and compares them field-by-field to verify
6
- * that the Content Lake migration produced identical TaskDefinition[] output.
6
+ * that the Content Lake migration produced identical LiteracyTaskDefinition[] output.
7
7
  *
8
8
  * This is Phase 3b of the tasks-as-content exec plan — parallel validation
9
9
  * before deleting YAML files.
@@ -34,7 +34,7 @@ if (existsSync(envPath)) {
34
34
  dotenvConfig({ override: true, path: envPath });
35
35
  }
36
36
  /**
37
- * Compare two TaskDefinition arrays field-by-field.
37
+ * Compare two LiteracyTaskDefinition arrays field-by-field.
38
38
  * Returns a list of differences.
39
39
  */
40
40
  function compareTasks(yamlTasks, clTasks) {
@@ -50,26 +50,26 @@ function compareTasks(yamlTasks, clTasks) {
50
50
  if (!clTask)
51
51
  continue;
52
52
  // Compare each field
53
- compareField(diffs, id, "description", yamlTask.description, clTask.description);
54
- compareField(diffs, id, "featureArea", yamlTask.featureArea, clTask.featureArea);
53
+ compareField(diffs, id, "title", yamlTask.title, clTask.title);
54
+ compareField(diffs, id, "area", yamlTask.area, clTask.area);
55
55
  compareField(diffs, id, "docCoverage", yamlTask.docCoverage, clTask.docCoverage);
56
- // taskPrompt — normalize whitespace for comparison (YAML multiline vs CL text)
57
- const yamlPrompt = yamlTask.taskPrompt.trim();
58
- const clPrompt = clTask.taskPrompt.trim();
56
+ // prompt.text — normalize whitespace for comparison (YAML multiline vs CL text)
57
+ const yamlPrompt = (yamlTask.prompt?.text ?? "").trim();
58
+ const clPrompt = (clTask.prompt?.text ?? "").trim();
59
59
  if (yamlPrompt !== clPrompt) {
60
60
  diffs.push({
61
61
  clValue: truncate(clPrompt, 100),
62
- field: "taskPrompt",
62
+ field: "prompt.text",
63
63
  taskId: id,
64
64
  yamlValue: truncate(yamlPrompt, 100),
65
65
  });
66
66
  }
67
- // canonicalDocs — compare slug sets (slug refs only for comparison)
68
- const yamlSlugs = yamlTask.canonicalDocs
67
+ // context.docs — compare slug sets (slug refs only for comparison)
68
+ const yamlSlugs = (yamlTask.context?.docs ?? [])
69
69
  .filter(isSlugRef)
70
70
  .map((d) => d.slug)
71
71
  .sort();
72
- const clSlugs = clTask.canonicalDocs
72
+ const clSlugs = (clTask.context?.docs ?? [])
73
73
  .filter(isSlugRef)
74
74
  .map((d) => d.slug)
75
75
  .sort();
@@ -82,7 +82,7 @@ function compareTasks(yamlTasks, clTasks) {
82
82
  });
83
83
  }
84
84
  // assertions — compare type + template arrays
85
- const yamlAssertTypes = yamlTask.assertions
85
+ const yamlAssertTypes = (yamlTask.assertions ?? [])
86
86
  .map((a) => {
87
87
  const base = a.type;
88
88
  if ("template" in a)
@@ -90,7 +90,7 @@ function compareTasks(yamlTasks, clTasks) {
90
90
  return base;
91
91
  })
92
92
  .sort();
93
- const clAssertTypes = clTask.assertions
93
+ const clAssertTypes = (clTask.assertions ?? [])
94
94
  .map((a) => {
95
95
  const base = a.type;
96
96
  if ("template" in a)
@@ -130,7 +130,7 @@ async function validate() {
130
130
  // Load from YAML
131
131
  console.log("\n1️⃣ Loading from YAML (tasks/*.yaml)...");
132
132
  const yamlSource = new YamlTaskSource(ROOT);
133
- const yamlTasks = await yamlSource.loadTasks();
133
+ const yamlTasks = (await yamlSource.loadTasks()).filter((t) => t.mode === "literacy");
134
134
  console.log(` Loaded ${yamlTasks.length} tasks`);
135
135
  // Load from Content Lake (use write token since task docs may require it)
136
136
  console.log("\n2️⃣ Loading from Content Lake (ailf.task)...");
@@ -140,7 +140,7 @@ async function validate() {
140
140
  undefined,
141
141
  });
142
142
  const clSource = new ContentLakeTaskSource(client);
143
- const clTasks = await clSource.loadTasks();
143
+ const clTasks = (await clSource.loadTasks()).filter((t) => t.mode === "literacy");
144
144
  console.log(` Loaded ${clTasks.length} tasks`);
145
145
  // Compare
146
146
  console.log("\n3️⃣ Comparing...");