@dailephd/my-dev-kit-lab 0.8.1 → 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 +10 -1
- package/README.md +5 -4
- package/dist/src/cli/help.js +16 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +8 -1
- package/dist/src/commands/runExperimentRunCommand.js +90 -3
- package/dist/src/evaluation/runMyDevKitRetrieval.js +1 -0
- package/dist/src/experiments/defaultRegistry.js +2 -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/report/experiments/buildContextPackGenerationReport.js +171 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +24 -2
- package/dist/src/report/experiments/contextPackGenerationReportModel.js +9 -0
- package/dist/src/report/experiments/index.js +5 -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 +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +7 -0
- package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonHtml.js +15 -15
- package/dist/src/report/experiments/writePluginExperimentReports.js +2 -0
- package/docs/METRICS.md +28 -1
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** Stable source boundary; it cannot be confused with Markdown code-fence nesting. */
|
|
2
|
+
export const CONTEXT_PACK_SOURCE_BEGIN = "<<<SOURCE";
|
|
3
|
+
export const CONTEXT_PACK_SOURCE_END = "SOURCE>>>";
|
|
4
|
+
export const CONTEXT_PACK_SECTION_HEADINGS = {
|
|
5
|
+
task: "## Task",
|
|
6
|
+
files: "## Relevant files",
|
|
7
|
+
symbols: "## Relevant symbols",
|
|
8
|
+
sourceSlices: "## Source slices",
|
|
9
|
+
callRelationships: "## Call relationships",
|
|
10
|
+
tests: "## Tests",
|
|
11
|
+
evidenceNotes: "## Evidence notes"
|
|
12
|
+
};
|
|
13
|
+
const lf = (text) => text.replace(/\r\n?/g, "\n");
|
|
14
|
+
function renderBody(id, lines, availability) {
|
|
15
|
+
const heading = CONTEXT_PACK_SECTION_HEADINGS[id];
|
|
16
|
+
if (lines.length > 0)
|
|
17
|
+
return [heading, ...lines].join("\n");
|
|
18
|
+
const { availability: state, reason } = availability[id];
|
|
19
|
+
const suffix = reason === null ? "" : `: ${reason}`;
|
|
20
|
+
const placeholder = state === "available" ? "(none)" : `(${state}${suffix})`;
|
|
21
|
+
return `${heading}\n${placeholder}`;
|
|
22
|
+
}
|
|
23
|
+
function renderSlice(slice) {
|
|
24
|
+
return [
|
|
25
|
+
`file: ${slice.file}`,
|
|
26
|
+
`lines: ${slice.startLine}-${slice.endLine}`,
|
|
27
|
+
`boundary: ${slice.boundaryKnown ? "known" : "unknown"}`,
|
|
28
|
+
`truncated: ${slice.truncated ? "yes" : "no"}`,
|
|
29
|
+
CONTEXT_PACK_SOURCE_BEGIN,
|
|
30
|
+
lf(slice.text),
|
|
31
|
+
CONTEXT_PACK_SOURCE_END
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
/** Deterministic, language-agnostic rendering. Source text is verbatim apart from line-ending normalization. */
|
|
35
|
+
export function renderContextPack(input) {
|
|
36
|
+
const availability = input.sectionAvailability;
|
|
37
|
+
const sections = {
|
|
38
|
+
task: `${CONTEXT_PACK_SECTION_HEADINGS.task}\nTitle: ${lf(input.task.title)}\nSummary: ${lf(input.task.summary)}`,
|
|
39
|
+
files: renderBody("files", input.files.map((file) => `- ${file.path}`), availability),
|
|
40
|
+
symbols: renderBody("symbols", input.symbols.map((symbol) => (symbol.file === null ? `- ${symbol.name}` : `- ${symbol.name} (${symbol.file})`)), availability),
|
|
41
|
+
sourceSlices: renderSourceSlices(input.sourceSlices, availability),
|
|
42
|
+
callRelationships: renderBody("callRelationships", input.callRelationships.map((call) => `${call.fromNodeId} -> ${call.toNodeId}`), availability),
|
|
43
|
+
tests: renderBody("tests", input.tests.map((test) => `- ${test.path}`), availability),
|
|
44
|
+
evidenceNotes: renderBody("evidenceNotes", input.evidenceNotes.map((code) => `- ${code}`), availability)
|
|
45
|
+
};
|
|
46
|
+
const renderedText = `${[
|
|
47
|
+
sections.task,
|
|
48
|
+
sections.files,
|
|
49
|
+
sections.symbols,
|
|
50
|
+
sections.sourceSlices,
|
|
51
|
+
sections.callRelationships,
|
|
52
|
+
sections.tests,
|
|
53
|
+
sections.evidenceNotes
|
|
54
|
+
].join("\n\n")}\n`;
|
|
55
|
+
return { sections, renderedText };
|
|
56
|
+
}
|
|
57
|
+
function renderSourceSlices(slices, availability) {
|
|
58
|
+
if (slices.length === 0)
|
|
59
|
+
return renderBody("sourceSlices", [], availability);
|
|
60
|
+
return `${CONTEXT_PACK_SECTION_HEADINGS.sourceSlices}\n${slices.map(renderSlice).join("\n\n")}`;
|
|
61
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Domain schema of the Lab-owned experimental context pack. It is a measurement artifact, not a production contract. */
|
|
2
|
+
export const CONTEXT_PACK_SCHEMA_VERSION = "my-dev-kit-lab-context-pack-experiment-v1";
|
|
3
|
+
/** Logical section order of the pack. */
|
|
4
|
+
export const CONTEXT_PACK_SECTION_IDS = [
|
|
5
|
+
"task",
|
|
6
|
+
"files",
|
|
7
|
+
"symbols",
|
|
8
|
+
"sourceSlices",
|
|
9
|
+
"callRelationships",
|
|
10
|
+
"tests",
|
|
11
|
+
"evidenceNotes"
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Closed fixed vocabulary. Only codes produced by the pure Batch 1 domain exist; no free-form warning text, and no
|
|
15
|
+
* execution, indexing, privacy or persistence failure codes.
|
|
16
|
+
*/
|
|
17
|
+
export const CONTEXT_PACK_EVIDENCE_NOTE_CODES = [
|
|
18
|
+
"symbol-end-unknown",
|
|
19
|
+
"source-slice-cap-reached",
|
|
20
|
+
"total-source-line-cap-reached",
|
|
21
|
+
"source-slice-count-cap-reached",
|
|
22
|
+
"seed-cap-reached",
|
|
23
|
+
"file-cap-reached",
|
|
24
|
+
"symbol-cap-reached",
|
|
25
|
+
"test-file-cap-reached",
|
|
26
|
+
"call-relationship-cap-reached",
|
|
27
|
+
"invalid-repository-path-ignored",
|
|
28
|
+
"invalid-source-slice-ignored"
|
|
29
|
+
];
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { CONTEXT_PACK_GENERATION_METHODOLOGY } from "../../experiments/plugins/contextPackGeneration/analysisArtifact.js";
|
|
3
|
+
import { CONTEXT_PACK_GENERATION_PLUGIN_ID, CONTEXT_PACK_GENERATION_TREATMENT_IDS } from "../../experiments/plugins/contextPackGeneration/metadata.js";
|
|
4
|
+
import { CONTEXT_PACK_GENERATION_REPORT_SCHEMA_VERSION, PACK_PREVIEW_MAX_CALL_RELATIONSHIPS, PACK_PREVIEW_MAX_EVIDENCE_NOTES, PACK_PREVIEW_MAX_FILES, PACK_PREVIEW_MAX_SOURCE_LINES_PER_SLICE, PACK_PREVIEW_MAX_SOURCE_SLICES, PACK_PREVIEW_MAX_SYMBOLS, PACK_PREVIEW_MAX_TESTS } from "./contextPackGenerationReportModel.js";
|
|
5
|
+
/** Fixed wording: observed evidence only. */
|
|
6
|
+
export const CONTEXT_PACK_GENERATION_LIMITATIONS = [
|
|
7
|
+
"This is a context-pack experiment over deterministic my-dev-kit workflows; it is not a coding-agent success evaluation.",
|
|
8
|
+
"Estimated tokens come from the existing Lab estimator over rendered text, not provider billing telemetry.",
|
|
9
|
+
"Unavailable and not-applicable values are not converted to zero.",
|
|
10
|
+
"Scope means use matched complete cases only and are unweighted macro means over task instances.",
|
|
11
|
+
"No composite score, ranking, or winning treatment is produced; treatments are reported side by side.",
|
|
12
|
+
"Tests and call relationships in the pack are descriptive and are not scored.",
|
|
13
|
+
"The context pack is an experimental Lab artifact, not the production my-dev-kit context API.",
|
|
14
|
+
"The pack preview is bounded for display only and does not change the stored pack or any measured size."
|
|
15
|
+
];
|
|
16
|
+
const copyMetric = (metric) => ({
|
|
17
|
+
availability: metric.availability,
|
|
18
|
+
numerator: metric.numerator,
|
|
19
|
+
denominator: metric.denominator,
|
|
20
|
+
value: metric.value,
|
|
21
|
+
reason: metric.reason
|
|
22
|
+
});
|
|
23
|
+
const NO_MEASUREMENT = { availability: "unavailable", numerator: null, denominator: null, value: null, reason: "no-retrieval-measurement" };
|
|
24
|
+
function bounded(source, limit, project) {
|
|
25
|
+
return { items: source.slice(0, limit).map(project), totalCount: source.length, omittedCount: Math.max(0, source.length - limit) };
|
|
26
|
+
}
|
|
27
|
+
/** Display truncation of a stored slice: first lines only. The pack's own `truncated` flag is copied untouched. */
|
|
28
|
+
function previewSlice(slice) {
|
|
29
|
+
const lines = slice.text.split("\n");
|
|
30
|
+
const shown = lines.slice(0, PACK_PREVIEW_MAX_SOURCE_LINES_PER_SLICE);
|
|
31
|
+
return {
|
|
32
|
+
file: slice.file,
|
|
33
|
+
symbolName: slice.symbolName,
|
|
34
|
+
startLine: slice.startLine,
|
|
35
|
+
endLine: slice.endLine,
|
|
36
|
+
lineCount: slice.lineCount,
|
|
37
|
+
boundaryKnown: slice.boundaryKnown,
|
|
38
|
+
truncated: slice.truncated,
|
|
39
|
+
previewText: shown.join("\n"),
|
|
40
|
+
previewLineCount: shown.length,
|
|
41
|
+
previewTruncated: lines.length > shown.length
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** External-local evidence carries a redaction marker and no pack body; the preview is counts and availability only. */
|
|
45
|
+
function buildRedactedPreview(entry, redaction) {
|
|
46
|
+
const packTreatment = entry.treatments.find((treatment) => treatment.treatmentId === "context-pack");
|
|
47
|
+
return {
|
|
48
|
+
caseId: entry.caseId,
|
|
49
|
+
status: "redacted-external-local",
|
|
50
|
+
packArtifactPath: null,
|
|
51
|
+
packAvailability: packTreatment?.availability ?? null,
|
|
52
|
+
sections: (packTreatment?.sections ?? []).map((section) => ({
|
|
53
|
+
id: section.id,
|
|
54
|
+
availability: section.availability,
|
|
55
|
+
reason: section.reason,
|
|
56
|
+
itemCount: section.itemCount,
|
|
57
|
+
estimatedTokens: section.estimatedTokens,
|
|
58
|
+
truncatedCount: section.truncatedCount ?? null
|
|
59
|
+
})),
|
|
60
|
+
task: null,
|
|
61
|
+
files: null,
|
|
62
|
+
symbols: null,
|
|
63
|
+
sourceSlices: null,
|
|
64
|
+
callRelationships: null,
|
|
65
|
+
tests: null,
|
|
66
|
+
evidenceNotes: null,
|
|
67
|
+
redaction: { ...redaction }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function buildPreview(entry, pack) {
|
|
71
|
+
if (entry.identityRedaction)
|
|
72
|
+
return buildRedactedPreview(entry, entry.identityRedaction);
|
|
73
|
+
const packTreatment = entry.treatments.find((treatment) => treatment.treatmentId === "context-pack");
|
|
74
|
+
const packArtifactPath = packTreatment?.packArtifactPath ?? null;
|
|
75
|
+
const base = {
|
|
76
|
+
caseId: entry.caseId,
|
|
77
|
+
packArtifactPath,
|
|
78
|
+
// Section summaries are persisted execution evidence; they never come from the preview truncation.
|
|
79
|
+
sections: (packTreatment?.sections ?? []).map((section) => ({
|
|
80
|
+
id: section.id,
|
|
81
|
+
availability: section.availability,
|
|
82
|
+
reason: section.reason,
|
|
83
|
+
itemCount: section.itemCount,
|
|
84
|
+
estimatedTokens: section.estimatedTokens
|
|
85
|
+
}))
|
|
86
|
+
};
|
|
87
|
+
if (!pack) {
|
|
88
|
+
return {
|
|
89
|
+
...base,
|
|
90
|
+
status: packArtifactPath === null ? "no-pack-produced" : "pack-artifact-unavailable",
|
|
91
|
+
packAvailability: packTreatment?.availability ?? null,
|
|
92
|
+
task: null,
|
|
93
|
+
files: null,
|
|
94
|
+
symbols: null,
|
|
95
|
+
sourceSlices: null,
|
|
96
|
+
callRelationships: null,
|
|
97
|
+
tests: null,
|
|
98
|
+
evidenceNotes: null
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
...base,
|
|
103
|
+
status: "available",
|
|
104
|
+
packAvailability: pack.availability,
|
|
105
|
+
task: { title: pack.task.title, summary: pack.task.summary },
|
|
106
|
+
files: bounded(pack.files, PACK_PREVIEW_MAX_FILES, (file) => ({ path: file.path, rank: file.rank, reason: file.reason })),
|
|
107
|
+
symbols: bounded(pack.symbols, PACK_PREVIEW_MAX_SYMBOLS, (symbol) => ({ name: symbol.name, file: symbol.file, line: symbol.line, nodeId: symbol.nodeId })),
|
|
108
|
+
sourceSlices: bounded(pack.sourceSlices, PACK_PREVIEW_MAX_SOURCE_SLICES, previewSlice),
|
|
109
|
+
callRelationships: bounded(pack.callRelationships.filter((relationship) => relationship.kind === "calls"), PACK_PREVIEW_MAX_CALL_RELATIONSHIPS, (relationship) => ({ fromNodeId: relationship.fromNodeId, toNodeId: relationship.toNodeId })),
|
|
110
|
+
tests: bounded(pack.tests, PACK_PREVIEW_MAX_TESTS, (test) => ({ path: test.path, how: test.how })),
|
|
111
|
+
evidenceNotes: bounded(pack.evidenceNotes, PACK_PREVIEW_MAX_EVIDENCE_NOTES, (code) => code)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Presentation-only adapter over the already-calculated `run.analysis` and `run.caseExecutionEvidence`. It recalculates no
|
|
116
|
+
* F1, fact coverage, token size, saving, matched-case membership, mean or delta; it copies persisted values. `contextPacks`
|
|
117
|
+
* are the persisted per-case pack artifacts, loaded by the caller; only display truncation is derived from them.
|
|
118
|
+
*/
|
|
119
|
+
export function buildContextPackGenerationReport(run, contextPacks = new Map()) {
|
|
120
|
+
if (run.pluginId !== CONTEXT_PACK_GENERATION_PLUGIN_ID)
|
|
121
|
+
return null;
|
|
122
|
+
const candidate = run;
|
|
123
|
+
const analysis = candidate.analysis;
|
|
124
|
+
const execution = candidate.caseExecutionEvidence;
|
|
125
|
+
if (!analysis || !execution) {
|
|
126
|
+
// A run that failed before analysis exists still gets the generic failure report.
|
|
127
|
+
if (run.status === "failed")
|
|
128
|
+
return null;
|
|
129
|
+
throw new Error("Invalid context-pack-generation report source: execution evidence and analysis are required.");
|
|
130
|
+
}
|
|
131
|
+
const inconsistent = analysis.cases.length !== execution.length ||
|
|
132
|
+
analysis.cases.some((entry, index) => entry.caseId !== execution[index].caseId ||
|
|
133
|
+
entry.treatments.length !== CONTEXT_PACK_GENERATION_TREATMENT_IDS.length ||
|
|
134
|
+
entry.treatments.some((treatment, position) => treatment.treatmentId !== CONTEXT_PACK_GENERATION_TREATMENT_IDS[position]));
|
|
135
|
+
if (inconsistent) {
|
|
136
|
+
throw new Error("Invalid context-pack-generation report source: execution evidence and analysis are inconsistent.");
|
|
137
|
+
}
|
|
138
|
+
const cases = analysis.cases.map((entry, caseIndex) => ({
|
|
139
|
+
caseId: entry.caseId,
|
|
140
|
+
caseName: execution[caseIndex].caseName,
|
|
141
|
+
benchmarkProject: entry.benchmarkProject,
|
|
142
|
+
taskLocality: entry.taskLocality,
|
|
143
|
+
treatments: entry.treatments.map((treatment) => ({
|
|
144
|
+
treatmentId: treatment.treatmentId,
|
|
145
|
+
executionStatus: treatment.executionStatus,
|
|
146
|
+
availability: treatment.availability,
|
|
147
|
+
fileF1: copyMetric(treatment.fileF1),
|
|
148
|
+
symbolF1: copyMetric(treatment.symbolF1),
|
|
149
|
+
factCoverage: treatment.quality ? copyMetric(treatment.quality.fact.coverage) : { ...NO_MEASUREMENT },
|
|
150
|
+
estimatedTokens: treatment.estimatedTokens,
|
|
151
|
+
tokenCountMethod: treatment.tokenCountMethod
|
|
152
|
+
})),
|
|
153
|
+
comparison: { ...entry.comparison }
|
|
154
|
+
}));
|
|
155
|
+
return {
|
|
156
|
+
schemaVersion: CONTEXT_PACK_GENERATION_REPORT_SCHEMA_VERSION,
|
|
157
|
+
experiment: { pluginId: run.pluginId, target: run.target.isSelf ? "self" : "external target" },
|
|
158
|
+
treatmentOrder: [...CONTEXT_PACK_GENERATION_TREATMENT_IDS],
|
|
159
|
+
methodology: { ...CONTEXT_PACK_GENERATION_METHODOLOGY },
|
|
160
|
+
cases,
|
|
161
|
+
scopes: structuredClone(analysis.scopes),
|
|
162
|
+
previews: execution.map((entry) => buildPreview(entry, contextPacks.get(entry.caseId))),
|
|
163
|
+
// Execution/analysis artifacts live directly in the output root; never expose a machine-local absolute path.
|
|
164
|
+
artifacts: run.artifacts.map((artifact) => ({
|
|
165
|
+
id: artifact.id,
|
|
166
|
+
path: artifact.path ? (path.isAbsolute(artifact.path) ? path.basename(artifact.path) : artifact.path) : null,
|
|
167
|
+
caseId: artifact.caseId ?? null
|
|
168
|
+
})),
|
|
169
|
+
limitations: [...CONTEXT_PACK_GENERATION_LIMITATIONS]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -5,6 +5,7 @@ import { buildIncrementalChangeStalenessPluginReport } from "./buildIncrementalC
|
|
|
5
5
|
import { buildContextWindowScalingReport } from "./buildContextWindowScalingReport.js";
|
|
6
6
|
import { buildRetrievalPrecisionRecallReport } from "./buildRetrievalPrecisionRecallReport.js";
|
|
7
7
|
import { buildRetrievalQueryStrategyComparisonReport } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
8
|
+
import { buildContextPackGenerationReport } from "./buildContextPackGenerationReport.js";
|
|
8
9
|
// Bulk context-window-scaling evidence is presented by the typed report section; the complete
|
|
9
10
|
// evidence stays in context-window-scaling-execution.json.
|
|
10
11
|
const CONTEXT_WINDOW_SCALING_BULK_KEYS = ["executionEvidence", "aggregate"];
|
|
@@ -12,6 +13,8 @@ const CONTEXT_WINDOW_SCALING_BULK_KEYS = ["executionEvidence", "aggregate"];
|
|
|
12
13
|
const RETRIEVAL_PRECISION_RECALL_BULK_KEYS = ["caseExecutionEvidence", "aggregate"];
|
|
13
14
|
// Same for retrieval-query-strategy-comparison: the typed section presents execution evidence and analysis.
|
|
14
15
|
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
16
|
+
// Same for context-pack-generation: the typed section presents execution evidence and analysis.
|
|
17
|
+
const CONTEXT_PACK_GENERATION_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
15
18
|
const V043_BULK_ARRAY_KEYS = [
|
|
16
19
|
"v043StageContextExecutions",
|
|
17
20
|
"v043StageContextEvaluations",
|
|
@@ -26,6 +29,7 @@ export function buildPluginExperimentReport(args) {
|
|
|
26
29
|
const contextWindowScaling = buildContextWindowScalingReport(args.run);
|
|
27
30
|
const retrievalPrecisionRecall = buildRetrievalPrecisionRecallReport(args.run);
|
|
28
31
|
const retrievalQueryStrategyComparison = buildRetrievalQueryStrategyComparisonReport(args.run);
|
|
32
|
+
const contextPackGeneration = buildContextPackGenerationReport(args.run, args.contextPacks);
|
|
29
33
|
const rawRun = { ...args.run, artifacts: relativizeArtifacts(args.run.artifacts, outputRoot) };
|
|
30
34
|
for (const key of V043_BULK_ARRAY_KEYS) {
|
|
31
35
|
delete rawRun[key];
|
|
@@ -45,6 +49,11 @@ export function buildPluginExperimentReport(args) {
|
|
|
45
49
|
delete rawRun[key];
|
|
46
50
|
}
|
|
47
51
|
}
|
|
52
|
+
if (contextPackGeneration) {
|
|
53
|
+
for (const key of CONTEXT_PACK_GENERATION_BULK_KEYS) {
|
|
54
|
+
delete rawRun[key];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
48
57
|
return {
|
|
49
58
|
metadata: {
|
|
50
59
|
generatedAt: args.generatedAt ?? new Date().toISOString(),
|
|
@@ -73,8 +82,9 @@ export function buildPluginExperimentReport(args) {
|
|
|
73
82
|
contextWindowScaling,
|
|
74
83
|
retrievalPrecisionRecall,
|
|
75
84
|
retrievalQueryStrategyComparison,
|
|
85
|
+
contextPackGeneration,
|
|
76
86
|
contextStrategyComparisonV043,
|
|
77
|
-
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison),
|
|
87
|
+
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration),
|
|
78
88
|
rawRun,
|
|
79
89
|
};
|
|
80
90
|
}
|
|
@@ -153,7 +163,19 @@ function affectedNeighborhoodSentence(report) {
|
|
|
153
163
|
`reindex recommendations were recommended=${summary.recommendedReindexCount}, not-indicated=${summary.notIndicatedReindexCount}, unknown=${summary.unknownReindexRecommendationCount}. ` +
|
|
154
164
|
"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.");
|
|
155
165
|
}
|
|
156
|
-
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison) {
|
|
166
|
+
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration = null) {
|
|
167
|
+
if (contextPackGeneration) {
|
|
168
|
+
const overall = contextPackGeneration.scopes.find((scope) => scope.scopeId === "overall");
|
|
169
|
+
const saved = overall?.tokenSavings;
|
|
170
|
+
const deltas = overall?.pairedDeltas;
|
|
171
|
+
const summary = overall && saved && deltas
|
|
172
|
+
? `Overall matched comparison over ${overall.includedCaseCount} of ${overall.caseCount} case(s): the context pack used a mean of ${saved.meanTokensSaved} fewer estimated tokens than raw full-file context (${saved.percentSavedOfMeans}% of the raw mean; a negative value means more tokens); mean fact coverage delta ${deltas.meanFactCoverageDelta}, mean file F1 delta ${deltas.meanFileF1Delta}, mean symbol F1 delta ${deltas.meanSymbolF1Delta}. The treatments are reported side by side without a composite score.`
|
|
173
|
+
: "No matched complete-case context-pack comparison was available for the overall scope. Review treatment availability and excluded cases before drawing conclusions.";
|
|
174
|
+
return {
|
|
175
|
+
summary,
|
|
176
|
+
recommendedNextStep: "Review per-case coverage and size, the scope aggregates, and the bounded pack preview; unavailable values are never treated as zero."
|
|
177
|
+
};
|
|
178
|
+
}
|
|
157
179
|
if (retrievalQueryStrategyComparison) {
|
|
158
180
|
const overall = retrievalQueryStrategyComparison.scopes.find((scope) => scope.scopeId === "overall");
|
|
159
181
|
const taskTypes = "Task-type results are reported separately for localized, cross-module, and broad-change cases.";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const CONTEXT_PACK_GENERATION_REPORT_SCHEMA_VERSION = "my-dev-kit-lab-context-pack-generation-report-v1";
|
|
2
|
+
/** Report-presentation limits only. They never change the experimental pack limits and are not CLI configuration. */
|
|
3
|
+
export const PACK_PREVIEW_MAX_FILES = 5;
|
|
4
|
+
export const PACK_PREVIEW_MAX_SYMBOLS = 5;
|
|
5
|
+
export const PACK_PREVIEW_MAX_SOURCE_SLICES = 3;
|
|
6
|
+
export const PACK_PREVIEW_MAX_SOURCE_LINES_PER_SLICE = 12;
|
|
7
|
+
export const PACK_PREVIEW_MAX_CALL_RELATIONSHIPS = 5;
|
|
8
|
+
export const PACK_PREVIEW_MAX_TESTS = 5;
|
|
9
|
+
export const PACK_PREVIEW_MAX_EVIDENCE_NOTES = 5;
|
|
@@ -28,6 +28,11 @@ 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";
|
|
31
36
|
export * from "./retrievalQueryStrategyComparisonReportModel.js";
|
|
32
37
|
export { buildRetrievalQueryStrategyComparisonReport, RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
33
38
|
export { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.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
|
+
}
|
|
@@ -4,6 +4,7 @@ import { renderIncrementalChangeStalenessHtml } from "./renderIncrementalChangeS
|
|
|
4
4
|
import { renderContextWindowScalingHtml } from "./renderContextWindowScalingHtml.js";
|
|
5
5
|
import { renderRetrievalPrecisionRecallHtml } from "./renderRetrievalPrecisionRecallHtml.js";
|
|
6
6
|
import { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
|
|
7
|
+
import { renderContextPackGenerationHtml } from "./renderContextPackGenerationHtml.js";
|
|
7
8
|
export function renderPluginExperimentReportHtml(report) {
|
|
8
9
|
const redactedTarget = report.target.privacyProjection === "external-local-redacted";
|
|
9
10
|
return `<!DOCTYPE html>
|
|
@@ -112,6 +113,8 @@ export function renderPluginExperimentReportHtml(report) {
|
|
|
112
113
|
|
|
113
114
|
${renderRetrievalQueryStrategyComparisonHtml(report.retrievalQueryStrategyComparison ?? null)}
|
|
114
115
|
|
|
116
|
+
${renderContextPackGenerationHtml(report.contextPackGeneration ?? null)}
|
|
117
|
+
|
|
115
118
|
<section>
|
|
116
119
|
<h2>Warnings, Skips, And Failures</h2>
|
|
117
120
|
${report.findings.length ? table(["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
|