@dailephd/my-dev-kit-lab 0.8.0 → 0.8.2
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 +27 -0
- package/README.md +7 -4
- package/dist/src/cli/help.js +35 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +15 -1
- package/dist/src/commands/runExperimentRunCommand.js +177 -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 +60 -51
- package/dist/src/evaluation/runSemanticRetrievalStrategy.js +303 -0
- package/dist/src/experiments/defaultRegistry.js +4 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysis.js +157 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysisArtifact.js +22 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysisTypes.js +1 -0
- package/dist/src/experiments/plugins/contextPackGeneration/buildContextPack.js +84 -0
- package/dist/src/experiments/plugins/contextPackGeneration/config.js +39 -0
- package/dist/src/experiments/plugins/contextPackGeneration/execution.js +361 -0
- package/dist/src/experiments/plugins/contextPackGeneration/executionArtifact.js +17 -0
- package/dist/src/experiments/plugins/contextPackGeneration/executionTypes.js +11 -0
- package/dist/src/experiments/plugins/contextPackGeneration/identityEvidence.js +71 -0
- package/dist/src/experiments/plugins/contextPackGeneration/index.js +19 -0
- package/dist/src/experiments/plugins/contextPackGeneration/localSubjectExecution.js +146 -0
- package/dist/src/experiments/plugins/contextPackGeneration/localSubjectPrivacy.js +276 -0
- package/dist/src/experiments/plugins/contextPackGeneration/metadata.js +24 -0
- package/dist/src/experiments/plugins/contextPackGeneration/metrics.js +46 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packArtifact.js +17 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packEvidence.js +152 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packSelectionPolicy.js +305 -0
- package/dist/src/experiments/plugins/contextPackGeneration/plugin.js +355 -0
- package/dist/src/experiments/plugins/contextPackGeneration/renderContextPack.js +61 -0
- package/dist/src/experiments/plugins/contextPackGeneration/types.js +29 -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/buildContextPackGenerationReport.js +171 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +47 -2
- package/dist/src/report/experiments/buildRetrievalQueryStrategyComparisonReport.js +91 -0
- package/dist/src/report/experiments/contextPackGenerationReportModel.js +9 -0
- package/dist/src/report/experiments/index.js +9 -0
- package/dist/src/report/experiments/loadContextPackArtifacts.js +43 -0
- package/dist/src/report/experiments/renderContextPackGenerationHtml.js +122 -0
- package/dist/src/report/experiments/renderContextPackGenerationText.js +101 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +6 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +16 -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/dist/src/report/experiments/writePluginExperimentReports.js +2 -0
- package/docs/METRICS.md +149 -2
- package/package.json +1 -1
|
@@ -28,3 +28,12 @@ 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 "./contextPackGenerationReportModel.js";
|
|
32
|
+
export { buildContextPackGenerationReport, CONTEXT_PACK_GENERATION_LIMITATIONS } from "./buildContextPackGenerationReport.js";
|
|
33
|
+
export { loadContextPackArtifacts } from "./loadContextPackArtifacts.js";
|
|
34
|
+
export { renderContextPackGenerationHtml } from "./renderContextPackGenerationHtml.js";
|
|
35
|
+
export { renderContextPackGenerationTextLines } from "./renderContextPackGenerationText.js";
|
|
36
|
+
export * from "./retrievalQueryStrategyComparisonReportModel.js";
|
|
37
|
+
export { buildRetrievalQueryStrategyComparisonReport, RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
38
|
+
export { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
|
|
39
|
+
export { renderRetrievalQueryStrategyComparisonTextLines } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolveWithinRoot } from "../../core/pathSafety.js";
|
|
3
|
+
import { CONTEXT_PACK_GENERATION_PLUGIN_ID } from "../../experiments/plugins/contextPackGeneration/metadata.js";
|
|
4
|
+
import { CONTEXT_PACK_SCHEMA_VERSION } from "../../experiments/plugins/contextPackGeneration/types.js";
|
|
5
|
+
function isContextPack(value, caseId) {
|
|
6
|
+
if (typeof value !== "object" || value === null)
|
|
7
|
+
return false;
|
|
8
|
+
const pack = value;
|
|
9
|
+
return (pack.schemaVersion === CONTEXT_PACK_SCHEMA_VERSION &&
|
|
10
|
+
pack.caseId === caseId &&
|
|
11
|
+
typeof pack.task === "object" &&
|
|
12
|
+
pack.task !== null &&
|
|
13
|
+
Array.isArray(pack.files) &&
|
|
14
|
+
Array.isArray(pack.symbols) &&
|
|
15
|
+
Array.isArray(pack.sourceSlices) &&
|
|
16
|
+
Array.isArray(pack.callRelationships) &&
|
|
17
|
+
Array.isArray(pack.tests) &&
|
|
18
|
+
Array.isArray(pack.evidenceNotes));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Reads the persisted per-case pack artifacts the plugin already wrote under the output root. It never reruns retrieval or
|
|
22
|
+
* reopens repository source. A missing or invalid artifact is simply absent: the report then marks that preview unavailable.
|
|
23
|
+
*/
|
|
24
|
+
export async function loadContextPackArtifacts(run, outputRoot) {
|
|
25
|
+
const packs = new Map();
|
|
26
|
+
if (run.pluginId !== CONTEXT_PACK_GENERATION_PLUGIN_ID)
|
|
27
|
+
return packs;
|
|
28
|
+
const evidence = run.caseExecutionEvidence ?? [];
|
|
29
|
+
for (const entry of evidence) {
|
|
30
|
+
const relativePath = entry.treatments.find((treatment) => treatment.treatmentId === "context-pack")?.packArtifactPath;
|
|
31
|
+
if (!relativePath)
|
|
32
|
+
continue;
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(await readFile(resolveWithinRoot(outputRoot, relativePath), "utf8"));
|
|
35
|
+
if (isContextPack(parsed, entry.caseId))
|
|
36
|
+
packs.set(entry.caseId, parsed);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Unreadable or unsafe artifact: the preview is reported as pack-artifact-unavailable, never as an empty pack.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return packs;
|
|
43
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { formatContextPackNumber, redactedPreviewDisplayLines } from "./renderContextPackGenerationText.js";
|
|
2
|
+
import { formatRetrievalQueryStrategyMetric } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
3
|
+
const formatMean = (value) => (value === undefined || value === null ? "unavailable" : value.toFixed(4));
|
|
4
|
+
export function renderContextPackGenerationHtml(section) {
|
|
5
|
+
if (section === null)
|
|
6
|
+
return "";
|
|
7
|
+
return `<section>
|
|
8
|
+
<h2>Context Pack Generation</h2>
|
|
9
|
+
<h3>Methodology</h3>
|
|
10
|
+
${table(["Field", "Value"], [
|
|
11
|
+
["Schema", section.schemaVersion],
|
|
12
|
+
["Experiment", `${section.experiment.pluginId} (${section.experiment.target})`],
|
|
13
|
+
["Treatment order", section.treatmentOrder.join(", ")],
|
|
14
|
+
["File F1", section.methodology.fileF1],
|
|
15
|
+
["Symbol F1", section.methodology.symbolF1],
|
|
16
|
+
["Aggregation", section.methodology.aggregation],
|
|
17
|
+
["Treatment comparison", section.methodology.treatmentComparison],
|
|
18
|
+
["Size measure", section.methodology.sizeMeasure]
|
|
19
|
+
])}
|
|
20
|
+
<h3>Per-Case Treatments</h3>
|
|
21
|
+
${table(["Case", "Project", "Task Type", "Treatment", "Status", "Availability", "File F1", "Symbol F1", "Fact Coverage", "Estimated Tokens"], section.cases.flatMap((entry) => entry.treatments.map((treatment) => [
|
|
22
|
+
entry.caseId,
|
|
23
|
+
entry.benchmarkProject,
|
|
24
|
+
entry.taskLocality ?? "unavailable",
|
|
25
|
+
treatment.treatmentId,
|
|
26
|
+
treatment.executionStatus,
|
|
27
|
+
treatment.availability ?? "unavailable",
|
|
28
|
+
formatRetrievalQueryStrategyMetric(treatment.fileF1),
|
|
29
|
+
formatRetrievalQueryStrategyMetric(treatment.symbolF1),
|
|
30
|
+
formatRetrievalQueryStrategyMetric(treatment.factCoverage),
|
|
31
|
+
formatContextPackNumber(treatment.estimatedTokens)
|
|
32
|
+
])))}
|
|
33
|
+
<h3>Paired Comparison (context-pack minus raw-full-file)</h3>
|
|
34
|
+
${table(["Case", "File F1 Delta", "Symbol F1 Delta", "Fact Coverage Delta", "Estimated Token Delta", "Tokens Saved", "Percent Saved"], section.cases.map((entry) => [
|
|
35
|
+
entry.caseId,
|
|
36
|
+
formatMean(entry.comparison.fileF1Delta),
|
|
37
|
+
formatMean(entry.comparison.symbolF1Delta),
|
|
38
|
+
formatMean(entry.comparison.factCoverageDelta),
|
|
39
|
+
formatContextPackNumber(entry.comparison.estimatedTokenDelta),
|
|
40
|
+
formatContextPackNumber(entry.comparison.tokensSaved),
|
|
41
|
+
formatMean(entry.comparison.percentSaved)
|
|
42
|
+
]))}
|
|
43
|
+
<h3>Scope Aggregates</h3>
|
|
44
|
+
${section.scopes
|
|
45
|
+
.map((scope) => `<h4>${escapeHtml(scope.scopeId)}</h4>
|
|
46
|
+
<p>Cases ${scope.caseCount}; included ${scope.includedCaseCount} (${escapeHtml(scope.includedCaseIds.join(", ") || "none")}); excluded ${scope.excludedCaseCount} (${escapeHtml(scope.excludedCaseIds.join(", ") || "none")}).</p>
|
|
47
|
+
${table(["Treatment", "Completed", "Partial", "Failed", "Mean File F1", "Mean Symbol F1", "Mean Fact Coverage", "Mean Estimated Tokens"], scope.treatmentSummaries.map((summary) => [
|
|
48
|
+
summary.treatmentId,
|
|
49
|
+
String(summary.completedCaseCount),
|
|
50
|
+
String(summary.partialCaseCount),
|
|
51
|
+
String(summary.failedCaseCount),
|
|
52
|
+
formatMean(summary.objectives?.meanFileF1),
|
|
53
|
+
formatMean(summary.objectives?.meanSymbolF1),
|
|
54
|
+
formatMean(summary.objectives?.meanFactCoverage),
|
|
55
|
+
formatContextPackNumber(summary.objectives?.meanEstimatedTokenCount)
|
|
56
|
+
]))}
|
|
57
|
+
${table(["Mean File F1 Delta", "Mean Symbol F1 Delta", "Mean Fact Coverage Delta", "Mean Estimated Token Delta", "Mean Tokens Saved", "Percent Saved Of Means"], [
|
|
58
|
+
[
|
|
59
|
+
formatMean(scope.pairedDeltas?.meanFileF1Delta),
|
|
60
|
+
formatMean(scope.pairedDeltas?.meanSymbolF1Delta),
|
|
61
|
+
formatMean(scope.pairedDeltas?.meanFactCoverageDelta),
|
|
62
|
+
formatContextPackNumber(scope.pairedDeltas?.meanEstimatedTokenDelta),
|
|
63
|
+
formatContextPackNumber(scope.tokenSavings?.meanTokensSaved),
|
|
64
|
+
formatMean(scope.tokenSavings?.percentSavedOfMeans)
|
|
65
|
+
]
|
|
66
|
+
])}`)
|
|
67
|
+
.join("\n ")}
|
|
68
|
+
<h3>Context Pack Preview</h3>
|
|
69
|
+
<p>Bounded for display only; stored packs and measured sizes are unchanged.</p>
|
|
70
|
+
${section.previews.map(previewHtml).join("\n ")}
|
|
71
|
+
<h3>Interpretation Limits</h3>
|
|
72
|
+
${listHtml(section.limitations)}
|
|
73
|
+
</section>`;
|
|
74
|
+
}
|
|
75
|
+
function previewHtml(preview) {
|
|
76
|
+
if (preview.status === "redacted-external-local") {
|
|
77
|
+
return `<h4>${escapeHtml(preview.caseId)}</h4>
|
|
78
|
+
<p>Status ${escapeHtml(preview.status)}; pack availability ${escapeHtml(preview.packAvailability ?? "unavailable")}.</p>
|
|
79
|
+
${table(["Section", "Availability", "Items", "Estimated Tokens"], preview.sections.map((s) => [s.id, s.availability, String(s.itemCount), String(s.estimatedTokens)]))}
|
|
80
|
+
${listHtml(redactedPreviewDisplayLines(preview))}`;
|
|
81
|
+
}
|
|
82
|
+
const head = `<h4>${escapeHtml(preview.caseId)}</h4>
|
|
83
|
+
<p>Status ${escapeHtml(preview.status)}; pack availability ${escapeHtml(preview.packAvailability ?? "unavailable")}.</p>
|
|
84
|
+
${table(["Section", "Availability", "Items", "Estimated Tokens"], preview.sections.map((s) => [s.id, s.availability, String(s.itemCount), String(s.estimatedTokens)]))}`;
|
|
85
|
+
if (preview.task === null)
|
|
86
|
+
return head;
|
|
87
|
+
const count = (list) => `shown ${list.items.length} of ${list.totalCount}; omitted ${list.omittedCount}`;
|
|
88
|
+
return `${head}
|
|
89
|
+
<h5>1. Task</h5>
|
|
90
|
+
<p><strong>${escapeHtml(preview.task.title)}</strong> ${escapeHtml(preview.task.summary)}</p>
|
|
91
|
+
<h5>2. Relevant files (${count(preview.files)})</h5>
|
|
92
|
+
${listHtml(preview.files.items.map((file) => `${file.path} rank=${file.rank} reason=${file.reason}`))}
|
|
93
|
+
<h5>3. Relevant symbols (${count(preview.symbols)})</h5>
|
|
94
|
+
${listHtml(preview.symbols.items.map((symbol) => `${symbol.name} file=${symbol.file ?? "unknown"} line=${symbol.line ?? "unknown"}`))}
|
|
95
|
+
<h5>4. Source slices (${count(preview.sourceSlices)})</h5>
|
|
96
|
+
${preview
|
|
97
|
+
.sourceSlices.items.map((slice) => `<p>${escapeHtml(`${slice.file}:${slice.startLine}-${slice.endLine}`)} boundary known: ${slice.boundaryKnown}; truncated by experiment policy: ${slice.truncated}; truncated by report preview: ${slice.previewTruncated} (showing ${slice.previewLineCount} of ${slice.lineCount} lines)</p>
|
|
98
|
+
<pre>${escapeHtml(slice.previewText)}</pre>`)
|
|
99
|
+
.join("\n ")}
|
|
100
|
+
<h5>5. Call relationships (${count(preview.callRelationships)})</h5>
|
|
101
|
+
${listHtml(preview.callRelationships.items.map((call) => `${call.fromNodeId} calls ${call.toNodeId}`))}
|
|
102
|
+
<h5>6. Tests (${count(preview.tests)})</h5>
|
|
103
|
+
${listHtml(preview.tests.items.map((test) => `${test.path} how=${test.how}`))}
|
|
104
|
+
<h5>7. Evidence notes (${count(preview.evidenceNotes)})</h5>
|
|
105
|
+
${listHtml(preview.evidenceNotes.items)}`;
|
|
106
|
+
}
|
|
107
|
+
function listHtml(items) {
|
|
108
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`;
|
|
109
|
+
}
|
|
110
|
+
function table(headers, rows) {
|
|
111
|
+
return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
|
|
112
|
+
.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
|
|
113
|
+
.join("")}</tbody></table>`;
|
|
114
|
+
}
|
|
115
|
+
function escapeHtml(value) {
|
|
116
|
+
return String(value ?? "")
|
|
117
|
+
.replaceAll("&", "&")
|
|
118
|
+
.replaceAll("<", "<")
|
|
119
|
+
.replaceAll(">", ">")
|
|
120
|
+
.replaceAll('"', """)
|
|
121
|
+
.replaceAll("'", "'");
|
|
122
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { formatRetrievalQueryStrategyMetric, formatRetrievalQueryStrategyNumber } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
2
|
+
const formatMean = (value) => (value === undefined || value === null ? "unavailable" : value.toFixed(4));
|
|
3
|
+
export const formatContextPackNumber = (value) => formatRetrievalQueryStrategyNumber(value ?? null);
|
|
4
|
+
const omitted = (list) => `shown=${list.items.length} total=${list.totalCount} omitted=${list.omittedCount}`;
|
|
5
|
+
const REDACTED_SECTION_LABELS = {
|
|
6
|
+
files: { label: "Relevant files", what: "identities redacted" },
|
|
7
|
+
symbols: { label: "Relevant symbols", what: "identities redacted" },
|
|
8
|
+
sourceSlices: { label: "Source slices", what: "content redacted" },
|
|
9
|
+
callRelationships: { label: "Call relationships", what: "identities redacted" },
|
|
10
|
+
tests: { label: "Tests", what: "identities redacted" },
|
|
11
|
+
evidenceNotes: { label: "Evidence notes", what: "counts only" }
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Literal, fixed-text lines for an external-local preview, generated from persisted safe counts only. Shared by the text
|
|
15
|
+
* and HTML renderers so both show identical wording; no identity or source text can reach this function.
|
|
16
|
+
*/
|
|
17
|
+
export function redactedPreviewDisplayLines(preview) {
|
|
18
|
+
const lines = ["Task: title and summary redacted"];
|
|
19
|
+
for (const section of preview.sections) {
|
|
20
|
+
const entry = REDACTED_SECTION_LABELS[section.id];
|
|
21
|
+
if (!entry)
|
|
22
|
+
continue;
|
|
23
|
+
const truncated = section.truncatedCount !== null ? `, ${section.truncatedCount} truncated by experiment policy` : "";
|
|
24
|
+
lines.push(`${entry.label}: ${section.itemCount} ${section.itemCount === 1 ? "item" : "items"}${truncated} — ${entry.what}`);
|
|
25
|
+
}
|
|
26
|
+
return lines;
|
|
27
|
+
}
|
|
28
|
+
function redactedPreviewLines(preview) {
|
|
29
|
+
const lines = [`Pack preview: case=${preview.caseId} status=${preview.status} packAvailability=${preview.packAvailability ?? "unavailable"}`];
|
|
30
|
+
for (const section of preview.sections) {
|
|
31
|
+
lines.push(` section ${section.id}: availability=${section.availability} items=${section.itemCount} estimatedTokens=${section.estimatedTokens}${section.reason ? ` reason=${section.reason}` : ""}`);
|
|
32
|
+
}
|
|
33
|
+
for (const [index, line] of redactedPreviewDisplayLines(preview).entries())
|
|
34
|
+
lines.push(` ${index + 1}. ${line}`);
|
|
35
|
+
return lines;
|
|
36
|
+
}
|
|
37
|
+
function previewLines(preview) {
|
|
38
|
+
if (preview.status === "redacted-external-local")
|
|
39
|
+
return redactedPreviewLines(preview);
|
|
40
|
+
const lines = [`Pack preview: case=${preview.caseId} status=${preview.status} packAvailability=${preview.packAvailability ?? "unavailable"}`];
|
|
41
|
+
for (const section of preview.sections) {
|
|
42
|
+
lines.push(` section ${section.id}: availability=${section.availability} items=${section.itemCount} estimatedTokens=${section.estimatedTokens}${section.reason ? ` reason=${section.reason}` : ""}`);
|
|
43
|
+
}
|
|
44
|
+
if (preview.task === null)
|
|
45
|
+
return lines;
|
|
46
|
+
lines.push(` 1. Task: ${preview.task.title}`, ` ${preview.task.summary}`);
|
|
47
|
+
lines.push(` 2. Relevant files (${omitted(preview.files)})`);
|
|
48
|
+
for (const file of preview.files.items)
|
|
49
|
+
lines.push(` - ${file.path} rank=${file.rank} reason=${file.reason}`);
|
|
50
|
+
lines.push(` 3. Relevant symbols (${omitted(preview.symbols)})`);
|
|
51
|
+
for (const symbol of preview.symbols.items)
|
|
52
|
+
lines.push(` - ${symbol.name} file=${symbol.file ?? "unknown"} line=${symbol.line ?? "unknown"}`);
|
|
53
|
+
lines.push(` 4. Source slices (${omitted(preview.sourceSlices)})`);
|
|
54
|
+
for (const slice of preview.sourceSlices.items) {
|
|
55
|
+
lines.push(` - ${slice.file}:${slice.startLine}-${slice.endLine} boundaryKnown=${slice.boundaryKnown} policyTruncated=${slice.truncated} previewTruncated=${slice.previewTruncated} (showing ${slice.previewLineCount} of ${slice.lineCount} lines)`);
|
|
56
|
+
for (const sourceLine of slice.previewText.split("\n"))
|
|
57
|
+
lines.push(` | ${sourceLine}`);
|
|
58
|
+
}
|
|
59
|
+
lines.push(` 5. Call relationships (${omitted(preview.callRelationships)})`);
|
|
60
|
+
for (const call of preview.callRelationships.items)
|
|
61
|
+
lines.push(` - ${call.fromNodeId} calls ${call.toNodeId}`);
|
|
62
|
+
lines.push(` 6. Tests (${omitted(preview.tests)})`);
|
|
63
|
+
for (const test of preview.tests.items)
|
|
64
|
+
lines.push(` - ${test.path} how=${test.how}`);
|
|
65
|
+
lines.push(` 7. Evidence notes (${omitted(preview.evidenceNotes)})`);
|
|
66
|
+
for (const note of preview.evidenceNotes.items)
|
|
67
|
+
lines.push(` - ${note}`);
|
|
68
|
+
return lines;
|
|
69
|
+
}
|
|
70
|
+
export function renderContextPackGenerationTextLines(section) {
|
|
71
|
+
const lines = [];
|
|
72
|
+
lines.push(`Schema: ${section.schemaVersion}`);
|
|
73
|
+
lines.push(`Experiment: ${section.experiment.pluginId} target=${section.experiment.target}`);
|
|
74
|
+
lines.push(`Treatment order: ${section.treatmentOrder.join(", ")}`);
|
|
75
|
+
lines.push(`Methodology: fileF1=${section.methodology.fileF1} symbolF1=${section.methodology.symbolF1} aggregation=${section.methodology.aggregation} comparison=${section.methodology.treatmentComparison} size=${section.methodology.sizeMeasure}`);
|
|
76
|
+
lines.push("", "Per-Case Treatments");
|
|
77
|
+
for (const entry of section.cases) {
|
|
78
|
+
for (const treatment of entry.treatments) {
|
|
79
|
+
lines.push(`- case=${entry.caseId} project=${entry.benchmarkProject} taskType=${entry.taskLocality ?? "unavailable"} treatment=${treatment.treatmentId} status=${treatment.executionStatus} availability=${treatment.availability ?? "unavailable"} fileF1=${formatRetrievalQueryStrategyMetric(treatment.fileF1)} symbolF1=${formatRetrievalQueryStrategyMetric(treatment.symbolF1)} factCoverage=${formatRetrievalQueryStrategyMetric(treatment.factCoverage)} estimatedTokens=${formatContextPackNumber(treatment.estimatedTokens)}`);
|
|
80
|
+
}
|
|
81
|
+
const c = entry.comparison;
|
|
82
|
+
lines.push(` paired case=${entry.caseId} fileF1Delta=${formatMean(c.fileF1Delta)} symbolF1Delta=${formatMean(c.symbolF1Delta)} factCoverageDelta=${formatMean(c.factCoverageDelta)} estimatedTokenDelta=${formatContextPackNumber(c.estimatedTokenDelta)} tokensSaved=${formatContextPackNumber(c.tokensSaved)} percentSaved=${formatMean(c.percentSaved)}`);
|
|
83
|
+
}
|
|
84
|
+
lines.push("", "Scope Aggregates");
|
|
85
|
+
for (const scope of section.scopes) {
|
|
86
|
+
lines.push(`Scope ${scope.scopeId}: cases=${scope.caseCount} included=${scope.includedCaseCount} excluded=${scope.excludedCaseCount} includedIds=${scope.includedCaseIds.join(", ") || "none"} excludedIds=${scope.excludedCaseIds.join(", ") || "none"}`);
|
|
87
|
+
for (const summary of scope.treatmentSummaries) {
|
|
88
|
+
const o = summary.objectives;
|
|
89
|
+
lines.push(`- ${summary.treatmentId} completed=${summary.completedCaseCount} partial=${summary.partialCaseCount} failed=${summary.failedCaseCount} meanFileF1=${formatMean(o?.meanFileF1)} meanSymbolF1=${formatMean(o?.meanSymbolF1)} meanFactCoverage=${formatMean(o?.meanFactCoverage)} meanEstimatedTokens=${formatContextPackNumber(o?.meanEstimatedTokenCount)}`);
|
|
90
|
+
}
|
|
91
|
+
const d = scope.pairedDeltas;
|
|
92
|
+
lines.push(` paired meanFileF1Delta=${formatMean(d?.meanFileF1Delta)} meanSymbolF1Delta=${formatMean(d?.meanSymbolF1Delta)} meanFactCoverageDelta=${formatMean(d?.meanFactCoverageDelta)} meanEstimatedTokenDelta=${formatContextPackNumber(d?.meanEstimatedTokenDelta)} meanTokensSaved=${formatContextPackNumber(scope.tokenSavings?.meanTokensSaved)} percentSavedOfMeans=${formatMean(scope.tokenSavings?.percentSavedOfMeans)}`);
|
|
93
|
+
}
|
|
94
|
+
lines.push("", "Context Pack Preview (bounded for display; stored packs are unchanged)");
|
|
95
|
+
for (const preview of section.previews)
|
|
96
|
+
lines.push(...previewLines(preview));
|
|
97
|
+
lines.push("", "Interpretation Limits");
|
|
98
|
+
for (const limitation of section.limitations)
|
|
99
|
+
lines.push(`- ${limitation}`);
|
|
100
|
+
return lines;
|
|
101
|
+
}
|
|
@@ -3,6 +3,8 @@ 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";
|
|
7
|
+
import { renderContextPackGenerationHtml } from "./renderContextPackGenerationHtml.js";
|
|
6
8
|
export function renderPluginExperimentReportHtml(report) {
|
|
7
9
|
const redactedTarget = report.target.privacyProjection === "external-local-redacted";
|
|
8
10
|
return `<!DOCTYPE html>
|
|
@@ -109,6 +111,10 @@ export function renderPluginExperimentReportHtml(report) {
|
|
|
109
111
|
|
|
110
112
|
${renderRetrievalPrecisionRecallHtml(report.retrievalPrecisionRecall ?? null)}
|
|
111
113
|
|
|
114
|
+
${renderRetrievalQueryStrategyComparisonHtml(report.retrievalQueryStrategyComparison ?? null)}
|
|
115
|
+
|
|
116
|
+
${renderContextPackGenerationHtml(report.contextPackGeneration ?? null)}
|
|
117
|
+
|
|
112
118
|
<section>
|
|
113
119
|
<h2>Warnings, Skips, And Failures</h2>
|
|
114
120
|
${report.findings.length ? table(["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
|
|
@@ -4,6 +4,8 @@ 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";
|
|
8
|
+
import { renderContextPackGenerationTextLines } from "./renderContextPackGenerationText.js";
|
|
7
9
|
function sanitizeScalar(value) {
|
|
8
10
|
const text = String(value);
|
|
9
11
|
let result = "";
|
|
@@ -680,6 +682,20 @@ export function renderPluginExperimentReportText(report) {
|
|
|
680
682
|
else {
|
|
681
683
|
lines.push(...renderRetrievalPrecisionRecallTextLines(retrievalPrecisionRecall));
|
|
682
684
|
}
|
|
685
|
+
pushSection(lines, "Retrieval Query Strategy Comparison");
|
|
686
|
+
const retrievalQueryStrategyComparison = report.retrievalQueryStrategyComparison ?? null;
|
|
687
|
+
if (retrievalQueryStrategyComparison === null) {
|
|
688
|
+
lines.push("Not applicable to this plugin.");
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
lines.push(...renderRetrievalQueryStrategyComparisonTextLines(retrievalQueryStrategyComparison));
|
|
692
|
+
}
|
|
693
|
+
// Additive: other plugins keep their existing text output unchanged.
|
|
694
|
+
const contextPackGeneration = report.contextPackGeneration ?? null;
|
|
695
|
+
if (contextPackGeneration !== null) {
|
|
696
|
+
pushSection(lines, "Context Pack Generation");
|
|
697
|
+
lines.push(...renderContextPackGenerationTextLines(contextPackGeneration));
|
|
698
|
+
}
|
|
683
699
|
pushSection(lines, "Warnings, Skips, And Failures");
|
|
684
700
|
pushDashList(lines, report.findings.map((finding) => `${finding.severity} ${finding.code} ${finding.message} variant=${finding.variantId ?? ""} case=${finding.caseId ?? ""}`));
|
|
685
701
|
pushSection(lines, "Artifacts");
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { RETRIEVAL_QUERY_STRATEGY_EXTERNAL_PRIVACY_NOTE, describeRetrievalQueryStrategyScope, formatRetrievalQueryStrategyList, formatRetrievalQueryStrategyMetric, formatRetrievalQueryStrategyNumber } from "./renderRetrievalQueryStrategyComparisonText.js";
|
|
2
|
+
export function renderRetrievalQueryStrategyComparisonHtml(section) {
|
|
3
|
+
if (section === null)
|
|
4
|
+
return "";
|
|
5
|
+
const methodology = section.methodology;
|
|
6
|
+
const objective = (value, digits) => (value === undefined ? "unavailable" : digits ? value.toFixed(4) : String(value));
|
|
7
|
+
return `<section>
|
|
8
|
+
<h2>Retrieval Query Strategy Comparison</h2>
|
|
9
|
+
<h3>Methodology</h3>
|
|
10
|
+
${table(["Field", "Value"], [
|
|
11
|
+
["Schema", section.schemaVersion],
|
|
12
|
+
["File F1", methodology.fileF1],
|
|
13
|
+
["Symbol F1", methodology.symbolF1],
|
|
14
|
+
["Aggregation", methodology.aggregation],
|
|
15
|
+
["Multi-objective comparison", methodology.multiObjectiveComparison],
|
|
16
|
+
["Unique-best rule", methodology.uniqueBestRule],
|
|
17
|
+
["Strategy order", section.strategyOrder.join(", ")]
|
|
18
|
+
])}
|
|
19
|
+
${section.identityRedaction !== null ? `<p><strong>Identity redaction.</strong> ${escapeHtml(RETRIEVAL_QUERY_STRATEGY_EXTERNAL_PRIVACY_NOTE)}</p>` : ""}
|
|
20
|
+
<h3>Task-Type Comparison</h3>
|
|
21
|
+
${table(["Scope", "Cases", "Matched Cases", "Excluded Cases", "Interpretation", "Best Strategy", "Pareto Front"], section.scopes.map((scope) => [
|
|
22
|
+
scope.scopeId,
|
|
23
|
+
String(scope.caseCount),
|
|
24
|
+
String(scope.comparisonCaseCount),
|
|
25
|
+
String(scope.excludedCaseCount),
|
|
26
|
+
scope.interpretation,
|
|
27
|
+
scope.bestStrategyId ?? "none",
|
|
28
|
+
formatRetrievalQueryStrategyList(scope.paretoFrontStrategyIds)
|
|
29
|
+
]))}
|
|
30
|
+
${listHtml(section.scopes.map(describeRetrievalQueryStrategyScope))}
|
|
31
|
+
<h3>Per-Scope Strategy Objectives</h3>
|
|
32
|
+
${section.scopes
|
|
33
|
+
.map((scope) => `<h4>${escapeHtml(scope.scopeId)}</h4>
|
|
34
|
+
${table(["Strategy", "Completed", "Partial", "Failed", "Mean File F1", "Mean Symbol F1", "Mean Fact Coverage", "Mean Retrieved Tokens"], scope.strategySummaries.map((summary) => [
|
|
35
|
+
summary.strategyId,
|
|
36
|
+
String(summary.completedCaseCount),
|
|
37
|
+
String(summary.partialCaseCount),
|
|
38
|
+
String(summary.failedCaseCount),
|
|
39
|
+
objective(summary.objectives?.meanFileF1, true),
|
|
40
|
+
objective(summary.objectives?.meanSymbolF1, true),
|
|
41
|
+
objective(summary.objectives?.meanFactCoverage, true),
|
|
42
|
+
objective(summary.objectives?.meanRetrievedTokenCount, false)
|
|
43
|
+
]))}`)
|
|
44
|
+
.join("\n ")}
|
|
45
|
+
<h3>Per-Case Treatments</h3>
|
|
46
|
+
${table([
|
|
47
|
+
"Case",
|
|
48
|
+
"Task Type",
|
|
49
|
+
"Strategy",
|
|
50
|
+
"Status",
|
|
51
|
+
"Evidence",
|
|
52
|
+
"File Precision",
|
|
53
|
+
"File Recall",
|
|
54
|
+
"File F1",
|
|
55
|
+
"Symbol Precision",
|
|
56
|
+
"Symbol Recall",
|
|
57
|
+
"Symbol F1",
|
|
58
|
+
"Fact Coverage",
|
|
59
|
+
"Irrelevant Context Ratio",
|
|
60
|
+
"Retrieved Tokens"
|
|
61
|
+
], section.cases.flatMap((entry) => entry.treatments.map((treatment) => [
|
|
62
|
+
entry.caseId,
|
|
63
|
+
entry.taskLocality ?? "unavailable",
|
|
64
|
+
treatment.strategyId,
|
|
65
|
+
treatment.executionStatus,
|
|
66
|
+
treatment.evidenceAvailability ?? "unavailable",
|
|
67
|
+
formatRetrievalQueryStrategyMetric(treatment.filePrecision),
|
|
68
|
+
formatRetrievalQueryStrategyMetric(treatment.fileRecall),
|
|
69
|
+
formatRetrievalQueryStrategyMetric(treatment.fileF1),
|
|
70
|
+
formatRetrievalQueryStrategyMetric(treatment.symbolPrecision),
|
|
71
|
+
formatRetrievalQueryStrategyMetric(treatment.symbolRecall),
|
|
72
|
+
formatRetrievalQueryStrategyMetric(treatment.symbolF1),
|
|
73
|
+
formatRetrievalQueryStrategyMetric(treatment.factCoverage),
|
|
74
|
+
formatRetrievalQueryStrategyMetric(treatment.irrelevantContextRatio),
|
|
75
|
+
formatRetrievalQueryStrategyNumber(treatment.retrievedTokenCount)
|
|
76
|
+
])))}
|
|
77
|
+
<h3>Interpretation Limits</h3>
|
|
78
|
+
${listHtml(section.limitations)}
|
|
79
|
+
</section>`;
|
|
80
|
+
}
|
|
81
|
+
function listHtml(items) {
|
|
82
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`;
|
|
83
|
+
}
|
|
84
|
+
function table(headers, rows) {
|
|
85
|
+
return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
|
|
86
|
+
.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
|
|
87
|
+
.join("")}</tbody></table>`;
|
|
88
|
+
}
|
|
89
|
+
function escapeHtml(value) {
|
|
90
|
+
return String(value ?? "")
|
|
91
|
+
.replaceAll("&", "&")
|
|
92
|
+
.replaceAll("<", "<")
|
|
93
|
+
.replaceAll(">", ">")
|
|
94
|
+
.replaceAll('"', """)
|
|
95
|
+
.replaceAll("'", "'");
|
|
96
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const RETRIEVAL_QUERY_STRATEGY_EXTERNAL_PRIVACY_NOTE = "External-local report: private file, symbol, fact, semantic-node, warning, and case-title identities are withheld; numeric scientific results and strategy interpretations are preserved.";
|
|
2
|
+
/** Available values keep four decimals; unavailable/not-applicable are never shown as zero. */
|
|
3
|
+
export function formatRetrievalQueryStrategyMetric(metric) {
|
|
4
|
+
if (metric.availability === "available" && metric.value !== null)
|
|
5
|
+
return metric.value.toFixed(4);
|
|
6
|
+
return `${metric.availability}${metric.reason ? ` (${metric.reason})` : ""}`;
|
|
7
|
+
}
|
|
8
|
+
export const formatRetrievalQueryStrategyNumber = (value) => (value === null ? "unavailable" : String(value));
|
|
9
|
+
const formatMean = (value) => (value === undefined ? "unavailable" : value.toFixed(4));
|
|
10
|
+
const formatTokenMean = (value) => (value === undefined ? "unavailable" : String(value));
|
|
11
|
+
export const formatRetrievalQueryStrategyList = (items) => (items.length === 0 ? "none" : items.join(", "));
|
|
12
|
+
/** One human sentence per scope: a best strategy only for a single-member front; otherwise an explicit tradeoff. */
|
|
13
|
+
export function describeRetrievalQueryStrategyScope(scope) {
|
|
14
|
+
if (scope.interpretation === "unique-best") {
|
|
15
|
+
return `${scope.scopeId}: unique-best ${scope.bestStrategyId} (single-member Pareto front).`;
|
|
16
|
+
}
|
|
17
|
+
if (scope.interpretation === "tradeoff") {
|
|
18
|
+
return `${scope.scopeId}: No Single Best strategy; Pareto front (nondominated, not ranked): ${formatRetrievalQueryStrategyList(scope.paretoFrontStrategyIds)}.`;
|
|
19
|
+
}
|
|
20
|
+
return `${scope.scopeId}: unavailable; no matched complete-case comparison.`;
|
|
21
|
+
}
|
|
22
|
+
export function renderRetrievalQueryStrategyComparisonTextLines(section) {
|
|
23
|
+
const lines = [];
|
|
24
|
+
const methodology = section.methodology;
|
|
25
|
+
lines.push(`Schema: ${section.schemaVersion}`);
|
|
26
|
+
lines.push(`File F1: ${methodology.fileF1}`);
|
|
27
|
+
lines.push(`Symbol F1: ${methodology.symbolF1}`);
|
|
28
|
+
lines.push(`Aggregation: ${methodology.aggregation}`);
|
|
29
|
+
lines.push(`Multi-objective comparison: ${methodology.multiObjectiveComparison}`);
|
|
30
|
+
lines.push(`Unique-best rule: ${methodology.uniqueBestRule}`);
|
|
31
|
+
lines.push(`Strategy order: ${section.strategyOrder.join(", ")}`);
|
|
32
|
+
if (section.identityRedaction !== null)
|
|
33
|
+
lines.push(RETRIEVAL_QUERY_STRATEGY_EXTERNAL_PRIVACY_NOTE);
|
|
34
|
+
lines.push("", "Task-Type Comparison");
|
|
35
|
+
for (const scope of section.scopes) {
|
|
36
|
+
lines.push(`- scope=${scope.scopeId} cases=${scope.caseCount} matched=${scope.comparisonCaseCount} excluded=${scope.excludedCaseCount} interpretation=${scope.interpretation} best=${scope.bestStrategyId ?? "none"} pareto=${formatRetrievalQueryStrategyList(scope.paretoFrontStrategyIds)}`);
|
|
37
|
+
}
|
|
38
|
+
for (const scope of section.scopes)
|
|
39
|
+
lines.push(describeRetrievalQueryStrategyScope(scope));
|
|
40
|
+
lines.push("", "Per-Scope Strategy Objectives");
|
|
41
|
+
for (const scope of section.scopes) {
|
|
42
|
+
lines.push(`Scope ${scope.scopeId}:`);
|
|
43
|
+
for (const summary of scope.strategySummaries) {
|
|
44
|
+
const objectives = summary.objectives;
|
|
45
|
+
lines.push(`- ${summary.strategyId} completed=${summary.completedCaseCount} partial=${summary.partialCaseCount} failed=${summary.failedCaseCount} meanFileF1=${formatMean(objectives?.meanFileF1)} meanSymbolF1=${formatMean(objectives?.meanSymbolF1)} meanFactCoverage=${formatMean(objectives?.meanFactCoverage)} meanRetrievedTokens=${formatTokenMean(objectives?.meanRetrievedTokenCount)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
lines.push("", "Per-Case Treatments");
|
|
49
|
+
for (const entry of section.cases) {
|
|
50
|
+
for (const treatment of entry.treatments) {
|
|
51
|
+
lines.push(`- case=${entry.caseId} taskType=${entry.taskLocality ?? "unavailable"} strategy=${treatment.strategyId} status=${treatment.executionStatus} evidence=${treatment.evidenceAvailability ?? "unavailable"} filePrecision=${formatRetrievalQueryStrategyMetric(treatment.filePrecision)} fileRecall=${formatRetrievalQueryStrategyMetric(treatment.fileRecall)} fileF1=${formatRetrievalQueryStrategyMetric(treatment.fileF1)} symbolPrecision=${formatRetrievalQueryStrategyMetric(treatment.symbolPrecision)} symbolRecall=${formatRetrievalQueryStrategyMetric(treatment.symbolRecall)} symbolF1=${formatRetrievalQueryStrategyMetric(treatment.symbolF1)} factCoverage=${formatRetrievalQueryStrategyMetric(treatment.factCoverage)} irrelevantContextRatio=${formatRetrievalQueryStrategyMetric(treatment.irrelevantContextRatio)} retrievedTokens=${formatRetrievalQueryStrategyNumber(treatment.retrievedTokenCount)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
lines.push("", "Interpretation Limits");
|
|
55
|
+
for (const limitation of section.limitations)
|
|
56
|
+
lines.push(`- ${limitation}`);
|
|
57
|
+
return lines;
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_REPORT_SCHEMA_VERSION = "my-dev-kit-lab-retrieval-query-strategy-comparison-report-v1";
|
|
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { resolveWithinRoot } from "../../core/pathSafety.js";
|
|
4
4
|
import { buildPluginExperimentReport } from "./buildPluginExperimentReport.js";
|
|
5
|
+
import { loadContextPackArtifacts } from "./loadContextPackArtifacts.js";
|
|
5
6
|
import { renderPluginExperimentReportHtml } from "./renderPluginExperimentReportHtml.js";
|
|
6
7
|
import { renderPluginExperimentReportText } from "./renderPluginExperimentReportText.js";
|
|
7
8
|
export async function writePluginExperimentReports(args) {
|
|
@@ -22,6 +23,7 @@ export async function writePluginExperimentReports(args) {
|
|
|
22
23
|
plugin: args.plugin,
|
|
23
24
|
outputRoot: outputPaths.outDir,
|
|
24
25
|
generatedAt: args.generatedAt,
|
|
26
|
+
contextPacks: await loadContextPackArtifacts(args.run, outputPaths.outDir),
|
|
25
27
|
});
|
|
26
28
|
const report = args.redactOutputRoot
|
|
27
29
|
? { ...builtReport, metadata: { ...builtReport.metadata, outputRoot: "[redacted]" } }
|