@velum-labs/routekit-eval-setup 1.3.2 → 1.5.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/adapters/authoring-responses-request.d.ts +5 -0
- package/dist/adapters/authoring-responses-request.js +38 -0
- package/dist/adapters/evaluation-evidence-freshness.d.ts +7 -0
- package/dist/adapters/evaluation-evidence-freshness.js +76 -0
- package/dist/adapters/git-task-history.d.ts +67 -0
- package/dist/adapters/git-task-history.js +171 -0
- package/dist/adapters/integrated-repository-history.d.ts +21 -0
- package/dist/adapters/integrated-repository-history.js +175 -0
- package/dist/adapters/repository-command-diagnostic.d.ts +8 -0
- package/dist/adapters/repository-command-diagnostic.js +46 -0
- package/dist/adapters/repository-command-evidence.d.ts +13 -0
- package/dist/adapters/repository-command-evidence.js +102 -0
- package/dist/adapters/repository-command-runner.d.ts +238 -0
- package/dist/adapters/repository-command-runner.js +1483 -0
- package/dist/adapters/repository-import-context.d.ts +47 -0
- package/dist/adapters/repository-import-context.js +469 -0
- package/dist/adapters/repository-node-test-reporter.d.ts +3 -0
- package/dist/adapters/repository-node-test-reporter.js +27 -0
- package/dist/adapters/repository-review-evidence.d.ts +39 -0
- package/dist/adapters/repository-review-evidence.js +632 -0
- package/dist/adapters/repository-seed-selection.d.ts +7 -0
- package/dist/adapters/repository-seed-selection.js +79 -0
- package/dist/adapters/repository-solution-edits.d.ts +49 -0
- package/dist/adapters/repository-solution-edits.js +136 -0
- package/dist/adapters/repository-vitest-phase-adapter.d.ts +8 -0
- package/dist/adapters/repository-vitest-phase-adapter.js +310 -0
- package/dist/adapters/repository-vitest-reporter.d.ts +24 -0
- package/dist/adapters/repository-vitest-reporter.js +314 -0
- package/dist/adapters/strict-authoring-schema.d.ts +5 -0
- package/dist/adapters/strict-authoring-schema.js +158 -0
- package/dist/adapters/test-discovery.d.ts +30 -0
- package/dist/adapters/test-discovery.js +124 -0
- package/dist/adapters/typescript-repository-index.d.ts +51 -0
- package/dist/adapters/typescript-repository-index.js +226 -0
- package/dist/agentic-capabilities-protocol.d.ts +1373 -0
- package/dist/agentic-capabilities-protocol.js +786 -0
- package/dist/case-checkpoint-store.d.ts +29 -0
- package/dist/case-checkpoint-store.js +133 -0
- package/dist/case-pipeline-protocol-v2.d.ts +184 -0
- package/dist/case-pipeline-protocol-v2.js +193 -0
- package/dist/case-pipeline-protocol.d.ts +2626 -0
- package/dist/case-pipeline-protocol.js +371 -0
- package/dist/effect-api.d.ts +74 -10
- package/dist/effect-api.js +56 -6
- package/dist/errors.d.ts +31 -0
- package/dist/errors.js +10 -0
- package/dist/eval-capability-execution-envelope.d.ts +64 -0
- package/dist/eval-capability-execution-envelope.js +98 -0
- package/dist/eval-capability-policy.d.ts +90 -0
- package/dist/eval-capability-policy.js +107 -0
- package/dist/eval-event-log.d.ts +140 -0
- package/dist/eval-event-log.js +220 -0
- package/dist/evaluation-authoring-policy.d.ts +18 -0
- package/dist/evaluation-authoring-policy.js +19 -0
- package/dist/evaluation-authoring-validation.d.ts +22 -0
- package/dist/evaluation-authoring-validation.js +72 -0
- package/dist/evaluation-evidence.d.ts +20 -0
- package/dist/evaluation-evidence.js +319 -0
- package/dist/evaluation-grader-calibration-protocol.d.ts +108 -0
- package/dist/evaluation-grader-calibration-protocol.js +80 -0
- package/dist/evaluation-grader-calibration.d.ts +18 -0
- package/dist/evaluation-grader-calibration.js +334 -0
- package/dist/evaluation-grading-policy.d.ts +24 -0
- package/dist/evaluation-grading-policy.js +54 -0
- package/dist/evaluation-proposal-policy.d.ts +4 -0
- package/dist/evaluation-proposal-policy.js +91 -0
- package/dist/evaluation-source-retrieval.d.ts +68 -0
- package/dist/evaluation-source-retrieval.js +513 -0
- package/dist/evaluation-structure-policy.d.ts +29 -0
- package/dist/evaluation-structure-policy.js +138 -0
- package/dist/index.d.ts +124 -17
- package/dist/index.js +69 -11
- package/dist/inspection.js +2 -3
- package/dist/project-artifacts.d.ts +7 -2
- package/dist/project-artifacts.js +49 -136
- package/dist/project-authoring.d.ts +66 -5
- package/dist/project-authoring.js +783 -109
- package/dist/project-contracts.d.ts +419 -84
- package/dist/project-contracts.js +160 -52
- package/dist/project-store.js +2 -1
- package/dist/project-workflow.d.ts +5 -4
- package/dist/project-workflow.js +154 -35
- package/dist/repository-adversary-protocol.d.ts +64 -0
- package/dist/repository-adversary-protocol.js +105 -0
- package/dist/repository-behavior-protocol.d.ts +188 -0
- package/dist/repository-behavior-protocol.js +202 -0
- package/dist/repository-benchmark-protocol.d.ts +487 -0
- package/dist/repository-benchmark-protocol.js +96 -0
- package/dist/repository-execution-protocol.d.ts +150 -0
- package/dist/repository-execution-protocol.js +38 -0
- package/dist/repository-fixture-instructions.d.ts +3 -0
- package/dist/repository-fixture-instructions.js +91 -0
- package/dist/repository-fixture-protocol.d.ts +79 -0
- package/dist/repository-fixture-protocol.js +79 -0
- package/dist/repository-foundry-plan-protocol.d.ts +118 -0
- package/dist/repository-foundry-plan-protocol.js +296 -0
- package/dist/repository-foundry-progress-protocol.d.ts +52 -0
- package/dist/repository-foundry-progress-protocol.js +52 -0
- package/dist/repository-improvement-protocol.d.ts +100 -0
- package/dist/repository-improvement-protocol.js +106 -0
- package/dist/repository-language-model-protocol.d.ts +43 -0
- package/dist/repository-language-model-protocol.js +146 -0
- package/dist/repository-oracle-coverage-protocol.d.ts +18 -0
- package/dist/repository-oracle-coverage-protocol.js +39 -0
- package/dist/repository-oracle-execution-binding.d.ts +27 -0
- package/dist/repository-oracle-execution-binding.js +59 -0
- package/dist/repository-oracle-protocol.d.ts +230 -0
- package/dist/repository-oracle-protocol.js +156 -0
- package/dist/repository-oracle-scope-policy.d.ts +22 -0
- package/dist/repository-oracle-scope-policy.js +92 -0
- package/dist/repository-quality-policy.d.ts +15 -0
- package/dist/repository-quality-policy.js +357 -0
- package/dist/repository-routing-benchmark-protocol.d.ts +176 -0
- package/dist/repository-routing-benchmark-protocol.js +103 -0
- package/dist/repository-routing-model-protocol.d.ts +36 -0
- package/dist/repository-routing-model-protocol.js +89 -0
- package/dist/repository-routing-plan-protocol.d.ts +112 -0
- package/dist/repository-routing-plan-protocol.js +58 -0
- package/dist/repository-routing-quality-policy.d.ts +9 -0
- package/dist/repository-routing-quality-policy.js +191 -0
- package/dist/repository-seed-qualification-progress-protocol.d.ts +205 -0
- package/dist/repository-seed-qualification-progress-protocol.js +28 -0
- package/dist/repository-semantic-calibration-protocol.d.ts +768 -0
- package/dist/repository-semantic-calibration-protocol.js +276 -0
- package/dist/repository-semantic-calibration.d.ts +163 -0
- package/dist/repository-semantic-calibration.js +581 -0
- package/dist/repository-specification-contract-facts-protocol.d.ts +224 -0
- package/dist/repository-specification-contract-facts-protocol.js +276 -0
- package/dist/repository-specification-critique-protocol.d.ts +189 -0
- package/dist/repository-specification-critique-protocol.js +103 -0
- package/dist/repository-task-family-protocol.d.ts +24 -0
- package/dist/repository-task-family-protocol.js +37 -0
- package/dist/repository-task-seed-protocol.d.ts +384 -0
- package/dist/repository-task-seed-protocol.js +236 -0
- package/dist/repository-trajectory-protocol.d.ts +20 -0
- package/dist/repository-trajectory-protocol.js +42 -0
- package/dist/service.js +1 -1
- package/dist/services/adversary/service.d.ts +64 -0
- package/dist/services/adversary/service.js +330 -0
- package/dist/services/benchmark-compiler/service.d.ts +450 -0
- package/dist/services/benchmark-compiler/service.js +9 -0
- package/dist/services/budgeted-model/service.d.ts +118 -0
- package/dist/services/budgeted-model/service.js +460 -0
- package/dist/services/case-authoring/service.d.ts +163 -0
- package/dist/services/case-authoring/service.js +1456 -0
- package/dist/services/case-finalization/service.d.ts +283 -0
- package/dist/services/case-finalization/service.js +370 -0
- package/dist/services/case-generation/service.d.ts +619 -0
- package/dist/services/case-generation/service.js +2628 -0
- package/dist/services/case-pipeline/service.d.ts +31 -0
- package/dist/services/case-pipeline/service.js +485 -0
- package/dist/services/case-pipeline-v2/service.d.ts +70 -0
- package/dist/services/case-pipeline-v2/service.js +477 -0
- package/dist/services/command-observability/service.d.ts +13 -0
- package/dist/services/command-observability/service.js +3 -0
- package/dist/services/dimension-labeling/service.d.ts +77 -0
- package/dist/services/dimension-labeling/service.js +188 -0
- package/dist/services/eval-candidate/service.d.ts +208 -0
- package/dist/services/eval-candidate/service.js +64 -0
- package/dist/services/eval-capabilities/service.d.ts +183 -0
- package/dist/services/eval-capabilities/service.js +1433 -0
- package/dist/services/eval-environment/service.d.ts +173 -0
- package/dist/services/eval-environment/service.js +127 -0
- package/dist/services/evidence-reconstruction/service.d.ts +36 -0
- package/dist/services/evidence-reconstruction/service.js +145 -0
- package/dist/services/fixture-builder/service.d.ts +62 -0
- package/dist/services/fixture-builder/service.js +36 -0
- package/dist/services/fixture-validation/service.d.ts +75 -0
- package/dist/services/fixture-validation/service.js +295 -0
- package/dist/services/foundry/service.d.ts +831 -0
- package/dist/services/foundry/service.js +442 -0
- package/dist/services/foundry-progress/service.d.ts +62 -0
- package/dist/services/foundry-progress/service.js +149 -0
- package/dist/services/foundry-v2/service.d.ts +54 -0
- package/dist/services/foundry-v2/service.js +28 -0
- package/dist/services/grounded-authoring/service.d.ts +126 -0
- package/dist/services/grounded-authoring/service.js +822 -0
- package/dist/services/historical-case/service.d.ts +722 -0
- package/dist/services/historical-case/service.js +177 -0
- package/dist/services/improvement-loop/service.d.ts +59 -0
- package/dist/services/improvement-loop/service.js +176 -0
- package/dist/services/language-model/service.d.ts +52 -0
- package/dist/services/language-model/service.js +194 -0
- package/dist/services/oracle-builder/service.d.ts +146 -0
- package/dist/services/oracle-builder/service.js +513 -0
- package/dist/services/oracle-coverage/service.d.ts +28 -0
- package/dist/services/oracle-coverage/service.js +50 -0
- package/dist/services/oracle-coverage-witness/service.d.ts +130 -0
- package/dist/services/oracle-coverage-witness/service.js +538 -0
- package/dist/services/pipeline-challenge/service.d.ts +551 -0
- package/dist/services/pipeline-challenge/service.js +427 -0
- package/dist/services/pipeline-controls/service.d.ts +130 -0
- package/dist/services/pipeline-controls/service.js +483 -0
- package/dist/services/pipeline-oracle/service.d.ts +8 -0
- package/dist/services/pipeline-oracle/service.js +256 -0
- package/dist/services/pipeline-seed/service.d.ts +298 -0
- package/dist/services/pipeline-seed/service.js +428 -0
- package/dist/services/pipeline-spec/service.d.ts +103 -0
- package/dist/services/pipeline-spec/service.js +619 -0
- package/dist/services/pipeline-tournament/service.d.ts +258 -0
- package/dist/services/pipeline-tournament/service.js +476 -0
- package/dist/services/quality-gate/service.d.ts +233 -0
- package/dist/services/quality-gate/service.js +136 -0
- package/dist/services/repository-bundle/service.d.ts +33 -0
- package/dist/services/repository-bundle/service.js +114 -0
- package/dist/services/repository-model/service.d.ts +105 -0
- package/dist/services/repository-model/service.js +250 -0
- package/dist/services/repository-public-artifact/service.d.ts +133 -0
- package/dist/services/repository-public-artifact/service.js +330 -0
- package/dist/services/routing-benchmark/service.d.ts +362 -0
- package/dist/services/routing-benchmark/service.js +96 -0
- package/dist/services/specification-critic/service.d.ts +92 -0
- package/dist/services/specification-critic/service.js +172 -0
- package/dist/services/task-family/service.d.ts +40 -0
- package/dist/services/task-family/service.js +55 -0
- package/dist/services/task-seed/service.d.ts +906 -0
- package/dist/services/task-seed/service.js +1406 -0
- package/dist/services/task-specification/service.d.ts +27 -0
- package/dist/services/task-specification/service.js +40 -0
- package/dist/services/trajectory-policy/service.d.ts +110 -0
- package/dist/services/trajectory-policy/service.js +216 -0
- package/dist/test/agentic-capabilities-protocol.test.d.ts +1 -0
- package/dist/test/agentic-capabilities-protocol.test.js +570 -0
- package/dist/test/agentic-capabilities.test.d.ts +1 -0
- package/dist/test/agentic-capabilities.test.js +1461 -0
- package/dist/test/agentic-environment.test.d.ts +1 -0
- package/dist/test/agentic-environment.test.js +213 -0
- package/dist/test/case-pipeline-foundation.test.d.ts +1 -0
- package/dist/test/case-pipeline-foundation.test.js +535 -0
- package/dist/test/case-pipeline-protocol-v2.test.d.ts +1 -0
- package/dist/test/case-pipeline-protocol-v2.test.js +124 -0
- package/dist/test/case-pipeline-v2.test.d.ts +1 -0
- package/dist/test/case-pipeline-v2.test.js +286 -0
- package/dist/test/case-pipeline.test.d.ts +1 -0
- package/dist/test/case-pipeline.test.js +851 -0
- package/dist/test/eval-capability-policy.test.d.ts +1 -0
- package/dist/test/eval-capability-policy.test.js +50 -0
- package/dist/test/eval-event-log.test.d.ts +1 -0
- package/dist/test/eval-event-log.test.js +125 -0
- package/dist/test/evaluation-evidence-freshness.test.d.ts +1 -0
- package/dist/test/evaluation-evidence-freshness.test.js +44 -0
- package/dist/test/evaluation-evidence.test.d.ts +1 -0
- package/dist/test/evaluation-evidence.test.js +230 -0
- package/dist/test/evaluation-grader-calibration.test.d.ts +1 -0
- package/dist/test/evaluation-grader-calibration.test.js +373 -0
- package/dist/test/evaluation-proposal-digest.test.d.ts +1 -0
- package/dist/test/evaluation-proposal-digest.test.js +187 -0
- package/dist/test/evaluation-source-retrieval.test.d.ts +1 -0
- package/dist/test/evaluation-source-retrieval.test.js +237 -0
- package/dist/test/evaluation-structure-policy.test.d.ts +1 -0
- package/dist/test/evaluation-structure-policy.test.js +196 -0
- package/dist/test/fixtures/repository-resource-panel.d.ts +39 -0
- package/dist/test/fixtures/repository-resource-panel.js +111 -0
- package/dist/test/fixtures/vitest-boundary-panel.d.ts +84 -0
- package/dist/test/fixtures/vitest-boundary-panel.js +120 -0
- package/dist/test/fixtures/vitest-phase-panel.d.ts +135 -0
- package/dist/test/fixtures/vitest-phase-panel.js +213 -0
- package/dist/test/fixtures/vitest-reporter-results.d.ts +76 -0
- package/dist/test/fixtures/vitest-reporter-results.js +94 -0
- package/dist/test/grounded-authoring.test.d.ts +1 -0
- package/dist/test/grounded-authoring.test.js +565 -0
- package/dist/test/integrated-repository-history.test.d.ts +1 -0
- package/dist/test/integrated-repository-history.test.js +227 -0
- package/dist/test/project-authoring.test.js +593 -43
- package/dist/test/project-workflow.test.js +419 -40
- package/dist/test/repository-authoring-artifacts.test.d.ts +1 -0
- package/dist/test/repository-authoring-artifacts.test.js +185 -0
- package/dist/test/repository-bundle.test.d.ts +1 -0
- package/dist/test/repository-bundle.test.js +52 -0
- package/dist/test/repository-case-generation.test.d.ts +1 -0
- package/dist/test/repository-case-generation.test.js +3465 -0
- package/dist/test/repository-command-diagnostic.test.d.ts +1 -0
- package/dist/test/repository-command-diagnostic.test.js +55 -0
- package/dist/test/repository-command-signals.test.d.ts +1 -0
- package/dist/test/repository-command-signals.test.js +124 -0
- package/dist/test/repository-fixture-scope-coverage.test.d.ts +1 -0
- package/dist/test/repository-fixture-scope-coverage.test.js +127 -0
- package/dist/test/repository-fixture-validation.test.d.ts +1 -0
- package/dist/test/repository-fixture-validation.test.js +362 -0
- package/dist/test/repository-foundry-progress.test.d.ts +1 -0
- package/dist/test/repository-foundry-progress.test.js +110 -0
- package/dist/test/repository-foundry-quality.test.d.ts +1 -0
- package/dist/test/repository-foundry-quality.test.js +1138 -0
- package/dist/test/repository-import-context.test.d.ts +1 -0
- package/dist/test/repository-import-context.test.js +354 -0
- package/dist/test/repository-model-authoring.test.d.ts +1 -0
- package/dist/test/repository-model-authoring.test.js +544 -0
- package/dist/test/repository-model.test.d.ts +1 -0
- package/dist/test/repository-model.test.js +2195 -0
- package/dist/test/repository-node-test-reporter.test.d.ts +1 -0
- package/dist/test/repository-node-test-reporter.test.js +104 -0
- package/dist/test/repository-oracle-concurrency.test.d.ts +1 -0
- package/dist/test/repository-oracle-concurrency.test.js +542 -0
- package/dist/test/repository-oracle-coverage-witness.test.d.ts +1 -0
- package/dist/test/repository-oracle-coverage-witness.test.js +511 -0
- package/dist/test/repository-oracle-coverage.test.d.ts +1 -0
- package/dist/test/repository-oracle-coverage.test.js +168 -0
- package/dist/test/repository-oracle-evidence.test.d.ts +1 -0
- package/dist/test/repository-oracle-evidence.test.js +185 -0
- package/dist/test/repository-oracle-plan.test.d.ts +1 -0
- package/dist/test/repository-oracle-plan.test.js +176 -0
- package/dist/test/repository-overlay-isolation.test.d.ts +1 -0
- package/dist/test/repository-overlay-isolation.test.js +85 -0
- package/dist/test/repository-preparation-cache.test.d.ts +1 -0
- package/dist/test/repository-preparation-cache.test.js +414 -0
- package/dist/test/repository-public-artifact.test.d.ts +1 -0
- package/dist/test/repository-public-artifact.test.js +273 -0
- package/dist/test/repository-qualification-diagnostics.test.d.ts +1 -0
- package/dist/test/repository-qualification-diagnostics.test.js +524 -0
- package/dist/test/repository-reference-authoring.test.d.ts +1 -0
- package/dist/test/repository-reference-authoring.test.js +1633 -0
- package/dist/test/repository-review-evidence-v2.test.d.ts +1 -0
- package/dist/test/repository-review-evidence-v2.test.js +183 -0
- package/dist/test/repository-review-evidence.test.d.ts +1 -0
- package/dist/test/repository-review-evidence.test.js +124 -0
- package/dist/test/repository-seed-exclusions.test.d.ts +1 -0
- package/dist/test/repository-seed-exclusions.test.js +96 -0
- package/dist/test/repository-seed-selection.test.d.ts +1 -0
- package/dist/test/repository-seed-selection.test.js +504 -0
- package/dist/test/repository-semantic-calibration.test.d.ts +1 -0
- package/dist/test/repository-semantic-calibration.test.js +688 -0
- package/dist/test/repository-solution-edits.test.d.ts +1 -0
- package/dist/test/repository-solution-edits.test.js +377 -0
- package/dist/test/repository-specification-budget.test.d.ts +1 -0
- package/dist/test/repository-specification-budget.test.js +171 -0
- package/dist/test/repository-specification-contract-checkpoint.test.d.ts +1 -0
- package/dist/test/repository-specification-contract-checkpoint.test.js +228 -0
- package/dist/test/repository-specification-contract-facts.test.d.ts +1 -0
- package/dist/test/repository-specification-contract-facts.test.js +177 -0
- package/dist/test/repository-trajectory-authoring.test.d.ts +1 -0
- package/dist/test/repository-trajectory-authoring.test.js +176 -0
- package/dist/test/repository-valid-control-plan.test.d.ts +1 -0
- package/dist/test/repository-valid-control-plan.test.js +45 -0
- package/dist/test/repository-vitest-phase.test.d.ts +1 -0
- package/dist/test/repository-vitest-phase.test.js +848 -0
- package/dist/test/repository-vitest-reporter.test.d.ts +1 -0
- package/dist/test/repository-vitest-reporter.test.js +158 -0
- package/dist/test/repository-workspace-build.test.d.ts +1 -0
- package/dist/test/repository-workspace-build.test.js +160 -0
- package/dist/test/strict-authoring-schema.test.d.ts +1 -0
- package/dist/test/strict-authoring-schema.test.js +169 -0
- package/package.json +48 -6
|
@@ -4,11 +4,14 @@ import os from "node:os";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { after, test } from "node:test";
|
|
6
6
|
import { layer as NodeServicesLayer } from "@effect/platform-node/NodeServices";
|
|
7
|
+
import { CostServiceLive, makeCostService } from "@velum-labs/routekit-registry";
|
|
7
8
|
import { Cause, Effect, Layer } from "effect";
|
|
8
9
|
import { EvalDimensionContrastError, EvalModelCatalogError } from "../errors.js";
|
|
10
|
+
import { compileEvaluationEvidence, renderEvaluationEvidenceContext } from "../evaluation-evidence.js";
|
|
11
|
+
import { renderEvaluationCriteria } from "../evaluation-structure-policy.js";
|
|
9
12
|
import { EvalRepositoryInspectorLive } from "../inspection.js";
|
|
10
13
|
import { EvalProjectArtifactsLive } from "../project-artifacts.js";
|
|
11
|
-
import { scopedExpectedCalls, summarizeEvalRunLedger } from "../project-contracts.js";
|
|
14
|
+
import { estimateEvalPlanCost, scopedExpectedCalls, summarizeEvalRunLedger } from "../project-contracts.js";
|
|
12
15
|
import { EvalProjectStoreLive } from "../project-store.js";
|
|
13
16
|
import { EvalProjectWorkflow, EvalProjectWorkflowLive } from "../project-workflow.js";
|
|
14
17
|
import { EvalModelCatalog } from "../services/model-catalog/service.js";
|
|
@@ -28,12 +31,13 @@ const catalogEfforts = (candidateModels) => Effect.sync(() => {
|
|
|
28
31
|
};
|
|
29
32
|
});
|
|
30
33
|
const EvalModelCatalogTest = Layer.succeed(EvalModelCatalog, EvalModelCatalog.of({ reasoningEfforts: catalogEfforts }));
|
|
31
|
-
const ProjectWorkflowTestLive = EvalProjectWorkflowLive.pipe(Layer.provide(ProjectDependenciesLive), Layer.provide(EvalModelCatalogTest), Layer.provide(NodeServicesLayer));
|
|
34
|
+
const ProjectWorkflowTestLive = EvalProjectWorkflowLive.pipe(Layer.provide(ProjectDependenciesLive), Layer.provide(EvalModelCatalogTest), Layer.provide(CostServiceLive), Layer.provide(NodeServicesLayer));
|
|
35
|
+
const testCostService = makeCostService();
|
|
32
36
|
const makeRepository = async () => {
|
|
33
37
|
const root = await mkdtemp(path.join(os.tmpdir(), "routekit-eval-project-"));
|
|
34
38
|
roots.push(root);
|
|
35
39
|
await mkdir(path.join(root, "src"), { recursive: true });
|
|
36
|
-
await writeFile(path.join(root, "README.md"), "# Example application\n");
|
|
40
|
+
await writeFile(path.join(root, "README.md"), "# Example application\n\nA terminal event must be last; later output is invalid and unsupported.\n");
|
|
37
41
|
await writeFile(path.join(root, "src", "model.ts"), 'client.responses.create({ model: "openai/current" });\n');
|
|
38
42
|
return root;
|
|
39
43
|
};
|
|
@@ -162,7 +166,7 @@ test("candidate selection fails closed when catalog reasoning metadata is unavai
|
|
|
162
166
|
detail: "models.list did not advertise candidate reasoning capabilities"
|
|
163
167
|
}))
|
|
164
168
|
}));
|
|
165
|
-
const workflowLayer = EvalProjectWorkflowLive.pipe(Layer.provide(ProjectDependenciesLive), Layer.provide(unavailableCatalog), Layer.provide(NodeServicesLayer));
|
|
169
|
+
const workflowLayer = EvalProjectWorkflowLive.pipe(Layer.provide(ProjectDependenciesLive), Layer.provide(unavailableCatalog), Layer.provide(CostServiceLive), Layer.provide(NodeServicesLayer));
|
|
166
170
|
await Effect.runPromise(Effect.gen(function* () {
|
|
167
171
|
const workflow = yield* EvalProjectWorkflow;
|
|
168
172
|
yield* workflow.setup(root);
|
|
@@ -203,15 +207,89 @@ const reviewedProposedDimensions = reviewedDimensions.map((dimension, index) =>
|
|
|
203
207
|
inScopeRequest: `Solve an in-scope request for dimension ${String(index + 1)}.`,
|
|
204
208
|
nearMissRequest: `Solve a neighboring request outside dimension ${String(index + 1)}.`
|
|
205
209
|
}));
|
|
210
|
+
const reviewedPrompt = (dimensionId, index) => {
|
|
211
|
+
switch (index % 5) {
|
|
212
|
+
case 0:
|
|
213
|
+
return `Transform raw ${dimensionId} capture ${String(index + 1)}`;
|
|
214
|
+
case 1:
|
|
215
|
+
return `Locate the fault in ${dimensionId} trace ${String(index + 1)}`;
|
|
216
|
+
case 2:
|
|
217
|
+
return `Construct the ${dimensionId} artifact for sample ${String(index + 1)}`;
|
|
218
|
+
case 3:
|
|
219
|
+
return `Analyze the ${dimensionId} boundary sample ${String(index + 1)}`;
|
|
220
|
+
default:
|
|
221
|
+
return `Explain the ${dimensionId} operational result ${String(index + 1)}`;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
const reviewedEvidenceSources = compileEvaluationEvidence({
|
|
225
|
+
sources: [
|
|
226
|
+
{
|
|
227
|
+
path: "README.md",
|
|
228
|
+
content: "# Example application\n\nA terminal event must be last; later output is invalid and unsupported.\n"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
path: "src/model.ts",
|
|
232
|
+
content: 'client.responses.create({ model: "openai/current" });\n'
|
|
233
|
+
}
|
|
234
|
+
],
|
|
235
|
+
maximumBlockBytes: 3_000
|
|
236
|
+
});
|
|
237
|
+
const reviewedEvidenceBlock = reviewedEvidenceSources[0].blocks[0];
|
|
238
|
+
const reviewedCompiledFields = (caseId, index) => {
|
|
239
|
+
const criterionId = `${caseId}-criterion`;
|
|
240
|
+
const criteria = [
|
|
241
|
+
{
|
|
242
|
+
id: criterionId,
|
|
243
|
+
description: `Derive the observable repository behavior for case ${String(index + 1)}.`,
|
|
244
|
+
importance: "critical",
|
|
245
|
+
gradingMode: "semantic"
|
|
246
|
+
}
|
|
247
|
+
];
|
|
248
|
+
return {
|
|
249
|
+
context: renderEvaluationEvidenceContext([reviewedEvidenceBlock]),
|
|
250
|
+
rubric: renderEvaluationCriteria(criteria),
|
|
251
|
+
evidenceIds: [reviewedEvidenceBlock.id],
|
|
252
|
+
criteria,
|
|
253
|
+
positiveControls: [
|
|
254
|
+
{
|
|
255
|
+
id: `${caseId}-positive`,
|
|
256
|
+
response: `The response derives the repository-backed behavior for case ${String(index + 1)}.`
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
negativeControls: [
|
|
260
|
+
{
|
|
261
|
+
id: `${caseId}-missing`,
|
|
262
|
+
response: "The response discusses the topic but omits the required behavior.",
|
|
263
|
+
targetedCriterionIds: [criterionId],
|
|
264
|
+
kind: "missing-criterion"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
id: `${caseId}-forbidden`,
|
|
268
|
+
response: "The response invents behavior contradicted by the repository evidence.",
|
|
269
|
+
targetedCriterionIds: [criterionId],
|
|
270
|
+
kind: "forbidden-behavior"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
id: `${caseId}-non-responsive`,
|
|
274
|
+
response: "This is a polished overview that never answers the concrete request.",
|
|
275
|
+
targetedCriterionIds: [criterionId],
|
|
276
|
+
kind: "non-responsive"
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
};
|
|
280
|
+
};
|
|
206
281
|
const reviewedSuites = reviewedDimensions.map((dimension) => ({
|
|
207
282
|
version: 1,
|
|
208
283
|
dimensionId: dimension.id,
|
|
209
284
|
maximumOutputTokens: 256,
|
|
210
|
-
cases: Array.from({ length: 20 }, (_, index) =>
|
|
211
|
-
id
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
285
|
+
cases: Array.from({ length: 20 }, (_, index) => {
|
|
286
|
+
const id = `${dimension.id}-case-${String(index + 1)}`;
|
|
287
|
+
return {
|
|
288
|
+
id,
|
|
289
|
+
prompt: reviewedPrompt(dimension.id, index),
|
|
290
|
+
...reviewedCompiledFields(id, index)
|
|
291
|
+
};
|
|
292
|
+
})
|
|
215
293
|
}));
|
|
216
294
|
const reviewedDecompositionBenchmark = {
|
|
217
295
|
maximumVectorL1Error: 0.3,
|
|
@@ -231,32 +309,53 @@ const reviewedCompositionSuite = {
|
|
|
231
309
|
maximumOutputTokens: 256,
|
|
232
310
|
minimumWinnerScoreGap: 0.05,
|
|
233
311
|
minimumWinnerAgreement: 0.8,
|
|
234
|
-
cases: Array.from({ length: 20 }, (_, index) =>
|
|
235
|
-
id
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
312
|
+
cases: Array.from({ length: 20 }, (_, index) => {
|
|
313
|
+
const id = `composition-case-${String(index + 1)}`;
|
|
314
|
+
return {
|
|
315
|
+
id,
|
|
316
|
+
prompt: reviewedPrompt("multi-dimension", index),
|
|
317
|
+
...reviewedCompiledFields(id, index),
|
|
318
|
+
decomposition: {
|
|
319
|
+
weights: reviewedDimensions.map((dimension, dimensionIndex) => ({
|
|
320
|
+
dimensionId: dimension.id,
|
|
321
|
+
weight: dimensionIndex === index % reviewedDimensions.length ||
|
|
322
|
+
dimensionIndex === (index + 1) % reviewedDimensions.length
|
|
323
|
+
? 0.5
|
|
324
|
+
: 0
|
|
325
|
+
})),
|
|
326
|
+
unknownWeight: 0
|
|
327
|
+
},
|
|
328
|
+
requirements: {
|
|
329
|
+
endpoint: "responses",
|
|
330
|
+
requiresTools: false,
|
|
331
|
+
requiresVision: false
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
})
|
|
254
335
|
};
|
|
255
336
|
const reviewedEvaluationInput = {
|
|
337
|
+
evidenceSources: reviewedEvidenceSources,
|
|
256
338
|
suites: reviewedSuites,
|
|
257
339
|
decompositionBenchmark: reviewedDecompositionBenchmark,
|
|
258
340
|
compositionSuite: reviewedCompositionSuite
|
|
259
341
|
};
|
|
342
|
+
const flashcardEvaluationInput = {
|
|
343
|
+
...reviewedEvaluationInput,
|
|
344
|
+
suites: reviewedSuites.map((suite) => ({
|
|
345
|
+
...suite,
|
|
346
|
+
cases: Array.from({ length: 20 }, (_, index) => {
|
|
347
|
+
const scenario = Math.floor(index / 2);
|
|
348
|
+
return {
|
|
349
|
+
id: `${suite.dimensionId}-flashcard-${String(index + 1)}`,
|
|
350
|
+
prompt: index % 2 === 0
|
|
351
|
+
? "Diagnose the described RouteKit behavior. Use only the supplied context."
|
|
352
|
+
: "Give a concise verification criterion. Use only the supplied context.",
|
|
353
|
+
context: `Captured scenario ${String(scenario + 1)} emitted output after completion. The terminal event must be last.`,
|
|
354
|
+
rubric: "Reject post-terminal output and require completion to be the final event."
|
|
355
|
+
};
|
|
356
|
+
})
|
|
357
|
+
}))
|
|
358
|
+
};
|
|
260
359
|
async function completeProjectSetup(root, candidateModels = "openai/gpt-5.6-luna openai/gpt-5.6-terra openai/gpt-5.6-sol") {
|
|
261
360
|
await Effect.runPromise(Effect.gen(function* () {
|
|
262
361
|
const workflow = yield* EvalProjectWorkflow;
|
|
@@ -356,7 +455,8 @@ test("reviewed artifacts are digest-bound and produce an immutable exact-call pl
|
|
|
356
455
|
assert.equal(ready.state.stage, "ready");
|
|
357
456
|
assert.equal(ready.nextAction, "run");
|
|
358
457
|
const plan = yield* workflow.createPlan(root, "pilot");
|
|
359
|
-
|
|
458
|
+
const repeatedPlan = yield* workflow.createPlan(root, "pilot", 2);
|
|
459
|
+
return { basisDigest, evaluationDigest, plan, repeatedPlan };
|
|
360
460
|
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
361
461
|
const storedBasis = JSON.parse(await readFile(path.join(root, ".routekit", "evals", "routing-basis.proposed.json"), "utf8"));
|
|
362
462
|
assert.equal(storedBasis.dimensionContrasts.length, reviewedDimensions.length);
|
|
@@ -374,20 +474,47 @@ test("reviewed artifacts are digest-bound and produce an immutable exact-call pl
|
|
|
374
474
|
assert.equal(result.plan.expectedJudgeCalls, 90);
|
|
375
475
|
assert.equal(result.plan.expectedCallCount, 185);
|
|
376
476
|
assert.equal(result.plan.maximumOutputTokens, 256);
|
|
477
|
+
assert.equal(result.plan.repetitions, 1);
|
|
478
|
+
assert.equal(result.repeatedPlan.repetitions, 2);
|
|
479
|
+
assert.equal(result.repeatedPlan.expectedCallCount, result.plan.expectedCallCount * 2);
|
|
480
|
+
assert.equal(result.plan.costEstimate !== undefined, true);
|
|
481
|
+
assert.equal(result.repeatedPlan.costEstimate !== undefined, true);
|
|
482
|
+
assert.equal(result.repeatedPlan.costEstimate?.knownPricedMaximumUsd, (result.plan.costEstimate?.knownPricedMaximumUsd ?? 0) * 2);
|
|
377
483
|
const planPath = path.join(root, ".routekit", "evals", "plans", `${result.plan.planId}.json`);
|
|
378
484
|
assert.equal((await stat(planPath)).mode & 0o777, 0o600);
|
|
379
485
|
assert.deepEqual(JSON.parse(await readFile(planPath, "utf8")), result.plan);
|
|
380
486
|
const firstDimension = reviewedDimensions[0];
|
|
381
487
|
const generatedSuite = await readFile(path.join(root, ".routekit", "evals", "plans", result.plan.planId, "dimensions", firstDimension.id, `${firstDimension.id}.eval.ts`), "utf8");
|
|
488
|
+
const candidateCases = JSON.parse(await readFile(path.join(root, ".routekit", "evals", "plans", result.plan.planId, "dimensions", firstDimension.id, "data", "candidate-cases.json"), "utf8"));
|
|
489
|
+
const hiddenOracles = JSON.parse(await readFile(path.join(root, ".routekit", "evals", "plans", result.plan.planId, "dimensions", firstDimension.id, "data", "hidden-oracles.json"), "utf8"));
|
|
382
490
|
const completionAssertion = generatedSuite.indexOf("run.toComplete();");
|
|
491
|
+
const oracleLookup = generatedSuite.indexOf("hiddenOracleById.get(candidateCase.id)");
|
|
383
492
|
const judgeCall = generatedSuite.indexOf("await judge.autoEvals");
|
|
384
493
|
const completionRethrow = generatedSuite.indexOf("throw candidateCompletionError;");
|
|
385
494
|
const judgeRethrow = generatedSuite.indexOf("throw judgeError;");
|
|
386
495
|
assert.match(generatedSuite, /emit the complete target-protocol envelope and terminal event/u);
|
|
387
496
|
assert.match(generatedSuite, /include the concrete output rather than only describing it/u);
|
|
497
|
+
assert.match(generatedSuite, /You are a strict evaluator for AI agent outputs\./u);
|
|
498
|
+
assert.deepEqual(Object.keys(candidateCases[0]).sort(), [
|
|
499
|
+
"context",
|
|
500
|
+
"id",
|
|
501
|
+
"prompt"
|
|
502
|
+
]);
|
|
503
|
+
assert.equal(Object.hasOwn(candidateCases[0], "rubric"), false);
|
|
504
|
+
assert.equal(Object.hasOwn(candidateCases[0], "criteria"), false);
|
|
505
|
+
assert.deepEqual(Object.keys(hiddenOracles[0]).sort(), [
|
|
506
|
+
"criteria",
|
|
507
|
+
"id",
|
|
508
|
+
"negativeControls",
|
|
509
|
+
"positiveControls",
|
|
510
|
+
"rubric"
|
|
511
|
+
]);
|
|
512
|
+
assert.equal(candidateCases[0].id, hiddenOracles[0].id);
|
|
388
513
|
assert.ok(judgeCall >= 0);
|
|
514
|
+
assert.ok(oracleLookup >= 0);
|
|
389
515
|
assert.ok(completionAssertion >= 0);
|
|
390
|
-
assert.ok(completionAssertion <
|
|
516
|
+
assert.ok(completionAssertion < oracleLookup);
|
|
517
|
+
assert.ok(oracleLookup < judgeCall);
|
|
391
518
|
assert.ok(judgeCall < completionRethrow);
|
|
392
519
|
assert.ok(completionRethrow < judgeRethrow);
|
|
393
520
|
for (const dimension of reviewedDimensions) {
|
|
@@ -395,6 +522,95 @@ test("reviewed artifacts are digest-bound and produce an immutable exact-call pl
|
|
|
395
522
|
.mode & 0o777, 0o600);
|
|
396
523
|
}
|
|
397
524
|
});
|
|
525
|
+
test("evaluation approval rejects historical cases without compiler provenance", async () => {
|
|
526
|
+
const root = await makeRepository();
|
|
527
|
+
await completeProjectSetup(root);
|
|
528
|
+
const exit = await Effect.runPromise(Effect.gen(function* () {
|
|
529
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
530
|
+
const proposedBasis = yield* workflow.proposeDimensions(root, reviewedProposedDimensions);
|
|
531
|
+
yield* workflow.approveDimensions(root, proposedBasis.artifacts.basisProposalDigest);
|
|
532
|
+
const proposed = yield* workflow.proposeEvaluations(root, flashcardEvaluationInput);
|
|
533
|
+
return yield* Effect.exit(workflow.approveEvaluations(root, proposed.artifacts.evaluationProposalDigest));
|
|
534
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
535
|
+
assert.equal(exit._tag, "Failure");
|
|
536
|
+
if (exit._tag === "Failure") {
|
|
537
|
+
const error = Cause.squash(exit.cause);
|
|
538
|
+
assert.match(String(error), /is not compiler-backed/u);
|
|
539
|
+
}
|
|
540
|
+
const approvalPath = path.join(root, ".routekit", "evals", "evaluations.approved.json");
|
|
541
|
+
await assert.rejects(stat(approvalPath), /ENOENT/u);
|
|
542
|
+
});
|
|
543
|
+
test("repository evidence changes stale approval before approval and planning", async () => {
|
|
544
|
+
const beforeApprovalRoot = await makeRepository();
|
|
545
|
+
await completeProjectSetup(beforeApprovalRoot);
|
|
546
|
+
const beforeApproval = await Effect.runPromise(Effect.gen(function* () {
|
|
547
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
548
|
+
const proposedBasis = yield* workflow.proposeDimensions(beforeApprovalRoot, reviewedProposedDimensions);
|
|
549
|
+
yield* workflow.approveDimensions(beforeApprovalRoot, proposedBasis.artifacts.basisProposalDigest);
|
|
550
|
+
const proposed = yield* workflow.proposeEvaluations(beforeApprovalRoot, reviewedEvaluationInput);
|
|
551
|
+
return proposed.artifacts.evaluationProposalDigest;
|
|
552
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
553
|
+
await writeFile(path.join(beforeApprovalRoot, "README.md"), "# Example application\n\nChanged after authoring.\n");
|
|
554
|
+
const approvalExit = await Effect.runPromise(Effect.gen(function* () {
|
|
555
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
556
|
+
return yield* Effect.exit(workflow.approveEvaluations(beforeApprovalRoot, beforeApproval));
|
|
557
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
558
|
+
assert.equal(approvalExit._tag, "Failure");
|
|
559
|
+
if (approvalExit._tag === "Failure") {
|
|
560
|
+
assert.match(String(Cause.squash(approvalExit.cause)), /evidence source changed after authoring/u);
|
|
561
|
+
}
|
|
562
|
+
const beforePlanRoot = await makeRepository();
|
|
563
|
+
await completeProjectSetup(beforePlanRoot);
|
|
564
|
+
await Effect.runPromise(Effect.gen(function* () {
|
|
565
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
566
|
+
const proposedBasis = yield* workflow.proposeDimensions(beforePlanRoot, reviewedProposedDimensions);
|
|
567
|
+
yield* workflow.approveDimensions(beforePlanRoot, proposedBasis.artifacts.basisProposalDigest);
|
|
568
|
+
const proposed = yield* workflow.proposeEvaluations(beforePlanRoot, reviewedEvaluationInput);
|
|
569
|
+
yield* workflow.approveEvaluations(beforePlanRoot, proposed.artifacts.evaluationProposalDigest);
|
|
570
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
571
|
+
await writeFile(path.join(beforePlanRoot, "README.md"), "# Example application\n\nChanged before planning.\n");
|
|
572
|
+
const stale = await Effect.runPromise(Effect.gen(function* () {
|
|
573
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
574
|
+
const status = yield* workflow.status(beforePlanRoot);
|
|
575
|
+
const planExit = yield* Effect.exit(workflow.createPlan(beforePlanRoot, "pilot"));
|
|
576
|
+
return { status, planExit };
|
|
577
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
578
|
+
assert.equal(stale.status?.artifacts?.evaluationsApproved, false);
|
|
579
|
+
assert.equal(stale.status?.nextAction, "approve-evaluations");
|
|
580
|
+
assert.equal(stale.planExit._tag, "Failure");
|
|
581
|
+
if (stale.planExit._tag === "Failure") {
|
|
582
|
+
assert.match(String(stale.planExit.cause), /evidence source changed after authoring/u);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
test("ready projects can explicitly rebind stale evaluation approvals", async () => {
|
|
586
|
+
const root = await makeRepository();
|
|
587
|
+
await completeProjectSetup(root);
|
|
588
|
+
const rebound = await Effect.runPromise(Effect.gen(function* () {
|
|
589
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
590
|
+
const proposedBasis = yield* workflow.proposeDimensions(root, reviewedProposedDimensions);
|
|
591
|
+
yield* workflow.approveDimensions(root, proposedBasis.artifacts.basisProposalDigest);
|
|
592
|
+
const proposed = yield* workflow.proposeEvaluations(root, reviewedEvaluationInput);
|
|
593
|
+
const digest = proposed.artifacts.evaluationProposalDigest;
|
|
594
|
+
const ready = yield* workflow.approveEvaluations(root, digest);
|
|
595
|
+
yield* Effect.promise(() => writeFile(path.join(root, ".routekit", "evals", "evaluations.approval.json"), `${JSON.stringify({
|
|
596
|
+
version: 1,
|
|
597
|
+
kind: "evaluations",
|
|
598
|
+
digest,
|
|
599
|
+
approvedAt: "2026-08-30T00:00:00.000Z"
|
|
600
|
+
})}\n`, { mode: 0o600 }));
|
|
601
|
+
const stale = yield* workflow.status(root);
|
|
602
|
+
const reboundStatus = yield* workflow.approveEvaluations(root, digest);
|
|
603
|
+
return { ready, stale, reboundStatus };
|
|
604
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
605
|
+
assert.equal(rebound.ready.state.stage, "ready");
|
|
606
|
+
assert.ok(rebound.stale !== undefined);
|
|
607
|
+
assert.equal(rebound.stale.state.stage, "ready");
|
|
608
|
+
assert.equal(rebound.stale.artifacts?.evaluationsApproved, false);
|
|
609
|
+
assert.equal(rebound.stale.nextAction, "approve-evaluations");
|
|
610
|
+
assert.equal(rebound.reboundStatus.state.stage, "ready");
|
|
611
|
+
assert.equal(rebound.reboundStatus.artifacts?.evaluationsApproved, true);
|
|
612
|
+
assert.equal(rebound.reboundStatus.nextAction, "run");
|
|
613
|
+
});
|
|
398
614
|
test("full qualification refuses fewer than twenty reviewed cases per dimension", async () => {
|
|
399
615
|
const root = await makeRepository();
|
|
400
616
|
await completeProjectSetup(root);
|
|
@@ -403,6 +619,7 @@ test("full qualification refuses fewer than twenty reviewed cases per dimension"
|
|
|
403
619
|
const proposed = yield* workflow.proposeDimensions(root, reviewedProposedDimensions);
|
|
404
620
|
yield* workflow.approveDimensions(root, proposed.artifacts.basisProposalDigest);
|
|
405
621
|
const evaluations = yield* workflow.proposeEvaluations(root, {
|
|
622
|
+
evidenceSources: reviewedEvidenceSources,
|
|
406
623
|
suites: reviewedSuites.map((suite) => ({ ...suite, cases: suite.cases.slice(0, 5) })),
|
|
407
624
|
decompositionBenchmark: {
|
|
408
625
|
...reviewedDecompositionBenchmark,
|
|
@@ -555,18 +772,25 @@ const qualifiedReport = (plan, runId, projectId, publishAllowed = true) => {
|
|
|
555
772
|
expectedCases: plan.selectedCompositionCaseIds.length,
|
|
556
773
|
comparableCases: plan.selectedCompositionCaseIds.length,
|
|
557
774
|
agreeingCases: plan.selectedCompositionCaseIds.length,
|
|
558
|
-
|
|
775
|
+
...(plan.selectedCompositionCaseIds.length === 0
|
|
776
|
+
? {}
|
|
777
|
+
: { winnerAgreement: 1 }),
|
|
778
|
+
conclusion: plan.selectedCompositionCaseIds.length === 0 ? "inconclusive" : "passed",
|
|
779
|
+
publishable: plan.scope === "full" && plan.selectedCompositionCaseIds.length > 0,
|
|
559
780
|
cases: plan.selectedCompositionCaseIds.map((caseId) => ({
|
|
560
781
|
caseId,
|
|
561
782
|
predictedWinner: candidateModels[0],
|
|
783
|
+
predictedTiedModels: [],
|
|
562
784
|
observedWinner: candidateModels[0],
|
|
785
|
+
observedTiedModels: [],
|
|
563
786
|
predictedScoreGap: 0.1,
|
|
564
787
|
observedScoreGap: 0.2,
|
|
788
|
+
comparable: true,
|
|
565
789
|
passed: true
|
|
566
790
|
}))
|
|
567
791
|
}
|
|
568
792
|
},
|
|
569
|
-
ledger: summarizeEvalRunLedger(comparisons, plan.expectedCallCount, observations),
|
|
793
|
+
ledger: Effect.runSync(summarizeEvalRunLedger(comparisons, plan.expectedCallCount, testCostService, observations, plan.classifierModel)),
|
|
570
794
|
activation: {
|
|
571
795
|
version: 2,
|
|
572
796
|
generatedAt: "2026-08-18T00:02:00.000Z",
|
|
@@ -590,7 +814,8 @@ const qualifiedReport = (plan, runId, projectId, publishAllowed = true) => {
|
|
|
590
814
|
failureRate: 0,
|
|
591
815
|
averageJudgeScore: 0.9,
|
|
592
816
|
p95DurationMs: 250,
|
|
593
|
-
|
|
817
|
+
averageCostUsd: 0.01,
|
|
818
|
+
unpricedCalls: 0
|
|
594
819
|
})))
|
|
595
820
|
}
|
|
596
821
|
};
|
|
@@ -624,7 +849,7 @@ test("run lifecycle retains complete sanitized evidence and activates only after
|
|
|
624
849
|
assert.equal(outcome.loaded?.status, "passed");
|
|
625
850
|
assert.equal(outcome.loaded?.ledger.observedCalls, 740);
|
|
626
851
|
assert.equal(outcome.loaded?.ledger.observedCandidateRows, 360);
|
|
627
|
-
assert.equal(outcome.loaded?.ledger.unpricedCalls,
|
|
852
|
+
assert.equal(outcome.loaded?.ledger.unpricedCalls, 0);
|
|
628
853
|
assert.equal(outcome.activated.state.stage, "activated");
|
|
629
854
|
const reportPath = path.join(root, ".routekit", "evals", "runs", outcome.started.runId, "report.json");
|
|
630
855
|
assert.equal((await stat(reportPath)).mode & 0o777, 0o600);
|
|
@@ -633,8 +858,9 @@ test("finishRun derives ledger expectations from the selected case slice", async
|
|
|
633
858
|
const root = await makeRepository();
|
|
634
859
|
const created = await createPlan(root, "full", "openai/gpt-5.6-luna openai/gpt-5.6-terra");
|
|
635
860
|
const selectedDimension = created.selectedCaseIds[0];
|
|
636
|
-
const
|
|
861
|
+
const stalePlanInput = {
|
|
637
862
|
...created,
|
|
863
|
+
repetitions: 1,
|
|
638
864
|
selectedCaseIds: [
|
|
639
865
|
{
|
|
640
866
|
dimensionId: selectedDimension.dimensionId,
|
|
@@ -652,6 +878,10 @@ test("finishRun derives ledger expectations from the selected case slice", async
|
|
|
652
878
|
expectedJudgeCalls: 3_400,
|
|
653
879
|
expectedCallCount: 6_820
|
|
654
880
|
};
|
|
881
|
+
const stalePlan = {
|
|
882
|
+
...stalePlanInput,
|
|
883
|
+
costEstimate: Effect.runSync(estimateEvalPlanCost(stalePlanInput, testCostService))
|
|
884
|
+
};
|
|
655
885
|
await writeFile(path.join(root, ".routekit", "evals", "plans", `${created.planId}.json`), `${JSON.stringify(stalePlan)}\n`);
|
|
656
886
|
const outcome = await Effect.runPromise(Effect.gen(function* () {
|
|
657
887
|
const workflow = yield* EvalProjectWorkflow;
|
|
@@ -671,7 +901,7 @@ test("finishRun derives ledger expectations from the selected case slice", async
|
|
|
671
901
|
});
|
|
672
902
|
const scopedReport = {
|
|
673
903
|
...report,
|
|
674
|
-
ledger: summarizeEvalRunLedger(report.comparisons, expected.expectedCallCount)
|
|
904
|
+
ledger: yield* summarizeEvalRunLedger(report.comparisons, expected.expectedCallCount, testCostService)
|
|
675
905
|
};
|
|
676
906
|
const finished = yield* workflow.finishRun(root, scopedReport);
|
|
677
907
|
return { finished, report: scopedReport };
|
|
@@ -681,6 +911,98 @@ test("finishRun derives ledger expectations from the selected case slice", async
|
|
|
681
911
|
assert.equal(outcome.report.ledger.observedCalls, 4);
|
|
682
912
|
assert.equal(outcome.report.ledger.observedCandidateRows, 2);
|
|
683
913
|
});
|
|
914
|
+
test("zero-comparable pilot persists a completed inconclusive diagnostic", async () => {
|
|
915
|
+
const root = await makeRepository();
|
|
916
|
+
const plan = await createPlan(root, "pilot");
|
|
917
|
+
const outcome = await Effect.runPromise(Effect.gen(function* () {
|
|
918
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
919
|
+
const started = yield* workflow.startRun(root, plan.planId);
|
|
920
|
+
const status = yield* workflow.status(root);
|
|
921
|
+
const passing = qualifiedReport(plan, started.runId, status.state.projectId);
|
|
922
|
+
assert.equal(passing.status, "passed");
|
|
923
|
+
if (passing.status !== "passed")
|
|
924
|
+
return undefined;
|
|
925
|
+
const report = {
|
|
926
|
+
...passing,
|
|
927
|
+
status: "completed",
|
|
928
|
+
qualification: {
|
|
929
|
+
...passing.qualification,
|
|
930
|
+
composition: {
|
|
931
|
+
expectedCases: plan.selectedCompositionCaseIds.length,
|
|
932
|
+
comparableCases: 0,
|
|
933
|
+
agreeingCases: 0,
|
|
934
|
+
conclusion: "inconclusive",
|
|
935
|
+
reason: "insufficient top-two separation",
|
|
936
|
+
publishable: false,
|
|
937
|
+
cases: plan.selectedCompositionCaseIds.map((caseId) => ({
|
|
938
|
+
caseId,
|
|
939
|
+
predictedWinner: null,
|
|
940
|
+
predictedTiedModels: [...plan.candidateModels],
|
|
941
|
+
observedWinner: null,
|
|
942
|
+
observedTiedModels: [...plan.candidateModels],
|
|
943
|
+
predictedScoreGap: 0,
|
|
944
|
+
observedScoreGap: 0,
|
|
945
|
+
comparable: false,
|
|
946
|
+
passed: false
|
|
947
|
+
}))
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
const finished = yield* workflow.finishRun(root, report);
|
|
952
|
+
const persisted = yield* workflow.result(root, started.runId);
|
|
953
|
+
return { finished, persisted };
|
|
954
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
955
|
+
assert.equal(outcome?.finished.state.stage, "ready");
|
|
956
|
+
assert.equal(outcome?.persisted?.status, "completed");
|
|
957
|
+
assert.equal(outcome?.persisted?.qualification?.composition.expectedCases, 5);
|
|
958
|
+
assert.equal(outcome?.persisted?.qualification?.composition.comparableCases, 0);
|
|
959
|
+
assert.equal(outcome?.persisted?.qualification?.composition.winnerAgreement, undefined);
|
|
960
|
+
assert.equal(outcome?.persisted?.qualification?.composition.conclusion, "inconclusive");
|
|
961
|
+
assert.equal(outcome?.persisted?.qualification?.composition.publishable, false);
|
|
962
|
+
});
|
|
963
|
+
test("zero-comparable full qualification remains fail closed and unpublished", async () => {
|
|
964
|
+
const root = await makeRepository();
|
|
965
|
+
const plan = await createPlan(root, "full");
|
|
966
|
+
const outcome = await Effect.runPromise(Effect.gen(function* () {
|
|
967
|
+
const workflow = yield* EvalProjectWorkflow;
|
|
968
|
+
const started = yield* workflow.startRun(root, plan.planId);
|
|
969
|
+
const status = yield* workflow.status(root);
|
|
970
|
+
const passing = qualifiedReport(plan, started.runId, status.state.projectId);
|
|
971
|
+
assert.equal(passing.status, "passed");
|
|
972
|
+
if (passing.status !== "passed")
|
|
973
|
+
return undefined;
|
|
974
|
+
const report = {
|
|
975
|
+
...passing,
|
|
976
|
+
qualification: {
|
|
977
|
+
...passing.qualification,
|
|
978
|
+
composition: {
|
|
979
|
+
expectedCases: plan.selectedCompositionCaseIds.length,
|
|
980
|
+
comparableCases: 0,
|
|
981
|
+
agreeingCases: 0,
|
|
982
|
+
conclusion: "inconclusive",
|
|
983
|
+
reason: "insufficient top-two separation",
|
|
984
|
+
publishable: false,
|
|
985
|
+
cases: plan.selectedCompositionCaseIds.map((caseId) => ({
|
|
986
|
+
caseId,
|
|
987
|
+
predictedWinner: null,
|
|
988
|
+
predictedTiedModels: [...plan.candidateModels],
|
|
989
|
+
observedWinner: null,
|
|
990
|
+
observedTiedModels: [...plan.candidateModels],
|
|
991
|
+
predictedScoreGap: 0,
|
|
992
|
+
observedScoreGap: 0,
|
|
993
|
+
comparable: false,
|
|
994
|
+
passed: false
|
|
995
|
+
}))
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
const finished = yield* Effect.exit(workflow.finishRun(root, report));
|
|
1000
|
+
const current = yield* workflow.status(root);
|
|
1001
|
+
return { finished, current };
|
|
1002
|
+
}).pipe(Effect.provide(ProjectWorkflowTestLive)));
|
|
1003
|
+
assert.equal(outcome?.finished._tag, "Failure");
|
|
1004
|
+
assert.equal(outcome?.current?.state.stage, "running");
|
|
1005
|
+
});
|
|
684
1006
|
test("failed runs preserve plan revisions for retry while mismatched plans remain stale", async () => {
|
|
685
1007
|
const root = await makeRepository();
|
|
686
1008
|
const plan = await createPlan(root, "full");
|
|
@@ -738,8 +1060,36 @@ test("failed runs preserve plan revisions for retry while mismatched plans remai
|
|
|
738
1060
|
knownOutputTokens: 0,
|
|
739
1061
|
unknownTokenMeasurements: 0,
|
|
740
1062
|
knownPricedSubtotalUsd: 0,
|
|
741
|
-
unpricedCalls: 0
|
|
1063
|
+
unpricedCalls: 0,
|
|
1064
|
+
subscriptionUnitCalls: 0
|
|
742
1065
|
},
|
|
1066
|
+
events: [
|
|
1067
|
+
{
|
|
1068
|
+
name: "plan",
|
|
1069
|
+
at: "2026-08-21T03:47:27.000Z",
|
|
1070
|
+
runId: first.runId,
|
|
1071
|
+
plannedCalls: plan.expectedCallCount
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
name: "failure-detail",
|
|
1075
|
+
at: "2026-08-21T03:47:48.000Z",
|
|
1076
|
+
runId: first.runId,
|
|
1077
|
+
phase: "target",
|
|
1078
|
+
reason: "qualification timed out before observing any calls",
|
|
1079
|
+
retryable: true,
|
|
1080
|
+
accounting: {
|
|
1081
|
+
expectedCalls: plan.expectedCallCount,
|
|
1082
|
+
observedCalls: 0,
|
|
1083
|
+
knownInputTokens: 0,
|
|
1084
|
+
knownOutputTokens: 0,
|
|
1085
|
+
unknownTokenMeasurements: 0,
|
|
1086
|
+
knownPricedSubtotalUsd: 0,
|
|
1087
|
+
unpricedCalls: 0,
|
|
1088
|
+
subscriptionUnitCalls: 0
|
|
1089
|
+
},
|
|
1090
|
+
cleanup: { sessionOpened: true, sessionClosed: true }
|
|
1091
|
+
}
|
|
1092
|
+
],
|
|
743
1093
|
failure: "qualification timed out before observing any calls",
|
|
744
1094
|
errors: []
|
|
745
1095
|
});
|
|
@@ -764,7 +1114,7 @@ test("startRun rematerializes stale dimension and composition suite templates",
|
|
|
764
1114
|
const dimensionManifestPath = path.join(dimensionRoot, "routekit.eval-manifest.json");
|
|
765
1115
|
const compositionManifestPath = path.join(compositionRoot, "routekit.eval-manifest.json");
|
|
766
1116
|
const staleTemplate = `// generated by RouteKit 1.0.21
|
|
767
|
-
await judge.autoEvals({ criteria:
|
|
1117
|
+
await judge.autoEvals({ criteria: mixedCase.rubric, prompt, run });
|
|
768
1118
|
run.toComplete();
|
|
769
1119
|
`;
|
|
770
1120
|
await Promise.all([
|
|
@@ -793,6 +1143,7 @@ run.toComplete();
|
|
|
793
1143
|
assert.doesNotMatch(suite, /generated by RouteKit 1\.0\.21/u);
|
|
794
1144
|
assert.match(suite, /emit the complete target-protocol envelope and terminal event/u);
|
|
795
1145
|
assert.match(suite, /include the concrete output rather than only describing it/u);
|
|
1146
|
+
assert.match(suite, /You are a strict evaluator for AI agent outputs\./u);
|
|
796
1147
|
assert.ok(judgeCall >= 0);
|
|
797
1148
|
assert.ok(completionAssertion >= 0);
|
|
798
1149
|
assert.ok(completionAssertion < judgeCall);
|
|
@@ -842,8 +1193,36 @@ test("failed run reports persist the nested qualification error chain", async ()
|
|
|
842
1193
|
knownOutputTokens: 0,
|
|
843
1194
|
unknownTokenMeasurements: 0,
|
|
844
1195
|
knownPricedSubtotalUsd: 0,
|
|
845
|
-
unpricedCalls: 0
|
|
1196
|
+
unpricedCalls: 0,
|
|
1197
|
+
subscriptionUnitCalls: 0
|
|
846
1198
|
},
|
|
1199
|
+
events: [
|
|
1200
|
+
{
|
|
1201
|
+
name: "plan",
|
|
1202
|
+
at: "2026-08-21T03:47:27.000Z",
|
|
1203
|
+
runId: started.runId,
|
|
1204
|
+
plannedCalls: plan.expectedCallCount
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
name: "failure-detail",
|
|
1208
|
+
at: "2026-08-21T03:47:48.000Z",
|
|
1209
|
+
runId: started.runId,
|
|
1210
|
+
phase: "comparison",
|
|
1211
|
+
reason: "RouteKit Eval comparison failed",
|
|
1212
|
+
retryable: false,
|
|
1213
|
+
accounting: {
|
|
1214
|
+
expectedCalls: plan.expectedCallCount,
|
|
1215
|
+
observedCalls: 0,
|
|
1216
|
+
knownInputTokens: 0,
|
|
1217
|
+
knownOutputTokens: 0,
|
|
1218
|
+
unknownTokenMeasurements: 0,
|
|
1219
|
+
knownPricedSubtotalUsd: 0,
|
|
1220
|
+
unpricedCalls: 0,
|
|
1221
|
+
subscriptionUnitCalls: 0
|
|
1222
|
+
},
|
|
1223
|
+
cleanup: { sessionOpened: true, sessionClosed: true }
|
|
1224
|
+
}
|
|
1225
|
+
],
|
|
847
1226
|
failure: 'qualification dimension "provider-protocol-translation" failed ' +
|
|
848
1227
|
"(per-test timeout 600000ms); observed call ids: none",
|
|
849
1228
|
errors: [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|