@dailephd/my-dev-kit-lab 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +7 -4
- package/dist/src/cli/help.js +35 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +15 -1
- package/dist/src/commands/runExperimentRunCommand.js +177 -4
- package/dist/src/evaluation/retrievalQuality/metrics.js +27 -2
- package/dist/src/evaluation/retrievalQueryStrategies.js +26 -0
- package/dist/src/evaluation/retrievalQueryStrategyEvidence.js +58 -0
- package/dist/src/evaluation/runMyDevKitRetrieval.js +60 -51
- package/dist/src/evaluation/runSemanticRetrievalStrategy.js +303 -0
- package/dist/src/experiments/defaultRegistry.js +4 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysis.js +157 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysisArtifact.js +22 -0
- package/dist/src/experiments/plugins/contextPackGeneration/analysisTypes.js +1 -0
- package/dist/src/experiments/plugins/contextPackGeneration/buildContextPack.js +84 -0
- package/dist/src/experiments/plugins/contextPackGeneration/config.js +39 -0
- package/dist/src/experiments/plugins/contextPackGeneration/execution.js +361 -0
- package/dist/src/experiments/plugins/contextPackGeneration/executionArtifact.js +17 -0
- package/dist/src/experiments/plugins/contextPackGeneration/executionTypes.js +11 -0
- package/dist/src/experiments/plugins/contextPackGeneration/identityEvidence.js +71 -0
- package/dist/src/experiments/plugins/contextPackGeneration/index.js +19 -0
- package/dist/src/experiments/plugins/contextPackGeneration/localSubjectExecution.js +146 -0
- package/dist/src/experiments/plugins/contextPackGeneration/localSubjectPrivacy.js +276 -0
- package/dist/src/experiments/plugins/contextPackGeneration/metadata.js +24 -0
- package/dist/src/experiments/plugins/contextPackGeneration/metrics.js +46 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packArtifact.js +17 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packEvidence.js +152 -0
- package/dist/src/experiments/plugins/contextPackGeneration/packSelectionPolicy.js +305 -0
- package/dist/src/experiments/plugins/contextPackGeneration/plugin.js +355 -0
- package/dist/src/experiments/plugins/contextPackGeneration/renderContextPack.js +61 -0
- package/dist/src/experiments/plugins/contextPackGeneration/types.js +29 -0
- package/dist/src/experiments/plugins/contextWindowScaling/localSubjectPrivacy.js +2 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysis.js +172 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysisArtifact.js +28 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/analysisTypes.js +2 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/config.js +39 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/execution.js +225 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/executionArtifact.js +15 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/index.js +12 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/localSubjectExecution.js +124 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/localSubjectPrivacy.js +176 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/metadata.js +51 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/metrics.js +60 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/plugin.js +292 -0
- package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/types.js +1 -0
- package/dist/src/report/experiments/buildContextPackGenerationReport.js +171 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +47 -2
- package/dist/src/report/experiments/buildRetrievalQueryStrategyComparisonReport.js +91 -0
- package/dist/src/report/experiments/contextPackGenerationReportModel.js +9 -0
- package/dist/src/report/experiments/index.js +9 -0
- package/dist/src/report/experiments/loadContextPackArtifacts.js +43 -0
- package/dist/src/report/experiments/renderContextPackGenerationHtml.js +122 -0
- package/dist/src/report/experiments/renderContextPackGenerationText.js +101 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +6 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +16 -0
- package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonHtml.js +96 -0
- package/dist/src/report/experiments/renderRetrievalQueryStrategyComparisonText.js +58 -0
- package/dist/src/report/experiments/retrievalQueryStrategyComparisonReportModel.js +1 -0
- package/dist/src/report/experiments/writePluginExperimentReports.js +2 -0
- package/docs/METRICS.md +149 -2
- package/package.json +1 -1
package/dist/src/experiments/plugins/retrievalQueryStrategyComparison/localSubjectExecution.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { validateRetrievalPrecisionRecallCase } from "../../../evaluation/retrievalQuality/index.js";
|
|
3
|
+
import { buildMyDevKitIndex } from "../../../evaluation/runMyDevKitRetrieval.js";
|
|
4
|
+
import { captureTargetSnapshot, compareTargetSnapshots } from "../../../evaluation/targetImmutability/index.js";
|
|
5
|
+
import { LocalSubjectExecutionError } from "../contextWindowScaling/localSubjectErrors.js";
|
|
6
|
+
import { deriveGuidedIndexExclusions } from "../contextWindowScaling/localSubjectExclusions.js";
|
|
7
|
+
import { assertWorkRootOutsideTarget, createPrivateScratch, defaultPrivateScratchIo, removePrivateScratch } from "../contextWindowScaling/localSubjectScratch.js";
|
|
8
|
+
import { executeRetrievalQueryStrategyCaseFromPreparedIndex } from "./execution.js";
|
|
9
|
+
/**
|
|
10
|
+
* Counts distinct repository-relative file identities exposed by the seven strategy-neutral evidence objects that lie
|
|
11
|
+
* outside the eligible subject universe. It inspects every non-null evidence file path and evidence symbol file, never
|
|
12
|
+
* source text, command output or node IDs. Only the count leaves this function; an offending identity is never named.
|
|
13
|
+
*/
|
|
14
|
+
export function countRetrievalQueryStrategyFilesOutsideEligibleUniverse(caseEvidence, eligibleFiles) {
|
|
15
|
+
const identities = new Set();
|
|
16
|
+
for (const treatment of caseEvidence.treatments) {
|
|
17
|
+
if (treatment.evidence === null)
|
|
18
|
+
continue;
|
|
19
|
+
for (const file of treatment.evidence.files)
|
|
20
|
+
identities.add(file.path);
|
|
21
|
+
for (const symbol of treatment.evidence.symbols) {
|
|
22
|
+
if (symbol.file !== null)
|
|
23
|
+
identities.add(symbol.file);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return [...identities].filter((identity) => !eligibleFiles.has(identity)).length;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs a loaded local subject through the seven-strategy comparison. One private base index per configured case (each
|
|
30
|
+
* case keeps its own exact source roots), all seven treatments from that one base, a private scratch outside the
|
|
31
|
+
* target, and pre/post target-immutability enforcement. It performs NO durable artifact writes; success is returned
|
|
32
|
+
* only when every safety gate passed. A mutated target is never reverted. Issue messages are fixed text and counts.
|
|
33
|
+
*/
|
|
34
|
+
export async function executeLocalRepositorySubjectRetrievalQueryStrategyComparison(args) {
|
|
35
|
+
const { subject } = args;
|
|
36
|
+
const buildIndex = args.dependencies?.buildIndex ?? buildMyDevKitIndex;
|
|
37
|
+
const executeCase = args.dependencies?.executeCase ?? executeRetrievalQueryStrategyCaseFromPreparedIndex;
|
|
38
|
+
await assertWorkRootOutsideTarget(args.workRoot, subject.repositoryRoot);
|
|
39
|
+
const guidedExclusions = deriveGuidedIndexExclusions({
|
|
40
|
+
eligibleFiles: subject.eligibleFiles,
|
|
41
|
+
excludedFiles: [...subject.runtimeSafetyExclusions.gitIgnoredFiles, ...subject.runtimeSafetyExclusions.oversizedFiles]
|
|
42
|
+
});
|
|
43
|
+
if (subject.evaluationCases.some((evaluationCase) => validateRetrievalPrecisionRecallCase(evaluationCase).length > 0)) {
|
|
44
|
+
throw new LocalSubjectExecutionError([
|
|
45
|
+
{ code: "GROUND_TRUTH_INVALID", message: "retrieval comparison ground truth is incomplete for configured case(s)." }
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
const snapshotConfig = { targetRootPath: subject.repositoryRoot, relativeFilePaths: subject.eligibleFiles };
|
|
49
|
+
const snapshotOptions = { externalLocalSafe: { maxHashedFileBytes: subject.manifest.safetyPolicy.maxFileBytes } };
|
|
50
|
+
const before = await captureTargetSnapshot(snapshotConfig, snapshotOptions);
|
|
51
|
+
if (!before.ok) {
|
|
52
|
+
throw new LocalSubjectExecutionError([{ code: "BEFORE_SNAPSHOT_FAILED", message: `target snapshot before execution failed (${before.code}).` }]);
|
|
53
|
+
}
|
|
54
|
+
const scratch = await createPrivateScratch(args.workRoot, subject.repositoryRoot);
|
|
55
|
+
const issues = [];
|
|
56
|
+
const caseEvidence = [];
|
|
57
|
+
const eligible = new Set(subject.eligibleFiles);
|
|
58
|
+
for (const [position, evaluationCase] of subject.evaluationCases.entries()) {
|
|
59
|
+
// Short, fixed segments: private case or repository names never become directory names.
|
|
60
|
+
const caseIndexRoot = path.join(scratch.path, `i${position + 1}`);
|
|
61
|
+
const baseIndexDir = path.join(caseIndexRoot, "base");
|
|
62
|
+
const commandsRoot = path.join(scratch.path, `c${position + 1}`);
|
|
63
|
+
try {
|
|
64
|
+
// requireKit: false keeps a failed index as a result we can classify instead of an arbitrary thrown message.
|
|
65
|
+
const build = await buildIndex({
|
|
66
|
+
target: { absoluteTargetRoot: evaluationCase.absoluteTargetRoot, sourceRoots: [...evaluationCase.sourceRoots] },
|
|
67
|
+
kitCommand: args.kitCommand,
|
|
68
|
+
indexDir: baseIndexDir,
|
|
69
|
+
commandsDir: path.join(commandsRoot, "index"),
|
|
70
|
+
requireKit: false,
|
|
71
|
+
excludePaths: guidedExclusions
|
|
72
|
+
});
|
|
73
|
+
if (!build.ok) {
|
|
74
|
+
issues.push({ code: "EXECUTION_FAILED", message: "retrieval comparison index preparation failed for a configured case." });
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const evidence = await executeCase({
|
|
78
|
+
evaluationCase,
|
|
79
|
+
kitCommand: args.kitCommand,
|
|
80
|
+
baseIndexDir,
|
|
81
|
+
commandsDir: path.join(commandsRoot, "retrieval"),
|
|
82
|
+
semanticIndexesRoot: path.join(caseIndexRoot, "strategies")
|
|
83
|
+
});
|
|
84
|
+
const outside = countRetrievalQueryStrategyFilesOutsideEligibleUniverse(evidence, eligible);
|
|
85
|
+
if (outside > 0) {
|
|
86
|
+
issues.push({
|
|
87
|
+
code: "RETRIEVAL_OUTSIDE_ELIGIBLE_UNIVERSE",
|
|
88
|
+
message: `retrieval comparison exposed ${outside} file identit${outside === 1 ? "y" : "ies"} outside the eligible subject universe.`
|
|
89
|
+
});
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
caseEvidence.push(evidence);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
issues.push({ code: "EXECUTION_FAILED", message: "retrieval comparison execution failed for a configured case." });
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Attempted even after a primary failure so a target mutation is never masked.
|
|
100
|
+
let comparison = null;
|
|
101
|
+
const after = await captureTargetSnapshot(snapshotConfig, snapshotOptions);
|
|
102
|
+
if (!after.ok) {
|
|
103
|
+
issues.push({ code: "AFTER_SNAPSHOT_FAILED", message: `target snapshot after execution failed (${after.code}).` });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
comparison = compareTargetSnapshots(before.snapshot, after.snapshot);
|
|
107
|
+
if (comparison.status === "mutated") {
|
|
108
|
+
issues.push({ code: "TARGET_MUTATED", message: "the target changed during execution; it was not restored." });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const cleanupFailure = await removePrivateScratch(scratch, args.scratchIo ?? defaultPrivateScratchIo);
|
|
112
|
+
if (cleanupFailure) {
|
|
113
|
+
issues.push({ code: "SCRATCH_CLEANUP_FAILED", message: "the private scratch could not be removed." });
|
|
114
|
+
}
|
|
115
|
+
if (issues.length > 0 || !comparison) {
|
|
116
|
+
throw new LocalSubjectExecutionError(issues.length > 0 ? issues : [{ code: "EXECUTION_FAILED", message: "execution did not complete." }], {
|
|
117
|
+
immutability: comparison
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
caseEvidence,
|
|
122
|
+
immutability: { status: "unchanged", preExistingGitStatusEntryCount: comparison.preExistingGitStatusEntryCount, newMutationCount: 0 }
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { compareCodeUnits } from "../../../evaluation/indexSnapshot.js";
|
|
2
|
+
import { redactedFileList } from "../contextWindowScaling/localSubjectPrivacy.js";
|
|
3
|
+
/** Single fixed placeholder for a withheld case title. */
|
|
4
|
+
export const RETRIEVAL_QUERY_STRATEGY_REDACTED_CASE_TITLE = "<redacted case title>";
|
|
5
|
+
/** The one V1 marker stamped on every externally projected case. */
|
|
6
|
+
export const RETRIEVAL_QUERY_STRATEGY_IDENTITY_REDACTION = {
|
|
7
|
+
fileIdentities: "redacted",
|
|
8
|
+
symbolIdentities: "redacted",
|
|
9
|
+
factIdentities: "redacted",
|
|
10
|
+
warningText: "redacted",
|
|
11
|
+
caseTitle: "redacted",
|
|
12
|
+
semanticNodeIds: "redacted"
|
|
13
|
+
};
|
|
14
|
+
export const RETRIEVAL_QUERY_STRATEGY_PRIVACY_FAILURE_MESSAGE = "External-local retrieval-query-strategy-comparison privacy projection failed; no durable output was written.";
|
|
15
|
+
export const RETRIEVAL_QUERY_STRATEGY_UNEXPECTED_EXTERNAL_FAILURE_MESSAGE = "Local subject retrieval-query-strategy comparison failed (EXECUTION_FAILED): execution failed before completing (details withheld).";
|
|
16
|
+
/** One opaque placeholder per identity: counts stay exact; null stays null; an empty list stays empty. */
|
|
17
|
+
function placeholders(kind, count) {
|
|
18
|
+
return Array.from({ length: count }, (_, index) => `<redacted ${kind} ${index + 1}>`);
|
|
19
|
+
}
|
|
20
|
+
const filePlaceholders = (list) => (list === null ? null : redactedFileList(list.length));
|
|
21
|
+
const symbolPlaceholders = (list) => (list === null ? null : placeholders("symbol", list.length));
|
|
22
|
+
const factPlaceholders = (list) => (list === null ? null : placeholders("fact", list.length));
|
|
23
|
+
/** Plugin-owned text per error code; a caught Error.message is never copied into durable external evidence. */
|
|
24
|
+
const FIXED_COMPARISON_ERROR_TEXT = {
|
|
25
|
+
"ground-truth-invalid": "The case ground truth was invalid.",
|
|
26
|
+
"project-group-inconsistent": "The case source configuration was inconsistent.",
|
|
27
|
+
"project-index-failed": "The my-dev-kit index could not be prepared.",
|
|
28
|
+
"strategy-index-copy-failed": "The isolated semantic-strategy index could not be prepared.",
|
|
29
|
+
"retrieval-failed": "The retrieval strategy did not complete.",
|
|
30
|
+
"strategy-index-cleanup-failed": "The isolated semantic-strategy index could not be removed."
|
|
31
|
+
};
|
|
32
|
+
function projectEvidence(evidence) {
|
|
33
|
+
// One deterministic mapping per treatment over every file identity the evidence exposes.
|
|
34
|
+
const identities = new Set();
|
|
35
|
+
for (const file of evidence.files)
|
|
36
|
+
identities.add(file.path);
|
|
37
|
+
for (const symbol of evidence.symbols) {
|
|
38
|
+
if (symbol.file !== null)
|
|
39
|
+
identities.add(symbol.file);
|
|
40
|
+
}
|
|
41
|
+
const mapping = new Map();
|
|
42
|
+
[...identities].sort(compareCodeUnits).forEach((identity, index) => mapping.set(identity, `<redacted file ${index + 1}>`));
|
|
43
|
+
return {
|
|
44
|
+
...evidence,
|
|
45
|
+
files: evidence.files.map((file) => ({ path: mapping.get(file.path) })),
|
|
46
|
+
symbols: evidence.symbols.map((symbol, index) => ({
|
|
47
|
+
name: `<redacted symbol ${index + 1}>`,
|
|
48
|
+
nodeId: null,
|
|
49
|
+
file: symbol.file === null ? null : mapping.get(symbol.file)
|
|
50
|
+
})),
|
|
51
|
+
steps: evidence.steps.map((step) => ({ ...step }))
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function projectTreatment(treatment) {
|
|
55
|
+
return {
|
|
56
|
+
...treatment,
|
|
57
|
+
retrieval: treatment.retrieval === null
|
|
58
|
+
? null
|
|
59
|
+
: {
|
|
60
|
+
...treatment.retrieval,
|
|
61
|
+
warnings: placeholders("warning", treatment.retrieval.warnings.length),
|
|
62
|
+
steps: treatment.retrieval.steps.map((step) => ({ ...step }))
|
|
63
|
+
},
|
|
64
|
+
evidence: treatment.evidence === null ? null : projectEvidence(treatment.evidence),
|
|
65
|
+
errors: treatment.errors.map((error) => ({ code: error.code, message: FIXED_COMPARISON_ERROR_TEXT[error.code] }))
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Projects identity-bearing seven-treatment execution evidence into privacy-safe durable evidence. It performs no
|
|
70
|
+
* metric calculation and changes no count or availability.
|
|
71
|
+
*/
|
|
72
|
+
export function projectRetrievalQueryStrategyExecutionForExternalLocalPersistence(evidence) {
|
|
73
|
+
return evidence.map((entry) => {
|
|
74
|
+
const cloned = structuredClone(entry);
|
|
75
|
+
return {
|
|
76
|
+
caseId: cloned.caseId,
|
|
77
|
+
caseName: RETRIEVAL_QUERY_STRATEGY_REDACTED_CASE_TITLE,
|
|
78
|
+
benchmarkProject: cloned.benchmarkProject,
|
|
79
|
+
taskLocality: cloned.taskLocality,
|
|
80
|
+
treatments: cloned.treatments.map(projectTreatment),
|
|
81
|
+
identityRedaction: { ...RETRIEVAL_QUERY_STRATEGY_IDENTITY_REDACTION }
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/** Only identity lists change; every number, availability and fixed-vocabulary reason is carried through untouched. */
|
|
86
|
+
function projectRetrievalQualityMetricsForExternalPersistence(quality) {
|
|
87
|
+
return {
|
|
88
|
+
...quality,
|
|
89
|
+
file: {
|
|
90
|
+
...quality.file,
|
|
91
|
+
relevantRetrievedFiles: filePlaceholders(quality.file.relevantRetrievedFiles),
|
|
92
|
+
irrelevantRetrievedFiles: filePlaceholders(quality.file.irrelevantRetrievedFiles),
|
|
93
|
+
missedFiles: filePlaceholders(quality.file.missedFiles)
|
|
94
|
+
},
|
|
95
|
+
symbol: {
|
|
96
|
+
...quality.symbol,
|
|
97
|
+
relevantRetrievedSymbols: symbolPlaceholders(quality.symbol.relevantRetrievedSymbols),
|
|
98
|
+
irrelevantRetrievedSymbols: symbolPlaceholders(quality.symbol.irrelevantRetrievedSymbols),
|
|
99
|
+
missedSymbols: symbolPlaceholders(quality.symbol.missedSymbols)
|
|
100
|
+
},
|
|
101
|
+
fact: {
|
|
102
|
+
...quality.fact,
|
|
103
|
+
coveredFactIds: factPlaceholders(quality.fact.coveredFactIds),
|
|
104
|
+
uncoveredFactIds: factPlaceholders(quality.fact.uncoveredFactIds)
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Preserves every numeric scientific value and the strategy interpretation; replaces only the private identity lists
|
|
110
|
+
* inside per-treatment quality. F1, means, Pareto fronts and the best strategy are never recalculated.
|
|
111
|
+
*/
|
|
112
|
+
export function projectRetrievalQueryStrategyAnalysisForExternalLocalPersistence(analysis) {
|
|
113
|
+
const cloned = structuredClone(analysis);
|
|
114
|
+
return {
|
|
115
|
+
cases: cloned.cases.map((entry) => ({
|
|
116
|
+
...entry,
|
|
117
|
+
treatments: entry.treatments.map((treatment) => ({
|
|
118
|
+
...treatment,
|
|
119
|
+
quality: treatment.quality === null ? null : projectRetrievalQualityMetricsForExternalPersistence(treatment.quality)
|
|
120
|
+
}))
|
|
121
|
+
})),
|
|
122
|
+
scopes: cloned.scopes
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Defense in depth: fails closed if a known private file path, case title, warning, or root survived projection. It
|
|
127
|
+
* deliberately does not substring-search short symbol or fact identifiers, which would collide with ordinary words.
|
|
128
|
+
*/
|
|
129
|
+
export function assertRetrievalQueryStrategyExternalProjectionIsPrivate(projected, privateValues) {
|
|
130
|
+
const serialized = JSON.stringify(projected);
|
|
131
|
+
const variants = (value) => {
|
|
132
|
+
const forward = value.replace(/\\/g, "/");
|
|
133
|
+
const backward = value.replace(/\//g, "\\");
|
|
134
|
+
return [value, forward, backward].flatMap((entry) => [entry, JSON.stringify(entry).slice(1, -1)]);
|
|
135
|
+
};
|
|
136
|
+
const candidates = [...privateValues.filePaths, ...privateValues.caseTitles, ...privateValues.warnings, ...privateValues.privateRoots].filter((value) => value.length > 0);
|
|
137
|
+
if (candidates.some((value) => variants(value).some((variant) => serialized.includes(variant)))) {
|
|
138
|
+
throw new Error(RETRIEVAL_QUERY_STRATEGY_PRIVACY_FAILURE_MESSAGE);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const GENERIC_ISSUE_TEXT = {
|
|
142
|
+
WORK_ROOT_INSIDE_TARGET: "the output directory must be outside the local subject repository",
|
|
143
|
+
BEFORE_SNAPSHOT_FAILED: "the target snapshot before execution could not be captured",
|
|
144
|
+
AFTER_SNAPSHOT_FAILED: "the target snapshot after execution could not be captured",
|
|
145
|
+
SCRATCH_CLEANUP_FAILED: "the private scratch could not be removed",
|
|
146
|
+
GUIDED_EXCLUSION_LIMIT: "the guided-index exclusions exceeded the safe limit",
|
|
147
|
+
GUIDED_EXCLUSION_UNREPRESENTABLE: "the guided-index exclusions could not be expressed exactly"
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Safe, persistable description of an external-local failure: issue codes, counts and mutation kinds only. Execution
|
|
151
|
+
* issue messages are fixed text by construction; nothing else (no path, identity, title, or caught text) can appear.
|
|
152
|
+
*/
|
|
153
|
+
export function describeRetrievalQueryStrategyLocalSubjectFailureForPersistence(error) {
|
|
154
|
+
const parts = [];
|
|
155
|
+
const seen = new Set();
|
|
156
|
+
for (const issue of error.issues) {
|
|
157
|
+
if (issue.code === "EXECUTION_FAILED" || issue.code === "GROUND_TRUTH_INVALID" || issue.code === "RETRIEVAL_OUTSIDE_ELIGIBLE_UNIVERSE") {
|
|
158
|
+
if (!seen.has(issue.message)) {
|
|
159
|
+
seen.add(issue.message);
|
|
160
|
+
parts.push(issue.message.replace(/\.$/, ""));
|
|
161
|
+
}
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (seen.has(issue.code))
|
|
165
|
+
continue;
|
|
166
|
+
seen.add(issue.code);
|
|
167
|
+
if (issue.code === "TARGET_MUTATED") {
|
|
168
|
+
const kinds = [...new Set((error.immutability?.mutations ?? []).map((mutation) => mutation.kind))].sort();
|
|
169
|
+
parts.push(`the target changed during execution${kinds.length > 0 ? ` (${kinds.join(", ")})` : ""}; it was not restored`);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
parts.push(GENERIC_ISSUE_TEXT[issue.code] ?? `issue ${issue.code}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return `Local subject retrieval-query-strategy comparison failed (${error.code}): ${parts.join("; ")}.`;
|
|
176
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID = "retrieval-query-strategy-comparison";
|
|
2
|
+
/** The v0.8.1 comparison reuses the frozen v0.8.0 corpus lineage; there is no second benchmark corpus. */
|
|
3
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_CASES_RESOURCE = "benchmarks/contracts/warm-index-benchmark-cases.json";
|
|
4
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_PROJECT_PROFILES_RESOURCE = "benchmarks/contracts/benchmark-project-profiles.json";
|
|
5
|
+
export const retrievalQueryStrategyComparisonMetadata = {
|
|
6
|
+
id: RETRIEVAL_QUERY_STRATEGY_COMPARISON_PLUGIN_ID,
|
|
7
|
+
name: "Retrieval Query Strategy Comparison",
|
|
8
|
+
description: "Compare seven deterministic my-dev-kit retrieval query strategies on matched bundled benchmark cases without coding agents.",
|
|
9
|
+
schemaVersion: "1.0.0",
|
|
10
|
+
status: "experimental",
|
|
11
|
+
supportedTargets: ["self", "external-local"],
|
|
12
|
+
supportedOutputs: ["json", "html", "text", "artifact"]
|
|
13
|
+
};
|
|
14
|
+
/** Exactly the seven treatment levels, in the frozen strategy order. */
|
|
15
|
+
export const RETRIEVAL_QUERY_STRATEGY_COMPARISON_VARIANTS = [
|
|
16
|
+
{
|
|
17
|
+
id: "keyword-search",
|
|
18
|
+
name: "Keyword search",
|
|
19
|
+
description: "Use the task query with my-dev-kit search and expose only the search result context."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "symbol-lookup",
|
|
23
|
+
name: "Symbol lookup",
|
|
24
|
+
description: "Search for the task query, select the top candidate, and inspect that exact graph node with lookup."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "graph-neighborhood",
|
|
28
|
+
name: "Graph neighborhood",
|
|
29
|
+
description: "Search for the task query, select the top candidate, and retrieve its bounded graph neighborhood with slice."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "source-slice",
|
|
33
|
+
name: "Source slice",
|
|
34
|
+
description: "Search for the task query, select the top candidate, and retrieve bounded numbered source for that node."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "data-model-graph",
|
|
38
|
+
name: "Data-model graph",
|
|
39
|
+
description: "Match task-query tokens against data-model graph labels and inspect the best deterministic entity or field."
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "model-view-lineage",
|
|
43
|
+
name: "Model-view lineage",
|
|
44
|
+
description: "Match the task query to a data-model entity or field and retrieve its conservative static model-to-view lineage."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "combined-graph-guided",
|
|
48
|
+
name: "Combined graph-guided",
|
|
49
|
+
description: "Use the v0.8.0 compatibility workflow: search, lookup, slice, then bounded source for the top candidate."
|
|
50
|
+
}
|
|
51
|
+
];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { RETRIEVAL_QUERY_STRATEGY_IDS } from "../../../evaluation/retrievalQueryStrategies.js";
|
|
2
|
+
/** Projects already-calculated scientific values onto generic metrics. Owns no formula. */
|
|
3
|
+
export function toRetrievalQueryStrategyOutcomeMetrics(treatment, caseId) {
|
|
4
|
+
const base = { variantId: treatment.strategyId, caseId };
|
|
5
|
+
const quality = treatment.quality;
|
|
6
|
+
const ratio = (id, name, metric) => ({
|
|
7
|
+
...base,
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
value: metric?.value ?? null,
|
|
11
|
+
unit: "ratio"
|
|
12
|
+
});
|
|
13
|
+
const count = (id, name, value) => ({
|
|
14
|
+
...base,
|
|
15
|
+
id,
|
|
16
|
+
name,
|
|
17
|
+
value: value ?? null,
|
|
18
|
+
unit: "count"
|
|
19
|
+
});
|
|
20
|
+
return [
|
|
21
|
+
ratio("file-precision", "File precision", quality?.file.precision),
|
|
22
|
+
ratio("file-recall", "File recall", quality?.file.recall),
|
|
23
|
+
ratio("file-f1", "File F1", treatment.fileF1),
|
|
24
|
+
ratio("symbol-precision", "Symbol precision", quality?.symbol.precision),
|
|
25
|
+
ratio("symbol-recall", "Symbol recall", quality?.symbol.recall),
|
|
26
|
+
ratio("symbol-f1", "Symbol F1", treatment.symbolF1),
|
|
27
|
+
ratio("fact-coverage", "Fact coverage", quality?.fact.coverage),
|
|
28
|
+
ratio("irrelevant-context-ratio", "Irrelevant context ratio", quality?.irrelevantContextRatio),
|
|
29
|
+
{ ...base, id: "retrieved-token-count", name: "Retrieved token count", value: quality?.retrievedTokenCount ?? null, unit: "tokens" },
|
|
30
|
+
count("missed-file-count", "Missed file count", quality?.file.missedFileCount),
|
|
31
|
+
count("missed-symbol-count", "Missed symbol count", quality?.symbol.missedSymbolCount),
|
|
32
|
+
count("uncovered-fact-count", "Uncovered fact count", quality?.fact.uncoveredFactCount),
|
|
33
|
+
count("irrelevant-retrieved-file-count", "Irrelevant retrieved file count", quality?.file.irrelevantRetrievedFiles?.length),
|
|
34
|
+
count("irrelevant-retrieved-symbol-count", "Irrelevant retrieved symbol count", quality?.symbol.irrelevantRetrievedSymbols?.length)
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
/** Per scope: one matched-case count, then four objective means per strategy in canonical order. No best/rank/score metric. */
|
|
38
|
+
export function toRetrievalQueryStrategyRunMetrics(analysis) {
|
|
39
|
+
const metrics = [];
|
|
40
|
+
for (const scope of analysis.scopes) {
|
|
41
|
+
const { scopeId } = scope;
|
|
42
|
+
metrics.push({
|
|
43
|
+
id: `${scopeId}-comparison-case-count`,
|
|
44
|
+
name: `${scopeId} matched comparison cases`,
|
|
45
|
+
value: scope.comparisonCaseCount,
|
|
46
|
+
unit: "count"
|
|
47
|
+
});
|
|
48
|
+
for (const strategyId of RETRIEVAL_QUERY_STRATEGY_IDS) {
|
|
49
|
+
const objectives = scope.strategySummaries.find((summary) => summary.strategyId === strategyId)?.objectives ?? null;
|
|
50
|
+
metrics.push({ id: `${scopeId}-mean-file-f1`, name: `${scopeId} mean file F1`, value: objectives?.meanFileF1 ?? null, unit: "ratio", variantId: strategyId }, { id: `${scopeId}-mean-symbol-f1`, name: `${scopeId} mean symbol F1`, value: objectives?.meanSymbolF1 ?? null, unit: "ratio", variantId: strategyId }, { id: `${scopeId}-mean-fact-coverage`, name: `${scopeId} mean fact coverage`, value: objectives?.meanFactCoverage ?? null, unit: "ratio", variantId: strategyId }, {
|
|
51
|
+
id: `${scopeId}-mean-retrieved-token-count`,
|
|
52
|
+
name: `${scopeId} mean retrieved token count`,
|
|
53
|
+
value: objectives?.meanRetrievedTokenCount ?? null,
|
|
54
|
+
unit: "tokens",
|
|
55
|
+
variantId: strategyId
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return metrics;
|
|
60
|
+
}
|