@dailephd/my-dev-kit-lab 0.8.1 → 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.
Files changed (127) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +20 -4
  3. package/benchmarks/contracts/agent-success-rate-project-profiles.json +33 -0
  4. package/benchmarks/contracts/agent-success-rate-tasks.json +671 -0
  5. package/benchmarks/projects/agent-success-inventory-node/.gitattributes +2 -0
  6. package/benchmarks/projects/agent-success-inventory-node/README.md +74 -0
  7. package/benchmarks/projects/agent-success-inventory-node/package.json +14 -0
  8. package/benchmarks/projects/agent-success-inventory-node/src/fulfillmentReport.js +38 -0
  9. package/benchmarks/projects/agent-success-inventory-node/src/index.js +26 -0
  10. package/benchmarks/projects/agent-success-inventory-node/src/inventoryStore.js +114 -0
  11. package/benchmarks/projects/agent-success-inventory-node/src/quantity.js +19 -0
  12. package/benchmarks/projects/agent-success-inventory-node/src/reservationService.js +45 -0
  13. package/benchmarks/projects/agent-success-inventory-node/tests/quantity-edge.check.mjs +51 -0
  14. package/benchmarks/projects/agent-success-inventory-node/tests/quantity-primary.check.mjs +46 -0
  15. package/benchmarks/projects/agent-success-inventory-node/tests/regression.check.mjs +115 -0
  16. package/benchmarks/projects/agent-success-inventory-node/tests/report-edge.check.mjs +109 -0
  17. package/benchmarks/projects/agent-success-inventory-node/tests/report-primary.check.mjs +47 -0
  18. package/benchmarks/projects/agent-success-inventory-node/tests/reservation-edge.check.mjs +63 -0
  19. package/benchmarks/projects/agent-success-inventory-node/tests/reservation-primary.check.mjs +44 -0
  20. package/benchmarks/projects/agent-success-task-board-node/.gitattributes +2 -0
  21. package/benchmarks/projects/agent-success-task-board-node/README.md +77 -0
  22. package/benchmarks/projects/agent-success-task-board-node/package.json +14 -0
  23. package/benchmarks/projects/agent-success-task-board-node/src/index.js +24 -0
  24. package/benchmarks/projects/agent-success-task-board-node/src/projectSummary.js +15 -0
  25. package/benchmarks/projects/agent-success-task-board-node/src/taskService.js +58 -0
  26. package/benchmarks/projects/agent-success-task-board-node/src/taskStore.js +58 -0
  27. package/benchmarks/projects/agent-success-task-board-node/src/validation.js +31 -0
  28. package/benchmarks/projects/agent-success-task-board-node/tests/import-edge.check.mjs +65 -0
  29. package/benchmarks/projects/agent-success-task-board-node/tests/import-primary.check.mjs +31 -0
  30. package/benchmarks/projects/agent-success-task-board-node/tests/regression.check.mjs +107 -0
  31. package/benchmarks/projects/agent-success-task-board-node/tests/summary-edge.check.mjs +67 -0
  32. package/benchmarks/projects/agent-success-task-board-node/tests/summary-primary.check.mjs +54 -0
  33. package/benchmarks/projects/agent-success-task-board-node/tests/title-edge.check.mjs +39 -0
  34. package/benchmarks/projects/agent-success-task-board-node/tests/title-primary.check.mjs +29 -0
  35. package/dist/src/cli/help.js +54 -2
  36. package/dist/src/commands/runExperimentDescribeCommand.js +60 -2
  37. package/dist/src/commands/runExperimentRunCommand.js +208 -5
  38. package/dist/src/core/processTree.js +55 -0
  39. package/dist/src/core/runMeasuredCommand.js +104 -22
  40. package/dist/src/evaluation/agentSuccess/applyPatchToSandbox.js +37 -0
  41. package/dist/src/evaluation/agentSuccess/assessBaseline.js +23 -0
  42. package/dist/src/evaluation/agentSuccess/corpusTypes.js +74 -0
  43. package/dist/src/evaluation/agentSuccess/extractPatchCandidate.js +46 -0
  44. package/dist/src/evaluation/agentSuccess/index.js +14 -0
  45. package/dist/src/evaluation/agentSuccess/parseUnifiedDiff.js +210 -0
  46. package/dist/src/evaluation/agentSuccess/patchTypes.js +5 -0
  47. package/dist/src/evaluation/agentSuccess/readAgentSuccessCorpus.js +183 -0
  48. package/dist/src/evaluation/agentSuccess/runVerificationCheck.js +100 -0
  49. package/dist/src/evaluation/agentSuccess/taskPaths.js +34 -0
  50. package/dist/src/evaluation/agentSuccess/taskTypes.js +14 -0
  51. package/dist/src/evaluation/agentSuccess/validateAgentSuccessCorpus.js +307 -0
  52. package/dist/src/evaluation/agentSuccess/validateAgentSuccessTask.js +297 -0
  53. package/dist/src/evaluation/agentSuccess/validatePatchPolicy.js +66 -0
  54. package/dist/src/evaluation/agentSuccess/verificationTypes.js +1 -0
  55. package/dist/src/evaluation/benchmarkSandbox/createBenchmarkSandbox.js +127 -0
  56. package/dist/src/evaluation/benchmarkSandbox/errors.js +11 -0
  57. package/dist/src/evaluation/benchmarkSandbox/gitExecutor.js +60 -0
  58. package/dist/src/evaluation/benchmarkSandbox/index.js +8 -0
  59. package/dist/src/evaluation/benchmarkSandbox/minimalHostEnv.js +37 -0
  60. package/dist/src/evaluation/benchmarkSandbox/pathPolicy.js +49 -0
  61. package/dist/src/evaluation/benchmarkSandbox/removeBenchmarkSandbox.js +74 -0
  62. package/dist/src/evaluation/benchmarkSandbox/treeSnapshot.js +57 -0
  63. package/dist/src/evaluation/benchmarkSandbox/types.js +3 -0
  64. package/dist/src/evaluation/changeSet/captureChangeSet.js +98 -0
  65. package/dist/src/evaluation/changeSet/index.js +2 -0
  66. package/dist/src/evaluation/changeSet/types.js +9 -0
  67. package/dist/src/evaluation/runMyDevKitRetrieval.js +1 -0
  68. package/dist/src/experiments/defaultRegistry.js +4 -0
  69. package/dist/src/experiments/plugins/agentSuccessRate/agentTaskProjection.js +12 -0
  70. package/dist/src/experiments/plugins/agentSuccessRate/analysis.js +425 -0
  71. package/dist/src/experiments/plugins/agentSuccessRate/analysisArtifact.js +155 -0
  72. package/dist/src/experiments/plugins/agentSuccessRate/analysisTypes.js +65 -0
  73. package/dist/src/experiments/plugins/agentSuccessRate/attemptEvidence.js +7 -0
  74. package/dist/src/experiments/plugins/agentSuccessRate/config.js +123 -0
  75. package/dist/src/experiments/plugins/agentSuccessRate/contextGeneration.js +497 -0
  76. package/dist/src/experiments/plugins/agentSuccessRate/execution.js +441 -0
  77. package/dist/src/experiments/plugins/agentSuccessRate/executionArtifact.js +126 -0
  78. package/dist/src/experiments/plugins/agentSuccessRate/executionTypes.js +2 -0
  79. package/dist/src/experiments/plugins/agentSuccessRate/index.js +20 -0
  80. package/dist/src/experiments/plugins/agentSuccessRate/metadata.js +28 -0
  81. package/dist/src/experiments/plugins/agentSuccessRate/metrics.js +76 -0
  82. package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +507 -0
  83. package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +250 -0
  84. package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +43 -0
  85. package/dist/src/experiments/plugins/agentSuccessRate/repairAnalysis.js +148 -0
  86. package/dist/src/experiments/plugins/agentSuccessRate/repairExecution.js +103 -0
  87. package/dist/src/experiments/plugins/agentSuccessRate/repairFeedback.js +91 -0
  88. package/dist/src/experiments/plugins/agentSuccessRate/repairPolicy.js +88 -0
  89. package/dist/src/experiments/plugins/agentSuccessRate/types.js +1 -0
  90. package/dist/src/experiments/plugins/contextPackGeneration/analysis.js +157 -0
  91. package/dist/src/experiments/plugins/contextPackGeneration/analysisArtifact.js +22 -0
  92. package/dist/src/experiments/plugins/contextPackGeneration/analysisTypes.js +1 -0
  93. package/dist/src/experiments/plugins/contextPackGeneration/buildContextPack.js +84 -0
  94. package/dist/src/experiments/plugins/contextPackGeneration/config.js +39 -0
  95. package/dist/src/experiments/plugins/contextPackGeneration/execution.js +361 -0
  96. package/dist/src/experiments/plugins/contextPackGeneration/executionArtifact.js +17 -0
  97. package/dist/src/experiments/plugins/contextPackGeneration/executionTypes.js +11 -0
  98. package/dist/src/experiments/plugins/contextPackGeneration/identityEvidence.js +71 -0
  99. package/dist/src/experiments/plugins/contextPackGeneration/index.js +19 -0
  100. package/dist/src/experiments/plugins/contextPackGeneration/localSubjectExecution.js +146 -0
  101. package/dist/src/experiments/plugins/contextPackGeneration/localSubjectPrivacy.js +276 -0
  102. package/dist/src/experiments/plugins/contextPackGeneration/metadata.js +24 -0
  103. package/dist/src/experiments/plugins/contextPackGeneration/metrics.js +46 -0
  104. package/dist/src/experiments/plugins/contextPackGeneration/packArtifact.js +17 -0
  105. package/dist/src/experiments/plugins/contextPackGeneration/packEvidence.js +152 -0
  106. package/dist/src/experiments/plugins/contextPackGeneration/packSelectionPolicy.js +305 -0
  107. package/dist/src/experiments/plugins/contextPackGeneration/plugin.js +355 -0
  108. package/dist/src/experiments/plugins/contextPackGeneration/renderContextPack.js +61 -0
  109. package/dist/src/experiments/plugins/contextPackGeneration/types.js +29 -0
  110. package/dist/src/experiments/plugins/warmIndexReuse/plugin.js +2 -3
  111. package/dist/src/report/experiments/agentSuccessRateReportModel.js +6 -0
  112. package/dist/src/report/experiments/buildAgentSuccessRateReport.js +255 -0
  113. package/dist/src/report/experiments/buildContextPackGenerationReport.js +171 -0
  114. package/dist/src/report/experiments/buildPluginExperimentReport.js +49 -2
  115. package/dist/src/report/experiments/contextPackGenerationReportModel.js +9 -0
  116. package/dist/src/report/experiments/index.js +9 -0
  117. package/dist/src/report/experiments/loadContextPackArtifacts.js +43 -0
  118. package/dist/src/report/experiments/renderAgentSuccessRateHtml.js +169 -0
  119. package/dist/src/report/experiments/renderAgentSuccessRateText.js +139 -0
  120. package/dist/src/report/experiments/renderContextPackGenerationHtml.js +122 -0
  121. package/dist/src/report/experiments/renderContextPackGenerationText.js +101 -0
  122. package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +6 -0
  123. package/dist/src/report/experiments/renderPluginExperimentReportText.js +12 -0
  124. package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonHtml.js +15 -15
  125. package/dist/src/report/experiments/writePluginExperimentReports.js +2 -0
  126. package/docs/METRICS.md +98 -1
  127. 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,157 @@
