@dailephd/my-dev-kit-lab 0.8.0 → 0.8.1
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 +18 -0
- package/README.md +6 -4
- package/dist/src/cli/help.js +20 -1
- package/dist/src/commands/runExperimentDescribeCommand.js +8 -1
- package/dist/src/commands/runExperimentRunCommand.js +90 -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 +59 -51
- package/dist/src/evaluation/runSemanticRetrievalStrategy.js +303 -0
- package/dist/src/experiments/defaultRegistry.js +2 -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/buildPluginExperimentReport.js +25 -2
- package/dist/src/report/experiments/buildRetrievalQueryStrategyComparisonReport.js +91 -0
- package/dist/src/report/experiments/index.js +4 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +9 -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/docs/METRICS.md +122 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,24 @@ All notable changes to my-dev-kit-lab are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.1] - 2026-10-05
|
|
8
|
+
|
|
9
|
+
### Retrieval query strategy comparison
|
|
10
|
+
|
|
11
|
+
- Added the experimental `retrieval-query-strategy-comparison` plugin with seven fixed variants: `keyword-search`, `symbol-lookup`, `graph-neighborhood`, `source-slice`, `data-model-graph`, `model-view-lineage`, and `combined-graph-guided`. It runs no coding agent.
|
|
12
|
+
- Bundled mode reuses the frozen 12-case warm-index corpus; every selected case runs all seven strategies, and `--case` and `--benchmark-project` narrow cases or projects, never strategies.
|
|
13
|
+
- Added strategy-neutral identity evidence and preserved the v0.8.0 retrieval-quality formulas through one generic identity-evidence metric owner.
|
|
14
|
+
- Added balanced file and symbol F1: `2TP / (2TP + FP + FN)`.
|
|
15
|
+
- Added matched-complete-case macro aggregation across the `overall`, `localized`, `cross-module`, and `broad-change` scopes.
|
|
16
|
+
- Added four-objective Pareto comparison: maximize mean file F1, maximize mean symbol F1, maximize mean fact coverage, and minimize mean retrieved tokens.
|
|
17
|
+
- A unique best strategy exists only for a single-member Pareto front; otherwise the report shows a tradeoff. There is no composite score and no total ranking.
|
|
18
|
+
- Added `retrieval-query-strategy-comparison-execution.json` and `retrieval-query-strategy-comparison-analysis.json`, plus a typed `report.json`, `report.txt`, and `report.html` presentation of the precomputed analysis.
|
|
19
|
+
- Added external-local `LocalRepositorySubject` execution: a private index per configured case, exact exclusions, isolated semantic copies, an eligible-universe check, target immutability, failure atomicity, science before redaction, and privacy-safe durable output.
|
|
20
|
+
- Registered list, describe, and run support in the installed CLI with exact bundled and external-local argument boundaries.
|
|
21
|
+
- Extended packed-package acceptance to cover discovery, bundled execution, reports, rejection and failure paths, privacy, immutability, and real `@dailephd/my-dev-kit@1.12.5` external-local compatibility.
|
|
22
|
+
- Validated the legitimate 0.8.0 to 0.8.1 package metadata transition in a disposable candidate copy with no transition defects.
|
|
23
|
+
- Not included: context packs, coding-agent evaluation, automatic strategy selection, scalar or composite scoring, ordinal ranking, and plots or screenshots.
|
|
24
|
+
|
|
7
25
|
## [0.8.0] - 2026-10-04
|
|
8
26
|
|
|
9
27
|
### Retrieval precision/recall
|
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@ my-dev-kit provides local repository indexing and graph-guided retrieval. my-dev
|
|
|
6
6
|
|
|
7
7
|
Whole-ecosystem workflows are centralized in [my-dev-kit/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md). Its [command-surface compatibility map](https://github.com/dailephd/my-dev-kit/blob/main/docs/ECOSYSTEM_DEVELOPMENT_WORKFLOWS.md#915-command-surface-compatibility-map) documents which Lab outputs can safely seed my-dev-kit investigation, Observer reference work, or Orchestrator evidence, and which look-alike artifacts are not direct inputs.
|
|
8
8
|
|
|
9
|
-
The latest release is v0.8.
|
|
9
|
+
The latest release is v0.8.1 (retrieval query strategy comparison), and the published package and installed CLI are version 0.8.1. The previous release is v0.8.0 (retrieval precision/recall). The v0.4.5 context-integrity evaluation remains intentionally frozen against the published `@dailephd/my-dev-kit@1.10.4` and `@dailephd/my-dev-kit-orchestrator@1.2.3` contracts; those versions are historical validation baselines, not a statement that they are the ecosystem's current releases. See [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md) for current state.
|
|
10
10
|
|
|
11
|
-
v0.8.
|
|
11
|
+
v0.8.2 (context-pack generation experiments) is the next planned milestone.
|
|
12
12
|
|
|
13
13
|
v0.4.6 adds a supported `my-dev-kit-lab` installed CLI router (`--help`, `--version`, `security validate`, `audit`, the `experiment` family, `report render`, `plots generate`, `gallery build`, `demo final`, and the historical direct final-demo invocation form), a writable lab workspace model kept separate from the installed package and the inspected target, and a permanent packed-tarball installation/execution acceptance gate (`npm run verify:packed-package`). See [docs/ROADMAP.md](docs/ROADMAP.md) and [docs/CURRENT_STATE.md](docs/CURRENT_STATE.md) for status detail and [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the runtime path model. The "Installed CLI" section below documents the shipped command surface; the source-checkout `npm run` workflow in Quickstart remains available for contributors.
|
|
14
14
|
|
|
@@ -23,6 +23,7 @@ v0.4.6 adds a supported `my-dev-kit-lab` installed CLI router (`--help`, `--vers
|
|
|
23
23
|
- **Run context-window scaling over synthetic repositories (v0.7.1, released):** `context-window-scaling` uses the bundled fixed corpus by default, or `--synthetic-config <path>` (a `SyntheticRepositoryConfigV1` JSON file) to generate disposable deterministic TypeScript/Python repositories beneath the run output. The option is a command input selector, not plugin scientific config; it is mutually exclusive with `--case`, and the scientific semantics, execution artifact, reports, and plots are unchanged. See [docs/COMMANDS.md](docs/COMMANDS.md) and [docs/WORKFLOWS.md](docs/WORKFLOWS.md).
|
|
24
24
|
- **Run context-window scaling over a local Git repository (v0.7.2, released):** `context-window-scaling` can also run against an explicitly selected local Git repository with `--target <repository> --local-subject-config <path>` (a `LocalRepositorySubjectConfigV1` JSON file). The Lab treats the repository as read-only and compares its state before and after the run, builds contexts only from files Git does not ignore (ignored, oversized (over 1 MiB), symbolic-link, and other unsafe entries are excluded), requires the output directory to be outside the repository, and keeps the repository path, source text, and file names out of durable artifacts (file identities appear as numbered `<redacted file N>` placeholders). A `local-repository-subject-manifest.json` records the logical subject ID, the full Git commit, the branch, and aggregate size metadata. This measures operational context fit only, not retrieval precision, recall, or ranking quality. It is part of the current package. See [docs/COMMANDS.md](docs/COMMANDS.md) and [docs/WORKFLOWS.md](docs/WORKFLOWS.md).
|
|
25
25
|
- **Measure retrieval precision and recall (v0.8.0, released):** the `retrieval-precision-recall` plugin runs the existing my-dev-kit search, lookup, slice, and source lifecycle for each case and compares the retrieved files and symbols with a deterministic answer key, without invoking any agent. It reports file precision and recall, symbol precision and recall, fact coverage (from explicit fact-to-context mappings), irrelevant context ratio, the estimated retrieved token count, and the missed and irrelevant identities, as `retrieval-precision-recall-execution.json` plus `report.json`, `report.html`, and `report.txt`. By default it runs a bundled 12-case corpus over two benchmark projects against the Lab itself. It can also run over an explicitly selected local Git repository with `--target <repository> --local-subject-config <path>`; in that mode the repository is treated as read-only and durable outputs redact the repository path and file, symbol, and fact identities while keeping the numeric evidence. These are set-based measures with no ranking, winner, or composite score. See [docs/COMMANDS.md](docs/COMMANDS.md), [docs/WORKFLOWS.md](docs/WORKFLOWS.md), and [docs/METRICS.md](docs/METRICS.md).
|
|
26
|
+
- **Compare retrieval query strategies (v0.8.1, released):** the `retrieval-query-strategy-comparison` plugin compares seven fixed my-dev-kit retrieval strategies without running any coding agent: `keyword-search`, `symbol-lookup`, `graph-neighborhood`, `source-slice`, `data-model-graph`, `model-view-lineage`, and `combined-graph-guided`. In bundled mode it uses the same frozen 12-case warm-index benchmark corpus as `retrieval-precision-recall`, and every selected case receives all seven strategies (filters narrow cases or projects, never strategies). File and symbol quality keeps the inherited v0.8.0 set-based semantics and adds balanced file and symbol F1 (`2TP / (2TP + FP + FN)`); fact coverage and the estimated retrieved token count complete the four comparison objectives. Strategies are compared over matched complete cases only, as macro means for four scopes (`overall`, `localized`, `cross-module`, `broad-change`, reusing each case's existing task-locality label), using Pareto dominance on mean file F1, mean symbol F1, mean fact coverage (all maximized) and mean retrieved tokens (minimized). A best strategy is named only when the Pareto front has a single member; when several strategies remain nondominated the report states a tradeoff. There is no scalar or composite score and no ordinal ranking. The plugin can also run over an explicitly selected local Git repository through the existing `LocalRepositorySubject` boundary with `--target <repository> --local-subject-config <path>`; the repository stays read-only and durable output redacts private file, symbol, fact, semantic-node, warning, and case-title identities while preserving the numeric results. It writes `retrieval-query-strategy-comparison-execution.json`, `retrieval-query-strategy-comparison-analysis.json`, and `report.json`, `report.html`, and `report.txt`. No context-pack behavior is included. See [docs/COMMANDS.md](docs/COMMANDS.md), [docs/WORKFLOWS.md](docs/WORKFLOWS.md), and [docs/METRICS.md](docs/METRICS.md#retrieval-query-strategy-comparison-metrics-v081).
|
|
26
27
|
- **Assess affected-neighborhood evidence (v0.6.1, released; retained in the current package):** using the baseline my-dev-kit graph of the same prepared warm index (loaded once per session), each task's confirmed modified or missing indexed files, and the baseline symbols those files contain, are mapped to graph nodes. The affected neighborhood is those seed nodes plus their direct one-hop neighbors (edges of any kind, either direction). The task's `expectedFiles` and file-scoped `expectedSymbols` are mapped to graph nodes and compared with that neighborhood, which yields six numeric warm-side metrics (`changedFileCount`, `changedSymbolCount`, `affectedNodeCount`, `affectedEdgeCount`, `taskOverlapCount`, `taskOverlapPercent`) plus categorical evidence: a `related`, `unrelated`, or `unknown` relationship and a `recommended`, `not-indicated`, or `unknown` `reindexRecommendation`. Incomplete evidence is reported as `unknown`, never as unrelated. The assessment is recorded additively in `warm-index-execution.json` and shown in the existing `report.json`/`report.txt`/`report.html` warm-index reports. It is observational only: retrieval, execution status, and provider status are unchanged, nothing is reindexed automatically, and no CLI flag, experiment plugin, plot, or gallery item was added. See [docs/METRICS.md](docs/METRICS.md#affected-neighborhood-metrics-v061) and [docs/WORKFLOWS.md](docs/WORKFLOWS.md#warm-index-reuse-experiment).
|
|
27
28
|
- **Audit repository health:** run conservative code-rot detectors for TypeScript/JavaScript, Python, Java, and Kotlin, or adapt security findings into the common audit report.
|
|
28
29
|
- **Validate CLI/package security:** inspect dependencies, package contents, path and subprocess boundaries, malformed inputs, optional static scanners, and bounded fuzz targets.
|
|
@@ -242,8 +243,9 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
|
|
|
242
243
|
- Claude and Codex token totals are used only when the CLI/run exposes them; missing provider token evidence stays explicitly unavailable (never zero, and never substituted with an estimated context-token count)
|
|
243
244
|
- Codex and Claude campaign runs can produce timeouts, invalid-output, agent-unavailable, or agent-limit-reached outcomes; these are structured provider states, not warm-index infrastructure failures
|
|
244
245
|
- Small projects may make raw-full-file cheaper than my-dev-kit-guided; larger localized tasks are where my-dev-kit is expected to become more useful
|
|
245
|
-
- The generic experiment-plugin framework has
|
|
246
|
-
- `retrieval-precision-recall` (v0.8.0) measures set-based retrieval quality, not ranked retrieval: it has no ranking metrics, no retrieval-strategy comparison
|
|
246
|
+
- The generic experiment-plugin framework has six registered plugins: `context-strategy-comparison`, `warm-index-reuse`, `incremental-change-staleness`, `context-window-scaling`, `retrieval-precision-recall`, and `retrieval-query-strategy-comparison`. The v0.7.0 context-window-scaling plugin is released; v0.7.1 lets it also run over caller-supplied synthetic repositories; v0.7.2 lets it run over an explicitly selected local Git repository. The fifth, `retrieval-precision-recall`, was added in v0.8.0. The sixth, `retrieval-query-strategy-comparison`, was added in v0.8.1; agent-success plugins remain planned.
|
|
247
|
+
- `retrieval-precision-recall` (v0.8.0) measures set-based retrieval quality, not ranked retrieval: it has no ranking metrics, no retrieval-strategy comparison of its own (the separate `retrieval-query-strategy-comparison` plugin added in v0.8.1 compares strategies), no winner or composite score, and no real agents. Its lifecycle expands only the top search candidate. An external-local run fails as a whole if any case fails a safety or execution check, and then writes no normal artifact or report family. Durable external-local identities are redacted, although the console summary of a successful run can still show the physical output directory. Platform path-length limits can make a run with very long paths fail cleanly instead of executing.
|
|
248
|
+
- `retrieval-query-strategy-comparison` (v0.8.1) runs no coding agent and does not evaluate answer quality. Its retrieved token count is a context-size estimate, not provider billing telemetry. Semantic strategies (`data-model-graph`, `model-view-lineage`) can truthfully be partial or unavailable for a case, and a case is then excluded from the matched cross-strategy comparison for every strategy while its per-strategy evidence is kept. The Pareto front is a set of nondominated strategies in canonical strategy order, not an ordinal ranking. The plugin reports no significance test or confidence interval, selects no strategy automatically, and generates no context pack. External-local mode rejects `--case` and `--benchmark-project`, builds one private base index per configured case, and fails as a whole, writing no normal artifact or report family, if any safety or privacy check fails.
|
|
247
249
|
- `warm-index-reuse` without `--campaign` still uses the deterministic fake agent only. With `--campaign`, exactly one real provider (Codex or Claude) runs per campaign preset — there is no multi-agent matrix, retry, provider switching, or scheduler. The v0.5.1 expanded corpus is deterministic fake-agent benchmark evidence when run without `--campaign`. No warm-index mode calculates a token-savings percentage, break-even task, winner, or ranking, and component-duration sums are not wall-clock latency
|
|
248
250
|
- The current release does not guarantee token savings; it produces auditable evidence for specific cases, targets, agents, and strategies
|
|
249
251
|
- Context-window scaling uses estimated tokens (`ceil(characters / 4)`) and experiment budgets, not actual provider tokenizer or model-window limits. Its deterministic fake-agent correctness check does not measure semantic sensitivity to the supplied context.
|
package/dist/src/cli/help.js
CHANGED
|
@@ -156,7 +156,7 @@ export function renderExperimentRunHelp() {
|
|
|
156
156
|
" --case <ids> Comma-separated case ids to run",
|
|
157
157
|
" --benchmark-project <ids> Comma-separated benchmark project ids to run",
|
|
158
158
|
"",
|
|
159
|
-
"my-dev-kit command override (warm-index-reuse, incremental-change-staleness,
|
|
159
|
+
"my-dev-kit command override (warm-index-reuse, incremental-change-staleness, context-window-scaling, retrieval-precision-recall, and retrieval-query-strategy-comparison):",
|
|
160
160
|
" --kit-command <command> my-dev-kit command used by the selected experiment plugin",
|
|
161
161
|
" (defaults: warm-index-reuse and context-window-scaling use",
|
|
162
162
|
" npx @dailephd/my-dev-kit@latest;",
|
|
@@ -200,6 +200,25 @@ export function renderExperimentRunHelp() {
|
|
|
200
200
|
" case; the repository is never modified and durable output omits its",
|
|
201
201
|
" path, source text, and file, symbol and fact identities.",
|
|
202
202
|
"",
|
|
203
|
+
"retrieval-query-strategy-comparison only:",
|
|
204
|
+
" Bundled mode (default) runs the frozen 12-case corpus and accepts",
|
|
205
|
+
" only --out, --case, --benchmark-project and --kit-command; --case",
|
|
206
|
+
" and --benchmark-project narrow the case set. All seven retrieval",
|
|
207
|
+
" query strategies always run for every case; there is no strategy",
|
|
208
|
+
" option. External-local mode requires an explicit --target together",
|
|
209
|
+
" with --local-subject-config and accepts only --out, --target,",
|
|
210
|
+
" --local-subject-config and --kit-command; --case and",
|
|
211
|
+
" --benchmark-project are rejected because the local-subject config",
|
|
212
|
+
" owns the case set. One private base index is built per configured",
|
|
213
|
+
" case; the semantic strategies use isolated copies of it; the",
|
|
214
|
+
" repository is never modified and durable output withholds private",
|
|
215
|
+
" file, symbol, fact, semantic-node, warning and case-title",
|
|
216
|
+
" identities. Reports compare the strategies for overall, localized,",
|
|
217
|
+
" cross-module and broad-change cases. A best strategy is named only",
|
|
218
|
+
" when the Pareto front has a single member; multiple nondominated",
|
|
219
|
+
" strategies are reported as a tradeoff. No scalar score or total",
|
|
220
|
+
" ranking exists.",
|
|
221
|
+
"",
|
|
203
222
|
"warm-index-reuse only:",
|
|
204
223
|
" Legacy/default mode uses the deterministic fake agent only;",
|
|
205
224
|
" agent options below are not accepted by this plugin.",
|
|
@@ -136,6 +136,9 @@ function describeTargetBehavior(plugin) {
|
|
|
136
136
|
if (plugin.metadata.id === "retrieval-precision-recall") {
|
|
137
137
|
return "Two subject modes. Bundled (default): the frozen bundled 12-case warm-index corpus against the Lab itself, filterable with --case and --benchmark-project. External local repository: an explicitly selected local Git worktree via --target <path> together with --local-subject-config <path> (a LocalRepositorySubjectConfigV1 JSON file whose cases carry a complete retrieval answer key with explicit fact-to-context mappings); the repository is never modified, the output directory must be outside it, one private index is built per configured case, and durable output withholds the repository path, source text, and file, symbol and fact identities. Bundled filters are not accepted in external mode.";
|
|
138
138
|
}
|
|
139
|
+
if (plugin.metadata.id === "retrieval-query-strategy-comparison") {
|
|
140
|
+
return "Two subject modes. Bundled (default): the frozen 12-case warm-index corpus compares all seven retrieval query strategies and may be narrowed with --case and --benchmark-project. External local repository: an explicitly selected local Git worktree via --target <path> together with --local-subject-config <path>; the local-subject config owns the case set, one private base index is built per configured case, core strategies share that base, semantic strategies use isolated copies, the repository is never modified, and durable output withholds private file, symbol, fact, semantic-node, warning and case-title identities while preserving numeric scientific results. Bundled filters are not accepted in external-local mode.";
|
|
141
|
+
}
|
|
139
142
|
if (supportsExternal) {
|
|
140
143
|
return "Runs against the current lab repository when --target is omitted, or against an explicit local target project when --target <path> is provided. Outputs stay under lab-controlled output directories by default.";
|
|
141
144
|
}
|
|
@@ -161,7 +164,8 @@ function describeExamples(plugin) {
|
|
|
161
164
|
// Only advertise an explicit --target example for plugins that support external targets.
|
|
162
165
|
if (plugin.metadata.supportedTargets.includes("external-local") &&
|
|
163
166
|
plugin.metadata.id !== "context-window-scaling" &&
|
|
164
|
-
plugin.metadata.id !== "retrieval-precision-recall"
|
|
167
|
+
plugin.metadata.id !== "retrieval-precision-recall" &&
|
|
168
|
+
plugin.metadata.id !== "retrieval-query-strategy-comparison") {
|
|
165
169
|
examples.push(`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target "Z:\\Users\\newuser\\Projects\\my-dev-kit-v1"${agentOptions}${screenshotOption}`);
|
|
166
170
|
}
|
|
167
171
|
// Only advertise campaign examples for plugins that declare the campaignPreset config field.
|
|
@@ -171,6 +175,9 @@ function describeExamples(plugin) {
|
|
|
171
175
|
if (plugin.metadata.id === "retrieval-precision-recall") {
|
|
172
176
|
examples.push(`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --case <case-id> --out <run-dir>`, `my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target <local-git-repository> --local-subject-config <path-to-local-subject-config.json> --out <run-dir-outside-the-repository>`);
|
|
173
177
|
}
|
|
178
|
+
if (plugin.metadata.id === "retrieval-query-strategy-comparison") {
|
|
179
|
+
examples.push(`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --case <case-id> --out <run-dir>`, `my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target <local-git-repository> --local-subject-config <path-to-local-subject-config.json> --out <run-dir-outside-the-repository>`);
|
|
180
|
+
}
|
|
174
181
|
if (plugin.metadata.id === "context-window-scaling") {
|
|
175
182
|
examples.push(`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --synthetic-config <path-to-config.json> --out <run-dir>`, `my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target <local-git-repository> --local-subject-config <path-to-local-subject-config.json> --out <run-dir-outside-the-repository>`);
|
|
176
183
|
}
|
|
@@ -5,7 +5,7 @@ import { parseAgentId } from "../agents/agentRegistry.js";
|
|
|
5
5
|
import { readBenchmarkProjectProfiles, readEvaluationCases } from "../evaluation/index.js";
|
|
6
6
|
import { summarizeRetrievalGroundTruthIssuesSafely, validateRetrievalPrecisionRecallCorpus } from "../evaluation/retrievalQuality/index.js";
|
|
7
7
|
import { loadLocalRepositorySubject } from "../evaluation/localRepositorySubject/index.js";
|
|
8
|
-
import { contextStrategyComparisonPlugin, contextWindowScalingPlugin, createDefaultExperimentPluginRegistry, getWarmIndexCampaignPreset, incrementalChangeStalenessPlugin, parseContextBudgetsCliValue, parseWarmIndexCampaignPresetId, resolveExperimentTarget, retrievalPrecisionRecallPlugin, runExperiment, warmIndexReusePlugin } from "../experiments/index.js";
|
|
8
|
+
import { contextStrategyComparisonPlugin, contextWindowScalingPlugin, createDefaultExperimentPluginRegistry, getWarmIndexCampaignPreset, incrementalChangeStalenessPlugin, parseContextBudgetsCliValue, parseWarmIndexCampaignPresetId, resolveExperimentTarget, retrievalPrecisionRecallPlugin, retrievalQueryStrategyComparisonPlugin, runExperiment, warmIndexReusePlugin } from "../experiments/index.js";
|
|
9
9
|
import { prepareSyntheticContextWindowScalingInputs } from "../experiments/plugins/contextWindowScaling/index.js";
|
|
10
10
|
import { projectExternalLocalTarget, projectRunForExternalLocalPersistence, } from "../experiments/plugins/contextWindowScaling/localSubjectPrivacy.js";
|
|
11
11
|
import { assertWorkRootOutsideTarget } from "../experiments/plugins/contextWindowScaling/localSubjectScratch.js";
|
|
@@ -59,6 +59,11 @@ const RETRIEVAL_PRECISION_RECALL_ALLOWED_FLAGS = ["--experiment", "--out", "--ca
|
|
|
59
59
|
// External-local mode (--target with --local-subject-config) is a different subject source: the local-subject config owns
|
|
60
60
|
// the case set, so the bundled corpus filters are not accepted there.
|
|
61
61
|
const RETRIEVAL_PRECISION_RECALL_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
62
|
+
// retrieval-query-strategy-comparison reuses the same frozen bundled corpus. All seven strategies always run, so there
|
|
63
|
+
// is no strategy option. External-local mode again accepts only the local-subject source flags.
|
|
64
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_CASES_RESOURCE = "benchmarks/contracts/warm-index-benchmark-cases.json";
|
|
65
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_ALLOWED_FLAGS = ["--experiment", "--out", "--case", "--benchmark-project", "--kit-command"];
|
|
66
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
62
67
|
export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
63
68
|
try {
|
|
64
69
|
const args = parseRunExperimentArgs(argv);
|
|
@@ -309,7 +314,8 @@ export function parseRunExperimentArgs(argv) {
|
|
|
309
314
|
warmIndexReusePlugin.metadata.id,
|
|
310
315
|
incrementalChangeStalenessPlugin.metadata.id,
|
|
311
316
|
contextWindowScalingPlugin.metadata.id,
|
|
312
|
-
retrievalPrecisionRecallPlugin.metadata.id
|
|
317
|
+
retrievalPrecisionRecallPlugin.metadata.id,
|
|
318
|
+
retrievalQueryStrategyComparisonPlugin.metadata.id
|
|
313
319
|
];
|
|
314
320
|
if (experimentId === retrievalPrecisionRecallPlugin.metadata.id) {
|
|
315
321
|
// Mode matrix, no precedence rules: neither flag = bundled; both = external-local; exactly one is rejected.
|
|
@@ -335,6 +341,32 @@ export function parseRunExperimentArgs(argv) {
|
|
|
335
341
|
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
336
342
|
}
|
|
337
343
|
}
|
|
344
|
+
if (experimentId === retrievalQueryStrategyComparisonPlugin.metadata.id) {
|
|
345
|
+
// Mode matrix, no precedence rules: neither flag = bundled; both = external-local; exactly one is rejected.
|
|
346
|
+
if (targetPath !== undefined && localSubjectConfigPath === undefined) {
|
|
347
|
+
throw new Error(`External ${experimentId} targets require --local-subject-config.`);
|
|
348
|
+
}
|
|
349
|
+
if (localSubjectConfigPath !== undefined && targetPath === undefined) {
|
|
350
|
+
throw new Error(`--local-subject-config requires an external --target for ${experimentId}.`);
|
|
351
|
+
}
|
|
352
|
+
const externalMode = targetPath !== undefined;
|
|
353
|
+
if (externalMode && (seenFlags.includes("--case") || seenFlags.includes("--benchmark-project"))) {
|
|
354
|
+
throw new Error("--case and --benchmark-project cannot be combined with --local-subject-config; the local subject config owns the case set.");
|
|
355
|
+
}
|
|
356
|
+
const allowedFlags = externalMode
|
|
357
|
+
? RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_ALLOWED_FLAGS
|
|
358
|
+
: RETRIEVAL_QUERY_STRATEGY_COMPARISON_ALLOWED_FLAGS;
|
|
359
|
+
const unsupported = [...new Set(seenFlags.filter((flag) => !allowedFlags.includes(flag)))];
|
|
360
|
+
if (unsupported.length > 0) {
|
|
361
|
+
throw new Error(`${unsupported.join(", ")} ${unsupported.length === 1 ? "is" : "are"} not supported for --experiment ${experimentId}${externalMode ? " in external-local mode" : ""}; supported options: ${allowedFlags.join(", ")}.`);
|
|
362
|
+
}
|
|
363
|
+
if (seenFlags.includes("--case") && caseIds.length === 0) {
|
|
364
|
+
throw new Error("--case must list at least one case id.");
|
|
365
|
+
}
|
|
366
|
+
if (seenFlags.includes("--benchmark-project") && benchmarkProjects.length === 0) {
|
|
367
|
+
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
338
370
|
if (contextBudgets !== undefined && experimentId !== contextWindowScalingPlugin.metadata.id) {
|
|
339
371
|
throw new Error(`--context-budgets is only supported for --experiment ${contextWindowScalingPlugin.metadata.id}.`);
|
|
340
372
|
}
|
|
@@ -343,8 +375,9 @@ export function parseRunExperimentArgs(argv) {
|
|
|
343
375
|
}
|
|
344
376
|
if (localSubjectConfigPath !== undefined &&
|
|
345
377
|
experimentId !== contextWindowScalingPlugin.metadata.id &&
|
|
346
|
-
experimentId !== retrievalPrecisionRecallPlugin.metadata.id
|
|
347
|
-
|
|
378
|
+
experimentId !== retrievalPrecisionRecallPlugin.metadata.id &&
|
|
379
|
+
experimentId !== retrievalQueryStrategyComparisonPlugin.metadata.id) {
|
|
380
|
+
throw new Error(`--local-subject-config is only supported for --experiment ${contextWindowScalingPlugin.metadata.id}, ${retrievalPrecisionRecallPlugin.metadata.id}, or ${retrievalQueryStrategyComparisonPlugin.metadata.id}.`);
|
|
348
381
|
}
|
|
349
382
|
if (experimentId === contextWindowScalingPlugin.metadata.id) {
|
|
350
383
|
const unsupported = [...new Set(seenFlags.filter((flag) => !CONTEXT_WINDOW_SCALING_ALLOWED_FLAGS.includes(flag)))];
|
|
@@ -466,8 +499,61 @@ async function loadPluginInputs(args, toolRoot, context, outputRoot) {
|
|
|
466
499
|
? loadRetrievalPrecisionRecallLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
467
500
|
: loadRetrievalPrecisionRecallInputs(args, toolRoot, context);
|
|
468
501
|
}
|
|
502
|
+
if (args.experimentId === retrievalQueryStrategyComparisonPlugin.metadata.id) {
|
|
503
|
+
return args.localSubjectConfigPath !== undefined
|
|
504
|
+
? loadRetrievalQueryStrategyComparisonLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
505
|
+
: loadRetrievalQueryStrategyComparisonInputs(args, toolRoot, context);
|
|
506
|
+
}
|
|
469
507
|
return undefined;
|
|
470
508
|
}
|
|
509
|
+
// External-local retrieval-query-strategy-comparison: same loader and safety order as retrieval-precision-recall, with
|
|
510
|
+
// the plugin's own config validation. Ground-truth issues carry logical case ids and counts only.
|
|
511
|
+
async function loadRetrievalQueryStrategyComparisonLocalSubjectInputs(args, toolRoot, context, outputRoot) {
|
|
512
|
+
const validation = retrievalQueryStrategyComparisonPlugin.validateConfig(args.config);
|
|
513
|
+
if (!validation.valid || !validation.config) {
|
|
514
|
+
throw new Error(`Invalid retrieval query strategy comparison config: ${validation.errors.join("; ")}`);
|
|
515
|
+
}
|
|
516
|
+
if (outputRoot === undefined || args.localSubjectConfigPath === undefined) {
|
|
517
|
+
throw new Error("Internal error: external-local runs require a resolved experiment output root and config path.");
|
|
518
|
+
}
|
|
519
|
+
const target = resolveExperimentTarget(args.targetPath, toolRoot);
|
|
520
|
+
if (target.isSelf || target.kind !== "external-local") {
|
|
521
|
+
throw new Error("Local-repository subject mode requires an external-local target; --target must not be the Lab repository.");
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
await assertWorkRootOutsideTarget(outputRoot, target.targetRoot);
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
throw new Error("Experiment output root must not be inside the external target project.");
|
|
528
|
+
}
|
|
529
|
+
const configPath = path.resolve(context.invocationCwd, args.localSubjectConfigPath);
|
|
530
|
+
const localSubjectConfig = await readJsonConfigFile("--local-subject-config", configPath, args.localSubjectConfigPath);
|
|
531
|
+
const localSubject = await loadLocalRepositorySubject({ config: localSubjectConfig, repositoryPath: target.targetRoot });
|
|
532
|
+
const groundTruthIssues = summarizeRetrievalGroundTruthIssuesSafely(localSubject.evaluationCases);
|
|
533
|
+
if (groundTruthIssues.length > 0) {
|
|
534
|
+
throw new Error(`Invalid retrieval-query-strategy-comparison ground truth for the local subject (${groundTruthIssues
|
|
535
|
+
.map((issue) => `case ${issue.caseId}: ${issue.issueCount} issue${issue.issueCount === 1 ? "" : "s"}`)
|
|
536
|
+
.join("; ")}); details withheld.`);
|
|
537
|
+
}
|
|
538
|
+
return { cases: localSubject.evaluationCases, localSubject, env: process.env };
|
|
539
|
+
}
|
|
540
|
+
// The frozen bundled corpus is the same warm-index corpus and must satisfy the same complete ground-truth contract.
|
|
541
|
+
async function loadRetrievalQueryStrategyComparisonInputs(args, toolRoot, context) {
|
|
542
|
+
const validation = retrievalQueryStrategyComparisonPlugin.validateConfig(args.config);
|
|
543
|
+
if (!validation.valid || !validation.config) {
|
|
544
|
+
throw new Error(`Invalid retrieval query strategy comparison config: ${validation.errors.join("; ")}`);
|
|
545
|
+
}
|
|
546
|
+
const projectProfiles = await readBenchmarkProjectProfiles(resolvePackageResource(context, DEFAULT_PROJECT_PROFILES_RESOURCE), toolRoot);
|
|
547
|
+
const cases = await readEvaluationCases(resolvePackageResource(context, RETRIEVAL_QUERY_STRATEGY_COMPARISON_CASES_RESOURCE), toolRoot, {
|
|
548
|
+
projectProfiles,
|
|
549
|
+
requireProjectProfileRef: true
|
|
550
|
+
});
|
|
551
|
+
const corpusErrors = validateRetrievalPrecisionRecallCorpus(cases);
|
|
552
|
+
if (corpusErrors.length > 0) {
|
|
553
|
+
throw new Error(`Invalid retrieval-query-strategy-comparison corpus: ${corpusErrors.join(" ")}`);
|
|
554
|
+
}
|
|
555
|
+
return { cases };
|
|
556
|
+
}
|
|
471
557
|
// External-local retrieval-precision-recall: the existing local-subject loader owns the repository, inventory and case
|
|
472
558
|
// adapter. The only additions are the physical output-root check before anything is created and a ground-truth gate
|
|
473
559
|
// whose message carries logical case ids and counts only (an external subject may have private fact ids and names).
|
|
@@ -134,15 +134,40 @@ function evidenceReason(evidence) {
|
|
|
134
134
|
* Unavailable or not-applicable results are never converted to zero.
|
|
135
135
|
*/
|
|
136
136
|
export function calculateRetrievalQualityMetrics(input) {
|
|
137
|
+
const evidence = input.retrieval.retrievalEvidence;
|
|
138
|
+
return calculateRetrievalQualityMetricsFromIdentityEvidence({
|
|
139
|
+
evaluationCase: input.evaluationCase,
|
|
140
|
+
retrieval: {
|
|
141
|
+
identityEvidence: evidence === undefined
|
|
142
|
+
? undefined
|
|
143
|
+
: {
|
|
144
|
+
availability: evidence.availability,
|
|
145
|
+
...(evidence.availabilityReason !== undefined ? { availabilityReason: evidence.availabilityReason } : {}),
|
|
146
|
+
files: evidence.files.map((file) => file.path),
|
|
147
|
+
symbols: evidence.symbols.map((symbol) => ({
|
|
148
|
+
name: symbol.name,
|
|
149
|
+
...(symbol.file !== undefined ? { file: symbol.file } : {})
|
|
150
|
+
}))
|
|
151
|
+
},
|
|
152
|
+
totalEstimatedTokens: input.retrieval.totalEstimatedTokens,
|
|
153
|
+
tokenCountMethod: input.retrieval.tokenCountMethod
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Single mathematical owner for retrieval-quality calculation. Applies the v0.8.0 formulas and availability
|
|
159
|
+
* semantics to already-normalized identity evidence, so any evidence contract can be scored identically.
|
|
160
|
+
*/
|
|
161
|
+
export function calculateRetrievalQualityMetricsFromIdentityEvidence(input) {
|
|
137
162
|
const { evaluationCase, retrieval } = input;
|
|
138
|
-
const evidence = retrieval.
|
|
163
|
+
const evidence = retrieval.identityEvidence;
|
|
139
164
|
const evidenceBlocked = evidenceReason(evidence);
|
|
140
165
|
const answerKey = evaluationCase.answerKey;
|
|
141
166
|
const hasAnswerKey = answerKey !== undefined && answerKey !== null && typeof answerKey === "object";
|
|
142
167
|
const expectedFiles = resolveExpectation(evaluationCase.expectedFiles, hasAnswerKey ? answerKey.expectedFiles : undefined, hasAnswerKey, normalizeExpectedFiles, "expected-files-answer-key-mismatch");
|
|
143
168
|
const expectedSymbols = resolveExpectation(evaluationCase.expectedSymbols, hasAnswerKey ? answerKey.expectedSymbols : undefined, hasAnswerKey, normalizeExpectedSymbols, "expected-symbols-answer-key-mismatch");
|
|
144
169
|
const usable = evidenceBlocked === null && evidence !== undefined ? evidence : undefined;
|
|
145
|
-
const retrievedFiles = usable ? sortedUnique(usable.files
|
|
170
|
+
const retrievedFiles = usable ? sortedUnique(usable.files) : undefined;
|
|
146
171
|
const retrievedSymbols = usable ? sortedUnique(usable.symbols.map((symbol) => symbol.name)) : undefined;
|
|
147
172
|
// ---- files
|
|
148
173
|
let file = {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const RETRIEVAL_QUERY_STRATEGY_IDS = [
|
|
2
|
+
"keyword-search",
|
|
3
|
+
"symbol-lookup",
|
|
4
|
+
"graph-neighborhood",
|
|
5
|
+
"source-slice",
|
|
6
|
+
"data-model-graph",
|
|
7
|
+
"model-view-lineage",
|
|
8
|
+
"combined-graph-guided"
|
|
9
|
+
];
|
|
10
|
+
export const CORE_GRAPH_RETRIEVAL_QUERY_STRATEGY_IDS = [
|
|
11
|
+
"keyword-search",
|
|
12
|
+
"symbol-lookup",
|
|
13
|
+
"graph-neighborhood",
|
|
14
|
+
"source-slice",
|
|
15
|
+
"combined-graph-guided"
|
|
16
|
+
];
|
|
17
|
+
export function isCoreGraphRetrievalQueryStrategyId(value) {
|
|
18
|
+
return CORE_GRAPH_RETRIEVAL_QUERY_STRATEGY_IDS.includes(value);
|
|
19
|
+
}
|
|
20
|
+
export const SEMANTIC_RETRIEVAL_QUERY_STRATEGY_IDS = [
|
|
21
|
+
"data-model-graph",
|
|
22
|
+
"model-view-lineage"
|
|
23
|
+
];
|
|
24
|
+
export function isSemanticRetrievalQueryStrategyId(value) {
|
|
25
|
+
return SEMANTIC_RETRIEVAL_QUERY_STRATEGY_IDS.includes(value);
|
|
26
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy-neutral retrieval identity evidence (V1).
|
|
3
|
+
*
|
|
4
|
+
* Additive to `retrieval-evidence-v1`, which is frozen to the search/lookup/slice/source command
|
|
5
|
+
* families. This contract records only which file and symbol identities a query strategy exposed and
|
|
6
|
+
* which steps ran. It carries no relevance judgement, score, answer-key data, source text,
|
|
7
|
+
* stdout/stderr, or machine-local paths.
|
|
8
|
+
*/
|
|
9
|
+
import { compareCodeUnits } from "./indexSnapshot.js";
|
|
10
|
+
/**
|
|
11
|
+
* Deduplicates and canonically orders identities. Files are keyed by path; symbols by `nodeId` when
|
|
12
|
+
* present, otherwise by `file + NUL + name`. Ordering is code-unit order, never locale-sensitive.
|
|
13
|
+
*/
|
|
14
|
+
export function canonicalizeQueryStrategyIdentities(files, symbols) {
|
|
15
|
+
const fileByPath = new Map();
|
|
16
|
+
for (const file of files) {
|
|
17
|
+
if (!fileByPath.has(file.path))
|
|
18
|
+
fileByPath.set(file.path, { path: file.path });
|
|
19
|
+
}
|
|
20
|
+
const symbolByKey = new Map();
|
|
21
|
+
for (const symbol of symbols) {
|
|
22
|
+
const key = symbol.nodeId !== null ? symbol.nodeId : `${symbol.file ?? ""}\0${symbol.name}`;
|
|
23
|
+
if (!symbolByKey.has(key)) {
|
|
24
|
+
symbolByKey.set(key, { name: symbol.name, nodeId: symbol.nodeId, file: symbol.file });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
files: [...fileByPath.values()].sort((a, b) => compareCodeUnits(a.path, b.path)),
|
|
29
|
+
symbols: [...symbolByKey.values()].sort((a, b) => compareCodeUnits(a.file ?? "", b.file ?? "") ||
|
|
30
|
+
compareCodeUnits(a.name, b.name) ||
|
|
31
|
+
compareCodeUnits(a.nodeId ?? "", b.nodeId ?? ""))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Converts an already-normalized `retrieval-evidence-v1` observation into the strategy-neutral
|
|
36
|
+
* contract. It recalculates no identity, parses no stdout, and does not reinterpret availability.
|
|
37
|
+
*/
|
|
38
|
+
export function buildQueryStrategyEvidenceFromRetrievalEvidence(strategyId, evidence) {
|
|
39
|
+
return {
|
|
40
|
+
schemaVersion: "retrieval-query-strategy-evidence-v1",
|
|
41
|
+
strategyId,
|
|
42
|
+
availability: evidence.availability,
|
|
43
|
+
availabilityReason: evidence.availabilityReason ?? null,
|
|
44
|
+
files: evidence.files.map(({ path }) => ({ path })),
|
|
45
|
+
symbols: evidence.symbols.map((symbol) => ({
|
|
46
|
+
name: symbol.name,
|
|
47
|
+
nodeId: symbol.nodeId ?? null,
|
|
48
|
+
file: symbol.file ?? null
|
|
49
|
+
})),
|
|
50
|
+
steps: evidence.commands.map((command) => ({
|
|
51
|
+
kind: command.family,
|
|
52
|
+
succeeded: command.succeeded,
|
|
53
|
+
evidenceAvailable: (command.parseState === "parsed" || command.parseState === "selection-attributed") &&
|
|
54
|
+
command.rejectedIdentityCount === 0,
|
|
55
|
+
reason: command.reason ?? null
|
|
56
|
+
}))
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -3,6 +3,7 @@ import { countEstimatedTokens, countTextChars, tokenCountMethod } from "../core/
|
|
|
3
3
|
import { runMeasuredCommand } from "../core/runMeasuredCommand.js";
|
|
4
4
|
import { interpretToolVersionOutput } from "./indexSnapshot.js";
|
|
5
5
|
import { buildRetrievalEvidenceFromCommands } from "./retrievalQuality/buildRetrievalEvidence.js";
|
|
6
|
+
import { buildQueryStrategyEvidenceFromRetrievalEvidence } from "./retrievalQueryStrategyEvidence.js";
|
|
6
7
|
const VERSION_PROBE_TIMEOUT_MS = 30_000;
|
|
7
8
|
function parseJsonIfPossible(text) {
|
|
8
9
|
try {
|
|
@@ -239,11 +240,31 @@ export async function probeMyDevKitVersion(options) {
|
|
|
239
240
|
});
|
|
240
241
|
}
|
|
241
242
|
}
|
|
243
|
+
function selectRetrievalStrategyContextText(strategyId, outputs) {
|
|
244
|
+
const ordered = strategyId === "keyword-search"
|
|
245
|
+
? [outputs.search]
|
|
246
|
+
: strategyId === "symbol-lookup"
|
|
247
|
+
? [outputs.lookup, outputs.search]
|
|
248
|
+
: strategyId === "graph-neighborhood"
|
|
249
|
+
? [outputs.slice, outputs.search]
|
|
250
|
+
: strategyId === "source-slice"
|
|
251
|
+
? [outputs.source, outputs.search]
|
|
252
|
+
: [outputs.source, outputs.slice, outputs.lookup, outputs.search];
|
|
253
|
+
return ordered.find((text) => text && text.trim().length > 0) ?? "";
|
|
254
|
+
}
|
|
255
|
+
const STRATEGY_DOWNSTREAM_COMMANDS = {
|
|
256
|
+
"keyword-search": [],
|
|
257
|
+
"symbol-lookup": ["lookup"],
|
|
258
|
+
"graph-neighborhood": ["slice"],
|
|
259
|
+
"source-slice": ["source"],
|
|
260
|
+
"combined-graph-guided": ["lookup", "slice", "source"]
|
|
261
|
+
};
|
|
242
262
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
263
|
+
* Executes exactly one core retrieval query strategy against an already prepared index. Every
|
|
264
|
+
* strategy starts with `search` and expands at most the first candidate. Never invokes `index`;
|
|
265
|
+
* `durationMs` covers retrieval only.
|
|
245
266
|
*/
|
|
246
|
-
export async function
|
|
267
|
+
export async function runMyDevKitRetrievalStrategyFromIndex(options) {
|
|
247
268
|
const started = Date.now();
|
|
248
269
|
const warnings = [];
|
|
249
270
|
const commands = [];
|
|
@@ -274,58 +295,37 @@ export async function runMyDevKitRetrievalFromIndex(options) {
|
|
|
274
295
|
const selectedNodeId = candidate.nodeId;
|
|
275
296
|
const selectedFile = candidate.file;
|
|
276
297
|
const selectedSymbol = candidate.symbol;
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
extraArgs: ["slice", "--index", indexDir, "--node", selectedNodeId, "--json"]
|
|
301
|
-
});
|
|
302
|
-
commands.push(sliceCommand);
|
|
303
|
-
if (sliceCommand.ok) {
|
|
304
|
-
sliceOutput = sliceCommand.stdout;
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
warnings.push("my-dev-kit slice command failed.");
|
|
308
|
-
}
|
|
309
|
-
const sourceCommand = await runMeasuredCommand({
|
|
310
|
-
commandId: "source",
|
|
311
|
-
commandString: options.kitCommand,
|
|
312
|
-
cwd: process.cwd(),
|
|
313
|
-
outDir: commandsDir,
|
|
314
|
-
extraArgs: ["source", "--index", indexDir, "--node", selectedNodeId, "--max-lines", "160", "--format", "numbered"]
|
|
315
|
-
});
|
|
316
|
-
commands.push(sourceCommand);
|
|
317
|
-
if (sourceCommand.ok) {
|
|
318
|
-
sourceOutput = sourceCommand.stdout;
|
|
298
|
+
const outputs = { search: searchCommand.stdout, lookup: "", slice: "", source: "" };
|
|
299
|
+
const downstream = STRATEGY_DOWNSTREAM_COMMANDS[options.strategyId];
|
|
300
|
+
if (downstream.length > 0) {
|
|
301
|
+
if (selectedNodeId) {
|
|
302
|
+
for (const commandId of downstream) {
|
|
303
|
+
const extraArgs = commandId === "source"
|
|
304
|
+
? ["source", "--index", indexDir, "--node", selectedNodeId, "--max-lines", "160", "--format", "numbered"]
|
|
305
|
+
: [commandId, "--index", indexDir, "--node", selectedNodeId, "--json"];
|
|
306
|
+
const command = await runMeasuredCommand({
|
|
307
|
+
commandId,
|
|
308
|
+
commandString: options.kitCommand,
|
|
309
|
+
cwd: process.cwd(),
|
|
310
|
+
outDir: commandsDir,
|
|
311
|
+
extraArgs
|
|
312
|
+
});
|
|
313
|
+
commands.push(command);
|
|
314
|
+
if (command.ok) {
|
|
315
|
+
outputs[commandId] = command.stdout;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
warnings.push(`my-dev-kit ${commandId} command failed.`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
319
321
|
}
|
|
320
322
|
else {
|
|
321
|
-
warnings.push("my-dev-kit
|
|
323
|
+
warnings.push("No my-dev-kit node id was available after search.");
|
|
322
324
|
}
|
|
323
325
|
}
|
|
324
|
-
|
|
325
|
-
warnings.push("No my-dev-kit node id was available after search.");
|
|
326
|
-
}
|
|
327
|
-
const contextText = [sourceOutput, sliceOutput, lookupOutput, searchCommand.stdout].find((text) => text && text.trim().length > 0) ?? "";
|
|
326
|
+
const contextText = selectRetrievalStrategyContextText(options.strategyId, outputs);
|
|
328
327
|
const filesRead = selectedFile ? [selectedFile] : [];
|
|
328
|
+
const retrievalEvidence = buildRetrievalEvidenceFromCommands(commands, { nodeId: selectedNodeId, file: selectedFile });
|
|
329
329
|
return {
|
|
330
330
|
caseId: options.evaluationCase.id,
|
|
331
331
|
skipped: contextText.length === 0,
|
|
@@ -339,10 +339,18 @@ export async function runMyDevKitRetrievalFromIndex(options) {
|
|
|
339
339
|
selectedNodeId,
|
|
340
340
|
selectedFile,
|
|
341
341
|
selectedSymbol,
|
|
342
|
-
retrievalEvidence
|
|
342
|
+
retrievalEvidence,
|
|
343
|
+
queryStrategyEvidence: buildQueryStrategyEvidenceFromRetrievalEvidence(options.strategyId, retrievalEvidence),
|
|
343
344
|
durationMs: Date.now() - started
|
|
344
345
|
};
|
|
345
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* v0.8.0 compatibility entrypoint: the historical search -> lookup -> slice -> source lifecycle,
|
|
349
|
+
* delegating to the `combined-graph-guided` strategy. Never invokes `index`.
|
|
350
|
+
*/
|
|
351
|
+
export async function runMyDevKitRetrievalFromIndex(options) {
|
|
352
|
+
return runMyDevKitRetrievalStrategyFromIndex({ ...options, strategyId: "combined-graph-guided" });
|
|
353
|
+
}
|
|
346
354
|
/**
|
|
347
355
|
* Legacy per-case lifecycle: builds a case-specific index and retrieves from it. `commands`
|
|
348
356
|
* starts with the index command and `durationMs` covers the whole lifecycle including indexing.
|