@dailephd/my-dev-kit-lab 0.5.0 → 0.5.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 +26 -0
- package/README.md +26 -10
- package/benchmarks/contracts/benchmark-project-profiles.json +14 -14
- package/benchmarks/contracts/warm-index-benchmark-cases.json +764 -0
- package/dist/src/agents/adapters/claudeAdapter.js +47 -4
- package/dist/src/agents/adapters/codexAdapter.js +64 -5
- package/dist/src/cli/help.js +22 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +6 -1
- package/dist/src/commands/runExperimentRunCommand.js +87 -5
- package/dist/src/commands/runWarmIndexCampaignPresentation.js +64 -0
- package/dist/src/core/runMeasuredCommand.js +49 -10
- package/dist/src/evaluation/benchmarkMetadata.js +191 -1
- package/dist/src/evaluation/projectComplexity.js +1 -1
- package/dist/src/evaluation/readEvaluationCases.js +7 -1
- package/dist/src/evaluation/types.js +1 -1
- package/dist/src/experiments/plugins/warmIndexReuse/agentEvaluation.js +233 -0
- package/dist/src/experiments/plugins/warmIndexReuse/campaignPresets.js +83 -0
- package/dist/src/experiments/plugins/warmIndexReuse/config.js +51 -0
- package/dist/src/experiments/plugins/warmIndexReuse/fakeAgentEvaluation.js +6 -118
- package/dist/src/experiments/plugins/warmIndexReuse/index.js +3 -1
- package/dist/src/experiments/plugins/warmIndexReuse/metrics.js +35 -11
- package/dist/src/experiments/plugins/warmIndexReuse/plugin.js +50 -14
- package/dist/src/experiments/plugins/warmIndexReuse/realAgentPrompt.js +52 -0
- package/dist/src/experiments/plugins/warmIndexReuse/selection.js +35 -0
- package/dist/src/gallery/index.js +1 -0
- package/dist/src/gallery/writeGalleryManifest.js +5 -1
- package/dist/src/gallery/writeWarmIndexCampaignGallery.js +116 -0
- package/dist/src/plots/buildWarmIndexPlotData.js +21 -2
- package/dist/src/report/experiments/buildPluginExperimentReport.js +35 -8
- package/dist/src/report/experiments/buildWarmIndexReuseReport.js +138 -12
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +35 -12
- package/dist/src/report/experiments/renderWarmIndexReuseHtml.js +45 -14
- package/docs/METRICS.md +33 -3
- package/package.json +1 -1
|
@@ -100,15 +100,42 @@ function buildFindings(run) {
|
|
|
100
100
|
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse) {
|
|
101
101
|
if (run.pluginId === "warm-index-reuse" && warmIndexReuse) {
|
|
102
102
|
const summary = warmIndexReuse.summary;
|
|
103
|
+
const campaign = warmIndexReuse.agentCampaign;
|
|
104
|
+
if (!campaign) {
|
|
105
|
+
return {
|
|
106
|
+
summary: `The warm-index run prepared ${summary.preparedSessionProjectCount} of ${summary.projectCount} project indexes and evaluated ${summary.taskCount} tasks. ` +
|
|
107
|
+
"The report separates one-time index-build duration from per-task retrieval duration and shows how the fixed build cost is amortized across repeated tasks. " +
|
|
108
|
+
`Deterministic fake-agent correctness/token evidence is available for ${summary.agentCorrectnessAvailableCount} of ${summary.agentSideCount} task sides (correctness) and ${summary.agentTotalTokensAvailableCount} (total tokens); ` +
|
|
109
|
+
"fake-agent totals are simulated harness evidence, not real-model or provider measurements. " +
|
|
110
|
+
"Estimated context-token values are context-size estimates, not provider token usage.",
|
|
111
|
+
recommendedNextStep: run.status === "completed"
|
|
112
|
+
? "Review per-task measurements and cumulative component costs for each project; the variants are reported side by side and are not ranked."
|
|
113
|
+
: "Inspect unavailable metrics, warnings, and failures before drawing conclusions from this run.",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const nonCompletedCounts = [
|
|
117
|
+
["failed", campaign.outcomeCounts.failed],
|
|
118
|
+
["timeout", campaign.outcomeCounts.timeout],
|
|
119
|
+
["invalid-output", campaign.outcomeCounts.invalidOutput],
|
|
120
|
+
["agent-unavailable", campaign.outcomeCounts.agentUnavailable],
|
|
121
|
+
["agent-limit-reached", campaign.outcomeCounts.agentLimitReached],
|
|
122
|
+
["skipped", campaign.outcomeCounts.skipped],
|
|
123
|
+
]
|
|
124
|
+
.filter(([, count]) => count > 0)
|
|
125
|
+
.map(([label, count]) => `${label}=${count}`)
|
|
126
|
+
.join(", ");
|
|
127
|
+
const isInfrastructureComplete = run.status === "completed";
|
|
128
|
+
const isAgentEvidenceComplete = campaign.agentEvidenceStatus === "complete";
|
|
103
129
|
return {
|
|
104
|
-
summary: `
|
|
105
|
-
|
|
106
|
-
`
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
summary: `Campaign preset ${campaign.presetId} selected agent ${campaign.agentId} over ${summary.projectCount} prepared project ` +
|
|
131
|
+
`index${summary.projectCount === 1 ? "" : "es"} and ${campaign.selectedCaseCount} selected task${campaign.selectedCaseCount === 1 ? "" : "s"}. ` +
|
|
132
|
+
`Agent evidence status: ${campaign.agentEvidenceStatus} (${campaign.executedSideCount} of ${campaign.scheduledSideCount} scheduled sides executed` +
|
|
133
|
+
`${nonCompletedCounts ? `; outcome counts: ${nonCompletedCounts}` : ""}). ` +
|
|
134
|
+
`Token evidence status: ${campaign.tokenEvidenceStatus} (${summary.agentTotalTokensAvailableCount} of ${summary.agentSideCount} task sides have a total-token value). ` +
|
|
135
|
+
"Context estimated tokens remain a separate character-based context-size estimate and are never substituted for provider telemetry.",
|
|
136
|
+
recommendedNextStep: isInfrastructureComplete && isAgentEvidenceComplete
|
|
137
|
+
? "Review per-task correctness, token provenance, and cumulative component measurements."
|
|
138
|
+
: "Review infrastructure gaps (index build, raw/warm retrieval) separately from provider/agent limitations before drawing conclusions from this run.",
|
|
112
139
|
};
|
|
113
140
|
}
|
|
114
141
|
if (run.pluginId === "context-strategy-comparison" && (contextStrategyComparisonV043?.summary.strategyCount ?? 0) > 0) {
|
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { WARM_INDEX_REUSE_REPORT_SCHEMA_VERSION, } from "./warmIndexReuseReportModel.js";
|
|
2
2
|
const WARM_INDEX_REUSE_PLUGIN_ID = "warm-index-reuse";
|
|
3
|
+
const COMMON_LIMITATIONS = [
|
|
4
|
+
"Context estimated-token counts use the character-based estimate and are not provider billing telemetry.",
|
|
5
|
+
"Correctness is deterministic answer-key scoring, not semantic LLM judging.",
|
|
6
|
+
"Component-duration sums are not equivalent to provider end-to-end latency.",
|
|
7
|
+
"Failed or missing prefix measurements make cumulative metrics unavailable; missing values are never treated as zero.",
|
|
8
|
+
"No composite score, winner, ranking, or break-even task is calculated.",
|
|
9
|
+
];
|
|
10
|
+
const FAKE_AGENT_LIMITATIONS = [
|
|
11
|
+
"Agent evidence is deterministic simulated fake-agent evidence.",
|
|
12
|
+
"Fake-agent total tokens are simulated harness telemetry, not provider billing telemetry.",
|
|
13
|
+
];
|
|
14
|
+
const CODEX_CAMPAIGN_LIMITATIONS = [
|
|
15
|
+
"Agent evidence comes from Codex CLI campaign execution over the exact runner-supplied benchmark contexts.",
|
|
16
|
+
"Token totals are CLI-exposed adapter telemetry when available.",
|
|
17
|
+
"Token evidence must be interpreted using tokenUsageSource/tokenUsageReliability.",
|
|
18
|
+
"Context estimates are never substituted for missing agent token totals.",
|
|
19
|
+
];
|
|
20
|
+
const CLAUDE_CAMPAIGN_LIMITATIONS = [
|
|
21
|
+
"Agent evidence comes from Claude CLI campaign execution over the exact runner-supplied benchmark contexts.",
|
|
22
|
+
"Token totals are used when exposed by the CLI output.",
|
|
23
|
+
"Token totals may be unavailable for some CLI/version/run outcomes.",
|
|
24
|
+
"Unavailable token totals remain unavailable and are never replaced by zero or a context estimate.",
|
|
25
|
+
];
|
|
3
26
|
const COST_MODEL = [
|
|
4
27
|
"Index construction is a one-time cost per benchmark project; task retrieval is the repeated warm cost.",
|
|
5
28
|
"Cold start: for the first task in a project, the cumulative warm component duration is the one-time index build plus the first retrieval.",
|
|
@@ -8,15 +31,15 @@ const COST_MODEL = [
|
|
|
8
31
|
"Raw cumulative duration sums one raw-context construction per task; warm cumulative component duration includes one index build plus each warm retrieval once.",
|
|
9
32
|
"Cumulative durations are sums of measured components, not wall-clock or end-to-end latency, and are not presented as a speedup.",
|
|
10
33
|
];
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
34
|
+
const OUTCOME_STATUS_TO_COUNT_KEY = {
|
|
35
|
+
completed: "completed",
|
|
36
|
+
failed: "failed",
|
|
37
|
+
timeout: "timeout",
|
|
38
|
+
"invalid-output": "invalidOutput",
|
|
39
|
+
"agent-unavailable": "agentUnavailable",
|
|
40
|
+
"agent-limit-reached": "agentLimitReached",
|
|
41
|
+
skipped: "skipped",
|
|
42
|
+
};
|
|
20
43
|
/**
|
|
21
44
|
* Presents the warm-index metrics already calculated on the run. It performs no amortization,
|
|
22
45
|
* cumulative, or availability calculation of its own.
|
|
@@ -31,6 +54,14 @@ export function buildWarmIndexReuseReport(run) {
|
|
|
31
54
|
if (executions.length !== metricProjects.length) {
|
|
32
55
|
throw new Error("Invalid warm-index report source: project executions and metrics are inconsistent.");
|
|
33
56
|
}
|
|
57
|
+
const campaignPreset = readString(run.metadata?.campaignPreset);
|
|
58
|
+
const campaignAgentId = readString(run.metadata?.campaignAgentId);
|
|
59
|
+
const campaignTimeoutMs = readNumber(run.metadata?.campaignTimeoutMs);
|
|
60
|
+
if (campaignAgentId !== undefined && campaignAgentId !== "codex" && campaignAgentId !== "claude") {
|
|
61
|
+
throw new Error(`Invalid warm-index report source: unsupported campaign agent id ${campaignAgentId}.`);
|
|
62
|
+
}
|
|
63
|
+
const isCampaign = campaignPreset !== undefined;
|
|
64
|
+
const allSides = [];
|
|
34
65
|
const projects = executions.map((execution, projectIndex) => {
|
|
35
66
|
const metrics = metricProjects[projectIndex];
|
|
36
67
|
if (metrics.benchmarkProject !== execution.benchmarkProject ||
|
|
@@ -47,6 +78,9 @@ export function buildWarmIndexReuseReport(run) {
|
|
|
47
78
|
taskCount: metrics.tasks.length,
|
|
48
79
|
tasks: metrics.tasks.map((task, taskIndex) => {
|
|
49
80
|
const agents = agentProjects?.[projectIndex]?.tasks[taskIndex];
|
|
81
|
+
const rawEvidence = agents?.raw ?? null;
|
|
82
|
+
const warmEvidence = agents?.warm ?? null;
|
|
83
|
+
allSides.push(rawEvidence, warmEvidence);
|
|
50
84
|
return {
|
|
51
85
|
taskOrdinal: task.taskOrdinal,
|
|
52
86
|
caseId: task.caseId,
|
|
@@ -54,12 +88,22 @@ export function buildWarmIndexReuseReport(run) {
|
|
|
54
88
|
warmStatus: execution.tasks[taskIndex].warmStatus,
|
|
55
89
|
raw: task.raw,
|
|
56
90
|
warm: task.warm,
|
|
57
|
-
rawAgent: toReportAgent(
|
|
58
|
-
warmAgent: toReportAgent(
|
|
91
|
+
rawAgent: toReportAgent(rawEvidence),
|
|
92
|
+
warmAgent: toReportAgent(warmEvidence),
|
|
59
93
|
};
|
|
60
94
|
}),
|
|
61
95
|
};
|
|
62
96
|
});
|
|
97
|
+
const agentIdentity = deriveAgentIdentity({ isCampaign, campaignAgentId, sides: allSides });
|
|
98
|
+
const agentCampaign = isCampaign
|
|
99
|
+
? buildCampaignSummary({
|
|
100
|
+
presetId: campaignPreset,
|
|
101
|
+
agentId: agentIdentity.id,
|
|
102
|
+
timeoutMs: campaignTimeoutMs ?? 0,
|
|
103
|
+
selectedCaseCount: projects.reduce((sum, project) => sum + project.taskCount, 0),
|
|
104
|
+
sides: allSides,
|
|
105
|
+
})
|
|
106
|
+
: null;
|
|
63
107
|
const sides = projects.flatMap((project) => project.tasks.flatMap((task) => [task.raw, task.warm]));
|
|
64
108
|
return {
|
|
65
109
|
schemaVersion: WARM_INDEX_REUSE_REPORT_SCHEMA_VERSION,
|
|
@@ -73,14 +117,90 @@ export function buildWarmIndexReuseReport(run) {
|
|
|
73
117
|
agentTotalTokensAvailableCount: sides.filter((side) => side.agentTotalTokens.availability === "available").length,
|
|
74
118
|
},
|
|
75
119
|
costModel: [...COST_MODEL],
|
|
76
|
-
limitations:
|
|
120
|
+
limitations: buildLimitations(agentIdentity),
|
|
77
121
|
projects,
|
|
122
|
+
agent: agentIdentity,
|
|
123
|
+
agentCampaign,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function deriveAgentIdentity(args) {
|
|
127
|
+
const nonNullAgentIds = new Set(args.sides.filter((side) => side !== null).map((side) => side.agentId));
|
|
128
|
+
if (!args.isCampaign) {
|
|
129
|
+
if ([...nonNullAgentIds].some((agentId) => agentId !== "fake-agent")) {
|
|
130
|
+
throw new Error(`Invalid warm-index report source: a legacy non-campaign run must not contain non-fake agent evidence, found ${[...nonNullAgentIds].join(", ")}.`);
|
|
131
|
+
}
|
|
132
|
+
return { id: "fake-agent", mode: "deterministic-fake" };
|
|
133
|
+
}
|
|
134
|
+
const campaignAgentId = args.campaignAgentId;
|
|
135
|
+
if (!campaignAgentId) {
|
|
136
|
+
throw new Error("Invalid warm-index report source: a campaign run must carry a campaignAgentId.");
|
|
137
|
+
}
|
|
138
|
+
for (const agentId of nonNullAgentIds) {
|
|
139
|
+
if (agentId !== campaignAgentId) {
|
|
140
|
+
throw new Error(`Invalid warm-index report source: campaign metadata says ${campaignAgentId} but agent evidence contains ${agentId}.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { id: campaignAgentId, mode: "real-provider" };
|
|
144
|
+
}
|
|
145
|
+
function buildCampaignSummary(args) {
|
|
146
|
+
const scheduledSideCount = args.selectedCaseCount * 2;
|
|
147
|
+
const executedSides = args.sides.filter((side) => side !== null);
|
|
148
|
+
const executedSideCount = executedSides.length;
|
|
149
|
+
const notRunForMissingContextCount = scheduledSideCount - executedSideCount;
|
|
150
|
+
const outcomeCounts = {
|
|
151
|
+
completed: 0,
|
|
152
|
+
failed: 0,
|
|
153
|
+
timeout: 0,
|
|
154
|
+
invalidOutput: 0,
|
|
155
|
+
agentUnavailable: 0,
|
|
156
|
+
agentLimitReached: 0,
|
|
157
|
+
skipped: 0,
|
|
158
|
+
};
|
|
159
|
+
for (const side of executedSides) {
|
|
160
|
+
const key = OUTCOME_STATUS_TO_COUNT_KEY[side.status];
|
|
161
|
+
if (!key) {
|
|
162
|
+
throw new Error(`Invalid warm-index report source: unsupported campaign agent status "${side.status}".`);
|
|
163
|
+
}
|
|
164
|
+
outcomeCounts[key] += 1;
|
|
165
|
+
}
|
|
166
|
+
const tokenAvailableSideCount = executedSides.filter((side) => typeof side.tokenUsage.totalTokens === "number" && Number.isFinite(side.tokenUsage.totalTokens)).length;
|
|
167
|
+
const agentEvidenceStatus = executedSideCount === 0
|
|
168
|
+
? "unavailable"
|
|
169
|
+
: executedSideCount === scheduledSideCount && outcomeCounts.completed === executedSideCount
|
|
170
|
+
? "complete"
|
|
171
|
+
: "partial";
|
|
172
|
+
const tokenEvidenceStatus = executedSideCount === 0
|
|
173
|
+
? "unavailable"
|
|
174
|
+
: tokenAvailableSideCount === 0
|
|
175
|
+
? "unavailable"
|
|
176
|
+
: tokenAvailableSideCount === executedSideCount
|
|
177
|
+
? "complete"
|
|
178
|
+
: "partial";
|
|
179
|
+
return {
|
|
180
|
+
presetId: args.presetId,
|
|
181
|
+
agentId: args.agentId,
|
|
182
|
+
timeoutMs: args.timeoutMs,
|
|
183
|
+
selectedCaseCount: args.selectedCaseCount,
|
|
184
|
+
scheduledSideCount,
|
|
185
|
+
executedSideCount,
|
|
186
|
+
notRunForMissingContextCount,
|
|
187
|
+
outcomeCounts,
|
|
188
|
+
agentEvidenceStatus,
|
|
189
|
+
tokenEvidenceStatus,
|
|
78
190
|
};
|
|
79
191
|
}
|
|
192
|
+
function buildLimitations(agent) {
|
|
193
|
+
if (agent.mode === "deterministic-fake") {
|
|
194
|
+
return [...COMMON_LIMITATIONS, ...FAKE_AGENT_LIMITATIONS];
|
|
195
|
+
}
|
|
196
|
+
const providerLimitations = agent.id === "codex" ? CODEX_CAMPAIGN_LIMITATIONS : CLAUDE_CAMPAIGN_LIMITATIONS;
|
|
197
|
+
return [...COMMON_LIMITATIONS, ...providerLimitations];
|
|
198
|
+
}
|
|
80
199
|
function toReportAgent(agent) {
|
|
81
200
|
if (!agent)
|
|
82
201
|
return null;
|
|
83
202
|
return {
|
|
203
|
+
agentId: agent.agentId,
|
|
84
204
|
status: agent.status,
|
|
85
205
|
passed: agent.correctness.passed,
|
|
86
206
|
tokenUsageSource: agent.tokenUsage.source,
|
|
@@ -89,3 +209,9 @@ function toReportAgent(agent) {
|
|
|
89
209
|
errors: [...agent.errors],
|
|
90
210
|
};
|
|
91
211
|
}
|
|
212
|
+
function readString(value) {
|
|
213
|
+
return typeof value === "string" && value ? value : undefined;
|
|
214
|
+
}
|
|
215
|
+
function readNumber(value) {
|
|
216
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
217
|
+
}
|
|
@@ -265,10 +265,28 @@ function formatWarmMetric(metric) {
|
|
|
265
265
|
}
|
|
266
266
|
function formatWarmAgent(agent) {
|
|
267
267
|
if (agent === null)
|
|
268
|
-
return "not run (no context evidence for this side)";
|
|
268
|
+
return "not run (no usable context evidence for this side)";
|
|
269
269
|
const passed = agent.passed === null ? "unavailable" : String(agent.passed);
|
|
270
270
|
const issues = [...agent.errors, ...agent.warnings].slice(0, 3).join("; ");
|
|
271
|
-
return `status ${agent.status}, passed ${passed}, token source ${agent.tokenUsageSource}, reliability ${agent.tokenUsageReliability}${issues ? `, notes: ${issues}` : ""}`;
|
|
271
|
+
return `agent ${agent.agentId}, status ${agent.status}, passed ${passed}, token source ${agent.tokenUsageSource}, reliability ${agent.tokenUsageReliability}${issues ? `, notes: ${issues}` : ""}`;
|
|
272
|
+
}
|
|
273
|
+
function renderCampaignSection(lines, campaign) {
|
|
274
|
+
pushSection(lines, "Real-Agent Campaign");
|
|
275
|
+
lines.push(fieldLine("Campaign Preset", campaign.presetId));
|
|
276
|
+
lines.push(fieldLine("Agent ID", campaign.agentId));
|
|
277
|
+
lines.push(fieldLine("Campaign Timeout", `${campaign.timeoutMs} ms`));
|
|
278
|
+
lines.push(fieldLine("Scheduled Agent Sides", campaign.scheduledSideCount));
|
|
279
|
+
lines.push(fieldLine("Executed Agent Sides", campaign.executedSideCount));
|
|
280
|
+
lines.push(fieldLine("Not Run For Missing Context", campaign.notRunForMissingContextCount));
|
|
281
|
+
lines.push(fieldLine("Agent Evidence Status", campaign.agentEvidenceStatus));
|
|
282
|
+
lines.push(fieldLine("Token Evidence Status", campaign.tokenEvidenceStatus));
|
|
283
|
+
lines.push(fieldLine("Completed Agent Sides", campaign.outcomeCounts.completed));
|
|
284
|
+
lines.push(fieldLine("Failed Agent Sides", campaign.outcomeCounts.failed));
|
|
285
|
+
lines.push(fieldLine("Timeout Agent Sides", campaign.outcomeCounts.timeout));
|
|
286
|
+
lines.push(fieldLine("Invalid-Output Agent Sides", campaign.outcomeCounts.invalidOutput));
|
|
287
|
+
lines.push(fieldLine("Agent-Unavailable Sides", campaign.outcomeCounts.agentUnavailable));
|
|
288
|
+
lines.push(fieldLine("Agent-Limit-Reached Sides", campaign.outcomeCounts.agentLimitReached));
|
|
289
|
+
lines.push(fieldLine("Skipped Agent Sides", campaign.outcomeCounts.skipped));
|
|
272
290
|
}
|
|
273
291
|
function renderWarmIndexReuseSection(lines, section) {
|
|
274
292
|
if (section === null) {
|
|
@@ -279,8 +297,13 @@ function renderWarmIndexReuseSection(lines, section) {
|
|
|
279
297
|
lines.push(fieldLine("Task Count", section.summary.taskCount));
|
|
280
298
|
lines.push(fieldLine("Prepared Session Project Count", section.summary.preparedSessionProjectCount));
|
|
281
299
|
lines.push(fieldLine("Incomplete Or Failed Project Count", section.summary.incompleteProjectCount));
|
|
282
|
-
lines.push(fieldLine("
|
|
283
|
-
lines.push(fieldLine("
|
|
300
|
+
lines.push(fieldLine("Agent ID", section.agent.id));
|
|
301
|
+
lines.push(fieldLine("Agent Mode", section.agent.mode));
|
|
302
|
+
lines.push(fieldLine("Agent Correctness Available (task sides)", `${section.summary.agentCorrectnessAvailableCount} of ${section.summary.agentSideCount}`));
|
|
303
|
+
lines.push(fieldLine("Agent Token Totals Available (task sides)", `${section.summary.agentTotalTokensAvailableCount} of ${section.summary.agentSideCount}`));
|
|
304
|
+
if (section.agentCampaign) {
|
|
305
|
+
renderCampaignSection(lines, section.agentCampaign);
|
|
306
|
+
}
|
|
284
307
|
lines.push("Cold Start And Warm Reuse:");
|
|
285
308
|
pushDashList(lines, section.costModel);
|
|
286
309
|
lines.push("Limitations:");
|
|
@@ -312,14 +335,14 @@ function renderWarmIndexReuseSection(lines, section) {
|
|
|
312
335
|
["Cumulative Warm Component Duration", formatWarmMetric(task.warm.cumulativeComponentDurationMs)],
|
|
313
336
|
["Cumulative Raw Estimated Context Tokens (estimate)", formatWarmMetric(task.raw.cumulativeEstimatedContextTokens)],
|
|
314
337
|
["Cumulative Warm Estimated Context Tokens (estimate)", formatWarmMetric(task.warm.cumulativeEstimatedContextTokens)],
|
|
315
|
-
["Raw
|
|
316
|
-
["Warm
|
|
317
|
-
["Raw Agent Correctness
|
|
318
|
-
["Warm Agent Correctness
|
|
319
|
-
["Raw Agent Total Tokens
|
|
320
|
-
["Warm Agent Total Tokens
|
|
321
|
-
["Cumulative Raw Agent Total Tokens
|
|
322
|
-
["Cumulative Warm Agent Total Tokens
|
|
338
|
+
["Raw Agent Evaluation", formatWarmAgent(task.rawAgent)],
|
|
339
|
+
["Warm Agent Evaluation", formatWarmAgent(task.warmAgent)],
|
|
340
|
+
["Raw Agent Correctness", formatWarmMetric(task.raw.agentCorrectness)],
|
|
341
|
+
["Warm Agent Correctness", formatWarmMetric(task.warm.agentCorrectness)],
|
|
342
|
+
["Raw Agent Total Tokens", formatWarmMetric(task.raw.agentTotalTokens)],
|
|
343
|
+
["Warm Agent Total Tokens", formatWarmMetric(task.warm.agentTotalTokens)],
|
|
344
|
+
["Cumulative Raw Agent Total Tokens", formatWarmMetric(task.raw.cumulativeAgentTotalTokens)],
|
|
345
|
+
["Cumulative Warm Agent Total Tokens", formatWarmMetric(task.warm.cumulativeAgentTotalTokens)],
|
|
323
346
|
];
|
|
324
347
|
for (const [label, value] of rows) {
|
|
325
348
|
lines.push(fieldLine(label, value));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { agentLabel } from "../../experiments/plugins/warmIndexReuse/metrics.js";
|
|
1
2
|
const UNIT_LABELS = {
|
|
2
3
|
ms: "ms",
|
|
3
4
|
characters: "chars",
|
|
@@ -19,17 +20,40 @@ export function renderWarmIndexReuseHtml(section) {
|
|
|
19
20
|
["Tasks", String(section.summary.taskCount)],
|
|
20
21
|
["Projects with a prepared index session", String(section.summary.preparedSessionProjectCount)],
|
|
21
22
|
["Incomplete or failed projects", String(section.summary.incompleteProjectCount)],
|
|
22
|
-
["
|
|
23
|
-
["
|
|
23
|
+
["Agent ID", section.agent.id],
|
|
24
|
+
["Agent mode", section.agent.mode],
|
|
25
|
+
["Agent correctness available", `${section.summary.agentCorrectnessAvailableCount} of ${section.summary.agentSideCount}`],
|
|
26
|
+
["Agent token totals available", `${section.summary.agentTotalTokensAvailableCount} of ${section.summary.agentSideCount}`],
|
|
24
27
|
])}
|
|
28
|
+
${section.agentCampaign ? renderCampaignSection(section.agentCampaign) : ""}
|
|
25
29
|
<h3>Cold Start And Warm Reuse</h3>
|
|
26
30
|
${list(section.costModel)}
|
|
27
31
|
<h3>Limitations</h3>
|
|
28
32
|
${list(section.limitations)}
|
|
29
|
-
${section.projects.length === 0 ? "<p>No warm-index project evidence was recorded.</p>" : section.projects.map(renderProject).join("\n")}
|
|
33
|
+
${section.projects.length === 0 ? "<p>No warm-index project evidence was recorded.</p>" : section.projects.map((project, index) => renderProject(project, index, section.agent.id)).join("\n")}
|
|
30
34
|
</section>`;
|
|
31
35
|
}
|
|
32
|
-
function
|
|
36
|
+
function renderCampaignSection(campaign) {
|
|
37
|
+
return `<h3>Real-Agent Campaign</h3>
|
|
38
|
+
${table(["Field", "Value"], [
|
|
39
|
+
["Preset", campaign.presetId],
|
|
40
|
+
["Agent", campaign.agentId],
|
|
41
|
+
["Timeout (ms)", String(campaign.timeoutMs)],
|
|
42
|
+
["Scheduled agent sides", String(campaign.scheduledSideCount)],
|
|
43
|
+
["Executed agent sides", String(campaign.executedSideCount)],
|
|
44
|
+
["Not run for missing context", String(campaign.notRunForMissingContextCount)],
|
|
45
|
+
["Agent evidence status", campaign.agentEvidenceStatus],
|
|
46
|
+
["Token evidence status", campaign.tokenEvidenceStatus],
|
|
47
|
+
["Completed agent sides", String(campaign.outcomeCounts.completed)],
|
|
48
|
+
["Failed agent sides", String(campaign.outcomeCounts.failed)],
|
|
49
|
+
["Timeout agent sides", String(campaign.outcomeCounts.timeout)],
|
|
50
|
+
["Invalid-output agent sides", String(campaign.outcomeCounts.invalidOutput)],
|
|
51
|
+
["Agent-unavailable sides", String(campaign.outcomeCounts.agentUnavailable)],
|
|
52
|
+
["Agent-limit-reached sides", String(campaign.outcomeCounts.agentLimitReached)],
|
|
53
|
+
["Skipped agent sides", String(campaign.outcomeCounts.skipped)],
|
|
54
|
+
])}`;
|
|
55
|
+
}
|
|
56
|
+
function renderProject(project, index, agentId) {
|
|
33
57
|
const unavailableRows = project.tasks.flatMap((task) => [
|
|
34
58
|
["Raw context characters", task.raw.contextCharacters],
|
|
35
59
|
["Warm context characters", task.warm.contextCharacters],
|
|
@@ -42,12 +66,12 @@ function renderProject(project, index) {
|
|
|
42
66
|
["Cumulative warm component duration", task.warm.cumulativeComponentDurationMs],
|
|
43
67
|
["Cumulative raw estimated context tokens", task.raw.cumulativeEstimatedContextTokens],
|
|
44
68
|
["Cumulative warm estimated context tokens", task.warm.cumulativeEstimatedContextTokens],
|
|
45
|
-
["Raw
|
|
46
|
-
["Warm
|
|
47
|
-
["Raw
|
|
48
|
-
["Warm
|
|
49
|
-
["Cumulative raw
|
|
50
|
-
["Cumulative warm
|
|
69
|
+
["Raw agent correctness", task.raw.agentCorrectness],
|
|
70
|
+
["Warm agent correctness", task.warm.agentCorrectness],
|
|
71
|
+
["Raw agent total tokens", task.raw.agentTotalTokens],
|
|
72
|
+
["Warm agent total tokens", task.warm.agentTotalTokens],
|
|
73
|
+
["Cumulative raw agent total tokens", task.raw.cumulativeAgentTotalTokens],
|
|
74
|
+
["Cumulative warm agent total tokens", task.warm.cumulativeAgentTotalTokens],
|
|
51
75
|
]
|
|
52
76
|
.filter(([, metric]) => metric.availability !== "available")
|
|
53
77
|
.map(([label, metric]) => [String(task.taskOrdinal), task.caseId, label, metric.availability, metric.reason ?? ""]));
|
|
@@ -92,11 +116,11 @@ function renderProject(project, index) {
|
|
|
92
116
|
formatMetric(task.warm.cumulativeEstimatedContextTokens),
|
|
93
117
|
]))}
|
|
94
118
|
<p class="muted">Estimated tokens are character-based context-size estimates, not provider token usage.</p>
|
|
95
|
-
<p class="muted"
|
|
119
|
+
<p class="muted">${agentColumnsNote(agentId)}</p>
|
|
96
120
|
${table([
|
|
97
121
|
"Task",
|
|
98
|
-
"Raw
|
|
99
|
-
"Warm
|
|
122
|
+
"Raw agent status",
|
|
123
|
+
"Warm agent status",
|
|
100
124
|
"Raw correctness",
|
|
101
125
|
"Warm correctness",
|
|
102
126
|
"Raw agent tokens",
|
|
@@ -120,11 +144,18 @@ function renderProject(project, index) {
|
|
|
120
144
|
]))}
|
|
121
145
|
${unavailableRows.length === 0 ? "" : table(["Task", "Case", "Metric", "Availability", "Reason"], unavailableRows)}`;
|
|
122
146
|
}
|
|
147
|
+
function agentColumnsNote(agentId) {
|
|
148
|
+
if (agentId === "fake-agent") {
|
|
149
|
+
return "Agent columns are deterministic fake-agent evidence; token totals are simulated harness telemetry, not provider billing telemetry.";
|
|
150
|
+
}
|
|
151
|
+
const label = agentLabel(agentId);
|
|
152
|
+
return `Agent columns are ${label} campaign evidence; token totals are provider-reported adapter telemetry when available and are never replaced by a context-size estimate.`;
|
|
153
|
+
}
|
|
123
154
|
function formatAgent(agent) {
|
|
124
155
|
if (agent === null)
|
|
125
156
|
return "not run";
|
|
126
157
|
const passed = agent.passed === null ? "" : agent.passed ? ", passed" : ", not passed";
|
|
127
|
-
return `${agent.status}${passed}${agent.errors[0] ? ` (${agent.errors[0]})` : ""}`;
|
|
158
|
+
return `${agentLabel(agent.agentId)}: ${agent.status}${passed}${agent.errors[0] ? ` (${agent.errors[0]})` : ""}`;
|
|
128
159
|
}
|
|
129
160
|
function formatMetric(metric) {
|
|
130
161
|
return metric.availability === "available" ? `${metric.value} ${UNIT_LABELS[metric.unit]}` : metric.availability;
|
package/docs/METRICS.md
CHANGED
|
@@ -26,7 +26,7 @@ Each entry below uses the same fields: **Meaning**, **Appears in** (the artifact
|
|
|
26
26
|
**Token savings notes:**
|
|
27
27
|
- A positive token savings value means the my-dev-kit-guided strategy used fewer tokens than raw-full-file for that run pair.
|
|
28
28
|
- A negative token savings value means my-dev-kit-guided used more tokens. This can happen on small projects where raw-full-file is cheaper.
|
|
29
|
-
- Token savings are only computed when both paired runs expose token totals. Claude
|
|
29
|
+
- Token savings are only computed when both paired runs expose token totals. Claude's token totals depend on that run's actual CLI output and may be unavailable; Codex may expose token totals but can produce timeouts or invalid-output runs. See "Warm-index real-agent campaign metrics" below for the explicit per-run `tokenEvidenceStatus` signal used by the `warm-index-reuse` campaign path.
|
|
30
30
|
- Token counts in fake-agent runs are estimated using `Math.ceil(characterCount / 4)`. These are context-size estimates, not provider billing totals.
|
|
31
31
|
|
|
32
32
|
**Correctness scoring notes:**
|
|
@@ -137,6 +137,15 @@ Each entry below uses the same fields: **Meaning**, **Appears in** (the artifact
|
|
|
137
137
|
Interpretation: human-readable size category.
|
|
138
138
|
Caveat: coarse label; use the score and metrics for detail.
|
|
139
139
|
|
|
140
|
+
In the released v0.5.1 benchmark suite, the task statistics (`taskCount`, `expectedRelevantFilesAverage`, `expectedRelevantSymbolsAverage`) of the two warm-index benchmark projects are derived from the dedicated corpus `benchmarks/contracts/warm-index-benchmark-cases.json`: the averages are the mean `expectedFiles` and `expectedSymbols` counts of that project's cases, rounded to two decimals. `npm run verify:benchmarks` fails if a profile disagrees with the corpus. The `benchmark-project-complexity-v1` formula, its weights, and its caps are unchanged; the current values are:
|
|
141
|
+
|
|
142
|
+
| Project | `taskCount` | `expectedRelevantFilesAverage` | `expectedRelevantSymbolsAverage` | `complexityScore` |
|
|
143
|
+
|---|---|---|---|---|
|
|
144
|
+
| `task-workflow-medium-ts` | 6 | 3.83 | 4.33 | 31 |
|
|
145
|
+
| `task-analytics-large-mixed` | 6 | 4.5 | 4.67 | 47 |
|
|
146
|
+
|
|
147
|
+
Task locality (`localized`, `cross-module`, `broad-change`) is benchmark metadata on each corpus case. It is not a metric, score, or ranking, and it does not enter the complexity formula.
|
|
148
|
+
|
|
140
149
|
## Prompt complexity metrics
|
|
141
150
|
|
|
142
151
|
- `promptChars`
|
|
@@ -444,7 +453,9 @@ Implemented in `src/evaluation/stageContextMetrics` and composed once per run by
|
|
|
444
453
|
|
|
445
454
|
## Warm-index reuse metrics
|
|
446
455
|
|
|
447
|
-
|
|
456
|
+
Introduced in v0.5.0. The `warm-index-reuse` plugin calculates these metrics once, in `src/experiments/plugins/warmIndexReuse/metrics.ts`, from bounded execution summaries and bounded fake-agent evidence. The report section (`report.warmIndexReuse` in `report.json`, and the text and HTML "Warm Index Reuse Evidence" sections) and the four warm-index plots render these precomputed values; they never recalculate a formula.
|
|
457
|
+
|
|
458
|
+
The released v0.5.1 expanded benchmark suite adds no warm-index metric and changes no formula: over the dedicated 12-case corpus the same metrics run through task ordinal 6 in each project, and amortization and cumulative sums restart for each project. No token-savings percentage, break-even task, winner, speedup, ranking, or provider billing metric is calculated.
|
|
448
459
|
|
|
449
460
|
**Availability contract.** Every warm-index metric carries `availability`, `value`, `unit`, `source`, `reason`, and `tokenCountMethod`:
|
|
450
461
|
|
|
@@ -504,4 +515,23 @@ The plugin evaluates each task side that has context evidence once with the dete
|
|
|
504
515
|
|
|
505
516
|
**Generic outcome metrics.** Available values also appear as `ExperimentMetric` entries on each outcome, with `variantId`, `caseId`, `unit`, and `description`. Both the `raw-full-file` and `warm-index-reuse` outcomes can carry `context-character-count`, `context-estimated-token-count`, `operation-duration-ms` (raw context construction or warm retrieval), `cumulative-component-duration-ms`, `cumulative-context-estimated-token-count`, `agent-correctness-score`, `agent-total-tokens`, and `cumulative-agent-total-tokens`. The warm outcome also carries `amortized-index-build-duration-ms`. Unavailable metrics are omitted rather than emitted as zero. Run-level metrics are limited to `warm-index-project-count`, `warm-index-task-count`, and `warm-index-session-prepared-project-count`.
|
|
506
517
|
|
|
507
|
-
**Interpretation.** The report separates the one-time index cost from per-task retrieval cost and shows the fixed cost falling per task as more tasks reuse the index. It calculates no token-savings percentage, duration-reduction percentage, break-even task, composite score, winner, or ranking. Warm-index plots map the same precomputed values: amortized index build duration, raw versus retrieved estimated context tokens, fake-agent correctness, and cumulative fake-agent total tokens. Unavailable values become skipped plot points with their reason.
|
|
518
|
+
**Interpretation.** The report separates the one-time index cost from per-task retrieval cost and shows the fixed cost falling per task as more tasks reuse the index. It calculates no token-savings percentage, duration-reduction percentage, break-even task, composite score, winner, or ranking. Warm-index plots map the same precomputed values: amortized index build duration, raw versus retrieved estimated context tokens, fake-agent correctness (or campaign agent correctness for a `--campaign` run), and cumulative fake-agent total tokens. Unavailable values become skipped plot points with their reason.
|
|
519
|
+
|
|
520
|
+
### Warm-index real-agent campaign metrics (v0.5.2, released)
|
|
521
|
+
|
|
522
|
+
Available in the installed v0.5.2 CLI. A `--campaign` run replaces the deterministic fake-agent evaluation described above with one real Codex or Claude provider evaluation per task side with context evidence; every direct measurement, derived measurement, and the strict-prefix rule above are unchanged and reused as-is. Only the per-side agent evidence and its rollup are additive.
|
|
523
|
+
|
|
524
|
+
- `presetId`, `agentId`, `timeoutMs`, `selectedCaseCount`
|
|
525
|
+
Meaning: which campaign preset ran, which single provider (`codex` or `claude`) it used, its resolved timeout, and how many cases were selected.
|
|
526
|
+
- `scheduledSideCount`, `executedSideCount`, `notRunForMissingContextCount`
|
|
527
|
+
Meaning: how many task sides were scheduled for real-agent evaluation, how many actually ran, and how many were not run because that side had no context evidence (for example, a warm side with no prepared index).
|
|
528
|
+
- `outcomeCounts` (`completed`, `failed`, `timeout`, `invalidOutput`, `agentUnavailable`, `agentLimitReached`, `skipped`)
|
|
529
|
+
Meaning: exact counts of each real-agent outcome across executed sides, from `classifyAgentRunOutcome`'s status mapping. None of these are folded into a single pass/fail count.
|
|
530
|
+
- `agentEvidenceStatus` (`complete`, `partial`, `unavailable`)
|
|
531
|
+
Meaning: `complete` when every scheduled side executed and every executed side reported `completed`; `unavailable` when no side executed; `partial` otherwise.
|
|
532
|
+
Interpretation: this is infrastructure/provider evidence completeness, not correctness. A campaign can report `agentEvidenceStatus: "partial"` while the underlying index/execution infrastructure completed normally — infrastructure status and provider status are reported separately, never conflated.
|
|
533
|
+
- `tokenEvidenceStatus` (`complete`, `partial`, `unavailable`)
|
|
534
|
+
Meaning: `complete` when every executed side reports a finite total-token count; `unavailable` when no executed side does; `partial` otherwise.
|
|
535
|
+
Caveat: Claude and Codex token exposure depends on that provider's actual CLI output for the run; token evidence is reported as `unavailable`/`partial` explicitly rather than assumed. No fixed claim that either provider always or never exposes a token total is made — `tokenEvidenceStatus` is the authoritative, per-run signal.
|
|
536
|
+
|
|
537
|
+
No token-savings percentage, break-even task, winner, ranking, or provider billing metric is calculated for a campaign run, matching the fake-agent path above.
|