@prismatic-io/lux 0.0.2-preview.14 → 0.0.2-preview.16
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/lib/answerers/claude-code/index.d.ts +14 -2
- package/lib/answerers/claude-code/index.d.ts.map +1 -1
- package/lib/answerers/claude-code/index.js +3 -3
- package/lib/answerers/claude-code/index.js.map +1 -1
- package/lib/answerers/persona/index.d.ts +28 -2
- package/lib/answerers/persona/index.d.ts.map +1 -1
- package/lib/answerers/persona/index.js +3 -3
- package/lib/answerers/persona/index.js.map +1 -1
- package/lib/answerers/terminal/index.d.ts +8 -2
- package/lib/answerers/terminal/index.d.ts.map +1 -1
- package/lib/answerers/terminal/index.js +3 -2
- package/lib/answerers/terminal/index.js.map +1 -1
- package/lib/assertions/core/resource-checks.d.ts.map +1 -1
- package/lib/assertions/core/resource-checks.js +3 -5
- package/lib/assertions/core/resource-checks.js.map +1 -1
- package/lib/assertions/rubric/index.d.ts.map +1 -1
- package/lib/assertions/rubric/index.js +7 -1
- package/lib/assertions/rubric/index.js.map +1 -1
- package/lib/assertions/rubric/internal.d.ts +16 -5
- package/lib/assertions/rubric/internal.d.ts.map +1 -1
- package/lib/assertions/rubric/internal.js +10 -6
- package/lib/assertions/rubric/internal.js.map +1 -1
- package/lib/authoring.d.ts +32 -2
- package/lib/authoring.d.ts.map +1 -1
- package/lib/authoring.js +33 -1
- package/lib/authoring.js.map +1 -1
- package/lib/cli/bin.js +2 -13
- package/lib/cli/bin.js.map +1 -1
- package/lib/cli/command-runtime.d.ts +3 -3
- package/lib/cli/command-runtime.d.ts.map +1 -1
- package/lib/cli/command-runtime.js +17 -6
- package/lib/cli/command-runtime.js.map +1 -1
- package/lib/cli/init-templates.d.ts +11 -0
- package/lib/cli/init-templates.d.ts.map +1 -0
- package/lib/cli/init-templates.js +207 -0
- package/lib/cli/init-templates.js.map +1 -0
- package/lib/cli/init.d.ts +7 -0
- package/lib/cli/init.d.ts.map +1 -1
- package/lib/cli/init.js +23 -173
- package/lib/cli/init.js.map +1 -1
- package/lib/cli/output-schemas.d.ts +1399 -0
- package/lib/cli/output-schemas.d.ts.map +1 -0
- package/lib/cli/output-schemas.js +91 -0
- package/lib/cli/output-schemas.js.map +1 -0
- package/lib/cli/program.d.ts +135 -4
- package/lib/cli/program.d.ts.map +1 -1
- package/lib/cli/program.js +725 -520
- package/lib/cli/program.js.map +1 -1
- package/lib/cli/render/reporter.d.ts.map +1 -1
- package/lib/cli/render/reporter.js +24 -4
- package/lib/cli/render/reporter.js.map +1 -1
- package/lib/cli/run-options.d.ts +0 -4
- package/lib/cli/run-options.d.ts.map +1 -1
- package/lib/cli/run-options.js +8 -19
- package/lib/cli/run-options.js.map +1 -1
- package/lib/cli/view.d.ts +5 -3
- package/lib/cli/view.d.ts.map +1 -1
- package/lib/cli/view.js +120 -78
- package/lib/cli/view.js.map +1 -1
- package/lib/core/annotation.d.ts +6 -2
- package/lib/core/annotation.d.ts.map +1 -1
- package/lib/core/annotation.js +2 -1
- package/lib/core/annotation.js.map +1 -1
- package/lib/core/answerer.d.ts +12 -0
- package/lib/core/answerer.d.ts.map +1 -1
- package/lib/core/answerer.js +2 -0
- package/lib/core/answerer.js.map +1 -1
- package/lib/core/assertion.d.ts +3 -0
- package/lib/core/assertion.d.ts.map +1 -1
- package/lib/core/assertion.js +3 -0
- package/lib/core/assertion.js.map +1 -1
- package/lib/core/case.d.ts +24 -2
- package/lib/core/case.d.ts.map +1 -1
- package/lib/core/case.js +5 -1
- package/lib/core/case.js.map +1 -1
- package/lib/core/driver.d.ts +40 -0
- package/lib/core/driver.d.ts.map +1 -1
- package/lib/core/driver.js +12 -0
- package/lib/core/driver.js.map +1 -1
- package/lib/core/environment.d.ts +18 -0
- package/lib/core/environment.d.ts.map +1 -0
- package/lib/core/environment.js +21 -0
- package/lib/core/environment.js.map +1 -0
- package/lib/core/evaluation-clusters.d.ts +17 -0
- package/lib/core/evaluation-clusters.d.ts.map +1 -0
- package/lib/core/evaluation-clusters.js +23 -0
- package/lib/core/evaluation-clusters.js.map +1 -0
- package/lib/core/experiment.d.ts +11 -0
- package/lib/core/experiment.d.ts.map +1 -1
- package/lib/core/experiment.js +31 -17
- package/lib/core/experiment.js.map +1 -1
- package/lib/core/file-tree.d.ts +7 -0
- package/lib/core/file-tree.d.ts.map +1 -0
- package/lib/core/file-tree.js +35 -0
- package/lib/core/file-tree.js.map +1 -0
- package/lib/core/index.d.ts +1 -0
- package/lib/core/index.d.ts.map +1 -1
- package/lib/core/index.js +1 -0
- package/lib/core/index.js.map +1 -1
- package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
- package/lib/core/lifecycle-fixtures.js +4 -2
- package/lib/core/lifecycle-fixtures.js.map +1 -1
- package/lib/core/platform-process.d.ts +5 -1
- package/lib/core/platform-process.d.ts.map +1 -1
- package/lib/core/platform-process.js +48 -10
- package/lib/core/platform-process.js.map +1 -1
- package/lib/core/run.d.ts +93 -1
- package/lib/core/run.d.ts.map +1 -1
- package/lib/core/run.js +16 -0
- package/lib/core/run.js.map +1 -1
- package/lib/core/usage.d.ts +5 -0
- package/lib/core/usage.d.ts.map +1 -1
- package/lib/core/usage.js +3 -0
- package/lib/core/usage.js.map +1 -1
- package/lib/drivers/claude-code/index.d.ts +65 -2
- package/lib/drivers/claude-code/index.d.ts.map +1 -1
- package/lib/drivers/claude-code/index.js +30 -3
- package/lib/drivers/claude-code/index.js.map +1 -1
- package/lib/drivers/codex/app-events.d.ts.map +1 -1
- package/lib/drivers/codex/app-events.js +41 -30
- package/lib/drivers/codex/app-events.js.map +1 -1
- package/lib/drivers/codex/exec-events.d.ts +4 -0
- package/lib/drivers/codex/exec-events.d.ts.map +1 -0
- package/lib/drivers/codex/exec-events.js +230 -0
- package/lib/drivers/codex/exec-events.js.map +1 -0
- package/lib/drivers/codex/index.d.ts +76 -4
- package/lib/drivers/codex/index.d.ts.map +1 -1
- package/lib/drivers/codex/index.js +35 -232
- package/lib/drivers/codex/index.js.map +1 -1
- package/lib/drivers/cursor/acp-transport.d.ts +38 -0
- package/lib/drivers/cursor/acp-transport.d.ts.map +1 -0
- package/lib/drivers/cursor/acp-transport.js +152 -0
- package/lib/drivers/cursor/acp-transport.js.map +1 -0
- package/lib/drivers/cursor/config.d.ts +22 -0
- package/lib/drivers/cursor/config.d.ts.map +1 -0
- package/lib/drivers/cursor/config.js +26 -0
- package/lib/drivers/cursor/config.js.map +1 -0
- package/lib/drivers/cursor/events.d.ts +13 -0
- package/lib/drivers/cursor/events.d.ts.map +1 -0
- package/lib/drivers/cursor/events.js +73 -0
- package/lib/drivers/cursor/events.js.map +1 -0
- package/lib/drivers/cursor/index.d.ts +37 -0
- package/lib/drivers/cursor/index.d.ts.map +1 -0
- package/lib/drivers/cursor/index.js +276 -0
- package/lib/drivers/cursor/index.js.map +1 -0
- package/lib/drivers/cursor/interaction.d.ts +7 -0
- package/lib/drivers/cursor/interaction.d.ts.map +1 -0
- package/lib/drivers/cursor/interaction.js +66 -0
- package/lib/drivers/cursor/interaction.js.map +1 -0
- package/lib/drivers/mcp/index.d.ts +35 -2
- package/lib/drivers/mcp/index.d.ts.map +1 -1
- package/lib/drivers/mcp/index.js +16 -3
- package/lib/drivers/mcp/index.js.map +1 -1
- package/lib/drivers/shared/experiment-behavior.d.ts +13 -0
- package/lib/drivers/shared/experiment-behavior.d.ts.map +1 -0
- package/lib/drivers/shared/experiment-behavior.js +197 -0
- package/lib/drivers/shared/experiment-behavior.js.map +1 -0
- package/lib/drivers/shared/experiment-policy.d.ts +4 -0
- package/lib/drivers/shared/experiment-policy.d.ts.map +1 -0
- package/lib/drivers/shared/experiment-policy.js +143 -0
- package/lib/drivers/shared/experiment-policy.js.map +1 -0
- package/lib/drivers/subprocess/index.d.ts +37 -2
- package/lib/drivers/subprocess/index.d.ts.map +1 -1
- package/lib/drivers/subprocess/index.js +46 -6
- package/lib/drivers/subprocess/index.js.map +1 -1
- package/lib/index.d.ts +6 -5
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -3
- package/lib/index.js.map +1 -1
- package/lib/orchestrator/annotation-loader.d.ts +6 -2
- package/lib/orchestrator/annotation-loader.d.ts.map +1 -1
- package/lib/orchestrator/annotation-loader.js +8 -0
- package/lib/orchestrator/annotation-loader.js.map +1 -1
- package/lib/orchestrator/annotation-store.d.ts.map +1 -1
- package/lib/orchestrator/annotation-store.js +16 -2
- package/lib/orchestrator/annotation-store.js.map +1 -1
- package/lib/orchestrator/campaign-lifecycle.d.ts +1 -0
- package/lib/orchestrator/campaign-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/campaign-lifecycle.js +4 -1
- package/lib/orchestrator/campaign-lifecycle.js.map +1 -1
- package/lib/orchestrator/compare.d.ts +4 -0
- package/lib/orchestrator/compare.d.ts.map +1 -1
- package/lib/orchestrator/compare.js +13 -1
- package/lib/orchestrator/compare.js.map +1 -1
- package/lib/orchestrator/comparison-identity.d.ts.map +1 -1
- package/lib/orchestrator/comparison-identity.js +1 -0
- package/lib/orchestrator/comparison-identity.js.map +1 -1
- package/lib/orchestrator/config.d.ts +42 -0
- package/lib/orchestrator/config.d.ts.map +1 -1
- package/lib/orchestrator/config.js +6 -2
- package/lib/orchestrator/config.js.map +1 -1
- package/lib/orchestrator/discover.d.ts +1 -0
- package/lib/orchestrator/discover.d.ts.map +1 -1
- package/lib/orchestrator/discover.js +7 -1
- package/lib/orchestrator/discover.js.map +1 -1
- package/lib/orchestrator/doctor.d.ts.map +1 -1
- package/lib/orchestrator/doctor.js +34 -26
- package/lib/orchestrator/doctor.js.map +1 -1
- package/lib/orchestrator/driver-capabilities.d.ts +3 -0
- package/lib/orchestrator/driver-capabilities.d.ts.map +1 -0
- package/lib/orchestrator/driver-capabilities.js +10 -0
- package/lib/orchestrator/driver-capabilities.js.map +1 -0
- package/lib/orchestrator/driver-identity.d.ts +5 -0
- package/lib/orchestrator/driver-identity.d.ts.map +1 -0
- package/lib/orchestrator/driver-identity.js +41 -0
- package/lib/orchestrator/driver-identity.js.map +1 -0
- package/lib/orchestrator/experiment-context.d.ts +10 -0
- package/lib/orchestrator/experiment-context.d.ts.map +1 -1
- package/lib/orchestrator/experiment-contracts.d.ts +10 -0
- package/lib/orchestrator/experiment-contracts.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.d.ts.map +1 -1
- package/lib/orchestrator/experiment-corpus.js +17 -19
- package/lib/orchestrator/experiment-corpus.js.map +1 -1
- package/lib/orchestrator/experiment-effects.d.ts +33 -0
- package/lib/orchestrator/experiment-effects.d.ts.map +1 -0
- package/lib/orchestrator/experiment-effects.js +44 -0
- package/lib/orchestrator/experiment-effects.js.map +1 -0
- package/lib/orchestrator/experiment-evaluation.d.ts.map +1 -1
- package/lib/orchestrator/experiment-evaluation.js +36 -3
- package/lib/orchestrator/experiment-evaluation.js.map +1 -1
- package/lib/orchestrator/experiment-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-identity.js +18 -29
- package/lib/orchestrator/experiment-identity.js.map +1 -1
- package/lib/orchestrator/experiment-loader.d.ts +7 -2
- package/lib/orchestrator/experiment-loader.d.ts.map +1 -1
- package/lib/orchestrator/experiment-report.d.ts +70 -0
- package/lib/orchestrator/experiment-report.d.ts.map +1 -1
- package/lib/orchestrator/experiment-report.js +9 -1
- package/lib/orchestrator/experiment-report.js.map +1 -1
- package/lib/orchestrator/experiment-runs.d.ts +22 -1
- package/lib/orchestrator/experiment-runs.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runs.js +13 -5
- package/lib/orchestrator/experiment-runs.js.map +1 -1
- package/lib/orchestrator/experiment-runtime-identity.d.ts +0 -16
- package/lib/orchestrator/experiment-runtime-identity.d.ts.map +1 -1
- package/lib/orchestrator/experiment-runtime-identity.js +5 -220
- package/lib/orchestrator/experiment-runtime-identity.js.map +1 -1
- package/lib/orchestrator/experiment-selection.d.ts.map +1 -1
- package/lib/orchestrator/experiment-selection.js +52 -0
- package/lib/orchestrator/experiment-selection.js.map +1 -1
- package/lib/orchestrator/experiment.d.ts +0 -2
- package/lib/orchestrator/experiment.d.ts.map +1 -1
- package/lib/orchestrator/experiment.js +23 -15
- package/lib/orchestrator/experiment.js.map +1 -1
- package/lib/orchestrator/fs-read.d.ts +1 -6
- package/lib/orchestrator/fs-read.d.ts.map +1 -1
- package/lib/orchestrator/fs-read.js +2 -32
- package/lib/orchestrator/fs-read.js.map +1 -1
- package/lib/orchestrator/grader-experiment.d.ts +17 -1
- package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
- package/lib/orchestrator/grader-experiment.js +52 -7
- package/lib/orchestrator/grader-experiment.js.map +1 -1
- package/lib/orchestrator/grading.d.ts.map +1 -1
- package/lib/orchestrator/grading.js +13 -6
- package/lib/orchestrator/grading.js.map +1 -1
- package/lib/orchestrator/list-runs.d.ts +1 -0
- package/lib/orchestrator/list-runs.d.ts.map +1 -1
- package/lib/orchestrator/list-runs.js +27 -6
- package/lib/orchestrator/list-runs.js.map +1 -1
- package/lib/orchestrator/optimization-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/optimization-lifecycle.js +3 -1
- package/lib/orchestrator/optimization-lifecycle.js.map +1 -1
- package/lib/orchestrator/orchestrator.d.ts +6 -1
- package/lib/orchestrator/orchestrator.d.ts.map +1 -1
- package/lib/orchestrator/orchestrator.js +17 -143
- package/lib/orchestrator/orchestrator.js.map +1 -1
- package/lib/orchestrator/paired-effects.d.ts +34 -0
- package/lib/orchestrator/paired-effects.d.ts.map +1 -0
- package/lib/orchestrator/paired-effects.js +158 -0
- package/lib/orchestrator/paired-effects.js.map +1 -0
- package/lib/orchestrator/provenance.d.ts +5 -1
- package/lib/orchestrator/provenance.d.ts.map +1 -1
- package/lib/orchestrator/provenance.js +16 -3
- package/lib/orchestrator/provenance.js.map +1 -1
- package/lib/orchestrator/regrade.d.ts +1 -0
- package/lib/orchestrator/regrade.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.d.ts +2 -0
- package/lib/orchestrator/run-execution.d.ts.map +1 -1
- package/lib/orchestrator/run-execution.js +18 -3
- package/lib/orchestrator/run-execution.js.map +1 -1
- package/lib/orchestrator/run-lifecycle.d.ts +1 -0
- package/lib/orchestrator/run-lifecycle.d.ts.map +1 -1
- package/lib/orchestrator/run-lifecycle.js +3 -2
- package/lib/orchestrator/run-lifecycle.js.map +1 -1
- package/lib/orchestrator/suite-runs.d.ts +3 -1
- package/lib/orchestrator/suite-runs.d.ts.map +1 -1
- package/lib/orchestrator/suite-runs.js +12 -2
- package/lib/orchestrator/suite-runs.js.map +1 -1
- package/lib/orchestrator/suite-summary.d.ts.map +1 -1
- package/lib/orchestrator/suite-summary.js +2 -1
- package/lib/orchestrator/suite-summary.js.map +1 -1
- package/lib/previewer/server.d.ts +6 -0
- package/lib/previewer/server.d.ts.map +1 -1
- package/lib/previewer/server.js +21 -5
- package/lib/previewer/server.js.map +1 -1
- package/package.json +2 -2
- package/skills/lux/references/cli.md +5 -2
- package/skills/lux/references/eval-authoring.md +0 -7
- package/skills/lux-answerer/SKILL.md +1 -1
- package/src/answerers/claude-code/index.ts +3 -3
- package/src/answerers/persona/index.ts +3 -3
- package/src/answerers/terminal/index.ts +4 -9
- package/src/assertions/core/resource-checks.ts +3 -5
- package/src/assertions/rubric/index.ts +7 -1
- package/src/assertions/rubric/internal.ts +13 -8
- package/src/authoring.ts +331 -2
- package/src/cli/bin.ts +2 -12
- package/src/cli/command-runtime.ts +20 -9
- package/src/cli/init-templates.ts +223 -0
- package/src/cli/init.ts +38 -184
- package/src/cli/output-schemas.ts +106 -0
- package/src/cli/program.ts +621 -548
- package/src/cli/render/reporter.ts +25 -4
- package/src/cli/run-options.ts +8 -25
- package/src/cli/view.ts +45 -84
- package/src/core/annotation.ts +2 -1
- package/src/core/answerer.ts +9 -0
- package/src/core/assertion.ts +3 -0
- package/src/core/case.ts +5 -1
- package/src/core/driver.ts +38 -0
- package/src/core/environment.ts +22 -0
- package/src/core/evaluation-clusters.ts +35 -0
- package/src/core/experiment.ts +60 -23
- package/src/core/file-tree.ts +35 -0
- package/src/core/index.ts +1 -0
- package/src/core/lifecycle-fixtures.ts +4 -2
- package/src/core/platform-process.ts +54 -6
- package/src/core/run.ts +20 -0
- package/src/core/usage.ts +8 -0
- package/src/drivers/claude-code/index.ts +34 -3
- package/src/drivers/codex/app-events.ts +43 -33
- package/src/drivers/codex/exec-events.ts +258 -0
- package/src/drivers/codex/index.ts +37 -262
- package/src/drivers/cursor/README.md +57 -0
- package/src/drivers/cursor/acp-transport.ts +182 -0
- package/src/drivers/cursor/config.ts +29 -0
- package/src/drivers/cursor/events.ts +83 -0
- package/src/drivers/cursor/index.ts +316 -0
- package/src/drivers/cursor/interaction.ts +90 -0
- package/src/drivers/mcp/index.ts +16 -3
- package/src/drivers/shared/experiment-behavior.ts +214 -0
- package/src/drivers/shared/experiment-policy.ts +195 -0
- package/src/drivers/subprocess/index.ts +45 -6
- package/src/index.ts +16 -5
- package/src/orchestrator/annotation-loader.ts +10 -0
- package/src/orchestrator/annotation-store.ts +24 -3
- package/src/orchestrator/campaign-lifecycle.ts +5 -1
- package/src/orchestrator/compare.ts +16 -1
- package/src/orchestrator/comparison-identity.ts +1 -0
- package/src/orchestrator/config.ts +6 -2
- package/src/orchestrator/discover.ts +8 -1
- package/src/orchestrator/doctor.ts +40 -22
- package/src/orchestrator/driver-capabilities.ts +16 -0
- package/src/orchestrator/driver-identity.ts +59 -0
- package/src/orchestrator/experiment-corpus.ts +23 -28
- package/src/orchestrator/experiment-effects.ts +61 -0
- package/src/orchestrator/experiment-evaluation.ts +45 -5
- package/src/orchestrator/experiment-identity.ts +27 -36
- package/src/orchestrator/experiment-report.ts +21 -1
- package/src/orchestrator/experiment-runs.ts +11 -4
- package/src/orchestrator/experiment-runtime-identity.ts +5 -243
- package/src/orchestrator/experiment-selection.ts +77 -0
- package/src/orchestrator/experiment.ts +26 -24
- package/src/orchestrator/fs-read.ts +2 -32
- package/src/orchestrator/grader-experiment.ts +72 -8
- package/src/orchestrator/grading.ts +15 -6
- package/src/orchestrator/list-runs.ts +29 -6
- package/src/orchestrator/optimization-lifecycle.ts +4 -1
- package/src/orchestrator/orchestrator.ts +26 -183
- package/src/orchestrator/paired-effects.ts +209 -0
- package/src/orchestrator/provenance.ts +23 -3
- package/src/orchestrator/run-execution.ts +18 -3
- package/src/orchestrator/run-lifecycle.ts +4 -2
- package/src/orchestrator/suite-runs.ts +10 -1
- package/src/orchestrator/suite-summary.ts +2 -1
- package/src/previewer/server.ts +27 -5
- package/viewer/app.js +27 -6
- package/viewer/detail.js +22 -5
- package/lib/answerers/scripted/index.d.ts +0 -18
- package/lib/answerers/scripted/index.d.ts.map +0 -1
- package/lib/answerers/scripted/index.js +0 -63
- package/lib/answerers/scripted/index.js.map +0 -1
- package/lib/cli/skills.d.ts +0 -38
- package/lib/cli/skills.d.ts.map +0 -1
- package/lib/cli/skills.js +0 -84
- package/lib/cli/skills.js.map +0 -1
- package/src/answerers/scripted/index.ts +0 -78
- package/src/cli/skills.ts +0 -129
|
@@ -5,20 +5,17 @@ import { ReflectiveOptimizerConfigSchema } from "../optimization/reflective.js";
|
|
|
5
5
|
import { authoredDependencyGraphHash } from "./authored-dependencies.js";
|
|
6
6
|
import { assertValidationArgumentsBound } from "./candidates.js";
|
|
7
7
|
import { selectCases } from "./discover.js";
|
|
8
|
+
import { experimentDriverIdentity, referencedDriverRuntime } from "./driver-identity.js";
|
|
8
9
|
import { evaluatorImplementationHash } from "./evaluator-identity.js";
|
|
9
10
|
import type { CampaignExecutionOptions, EvaluationCorpus } from "./experiment-context.js";
|
|
10
11
|
import { profileOrThrow } from "./experiment-context.js";
|
|
11
12
|
import { caseRubricCliReferences, selectorsBySplit } from "./experiment-corpus.js";
|
|
12
13
|
import {
|
|
13
|
-
claudeConfigRoot,
|
|
14
|
-
claudeUserBehaviorHash,
|
|
15
14
|
cliIdentity,
|
|
16
|
-
configuredClaudeSourceHashes,
|
|
17
15
|
LUX_VERSION,
|
|
18
16
|
type RuntimeCliReference,
|
|
19
17
|
RuntimeCliReferenceSchema,
|
|
20
18
|
referencedAnswererCli,
|
|
21
|
-
referencedCli,
|
|
22
19
|
runtimeCliIdentityHashForReferences,
|
|
23
20
|
} from "./experiment-runtime-identity.js";
|
|
24
21
|
import { loadEvalCase } from "./loader.js";
|
|
@@ -33,15 +30,6 @@ import { validatePluginConfig } from "./validate-plugin-config.js";
|
|
|
33
30
|
export const resolvedProfileSnapshot = async (
|
|
34
31
|
execution: CampaignExecutionOptions,
|
|
35
32
|
): Promise<Record<string, unknown>> => {
|
|
36
|
-
const behaviorHashes = new Map<string, Promise<string | null>>();
|
|
37
|
-
const configuredSourceHashes = new Map<string, Promise<string>>();
|
|
38
|
-
const behaviorHash = (root: string): Promise<string | null> => {
|
|
39
|
-
const existing = behaviorHashes.get(root);
|
|
40
|
-
if (existing) return existing;
|
|
41
|
-
const created = claudeUserBehaviorHash(root);
|
|
42
|
-
behaviorHashes.set(root, created);
|
|
43
|
-
return created;
|
|
44
|
-
};
|
|
45
33
|
return Object.fromEntries(
|
|
46
34
|
await Promise.all(
|
|
47
35
|
execution.loaded.campaign.profiles.map(async (profileId) => {
|
|
@@ -53,20 +41,12 @@ export const resolvedProfileSnapshot = async (
|
|
|
53
41
|
execution.loaded.campaign.evaluation.kind === "agent"
|
|
54
42
|
? (profile.driver ?? execution.config.defaultDriver)
|
|
55
43
|
: undefined;
|
|
56
|
-
const driverCli = referencedCli(driver);
|
|
57
44
|
const harness = profile.harness ?? execution.config.harness;
|
|
58
45
|
const answerer =
|
|
59
46
|
execution.loaded.campaign.evaluation.kind === "agent"
|
|
60
47
|
? (profile.answerer ?? execution.config.defaultAnswerer)
|
|
61
48
|
: undefined;
|
|
62
49
|
const answererCli = referencedAnswererCli(answerer);
|
|
63
|
-
const driverUserBehaviorHash =
|
|
64
|
-
driverCli?.name === "claude-code" ? await behaviorHash(claudeConfigRoot(driver)) : null;
|
|
65
|
-
const driverConfiguredSources = await configuredClaudeSourceHashes(
|
|
66
|
-
driver,
|
|
67
|
-
execution.config.rootDir,
|
|
68
|
-
configuredSourceHashes,
|
|
69
|
-
);
|
|
70
50
|
let harnessCommand = harness?.command;
|
|
71
51
|
if (!harnessCommand) {
|
|
72
52
|
harnessCommand = harness?.provider === "codex" ? "codex" : "claude";
|
|
@@ -80,17 +60,15 @@ export const resolvedProfileSnapshot = async (
|
|
|
80
60
|
profileId,
|
|
81
61
|
{
|
|
82
62
|
id: profileId,
|
|
63
|
+
environment: profile.environment ?? execution.config.environment ?? null,
|
|
64
|
+
configuredConcurrency: execution.config.concurrency,
|
|
65
|
+
evaluationConcurrency: 1,
|
|
83
66
|
driver: driver ?? null,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
driverClaudeConfigRoot: claudeConfigRoot(driver),
|
|
90
|
-
driverUserBehaviorHash,
|
|
91
|
-
driverConfiguredSources,
|
|
92
|
-
}
|
|
93
|
-
: {}),
|
|
67
|
+
driverIdentity: await experimentDriverIdentity(
|
|
68
|
+
driver,
|
|
69
|
+
execution.registry,
|
|
70
|
+
execution.config.rootDir,
|
|
71
|
+
),
|
|
94
72
|
answerer,
|
|
95
73
|
answererCliIdentity: answererCli
|
|
96
74
|
? await cliIdentity(answererCli.command, execution.config.rootDir)
|
|
@@ -130,7 +108,11 @@ const trustedExecutableEntrypoints = async (
|
|
|
130
108
|
}
|
|
131
109
|
for (const profileId of execution.loaded.campaign.profiles) {
|
|
132
110
|
const profile = profileOrThrow(execution.config, profileId);
|
|
133
|
-
|
|
111
|
+
for (const command of referencedDriverRuntime(
|
|
112
|
+
profile.driver ?? execution.config.defaultDriver,
|
|
113
|
+
execution.registry,
|
|
114
|
+
).commands)
|
|
115
|
+
add(command);
|
|
134
116
|
add(referencedAnswererCli(profile.answerer ?? execution.config.defaultAnswerer)?.command);
|
|
135
117
|
add((profile.harness ?? execution.config.harness)?.command);
|
|
136
118
|
}
|
|
@@ -147,7 +129,15 @@ const trustedExecutableEntrypoints = async (
|
|
|
147
129
|
await loadEvalCase(evalCase.path),
|
|
148
130
|
execution.loaded.subjectRoot,
|
|
149
131
|
);
|
|
150
|
-
|
|
132
|
+
for (const profileId of execution.loaded.campaign.profiles) {
|
|
133
|
+
const profile = profileOrThrow(execution.config, profileId);
|
|
134
|
+
const driver = resolveDriverReference(
|
|
135
|
+
authored,
|
|
136
|
+
profile.driver ?? execution.config.defaultDriver,
|
|
137
|
+
);
|
|
138
|
+
for (const command of referencedDriverRuntime(driver, execution.registry).commands)
|
|
139
|
+
add(command);
|
|
140
|
+
}
|
|
151
141
|
add(referencedAnswererCli(authored.answerer)?.command);
|
|
152
142
|
for (const reference of caseRubricCliReferences(execution, authored)) {
|
|
153
143
|
add(reference.command);
|
|
@@ -216,7 +206,8 @@ export const runtimeCliReferences = async (
|
|
|
216
206
|
profile.answerer ?? execution.config.defaultAnswerer,
|
|
217
207
|
harness,
|
|
218
208
|
);
|
|
219
|
-
|
|
209
|
+
for (const command of referencedDriverRuntime(driver, execution.registry).commands)
|
|
210
|
+
add(command, execution.config.rootDir);
|
|
220
211
|
add(referencedAnswererCli(answerer)?.command, execution.config.rootDir);
|
|
221
212
|
}
|
|
222
213
|
for (const reference of caseRubricCliReferences(execution, authored)) {
|
|
@@ -286,7 +277,6 @@ const validateAgentCaseProfileDrivers = async (
|
|
|
286
277
|
);
|
|
287
278
|
const name = typeof reference === "string" ? reference : reference.name;
|
|
288
279
|
const rawConfig = typeof reference === "object" ? reference.config : undefined;
|
|
289
|
-
boundExperimentDriverConfig(name, rawConfig, "identity-check");
|
|
290
280
|
const plugin = execution.registry.getDriver(name);
|
|
291
281
|
if (!plugin) {
|
|
292
282
|
throw new Error(
|
|
@@ -298,7 +288,7 @@ const validateAgentCaseProfileDrivers = async (
|
|
|
298
288
|
rawConfig,
|
|
299
289
|
`case '${evalCase.id}' profile '${profileId}' driver '${name}'`,
|
|
300
290
|
);
|
|
301
|
-
boundExperimentDriverConfig(
|
|
291
|
+
boundExperimentDriverConfig(plugin, validated, "identity-check");
|
|
302
292
|
}
|
|
303
293
|
}
|
|
304
294
|
};
|
|
@@ -334,6 +324,7 @@ export const evaluationIdentity = async (
|
|
|
334
324
|
luxVersion: LUX_VERSION,
|
|
335
325
|
evaluatorImplementationHash: await evaluatorImplementationHash(),
|
|
336
326
|
runtime: runtimeIdentity(),
|
|
327
|
+
concurrency: execution.config.concurrency,
|
|
337
328
|
corpusHash: corpus.hash,
|
|
338
329
|
profiles,
|
|
339
330
|
configSourceHash,
|
|
@@ -2,6 +2,7 @@ import { basename } from "node:path";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { valueHash } from "../core/index.js";
|
|
4
4
|
import { deriveExperimentSelection, rankCandidates } from "./experiment.js";
|
|
5
|
+
import { ExperimentEffectsSchema, experimentEffects } from "./experiment-effects.js";
|
|
5
6
|
import {
|
|
6
7
|
type ExperimentRun,
|
|
7
8
|
ExperimentRunSchema,
|
|
@@ -24,6 +25,7 @@ export const ExperimentDecisionReportSchema = z
|
|
|
24
25
|
status: z.enum(["running", "passed", "failed", "aborted"]),
|
|
25
26
|
outcome: z.enum(["promote", "retain", "reject", "error", "incomplete"]),
|
|
26
27
|
error: z.string().nullable(),
|
|
28
|
+
effects: z.exactOptional(ExperimentEffectsSchema),
|
|
27
29
|
candidateAndAccountingVerified: z.boolean(),
|
|
28
30
|
championCandidateId: z.string().nullable(),
|
|
29
31
|
ranking: z.array(z.string()),
|
|
@@ -39,6 +41,7 @@ export const ExperimentDecisionReportSchema = z
|
|
|
39
41
|
z
|
|
40
42
|
.object({
|
|
41
43
|
metric: z.string(),
|
|
44
|
+
relativeTo: z.exactOptional(z.literal("baseline")),
|
|
42
45
|
profile: z.string().nullable(),
|
|
43
46
|
split: ExperimentReportSplitSchema,
|
|
44
47
|
passed: z.boolean(),
|
|
@@ -596,6 +599,7 @@ const projectExperimentDecisionReport = (
|
|
|
596
599
|
evaluationKind: experiment.evaluationKind,
|
|
597
600
|
status: experiment.status,
|
|
598
601
|
error: experiment.errorMessage ?? null,
|
|
602
|
+
effects: experimentEffects(experiment),
|
|
599
603
|
candidateAndAccountingVerified,
|
|
600
604
|
outcome: reportOutcome(experiment, championId),
|
|
601
605
|
championCandidateId: championId,
|
|
@@ -610,6 +614,7 @@ const projectExperimentDecisionReport = (
|
|
|
610
614
|
.filter((constraint) => constraint.candidateId === targetId)
|
|
611
615
|
.map((constraint) => ({
|
|
612
616
|
metric: constraint.metric,
|
|
617
|
+
...(constraint.relativeTo ? { relativeTo: constraint.relativeTo } : {}),
|
|
613
618
|
profile: constraint.profile ?? null,
|
|
614
619
|
split: constraint.split,
|
|
615
620
|
passed: constraint.passed,
|
|
@@ -733,7 +738,7 @@ export const formatExperimentDecisionMarkdown = (report: ExperimentDecisionRepor
|
|
|
733
738
|
"| --- | --- | --- | --- | ---: | --- | --- |",
|
|
734
739
|
...report.gates.map(
|
|
735
740
|
(gate) =>
|
|
736
|
-
`| ${gate.passed ? "PASS" : "FAIL"} | ${markdown(gate.metric)} | ${markdown(gate.profile ?? "all")} | ${gate.split} | ${gate.values.map(concise).join(", ") || "—"} | ${markdown(gate.interval ?? "—")} | ${markdown(gate.note ?? "—")} |`,
|
|
741
|
+
`| ${gate.passed ? "PASS" : "FAIL"} | ${markdown(gate.metric)}${gate.relativeTo ? " Δ baseline" : ""} | ${markdown(gate.profile ?? "all")} | ${gate.split} | ${gate.values.map(concise).join(", ") || "—"} | ${markdown(gate.interval ?? "—")} | ${markdown(gate.note ?? "—")} |`,
|
|
737
742
|
),
|
|
738
743
|
);
|
|
739
744
|
}
|
|
@@ -763,6 +768,21 @@ export const formatExperimentDecisionMarkdown = (report: ExperimentDecisionRepor
|
|
|
763
768
|
),
|
|
764
769
|
);
|
|
765
770
|
}
|
|
771
|
+
if (report.effects?.length) {
|
|
772
|
+
lines.push(
|
|
773
|
+
"",
|
|
774
|
+
"## Paired case effects",
|
|
775
|
+
"",
|
|
776
|
+
"Differences are candidate minus baseline; repetitions are averaged within cases and authored clusters receive equal weight.",
|
|
777
|
+
"",
|
|
778
|
+
"| Candidate | Profile | Split | Metric | Mean difference | Cases | Clusters | Confidence | Note |",
|
|
779
|
+
"| --- | --- | --- | --- | ---: | ---: | ---: | --- | --- |",
|
|
780
|
+
...report.effects.map(
|
|
781
|
+
(effect) =>
|
|
782
|
+
`| ${markdown(effect.candidateId)} | ${markdown(effect.profileId)} | ${effect.split} | ${markdown(effect.metric)} | ${effect.meanDelta === null ? "—" : signed(effect.meanDelta)} | ${effect.caseCount ?? effect.caseIds.length} | ${effect.n} | ${markdown(intervalText(effect.interval ?? undefined) ?? "—")} | ${markdown(effect.note ?? "—")} |`,
|
|
783
|
+
),
|
|
784
|
+
);
|
|
785
|
+
}
|
|
766
786
|
lines.push(
|
|
767
787
|
"",
|
|
768
788
|
"## Source and spend",
|
|
@@ -86,6 +86,7 @@ export const ExperimentSelectionSchema = z.object({
|
|
|
86
86
|
z.object({
|
|
87
87
|
candidateId: CandidateIdSchema,
|
|
88
88
|
metric: z.string(),
|
|
89
|
+
relativeTo: z.exactOptional(z.literal("baseline")),
|
|
89
90
|
profile: z.exactOptional(z.string()),
|
|
90
91
|
split: z.enum(["train", "validation", "test"]),
|
|
91
92
|
values: z.array(z.number()),
|
|
@@ -738,6 +739,7 @@ export const resumeExperimentRun = async (path: string): Promise<OpenedExperimen
|
|
|
738
739
|
|
|
739
740
|
export const listExperimentRuns = async (
|
|
740
741
|
runsRoot: string,
|
|
742
|
+
options: { onReadError?: (path: string, error: unknown) => void } = {},
|
|
741
743
|
): Promise<Array<ExperimentRun & { experimentDir: string }>> => {
|
|
742
744
|
const root = join(resolve(runsRoot), EXPERIMENTS_DIR);
|
|
743
745
|
if (!existsSync(root)) return [];
|
|
@@ -745,10 +747,15 @@ export const listExperimentRuns = async (
|
|
|
745
747
|
.filter((entry) => entry.isDirectory())
|
|
746
748
|
.map((entry) => join(root, entry.name));
|
|
747
749
|
const runs = await Promise.all(
|
|
748
|
-
directories.map(async (experimentDir) =>
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
750
|
+
directories.map(async (experimentDir) => {
|
|
751
|
+
try {
|
|
752
|
+
return { experimentDir, experiment: await loadExperimentRun(experimentDir) };
|
|
753
|
+
} catch (error) {
|
|
754
|
+
if (!options.onReadError) throw error;
|
|
755
|
+
options.onReadError(experimentDir, error);
|
|
756
|
+
return { experimentDir, experiment: null };
|
|
757
|
+
}
|
|
758
|
+
}),
|
|
752
759
|
);
|
|
753
760
|
return runs
|
|
754
761
|
.flatMap(({ experimentDir, experiment }) =>
|
|
@@ -1,61 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { access, constants, lstat, open,
|
|
3
|
-
import { homedir } from "node:os";
|
|
2
|
+
import { access, constants, lstat, open, realpath } from "node:fs/promises";
|
|
4
3
|
import { delimiter, isAbsolute, join, resolve } from "node:path";
|
|
5
4
|
import { z } from "zod";
|
|
6
|
-
import {
|
|
7
|
-
import { walkAuthoredFiles } from "./fs-read.js";
|
|
8
|
-
import { boundExperimentDriverConfig } from "./orchestrator.js";
|
|
5
|
+
import { discoverCliVersion, valueHash } from "../core/index.js";
|
|
9
6
|
|
|
10
7
|
export { LUX_VERSION } from "./package-version.js";
|
|
11
8
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
const ClaudeInstalledPluginRecordSchema = z.looseObject({
|
|
16
|
-
installPath: z.string(),
|
|
17
|
-
});
|
|
18
|
-
const ClaudeInstalledPluginsFileSchema = z.looseObject({
|
|
19
|
-
plugins: z.record(z.string(), z.array(ClaudeInstalledPluginRecordSchema)),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
export const referencedCli = (
|
|
23
|
-
reference: unknown,
|
|
24
|
-
): { name: string; command: string } | undefined => {
|
|
25
|
-
let name: string | undefined;
|
|
26
|
-
if (typeof reference === "string") {
|
|
27
|
-
name = reference;
|
|
28
|
-
} else if (
|
|
29
|
-
typeof reference === "object" &&
|
|
30
|
-
reference !== null &&
|
|
31
|
-
typeof (reference as { name?: unknown }).name === "string"
|
|
32
|
-
) {
|
|
33
|
-
name = (reference as { name: string }).name;
|
|
34
|
-
}
|
|
35
|
-
const config =
|
|
36
|
-
typeof reference === "object" && reference !== null
|
|
37
|
-
? (reference as { config?: unknown }).config
|
|
38
|
-
: undefined;
|
|
39
|
-
if (name === "subprocess") {
|
|
40
|
-
if (
|
|
41
|
-
typeof config !== "object" ||
|
|
42
|
-
config === null ||
|
|
43
|
-
typeof (config as { command?: unknown }).command !== "string"
|
|
44
|
-
) {
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
return { name, command: (config as { command: string }).command };
|
|
48
|
-
}
|
|
49
|
-
if (name !== "claude-code" && name !== "codex") return undefined;
|
|
50
|
-
let command = name === "codex" ? "codex" : "claude";
|
|
51
|
-
if (
|
|
52
|
-
typeof config === "object" &&
|
|
53
|
-
config !== null &&
|
|
54
|
-
typeof (config as { command?: unknown }).command === "string"
|
|
55
|
-
) {
|
|
56
|
-
command = (config as { command: string }).command;
|
|
57
|
-
}
|
|
58
|
-
return { name, command };
|
|
9
|
+
const driverConfigRecord = (reference: unknown): Record<string, unknown> => {
|
|
10
|
+
const config = (reference as { config?: unknown } | null)?.config;
|
|
11
|
+
return config && typeof config === "object" ? (config as Record<string, unknown>) : {};
|
|
59
12
|
};
|
|
60
13
|
|
|
61
14
|
export const referencedAnswererCli = (
|
|
@@ -261,197 +214,6 @@ export const cliIdentity = async (command: string, baseRoot: string): Promise<Cl
|
|
|
261
214
|
});
|
|
262
215
|
};
|
|
263
216
|
|
|
264
|
-
const optionalRegularFileHash = async (path: string): Promise<string | null> => {
|
|
265
|
-
try {
|
|
266
|
-
const info = await lstat(path);
|
|
267
|
-
if (info.isSymbolicLink() || !info.isFile()) {
|
|
268
|
-
throw new Error(`behavior-affecting settings must be a regular file: ${path}`);
|
|
269
|
-
}
|
|
270
|
-
return contentHash(await readFile(path));
|
|
271
|
-
} catch (error) {
|
|
272
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
273
|
-
throw error;
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const driverConfigRecord = (driver: unknown): Record<string, unknown> => {
|
|
278
|
-
if (!driver || typeof driver !== "object") return {};
|
|
279
|
-
const config = (driver as { config?: unknown }).config;
|
|
280
|
-
return config && typeof config === "object" ? (config as Record<string, unknown>) : {};
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
export const claudeConfigRoot = (driver: unknown): string => {
|
|
284
|
-
const config = driverConfigRecord(driver);
|
|
285
|
-
const env =
|
|
286
|
-
config.env && typeof config.env === "object"
|
|
287
|
-
? (config.env as Record<string, unknown>)
|
|
288
|
-
: undefined;
|
|
289
|
-
const configured = env?.CLAUDE_CONFIG_DIR;
|
|
290
|
-
const root =
|
|
291
|
-
typeof configured === "string"
|
|
292
|
-
? configured
|
|
293
|
-
: (process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"));
|
|
294
|
-
if (!isAbsolute(root)) {
|
|
295
|
-
throw new Error("CLAUDE_CONFIG_DIR must be absolute for reproducible experiment identity");
|
|
296
|
-
}
|
|
297
|
-
return resolve(root);
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const hashBehaviorDirectory = async (
|
|
301
|
-
root: string,
|
|
302
|
-
label: string,
|
|
303
|
-
): Promise<Array<{ path: string; hash: string }>> => {
|
|
304
|
-
try {
|
|
305
|
-
const info = await lstat(root);
|
|
306
|
-
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
307
|
-
throw new Error(`behavior-affecting Claude source must be a real directory: ${root}`);
|
|
308
|
-
}
|
|
309
|
-
} catch (error) {
|
|
310
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
311
|
-
throw error;
|
|
312
|
-
}
|
|
313
|
-
const paths = await walkAuthoredFiles(root);
|
|
314
|
-
if (paths.length > 5_000) {
|
|
315
|
-
throw new Error(`behavior-affecting Claude source '${label}' exceeds 5000 files`);
|
|
316
|
-
}
|
|
317
|
-
let bytes = 0;
|
|
318
|
-
const hashes: Array<{ path: string; hash: string }> = [];
|
|
319
|
-
for (const path of paths) {
|
|
320
|
-
const content = await readFile(join(root, path));
|
|
321
|
-
bytes += content.length;
|
|
322
|
-
if (bytes > 256 * 1024 * 1024) {
|
|
323
|
-
throw new Error(`behavior-affecting Claude source '${label}' exceeds 256 MiB`);
|
|
324
|
-
}
|
|
325
|
-
hashes.push({ path: `${label}/${path}`, hash: contentHash(content) });
|
|
326
|
-
}
|
|
327
|
-
return hashes;
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Hash every intentionally loaded user behavior source without persisting its
|
|
332
|
-
* contents. This covers user instructions/rules/output styles and the exact
|
|
333
|
-
* installed contents of plugins enabled by settings.json.
|
|
334
|
-
*/
|
|
335
|
-
export const claudeUserBehaviorHash = async (configRoot: string): Promise<string | null> => {
|
|
336
|
-
const settingsPath = join(configRoot, "settings.json");
|
|
337
|
-
const settingsHash = await optionalRegularFileHash(settingsPath);
|
|
338
|
-
const sources: Array<{ path: string; hash: string }> = [];
|
|
339
|
-
if (settingsHash) sources.push({ path: "settings.json", hash: settingsHash });
|
|
340
|
-
const claudeMdHash = await optionalRegularFileHash(join(configRoot, "CLAUDE.md"));
|
|
341
|
-
if (claudeMdHash) sources.push({ path: "CLAUDE.md", hash: claudeMdHash });
|
|
342
|
-
for (const directory of ["rules", "output-styles", "agents", "commands", "skills"]) {
|
|
343
|
-
sources.push(...(await hashBehaviorDirectory(join(configRoot, directory), directory)));
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
let settings: unknown;
|
|
347
|
-
try {
|
|
348
|
-
const contents = await readFile(settingsPath, "utf8");
|
|
349
|
-
try {
|
|
350
|
-
settings = JSON.parse(contents);
|
|
351
|
-
} catch {
|
|
352
|
-
throw new Error("Claude settings.json contains invalid JSON");
|
|
353
|
-
}
|
|
354
|
-
} catch (error) {
|
|
355
|
-
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
356
|
-
settings = undefined;
|
|
357
|
-
} else {
|
|
358
|
-
throw error;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
const parsedSettings = ClaudeSettingsFileSchema.safeParse(settings);
|
|
362
|
-
if (settings !== undefined && !parsedSettings.success) {
|
|
363
|
-
throw new Error("Claude settings.json has an invalid enabledPlugins map");
|
|
364
|
-
}
|
|
365
|
-
const enabledPlugins = parsedSettings.success ? parsedSettings.data.enabledPlugins : undefined;
|
|
366
|
-
const enabledIds = Object.entries(enabledPlugins ?? {})
|
|
367
|
-
.filter(([, value]) => value === true)
|
|
368
|
-
.map(([id]) => id)
|
|
369
|
-
.sort();
|
|
370
|
-
if (enabledIds.length > 0) {
|
|
371
|
-
const installedPath = join(configRoot, "plugins", "installed_plugins.json");
|
|
372
|
-
const installedHash = await optionalRegularFileHash(installedPath);
|
|
373
|
-
if (installedHash) {
|
|
374
|
-
sources.push({ path: "plugins/installed_plugins.json", hash: installedHash });
|
|
375
|
-
}
|
|
376
|
-
let installed: unknown;
|
|
377
|
-
try {
|
|
378
|
-
installed = JSON.parse(await readFile(installedPath, "utf8"));
|
|
379
|
-
} catch {
|
|
380
|
-
installed = undefined;
|
|
381
|
-
}
|
|
382
|
-
const parsedInstalled = ClaudeInstalledPluginsFileSchema.safeParse(installed);
|
|
383
|
-
if (!parsedInstalled.success) {
|
|
384
|
-
throw new Error("enabled Claude plugins have invalid installed metadata");
|
|
385
|
-
}
|
|
386
|
-
const plugins = parsedInstalled.data.plugins;
|
|
387
|
-
for (const id of enabledIds) {
|
|
388
|
-
const records = plugins[id];
|
|
389
|
-
if (!records || records.length === 0) {
|
|
390
|
-
throw new Error(`enabled Claude plugin '${id}' has no installed content to hash`);
|
|
391
|
-
}
|
|
392
|
-
for (const [index, record] of records.entries()) {
|
|
393
|
-
const installPath = record.installPath;
|
|
394
|
-
sources.push(
|
|
395
|
-
...(await hashBehaviorDirectory(resolve(installPath), `plugins/enabled/${id}/${index}`)),
|
|
396
|
-
);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
return sources.length === 0 ? null : valueHash(sources);
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
export const configuredClaudeSourceHashes = async (
|
|
404
|
-
driver: unknown,
|
|
405
|
-
baseRoot: string,
|
|
406
|
-
cache: Map<string, Promise<string>> = new Map(),
|
|
407
|
-
): Promise<Array<{ kind: "plugin" | "read"; path: string; hash: string }>> => {
|
|
408
|
-
const driverCli = referencedCli(driver);
|
|
409
|
-
if (!driverCli) return [];
|
|
410
|
-
const config = driverConfigRecord(driver);
|
|
411
|
-
// This also rejects path-bearing extraArgs that would evade the source
|
|
412
|
-
// hashes below. Runtime applies the same guard before driver creation.
|
|
413
|
-
boundExperimentDriverConfig(driverCli.name, config, "identity-check");
|
|
414
|
-
const isolation =
|
|
415
|
-
config.isolation && typeof config.isolation === "object"
|
|
416
|
-
? (config.isolation as Record<string, unknown>)
|
|
417
|
-
: undefined;
|
|
418
|
-
const configured = [
|
|
419
|
-
...(driverCli.name === "claude-code" && Array.isArray(config.pluginDirs)
|
|
420
|
-
? config.pluginDirs.map((path) => ({ kind: "plugin" as const, path }))
|
|
421
|
-
: []),
|
|
422
|
-
...(Array.isArray(config.addDirs)
|
|
423
|
-
? config.addDirs.map((path) => ({ kind: "read" as const, path }))
|
|
424
|
-
: []),
|
|
425
|
-
...(driverCli.name === "claude-code" && isolation && Array.isArray(isolation.allowRead)
|
|
426
|
-
? isolation.allowRead.map((path) => ({ kind: "read" as const, path }))
|
|
427
|
-
: []),
|
|
428
|
-
].filter(
|
|
429
|
-
(entry): entry is { kind: "plugin" | "read"; path: string } =>
|
|
430
|
-
typeof entry.path === "string" && entry.path.length > 0,
|
|
431
|
-
);
|
|
432
|
-
return Promise.all(
|
|
433
|
-
configured
|
|
434
|
-
.sort((a, b) => `${a.kind}:${a.path}`.localeCompare(`${b.kind}:${b.path}`))
|
|
435
|
-
.map(async ({ kind, path }) => {
|
|
436
|
-
const absolute = resolve(baseRoot, path);
|
|
437
|
-
const key = `${kind}:${absolute}`;
|
|
438
|
-
let hash = cache.get(key);
|
|
439
|
-
if (!hash) {
|
|
440
|
-
hash = hashBehaviorDirectory(
|
|
441
|
-
absolute,
|
|
442
|
-
`driver/${kind}/${contentHash(absolute).slice(0, 12)}`,
|
|
443
|
-
).then(valueHash);
|
|
444
|
-
cache.set(key, hash);
|
|
445
|
-
}
|
|
446
|
-
return {
|
|
447
|
-
kind,
|
|
448
|
-
path: absolute,
|
|
449
|
-
hash: await hash,
|
|
450
|
-
};
|
|
451
|
-
}),
|
|
452
|
-
);
|
|
453
|
-
};
|
|
454
|
-
|
|
455
217
|
export const runtimeCliIdentityHashForReferences = async (
|
|
456
218
|
references: readonly RuntimeCliReference[],
|
|
457
219
|
): Promise<string> =>
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ExperimentSelectionSchema,
|
|
14
14
|
type OpenedExperimentRun,
|
|
15
15
|
} from "./experiment-runs.js";
|
|
16
|
+
import { confidenceGateIssue, type PairedEffect, pairedEffect } from "./paired-effects.js";
|
|
16
17
|
|
|
17
18
|
type ExperimentSplit = "train" | "validation" | "test";
|
|
18
19
|
|
|
@@ -103,6 +104,7 @@ const satisfiesConstraint = (
|
|
|
103
104
|
constraint: ExperimentConstraint,
|
|
104
105
|
experiment: OpenedExperimentRun["manifest"],
|
|
105
106
|
): boolean => {
|
|
107
|
+
if (constraint.relativeTo && candidate.id === "baseline") return true;
|
|
106
108
|
let profiles: (string | undefined)[];
|
|
107
109
|
if (constraint.metric === "subject.bytes" || constraint.profile) {
|
|
108
110
|
profiles = [constraint.profile];
|
|
@@ -110,6 +112,16 @@ const satisfiesConstraint = (
|
|
|
110
112
|
profiles = campaign.profiles;
|
|
111
113
|
}
|
|
112
114
|
return profiles.every((profile) => {
|
|
115
|
+
if (constraint.relativeTo) {
|
|
116
|
+
const effect = relativeConstraintEffect(candidate, constraint, profile, experiment);
|
|
117
|
+
if (effect.note || effect.meanDelta === null) return false;
|
|
118
|
+
const lower = effect.interval?.lower ?? effect.meanDelta;
|
|
119
|
+
const upper = effect.interval?.upper ?? effect.meanDelta;
|
|
120
|
+
return (
|
|
121
|
+
(constraint.min === undefined || lower >= constraint.min) &&
|
|
122
|
+
(constraint.max === undefined || upper <= constraint.max)
|
|
123
|
+
);
|
|
124
|
+
}
|
|
113
125
|
const values = metricValues(
|
|
114
126
|
candidate,
|
|
115
127
|
constraint.metric,
|
|
@@ -135,6 +147,7 @@ const satisfiesConstraint = (
|
|
|
135
147
|
return sampleGroups.every((samples) => {
|
|
136
148
|
try {
|
|
137
149
|
const interval = bootstrapMeanConfidenceInterval(samples, constraint.confidence);
|
|
150
|
+
if (confidenceGateIssue(interval)) return false;
|
|
138
151
|
return (
|
|
139
152
|
(constraint.min === undefined || interval.lower >= constraint.min) &&
|
|
140
153
|
(constraint.max === undefined || interval.upper <= constraint.max)
|
|
@@ -156,12 +169,29 @@ const satisfiesConstraint = (
|
|
|
156
169
|
export const average = (values: readonly number[]): number =>
|
|
157
170
|
values.reduce((sum, value) => sum + value, 0) / Math.max(values.length, 1);
|
|
158
171
|
|
|
172
|
+
const relativeConstraintEffect = (
|
|
173
|
+
candidate: ExperimentDecisionCandidate,
|
|
174
|
+
constraint: ExperimentConstraint,
|
|
175
|
+
profile: string | undefined,
|
|
176
|
+
experiment: OpenedExperimentRun["manifest"],
|
|
177
|
+
): PairedEffect => {
|
|
178
|
+
const scope = (entry: ExperimentEvaluation): boolean =>
|
|
179
|
+
entry.profileId === profile && entry.split === constraint.split;
|
|
180
|
+
return pairedEffect(
|
|
181
|
+
candidateEvaluations(experiment, "baseline").find(scope),
|
|
182
|
+
candidateEvaluations(experiment, candidate.id).find(scope),
|
|
183
|
+
constraint.metric,
|
|
184
|
+
constraint.confidence,
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
159
188
|
const normalizedConstraintViolation = (
|
|
160
189
|
campaign: ExperimentCampaign,
|
|
161
190
|
candidate: ExperimentDecisionCandidate,
|
|
162
191
|
constraint: ExperimentConstraint,
|
|
163
192
|
experiment: OpenedExperimentRun["manifest"],
|
|
164
193
|
): number => {
|
|
194
|
+
if (constraint.relativeTo && candidate.id === "baseline") return 0;
|
|
165
195
|
let profiles: Array<string | undefined> = campaign.profiles;
|
|
166
196
|
if (constraint.metric === "subject.bytes" || constraint.profile) {
|
|
167
197
|
profiles = [constraint.profile];
|
|
@@ -177,6 +207,18 @@ const normalizedConstraintViolation = (
|
|
|
177
207
|
);
|
|
178
208
|
let worst = 0;
|
|
179
209
|
for (const profile of profiles) {
|
|
210
|
+
if (constraint.relativeTo) {
|
|
211
|
+
const effect = relativeConstraintEffect(candidate, constraint, profile, experiment);
|
|
212
|
+
if (effect.note || effect.meanDelta === null) return Number.POSITIVE_INFINITY;
|
|
213
|
+
worst = Math.max(
|
|
214
|
+
worst,
|
|
215
|
+
violation(
|
|
216
|
+
effect.interval?.lower ?? effect.meanDelta,
|
|
217
|
+
effect.interval?.upper ?? effect.meanDelta,
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
180
222
|
if (constraint.confidence) {
|
|
181
223
|
const groups = metricSampleGroups(
|
|
182
224
|
candidate,
|
|
@@ -189,6 +231,7 @@ const normalizedConstraintViolation = (
|
|
|
189
231
|
for (const samples of groups) {
|
|
190
232
|
try {
|
|
191
233
|
const interval = bootstrapMeanConfidenceInterval(samples, constraint.confidence);
|
|
234
|
+
if (confidenceGateIssue(interval)) return Number.POSITIVE_INFINITY;
|
|
192
235
|
worst = Math.max(worst, violation(interval.lower, interval.upper));
|
|
193
236
|
} catch {
|
|
194
237
|
return Number.POSITIVE_INFINITY;
|
|
@@ -379,6 +422,36 @@ export const searchFrontier = (
|
|
|
379
422
|
});
|
|
380
423
|
};
|
|
381
424
|
|
|
425
|
+
const relativeConstraintReport = (
|
|
426
|
+
campaign: ExperimentCampaign,
|
|
427
|
+
candidate: ExperimentDecisionCandidate,
|
|
428
|
+
constraint: ExperimentConstraint,
|
|
429
|
+
profile: string | undefined,
|
|
430
|
+
experiment: OpenedExperimentRun["manifest"],
|
|
431
|
+
): ExperimentSelection["constraints"][number] => {
|
|
432
|
+
const effect = relativeConstraintEffect(candidate, constraint, profile, experiment);
|
|
433
|
+
const reference = candidate.id === "baseline";
|
|
434
|
+
let values = effect.meanDelta === null ? [] : [effect.meanDelta];
|
|
435
|
+
if (reference) values = [0];
|
|
436
|
+
const note = reference ? "baseline reference; relative gate does not apply" : effect.note;
|
|
437
|
+
return {
|
|
438
|
+
candidateId: candidate.id,
|
|
439
|
+
metric: constraint.metric,
|
|
440
|
+
relativeTo: "baseline",
|
|
441
|
+
...(profile ? { profile } : {}),
|
|
442
|
+
split: constraint.split,
|
|
443
|
+
values,
|
|
444
|
+
passed: satisfiesConstraint(
|
|
445
|
+
campaign,
|
|
446
|
+
candidate,
|
|
447
|
+
{ ...constraint, ...(profile ? { profile } : {}) },
|
|
448
|
+
experiment,
|
|
449
|
+
),
|
|
450
|
+
...(!reference && effect.interval ? { interval: effect.interval } : {}),
|
|
451
|
+
...(note ? { note } : {}),
|
|
452
|
+
};
|
|
453
|
+
};
|
|
454
|
+
|
|
382
455
|
export const deriveExperimentSelection = (
|
|
383
456
|
campaign: ExperimentCampaign,
|
|
384
457
|
candidates: readonly ExperimentDecisionCandidate[],
|
|
@@ -405,6 +478,9 @@ export const deriveExperimentSelection = (
|
|
|
405
478
|
? [constraint.profile]
|
|
406
479
|
: campaign.profiles;
|
|
407
480
|
return profiles.map((profile) => {
|
|
481
|
+
if (constraint.relativeTo) {
|
|
482
|
+
return relativeConstraintReport(campaign, candidate, constraint, profile, experiment);
|
|
483
|
+
}
|
|
408
484
|
const values = metricValues(
|
|
409
485
|
candidate,
|
|
410
486
|
constraint.metric,
|
|
@@ -426,6 +502,7 @@ export const deriveExperimentSelection = (
|
|
|
426
502
|
if (samples) {
|
|
427
503
|
try {
|
|
428
504
|
interval = bootstrapMeanConfidenceInterval(samples, constraint.confidence);
|
|
505
|
+
note = confidenceGateIssue(interval);
|
|
429
506
|
} catch (error) {
|
|
430
507
|
note = error instanceof Error ? error.message : String(error);
|
|
431
508
|
}
|