@dailephd/my-dev-kit-lab 0.8.2 → 0.9.0
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 -0
- package/README.md +18 -3
- package/benchmarks/contracts/agent-success-rate-project-profiles.json +33 -0
- package/benchmarks/contracts/agent-success-rate-tasks.json +671 -0
- package/benchmarks/projects/agent-success-inventory-node/.gitattributes +2 -0
- package/benchmarks/projects/agent-success-inventory-node/README.md +74 -0
- package/benchmarks/projects/agent-success-inventory-node/package.json +14 -0
- package/benchmarks/projects/agent-success-inventory-node/src/fulfillmentReport.js +38 -0
- package/benchmarks/projects/agent-success-inventory-node/src/index.js +26 -0
- package/benchmarks/projects/agent-success-inventory-node/src/inventoryStore.js +114 -0
- package/benchmarks/projects/agent-success-inventory-node/src/quantity.js +19 -0
- package/benchmarks/projects/agent-success-inventory-node/src/reservationService.js +45 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/quantity-edge.check.mjs +51 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/quantity-primary.check.mjs +46 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/regression.check.mjs +115 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/report-edge.check.mjs +109 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/report-primary.check.mjs +47 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/reservation-edge.check.mjs +63 -0
- package/benchmarks/projects/agent-success-inventory-node/tests/reservation-primary.check.mjs +44 -0
- package/benchmarks/projects/agent-success-task-board-node/.gitattributes +2 -0
- package/benchmarks/projects/agent-success-task-board-node/README.md +77 -0
- package/benchmarks/projects/agent-success-task-board-node/package.json +14 -0
- package/benchmarks/projects/agent-success-task-board-node/src/index.js +24 -0
- package/benchmarks/projects/agent-success-task-board-node/src/projectSummary.js +15 -0
- package/benchmarks/projects/agent-success-task-board-node/src/taskService.js +58 -0
- package/benchmarks/projects/agent-success-task-board-node/src/taskStore.js +58 -0
- package/benchmarks/projects/agent-success-task-board-node/src/validation.js +31 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/import-edge.check.mjs +65 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/import-primary.check.mjs +31 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/regression.check.mjs +107 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/summary-edge.check.mjs +67 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/summary-primary.check.mjs +54 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/title-edge.check.mjs +39 -0
- package/benchmarks/projects/agent-success-task-board-node/tests/title-primary.check.mjs +29 -0
- package/dist/src/cli/help.js +38 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +52 -1
- package/dist/src/commands/runExperimentRunCommand.js +118 -2
- package/dist/src/core/processTree.js +55 -0
- package/dist/src/core/runMeasuredCommand.js +104 -22
- package/dist/src/evaluation/agentSuccess/applyPatchToSandbox.js +37 -0
- package/dist/src/evaluation/agentSuccess/assessBaseline.js +23 -0
- package/dist/src/evaluation/agentSuccess/corpusTypes.js +74 -0
- package/dist/src/evaluation/agentSuccess/extractPatchCandidate.js +46 -0
- package/dist/src/evaluation/agentSuccess/index.js +14 -0
- package/dist/src/evaluation/agentSuccess/parseUnifiedDiff.js +210 -0
- package/dist/src/evaluation/agentSuccess/patchTypes.js +5 -0
- package/dist/src/evaluation/agentSuccess/readAgentSuccessCorpus.js +183 -0
- package/dist/src/evaluation/agentSuccess/runVerificationCheck.js +100 -0
- package/dist/src/evaluation/agentSuccess/taskPaths.js +34 -0
- package/dist/src/evaluation/agentSuccess/taskTypes.js +14 -0
- package/dist/src/evaluation/agentSuccess/validateAgentSuccessCorpus.js +307 -0
- package/dist/src/evaluation/agentSuccess/validateAgentSuccessTask.js +297 -0
- package/dist/src/evaluation/agentSuccess/validatePatchPolicy.js +66 -0
- package/dist/src/evaluation/agentSuccess/verificationTypes.js +1 -0
- package/dist/src/evaluation/benchmarkSandbox/createBenchmarkSandbox.js +127 -0
- package/dist/src/evaluation/benchmarkSandbox/errors.js +11 -0
- package/dist/src/evaluation/benchmarkSandbox/gitExecutor.js +60 -0
- package/dist/src/evaluation/benchmarkSandbox/index.js +8 -0
- package/dist/src/evaluation/benchmarkSandbox/minimalHostEnv.js +37 -0
- package/dist/src/evaluation/benchmarkSandbox/pathPolicy.js +49 -0
- package/dist/src/evaluation/benchmarkSandbox/removeBenchmarkSandbox.js +74 -0
- package/dist/src/evaluation/benchmarkSandbox/treeSnapshot.js +57 -0
- package/dist/src/evaluation/benchmarkSandbox/types.js +3 -0
- package/dist/src/evaluation/changeSet/captureChangeSet.js +98 -0
- package/dist/src/evaluation/changeSet/index.js +2 -0
- package/dist/src/evaluation/changeSet/types.js +9 -0
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/agentSuccessRate/agentTaskProjection.js +12 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysis.js +425 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysisArtifact.js +155 -0
- package/dist/src/experiments/plugins/agentSuccessRate/analysisTypes.js +65 -0
- package/dist/src/experiments/plugins/agentSuccessRate/attemptEvidence.js +7 -0
- package/dist/src/experiments/plugins/agentSuccessRate/config.js +123 -0
- package/dist/src/experiments/plugins/agentSuccessRate/contextGeneration.js +497 -0
- package/dist/src/experiments/plugins/agentSuccessRate/execution.js +441 -0
- package/dist/src/experiments/plugins/agentSuccessRate/executionArtifact.js +126 -0
- package/dist/src/experiments/plugins/agentSuccessRate/executionTypes.js +2 -0
- package/dist/src/experiments/plugins/agentSuccessRate/index.js +20 -0
- package/dist/src/experiments/plugins/agentSuccessRate/metadata.js +28 -0
- package/dist/src/experiments/plugins/agentSuccessRate/metrics.js +76 -0
- package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +507 -0
- package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +250 -0
- package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +43 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairAnalysis.js +148 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairExecution.js +103 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairFeedback.js +91 -0
- package/dist/src/experiments/plugins/agentSuccessRate/repairPolicy.js +88 -0
- package/dist/src/experiments/plugins/agentSuccessRate/types.js +1 -0
- package/dist/src/experiments/plugins/warmIndexReuse/plugin.js +2 -3
- package/dist/src/report/experiments/agentSuccessRateReportModel.js +6 -0
- package/dist/src/report/experiments/buildAgentSuccessRateReport.js +255 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +27 -2
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderAgentSuccessRateHtml.js +169 -0
- package/dist/src/report/experiments/renderAgentSuccessRateText.js +139 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +5 -0
- package/docs/METRICS.md +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure repair policy: decides how one evaluated attempt ended and whether a repair may follow. It performs no
|
|
3
|
+
* filesystem, subprocess or provider work and reads only normalized execution evidence plus already-calculated metrics.
|
|
4
|
+
*/
|
|
5
|
+
/** Categories for which a completed provider attempt produced an evaluable implementation failure. */
|
|
6
|
+
export const AGENT_SUCCESS_REPAIR_ELIGIBLE_CATEGORIES = [
|
|
7
|
+
"patch-malformed",
|
|
8
|
+
"patch-policy-rejected",
|
|
9
|
+
"patch-check-failed",
|
|
10
|
+
"patch-apply-failed",
|
|
11
|
+
"task-check-failed",
|
|
12
|
+
"regression-failed",
|
|
13
|
+
"required-behavior-unsatisfied"
|
|
14
|
+
];
|
|
15
|
+
function booleanValue(metric) {
|
|
16
|
+
return metric !== undefined && metric.availability === "available" && typeof metric.value === "boolean" ? metric.value : null;
|
|
17
|
+
}
|
|
18
|
+
/** Classifies an attempt. Infrastructure and provider-health outcomes are never eligible, regardless of task success. */
|
|
19
|
+
export function classifyAgentSuccessAttempt(evidence, metrics) {
|
|
20
|
+
const taskSuccess = booleanValue(metrics.taskSuccess);
|
|
21
|
+
const result = (category) => ({
|
|
22
|
+
category,
|
|
23
|
+
repairEligible: AGENT_SUCCESS_REPAIR_ELIGIBLE_CATEGORIES.includes(category),
|
|
24
|
+
taskSuccess
|
|
25
|
+
});
|
|
26
|
+
const real = evidence.realAgent;
|
|
27
|
+
if (evidence.availability === "infrastructure-failure")
|
|
28
|
+
return result("infrastructure-failure");
|
|
29
|
+
if (evidence.availability === "baseline-invalid" || evidence.baselineAssessment === null || !evidence.baselineAssessment.evaluable)
|
|
30
|
+
return result("baseline-invalid");
|
|
31
|
+
if (real === undefined)
|
|
32
|
+
return result("evidence-incomplete");
|
|
33
|
+
if (real.context.availability === "unavailable" && !real.providerInvoked)
|
|
34
|
+
return result("context-unavailable");
|
|
35
|
+
if (!real.providerInvoked)
|
|
36
|
+
return result("provider-not-invoked");
|
|
37
|
+
switch (real.providerStatus) {
|
|
38
|
+
case "agent-unavailable":
|
|
39
|
+
return result("provider-unavailable");
|
|
40
|
+
case "agent-limit-reached":
|
|
41
|
+
return result("provider-limit-reached");
|
|
42
|
+
case "timeout":
|
|
43
|
+
return result("provider-timeout");
|
|
44
|
+
case "completed":
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
return result("provider-failed");
|
|
48
|
+
}
|
|
49
|
+
if (!real.finalAnswerAvailable || !evidence.patch.attempted)
|
|
50
|
+
return result("provider-empty-answer");
|
|
51
|
+
if (!evidence.cleanup.removed || (real.cwdCleanup.attempted && !real.cwdCleanup.removed))
|
|
52
|
+
return result("cleanup-failed");
|
|
53
|
+
if (evidence.protectedIntegrity.status === "unproven")
|
|
54
|
+
return result("integrity-unproven");
|
|
55
|
+
if (evidence.availability !== "complete")
|
|
56
|
+
return result("evidence-incomplete");
|
|
57
|
+
switch (evidence.patch.outcome) {
|
|
58
|
+
case "parse-failure":
|
|
59
|
+
return result("patch-malformed");
|
|
60
|
+
case "policy-rejection":
|
|
61
|
+
return result("patch-policy-rejected");
|
|
62
|
+
case "git-check-failure":
|
|
63
|
+
return result("patch-check-failed");
|
|
64
|
+
case "git-apply-failure":
|
|
65
|
+
return result("patch-apply-failed");
|
|
66
|
+
case "success":
|
|
67
|
+
break;
|
|
68
|
+
default:
|
|
69
|
+
return result("evidence-incomplete");
|
|
70
|
+
}
|
|
71
|
+
if (evidence.protectedIntegrity.status === "mutated")
|
|
72
|
+
return result("integrity-mutated");
|
|
73
|
+
if (taskSuccess === true)
|
|
74
|
+
return result("none");
|
|
75
|
+
if (taskSuccess === null)
|
|
76
|
+
return result("evidence-incomplete");
|
|
77
|
+
if (booleanValue(metrics.taskChecksPassed) === false)
|
|
78
|
+
return result("task-check-failed");
|
|
79
|
+
if (booleanValue(metrics.regressionSafe) === false)
|
|
80
|
+
return result("regression-failed");
|
|
81
|
+
if (booleanValue(metrics.requiredFactsSatisfied) === false)
|
|
82
|
+
return result("required-behavior-unsatisfied");
|
|
83
|
+
return result("task-check-failed");
|
|
84
|
+
}
|
|
85
|
+
/** A repair runs only after an eligible failure, and only while the allowance and the hard total bound both remain. */
|
|
86
|
+
export function shouldRepairAgentSuccessAttempt(args) {
|
|
87
|
+
return args.classification.repairEligible && args.classification.taskSuccess === false && args.attemptsExecuted <= args.repairAttemptsAllowed && args.attemptsExecuted < 3;
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -42,9 +42,8 @@ export const warmIndexReusePlugin = {
|
|
|
42
42
|
const startedAt = context.startedAt.toISOString();
|
|
43
43
|
// A campaign preset selects campaign-aware case selection and real-agent evaluation for
|
|
44
44
|
// exactly one provider; its absence preserves the legacy fake-agent path unchanged. Real-agent
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// implements campaign-accurate reporting.
|
|
45
|
+
// execution stays explicitly gated: the config requires includeRealAgents for a preset, and the
|
|
46
|
+
// public experiment-run command rejects incompatible options before this path is reached.
|
|
48
47
|
const preset = context.config.campaignPreset ? getWarmIndexCampaignPreset(context.config.campaignPreset) : undefined;
|
|
49
48
|
const cases = preset
|
|
50
49
|
? selectWarmIndexCampaignCases(readCasesInput(context.inputs), preset, context.config)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed agent-success-rate report section. Every value here is copied from the plugin's already-calculated analysis and
|
|
3
|
+
* execution evidence: the report layer rescored nothing, reran no check, reparsed no patch and read no reference patch.
|
|
4
|
+
* It holds no prompt, source-file or patch body and no raw command output.
|
|
5
|
+
*/
|
|
6
|
+
export const AGENT_SUCCESS_RATE_REPORT_SCHEMA_VERSION = "my-dev-kit-lab-agent-success-rate-report-v1";
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { attemptEvidenceOf } from "../../experiments/plugins/agentSuccessRate/attemptEvidence.js";
|
|
2
|
+
import { AGENT_SUCCESS_RATE_PLUGIN_ID, AGENT_SUCCESS_RATE_REAL_AGENT_EXECUTION_MODE, AGENT_SUCCESS_RATE_TREATMENT_IDS } from "../../experiments/plugins/agentSuccessRate/metadata.js";
|
|
3
|
+
import { AGENT_SUCCESS_RATE_REPORT_SCHEMA_VERSION } from "./agentSuccessRateReportModel.js";
|
|
4
|
+
export const AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT = "The fixture validates the patch evaluation pipeline and benchmark corpus. It does not measure a coding agent or the effect of context selection.";
|
|
5
|
+
export const AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT = "The observed outcomes are a descriptive matched comparison of two source-context treatments under the selected provider.";
|
|
6
|
+
const MAX_TEXT = 300;
|
|
7
|
+
const MAX_CHANGED_FILES = 20;
|
|
8
|
+
const bound = (text) => {
|
|
9
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
10
|
+
return normalized.length > MAX_TEXT ? `${normalized.slice(0, MAX_TEXT)}...` : normalized;
|
|
11
|
+
};
|
|
12
|
+
/** Copies a plugin metric. Availability, value and reason are carried through untouched. */
|
|
13
|
+
function copyMetric(metric, fallbackUnit = "count") {
|
|
14
|
+
if (!metric)
|
|
15
|
+
return { availability: "unavailable", value: null, unit: fallbackUnit, reason: "the measurement was not recorded." };
|
|
16
|
+
return { availability: metric.availability, value: metric.value, unit: metric.unit, reason: metric.reason === null ? null : bound(metric.reason) };
|
|
17
|
+
}
|
|
18
|
+
function copyTotal(total) {
|
|
19
|
+
return {
|
|
20
|
+
basis: total.basis,
|
|
21
|
+
contributingCaseCount: total.contributingCaseCount,
|
|
22
|
+
availableCaseCount: total.availableCaseCount,
|
|
23
|
+
unavailableCaseCount: total.unavailableCaseCount,
|
|
24
|
+
sumOfAvailable: total.sumOfAvailable,
|
|
25
|
+
total: copyMetric(total.total)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function copyDifference(entry) {
|
|
29
|
+
return {
|
|
30
|
+
id: entry.id,
|
|
31
|
+
basis: entry.basis,
|
|
32
|
+
matchedCaseIds: [...entry.matchedCaseIds],
|
|
33
|
+
rawFullFileMean: copyMetric(entry.rawFullFileMean),
|
|
34
|
+
contextPackMean: copyMetric(entry.contextPackMean),
|
|
35
|
+
meanDifference: copyMetric(entry.meanDifference)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A relative, forward-slash artifact reference with no traversal, drive, scheme or absolute form. Anything else is
|
|
40
|
+
* withheld rather than rendered, so a report can never point outside the experiment output directory.
|
|
41
|
+
*/
|
|
42
|
+
export function safeAgentSuccessArtifactReference(reference) {
|
|
43
|
+
if (typeof reference !== "string" || reference.length === 0 || reference.length > 400)
|
|
44
|
+
return null;
|
|
45
|
+
if (reference.includes("\\") || reference.includes("\0") || reference.startsWith("/") || /^[A-Za-z]:/.test(reference) || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(reference))
|
|
46
|
+
return null;
|
|
47
|
+
const segments = reference.split("/");
|
|
48
|
+
if (segments.some((segment) => segment === "" || segment === "." || segment === ".."))
|
|
49
|
+
return null;
|
|
50
|
+
return reference;
|
|
51
|
+
}
|
|
52
|
+
function patchOutcome(evidence) {
|
|
53
|
+
return evidence.patch.outcome ?? (evidence.patch.attempted ? "unrecorded" : "not-attempted");
|
|
54
|
+
}
|
|
55
|
+
function verificationAvailability(evidence) {
|
|
56
|
+
return evidence.postEditVerification !== null ? "available" : "unavailable";
|
|
57
|
+
}
|
|
58
|
+
export function buildAgentSuccessRateReport(run) {
|
|
59
|
+
if (run.pluginId !== AGENT_SUCCESS_RATE_PLUGIN_ID)
|
|
60
|
+
return null;
|
|
61
|
+
const candidate = run;
|
|
62
|
+
const analysis = candidate.analysis;
|
|
63
|
+
const execution = candidate.caseExecutionEvidence;
|
|
64
|
+
if (!analysis || !execution) {
|
|
65
|
+
// A run that failed before analysis exists still gets the generic failure report.
|
|
66
|
+
if (run.status === "failed")
|
|
67
|
+
return null;
|
|
68
|
+
throw new Error("Invalid agent-success-rate report source: execution evidence and analysis are required.");
|
|
69
|
+
}
|
|
70
|
+
const inconsistent = analysis.cases.length !== execution.length ||
|
|
71
|
+
analysis.cases.some((entry, index) => entry.caseId !== execution[index].caseId ||
|
|
72
|
+
entry.treatments.length !== AGENT_SUCCESS_RATE_TREATMENT_IDS.length ||
|
|
73
|
+
entry.treatments.some((treatment, position) => treatment.treatmentId !== AGENT_SUCCESS_RATE_TREATMENT_IDS[position]));
|
|
74
|
+
if (inconsistent) {
|
|
75
|
+
throw new Error("Invalid agent-success-rate report source: execution evidence and analysis are inconsistent.");
|
|
76
|
+
}
|
|
77
|
+
const realMode = analysis.executionMode === AGENT_SUCCESS_RATE_REAL_AGENT_EXECUTION_MODE;
|
|
78
|
+
const metadata = (run.metadata ?? {});
|
|
79
|
+
const repairAttempts = realMode && typeof metadata.repairAttempts === "number" ? metadata.repairAttempts : realMode ? 0 : null;
|
|
80
|
+
const timeoutMs = realMode && typeof metadata.timeoutMs === "number" ? metadata.timeoutMs : null;
|
|
81
|
+
const cases = analysis.cases.map((entry, caseIndex) => {
|
|
82
|
+
const evidence = execution[caseIndex];
|
|
83
|
+
return {
|
|
84
|
+
caseId: entry.caseId,
|
|
85
|
+
caseName: bound(evidence.caseName),
|
|
86
|
+
benchmarkProject: entry.benchmarkProject,
|
|
87
|
+
taskLocality: evidence.taskLocality,
|
|
88
|
+
treatments: entry.treatments.map((treatment, treatmentIndex) => {
|
|
89
|
+
const treatmentEvidence = evidence.treatments[treatmentIndex];
|
|
90
|
+
const repair = treatment.repair;
|
|
91
|
+
const changed = treatmentEvidence.change?.changedFiles.map((file) => file.relativePath).filter((file) => safeAgentSuccessArtifactReference(file) !== null) ?? null;
|
|
92
|
+
const errors = attemptEvidenceOf(treatmentEvidence).flatMap((attempt, index) => attempt.errors.slice(0, 10).map((error) => ({ attemptNumber: index + 1, code: bound(error.code), message: bound(error.message) })));
|
|
93
|
+
return {
|
|
94
|
+
treatmentId: treatment.treatmentId,
|
|
95
|
+
executionStatus: treatment.executionStatus,
|
|
96
|
+
evidenceAvailability: treatment.evidenceAvailability,
|
|
97
|
+
initialTaskSuccess: copyMetric(repair ? repair.initialAttemptTaskSuccess : treatment.metrics.taskSuccess, "boolean"),
|
|
98
|
+
finalTaskSuccess: copyMetric(treatment.metrics.taskSuccess, "boolean"),
|
|
99
|
+
attemptCount: repair ? repair.attemptCount : 1,
|
|
100
|
+
repairAttemptCount: repair ? repair.repairAttemptCount : 0,
|
|
101
|
+
repairSucceeded: repair
|
|
102
|
+
? copyMetric(repair.repairSucceeded, "boolean")
|
|
103
|
+
: { availability: "not-applicable", value: null, unit: "boolean", reason: "deterministic-fixture mode has no repair." },
|
|
104
|
+
taskCheckPassRate: copyMetric(treatment.metrics.taskCheckPassRate, "ratio"),
|
|
105
|
+
regressionCheckPassRate: copyMetric(treatment.metrics.regressionCheckPassRate, "ratio"),
|
|
106
|
+
requiredFactCoverage: copyMetric(treatment.metrics.requiredFactCoverage, "ratio"),
|
|
107
|
+
expectedEditCoverage: copyMetric(treatment.metrics.expectedEditCoverage, "ratio"),
|
|
108
|
+
editScopePrecision: copyMetric(treatment.metrics.editScopePrecision, "ratio"),
|
|
109
|
+
unexpectedChangedFiles: copyMetric(treatment.metrics.unexpectedChangedFileCount, "count"),
|
|
110
|
+
protectedMutations: copyMetric(treatment.metrics.protectedMutationCount, "count"),
|
|
111
|
+
changedFileCount: copyMetric(treatment.metrics.changedFileCount, "count"),
|
|
112
|
+
changedFiles: changed === null || treatment.metrics.changedFileCount.availability !== "available"
|
|
113
|
+
? null
|
|
114
|
+
: { items: changed.slice(0, MAX_CHANGED_FILES), totalCount: changed.length, omittedCount: Math.max(0, changed.length - MAX_CHANGED_FILES) },
|
|
115
|
+
totalChurn: copyMetric(treatment.metrics.totalChurn, "lines"),
|
|
116
|
+
relativeChurn: copyMetric(treatment.metrics.relativeChurn, "ratio"),
|
|
117
|
+
finalAttemptProviderDurationMs: copyMetric(treatment.metrics.agentDurationMs, "ms"),
|
|
118
|
+
finalAttemptProviderTokens: copyMetric(treatment.metrics.agentTotalTokens, "tokens"),
|
|
119
|
+
totalProviderDurationMs: repair ? copyMetric(repair.totalProviderDurationMs, "ms") : null,
|
|
120
|
+
totalProviderTokens: repair ? copyMetric(repair.totalProviderTokens, "tokens") : null,
|
|
121
|
+
errors: errors.slice(0, 20)
|
|
122
|
+
};
|
|
123
|
+
})
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
const treatments = analysis.aggregates.map((aggregate) => {
|
|
127
|
+
const repair = aggregate.repair;
|
|
128
|
+
return {
|
|
129
|
+
treatmentId: aggregate.treatmentId,
|
|
130
|
+
evaluableCases: repair ? repair.finalEvaluableCount : aggregate.evaluableCaseCount,
|
|
131
|
+
initialSuccessfulCases: repair ? repair.initialAttemptSuccessfulCount : aggregate.successfulCaseCount,
|
|
132
|
+
initialSuccessRate: copyMetric(aggregate.initialAttemptSuccessRate, "ratio"),
|
|
133
|
+
finalSuccessfulCases: repair ? repair.finalSuccessfulCount : aggregate.successfulCaseCount,
|
|
134
|
+
finalSuccessRate: copyMetric(repair ? repair.finalTaskSuccessRate : aggregate.taskSuccessRate, "ratio"),
|
|
135
|
+
repair: repair
|
|
136
|
+
? {
|
|
137
|
+
repairEligibleCases: repair.repairEligibleCaseCount,
|
|
138
|
+
repairAttemptedCases: repair.repairAttemptedCaseCount,
|
|
139
|
+
repairAttempts: repair.totalRepairAttemptCount,
|
|
140
|
+
repairedCases: repair.repairedCaseCount,
|
|
141
|
+
repairSuccessRate: copyMetric(repair.repairSuccessRate, "ratio"),
|
|
142
|
+
meanAttemptsPerEvaluableCase: copyMetric(repair.meanAttemptsPerEvaluableCase, "count"),
|
|
143
|
+
providerDurationMs: repair.providerDurationMs.map(copyTotal),
|
|
144
|
+
providerTokens: repair.providerTokens.map(copyTotal)
|
|
145
|
+
}
|
|
146
|
+
: null
|
|
147
|
+
};
|
|
148
|
+
});
|
|
149
|
+
const repairHistory = realMode
|
|
150
|
+
? analysis.cases.flatMap((entry, caseIndex) => entry.treatments.flatMap((treatment, treatmentIndex) => {
|
|
151
|
+
const repair = treatment.repair;
|
|
152
|
+
const evidence = execution[caseIndex].treatments[treatmentIndex];
|
|
153
|
+
if (!repair || !evidence.attempts)
|
|
154
|
+
return [];
|
|
155
|
+
return [
|
|
156
|
+
{
|
|
157
|
+
caseId: entry.caseId,
|
|
158
|
+
treatmentId: treatment.treatmentId,
|
|
159
|
+
attempts: repair.attempts.map((attempt, index) => {
|
|
160
|
+
const attemptEvidence = evidence.attempts[index];
|
|
161
|
+
const inner = attemptEvidence.evidence;
|
|
162
|
+
return {
|
|
163
|
+
attemptNumber: attempt.attemptNumber,
|
|
164
|
+
providerStatus: attempt.providerStatus,
|
|
165
|
+
patchOutcome: patchOutcome(inner),
|
|
166
|
+
verificationAvailability: verificationAvailability(inner),
|
|
167
|
+
taskSuccess: copyMetric(attempt.metrics.taskSuccess, "boolean"),
|
|
168
|
+
failureCategory: attempt.failureCategory,
|
|
169
|
+
repairEligible: attempt.repairEligible,
|
|
170
|
+
providerDurationMs: copyMetric(attempt.metrics.agentDurationMs, "ms"),
|
|
171
|
+
providerTokens: copyMetric(attempt.metrics.agentTotalTokens, "tokens"),
|
|
172
|
+
tokenAvailability: attempt.metrics.agentTotalTokens.availability === "available" ? "available" : "unavailable",
|
|
173
|
+
proposedPatchPath: safeAgentSuccessArtifactReference(inner.proposedPatchPath),
|
|
174
|
+
appliedPatchPath: safeAgentSuccessArtifactReference(inner.appliedPatchPath),
|
|
175
|
+
agentArtifactDirectory: safeAgentSuccessArtifactReference(inner.realAgent?.agentArtifactDirectory ?? null)
|
|
176
|
+
};
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
];
|
|
180
|
+
}))
|
|
181
|
+
: [];
|
|
182
|
+
const outcomeAvailability = analysis.cases.flatMap((entry) => entry.treatments.map((treatment) => treatment.metrics.taskSuccess.availability));
|
|
183
|
+
const evaluable = outcomeAvailability.filter((availability) => availability === "available").length;
|
|
184
|
+
const total = outcomeAvailability.length;
|
|
185
|
+
const unavailable = total - evaluable;
|
|
186
|
+
const notes = [];
|
|
187
|
+
if (unavailable > 0)
|
|
188
|
+
notes.push(`${unavailable} of ${total} treatment outcome(s) have no determinate task-success verdict; they are excluded from rates and never counted as failures.`);
|
|
189
|
+
if (realMode && analysis.comparison && analysis.comparison.incompleteCases.length > 0) {
|
|
190
|
+
notes.push(`${analysis.comparison.incompleteCases.length} case(s) are excluded from matched comparisons because a treatment verdict is unavailable.`);
|
|
191
|
+
}
|
|
192
|
+
const comparison = analysis.comparison
|
|
193
|
+
? (() => {
|
|
194
|
+
const repairComparison = analysis.repairComparison;
|
|
195
|
+
const finalMatched = analysis.comparison.matchedCaseIds.length;
|
|
196
|
+
const evaluableComparison = repairComparison ? repairComparison.interpretation.evaluable : finalMatched > 0;
|
|
197
|
+
const finalPairs = analysis.comparison.pairedOutcomes;
|
|
198
|
+
const differs = finalPairs.onlyRawFullFileSucceeded + finalPairs.onlyContextPackSucceeded > 0;
|
|
199
|
+
return {
|
|
200
|
+
comparisonEvaluable: evaluableComparison,
|
|
201
|
+
observedOutcomeDifference: repairComparison ? repairComparison.interpretation.observedOutcomeDifference : !evaluableComparison ? "not-evaluable" : differs ? "observed" : "not-observed",
|
|
202
|
+
statisticalEffect: "not-assessed",
|
|
203
|
+
finalMatchedCaseIds: [...analysis.comparison.matchedCaseIds],
|
|
204
|
+
finalIncompleteCases: analysis.comparison.incompleteCases.map((entry) => ({ caseId: entry.caseId, unavailableTreatmentIds: [...entry.unavailableTreatmentIds] })),
|
|
205
|
+
finalPairedOutcomes: { ...finalPairs },
|
|
206
|
+
initialMatchedCaseIds: repairComparison ? [...repairComparison.initialAttempt.matchedCaseIds] : null,
|
|
207
|
+
initialIncompleteCases: repairComparison ? repairComparison.initialAttempt.incompleteCases.map((entry) => ({ caseId: entry.caseId, unavailableTreatmentIds: [...entry.unavailableTreatmentIds] })) : null,
|
|
208
|
+
initialPairedOutcomes: repairComparison ? { ...repairComparison.initialAttempt.pairedOutcomes } : null,
|
|
209
|
+
pairedDifferences: repairComparison ? repairComparison.pairedDifferences.map(copyDifference) : []
|
|
210
|
+
};
|
|
211
|
+
})()
|
|
212
|
+
: null;
|
|
213
|
+
const overall = total === 0 || evaluable === 0 ? "unavailable" : unavailable === 0 ? "complete" : "partial";
|
|
214
|
+
return {
|
|
215
|
+
schemaVersion: AGENT_SUCCESS_RATE_REPORT_SCHEMA_VERSION,
|
|
216
|
+
identity: {
|
|
217
|
+
runId: run.runId,
|
|
218
|
+
pluginId: run.pluginId,
|
|
219
|
+
pluginSchemaVersion: "1.0.0",
|
|
220
|
+
runStatus: run.status,
|
|
221
|
+
executionMode: analysis.executionMode,
|
|
222
|
+
providerId: typeof metadata.providerId === "string" ? metadata.providerId : null,
|
|
223
|
+
timeoutMs,
|
|
224
|
+
repairAttempts,
|
|
225
|
+
maxAttemptsPerTreatment: repairAttempts === null ? 1 : 1 + repairAttempts,
|
|
226
|
+
treatmentOrder: [...analysis.treatmentOrder],
|
|
227
|
+
caseCount: analysis.caseCount,
|
|
228
|
+
treatmentOutcomeCount: total
|
|
229
|
+
},
|
|
230
|
+
scientificStatement: realMode ? AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT : AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT,
|
|
231
|
+
limitations: [
|
|
232
|
+
...analysis.limitations.map(bound),
|
|
233
|
+
...(realMode
|
|
234
|
+
? [
|
|
235
|
+
"The corpus is finite and a single run supports no generalization.",
|
|
236
|
+
"Provider output can vary between runs, and provider usage or availability can differ between treatments.",
|
|
237
|
+
"No causal or statistical-significance claim is made; no weighted composite score and no automatic best treatment exist."
|
|
238
|
+
]
|
|
239
|
+
: [])
|
|
240
|
+
],
|
|
241
|
+
resultAvailability: { overall, evaluableTreatmentOutcomes: evaluable, unavailableTreatmentOutcomes: unavailable, totalTreatmentOutcomes: total, notes },
|
|
242
|
+
treatments,
|
|
243
|
+
comparison,
|
|
244
|
+
cases,
|
|
245
|
+
repairHistory,
|
|
246
|
+
warnings: run.warnings.map((warning) => ({ code: bound(warning.code), message: bound(warning.message) })),
|
|
247
|
+
artifacts: run.artifacts.map((artifact) => ({
|
|
248
|
+
id: bound(artifact.id),
|
|
249
|
+
label: bound(artifact.label),
|
|
250
|
+
path: safeAgentSuccessArtifactReference(artifact.path),
|
|
251
|
+
caseId: artifact.caseId ?? null,
|
|
252
|
+
variantId: artifact.variantId ?? null
|
|
253
|
+
}))
|
|
254
|
+
};
|
|
255
|
+
}
|
|
@@ -6,6 +6,7 @@ import { buildContextWindowScalingReport } from "./buildContextWindowScalingRepo
|
|
|
6
6
|
import { buildRetrievalPrecisionRecallReport } from "./buildRetrievalPrecisionRecallReport.js";
|
|
7
7
|
import { buildRetrievalQueryStrategyComparisonReport } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
8
8
|
import { buildContextPackGenerationReport } from "./buildContextPackGenerationReport.js";
|
|
9
|
+
import { AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT, AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT, buildAgentSuccessRateReport } from "./buildAgentSuccessRateReport.js";
|
|
9
10
|
// Bulk context-window-scaling evidence is presented by the typed report section; the complete
|
|
10
11
|
// evidence stays in context-window-scaling-execution.json.
|
|
11
12
|
const CONTEXT_WINDOW_SCALING_BULK_KEYS = ["executionEvidence", "aggregate"];
|
|
@@ -15,6 +16,8 @@ const RETRIEVAL_PRECISION_RECALL_BULK_KEYS = ["caseExecutionEvidence", "aggregat
|
|
|
15
16
|
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
16
17
|
// Same for context-pack-generation: the typed section presents execution evidence and analysis.
|
|
17
18
|
const CONTEXT_PACK_GENERATION_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
19
|
+
// Same for agent-success-rate: the typed section presents execution evidence and analysis.
|
|
20
|
+
const AGENT_SUCCESS_RATE_BULK_KEYS = ["caseExecutionEvidence", "analysis"];
|
|
18
21
|
const V043_BULK_ARRAY_KEYS = [
|
|
19
22
|
"v043StageContextExecutions",
|
|
20
23
|
"v043StageContextEvaluations",
|
|
@@ -30,6 +33,7 @@ export function buildPluginExperimentReport(args) {
|
|
|
30
33
|
const retrievalPrecisionRecall = buildRetrievalPrecisionRecallReport(args.run);
|
|
31
34
|
const retrievalQueryStrategyComparison = buildRetrievalQueryStrategyComparisonReport(args.run);
|
|
32
35
|
const contextPackGeneration = buildContextPackGenerationReport(args.run, args.contextPacks);
|
|
36
|
+
const agentSuccessRate = buildAgentSuccessRateReport(args.run);
|
|
33
37
|
const rawRun = { ...args.run, artifacts: relativizeArtifacts(args.run.artifacts, outputRoot) };
|
|
34
38
|
for (const key of V043_BULK_ARRAY_KEYS) {
|
|
35
39
|
delete rawRun[key];
|
|
@@ -54,6 +58,11 @@ export function buildPluginExperimentReport(args) {
|
|
|
54
58
|
delete rawRun[key];
|
|
55
59
|
}
|
|
56
60
|
}
|
|
61
|
+
if (agentSuccessRate) {
|
|
62
|
+
for (const key of AGENT_SUCCESS_RATE_BULK_KEYS) {
|
|
63
|
+
delete rawRun[key];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
57
66
|
return {
|
|
58
67
|
metadata: {
|
|
59
68
|
generatedAt: args.generatedAt ?? new Date().toISOString(),
|
|
@@ -83,8 +92,9 @@ export function buildPluginExperimentReport(args) {
|
|
|
83
92
|
retrievalPrecisionRecall,
|
|
84
93
|
retrievalQueryStrategyComparison,
|
|
85
94
|
contextPackGeneration,
|
|
95
|
+
agentSuccessRate,
|
|
86
96
|
contextStrategyComparisonV043,
|
|
87
|
-
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration),
|
|
97
|
+
interpretation: buildInterpretation(args.run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration, agentSuccessRate),
|
|
88
98
|
rawRun,
|
|
89
99
|
};
|
|
90
100
|
}
|
|
@@ -163,7 +173,22 @@ function affectedNeighborhoodSentence(report) {
|
|
|
163
173
|
`reindex recommendations were recommended=${summary.recommendedReindexCount}, not-indicated=${summary.notIndicatedReindexCount}, unknown=${summary.unknownReindexRecommendationCount}. ` +
|
|
164
174
|
"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.");
|
|
165
175
|
}
|
|
166
|
-
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration = null) {
|
|
176
|
+
function buildInterpretation(run, contextStrategyComparisonV043, warmIndexReuse, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration = null, agentSuccessRate = null) {
|
|
177
|
+
if (agentSuccessRate) {
|
|
178
|
+
const real = agentSuccessRate.identity.executionMode === "real-agent";
|
|
179
|
+
const [raw, pack] = agentSuccessRate.treatments;
|
|
180
|
+
const rate = (treatment, kind) => {
|
|
181
|
+
const metric = kind === "initial" ? treatment?.initialSuccessRate : treatment?.finalSuccessRate;
|
|
182
|
+
return metric && metric.availability === "available" && typeof metric.value === "number" ? metric.value.toFixed(4) : "unavailable";
|
|
183
|
+
};
|
|
184
|
+
const summary = real
|
|
185
|
+
? `${AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT} Provider ${agentSuccessRate.identity.providerId ?? "unknown"} ran ${agentSuccessRate.identity.caseCount} case(s) with up to ${agentSuccessRate.identity.maxAttemptsPerTreatment} attempt(s) per treatment. Initial-attempt success rate: raw-full-file ${rate(raw, "initial")}, context-pack ${rate(pack, "initial")}; final success rate: raw-full-file ${rate(raw, "final")}, context-pack ${rate(pack, "final")}. No treatment is declared best and no statistical or causal claim is made.`
|
|
186
|
+
: `${AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT} ${agentSuccessRate.identity.caseCount} case(s) were evaluated for each treatment identity.`;
|
|
187
|
+
return {
|
|
188
|
+
summary,
|
|
189
|
+
recommendedNextStep: "Review per-case results, the repair history and the scientific limitations; unavailable evidence is never counted as a failure or as zero."
|
|
190
|
+
};
|
|
191
|
+
}
|
|
167
192
|
if (contextPackGeneration) {
|
|
168
193
|
const overall = contextPackGeneration.scopes.find((scope) => scope.scopeId === "overall");
|
|
169
194
|
const saved = overall?.tokenSavings;
|
|
@@ -33,6 +33,10 @@ export { buildContextPackGenerationReport, CONTEXT_PACK_GENERATION_LIMITATIONS }
|
|
|
33
33
|
export { loadContextPackArtifacts } from "./loadContextPackArtifacts.js";
|
|
34
34
|
export { renderContextPackGenerationHtml } from "./renderContextPackGenerationHtml.js";
|
|
35
35
|
export { renderContextPackGenerationTextLines } from "./renderContextPackGenerationText.js";
|
|
36
|
+
export * from "./agentSuccessRateReportModel.js";
|
|
37
|
+
export { buildAgentSuccessRateReport, safeAgentSuccessArtifactReference } from "./buildAgentSuccessRateReport.js";
|
|
38
|
+
export { renderAgentSuccessRateHtml } from "./renderAgentSuccessRateHtml.js";
|
|
39
|
+
export { renderAgentSuccessRateTextLines } from "./renderAgentSuccessRateText.js";
|
|
36
40
|
export * from "./retrievalQueryStrategyComparisonReportModel.js";
|
|
37
41
|
export { buildRetrievalQueryStrategyComparisonReport, RETRIEVAL_QUERY_STRATEGY_COMPARISON_LIMITATIONS } from "./buildRetrievalQueryStrategyComparisonReport.js";
|
|
38
42
|
export { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { AGENT_SUCCESS_BASIS_LABEL, formatAgentSuccessMetric, formatAgentSuccessPairs, formatAgentSuccessTotal } from "./renderAgentSuccessRateText.js";
|
|
2
|
+
function escapeHtml(value) {
|
|
3
|
+
return String(value ?? "")
|
|
4
|
+
.replaceAll("&", "&")
|
|
5
|
+
.replaceAll("<", "<")
|
|
6
|
+
.replaceAll(">", ">")
|
|
7
|
+
.replaceAll('"', """)
|
|
8
|
+
.replaceAll("'", "'");
|
|
9
|
+
}
|
|
10
|
+
function table(headers, rows) {
|
|
11
|
+
return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
|
|
12
|
+
.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
|
|
13
|
+
.join("")}</tbody></table>`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* HTML rendering of the typed section. All dynamic content is escaped, artifact references are plain text (never links
|
|
17
|
+
* or embedded files), and no value is recalculated here.
|
|
18
|
+
*/
|
|
19
|
+
export function renderAgentSuccessRateHtml(report) {
|
|
20
|
+
if (!report)
|
|
21
|
+
return "";
|
|
22
|
+
const realMode = report.identity.executionMode === "real-agent";
|
|
23
|
+
const m = formatAgentSuccessMetric;
|
|
24
|
+
const parts = [];
|
|
25
|
+
parts.push(`<section id="agent-success-rate">
|
|
26
|
+
<h2>Agent Success Rate</h2>
|
|
27
|
+
<h3>Experiment identity</h3>
|
|
28
|
+
${table(["Field", "Value"], [
|
|
29
|
+
["Run ID", report.identity.runId],
|
|
30
|
+
["Plugin", `${report.identity.pluginId} (schema ${report.identity.pluginSchemaVersion})`],
|
|
31
|
+
["Run status", report.identity.runStatus],
|
|
32
|
+
["Cases", String(report.identity.caseCount)],
|
|
33
|
+
["Treatments", report.identity.treatmentOrder.join(", ")],
|
|
34
|
+
["Treatment outcomes", String(report.identity.treatmentOutcomeCount)]
|
|
35
|
+
])}
|
|
36
|
+
<h3>Execution mode and provider</h3>
|
|
37
|
+
${table(["Field", "Value"], [
|
|
38
|
+
["Execution mode", report.identity.executionMode],
|
|
39
|
+
["Provider", report.identity.providerId ?? "none (no coding agent was invoked)"],
|
|
40
|
+
["Provider timeout per attempt", report.identity.timeoutMs === null ? "not applicable" : `${report.identity.timeoutMs} ms`],
|
|
41
|
+
["Repair attempts allowed", report.identity.repairAttempts === null ? "not applicable" : String(report.identity.repairAttempts)],
|
|
42
|
+
["Maximum attempts per treatment", String(report.identity.maxAttemptsPerTreatment)]
|
|
43
|
+
])}
|
|
44
|
+
<h3>Scientific limitations</h3>
|
|
45
|
+
<p><strong>${escapeHtml(report.scientificStatement)}</strong></p>
|
|
46
|
+
<ul>${report.limitations.map((limitation) => `<li>${escapeHtml(limitation)}</li>`).join("")}</ul>
|
|
47
|
+
<h3>Overall result availability</h3>
|
|
48
|
+
${table(["Field", "Value"], [
|
|
49
|
+
["Overall", report.resultAvailability.overall],
|
|
50
|
+
["Outcomes with a determinate verdict", `${report.resultAvailability.evaluableTreatmentOutcomes} of ${report.resultAvailability.totalTreatmentOutcomes}`],
|
|
51
|
+
["Outcomes without a determinate verdict", String(report.resultAvailability.unavailableTreatmentOutcomes)]
|
|
52
|
+
])}
|
|
53
|
+
${report.resultAvailability.notes.length > 0 ? `<ul>${report.resultAvailability.notes.map((note) => `<li>${escapeHtml(note)}</li>`).join("")}</ul>` : ""}
|
|
54
|
+
</section>`);
|
|
55
|
+
const treatmentRows = report.treatments.map((treatment) => [
|
|
56
|
+
treatment.treatmentId,
|
|
57
|
+
String(treatment.evaluableCases),
|
|
58
|
+
String(treatment.initialSuccessfulCases),
|
|
59
|
+
m(treatment.initialSuccessRate),
|
|
60
|
+
String(treatment.finalSuccessfulCases),
|
|
61
|
+
m(treatment.finalSuccessRate),
|
|
62
|
+
treatment.repair ? String(treatment.repair.repairAttempts) : "not applicable",
|
|
63
|
+
treatment.repair ? String(treatment.repair.repairedCases) : "not applicable"
|
|
64
|
+
]);
|
|
65
|
+
const comparison = report.comparison;
|
|
66
|
+
parts.push(`<section id="agent-success-rate-comparison">
|
|
67
|
+
<h2>Treatment Comparison</h2>
|
|
68
|
+
${table(["Treatment", "Evaluable cases", "Initial successful cases", "Initial success rate", "Final successful cases", "Final success rate", "Repair attempts", "Repaired cases"], treatmentRows)}
|
|
69
|
+
${report.treatments
|
|
70
|
+
.filter((treatment) => treatment.repair)
|
|
71
|
+
.map((treatment) => `<p class="muted">${escapeHtml(treatment.treatmentId)}: repair-eligible cases ${treatment.repair.repairEligibleCases}; cases with a repair ${treatment.repair.repairAttemptedCases}; repair success rate ${escapeHtml(m(treatment.repair.repairSuccessRate))}; mean attempts per evaluable case ${escapeHtml(m(treatment.repair.meanAttemptsPerEvaluableCase))}.</p>`)
|
|
72
|
+
.join("")}
|
|
73
|
+
${comparison
|
|
74
|
+
? `<h3>Matched comparison (descriptive; no winner is declared)</h3>
|
|
75
|
+
${table(["Statement", "Value"], [
|
|
76
|
+
["Comparison evaluable", comparison.comparisonEvaluable ? "yes" : "no"],
|
|
77
|
+
["Observed outcome difference between treatments", comparison.observedOutcomeDifference],
|
|
78
|
+
["Statistical or causal effect", `${comparison.statisticalEffect} (no significance or causal claim is made)`],
|
|
79
|
+
["Final matched cases", comparison.finalMatchedCaseIds.join(", ") || "none"],
|
|
80
|
+
["Final paired outcomes", formatAgentSuccessPairs(comparison.finalPairedOutcomes)],
|
|
81
|
+
["Initial-attempt matched cases", comparison.initialMatchedCaseIds ? comparison.initialMatchedCaseIds.join(", ") || "none" : "not applicable"],
|
|
82
|
+
["Initial-attempt paired outcomes", formatAgentSuccessPairs(comparison.initialPairedOutcomes)]
|
|
83
|
+
])}
|
|
84
|
+
${comparison.pairedDifferences.length > 0
|
|
85
|
+
? table(["Measurement", "Basis", "Matched cases", "raw-full-file mean", "context-pack mean", "context-pack minus raw-full-file"], comparison.pairedDifferences.map((difference) => [
|
|
86
|
+
difference.id,
|
|
87
|
+
AGENT_SUCCESS_BASIS_LABEL[difference.basis] ?? difference.basis,
|
|
88
|
+
String(difference.matchedCaseIds.length),
|
|
89
|
+
m(difference.rawFullFileMean),
|
|
90
|
+
m(difference.contextPackMean),
|
|
91
|
+
m(difference.meanDifference)
|
|
92
|
+
]))
|
|
93
|
+
: ""}`
|
|
94
|
+
: `<p class="muted">Matched comparison: not applicable (deterministic-fixture mode does not compare context treatments).</p>`}
|
|
95
|
+
</section>`);
|
|
96
|
+
parts.push(`<section id="agent-success-rate-cases">
|
|
97
|
+
<h2>Per-Case Results</h2>
|
|
98
|
+
${table(["Case", "Project", "Locality", "Treatment", "Status", "Initial task success", "Final task success", "Attempts", "Task-check pass rate", "Regression-check pass rate", "Required-fact coverage"], report.cases.flatMap((entry) => entry.treatments.map((treatment) => [
|
|
99
|
+
entry.caseId,
|
|
100
|
+
entry.benchmarkProject,
|
|
101
|
+
entry.taskLocality,
|
|
102
|
+
treatment.treatmentId,
|
|
103
|
+
`${treatment.executionStatus} / ${treatment.evidenceAvailability}`,
|
|
104
|
+
m(treatment.initialTaskSuccess),
|
|
105
|
+
m(treatment.finalTaskSuccess),
|
|
106
|
+
String(treatment.attemptCount),
|
|
107
|
+
m(treatment.taskCheckPassRate),
|
|
108
|
+
m(treatment.regressionCheckPassRate),
|
|
109
|
+
m(treatment.requiredFactCoverage)
|
|
110
|
+
])))}
|
|
111
|
+
</section>`);
|
|
112
|
+
parts.push(`<section id="agent-success-rate-repair-history">
|
|
113
|
+
<h2>Per-Attempt Repair History</h2>
|
|
114
|
+
${!realMode || report.repairHistory.length === 0
|
|
115
|
+
? `<p class="muted">${realMode ? "No attempt history was recorded." : "Not applicable: deterministic-fixture mode runs a single fixture patch and no provider attempts."}</p>`
|
|
116
|
+
: table(["Case", "Treatment", "Attempt", "Provider status", "Patch outcome", "Verification", "Task success", "Outcome", "Duration", "Tokens", "Artifacts"], report.repairHistory.flatMap((history) => history.attempts.map((attempt) => [
|
|
117
|
+
history.caseId,
|
|
118
|
+
history.treatmentId,
|
|
119
|
+
String(attempt.attemptNumber),
|
|
120
|
+
attempt.providerStatus,
|
|
121
|
+
attempt.patchOutcome,
|
|
122
|
+
attempt.verificationAvailability,
|
|
123
|
+
m(attempt.taskSuccess),
|
|
124
|
+
`${attempt.failureCategory}${attempt.repairEligible ? " (repair-eligible)" : ""}`,
|
|
125
|
+
m(attempt.providerDurationMs),
|
|
126
|
+
attempt.tokenAvailability === "available" ? m(attempt.providerTokens) : "unavailable",
|
|
127
|
+
`proposed: ${attempt.proposedPatchPath ?? "none"}; applied: ${attempt.appliedPatchPath ?? "none"}; agent: ${attempt.agentArtifactDirectory ?? "none"}`
|
|
128
|
+
])))}
|
|
129
|
+
</section>`);
|
|
130
|
+
parts.push(`<section id="agent-success-rate-edit-quality">
|
|
131
|
+
<h2>Edit Quality And Blast Radius (final evaluated patch)</h2>
|
|
132
|
+
${table(["Case", "Treatment", "Expected-edit coverage", "Edit-scope precision", "Unexpected changed files", "Protected mutations", "Changed files", "Total churn", "Relative churn"], report.cases.flatMap((entry) => entry.treatments.map((treatment) => [
|
|
133
|
+
entry.caseId,
|
|
134
|
+
treatment.treatmentId,
|
|
135
|
+
m(treatment.expectedEditCoverage),
|
|
136
|
+
m(treatment.editScopePrecision),
|
|
137
|
+
m(treatment.unexpectedChangedFiles),
|
|
138
|
+
m(treatment.protectedMutations),
|
|
139
|
+
`${m(treatment.changedFileCount)}${treatment.changedFiles ? ` [${treatment.changedFiles.items.join(", ")}${treatment.changedFiles.omittedCount > 0 ? `, +${treatment.changedFiles.omittedCount} more` : ""}]` : ""}`,
|
|
140
|
+
m(treatment.totalChurn),
|
|
141
|
+
m(treatment.relativeChurn)
|
|
142
|
+
])))}
|
|
143
|
+
</section>`);
|
|
144
|
+
parts.push(`<section id="agent-success-rate-cost">
|
|
145
|
+
<h2>Duration And Token Evidence</h2>
|
|
146
|
+
${table(["Case", "Treatment", "Final-attempt provider duration", "Final-attempt provider tokens", "Total provider duration (all attempts)", "Total provider tokens (all attempts)"], report.cases.flatMap((entry) => entry.treatments.map((treatment) => [
|
|
147
|
+
entry.caseId,
|
|
148
|
+
treatment.treatmentId,
|
|
149
|
+
m(treatment.finalAttemptProviderDurationMs),
|
|
150
|
+
m(treatment.finalAttemptProviderTokens),
|
|
151
|
+
treatment.totalProviderDurationMs ? m(treatment.totalProviderDurationMs) : "not applicable",
|
|
152
|
+
treatment.totalProviderTokens ? m(treatment.totalProviderTokens) : "not applicable"
|
|
153
|
+
])))}
|
|
154
|
+
${report.treatments
|
|
155
|
+
.filter((treatment) => treatment.repair)
|
|
156
|
+
.flatMap((treatment) => [
|
|
157
|
+
...treatment.repair.providerDurationMs.map((total) => `<p class="muted">${escapeHtml(treatment.treatmentId)} provider duration [${escapeHtml(AGENT_SUCCESS_BASIS_LABEL[total.basis] ?? total.basis)}]: ${escapeHtml(formatAgentSuccessTotal(total))}</p>`),
|
|
158
|
+
...treatment.repair.providerTokens.map((total) => `<p class="muted">${escapeHtml(treatment.treatmentId)} provider tokens [${escapeHtml(AGENT_SUCCESS_BASIS_LABEL[total.basis] ?? total.basis)}]: ${escapeHtml(formatAgentSuccessTotal(total))}</p>`)
|
|
159
|
+
])
|
|
160
|
+
.join("")}
|
|
161
|
+
</section>`);
|
|
162
|
+
const errors = report.cases.flatMap((entry) => entry.treatments.flatMap((treatment) => treatment.errors.map((error) => [entry.caseId, treatment.treatmentId, String(error.attemptNumber), error.code, error.message])));
|
|
163
|
+
parts.push(`<section id="agent-success-rate-findings">
|
|
164
|
+
<h2>Agent Success Rate Warnings, Failures And Artifacts</h2>
|
|
165
|
+
${report.warnings.length === 0 && errors.length === 0 ? "<p>No warnings or recorded errors.</p>" : table(["Source", "Case", "Treatment", "Attempt", "Code", "Message"], [...report.warnings.map((warning) => ["warning", "", "", "", warning.code, warning.message]), ...errors.map((error) => ["error", ...error])])}
|
|
166
|
+
${table(["Artifact", "Reference", "Case", "Treatment"], report.artifacts.map((artifact) => [artifact.id, artifact.path ?? "[reference withheld]", artifact.caseId ?? "", artifact.variantId ?? ""]))}
|
|
167
|
+
</section>`);
|
|
168
|
+
return parts.join("\n\n ");
|
|
169
|
+
}
|