@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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,33 @@ All notable changes to my-dev-kit-lab are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.2] - 2026-10-07
|
|
8
|
+
|
|
9
|
+
### Context-pack generation experiments
|
|
10
|
+
|
|
11
|
+
- Added the experimental `context-pack-generation` plugin, which always compares the fixed `raw-full-file` and `context-pack` treatments using reproducible, bounded task-specific context packs.
|
|
12
|
+
- Reused retrieval-quality coverage metrics and estimated context-size semantics, with paired token savings, percent savings, and objective deltas; reports include bounded JSON, text, and HTML pack previews without a composite winner or ranking.
|
|
13
|
+
- Added external-local execution that calculates science on real identities before privacy projection, persists redacted reports, and writes no durable context-pack body.
|
|
14
|
+
- Extended installed-package acceptance and verified compatibility with the published `@dailephd/my-dev-kit@1.12.5` package.
|
|
15
|
+
|
|
16
|
+
## [0.8.1] - 2026-10-06
|
|
17
|
+
|
|
18
|
+
### Retrieval query strategy comparison
|
|
19
|
+
|
|
20
|
+
- 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.
|
|
21
|
+
- 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.
|
|
22
|
+
- Added strategy-neutral identity evidence and preserved the v0.8.0 retrieval-quality formulas through one generic identity-evidence metric owner.
|
|
23
|
+
- Added balanced file and symbol F1: `2TP / (2TP + FP + FN)`.
|
|
24
|
+
- Added matched-complete-case macro aggregation across the `overall`, `localized`, `cross-module`, and `broad-change` scopes.
|
|
25
|
+
- Added four-objective Pareto comparison: maximize mean file F1, maximize mean symbol F1, maximize mean fact coverage, and minimize mean retrieved tokens.
|
|
26
|
+
- 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.
|
|
27
|
+
- 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.
|
|
28
|
+
- 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.
|
|
29
|
+
- Registered list, describe, and run support in the installed CLI with exact bundled and external-local argument boundaries.
|
|
30
|
+
- 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.
|
|
31
|
+
- Validated the legitimate 0.8.0 to 0.8.1 package metadata transition in a disposable candidate copy with no transition defects.
|
|
32
|
+
- Not included: context packs, coding-agent evaluation, automatic strategy selection, scalar or composite scoring, ordinal ranking, and plots or screenshots.
|
|
33
|
+
|
|
7
34
|
## [0.8.0] - 2026-10-04
|
|
8
35
|
|
|
9
36
|
### 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 published release is v0.8.2 (context-pack generation experiments), and the repository package version is 0.8.2. The previous published release is v0.8.1 (retrieval query strategy comparison). 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
|
+
The next planned milestone after v0.8.2 is v0.9.0 (agent-success-rate experiments).
|
|
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,8 @@ 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).
|
|
27
|
+
- **Generate auditable task context packs (v0.8.2):** `context-pack-generation` runs both fixed treatments, `raw-full-file` and `context-pack`, for each selected case. A pack contains a task summary, relevant files and symbols, bounded source slices, call relationships, tests, and evidence notes. Analysis reuses retrieval fact coverage and file/symbol precision, recall, and F1, reports estimated context size and paired raw-versus-pack tokens saved, percent saved, and objective deltas, and names no composite winner or ranking. Bundled runs persist pack artifacts and reports include a bounded preview. External-local runs calculate science on real identities, redact before persistence, omit durable pack bodies, and show redacted report previews. See [docs/COMMANDS.md](docs/COMMANDS.md), [docs/WORKFLOWS.md](docs/WORKFLOWS.md), and [docs/METRICS.md](docs/METRICS.md).
|
|
26
28
|
- **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
29
|
- **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
30
|
- **Validate CLI/package security:** inspect dependencies, package contents, path and subprocess boundaries, malformed inputs, optional static scanners, and bounded fuzz targets.
|
|
@@ -242,8 +244,9 @@ See [docs/METRICS.md](docs/METRICS.md) for full metric definitions.
|
|
|
242
244
|
- 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
245
|
- 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
246
|
- 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
|
|
247
|
+
- The generic experiment-plugin framework has seven registered plugins: `context-strategy-comparison`, `warm-index-reuse`, `incremental-change-staleness`, `context-window-scaling`, `retrieval-precision-recall`, `retrieval-query-strategy-comparison`, and `context-pack-generation`. The v0.7.0 context-window-scaling plugin is released; v0.7.1 added caller-supplied synthetic repositories; v0.7.2 added an explicitly selected local Git repository. The context-pack-generation plugin shipped in v0.8.2; agent-success plugins remain planned.
|
|
248
|
+
- `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.
|
|
249
|
+
- `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 and selects no strategy automatically. 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
250
|
- `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
251
|
- The current release does not guarantee token savings; it produces auditable evidence for specific cases, targets, agents, and strategies
|
|
249
252
|
- 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, and context-
|
|
159
|
+
"my-dev-kit command override (warm-index-reuse, incremental-change-staleness, context-window-scaling, retrieval-precision-recall, retrieval-query-strategy-comparison, and context-pack-generation):",
|
|
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,40 @@ 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
|
+
"",
|
|
222
|
+
"context-pack-generation only:",
|
|
223
|
+
" Bundled/self mode runs the frozen 12-case corpus and accepts only",
|
|
224
|
+
" --out, --case, --benchmark-project and --kit-command (default",
|
|
225
|
+
" npx @dailephd/my-dev-kit@latest); --case and --benchmark-project",
|
|
226
|
+
" narrow the case set. Both treatments, raw-full-file and",
|
|
227
|
+
" context-pack, always run for every case; there is no treatment,",
|
|
228
|
+
" strategy, or selection-policy option. External-local mode requires",
|
|
229
|
+
" --target together with --local-subject-config and accepts only",
|
|
230
|
+
" --out, --target, --local-subject-config and --kit-command; --case",
|
|
231
|
+
" and --benchmark-project are rejected because the local-subject",
|
|
232
|
+
" config owns the case set. The repository is never modified and",
|
|
233
|
+
" durable output withholds private file, symbol, fact, semantic-node,",
|
|
234
|
+
" call-relationship, test, source-text, warning, task and case-title",
|
|
235
|
+
" identities; no context-pack body is written for an external run.",
|
|
236
|
+
"",
|
|
203
237
|
"warm-index-reuse only:",
|
|
204
238
|
" Legacy/default mode uses the deterministic fake agent only;",
|
|
205
239
|
" agent options below are not accepted by this plugin.",
|
|
@@ -136,6 +136,12 @@ 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
|
+
}
|
|
142
|
+
if (plugin.metadata.id === "context-pack-generation") {
|
|
143
|
+
return "Two subject modes. Bundled (default): the frozen 12-case warm-index corpus against the Lab itself, narrowable 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, the repository is never modified, the output directory must be outside it, one private base index is built per configured case, and durable output withholds private file, symbol, fact, semantic-node, call-relationship, test, source-text, warning, task and case-title identities while preserving numeric scientific results; no context-pack body is written externally. Bundled filters are not accepted in external-local mode. Both treatments (raw-full-file and context-pack) always run for every case; there is no treatment, strategy, or selection-policy option.";
|
|
144
|
+
}
|
|
139
145
|
if (supportsExternal) {
|
|
140
146
|
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
147
|
}
|
|
@@ -161,7 +167,9 @@ function describeExamples(plugin) {
|
|
|
161
167
|
// Only advertise an explicit --target example for plugins that support external targets.
|
|
162
168
|
if (plugin.metadata.supportedTargets.includes("external-local") &&
|
|
163
169
|
plugin.metadata.id !== "context-window-scaling" &&
|
|
164
|
-
plugin.metadata.id !== "retrieval-precision-recall"
|
|
170
|
+
plugin.metadata.id !== "retrieval-precision-recall" &&
|
|
171
|
+
plugin.metadata.id !== "retrieval-query-strategy-comparison" &&
|
|
172
|
+
plugin.metadata.id !== "context-pack-generation") {
|
|
165
173
|
examples.push(`my-dev-kit-lab experiment run --experiment ${plugin.metadata.id} --target "Z:\\Users\\newuser\\Projects\\my-dev-kit-v1"${agentOptions}${screenshotOption}`);
|
|
166
174
|
}
|
|
167
175
|
// Only advertise campaign examples for plugins that declare the campaignPreset config field.
|
|
@@ -171,6 +179,12 @@ function describeExamples(plugin) {
|
|
|
171
179
|
if (plugin.metadata.id === "retrieval-precision-recall") {
|
|
172
180
|
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
181
|
}
|
|
182
|
+
if (plugin.metadata.id === "retrieval-query-strategy-comparison") {
|
|
183
|
+
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>`);
|
|
184
|
+
}
|
|
185
|
+
if (plugin.metadata.id === "context-pack-generation") {
|
|
186
|
+
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>`);
|
|
187
|
+
}
|
|
174
188
|
if (plugin.metadata.id === "context-window-scaling") {
|
|
175
189
|
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
190
|
}
|
|
@@ -5,7 +5,8 @@ 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
|
+
import { contextPackGenerationPlugin } from "../experiments/plugins/contextPackGeneration/index.js";
|
|
9
10
|
import { prepareSyntheticContextWindowScalingInputs } from "../experiments/plugins/contextWindowScaling/index.js";
|
|
10
11
|
import { projectExternalLocalTarget, projectRunForExternalLocalPersistence, } from "../experiments/plugins/contextWindowScaling/localSubjectPrivacy.js";
|
|
11
12
|
import { assertWorkRootOutsideTarget } from "../experiments/plugins/contextWindowScaling/localSubjectScratch.js";
|
|
@@ -59,6 +60,17 @@ const RETRIEVAL_PRECISION_RECALL_ALLOWED_FLAGS = ["--experiment", "--out", "--ca
|
|
|
59
60
|
// External-local mode (--target with --local-subject-config) is a different subject source: the local-subject config owns
|
|
60
61
|
// the case set, so the bundled corpus filters are not accepted there.
|
|
61
62
|
const RETRIEVAL_PRECISION_RECALL_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
63
|
+
// retrieval-query-strategy-comparison reuses the same frozen bundled corpus. All seven strategies always run, so there
|
|
64
|
+
// is no strategy option. External-local mode again accepts only the local-subject source flags.
|
|
65
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_CASES_RESOURCE = "benchmarks/contracts/warm-index-benchmark-cases.json";
|
|
66
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_ALLOWED_FLAGS = ["--experiment", "--out", "--case", "--benchmark-project", "--kit-command"];
|
|
67
|
+
const RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
68
|
+
// context-pack-generation (v0.8.2) reuses the same frozen bundled corpus. Both treatments (raw-full-file and context-pack)
|
|
69
|
+
// always run, so there is no treatment or strategy option. External-local mode (--target with --local-subject-config) is a
|
|
70
|
+
// different subject source: the local-subject config owns the case set, so the bundled corpus filters are not accepted there.
|
|
71
|
+
const CONTEXT_PACK_GENERATION_CASES_RESOURCE = "benchmarks/contracts/warm-index-benchmark-cases.json";
|
|
72
|
+
const CONTEXT_PACK_GENERATION_ALLOWED_FLAGS = ["--experiment", "--out", "--case", "--benchmark-project", "--kit-command"];
|
|
73
|
+
const CONTEXT_PACK_GENERATION_EXTERNAL_ALLOWED_FLAGS = ["--experiment", "--out", "--target", "--local-subject-config", "--kit-command"];
|
|
62
74
|
export async function runExperimentRunCommandFromArgs(argv, options = {}) {
|
|
63
75
|
try {
|
|
64
76
|
const args = parseRunExperimentArgs(argv);
|
|
@@ -309,7 +321,9 @@ export function parseRunExperimentArgs(argv) {
|
|
|
309
321
|
warmIndexReusePlugin.metadata.id,
|
|
310
322
|
incrementalChangeStalenessPlugin.metadata.id,
|
|
311
323
|
contextWindowScalingPlugin.metadata.id,
|
|
312
|
-
retrievalPrecisionRecallPlugin.metadata.id
|
|
324
|
+
retrievalPrecisionRecallPlugin.metadata.id,
|
|
325
|
+
retrievalQueryStrategyComparisonPlugin.metadata.id,
|
|
326
|
+
contextPackGenerationPlugin.metadata.id
|
|
313
327
|
];
|
|
314
328
|
if (experimentId === retrievalPrecisionRecallPlugin.metadata.id) {
|
|
315
329
|
// Mode matrix, no precedence rules: neither flag = bundled; both = external-local; exactly one is rejected.
|
|
@@ -335,6 +349,56 @@ export function parseRunExperimentArgs(argv) {
|
|
|
335
349
|
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
336
350
|
}
|
|
337
351
|
}
|
|
352
|
+
if (experimentId === retrievalQueryStrategyComparisonPlugin.metadata.id) {
|
|
353
|
+
// Mode matrix, no precedence rules: neither flag = bundled; both = external-local; exactly one is rejected.
|
|
354
|
+
if (targetPath !== undefined && localSubjectConfigPath === undefined) {
|
|
355
|
+
throw new Error(`External ${experimentId} targets require --local-subject-config.`);
|
|
356
|
+
}
|
|
357
|
+
if (localSubjectConfigPath !== undefined && targetPath === undefined) {
|
|
358
|
+
throw new Error(`--local-subject-config requires an external --target for ${experimentId}.`);
|
|
359
|
+
}
|
|
360
|
+
const externalMode = targetPath !== undefined;
|
|
361
|
+
if (externalMode && (seenFlags.includes("--case") || seenFlags.includes("--benchmark-project"))) {
|
|
362
|
+
throw new Error("--case and --benchmark-project cannot be combined with --local-subject-config; the local subject config owns the case set.");
|
|
363
|
+
}
|
|
364
|
+
const allowedFlags = externalMode
|
|
365
|
+
? RETRIEVAL_QUERY_STRATEGY_COMPARISON_EXTERNAL_ALLOWED_FLAGS
|
|
366
|
+
: RETRIEVAL_QUERY_STRATEGY_COMPARISON_ALLOWED_FLAGS;
|
|
367
|
+
const unsupported = [...new Set(seenFlags.filter((flag) => !allowedFlags.includes(flag)))];
|
|
368
|
+
if (unsupported.length > 0) {
|
|
369
|
+
throw new Error(`${unsupported.join(", ")} ${unsupported.length === 1 ? "is" : "are"} not supported for --experiment ${experimentId}${externalMode ? " in external-local mode" : ""}; supported options: ${allowedFlags.join(", ")}.`);
|
|
370
|
+
}
|
|
371
|
+
if (seenFlags.includes("--case") && caseIds.length === 0) {
|
|
372
|
+
throw new Error("--case must list at least one case id.");
|
|
373
|
+
}
|
|
374
|
+
if (seenFlags.includes("--benchmark-project") && benchmarkProjects.length === 0) {
|
|
375
|
+
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (experimentId === contextPackGenerationPlugin.metadata.id) {
|
|
379
|
+
// Mode matrix, no precedence rules: neither flag = bundled; both = external-local; exactly one is rejected.
|
|
380
|
+
if (targetPath !== undefined && localSubjectConfigPath === undefined) {
|
|
381
|
+
throw new Error(`External ${experimentId} targets require --local-subject-config.`);
|
|
382
|
+
}
|
|
383
|
+
if (localSubjectConfigPath !== undefined && targetPath === undefined) {
|
|
384
|
+
throw new Error(`--local-subject-config requires an external --target for ${experimentId}.`);
|
|
385
|
+
}
|
|
386
|
+
const externalMode = targetPath !== undefined;
|
|
387
|
+
if (externalMode && (seenFlags.includes("--case") || seenFlags.includes("--benchmark-project"))) {
|
|
388
|
+
throw new Error("--case and --benchmark-project cannot be combined with --local-subject-config; the local subject config owns the case set.");
|
|
389
|
+
}
|
|
390
|
+
const allowedFlags = externalMode ? CONTEXT_PACK_GENERATION_EXTERNAL_ALLOWED_FLAGS : CONTEXT_PACK_GENERATION_ALLOWED_FLAGS;
|
|
391
|
+
const unsupported = [...new Set(seenFlags.filter((flag) => !allowedFlags.includes(flag)))];
|
|
392
|
+
if (unsupported.length > 0) {
|
|
393
|
+
throw new Error(`${unsupported.join(", ")} ${unsupported.length === 1 ? "is" : "are"} not supported for --experiment ${experimentId}${externalMode ? " in external-local mode" : ""}; supported options: ${allowedFlags.join(", ")}.`);
|
|
394
|
+
}
|
|
395
|
+
if (seenFlags.includes("--case") && caseIds.length === 0) {
|
|
396
|
+
throw new Error("--case must list at least one case id.");
|
|
397
|
+
}
|
|
398
|
+
if (seenFlags.includes("--benchmark-project") && benchmarkProjects.length === 0) {
|
|
399
|
+
throw new Error("--benchmark-project must list at least one benchmark project id.");
|
|
400
|
+
}
|
|
401
|
+
}
|
|
338
402
|
if (contextBudgets !== undefined && experimentId !== contextWindowScalingPlugin.metadata.id) {
|
|
339
403
|
throw new Error(`--context-budgets is only supported for --experiment ${contextWindowScalingPlugin.metadata.id}.`);
|
|
340
404
|
}
|
|
@@ -343,8 +407,10 @@ export function parseRunExperimentArgs(argv) {
|
|
|
343
407
|
}
|
|
344
408
|
if (localSubjectConfigPath !== undefined &&
|
|
345
409
|
experimentId !== contextWindowScalingPlugin.metadata.id &&
|
|
346
|
-
experimentId !== retrievalPrecisionRecallPlugin.metadata.id
|
|
347
|
-
|
|
410
|
+
experimentId !== retrievalPrecisionRecallPlugin.metadata.id &&
|
|
411
|
+
experimentId !== retrievalQueryStrategyComparisonPlugin.metadata.id &&
|
|
412
|
+
experimentId !== contextPackGenerationPlugin.metadata.id) {
|
|
413
|
+
throw new Error(`--local-subject-config is only supported for --experiment ${contextWindowScalingPlugin.metadata.id}, ${retrievalPrecisionRecallPlugin.metadata.id}, ${retrievalQueryStrategyComparisonPlugin.metadata.id}, or ${contextPackGenerationPlugin.metadata.id}.`);
|
|
348
414
|
}
|
|
349
415
|
if (experimentId === contextWindowScalingPlugin.metadata.id) {
|
|
350
416
|
const unsupported = [...new Set(seenFlags.filter((flag) => !CONTEXT_WINDOW_SCALING_ALLOWED_FLAGS.includes(flag)))];
|
|
@@ -466,8 +532,115 @@ async function loadPluginInputs(args, toolRoot, context, outputRoot) {
|
|
|
466
532
|
? loadRetrievalPrecisionRecallLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
467
533
|
: loadRetrievalPrecisionRecallInputs(args, toolRoot, context);
|
|
468
534
|
}
|
|
535
|
+
if (args.experimentId === contextPackGenerationPlugin.metadata.id) {
|
|
536
|
+
return args.localSubjectConfigPath !== undefined
|
|
537
|
+
? loadContextPackGenerationLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
538
|
+
: loadContextPackGenerationInputs(args, toolRoot, context);
|
|
539
|
+
}
|
|
540
|
+
if (args.experimentId === retrievalQueryStrategyComparisonPlugin.metadata.id) {
|
|
541
|
+
return args.localSubjectConfigPath !== undefined
|
|
542
|
+
? loadRetrievalQueryStrategyComparisonLocalSubjectInputs(args, toolRoot, context, outputRoot)
|
|
543
|
+
: loadRetrievalQueryStrategyComparisonInputs(args, toolRoot, context);
|
|
544
|
+
}
|
|
469
545
|
return undefined;
|
|
470
546
|
}
|
|
547
|
+
// context-pack-generation (bundled/self): the frozen warm-index corpus through the established resource resolver, profile
|
|
548
|
+
// and corpus validation. The plugin applies --case/--benchmark-project selection and owns execution, science and artifacts.
|
|
549
|
+
async function loadContextPackGenerationInputs(args, toolRoot, context) {
|
|
550
|
+
const validation = contextPackGenerationPlugin.validateConfig(args.config);
|
|
551
|
+
if (!validation.valid || !validation.config) {
|
|
552
|
+
throw new Error(`Invalid context pack generation config: ${validation.errors.join("; ")}`);
|
|
553
|
+
}
|
|
554
|
+
const projectProfiles = await readBenchmarkProjectProfiles(resolvePackageResource(context, DEFAULT_PROJECT_PROFILES_RESOURCE), toolRoot);
|
|
555
|
+
const cases = await readEvaluationCases(resolvePackageResource(context, CONTEXT_PACK_GENERATION_CASES_RESOURCE), toolRoot, {
|
|
556
|
+
projectProfiles,
|
|
557
|
+
requireProjectProfileRef: true
|
|
558
|
+
});
|
|
559
|
+
const corpusErrors = validateRetrievalPrecisionRecallCorpus(cases);
|
|
560
|
+
if (corpusErrors.length > 0) {
|
|
561
|
+
throw new Error(`Invalid context-pack-generation corpus: ${corpusErrors.join(" ")}`);
|
|
562
|
+
}
|
|
563
|
+
return { cases };
|
|
564
|
+
}
|
|
565
|
+
// External-local context-pack-generation: same loader and safety order as the other external-local retrieval experiments,
|
|
566
|
+
// with the plugin's own config validation. Ground-truth issues carry logical case ids and counts only.
|
|
567
|
+
async function loadContextPackGenerationLocalSubjectInputs(args, toolRoot, context, outputRoot) {
|
|
568
|
+
const validation = contextPackGenerationPlugin.validateConfig(args.config);
|
|
569
|
+
if (!validation.valid || !validation.config) {
|
|
570
|
+
throw new Error(`Invalid context pack generation config: ${validation.errors.join("; ")}`);
|
|
571
|
+
}
|
|
572
|
+
if (outputRoot === undefined || args.localSubjectConfigPath === undefined) {
|
|
573
|
+
throw new Error("Internal error: external-local runs require a resolved experiment output root and config path.");
|
|
574
|
+
}
|
|
575
|
+
const target = resolveExperimentTarget(args.targetPath, toolRoot);
|
|
576
|
+
if (target.isSelf || target.kind !== "external-local") {
|
|
577
|
+
throw new Error("Local-repository subject mode requires an external-local target; --target must not be the Lab repository.");
|
|
578
|
+
}
|
|
579
|
+
try {
|
|
580
|
+
await assertWorkRootOutsideTarget(outputRoot, target.targetRoot);
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
throw new Error("Experiment output root must not be inside the external target project.");
|
|
584
|
+
}
|
|
585
|
+
const configPath = path.resolve(context.invocationCwd, args.localSubjectConfigPath);
|
|
586
|
+
const localSubjectConfig = await readJsonConfigFile("--local-subject-config", configPath, args.localSubjectConfigPath);
|
|
587
|
+
const localSubject = await loadLocalRepositorySubject({ config: localSubjectConfig, repositoryPath: target.targetRoot });
|
|
588
|
+
const groundTruthIssues = summarizeRetrievalGroundTruthIssuesSafely(localSubject.evaluationCases);
|
|
589
|
+
if (groundTruthIssues.length > 0) {
|
|
590
|
+
throw new Error(`Invalid context-pack-generation ground truth for the local subject (${groundTruthIssues
|
|
591
|
+
.map((issue) => `case ${issue.caseId}: ${issue.issueCount} issue${issue.issueCount === 1 ? "" : "s"}`)
|
|
592
|
+
.join("; ")}); details withheld.`);
|
|
593
|
+
}
|
|
594
|
+
return { cases: localSubject.evaluationCases, localSubject, env: process.env };
|
|
595
|
+
}
|
|
596
|
+
// External-local retrieval-query-strategy-comparison: same loader and safety order as retrieval-precision-recall, with
|
|
597
|
+
// the plugin's own config validation. Ground-truth issues carry logical case ids and counts only.
|
|
598
|
+
async function loadRetrievalQueryStrategyComparisonLocalSubjectInputs(args, toolRoot, context, outputRoot) {
|
|
599
|
+
const validation = retrievalQueryStrategyComparisonPlugin.validateConfig(args.config);
|
|
600
|
+
if (!validation.valid || !validation.config) {
|
|
601
|
+
throw new Error(`Invalid retrieval query strategy comparison config: ${validation.errors.join("; ")}`);
|
|
602
|
+
}
|
|
603
|
+
if (outputRoot === undefined || args.localSubjectConfigPath === undefined) {
|
|
604
|
+
throw new Error("Internal error: external-local runs require a resolved experiment output root and config path.");
|
|
605
|
+
}
|
|
606
|
+
const target = resolveExperimentTarget(args.targetPath, toolRoot);
|
|
607
|
+
if (target.isSelf || target.kind !== "external-local") {
|
|
608
|
+
throw new Error("Local-repository subject mode requires an external-local target; --target must not be the Lab repository.");
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
await assertWorkRootOutsideTarget(outputRoot, target.targetRoot);
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
throw new Error("Experiment output root must not be inside the external target project.");
|
|
615
|
+
}
|
|
616
|
+
const configPath = path.resolve(context.invocationCwd, args.localSubjectConfigPath);
|
|
617
|
+
const localSubjectConfig = await readJsonConfigFile("--local-subject-config", configPath, args.localSubjectConfigPath);
|
|
618
|
+
const localSubject = await loadLocalRepositorySubject({ config: localSubjectConfig, repositoryPath: target.targetRoot });
|
|
619
|
+
const groundTruthIssues = summarizeRetrievalGroundTruthIssuesSafely(localSubject.evaluationCases);
|
|
620
|
+
if (groundTruthIssues.length > 0) {
|
|
621
|
+
throw new Error(`Invalid retrieval-query-strategy-comparison ground truth for the local subject (${groundTruthIssues
|
|
622
|
+
.map((issue) => `case ${issue.caseId}: ${issue.issueCount} issue${issue.issueCount === 1 ? "" : "s"}`)
|
|
623
|
+
.join("; ")}); details withheld.`);
|
|
624
|
+
}
|
|
625
|
+
return { cases: localSubject.evaluationCases, localSubject, env: process.env };
|
|
626
|
+
}
|
|
627
|
+
// The frozen bundled corpus is the same warm-index corpus and must satisfy the same complete ground-truth contract.
|
|
628
|
+
async function loadRetrievalQueryStrategyComparisonInputs(args, toolRoot, context) {
|
|
629
|
+
const validation = retrievalQueryStrategyComparisonPlugin.validateConfig(args.config);
|
|
630
|
+
if (!validation.valid || !validation.config) {
|
|
631
|
+
throw new Error(`Invalid retrieval query strategy comparison config: ${validation.errors.join("; ")}`);
|
|
632
|
+
}
|
|
633
|
+
const projectProfiles = await readBenchmarkProjectProfiles(resolvePackageResource(context, DEFAULT_PROJECT_PROFILES_RESOURCE), toolRoot);
|
|
634
|
+
const cases = await readEvaluationCases(resolvePackageResource(context, RETRIEVAL_QUERY_STRATEGY_COMPARISON_CASES_RESOURCE), toolRoot, {
|
|
635
|
+
projectProfiles,
|
|
636
|
+
requireProjectProfileRef: true
|
|
637
|
+
});
|
|
638
|
+
const corpusErrors = validateRetrievalPrecisionRecallCorpus(cases);
|
|
639
|
+
if (corpusErrors.length > 0) {
|
|
640
|
+
throw new Error(`Invalid retrieval-query-strategy-comparison corpus: ${corpusErrors.join(" ")}`);
|
|
641
|
+
}
|
|
642
|
+
return { cases };
|
|
643
|
+
}
|
|
471
644
|
// External-local retrieval-precision-recall: the existing local-subject loader owns the repository, inventory and case
|
|
472
645
|
// adapter. The only additions are the physical output-root check before anything is created and a ground-truth gate
|
|
473
646
|
// 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
|
+
}
|