1
+ import { calculateRetrievalQualityMetricsFromIdentityEvidence } from "../../../evaluation/retrievalQuality/metrics.js";
2
+ import { calculateBalancedIdentityF1 } from "../retrievalQueryStrategyComparison/analysis.js";
3
+ import { RETRIEVAL_QUERY_STRATEGY_COMPARISON_SCOPE_IDS } from "../retrievalQueryStrategyComparison/analysisTypes.js";
4
+ import { CONTEXT_PACK_GENERATION_TREATMENT_IDS } from "./metadata.js";
5
+ const noMeasurementF1 = () => ({
6
+ availability: "unavailable",
7
+ numerator: null,
8
+ denominator: null,
9
+ value: null,
10
+ reason: "no-measurement"
11
+ });
12
+ /** Scores already-completed treatment evidence with the frozen retrieval-quality science. It never influences retrieval. */
13
+ export function analyzeContextPackGenerationTreatment(evaluationCase, treatment) {
14
+ if (treatment.identityEvidence === null || treatment.size === null) {
15
+ return {
16
+ treatmentId: treatment.treatmentId,
17
+ executionStatus: treatment.status,
18
+ availability: treatment.availability,
19
+ quality: null,
20
+ fileF1: noMeasurementF1(),
21
+ symbolF1: noMeasurementF1(),
22
+ estimatedTokens: null,
23
+ tokenCountMethod: null
24
+ };
25
+ }
26
+ const quality = calculateRetrievalQualityMetricsFromIdentityEvidence({
27
+ evaluationCase,
28
+ retrieval: {
29
+ identityEvidence: treatment.identityEvidence,
30
+ totalEstimatedTokens: treatment.size.totalEstimatedTokens,
31
+ tokenCountMethod: treatment.size.tokenCountMethod
32
+ }
33
+ });
34
+ return {
35
+ treatmentId: treatment.treatmentId,
36
+ executionStatus: treatment.status,
37
+ availability: treatment.availability,
38
+ quality,
39
+ fileF1: calculateBalancedIdentityF1(quality.file.relevantRetrievedFiles, quality.file.irrelevantRetrievedFiles, quality.file.missedFiles),
40
+ symbolF1: calculateBalancedIdentityF1(quality.symbol.relevantRetrievedSymbols, quality.symbol.irrelevantRetrievedSymbols, quality.symbol.missedSymbols),
41
+ estimatedTokens: treatment.size.totalEstimatedTokens,
42
+ tokenCountMethod: treatment.size.tokenCountMethod
43
+ };
44
+ }
45
+ /**
46
+ * Same arithmetic as the existing repository token-savings comparison (tokensSaved = raw - other; percentSaved =
47
+ * tokensSaved / raw * 100, 0 when raw is 0). Negative savings are valid and never clamped.
48
+ */
49
+ export function calculateContextPackTokenSavings(rawTokens, packTokens) {
50
+ const tokensSaved = rawTokens - packTokens;
51
+ return { tokensSaved, percentSaved: rawTokens === 0 ? 0 : (tokensSaved / rawTokens) * 100 };
52
+ }
53
+ const valueOf = (metric) => (metric && metric.availability === "available" ? metric.value : null);
54
+ function compareTreatments(raw, pack) {
55
+ const delta = (packValue, rawValue) => (packValue === null || rawValue === null ? null : packValue - rawValue);
56
+ const factPack = valueOf(pack.quality?.fact.coverage);
57
+ const factRaw = valueOf(raw.quality?.fact.coverage);
58
+ const sizes = raw.estimatedTokens !== null && pack.estimatedTokens !== null ? calculateContextPackTokenSavings(raw.estimatedTokens, pack.estimatedTokens) : null;
59
+ return {
60
+ fileF1Delta: delta(valueOf(pack.fileF1), valueOf(raw.fileF1)),
61
+ symbolF1Delta: delta(valueOf(pack.symbolF1), valueOf(raw.symbolF1)),
62
+ factCoverageDelta: delta(factPack, factRaw),
63
+ estimatedTokenDelta: delta(pack.estimatedTokens, raw.estimatedTokens),
64
+ tokensSaved: sizes?.tokensSaved ?? null,
65
+ percentSaved: sizes?.percentSaved ?? null
66
+ };
67
+ }
68
+ export function analyzeContextPackGenerationCase(evaluationCase, executionCase) {
69
+ if (evaluationCase.id !== executionCase.caseId)
70
+ throw new Error("Context pack generation analysis case identity mismatch.");
71
+ const order = executionCase.treatments.map((treatment) => treatment.treatmentId);
72
+ if (order.length !== CONTEXT_PACK_GENERATION_TREATMENT_IDS.length || order.some((id, index) => id !== CONTEXT_PACK_GENERATION_TREATMENT_IDS[index])) {
73
+ throw new Error("Context pack generation analysis requires the two canonical treatments in canonical order.");
74
+ }
75
+ const treatments = executionCase.treatments.map((treatment) => analyzeContextPackGenerationTreatment(evaluationCase, treatment));
76
+ return {
77
+ caseId: executionCase.caseId,
78
+ benchmarkProject: executionCase.benchmarkProject,
79
+ taskLocality: executionCase.taskLocality,
80
+ treatments,
81
+ comparison: compareTreatments(treatments[0], treatments[1])
82
+ };
83
+ }
84
+ function isComparable(treatment) {
85
+ const quality = treatment.quality;
86
+ return (quality !== null &&
87
+ treatment.fileF1.availability === "available" &&
88
+ treatment.symbolF1.availability === "available" &&
89
+ quality.fact.coverage.availability === "available" &&
90
+ typeof quality.retrievedTokenCount === "number" &&
91
+ Number.isFinite(quality.retrievedTokenCount) &&
92
+ quality.retrievedTokenCount >= 0);
93
+ }
94
+ /** Matched design: a case counts only when BOTH treatments expose all four required measures. */
95
+ export function isContextPackCaseComparable(caseAnalysis) {
96
+ return caseAnalysis.treatments.length === CONTEXT_PACK_GENERATION_TREATMENT_IDS.length && caseAnalysis.treatments.every(isComparable);
97
+ }
98
+ const mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
99
+ function casesForScope(cases, scopeId) {
100
+ return scopeId === "overall" ? [...cases] : cases.filter((entry) => entry.taskLocality === scopeId);
101
+ }
102
+ export function aggregateContextPackGenerationScope(scopeId, cases) {
103
+ const scopeCases = casesForScope(cases, scopeId);
104
+ const included = scopeCases.filter(isContextPackCaseComparable);
105
+ const excluded = scopeCases.filter((entry) => !isContextPackCaseComparable(entry));
106
+ const treatmentSummaries = CONTEXT_PACK_GENERATION_TREATMENT_IDS.map((treatmentId, index) => {
107
+ const countStatus = (status) => scopeCases.filter((entry) => entry.treatments[index].executionStatus === status).length;
108
+ const rows = included.map((entry) => entry.treatments[index]);
109
+ return {
110
+ treatmentId,
111
+ completedCaseCount: countStatus("completed"),
112
+ partialCaseCount: countStatus("partial"),
113
+ failedCaseCount: countStatus("failed"),
114
+ objectives: rows.length === 0
115
+ ? null
116
+ : {
117
+ meanFileF1: mean(rows.map((row) => row.fileF1.value)),
118
+ meanSymbolF1: mean(rows.map((row) => row.symbolF1.value)),
119
+ meanFactCoverage: mean(rows.map((row) => row.quality.fact.coverage.value)),
120
+ meanEstimatedTokenCount: mean(rows.map((row) => row.quality.retrievedTokenCount))
121
+ }
122
+ };
123
+ });
124
+ const [rawSummary, packSummary] = treatmentSummaries;
125
+ const rawObjectives = rawSummary.objectives;
126
+ const packObjectives = packSummary.objectives;
127
+ const paired = rawObjectives && packObjectives;
128
+ return {
129
+ scopeId,
130
+ caseCount: scopeCases.length,
131
+ includedCaseCount: included.length,
132
+ excludedCaseCount: excluded.length,
133
+ includedCaseIds: included.map((entry) => entry.caseId),
134
+ excludedCaseIds: excluded.map((entry) => entry.caseId),
135
+ treatmentSummaries,
136
+ pairedDeltas: paired
137
+ ? {
138
+ meanFileF1Delta: mean(included.map((entry) => entry.comparison.fileF1Delta)),
139
+ meanSymbolF1Delta: mean(included.map((entry) => entry.comparison.symbolF1Delta)),
140
+ meanFactCoverageDelta: mean(included.map((entry) => entry.comparison.factCoverageDelta)),
141
+ meanEstimatedTokenDelta: mean(included.map((entry) => entry.comparison.estimatedTokenDelta))
142
+ }
143
+ : null,
144
+ tokenSavings: paired
145
+ ? {
146
+ meanTokensSaved: mean(included.map((entry) => entry.comparison.tokensSaved)),
147
+ percentSavedOfMeans: calculateContextPackTokenSavings(rawObjectives.meanEstimatedTokenCount, packObjectives.meanEstimatedTokenCount).percentSaved
148
+ }
149
+ : null
150
+ };
151
+ }
152
+ export function analyzeContextPackGeneration(evaluationCases, executionCases) {
153
+ if (evaluationCases.length !== executionCases.length)
154
+ throw new Error("Context pack generation analysis requires one execution case per evaluation case.");
155
+ const cases = evaluationCases.map((evaluationCase, index) => analyzeContextPackGenerationCase(evaluationCase, executionCases[index]));
156
+ return { cases, scopes: RETRIEVAL_QUERY_STRATEGY_COMPARISON_SCOPE_IDS.map((scopeId) => aggregateContextPackGenerationScope(scopeId, cases)) };
157
+ }
@@ -0,0 +1,22 @@
1
+ export const CONTEXT_PACK_GENERATION_ANALYSIS_ARTIFACT_FILE = "context-pack-generation-analysis.json";
2
+ export const CONTEXT_PACK_GENERATION_ANALYSIS_SCHEMA_VERSION = "my-dev-kit-lab-context-pack-generation-analysis-v1";
3
+ /** Single owner of the frozen methodology. No ranking, winner, composite score or Pareto front exists for this experiment. */
4
+ export const CONTEXT_PACK_GENERATION_METHODOLOGY = {
5
+ fileF1: "balanced-f1",
6
+ symbolF1: "balanced-f1",
7
+ aggregation: "matched-complete-case-macro-mean",
8
+ treatmentComparison: "paired-descriptive-delta",
9
+ sizeMeasure: "estimated-tokens-of-rendered-text"
10
+ };
11
+ export function buildContextPackGenerationAnalysisArtifact(args) {
12
+ return {
13
+ schemaVersion: CONTEXT_PACK_GENERATION_ANALYSIS_SCHEMA_VERSION,
14
+ runId: args.runId,
15
+ pluginId: args.pluginId,
16
+ pluginSchemaVersion: args.pluginSchemaVersion,
17
+ startedAt: args.startedAt,
18
+ completedAt: args.completedAt,
19
+ methodology: { ...CONTEXT_PACK_GENERATION_METHODOLOGY },
20
+ analysis: structuredClone(args.analysis)
21
+ };
22
+ }
@@ -0,0 +1,84 @@
1
+ import { countEstimatedTokens, countTextChars, tokenCountMethod } from "../../../core/countTokens.js";
2
+ import { compareCodeUnits } from "../../../evaluation/indexSnapshot.js";
3
+ import { buildContextPackSelectionPolicy, selectCallRelationships, selectRelevantFiles, selectRelevantSymbols, selectSourceSlices, selectTestFiles } from "./packSelectionPolicy.js";
4
+ import { renderContextPack } from "./renderContextPack.js";
5
+ import { CONTEXT_PACK_SCHEMA_VERSION, CONTEXT_PACK_SECTION_IDS } from "./types.js";
6
+ /**
7
+ * Pure context-pack composition. No filesystem, process, environment, network, clock or randomness: identical input
8
+ * yields structurally identical output. It selects within the frozen caps, renders deterministically, and measures size
9
+ * from the exact rendered text.
10
+ */
11
+ export function buildContextPack(input) {
12
+ const files = selectRelevantFiles(input.fileCandidates);
13
+ const symbols = selectRelevantSymbols(input.symbolCandidates);
14
+ const sourceSlices = selectSourceSlices(input.sourceSliceCandidates);
15
+ const calls = selectCallRelationships(input.callCandidates);
16
+ const tests = selectTestFiles(input.testCandidates);
17
+ const evidenceNotes = [
18
+ ...new Set([
19
+ ...(input.evidenceNotes ?? []),
20
+ ...files.notes,
21
+ ...symbols.notes,
22
+ ...sourceSlices.notes,
23
+ ...calls.notes,
24
+ ...tests.notes
25
+ ])
26
+ ].sort(compareCodeUnits);
27
+ const sectionAvailability = {
28
+ task: { availability: "available", reason: null },
29
+ files: { ...input.sectionAvailability.files },
30
+ symbols: { ...input.sectionAvailability.symbols },
31
+ sourceSlices: { ...input.sectionAvailability.sourceSlices },
32
+ callRelationships: { ...input.sectionAvailability.callRelationships },
33
+ tests: { ...input.sectionAvailability.tests },
34
+ evidenceNotes: { ...input.sectionAvailability.evidenceNotes }
35
+ };
36
+ const itemCounts = {
37
+ task: 1,
38
+ files: files.items.length,
39
+ symbols: symbols.items.length,
40
+ sourceSlices: sourceSlices.items.length,
41
+ callRelationships: calls.items.length,
42
+ tests: tests.items.length,
43
+ evidenceNotes: evidenceNotes.length
44
+ };
45
+ const rendered = renderContextPack({
46
+ task: input.task,
47
+ files: files.items,
48
+ symbols: symbols.items,
49
+ sourceSlices: sourceSlices.items,
50
+ callRelationships: calls.items,
51
+ tests: tests.items,
52
+ evidenceNotes,
53
+ sectionAvailability
54
+ });
55
+ return {
56
+ schemaVersion: CONTEXT_PACK_SCHEMA_VERSION,
57
+ caseId: input.caseId,
58
+ benchmarkProject: input.benchmarkProject,
59
+ taskLocality: input.taskLocality,
60
+ task: { title: input.task.title, summary: input.task.summary },
61
+ selectionPolicy: buildContextPackSelectionPolicy(input.myDevKitVersion),
62
+ files: files.items,
63
+ symbols: symbols.items,
64
+ sourceSlices: sourceSlices.items,
65
+ callRelationships: calls.items,
66
+ tests: tests.items,
67
+ evidenceNotes,
68
+ sections: CONTEXT_PACK_SECTION_IDS.map((id) => ({
69
+ id,
70
+ availability: sectionAvailability[id].availability,
71
+ reason: sectionAvailability[id].reason,
72
+ itemCount: itemCounts[id],
73
+ estimatedTokens: countEstimatedTokens(rendered.sections[id])
74
+ })),
75
+ size: {
76
+ totalChars: countTextChars(rendered.renderedText),
77
+ totalEstimatedTokens: countEstimatedTokens(rendered.renderedText),
78
+ tokenCountMethod
79
+ },
80
+ availability: input.availability,
81
+ reason: input.reason,
82
+ renderedText: rendered.renderedText
83
+ };
84
+ }
@@ -0,0 +1,39 @@
1
+ import { invalidExperimentConfig, isPlainObject, mergeConfig, validExperimentConfig } from "../../config.js";
2
+ export const defaultContextPackGenerationConfig = {
3
+ outDir: "lab-output/context-pack-generation",
4
+ kitCommand: "npx @dailephd/my-dev-kit@latest"
5
+ };
6
+ const SUPPORTED_FIELDS = ["outDir", "kitCommand", "caseIds", "benchmarkProjects"];
7
+ export const contextPackGenerationConfigDefinition = {
8
+ fields: [
9
+ { name: "outDir", type: "string", required: true, description: "Experiment output root." },
10
+ { name: "kitCommand", type: "string", description: "my-dev-kit command used for index and retrieval." },
11
+ { name: "caseIds", type: "array", description: "Bundled evaluation case IDs to include; corpus order is preserved." },
12
+ { name: "benchmarkProjects", type: "array", description: "Bundled benchmark project IDs to include; corpus order is preserved." }
13
+ ]
14
+ };
15
+ export function validateContextPackGenerationConfig(config) {
16
+ if (config !== undefined && !isPlainObject(config)) {
17
+ return invalidExperimentConfig(["context pack generation config must be an object."]);
18
+ }
19
+ const errors = [];
20
+ const unsupported = Object.keys(config ?? {}).filter((key) => !SUPPORTED_FIELDS.includes(key));
21
+ if (unsupported.length > 0) {
22
+ errors.push(`Unsupported context-pack-generation config field(s): ${unsupported.sort().join(", ")}.`);
23
+ }
24
+ const normalized = mergeConfig(defaultContextPackGenerationConfig, config);
25
+ for (const field of ["outDir", "kitCommand"]) {
26
+ const value = normalized[field];
27
+ if (typeof value !== "string" || !value.trim())
28
+ errors.push(`${field} must be a non-empty string.`);
29
+ }
30
+ for (const field of ["caseIds", "benchmarkProjects"]) {
31
+ const value = normalized[field];
32
+ if (value === undefined)
33
+ continue;
34
+ if (!Array.isArray(value) || value.length === 0 || !value.every((item) => typeof item === "string" && item.trim())) {
35
+ errors.push(`${field} must be a non-empty array of non-empty strings when provided.`);
36
+ }
37
+ }
38
+ return errors.length > 0 ? invalidExperimentConfig(errors) : validExperimentConfig(normalized);
39
+ }