@dailephd/my-dev-kit-lab 0.8.0 → 0.8.1
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/CHANGELOG.md +18 -0
- package/README.md +6 -4
- package/dist/src/cli/help.js +20 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +8 -1
- package/dist/src/commands/runExperimentRunCommand.js +90 -4
- package/dist/src/evaluation/retrievalQuality/metrics.js +27 -2
- package/dist/src/evaluation/retrievalQueryStrategies.js +26 -0
- package/dist/src/evaluation/retrievalQueryStrategyEvidence.js +58 -0
- package/dist/src/evaluation/runMyDevKitRetrieval.js +59 -51
- package/dist/src/evaluation/runSemanticRetrievalStrategy.js +303 -0
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/contextWindowScaling/localSubjectPrivacy.js +2 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysis.js +172 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysisArtifact.js +28 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysisTypes.js +2 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/config.js +39 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/execution.js +225 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/executionArtifact.js +15 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/index.js +12 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/localSubjectExecution.js +124 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/localSubjectPrivacy.js +176 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/metadata.js +51 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/metrics.js +60 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/plugin.js +292 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/types.js +1 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +25 -2
- package/dist/src/report/experiments/buildRetrievalQueryStrategyComparisonReport.js +91 -0
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +9 -0
- package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonHtml.js +96 -0
- package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonText.js +58 -0
- package/dist/src/report/experiments/retrievalQueryStrategyComparisonReportModel.js +1 -0
- package/docs/METRICS.md +122 -2
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { RETRIEVAL_QUERY_STRATEGY_IDS } from "../../../evaluation/retrievalQueryStrategies.js";
|
|
2
|
+
/** Projects already-calculated scientific values onto generic metrics. Owns no formula. */
|
|
3
|
+
export function toRetrievalQueryStrategyOutcomeMetrics(treatment, caseId) {
|
|
4
|
+
const base = { variantId: treatment.strategyId, caseId };
|
|
5
|
+
const quality = treatment.quality;
|
|
6
|
+
const ratio = (id, name, metric) => ({
|
|
7
|
+
...base,
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
value: metric?.value ?? null,
|
|
11
|
+
unit: "ratio"
|
|
12
|
+
});
|
|
13
|
+
const count = (id, name, value) => ({
|
|
14
|
+
...base,
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
value: value ?? null,
|
|
18
|
+
unit: "count"
|
|
19
|
+
});
|
|
20
|
+
return [
|
|
21
|
+
ratio("file-precision", "File precision", quality?.file.precision),
|
|
22
|
+
ratio("file-recall", "File recall", quality?.file.recall),
|
|
23
|
+
ratio("file-f1", "File F1", treatment.fileF1),
|
|
24
|
+
ratio("symbol-precision", "Symbol precision", quality?.symbol.precision),
|
|
25
|
+
ratio("symbol-recall", "Symbol recall", quality?.symbol.recall),
|
|
26
|
+
ratio("symbol-f1", "Symbol F1", treatment.symbolF1),
|
|
27
|
+
ratio("fact-coverage", "Fact coverage", quality?.fact.coverage),
|
|
28
|
+
ratio("irrelevant-context-ratio", "Irrelevant context ratio", quality?.irrelevantContextRatio),
|
|
29
|
+
{ ...base, id: "retrieved-token-count", name: "Retrieved token count", value: quality?.retrievedTokenCount ?? null, unit: "tokens" },
|
|
30
|
+
count("missed-file-count", "Missed file count", quality?.file.missedFileCount),
|
|
31
|
+
count("missed-symbol-count", "Missed symbol count", quality?.symbol.missedSymbolCount),
|
|
32
|
+
count("uncovered-fact-count", "Uncovered fact count", quality?.fact.uncoveredFactCount),
|
|
33
|
+
count("irrelevant-retrieved-file-count", "Irrelevant retrieved file count", quality?.file.irrelevantRetrievedFiles?.length),
|
|
34
|
+
count("irrelevant-retrieved-symbol-count", "Irrelevant retrieved symbol count", quality?.symbol.irrelevantRetrievedSymbols?.length)
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
/** Per scope: one matched-case count, then four objective means per strategy in canonical order. No best/rank/score metric. */
|
|
38
|
+
export function toRetrievalQueryStrategyRunMetrics(analysis) {
|
|
39
|
+
const metrics = [];
|
|
40
|
+
for (const scope of analysis.scopes) {
|
|
41
|
+
const { scopeId } = scope;
|
|
42
|
+
metrics.push({
|
|
43
|
+
id: `${scopeId}-comparison-case-count`,
|
|
44
|
+
name: `${scopeId} matched comparison cases`,
|
|
45
|
+
value: scope.comparisonCaseCount,
|
|
46
|
+
unit: "count"
|
|
47
|
+
});
|
|
48
|
+
for (const strategyId of RETRIEVAL_QUERY_STRATEGY_IDS) {
|
|
49
|
+
const objectives = scope.strategySummaries.find((summary) => summary.strategyId === strategyId)?.objectives ?? null;
|
|
50
|
+
metrics.push({ id: `${scopeId}-mean-file-f1`, name: `${scopeId} mean file F1`, value: objectives?.meanFileF1 ?? null, unit: "ratio", variantId: strategyId }, { id: `${scopeId}-mean-symbol-f1`, name: `${scopeId} mean symbol F1`, value: objectives?.meanSymbolF1 ?? null, unit: "ratio", variantId: strategyId }, { id: `${scopeId}-mean-fact-coverage`, name: `${scopeId} mean fact coverage`, value: objectives?.meanFactCoverage ?? null, unit: "ratio", variantId: strategyId }, {
|
|
51
|
+
id: `${scopeId}-mean-retrieved-token-count`,
|
|
52
|
+
name: `${scopeId} mean retrieved token count`,
|
|
53
|
+
value: objectives?.meanRetrievedTokenCount ?? null,
|
|
54
|
+
unit: "tokens",
|
|
55
|
+
variantId: strategyId
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return metrics;
|
|
60
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { resolveWithinRoot } from "../../../core/pathSafety.js";
|
|
4
|
+
import { serializeLocalRepositorySubjectManifest } from "../../../evaluation/localRepositorySubject/index.js";
|
|
5
|
+
import { summarizeExperimentRun } from "../../results.js";
|
|
6
|
+
import { LocalSubjectExecutionError } from "../contextWindowScaling/localSubjectErrors.js";
|
|
7
|
+
import { projectExternalLocalTarget } from "../contextWindowScaling/localSubjectPrivacy.js";
|
|
8
|
+
import { isSamePhysicalDirectory } from "../contextWindowScaling/localSubjectScratch.js";
|
|
9
|
+
import { selectWarmIndexCases } from "../warmIndexReuse/selection.js";
|
|
10
|
+
import { defaultRetrievalQueryStrategyComparisonConfig, retrievalQueryStrategyComparisonConfigDefinition, validateRetrievalQueryStrategyComparisonConfig } from "./config.js";
|
|
11
|
+
import { analyzeRetrievalQueryStrategyComparison } from "./analysis.js";
|
|
12
|
+
import { buildRetrievalQueryStrategyComparisonAnalysisArtifact, RETRIEVAL_QUERY_STRATEGY_COMPARISON_ANALYSIS_ARTIFACT_FILE } from "./analysisArtifact.js";
|
|
13
|
+
import { executeRetrievalQueryStrategyCaseFromPreparedIndex, executeRetrievalQueryStrategyComparison } from "./execution.js";
|
|
14
|
+
import { executeLocalRepositorySubjectRetrievalQueryStrategyComparison } from "./localSubjectExecution.js";
|
|
15
|
+
import { assertRetrievalQueryStrategyExternalProjectionIsPrivate, describeRetrievalQueryStrategyLocalSubjectFailureForPersistence, projectRetrievalQueryStrategyAnalysisForExternalLocalPersistence, projectRetrievalQueryStrategyExecutionForExternalLocalPersistence, RETRIEVAL_QUERY_STRATEGY_UNEXPECTED_EXTERNAL_FAILURE_MESSAGE } from "./localSubjectPrivacy.js";
|
|
16
|
+
import { buildRetrievalQueryStrategyComparisonExecutionArtifact, RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXECUTION_ARTIFACT_FILE } from "./executionArtifact.js";
|
|
17
|
+
import { RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID, RETRIEVAL_QUERY_STRATEGY_COMPARISON_VARIANTS, retrievalQueryStrategyComparisonMetadata } from "./metadata.js";
|
|
18
|
+
import { toRetrievalQueryStrategyOutcomeMetrics, toRetrievalQueryStrategyRunMetrics } from "./metrics.js";
|
|
19
|
+
/** Privacy-safe subject manifest persisted beside the artifacts for external-local runs (same file as the other local-subject plugins). */
|
|
20
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_LOCAL_SUBJECT_MANIFEST_FILE = "local-repository-subject-manifest.json";
|
|
21
|
+
/** An external target must always arrive with a loaded local repository subject; there is no bundled fallback against it. */
|
|
22
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_REQUIRES_SUBJECT_MESSAGE = "External-local retrieval-query-strategy-comparison targets require a loaded local repository subject (--local-subject-config).";
|
|
23
|
+
export const retrievalQueryStrategyComparisonPlugin = {
|
|
24
|
+
metadata: retrievalQueryStrategyComparisonMetadata,
|
|
25
|
+
defaultConfig: defaultRetrievalQueryStrategyComparisonConfig,
|
|
26
|
+
configDefinition: retrievalQueryStrategyComparisonConfigDefinition,
|
|
27
|
+
supportedVariants: RETRIEVAL_QUERY_STRATEGY_COMPARISON_VARIANTS.map((variant) => variant.id),
|
|
28
|
+
validateConfig: validateRetrievalQueryStrategyComparisonConfig,
|
|
29
|
+
async run(context) {
|
|
30
|
+
const startedAt = context.startedAt.toISOString();
|
|
31
|
+
const localSubject = readLocalSubjectInput(context.inputs);
|
|
32
|
+
if (localSubject)
|
|
33
|
+
return runLocalSubject(context, localSubject, startedAt);
|
|
34
|
+
// The runner does not enforce supportedTargets; never fall back to the bundled corpus against an external repository.
|
|
35
|
+
if (context.target.kind !== "self" || context.target.isSelf !== true) {
|
|
36
|
+
throw new Error(RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_REQUIRES_SUBJECT_MESSAGE);
|
|
37
|
+
}
|
|
38
|
+
const cases = selectWarmIndexCases(readCasesInput(context.inputs), context.config);
|
|
39
|
+
const outDir = context.outputRoot ?? path.resolve(context.toolRoot, context.config.outDir);
|
|
40
|
+
await mkdir(outDir, { recursive: true });
|
|
41
|
+
const caseEvidence = await executeRetrievalQueryStrategyComparison({
|
|
42
|
+
cases,
|
|
43
|
+
kitCommand: context.config.kitCommand,
|
|
44
|
+
outputRoot: outDir,
|
|
45
|
+
dependencies: readDependenciesInput(context.inputs)
|
|
46
|
+
});
|
|
47
|
+
// Scientific analysis is calculated in memory before persistence; reports later consume it, never recalculate it.
|
|
48
|
+
const analysis = analyzeRetrievalQueryStrategyComparison(cases, caseEvidence);
|
|
49
|
+
const completedAt = new Date().toISOString();
|
|
50
|
+
const artifact = buildRetrievalQueryStrategyComparisonExecutionArtifact({
|
|
51
|
+
runId: context.runId,
|
|
52
|
+
pluginId: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
53
|
+
pluginSchemaVersion: retrievalQueryStrategyComparisonMetadata.schemaVersion,
|
|
54
|
+
startedAt,
|
|
55
|
+
completedAt,
|
|
56
|
+
cases: caseEvidence
|
|
57
|
+
});
|
|
58
|
+
const analysisArtifact = buildRetrievalQueryStrategyComparisonAnalysisArtifact({
|
|
59
|
+
runId: context.runId,
|
|
60
|
+
pluginId: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
61
|
+
pluginSchemaVersion: retrievalQueryStrategyComparisonMetadata.schemaVersion,
|
|
62
|
+
startedAt,
|
|
63
|
+
completedAt,
|
|
64
|
+
analysis
|
|
65
|
+
});
|
|
66
|
+
const executionArtifactPath = resolveWithinRoot(outDir, RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXECUTION_ARTIFACT_FILE);
|
|
67
|
+
const analysisArtifactPath = resolveWithinRoot(outDir, RETRIEVAL_QUERY_STRATEGY_COMPARISON_ANALYSIS_ARTIFACT_FILE);
|
|
68
|
+
await writeFile(executionArtifactPath, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
|
|
69
|
+
await writeFile(analysisArtifactPath, `${JSON.stringify(analysisArtifact, null, 2)}\n`, "utf8");
|
|
70
|
+
return mapRetrievalQueryStrategyComparisonToRun({
|
|
71
|
+
runId: context.runId,
|
|
72
|
+
startedAt,
|
|
73
|
+
completedAt,
|
|
74
|
+
target: context.target,
|
|
75
|
+
caseEvidence,
|
|
76
|
+
analysis,
|
|
77
|
+
executionArtifactPath,
|
|
78
|
+
analysisArtifactPath
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
summarize(result) {
|
|
82
|
+
return result.summary ?? summarizeExperimentRun(result);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
function readLocalSubjectInput(inputs) {
|
|
86
|
+
const value = inputs?.localSubject;
|
|
87
|
+
return value && typeof value === "object" ? value : undefined;
|
|
88
|
+
}
|
|
89
|
+
/** Test seams for the external path derive from the same optional dependency input the bundled path reads. */
|
|
90
|
+
function readLocalSubjectDependencies(inputs) {
|
|
91
|
+
const injected = readDependenciesInput(inputs);
|
|
92
|
+
if (!injected)
|
|
93
|
+
return undefined;
|
|
94
|
+
const { buildIndex, runCoreStrategy, runSemanticStrategy } = injected;
|
|
95
|
+
return {
|
|
96
|
+
...(buildIndex ? { buildIndex } : {}),
|
|
97
|
+
...(runCoreStrategy || runSemanticStrategy
|
|
98
|
+
? {
|
|
99
|
+
executeCase: (options) => executeRetrievalQueryStrategyCaseFromPreparedIndex({
|
|
100
|
+
...options,
|
|
101
|
+
dependencies: { ...(runCoreStrategy ? { runCoreStrategy } : {}), ...(runSemanticStrategy ? { runSemanticStrategy } : {}) }
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
: {})
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* External-local path. The safe seam owns execution, immutability and scratch cleanup and either returns evidence or
|
|
109
|
+
* throws. Scientific analysis is calculated from the real, unprojected identities; only then is the evidence
|
|
110
|
+
* privacy-projected and asserted private. Nothing durable is written before every gate has passed, and then in this
|
|
111
|
+
* order: execution artifact, analysis artifact, manifest.
|
|
112
|
+
*/
|
|
113
|
+
async function runLocalSubject(context, subject, startedAt) {
|
|
114
|
+
if (context.target.isSelf || context.target.kind !== "external-local") {
|
|
115
|
+
throw new Error("Local-repository subject mode requires an external-local target.");
|
|
116
|
+
}
|
|
117
|
+
if (context.config.caseIds !== undefined || context.config.benchmarkProjects !== undefined) {
|
|
118
|
+
throw new Error("Case and benchmark-project filters are not supported for an external local repository subject; the subject config owns the case set.");
|
|
119
|
+
}
|
|
120
|
+
if (!(await isSamePhysicalDirectory(context.target.targetRoot, subject.repositoryRoot))) {
|
|
121
|
+
throw new Error("The selected --target is not the repository the local subject was loaded from.");
|
|
122
|
+
}
|
|
123
|
+
const outDir = context.outputRoot;
|
|
124
|
+
if (!outDir)
|
|
125
|
+
throw new Error("Local-repository subject mode requires an experiment output directory.");
|
|
126
|
+
let localResult;
|
|
127
|
+
try {
|
|
128
|
+
localResult = await executeLocalRepositorySubjectRetrievalQueryStrategyComparison({
|
|
129
|
+
subject,
|
|
130
|
+
kitCommand: context.config.kitCommand,
|
|
131
|
+
workRoot: outDir,
|
|
132
|
+
dependencies: readLocalSubjectDependencies(context.inputs)
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof LocalSubjectExecutionError)
|
|
137
|
+
throw new Error(describeRetrievalQueryStrategyLocalSubjectFailureForPersistence(error));
|
|
138
|
+
throw new Error(RETRIEVAL_QUERY_STRATEGY_UNEXPECTED_EXTERNAL_FAILURE_MESSAGE);
|
|
139
|
+
}
|
|
140
|
+
// Science first, on real identities; placeholders are never scientific identities.
|
|
141
|
+
const analysis = analyzeRetrievalQueryStrategyComparison(subject.evaluationCases, localResult.caseEvidence);
|
|
142
|
+
const projectedExecution = projectRetrievalQueryStrategyExecutionForExternalLocalPersistence(localResult.caseEvidence);
|
|
143
|
+
const projectedAnalysis = projectRetrievalQueryStrategyAnalysisForExternalLocalPersistence(analysis);
|
|
144
|
+
const privateValues = {
|
|
145
|
+
filePaths: [...subject.eligibleFiles, ...subject.runtimeSafetyExclusions.gitIgnoredFiles, ...subject.runtimeSafetyExclusions.oversizedFiles],
|
|
146
|
+
caseTitles: subject.evaluationCases.map((evaluationCase) => evaluationCase.title),
|
|
147
|
+
warnings: localResult.caseEvidence.flatMap((entry) => entry.treatments.flatMap((treatment) => treatment.retrieval?.warnings ?? [])),
|
|
148
|
+
privateRoots: [subject.repositoryRoot, outDir]
|
|
149
|
+
};
|
|
150
|
+
assertRetrievalQueryStrategyExternalProjectionIsPrivate(projectedExecution, privateValues);
|
|
151
|
+
assertRetrievalQueryStrategyExternalProjectionIsPrivate(projectedAnalysis, privateValues);
|
|
152
|
+
await mkdir(outDir, { recursive: true });
|
|
153
|
+
const completedAt = new Date().toISOString();
|
|
154
|
+
const executionArtifact = buildRetrievalQueryStrategyComparisonExecutionArtifact({
|
|
155
|
+
runId: context.runId,
|
|
156
|
+
pluginId: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
157
|
+
pluginSchemaVersion: retrievalQueryStrategyComparisonMetadata.schemaVersion,
|
|
158
|
+
startedAt,
|
|
159
|
+
completedAt,
|
|
160
|
+
cases: projectedExecution
|
|
161
|
+
});
|
|
162
|
+
const analysisArtifact = buildRetrievalQueryStrategyComparisonAnalysisArtifact({
|
|
163
|
+
runId: context.runId,
|
|
164
|
+
pluginId: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
165
|
+
pluginSchemaVersion: retrievalQueryStrategyComparisonMetadata.schemaVersion,
|
|
166
|
+
startedAt,
|
|
167
|
+
completedAt,
|
|
168
|
+
analysis: projectedAnalysis
|
|
169
|
+
});
|
|
170
|
+
await writeFile(resolveWithinRoot(outDir, RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXECUTION_ARTIFACT_FILE), `${JSON.stringify(executionArtifact, null, 2)}\n`, "utf8");
|
|
171
|
+
await writeFile(resolveWithinRoot(outDir, RETRIEVAL_QUERY_STRATEGY_COMPARISON_ANALYSIS_ARTIFACT_FILE), `${JSON.stringify(analysisArtifact, null, 2)}\n`, "utf8");
|
|
172
|
+
await writeFile(resolveWithinRoot(outDir, RETRIEVAL_QUERY_STRATEGY_COMPARISON_LOCAL_SUBJECT_MANIFEST_FILE), serializeLocalRepositorySubjectManifest(subject.manifest), "utf8");
|
|
173
|
+
// Durable external run: logical target and basenames only; metrics come from the unchanged numeric analysis.
|
|
174
|
+
const run = mapRetrievalQueryStrategyComparisonToRun({
|
|
175
|
+
runId: context.runId,
|
|
176
|
+
startedAt,
|
|
177
|
+
completedAt,
|
|
178
|
+
target: projectExternalLocalTarget(subject.manifest),
|
|
179
|
+
caseEvidence: projectedExecution,
|
|
180
|
+
analysis: projectedAnalysis,
|
|
181
|
+
executionArtifactPath: RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXECUTION_ARTIFACT_FILE,
|
|
182
|
+
analysisArtifactPath: RETRIEVAL_QUERY_STRATEGY_COMPARISON_ANALYSIS_ARTIFACT_FILE
|
|
183
|
+
});
|
|
184
|
+
run.artifacts.push({
|
|
185
|
+
id: "local-repository-subject-manifest",
|
|
186
|
+
label: "Privacy-safe local repository subject manifest",
|
|
187
|
+
path: RETRIEVAL_QUERY_STRATEGY_COMPARISON_LOCAL_SUBJECT_MANIFEST_FILE,
|
|
188
|
+
kind: "artifact",
|
|
189
|
+
mimeType: "application/json",
|
|
190
|
+
description: "Privacy-safe logical subject, Git, safety and aggregate inventory metadata without source or private identity lists."
|
|
191
|
+
});
|
|
192
|
+
return run;
|
|
193
|
+
}
|
|
194
|
+
export function mapRetrievalQueryStrategyComparisonToRun(args) {
|
|
195
|
+
const cases = args.caseEvidence.map((entry, caseIndex) => ({
|
|
196
|
+
id: entry.caseId,
|
|
197
|
+
name: entry.caseName,
|
|
198
|
+
outcomes: entry.treatments.map((treatment, treatmentIndex) => buildOutcome(entry, treatment, args.analysis.cases[caseIndex].treatments[treatmentIndex])),
|
|
199
|
+
metadata: { benchmarkProject: entry.benchmarkProject, taskLocality: entry.taskLocality }
|
|
200
|
+
}));
|
|
201
|
+
const run = {
|
|
202
|
+
runId: args.runId,
|
|
203
|
+
pluginId: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
204
|
+
startedAt: args.startedAt,
|
|
205
|
+
completedAt: args.completedAt,
|
|
206
|
+
status: aggregateTreatmentStatus(args.caseEvidence.flatMap((entry) => entry.treatments.map((treatment) => treatment.status))),
|
|
207
|
+
target: args.target,
|
|
208
|
+
variants: RETRIEVAL_QUERY_STRATEGY_COMPARISON_VARIANTS.map((variant) => ({ ...variant })),
|
|
209
|
+
cases,
|
|
210
|
+
metrics: toRetrievalQueryStrategyRunMetrics(args.analysis),
|
|
211
|
+
artifacts: [
|
|
212
|
+
{
|
|
213
|
+
id: "retrieval-query-strategy-comparison-execution",
|
|
214
|
+
label: "Retrieval query strategy comparison execution evidence",
|
|
215
|
+
path: args.executionArtifactPath,
|
|
216
|
+
kind: "artifact",
|
|
217
|
+
mimeType: "application/json",
|
|
218
|
+
description: "Matched per-case execution evidence for all seven retrieval query strategies without context text or raw command output."
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: "retrieval-query-strategy-comparison-analysis",
|
|
222
|
+
label: "Retrieval query strategy comparison scientific analysis",
|
|
223
|
+
path: args.analysisArtifactPath,
|
|
224
|
+
kind: "artifact",
|
|
225
|
+
mimeType: "application/json",
|
|
226
|
+
description: "Planner-defined retrieval-quality, F1, matched task-type aggregates, and Pareto comparison for the seven retrieval query strategies."
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
warnings: [],
|
|
230
|
+
// Outcome failures are the authoritative per-treatment failure location.
|
|
231
|
+
failures: [],
|
|
232
|
+
metadata: { executionArtifactPath: args.executionArtifactPath, analysisArtifactPath: args.analysisArtifactPath },
|
|
233
|
+
caseExecutionEvidence: structuredClone(args.caseEvidence),
|
|
234
|
+
analysis: structuredClone(args.analysis)
|
|
235
|
+
};
|
|
236
|
+
run.summary = summarizeExperimentRun(run);
|
|
237
|
+
return run;
|
|
238
|
+
}
|
|
239
|
+
function buildOutcome(caseEvidence, treatment, treatmentAnalysis) {
|
|
240
|
+
const { caseId, benchmarkProject, taskLocality } = caseEvidence;
|
|
241
|
+
const { strategyId } = treatment;
|
|
242
|
+
return {
|
|
243
|
+
id: `${caseId}:${strategyId}`,
|
|
244
|
+
caseId,
|
|
245
|
+
variantId: strategyId,
|
|
246
|
+
status: treatment.status,
|
|
247
|
+
metrics: toRetrievalQueryStrategyOutcomeMetrics(treatmentAnalysis, caseId),
|
|
248
|
+
artifacts: [],
|
|
249
|
+
warnings: (treatment.retrieval?.warnings ?? []).map((message) => ({
|
|
250
|
+
code: "retrieval-warning",
|
|
251
|
+
message,
|
|
252
|
+
variantId: strategyId,
|
|
253
|
+
caseId
|
|
254
|
+
})),
|
|
255
|
+
failures: treatment.errors.map((error) => ({
|
|
256
|
+
code: error.code,
|
|
257
|
+
message: error.message,
|
|
258
|
+
variantId: strategyId,
|
|
259
|
+
caseId,
|
|
260
|
+
recoverable: true
|
|
261
|
+
})),
|
|
262
|
+
metadata: {
|
|
263
|
+
benchmarkProject,
|
|
264
|
+
taskLocality,
|
|
265
|
+
evidenceAvailability: treatment.retrieval?.evidenceAvailability ?? null
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function aggregateTreatmentStatus(statuses) {
|
|
270
|
+
if (statuses.length === 0)
|
|
271
|
+
return "skipped";
|
|
272
|
+
if (statuses.every((status) => status === "completed"))
|
|
273
|
+
return "completed";
|
|
274
|
+
if (statuses.every((status) => status === "skipped"))
|
|
275
|
+
return "skipped";
|
|
276
|
+
if (statuses.every((status) => status === "failed"))
|
|
277
|
+
return "failed";
|
|
278
|
+
return "partial";
|
|
279
|
+
}
|
|
280
|
+
function readCasesInput(inputs) {
|
|
281
|
+
const value = inputs?.cases;
|
|
282
|
+
if (!Array.isArray(value)) {
|
|
283
|
+
throw new Error("Retrieval query strategy comparison requires cases input.");
|
|
284
|
+
}
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
function readDependenciesInput(inputs) {
|
|
288
|
+
const value = inputs?.retrievalDependencies;
|
|
289
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
290
|
+
? value
|
|
291
|
+
: undefined;
|
|
292
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,11 +4,14 @@ import { buildWarmIndexReuseReport } from "./buildWarmIndexReuseReport.js";
|
|
|
4
4
|
import { buildIncrementalChangeStalenessPluginReport } from "./buildIncrementalChangeStalenessPluginReport.js";
|
|
5
5
|
import { buildContextWindowScalingReport } from "./buildContextWindowScalingReport.js";
|
|
6
6
|
import { buildRetrievalPrecisionRecallReport } from "./buildRetrievalPrecisionRecallReport.js";
|
|
7
|
+
import { buildRetrievalQueryStrategyComparisonReport } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
7
8
|
// Bulk context-window-scaling evidence is presented by the typed report section; the complete
|
|
8
9
|
// evidence stays in context-window-scaling-execution.json.
|
|
9
10
|
const CONTEXT_WINDOW_SCALING_BULK_KEYS = ["executionEvidence", "aggregate"];
|
|
10
11
|
// Same for retrieval-precision-recall: the typed section presents the evidence; the execution artifact keeps all of it.
|
|
11
12
|
const RETRIEVAL_PRECISION_RECALL_BULK_KEYS = ["caseExecutionEvidence", "aggregate"];
|
|
13
|
+
// Same for retrieval-query-strategy-comparison: the typed section presents execution evidence and analysis.
|
|
14
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
12
15
|
const V043_BULK_ARRAY_KEYS = [
|
|
13
16
|
"v043StageContextExecutions",
|
|
14
17
|
"v043StageContextEvaluations",
|
|
@@ -22,6 +25,7 @@ export function buildPluginExperimentReport(args) {
|
|
|
22
25
|
const incrementalChangeStaleness = buildIncrementalChangeStalenessPluginReport(args.run);
|
|
23
26
|
const contextWindowScaling = buildContextWindowScalingReport(args.run);
|
|
24
27
|
const retrievalPrecisionRecall = buildRetrievalPrecisionRecallReport(args.run);
|
|
28
|
+
const retrievalQueryStrategyComparison = buildRetrievalQueryStrategyComparisonReport(args.run);
|
|
25
29
|
const rawRun = { ...args.run, artifacts: relativizeArtifacts(args.run.artifacts, outputRoot) };
|
|
26
30
|
for (const key of V043_BULK_ARRAY_KEYS) {
|
|
27
31
|
delete rawRun[key];
|
|
@@ -36,6 +40,11 @@ export function buildPluginExperimentReport(args) {
|
|
|
36
40
|
delete rawRun[key];
|
|
37
41
|
}
|
|
38
42
|
}
|
|
43
|
+
if (retrievalQueryStrategyComparison) {
|
|
44
|
+
for (const key of RETRIEVAL_QUERY_STRATEGY_COMPARISON_BULK_KEYS) {
|
|
45
|
+
delete rawRun[key];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
39
48
|
return {
|
|
40
49
|
metadata: {
|
|
41
50
|
generatedAt: args.generatedAt ?? new Date().toISOString(),
|
|
@@ -63,8 +72,9 @@ export function buildPluginExperimentReport(args) {
|
|
|
63
72
|
incrementalChangeStaleness,
|
|
64
73
|
contextWindowScaling,
|
|
65
74
|
retrievalPrecisionRecall,
|
|
75
|
+
retrievalQueryStrategyComparison,
|
|
66
76
|
contextStrategyComparisonV043,
|
|
67
|
-
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall),
|
|
77
|
+
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison),
|
|
68
78
|
rawRun,
|
|
69
79
|
};
|
|
70
80
|
}
|
|
@@ -143,7 +153,20 @@ function affectedNeighborhoodSentence(report) {
|
|
|
143
153
|
`reindex recommendations were recommended=${summary.recommendedReindexCount}, not-indicated=${summary.notIndicatedReindexCount}, unknown=${summary.unknownReindexRecommendationCount}. ` +
|
|
144
154
|
"Affected-neighborhood evidence uses a one-hop baseline graph around confirmed changed indexed files/symbols. It is observational and does not alter retrieval or execution status.");
|
|
145
155
|
}
|
|
146
|
-
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall) {
|
|
156
|
+
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison) {
|
|
157
|
+
if (retrievalQueryStrategyComparison) {
|
|
158
|
+
const overall = retrievalQueryStrategyComparison.scopes.find((scope) => scope.scopeId === "overall");
|
|
159
|
+
const taskTypes = "Task-type results are reported separately for localized, cross-module, and broad-change cases.";
|
|
160
|
+
const summary = overall?.interpretation === "unique-best"
|
|
161
|
+
? `Overall matched comparison: ${overall.bestStrategyId} is the unique nondominated strategy across ${overall.comparisonCaseCount} matched case(s). ${taskTypes}`
|
|
162
|
+
: overall?.interpretation === "tradeoff"
|
|
163
|
+
? `Overall matched comparison has no single best strategy across ${overall.comparisonCaseCount} matched case(s); the Pareto front is ${overall.paretoFrontStrategyIds.join(", ")}. ${taskTypes}`
|
|
164
|
+
: "No matched complete-case retrieval-strategy comparison was available for the overall scope. Review treatment availability and excluded cases before drawing conclusions.";
|
|
165
|
+
return {
|
|
166
|
+
summary,
|
|
167
|
+
recommendedNextStep: "Review the task-type scope table, Pareto fronts, and per-case treatment metrics; do not treat Pareto-front order as a ranking."
|
|
168
|
+
};
|
|
169
|
+
}
|
|
147
170
|
if (retrievalPrecisionRecall) {
|
|
148
171
|
const summary = retrievalPrecisionRecall.runSummary;
|
|
149
172
|
return {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { RETRIEVAL_QUERY_STRATEGY_IDS } from "../../evaluation/retrievalQueryStrategies.js";
|
|
2
|
+
import { RETRIEVAL_QUERY_STRATEGY_COMPARISON_METHODOLOGY } from "../../experiments/plugins/retrievalQueryStrategyComparison/analysisArtifact.js";
|
|
3
|
+
import { RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID } from "../../experiments/plugins/retrievalQueryStrategyComparison/metadata.js";
|
|
4
|
+
import { RETRIEVAL_QUERY_STRATEGY_COMPARISON_REPORT_SCHEMA_VERSION } from "./retrievalQueryStrategyComparisonReportModel.js";
|
|
5
|
+
/** Fixed wording: observed evidence only. It never claims a strategy is generally superior. */
|
|
6
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS = [
|
|
7
|
+
"Retrieval treatments are deterministic my-dev-kit workflows; no coding agent or learned judge participates.",
|
|
8
|
+
"File and symbol precision and recall retain the v0.8.0 set-based identity semantics.",
|
|
9
|
+
"Balanced file and symbol F1 use 2TP / (2TP + FP + FN).",
|
|
10
|
+
"Cross-strategy scope means use matched complete cases only: a task is excluded from every strategy aggregate when any strategy lacks a primary objective.",
|
|
11
|
+
"Scope means are unweighted macro means over task instances.",
|
|
12
|
+
"Pareto dominance compares mean file F1, mean symbol F1, mean fact coverage, and mean retrieved token count without scalar weights.",
|
|
13
|
+
"A unique best strategy is reported only when the Pareto front has exactly one member; multiple nondominated strategies are reported as a tradeoff, not forcibly ranked.",
|
|
14
|
+
"Task-type scopes reuse the existing taskLocality labels: localized, cross-module, and broad-change.",
|
|
15
|
+
"Retrieved token count is a context-size estimate from the selected retrieval payload, not provider billing telemetry.",
|
|
16
|
+
"Unavailable and not-applicable metric values are not converted to zero.",
|
|
17
|
+
"External-local durable reports withhold private file, symbol, fact, semantic-node, warning, and case-title identities."
|
|
18
|
+
];
|
|
19
|
+
const NO_RETRIEVAL_MEASUREMENT = {
|
|
20
|
+
availability: "unavailable",
|
|
21
|
+
numerator: null,
|
|
22
|
+
denominator: null,
|
|
23
|
+
value: null,
|
|
24
|
+
reason: "no-retrieval-measurement"
|
|
25
|
+
};
|
|
26
|
+
const noMeasurement = () => ({ ...NO_RETRIEVAL_MEASUREMENT });
|
|
27
|
+
const copyMetric = (metric) => ({
|
|
28
|
+
availability: metric.availability,
|
|
29
|
+
numerator: metric.numerator,
|
|
30
|
+
denominator: metric.denominator,
|
|
31
|
+
value: metric.value,
|
|
32
|
+
reason: metric.reason
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* Presentation-only adapter over the already-calculated `run.analysis`. It recalculates no precision, recall, F1, fact
|
|
36
|
+
* coverage, mean, Pareto front or best strategy; it only copies values and reads persisted execution status.
|
|
37
|
+
*/
|
|
38
|
+
export function buildRetrievalQueryStrategyComparisonReport(run) {
|
|
39
|
+
if (run.pluginId !== RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID)
|
|
40
|
+
return null;
|
|
41
|
+
const candidate = run;
|
|
42
|
+
const analysis = candidate.analysis;
|
|
43
|
+
const execution = candidate.caseExecutionEvidence;
|
|
44
|
+
if (!analysis || !execution) {
|
|
45
|
+
// A run that failed before analysis exists still gets the generic failure report.
|
|
46
|
+
if (run.status === "failed")
|
|
47
|
+
return null;
|
|
48
|
+
throw new Error("Invalid retrieval-query-strategy-comparison report source: execution evidence and analysis are required.");
|
|
49
|
+
}
|
|
50
|
+
const inconsistent = analysis.cases.length !== execution.length ||
|
|
51
|
+
analysis.cases.some((entry, index) => entry.caseId !== execution[index].caseId ||
|
|
52
|
+
entry.treatments.length !== RETRIEVAL_QUERY_STRATEGY_IDS.length ||
|
|
53
|
+
entry.treatments.some((treatment, position) => treatment.strategyId !== RETRIEVAL_QUERY_STRATEGY_IDS[position]) ||
|
|
54
|
+
execution[index].treatments.length !== RETRIEVAL_QUERY_STRATEGY_IDS.length);
|
|
55
|
+
if (inconsistent) {
|
|
56
|
+
throw new Error("Invalid retrieval-query-strategy-comparison report source: execution evidence and analysis are inconsistent.");
|
|
57
|
+
}
|
|
58
|
+
const cases = analysis.cases.map((entry, caseIndex) => ({
|
|
59
|
+
caseId: entry.caseId,
|
|
60
|
+
caseName: execution[caseIndex].caseName,
|
|
61
|
+
benchmarkProject: entry.benchmarkProject,
|
|
62
|
+
taskLocality: entry.taskLocality,
|
|
63
|
+
treatments: entry.treatments.map((treatment, position) => {
|
|
64
|
+
const quality = treatment.quality;
|
|
65
|
+
return {
|
|
66
|
+
strategyId: treatment.strategyId,
|
|
67
|
+
executionStatus: treatment.executionStatus,
|
|
68
|
+
evidenceAvailability: execution[caseIndex].treatments[position].retrieval?.evidenceAvailability ?? null,
|
|
69
|
+
filePrecision: quality ? copyMetric(quality.file.precision) : noMeasurement(),
|
|
70
|
+
fileRecall: quality ? copyMetric(quality.file.recall) : noMeasurement(),
|
|
71
|
+
fileF1: copyMetric(treatment.fileF1),
|
|
72
|
+
symbolPrecision: quality ? copyMetric(quality.symbol.precision) : noMeasurement(),
|
|
73
|
+
symbolRecall: quality ? copyMetric(quality.symbol.recall) : noMeasurement(),
|
|
74
|
+
symbolF1: copyMetric(treatment.symbolF1),
|
|
75
|
+
factCoverage: quality ? copyMetric(quality.fact.coverage) : noMeasurement(),
|
|
76
|
+
irrelevantContextRatio: quality ? copyMetric(quality.irrelevantContextRatio) : noMeasurement(),
|
|
77
|
+
retrievedTokenCount: quality?.retrievedTokenCount ?? null,
|
|
78
|
+
tokenCountMethod: quality?.tokenCountMethod ?? null
|
|
79
|
+
};
|
|
80
|
+
})
|
|
81
|
+
}));
|
|
82
|
+
return {
|
|
83
|
+
schemaVersion: RETRIEVAL_QUERY_STRATEGY_COMPARISON_REPORT_SCHEMA_VERSION,
|
|
84
|
+
methodology: structuredClone(RETRIEVAL_QUERY_STRATEGY_COMPARISON_METHODOLOGY),
|
|
85
|
+
strategyOrder: [...RETRIEVAL_QUERY_STRATEGY_IDS],
|
|
86
|
+
identityRedaction: run.target.privacyProjection === "external-local-redacted" ? "external-local-redacted" : null,
|
|
87
|
+
scopes: structuredClone(analysis.scopes),
|
|
88
|
+
cases,
|
|
89
|
+
limitations: [...RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -28,3 +28,7 @@ export * from "./retrievalPrecisionRecallReportModel.js";
|
|
|
28
28
|
export { buildRetrievalPrecisionRecallReport, RETRIEVAL_PRECISION_RECALL_LIMITATIONS } from "./buildRetrievalPrecisionRecallReport.js";
|
|
29
29
|
export { renderRetrievalPrecisionRecallHtml } from "./renderRetrievalPrecisionRecallHtml.js";
|
|
30
30
|
export { renderRetrievalPrecisionRecallTextLines } from "./renderRetrievalPrecisionRecallText.js";
|
|
31
|
+
export * from "./retrievalQueryStrategyComparisonReportModel.js";
|
|
32
|
+
export { buildRetrievalQueryStrategyComparisonReport, RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
33
|
+
export { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
|
|
34
|
+
export { renderRetrievalQueryStrategyComparisonTextLines } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
@@ -3,6 +3,7 @@ import { renderWarmIndexReuseHtml } from "./renderWarmIndexReuseHtml.js";
|
|
|
3
3
|
import { renderIncrementalChangeStalenessHtml } from "./renderIncrementalChangeStalenessHtml.js";
|
|
4
4
|
import { renderContextWindowScalingHtml } from "./renderContextWindowScalingHtml.js";
|
|
5
5
|
import { renderRetrievalPrecisionRecallHtml } from "./renderRetrievalPrecisionRecallHtml.js";
|
|
6
|
+
import { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
|
|
6
7
|
export function renderPluginExperimentReportHtml(report) {
|
|
7
8
|
const redactedTarget = report.target.privacyProjection === "external-local-redacted";
|
|
8
9
|
return `<!DOCTYPE html>
|
|
@@ -109,6 +110,8 @@ export function renderPluginExperimentReportHtml(report) {
|
|
|
109
110
|
|
|
110
111
|
${renderRetrievalPrecisionRecallHtml(report.retrievalPrecisionRecall ?? null)}
|
|
111
112
|
|
|
113
|
+
${renderRetrievalQueryStrategyComparisonHtml(report.retrievalQueryStrategyComparison ?? null)}
|
|
114
|
+
|
|
112
115
|
<section>
|
|
113
116
|
<h2>Warnings, Skips, And Failures</h2>
|
|
114
117
|
${report.findings.length ? table(["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
|
|
@@ -4,6 +4,7 @@ import { renderIncrementalChangeStalenessSectionV2 } from "./renderIncrementalCh
|
|
|
4
4
|
import { REINDEX_RECOMMENDATION_EXPLANATIONS } from "./buildWarmIndexReuseReport.js";
|
|
5
5
|
import { renderContextWindowScalingTextLines } from "./renderContextWindowScalingText.js";
|
|
6
6
|
import { renderRetrievalPrecisionRecallTextLines } from "./renderRetrievalPrecisionRecallText.js";
|
|
7
|
+
import { renderRetrievalQueryStrategyComparisonTextLines } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
7
8
|
function sanitizeScalar(value) {
|
|
8
9
|
const text = String(value);
|
|
9
10
|
let result = "";
|
|
@@ -680,6 +681,14 @@ export function renderPluginExperimentReportText(report) {
|
|
|
680
681
|
else {
|
|
681
682
|
lines.push(...renderRetrievalPrecisionRecallTextLines(retrievalPrecisionRecall));
|
|
682
683
|
}
|
|
684
|
+
pushSection(lines, "Retrieval Query Strategy Comparison");
|
|
685
|
+
const retrievalQueryStrategyComparison = report.retrievalQueryStrategyComparison ?? null;
|
|
686
|
+
if (retrievalQueryStrategyComparison === null) {
|
|
687
|
+
lines.push("Not applicable to this plugin.");
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
lines.push(...renderRetrievalQueryStrategyComparisonTextLines(retrievalQueryStrategyComparison));
|
|
691
|
+
}
|
|
683
692
|
pushSection(lines, "Warnings, Skips, And Failures");
|
|
684
693
|
pushDashList(lines, report.findings.map((finding) => `${finding.severity} ${finding.code} ${finding.message} variant=${finding.variantId ?? ""} case=${finding.caseId ?? ""}`));
|
|
685
694
|
pushSection(lines, "Artifacts");
|