@dailephd/my-dev-kit-lab 0.5.1 → 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 +14 -1
- package/README.md +22 -6
- 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/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 +21 -2
- package/package.json +1 -1
|
@@ -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:**
|
|
@@ -515,4 +515,23 @@ The plugin evaluates each task side that has context evidence once with the dete
|
|
|
515
515
|
|
|
516
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`.
|
|
517
517
|
|
|
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, 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.
|