@dailephd/my-dev-kit-lab 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/README.md +7 -5
  3. package/dist/src/agents/adapters/codexAdapter.js +4 -1
  4. package/dist/src/cli/help.js +38 -2
  5. package/dist/src/cli/runLabCli.js +9 -1
  6. package/dist/src/commands/buildGalleryCommand.js +16 -3
  7. package/dist/src/commands/runComparePromptsCommand.js +109 -0
  8. package/dist/src/commands/runExperimentRunCommand.js +47 -2
  9. package/dist/src/core/processTree.js +41 -17
  10. package/dist/src/core/runMeasuredCommand.js +14 -8
  11. package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +23 -1
  12. package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +7 -2
  13. package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +29 -4
  14. package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +1 -1
  15. package/dist/src/experiments/plugins/agentSuccessRate/config.js +47 -2
  16. package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +4 -2
  17. package/dist/src/experiments/plugins/agentSuccessRate/promptContract.js +121 -0
  18. package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +32 -2
  19. package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +105 -11
  20. package/dist/src/experiments/plugins/agentSuccessRate/strictDiffResponse.js +312 -0
  21. package/dist/src/gallery/galleryPathSafety.js +111 -0
  22. package/dist/src/gallery/readGallerySources.js +542 -0
  23. package/dist/src/gallery/writeGalleryManifest.js +57 -3
  24. package/dist/src/report/experiments/buildPluginExperimentReport.js +4 -16
  25. package/dist/src/report/experiments/htmlSafety.js +32 -0
  26. package/dist/src/report/experiments/metricEvidence.js +389 -0
  27. package/dist/src/report/experiments/metricGlossary.js +227 -0
  28. package/dist/src/report/experiments/metricGlossaryCatalog.js +199 -0
  29. package/dist/src/report/experiments/promptTemplateComparisonEvidence.js +377 -0
  30. package/dist/src/report/experiments/promptTemplateComparisonModel.js +436 -0
  31. package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +158 -64
  32. package/dist/src/report/experiments/renderPluginExperimentReportText.js +22 -677
  33. package/dist/src/report/experiments/renderPromptTemplateComparison.js +192 -0
  34. package/dist/src/report/experiments/renderSpecializedTextSections.js +633 -0
  35. package/dist/src/report/experiments/reportSectionRegistry.js +79 -0
  36. package/dist/src/report/experiments/specializedReportSections.js +142 -0
  37. package/dist/src/report/experiments/writePluginExperimentReports.js +4 -2
  38. package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +146 -133
  39. package/dist/src/securityValidation/attackScenarios/scenarios/subprocessInjectionScenario.js +54 -36
  40. package/dist/src/securityValidation/commandRunner.js +13 -6
  41. package/docs/METRICS.md +11 -1
  42. package/package.json +1 -1
@@ -0,0 +1,227 @@
1
+ // Typed metric glossary for report presentation. Definitions are condensed from docs/METRICS.md, which stays the
2
+ // authoritative source of metric meaning. This module performs no calculation and no I/O; it only maps a
3
+ // plugin-scoped metric identity to presentation metadata and a stable in-document glossary anchor.
4
+ import { APPENDED_ROWS, EXTRA_AGENT_SUCCESS_ROWS, EXTRA_GROUPS, FAMILIES } from "./metricGlossaryCatalog.js";
5
+ const AGENT_AVAILABILITY_CAVEAT = "Unavailable evidence is excluded from rates and means and is never converted to zero.";
6
+ const ESTIMATE_CAVEAT = "Character-based estimate (ceil(characters / 4)); not provider token usage or billing telemetry.";
7
+ const GROUPS = [
8
+ {
9
+ pluginId: "agent-success-rate",
10
+ docsSection: "Agent-success-rate metrics (v0.9.0)",
11
+ prefix: "agent-success-rate.",
12
+ rows: [
13
+ { id: "taskChecksPassed", name: "Task checks passed", def: "Every trusted task check passed on the changed state.", unit: "boolean", interp: "true means complete passing task-check evidence.", caveats: ["Unknown if any task check is missing, timed out, or errored."], basis: "verification-verdict" },
14
+ { id: "regressionSafe", name: "Regression safe", def: "Every regression check that passed on the baseline still passes.", unit: "boolean", interp: "Only baseline-passing regression checks are considered.", basis: "verification-verdict" },
15
+ { id: "requiredFactsSatisfied", name: "Required facts satisfied", def: "Every behavior fact marked required has all its verification checks passing.", unit: "boolean", interp: "true means all required facts were verified.", basis: "verification-verdict" },
16
+ { id: "protectedIntegrity", name: "Protected integrity", def: "Protected files are byte-intact after the patch.", unit: "boolean", interp: "true means no protected file changed.", basis: "verification-verdict" },
17
+ { id: "taskResolved", name: "Task resolved", def: "taskChecksPassed AND requiredFactsSatisfied.", unit: "boolean", interp: "A component of taskSuccess.", basis: "verification-verdict" },
18
+ { id: "taskSuccess", name: "Task success", def: "taskResolved AND regressionSafe AND protected files intact.", unit: "boolean", interp: "The only success verdict; agent prose is never evidence.", caveats: ["A patch that was rejected or not applied yields false; an uncomputable verdict is unavailable."], basis: "verification-verdict" },
19
+ { id: "initialAttemptTaskSuccess", name: "Initial-attempt task success", def: "taskSuccess of attempt 1.", unit: "boolean", interp: "Separate from final success; a repaired success never raises it.", caveats: ["Real-agent repair evidence only."], basis: "verification-verdict" },
20
+ { id: "finalTaskSuccess", name: "Final task success", def: "taskSuccess of the last attempt executed.", unit: "boolean", interp: "Equals taskSuccess.", basis: "verification-verdict" },
21
+ { id: "repairSucceeded", name: "Repair succeeded", def: "initialAttemptTaskSuccess is false AND finalTaskSuccess is true.", unit: "boolean", interp: "true means a failed initial attempt was repaired.", caveats: ["Not applicable when no repair attempt ran; unavailable if either verdict is unavailable."], basis: "verification-verdict" },
22
+ { id: "repairSuccessRate", name: "Repair success rate", def: "Repaired cases / repair-attempted cases with a determinate repair verdict.", unit: "ratio", interp: "Descriptive rate over a finite controlled corpus.", caveats: ["Not applicable when no repair attempt ran for the treatment."], basis: "derived" },
23
+ { id: "taskSuccessRate", name: "Task success rate", def: "Successful cases / cases with a determinate verdict for that attempt basis.", unit: "ratio", interp: "Descriptive; no effect estimate or significance test is produced.", caveats: ["Unavailable when no case has a determinate verdict."], basis: "derived" },
24
+ { id: "initialAttemptSuccessRate", name: "Initial-attempt success rate", def: "Successful cases / cases with a determinate initial-attempt verdict.", unit: "ratio", interp: "Measured separately from the final rate.", basis: "derived" },
25
+ { id: "finalTaskSuccessRate", name: "Final task success rate", def: "Successful cases / cases with a determinate final-attempt verdict.", unit: "ratio", interp: "A repaired success raises this rate, never the initial rate.", basis: "derived" },
26
+ { id: "taskCheckPassRate", name: "Task check pass rate", def: "Passed task checks / task checks.", unit: "ratio", interp: "taskCheckPassedCount / taskCheckTotalCount.", caveats: ["Unavailable unless every task check has a complete result; not applicable with zero checks."], basis: "derived" },
27
+ { id: "taskCheckPassedCount", name: "Task checks passed (count)", def: "Numerator of taskCheckPassRate: passed task checks.", unit: "count", interp: "Read with taskCheckTotalCount.", basis: "derived" },
28
+ { id: "taskCheckTotalCount", name: "Task checks (count)", def: "Denominator of taskCheckPassRate: task checks.", unit: "count", interp: "Read with taskCheckPassedCount.", basis: "derived" },
29
+ { id: "regressionCheckPassRate", name: "Regression check pass rate", def: "Passed regression checks / regression checks.", unit: "ratio", interp: "regressionCheckPassedCount / regressionCheckTotalCount.", basis: "derived" },
30
+ { id: "regressionCheckPassedCount", name: "Regression checks passed (count)", def: "Numerator of regressionCheckPassRate.", unit: "count", interp: "Read with regressionCheckTotalCount.", basis: "derived" },
31
+ { id: "regressionCheckTotalCount", name: "Regression checks (count)", def: "Denominator of regressionCheckPassRate.", unit: "count", interp: "Read with regressionCheckPassedCount.", basis: "derived" },
32
+ { id: "regressionFailureCount", name: "Regression failures (count)", def: "Failed checks that passed on the baseline.", unit: "count", interp: "Lower is better.", basis: "derived" },
33
+ { id: "requiredFactCoverage", name: "Required fact coverage", def: "Satisfied required facts / required facts.", unit: "ratio", interp: "requiredFactsSatisfiedCount / requiredFactsTotal.", caveats: ["Not applicable with no required facts."], basis: "derived" },
34
+ { id: "requiredFactsSatisfiedCount", name: "Required facts satisfied (count)", def: "Numerator of requiredFactCoverage.", unit: "count", interp: "Read with requiredFactsTotal.", basis: "derived" },
35
+ { id: "requiredFactsTotal", name: "Required facts (count)", def: "Denominator of requiredFactCoverage.", unit: "count", interp: "Read with requiredFactsSatisfiedCount.", basis: "derived" },
36
+ { id: "factCoverage", name: "Fact coverage", def: "Satisfied facts (required and optional) / all facts.", unit: "ratio", interp: "Higher means more behavior facts were verified.", caveats: ["Not applicable with no facts."], basis: "derived" },
37
+ { id: "expectedEditCoverage", name: "Expected edit coverage", def: "Changed files that were expected / expected edit files.", unit: "ratio", interp: "expectedEditFilesChangedCount / expectedEditFileCount.", basis: "derived" },
38
+ { id: "expectedEditFilesChangedCount", name: "Expected edit files changed (count)", def: "Numerator of expectedEditCoverage.", unit: "count", interp: "Read with expectedEditFileCount.", basis: "derived" },
39
+ { id: "expectedEditFileCount", name: "Expected edit files (count)", def: "Denominator of expectedEditCoverage.", unit: "count", interp: "Read with expectedEditFilesChangedCount.", basis: "derived" },
40
+ { id: "editScopePrecision", name: "Edit scope precision", def: "Changed files inside the allowed edit scope / changed files.", unit: "ratio", interp: "allowedEditChangedFileCount / changedFileCount.", caveats: ["Not applicable when no file changed."], basis: "derived" },
41
+ { id: "allowedEditChangedFileCount", name: "Allowed-scope changed files (count)", def: "Changed files inside the allowed edit scope.", unit: "count", interp: "Numerator of editScopePrecision.", basis: "derived" },
42
+ { id: "unexpectedChangedFileCount", name: "Unexpected changed files (count)", def: "Changed files minus changed files inside the allowed scope.", unit: "count", interp: "Files outside the allowed scope.", basis: "derived" },
43
+ { id: "protectedMutationCount", name: "Protected mutations (count)", def: "Protected paths found mutated after the patch.", unit: "count", interp: "Lower is better.", caveats: ["Unavailable if integrity could not be proven."], basis: "derived" },
44
+ { id: "attemptedProtectedEditCount", name: "Attempted protected edits (count)", def: "Protected paths a patch tried to edit.", unit: "count", interp: "Counts attempts, separate from protectedMutationCount.", basis: "derived" },
45
+ { id: "changedFileCount", name: "Changed files (count)", def: "Captured changed files of the final attempt.", unit: "count", interp: "Blast-radius evidence for the final patch.", caveats: ["Unavailable, not zero, when no patch was applied."], basis: "derived" },
46
+ { id: "addedFileCount", name: "Added files (count)", def: "Captured changed files with added status.", unit: "count", interp: "Final attempt only.", basis: "derived" },
47
+ { id: "modifiedFileCount", name: "Modified files (count)", def: "Captured changed files with modified status.", unit: "count", interp: "Final attempt only.", basis: "derived" },
48
+ { id: "deletedFileCount", name: "Deleted files (count)", def: "Captured changed files with deleted status.", unit: "count", interp: "Final attempt only.", basis: "derived" },
49
+ { id: "linesAdded", name: "Lines added", def: "Sum of per-file additions.", unit: "lines", interp: "Final patch only.", caveats: ["Unavailable if any changed file has no line counts (binary); never estimated."], basis: "derived" },
50
+ { id: "linesDeleted", name: "Lines deleted", def: "Sum of per-file deletions.", unit: "lines", interp: "Final patch only.", caveats: ["Unavailable if any changed file has no line counts (binary); never estimated."], basis: "derived" },
51
+ { id: "totalChurn", name: "Total churn", def: "linesAdded + linesDeleted.", unit: "lines", interp: "The final patch's churn, not churn accumulated over repair attempts.", basis: "derived" },
52
+ { id: "baselineTextLineCount", name: "Baseline text line count", def: "Baseline text-line count used as the relativeChurn denominator.", unit: "lines", interp: "Context for relativeChurn.", basis: "measured" },
53
+ { id: "relativeChurn", name: "Relative churn", def: "totalChurn / baselineTextLineCount.", unit: "ratio", interp: "Churn relative to the baseline size.", caveats: ["Unavailable when the baseline text-line count is unknown; not applicable at zero baseline lines."], basis: "derived" },
54
+ { id: "agentDurationMs", name: "Agent duration", def: "Measured wall-clock duration of the provider attempt.", unit: "ms", interp: "Lab-measured elapsed time, not provider-native time.", caveats: ["Unavailable in deterministic mode and when no provider ran."], basis: "measured" },
55
+ { id: "totalProviderDurationMs", name: "Total provider duration", def: "Sum of provider durations over attempts that ran.", unit: "ms", interp: "Includes repair attempts.", caveats: ["Unavailable if any invoked attempt was not measured."], basis: "measured" },
56
+ { id: "agentTotalTokens", name: "Agent total tokens", def: "Total token usage reported for the attempt.", unit: "tokens", interp: "Read with the recorded token source and reliability.", caveats: ["Unavailable when no usage was reported; the report model does not carry the per-attempt source or reliability."], basis: "agent-token-telemetry" },
57
+ { id: "totalProviderTokens", name: "Total provider tokens", def: "Sum of provider total tokens over attempts that ran.", unit: "tokens", interp: "Accumulates over repair attempts.", caveats: ["Unavailable if any invoked attempt lacks a total."], basis: "agent-token-telemetry" },
58
+ { id: "baselineVerificationDurationMs", name: "Baseline verification duration", def: "Measured duration of the evaluator's baseline verification stage.", unit: "ms", interp: "Evaluator time, not agent time.", basis: "measured" },
59
+ { id: "patchPipelineDurationMs", name: "Patch pipeline duration", def: "Measured duration of the evaluator's patch pipeline stage.", unit: "ms", interp: "Evaluator time, not agent time.", basis: "measured" },
60
+ { id: "postEditVerificationDurationMs", name: "Post-edit verification duration", def: "Measured duration of the evaluator's post-edit verification stage.", unit: "ms", interp: "Evaluator time, not agent time.", basis: "measured" },
61
+ { id: "evaluationDurationMs", name: "Evaluation duration", def: "Measured duration of the evaluator's own stages.", unit: "ms", interp: "Evaluator time, not agent time.", basis: "measured" },
62
+ { id: "totalEvaluationDurationMs", name: "Total evaluation duration", def: "Sum of evaluation durations over attempts.", unit: "ms", interp: "Evaluator time, not agent time.", basis: "measured" },
63
+ { id: "meanTaskCheckPassRate", name: "Mean task check pass rate", def: "Unweighted mean of taskCheckPassRate over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
64
+ { id: "meanRegressionCheckPassRate", name: "Mean regression check pass rate", def: "Unweighted mean of regressionCheckPassRate over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
65
+ { id: "meanRequiredFactCoverage", name: "Mean required fact coverage", def: "Unweighted mean of requiredFactCoverage over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
66
+ { id: "meanExpectedEditCoverage", name: "Mean expected edit coverage", def: "Unweighted mean of expectedEditCoverage over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
67
+ { id: "meanEditScopePrecision", name: "Mean edit scope precision", def: "Unweighted mean of editScopePrecision over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
68
+ { id: "meanUnexpectedChangedFileCount", name: "Mean unexpected changed files", def: "Unweighted mean of unexpectedChangedFileCount over matched cases.", unit: "count", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
69
+ { id: "meanChangedFileCount", name: "Mean changed files", def: "Unweighted mean of changedFileCount over matched cases.", unit: "count", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
70
+ { id: "meanTotalChurn", name: "Mean total churn", def: "Unweighted mean of totalChurn over matched cases.", unit: "lines", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
71
+ { id: "meanRelativeChurn", name: "Mean relative churn", def: "Unweighted mean of relativeChurn over matched cases.", unit: "ratio", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
72
+ { id: "meanEvaluationDurationMs", name: "Mean evaluation duration", def: "Unweighted mean of evaluationDurationMs over matched cases.", unit: "ms", interp: "Descriptive.", caveats: [AGENT_AVAILABILITY_CAVEAT], basis: "derived" },
73
+ ],
74
+ },
75
+ {
76
+ pluginId: "warm-index-reuse",
77
+ docsSection: "Warm-index reuse metrics",
78
+ rows: [
79
+ { id: "context-character-count", name: "Context characters", def: "Characters in the raw full-file context or in the retrieved my-dev-kit context.", unit: "characters", interp: "Context size by characters.", caveats: ["Unavailable when that side produced no evidence."], basis: "measured" },
80
+ { id: "context-estimated-token-count", name: "Estimated context tokens", def: "Character-based context-size estimate, ceil(characters / 4).", unit: "tokens", interp: "Estimated context size.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
81
+ { id: "operation-duration-ms", name: "Operation duration", def: "Time to build the raw full-file context, or the warm retrieval duration against the prepared index.", unit: "ms", interp: "Not agent duration and never includes the index build.", basis: "measured" },
82
+ { id: "amortized-index-build-duration-ms", name: "Amortized index build duration", def: "indexBuildDurationMs / N for task ordinal N, without rounding.", unit: "ms", interp: "Shows the fixed index cost falling per task.", caveats: ["Available only when the project prepared a valid index session."], basis: "derived" },
83
+ { id: "cumulative-component-duration-ms", name: "Cumulative component duration", def: "Sum of measured component durations for tasks 1..N (the warm side counts the index build once).", unit: "ms", interp: "A sum of measured components.", caveats: ["Not wall-clock or end-to-end latency, and not a speedup claim."], basis: "derived" },
84
+ { id: "cumulative-context-estimated-token-count", name: "Cumulative estimated context tokens", def: "Sum of that side's estimated context tokens for tasks 1..N.", unit: "tokens", interp: "Estimated size only.", caveats: [ESTIMATE_CAVEAT, "Strict-prefix rule: unavailable after the first incomplete task."], basis: "estimated" },
85
+ { id: "agent-correctness-score", name: "Agent correctness score", def: "correctnessScore from the benchmark answer-key scorer for the selected agent output.", unit: "score", interp: "Deterministic answer-key scoring.", caveats: ["Not semantic LLM judging; deterministic fake-agent evidence is not real-model correctness."], basis: "answer-key-set-measure" },
86
+ { id: "agent-total-tokens", name: "Agent total tokens", def: "tokenUsage.totalTokens as reported by the selected agent adapter.", unit: "tokens", interp: "Read with the recorded token source and reliability.", caveats: ["Fake-agent totals are simulated harness telemetry; context estimates are never substituted."], basis: "agent-token-telemetry" },
87
+ { id: "cumulative-agent-total-tokens", name: "Cumulative agent total tokens", def: "Sum of agent total tokens for tasks 1..N on that side.", unit: "tokens", interp: "Read with the per-task token source and reliability.", caveats: ["Strict-prefix rule: unavailable after the first incomplete task."], basis: "agent-token-telemetry" },
88
+ { id: "affected-neighborhood-changed-file-count", name: "Affected neighborhood: changed files", def: "Unique confirmed modified or missing files that the index snapshot represents.", unit: "count", interp: "Observational evidence only.", caveats: ["New files the snapshot does not represent are not counted."], basis: "derived" },
89
+ { id: "affected-neighborhood-changed-symbol-count", name: "Affected neighborhood: changed symbols", def: "Unique baseline symbol identities that belong to confirmed changed indexed files.", unit: "count", interp: "Observational evidence only.", caveats: ["Does not prove an individual symbol's own source changed."], basis: "derived" },
90
+ { id: "affected-neighborhood-node-count", name: "Affected neighborhood: nodes", def: "Unique graph node IDs among resolved seed nodes and their direct one-hop neighbors.", unit: "count", interp: "Observational evidence only.", basis: "derived" },
91
+ { id: "affected-neighborhood-edge-count", name: "Affected neighborhood: edges", def: "Unique baseline graph edges incident to at least one resolved seed node.", unit: "count", interp: "Observational evidence only.", caveats: ["Not the number of edges among all affected nodes."], basis: "derived" },
92
+ { id: "affected-neighborhood-task-overlap-count", name: "Affected neighborhood: task overlap", def: "Unique graph nodes in both the affected neighborhood and the task's resolved task nodes.", unit: "count", interp: "Observational evidence only.", basis: "derived" },
93
+ { id: "affected-neighborhood-task-overlap-percent", name: "Affected neighborhood: task overlap percent", def: "taskOverlapCount / resolvableTaskNodeCount * 100, unrounded.", unit: "percent", interp: "Covers only the resolvable subset when task mapping is partial.", caveats: ["Unavailable, never zero, when there are no resolvable task nodes."], basis: "derived" },
94
+ ],
95
+ },
96
+ {
97
+ pluginId: "context-window-scaling",
98
+ docsSection: "Context-window scaling evidence (v0.7.0)",
99
+ rows: [
100
+ { id: "context-estimated-tokens", name: "Context estimated tokens", def: "Estimated token count of the treatment context using ceil(characters / 4).", unit: "tokens", interp: "Not a provider tokenizer result or model-window measurement.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
101
+ { id: "omitted-relevant-file-count", name: "Omitted relevant files", def: "Benchmark-expected relevant files absent from the treatment's observed context provenance.", unit: "files", interp: "An evidence count, not retrieval precision or recall.", basis: "derived" },
102
+ ],
103
+ },
104
+ {
105
+ pluginId: "retrieval-precision-recall",
106
+ docsSection: "Retrieval precision/recall evidence (v0.8.0)",
107
+ rows: [
108
+ { id: "file-precision", name: "File precision", def: "Relevant retrieved files / retrieved files.", unit: "ratio", interp: "Higher means a larger share of retrieved files were expected.", caveats: ["Not applicable, not zero, when no file was retrieved."], basis: "answer-key-set-measure" },
109
+ { id: "file-recall", name: "File recall", def: "Relevant retrieved files / expected files.", unit: "ratio", interp: "Higher means more expected files were retrieved.", caveats: ["Not applicable with no expected files."], basis: "answer-key-set-measure" },
110
+ { id: "symbol-precision", name: "Symbol precision", def: "Relevant retrieved symbol names / retrieved symbol names (exact, case-sensitive).", unit: "ratio", interp: "No fuzzy or semantic matching.", basis: "answer-key-set-measure" },
111
+ { id: "symbol-recall", name: "Symbol recall", def: "Relevant retrieved symbol names / expected symbol names (exact, case-sensitive).", unit: "ratio", interp: "No fuzzy or semantic matching.", basis: "answer-key-set-measure" },
112
+ { id: "fact-coverage", name: "Fact coverage", def: "Covered expected facts / all expected facts through explicit factIds-to-context mappings.", unit: "ratio", interp: "Unweighted; no fact-prose matching or model judge.", caveats: ["Unavailable when a required fact has no usable mapping."], basis: "answer-key-set-measure" },
113
+ { id: "irrelevant-context-ratio", name: "Irrelevant context ratio", def: "Irrelevant retrieved files / retrieved files.", unit: "ratio", interp: "A diagnostic complement of file precision, not an independent measure.", basis: "answer-key-set-measure" },
114
+ { id: "retrieved-token-count", name: "Retrieved token count", def: "The selected retrieval context's totalEstimatedTokens, ceil(characters / 4).", unit: "tokens", interp: "Context-size evidence.", caveats: [ESTIMATE_CAVEAT, "Not a per-file relevant-token attribution."], basis: "estimated" },
115
+ ],
116
+ },
117
+ {
118
+ pluginId: "retrieval-query-strategy-comparison",
119
+ docsSection: "Retrieval query strategy comparison metrics (v0.8.1)",
120
+ rows: [
121
+ { id: "retrieved-token-count", name: "Retrieved token count", def: "The measured retrieval payload size, ceil(character count / 4) through the existing estimator.", unit: "tokens", interp: "Context-size evidence.", caveats: [ESTIMATE_CAVEAT, "Not tokenizer-exact model usage and not a relevant-token attribution."], basis: "estimated" },
122
+ ],
123
+ },
124
+ {
125
+ pluginId: "context-pack-generation",
126
+ docsSection: "Context-pack generation metrics (v0.8.2)",
127
+ rows: [
128
+ { id: "estimated-token-count", name: "Estimated context tokens", def: "Estimated context size using ceil(character count / 4).", unit: "tokens", interp: "Not a provider tokenizer or billing measurement.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
129
+ { id: "tokens-saved", name: "Estimated tokens saved", def: "rawEstimatedTokens - packEstimatedTokens for a matched case.", unit: "tokens", interp: "Negative savings are valid and are not clamped.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
130
+ { id: "percent-saved", name: "Estimated percent saved", def: "tokensSaved / rawEstimatedTokens * 100 (zero when the raw estimate is zero).", unit: "percent", interp: "Negative values are valid and are not clamped.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
131
+ { id: "estimated-token-delta", name: "Estimated token delta", def: "Pack-minus-raw estimated tokens for a matched case.", unit: "tokens", interp: "Descriptive only; values are not combined into one figure.", caveats: [ESTIMATE_CAVEAT], basis: "estimated" },
132
+ { id: "file-f1-delta", name: "File F1 delta", def: "Pack-minus-raw file F1 for a matched case.", unit: "score", interp: "Descriptive only; values are not combined into one figure.", basis: "answer-key-set-measure" },
133
+ { id: "symbol-f1-delta", name: "Symbol F1 delta", def: "Pack-minus-raw symbol F1 for a matched case.", unit: "score", interp: "Descriptive only; values are not combined into one figure.", basis: "answer-key-set-measure" },
134
+ { id: "fact-coverage-delta", name: "Fact coverage delta", def: "Pack-minus-raw fact coverage for a matched case.", unit: "score", interp: "Descriptive only; values are not combined into one figure.", basis: "answer-key-set-measure" },
135
+ ],
136
+ },
137
+ {
138
+ pluginId: "context-strategy-comparison",
139
+ docsSection: "Experiment and run metrics",
140
+ rows: [
141
+ { id: "average-token-savings-percent", name: "Average token savings percent", def: "Average of tokenSavingsPercent: (rawTotalTokens - myDevKitTotalTokens) / rawTotalTokens * 100.", unit: "percent", interp: "Positive means my-dev-kit used fewer tokens; negative means it used more.", caveats: ["Only valid when both paired runs expose total tokens."], basis: "derived" },
142
+ { id: "average-duration-reduction-percent", name: "Average duration reduction percent", def: "Average of durationReductionPercent: (rawDurationMs - myDevKitDurationMs) / rawDurationMs * 100.", unit: "percent", interp: "Positive means my-dev-kit was faster; negative means it was slower.", caveats: ["Local machine noise affects timing."], basis: "derived" },
143
+ ],
144
+ },
145
+ ];
146
+ function slug(value) {
147
+ return value
148
+ .toLowerCase()
149
+ .replace(/[^a-z0-9]+/g, "-")
150
+ .replace(/^-+|-+$/g, "");
151
+ }
152
+ export function metricGlossaryAnchor(pluginId, metricId) {
153
+ return `metric-${slug(pluginId)}--${slug(metricId)}`;
154
+ }
155
+ function mergedGroups() {
156
+ const groups = GROUPS.map((group) => ({
157
+ ...group,
158
+ rows: [
159
+ ...group.rows,
160
+ ...(group.pluginId === "agent-success-rate" ? EXTRA_AGENT_SUCCESS_ROWS : []),
161
+ ...(APPENDED_ROWS[group.pluginId] ?? [])
162
+ ]
163
+ }));
164
+ return [...groups, ...EXTRA_GROUPS];
165
+ }
166
+ const DESCRIPTORS = (() => {
167
+ const out = [];
168
+ const anchors = new Map();
169
+ const push = (pluginId, docsSection, row) => {
170
+ const key = `${pluginId}#${row.id}`;
171
+ const anchor = metricGlossaryAnchor(pluginId, row.id);
172
+ if (anchors.has(anchor))
173
+ throw new Error(`Duplicate or colliding metric glossary entry: ${key}.`);
174
+ anchors.set(anchor, key);
175
+ out.push(Object.freeze({
176
+ key,
177
+ pluginId,
178
+ metricId: row.id,
179
+ displayName: row.name,
180
+ definition: row.def,
181
+ unit: row.unit ?? null,
182
+ anchor,
183
+ interpretation: row.interp,
184
+ caveats: Object.freeze([...(row.caveats ?? [])]),
185
+ measurementBasis: row.basis ?? "not-specified",
186
+ docsSection
187
+ }));
188
+ };
189
+ for (const group of mergedGroups())
190
+ for (const row of group.rows)
191
+ push(group.pluginId, group.docsSection, row);
192
+ for (const family of FAMILIES)
193
+ push(family.pluginId, family.docsSection, { id: family.template, ...family.row });
194
+ return Object.freeze(out);
195
+ })();
196
+ const BY_KEY = new Map(DESCRIPTORS.map((descriptor) => [descriptor.key, descriptor]));
197
+ const FAMILIES_BY_PLUGIN = new Map();
198
+ for (const family of FAMILIES) {
199
+ const list = FAMILIES_BY_PLUGIN.get(family.pluginId) ?? [];
200
+ list.push({ pattern: family.pattern, descriptor: BY_KEY.get(`${family.pluginId}#${family.template}`) });
201
+ FAMILIES_BY_PLUGIN.set(family.pluginId, list);
202
+ }
203
+ const PREFIX_BY_PLUGIN = new Map(GROUPS.filter((g) => g.prefix).map((g) => [g.pluginId, g.prefix]));
204
+ /** All glossary descriptors, in deterministic table order. */
205
+ export function listMetricDescriptors() {
206
+ return DESCRIPTORS;
207
+ }
208
+ /** The plugin-local metric id for a rendered generic metric id (strips a plugin's own id prefix). */
209
+ export function pluginLocalMetricId(pluginId, renderedMetricId) {
210
+ const prefix = PREFIX_BY_PLUGIN.get(pluginId);
211
+ return prefix !== undefined && renderedMetricId.startsWith(prefix) ? renderedMetricId.slice(prefix.length) : renderedMetricId;
212
+ }
213
+ /**
214
+ * Resolves a rendered metric to its descriptor. The identity is always plugin-scoped: the same short id under
215
+ * a different plugin is a different metric and is never matched. Unknown ids return null.
216
+ */
217
+ export function resolveMetricDescriptor(pluginId, renderedMetricId) {
218
+ const localId = pluginLocalMetricId(pluginId, renderedMetricId);
219
+ const exact = BY_KEY.get(`${pluginId}#${localId}`);
220
+ if (exact)
221
+ return exact;
222
+ // Bounded dynamic families: a closed pattern per plugin; an id that matches no pattern stays unknown.
223
+ for (const family of FAMILIES_BY_PLUGIN.get(pluginId) ?? [])
224
+ if (family.pattern.test(localId))
225
+ return family.descriptor;
226
+ return null;
227
+ }
@@ -0,0 +1,199 @@
1
+ // Additional metric glossary data (v0.9.3 completeness correction). Every definition below is condensed from the
2
+ // owning metric calculation code or docs/METRICS.md; nothing here defines a calculation. Static ids are listed
3
+ // explicitly; bounded dynamic id families are described by a closed pattern so an unknown id never matches.
4
+ const ESTIMATE = "Character-based estimate (ceil(characters / 4)); not provider token usage or billing telemetry.";
5
+ const UNAVAILABLE_NOT_ZERO = "Unavailable evidence is never converted to zero.";
6
+ export const EXTRA_AGENT_SUCCESS_ROWS = [
7
+ { id: "patchApplied", name: "Patch applied", def: "Whether the proposed patch was applied to the sandbox: true when applied, false when the patch pipeline attempted and failed.", unit: "boolean", interp: "Describes patch application only; it is not a task-success verdict.", caveats: ["Unavailable when no patch-pipeline evidence exists."], basis: "verification-verdict" },
8
+ { id: "optionalFactsTotal", name: "Optional facts (count)", def: "Behavior facts of the task that are not marked required.", unit: "count", interp: "Denominator context for optional-fact satisfaction.", basis: "derived" },
9
+ { id: "optionalFactsSatisfiedCount", name: "Optional facts satisfied (count)", def: "Optional behavior facts whose referenced post-edit checks all passed.", unit: "count", interp: "Read with optionalFactsTotal.", caveats: ["Unavailable when an optional fact cannot be determined or the patch was not applied."], basis: "derived" },
10
+ { id: "attemptCount", name: "Attempts (count)", def: "Provider attempts executed for the treatment: the initial attempt plus repair attempts.", unit: "count", interp: "Cost context for repair-enabled runs.", caveats: ["Reported only for real-agent runs with attempt history."], basis: "derived" },
11
+ { id: "repairAttemptCount", name: "Repair attempts (count)", def: "Repair attempts executed after the initial attempt.", unit: "count", interp: "Equals attemptCount minus one.", basis: "derived" },
12
+ { id: "evaluableCaseCount", name: "Evaluable cases (count)", def: "Cases with a determinate task-success verdict for the treatment; the denominator of taskSuccessRate.", unit: "count", interp: "Cases without a determinate verdict are excluded, not counted as failures.", basis: "derived" },
13
+ { id: "successfulCaseCount", name: "Successful cases (count)", def: "Evaluable cases whose task-success verdict is true; the numerator of taskSuccessRate.", unit: "count", interp: "Read with evaluableCaseCount.", basis: "derived" },
14
+ { id: "initialAttemptSuccessfulCount", name: "Initial-attempt successful cases (count)", def: "Cases with a determinate initial-attempt verdict whose attempt-1 task success is true.", unit: "count", interp: "A repaired success is never counted here.", basis: "derived" },
15
+ { id: "finalSuccessfulCount", name: "Final successful cases (count)", def: "Cases with a determinate final verdict whose final task success is true.", unit: "count", interp: "Includes repaired successes.", basis: "derived" },
16
+ { id: "repairAttemptedCaseCount", name: "Repair-attempted cases (count)", def: "Cases in which at least one repair attempt was executed.", unit: "count", interp: "Denominator context for repair success.", basis: "derived" },
17
+ { id: "repairedCaseCount", name: "Repaired cases (count)", def: "Repair-attempted cases with a determinate repair verdict whose repair succeeded (initial attempt false, final true).", unit: "count", interp: "Numerator of repairSuccessRate.", basis: "derived" },
18
+ { id: "meanAttemptsPerEvaluableCase", name: "Mean attempts per evaluable case", def: "Mean number of attempts over cases with a determinate final verdict.", unit: "count", interp: "Descriptive.", caveats: ["Unavailable when no case has a determinate final verdict."], basis: "derived" },
19
+ { id: "firstAttemptProviderDurationMs", name: "First-attempt provider duration", def: "Provider duration of the first attempt.", unit: "ms", interp: "Reported separately from the final attempt and the total.", caveats: ["Unavailable when no provider attempt ran."], basis: "measured" },
20
+ { id: "finalAttemptProviderDurationMs", name: "Final-attempt provider duration", def: "Provider duration of the last attempt executed.", unit: "ms", interp: "Reported separately from the first attempt and the total.", caveats: ["Unavailable when no provider attempt ran."], basis: "measured" },
21
+ { id: "firstAttemptProviderTokens", name: "First-attempt provider tokens", def: "Total token usage reported for the first attempt.", unit: "tokens", interp: "Read with the recorded token source and reliability.", caveats: ["Unavailable when no provider attempt ran or the provider reported no total."], basis: "agent-token-telemetry" },
22
+ { id: "finalAttemptProviderTokens", name: "Final-attempt provider tokens", def: "Total token usage reported for the last attempt executed.", unit: "tokens", interp: "Read with the recorded token source and reliability.", caveats: ["Unavailable when no provider attempt ran or the provider reported no total."], basis: "agent-token-telemetry" }
23
+ ];
24
+ /** Plugins whose static rows are new in this correction. */
25
+ export const EXTRA_GROUPS = [
26
+ {
27
+ pluginId: "incremental-change-staleness",
28
+ docsSection: "Incremental-change and staleness evidence (v0.6.2)",
29
+ rows: [
30
+ { id: "context-character-count", name: "Context characters", def: "Characters in the retrieved my-dev-kit context.", unit: "characters", interp: "Context size by characters.", caveats: ["Emitted only when the treatment produced retrieval evidence."], basis: "measured" },
31
+ { id: "context-estimated-token-count", name: "Estimated context tokens", def: "Character-based estimate of retrieved context tokens.", unit: "tokens", interp: "Estimated context size.", caveats: [ESTIMATE], basis: "estimated" },
32
+ { id: "operation-duration-ms", name: "Operation duration", def: "Measured retrieval-only duration (search, lookup, slice, source); excludes index build.", unit: "ms", interp: "Not agent duration.", basis: "measured" },
33
+ { id: "agent-correctness-score", name: "Agent correctness score", def: "Deterministic benchmark answer-key correctness score for the selected agent output.", unit: "score", interp: "Deterministic answer-key scoring.", caveats: ["Not semantic LLM judging."], basis: "answer-key-set-measure" },
34
+ { id: "affected-neighborhood-changed-file-count", name: "Changed indexed files", def: "Unique confirmed modified or missing files that the baseline index snapshot represents.", unit: "count", interp: "Observational evidence only.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
35
+ { id: "affected-neighborhood-changed-symbol-count", name: "Changed baseline symbols", def: "Baseline indexed symbol identities belonging to confirmed changed indexed files.", unit: "count", interp: "Observational evidence only.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
36
+ { id: "affected-neighborhood-node-count", name: "Affected graph nodes", def: "Unique resolved seed nodes plus their direct one-hop baseline graph neighbors.", unit: "count", interp: "Observational evidence only.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
37
+ { id: "affected-neighborhood-edge-count", name: "Affected graph edges", def: "Unique baseline graph edges incident to at least one resolved seed node.", unit: "count", interp: "Observational evidence only.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
38
+ { id: "affected-neighborhood-task-overlap-count", name: "Task-overlap nodes", def: "Unique baseline graph nodes both in the affected neighborhood and resolved from the task's expected files and symbols.", unit: "count", interp: "Observational evidence only.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
39
+ { id: "affected-neighborhood-task-overlap-percent", name: "Task-overlap percent", def: "Task-overlap nodes as a percentage of resolvable task graph nodes.", unit: "percent", interp: "Covers only the resolvable subset when task mapping is partial.", caveats: [UNAVAILABLE_NOT_ZERO], basis: "derived" },
40
+ { id: "upstream-refresh-fresh-extraction-file-count", name: "Upstream fresh-extraction files", def: "Files my-dev-kit freshly re-extracted during the refresh invocation.", unit: "count", interp: "Upstream execution evidence, not Lab task overlap.", caveats: ["Omitted when upstream reported no value."], basis: "measured" },
41
+ { id: "upstream-refresh-reused-file-count", name: "Upstream reused files", def: "Files my-dev-kit reused from the previous index during the refresh invocation.", unit: "count", interp: "Upstream execution evidence.", caveats: ["Omitted when upstream reported no value."], basis: "measured" },
42
+ { id: "upstream-refresh-forced-neighbor-reanalysis-file-count", name: "Upstream forced-neighbor reanalysis files", def: "Neighbor files my-dev-kit forced to re-analyze during the refresh invocation.", unit: "count", interp: "Upstream execution evidence.", caveats: ["Omitted when upstream reported no value."], basis: "measured" },
43
+ { id: "upstream-refresh-seed-file-count", name: "Upstream seed files", def: "Seed files my-dev-kit selected for the affected-neighborhood refresh.", unit: "count", interp: "Upstream execution evidence.", caveats: ["Absent when upstream reported none."], basis: "measured" },
44
+ { id: "upstream-refresh-seed-symbol-count", name: "Upstream seed symbols", def: "Seed symbols my-dev-kit selected for the affected-neighborhood refresh.", unit: "count", interp: "Upstream execution evidence.", caveats: ["Absent when upstream reported none."], basis: "measured" },
45
+ { id: "upstream-refresh-affected-node-count", name: "Upstream affected nodes", def: "Graph nodes my-dev-kit treated as affected during the refresh.", unit: "count", interp: "Upstream execution evidence, distinct from the Lab affected-neighborhood metrics.", basis: "measured" },
46
+ { id: "upstream-refresh-affected-edge-count", name: "Upstream affected edges", def: "Graph edges my-dev-kit treated as affected during the refresh.", unit: "count", interp: "Upstream execution evidence, distinct from the Lab affected-neighborhood metrics.", basis: "measured" },
47
+ { id: "incremental-change-staleness-scenario-count", name: "Scenario count", def: "Selected scenarios in this run.", unit: "count", interp: "Run-level count.", basis: "derived" },
48
+ { id: "incremental-change-staleness-observed-regression-count", name: "Observed stale regression count", def: "Scenarios whose no-refresh treatment was classified observed-stale-regression against full refresh.", unit: "count", interp: "Descriptive count; not a score.", basis: "derived" },
49
+ { id: "incremental-change-staleness-changed-files-applied-partial-count", name: "Changed-files applied-partial count", def: "Scenarios where the changed-files refresh was applied as a partial refresh.", unit: "count", interp: "Run-level count.", basis: "derived" },
50
+ { id: "incremental-change-staleness-changed-files-fallback-full-count", name: "Changed-files fallback-full count", def: "Scenarios where the changed-files refresh fell back to a full rebuild.", unit: "count", interp: "Run-level count.", basis: "derived" },
51
+ { id: "incremental-change-staleness-affected-neighborhood-applied-partial-count", name: "Affected-neighborhood applied-partial count", def: "Scenarios where the affected-neighborhood refresh was applied as a partial refresh.", unit: "count", interp: "Run-level count.", basis: "derived" },
52
+ { id: "incremental-change-staleness-affected-neighborhood-fallback-full-count", name: "Affected-neighborhood fallback-full count", def: "Scenarios where the affected-neighborhood refresh fell back to a full rebuild.", unit: "count", interp: "Run-level count.", basis: "derived" }
53
+ ]
54
+ }
55
+ ];
56
+ /** Rows appended to existing groups, keyed by plugin id. */
57
+ export const APPENDED_ROWS = {
58
+ "warm-index-reuse": [
59
+ { id: "warm-index-project-count", name: "Warm-index project count", def: "Benchmark-project groups in this run.", unit: "count", interp: "Run-level count.", basis: "derived" },
60
+ { id: "warm-index-task-count", name: "Warm-index task count", def: "Tasks evaluated across all benchmark-project groups.", unit: "count", interp: "Run-level count.", basis: "derived" },
61
+ { id: "warm-index-session-prepared-project-count", name: "Prepared warm-index session count", def: "Benchmark-project groups for which exactly one warm index session was prepared.", unit: "count", interp: "Run-level count.", basis: "derived" }
62
+ ],
63
+ "context-window-scaling": [
64
+ { id: "context-window-scaling-case-count", name: "Context-window-scaling case count", def: "Cases in the run summary.", unit: "count", interp: "Run-level count.", basis: "derived" },
65
+ { id: "context-window-scaling-budget-count", name: "Context budget count", def: "Selected estimated-token budgets in the run summary.", unit: "count", interp: "Run-level count.", basis: "derived" }
66
+ ],
67
+ "retrieval-precision-recall": [
68
+ { id: "missed-file-count", name: "Missed file count", def: "Expected files that were not retrieved.", unit: "count", interp: "Reported separately from symbols and facts; never combined into a score.", basis: "answer-key-set-measure" },
69
+ { id: "missed-symbol-count", name: "Missed symbol count", def: "Expected symbol names that were not retrieved.", unit: "count", interp: "Reported separately from files and facts.", basis: "answer-key-set-measure" },
70
+ { id: "uncovered-fact-count", name: "Uncovered fact count", def: "Expected facts not covered through the answer key's explicit factIds mappings.", unit: "count", interp: "Reported separately from files and symbols.", basis: "answer-key-set-measure" },
71
+ { id: "irrelevant-retrieved-file-count", name: "Irrelevant retrieved file count", def: "Retrieved files that are not in the expected file set.", unit: "count", interp: "Numerator of irrelevantContextRatio.", basis: "answer-key-set-measure" },
72
+ { id: "irrelevant-retrieved-symbol-count", name: "Irrelevant retrieved symbol count", def: "Retrieved symbol names that are not in the expected symbol set.", unit: "count", interp: "Reported separately from files.", basis: "answer-key-set-measure" },
73
+ { id: "case-count", name: "Cases", def: "Cases in the run summary.", unit: "count", interp: "Run-level count.", basis: "derived" },
74
+ { id: "completed-case-count", name: "Completed cases", def: "Cases whose retrieval completed.", unit: "count", interp: "Run-level count.", basis: "derived" },
75
+ { id: "partial-case-count", name: "Partial cases", def: "Cases whose retrieval evidence was partial.", unit: "count", interp: "Run-level count.", basis: "derived" },
76
+ { id: "failed-case-count", name: "Failed cases", def: "Cases that failed and have no retrieval result.", unit: "count", interp: "Run-level count.", basis: "derived" },
77
+ { id: "mean-file-precision", name: "Mean file precision", def: "Macro mean of per-case file precision over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
78
+ { id: "mean-file-recall", name: "Mean file recall", def: "Macro mean of per-case file recall over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
79
+ { id: "mean-symbol-precision", name: "Mean symbol precision", def: "Macro mean of per-case symbol precision over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
80
+ { id: "mean-symbol-recall", name: "Mean symbol recall", def: "Macro mean of per-case symbol recall over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
81
+ { id: "mean-fact-coverage", name: "Mean fact coverage", def: "Macro mean of per-case fact coverage over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
82
+ { id: "mean-irrelevant-context-ratio", name: "Mean irrelevant context ratio", def: "Macro mean of per-case irrelevant context ratio over available cases.", unit: "ratio", interp: "Unavailable and not-applicable cases are excluded from the mean, not counted as zero.", basis: "derived" },
83
+ { id: "total-retrieved-tokens", name: "Total retrieved tokens", def: "Sum of per-case retrieved token counts over cases with an available count.", unit: "tokens", interp: "Context-size evidence.", caveats: [ESTIMATE], basis: "estimated" },
84
+ { id: "mean-retrieved-tokens", name: "Mean retrieved tokens", def: "Mean of per-case retrieved token counts over cases with an available count.", unit: "tokens", interp: "Context-size evidence.", caveats: [ESTIMATE], basis: "estimated" },
85
+ { id: "total-missed-file-occurrences", name: "Total missed file occurrences", def: "Task-instance sum of missed files; identical paths in different cases are never deduplicated.", unit: "count", interp: "Excludes cases whose identity lists were not computable.", basis: "answer-key-set-measure" },
86
+ { id: "total-missed-symbol-occurrences", name: "Total missed symbol occurrences", def: "Task-instance sum of missed symbols; identical names in different cases are never deduplicated.", unit: "count", interp: "Excludes cases whose identity lists were not computable.", basis: "answer-key-set-measure" },
87
+ { id: "total-uncovered-fact-occurrences", name: "Total uncovered fact occurrences", def: "Task-instance sum of uncovered facts.", unit: "count", interp: "Excludes cases whose fact lists were not computable.", basis: "answer-key-set-measure" },
88
+ { id: "total-irrelevant-file-occurrences", name: "Total irrelevant file occurrences", def: "Task-instance sum of irrelevant retrieved files.", unit: "count", interp: "Excludes cases whose identity lists were not computable.", basis: "answer-key-set-measure" }
89
+ ],
90
+ "retrieval-query-strategy-comparison": [
91
+ { id: "file-precision", name: "File precision", def: "Relevant retrieved files / retrieved files.", unit: "ratio", interp: "Inherited v0.8.0 definition.", caveats: ["Not applicable, not zero, when no file was retrieved."], basis: "answer-key-set-measure" },
92
+ { id: "file-recall", name: "File recall", def: "Relevant retrieved files / expected files.", unit: "ratio", interp: "Inherited v0.8.0 definition.", basis: "answer-key-set-measure" },
93
+ { id: "file-f1", name: "File F1", def: "2 * TP_file / (2 * TP_file + FP_file + FN_file) over relevant retrieved, irrelevant retrieved and missed files.", unit: "ratio", interp: "Balanced file F1 computed from counts, not by averaging stored precision and recall.", caveats: ["Unavailable when an identity set is unavailable; not applicable when the denominator is zero."], basis: "answer-key-set-measure" },
94
+ { id: "symbol-precision", name: "Symbol precision", def: "Relevant retrieved symbol names / retrieved symbol names (exact, case-sensitive).", unit: "ratio", interp: "Inherited v0.8.0 definition.", basis: "answer-key-set-measure" },
95
+ { id: "symbol-recall", name: "Symbol recall", def: "Relevant retrieved symbol names / expected symbol names (exact, case-sensitive).", unit: "ratio", interp: "Inherited v0.8.0 definition.", basis: "answer-key-set-measure" },
96
+ { id: "symbol-f1", name: "Symbol F1", def: "2 * TP_symbol / (2 * TP_symbol + FP_symbol + FN_symbol) over exact unique symbol names.", unit: "ratio", interp: "Balanced symbol F1 computed from counts.", caveats: ["No fuzzy or semantic name matching."], basis: "answer-key-set-measure" },
97
+ { id: "fact-coverage", name: "Fact coverage", def: "Covered expected facts / all expected facts through explicit factIds-to-context mappings.", unit: "ratio", interp: "Inherited v0.8.0 definition.", basis: "answer-key-set-measure" },
98
+ { id: "irrelevant-context-ratio", name: "Irrelevant context ratio", def: "Irrelevant retrieved files / retrieved files.", unit: "ratio", interp: "Inherited v0.8.0 definition.", basis: "answer-key-set-measure" },
99
+ { id: "missed-file-count", name: "Missed file count", def: "Expected files that were not retrieved.", unit: "count", interp: "Reported separately from symbols and facts.", basis: "answer-key-set-measure" },
100
+ { id: "missed-symbol-count", name: "Missed symbol count", def: "Expected symbol names that were not retrieved.", unit: "count", interp: "Reported separately from files and facts.", basis: "answer-key-set-measure" },
101
+ { id: "uncovered-fact-count", name: "Uncovered fact count", def: "Expected facts not covered through explicit factIds mappings.", unit: "count", interp: "Reported separately from files and symbols.", basis: "answer-key-set-measure" },
102
+ { id: "irrelevant-retrieved-file-count", name: "Irrelevant retrieved file count", def: "Retrieved files that are not in the expected file set.", unit: "count", interp: "Numerator of irrelevantContextRatio.", basis: "answer-key-set-measure" },
103
+ { id: "irrelevant-retrieved-symbol-count", name: "Irrelevant retrieved symbol count", def: "Retrieved symbol names that are not in the expected symbol set.", unit: "count", interp: "Reported separately from files.", basis: "answer-key-set-measure" }
104
+ ],
105
+ "context-pack-generation": [
106
+ { id: "file-precision", name: "File precision", def: "Relevant retrieved files / retrieved files.", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
107
+ { id: "file-recall", name: "File recall", def: "Relevant retrieved files / expected files.", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
108
+ { id: "file-f1", name: "File F1", def: "2 * TP_file / (2 * TP_file + FP_file + FN_file).", unit: "ratio", interp: "Balanced file F1 computed from counts.", basis: "answer-key-set-measure" },
109
+ { id: "symbol-precision", name: "Symbol precision", def: "Relevant retrieved symbol names / retrieved symbol names (exact, case-sensitive).", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
110
+ { id: "symbol-recall", name: "Symbol recall", def: "Relevant retrieved symbol names / expected symbol names (exact, case-sensitive).", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
111
+ { id: "symbol-f1", name: "Symbol F1", def: "2 * TP_symbol / (2 * TP_symbol + FP_symbol + FN_symbol).", unit: "ratio", interp: "Balanced symbol F1 computed from counts.", basis: "answer-key-set-measure" },
112
+ { id: "fact-coverage", name: "Fact coverage", def: "Covered expected facts / all expected facts through explicit factIds mappings.", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
113
+ { id: "irrelevant-context-ratio", name: "Irrelevant context ratio", def: "Irrelevant retrieved files / retrieved files.", unit: "ratio", interp: "Reused v0.8.0 definition.", basis: "answer-key-set-measure" },
114
+ { id: "missed-file-count", name: "Missed file count", def: "Expected files that were not retrieved.", unit: "count", interp: "Reported separately from symbols and facts.", basis: "answer-key-set-measure" },
115
+ { id: "missed-symbol-count", name: "Missed symbol count", def: "Expected symbol names that were not retrieved.", unit: "count", interp: "Reported separately from files and facts.", basis: "answer-key-set-measure" },
116
+ { id: "uncovered-fact-count", name: "Uncovered fact count", def: "Expected facts not covered through explicit factIds mappings.", unit: "count", interp: "Reported separately from files and symbols.", basis: "answer-key-set-measure" }
117
+ ],
118
+ "context-strategy-comparison": [
119
+ { id: "total-runs", name: "Total runs", def: "Controlled experiment runs in the summary.", unit: "count", interp: "Run-level count.", basis: "derived" },
120
+ { id: "completed-runs", name: "Completed runs", def: "Controlled experiment runs that completed.", unit: "count", interp: "Run-level count.", basis: "derived" },
121
+ { id: "failed-runs", name: "Failed runs", def: "Controlled experiment runs that failed.", unit: "count", interp: "Run-level count.", basis: "derived" },
122
+ { id: "total-comparisons", name: "Total comparisons", def: "Raw-versus-guided comparisons in the summary.", unit: "count", interp: "Run-level count.", basis: "derived" },
123
+ { id: "average-correctness-delta", name: "Average correctness delta", def: "Average of per-comparison correctnessDelta, where correctnessDelta is the guided correctnessScore minus the raw correctnessScore.", unit: "score", interp: "Positive means the guided strategy scored higher.", caveats: ["Deterministic answer-key scoring; not semantic judging."], basis: "derived" },
124
+ { id: "correctness-score", name: "Correctness score", def: "Deterministic answer-key correctnessScore for the run: 0.25 * fileMatchScore + 0.25 * symbolMatchScore + 0.50 * factMatchScore.", unit: "score", interp: "Higher is better; a run passes at 0.70 or above with the required fact checks.", caveats: ["Not semantic judging."], basis: "answer-key-set-measure" },
125
+ { id: "duration-ms", name: "Duration", def: "Measured wall-clock duration of the normalized run.", unit: "ms", interp: "Lower is faster.", caveats: ["Includes local CLI overhead."], basis: "measured" },
126
+ { id: "total-tokens", name: "Total tokens", def: "Total token count reported for the run when available.", unit: "tokens", interp: "Used for token savings comparisons.", caveats: ["Emitted only when the run reported a numeric total; read with the recorded token source and reliability."], basis: "agent-token-telemetry" }
127
+ ]
128
+ };
129
+ const SCOPES = "overall|localized|cross-module|broad-change";
130
+ export const FAMILIES = [
131
+ {
132
+ pluginId: "context-window-scaling",
133
+ docsSection: "Context-window scaling evidence (v0.7.0)",
134
+ template: "budget-{tokens}-context-budget-utilization-percent",
135
+ pattern: /^budget-([1-9]\d{0,15})-context-budget-utilization-percent$/,
136
+ row: { name: "Context budget utilization", def: "estimatedContextTokens / budget * 100 for one selected budget cell.", unit: "percent", interp: "Not capped at 100; an over-budget context can exceed 100.", caveats: ["Unavailable when context size is unavailable."], basis: "derived" }
137
+ },
138
+ {
139
+ pluginId: "context-window-scaling",
140
+ docsSection: "Context-window scaling evidence (v0.7.0)",
141
+ template: "budget-{tokens}-correctness-score",
142
+ pattern: /^budget-([1-9]\d{0,15})-correctness-score$/,
143
+ row: { name: "Correctness score by budget", def: "The existing deterministic answer-key correctness score for one budget cell where the treatment context fits.", unit: "score", interp: "Shared by every budget cell where the treatment's context fits.", caveats: ["Unavailable for a context-too-large cell; not converted to zero."], basis: "answer-key-set-measure" }
144
+ },
145
+ {
146
+ pluginId: "context-window-scaling",
147
+ docsSection: "Context-window scaling evidence (v0.7.0)",
148
+ template: "budget-{tokens}-success",
149
+ pattern: /^budget-([1-9]\d{0,15})-success$/,
150
+ row: { name: "Success by budget", def: "True only when the context fits the budget and the available correctness evidence passes.", unit: "boolean", interp: "A context-too-large cell is an available unsuccessful cell.", caveats: ["Operational or evidence-unavailable cells remain unavailable."], basis: "derived" }
151
+ },
152
+ ...[
153
+ ["fit-count", "Cases whose context fits", "Cases whose estimated context tokens are less than or equal to the budget, for one budget and treatment.", "count"],
154
+ ["context-too-large-count", "Cases whose context is too large", "Cases whose estimated context tokens exceed the budget, for one budget and treatment.", "count"],
155
+ ["successful-cell-count", "Successful cells", "Budget cells counted as successful for one budget and treatment.", "count"],
156
+ ["success-rate-percent", "Success rate", "Successful cells over success-evidence-available cells, as a percentage.", "percent"],
157
+ ["mean-correctness-score", "Mean correctness score", "Mean of available existing correctness scores; unavailable correctness is excluded, not zero.", "score"],
158
+ ["mean-context-budget-utilization-percent", "Mean context budget utilization", "Mean of measured context budget utilization percentages; uncapped, so it may exceed 100.", "percent"]
159
+ ].map(([suffix, name, def, unit]) => ({
160
+ pluginId: "context-window-scaling",
161
+ docsSection: "Context-window scaling evidence (v0.7.0)",
162
+ template: `context-budget-{tokens}-${suffix}`,
163
+ pattern: new RegExp(`^context-budget-([1-9]\\d{0,15})-${suffix}$`),
164
+ row: { name, def, unit, interp: "Descriptive aggregate for one budget and treatment.", caveats: ["Null (unavailable) when no cell provides the evidence."], basis: "derived" }
165
+ })),
166
+ ...["retrieval-query-strategy-comparison", "context-pack-generation"].flatMap((pluginId) => {
167
+ const section = pluginId === "context-pack-generation" ? "Context-pack generation metrics (v0.8.2)" : "Retrieval query strategy comparison metrics (v0.8.1)";
168
+ const scoped = (suffix, name, def, unit, basis = "derived") => ({
169
+ pluginId,
170
+ docsSection: section,
171
+ template: `{scope}-${suffix}`,
172
+ pattern: new RegExp(`^(?:${SCOPES})-${suffix}$`),
173
+ row: { name, def, unit, interp: "Descriptive per task-type scope; values are not combined or ordered.", caveats: ["Computed over matched comparison cases only; null (unavailable) when none matched."], basis }
174
+ });
175
+ return [
176
+ scoped("comparison-case-count", "Matched comparison cases", "Cases that participate in the cross-strategy or paired aggregation for the scope.", "count"),
177
+ scoped("mean-file-f1", "Mean file F1", "Unweighted mean of per-case file F1 over matched comparison cases.", "ratio"),
178
+ scoped("mean-symbol-f1", "Mean symbol F1", "Unweighted mean of per-case symbol F1 over matched comparison cases.", "ratio"),
179
+ scoped("mean-fact-coverage", "Mean fact coverage", "Unweighted mean of per-case fact coverage over matched comparison cases.", "ratio"),
180
+ pluginId === "context-pack-generation"
181
+ ? scoped("mean-estimated-token-count", "Mean estimated token count", "Unweighted mean of per-case estimated context tokens over matched comparison cases.", "tokens", "estimated")
182
+ : scoped("mean-retrieved-token-count", "Mean retrieved token count", "Unweighted mean of per-case retrieved token counts over matched comparison cases.", "tokens", "estimated")
183
+ ];
184
+ }),
185
+ {
186
+ pluginId: "context-strategy-comparison",
187
+ docsSection: "Experiment and run metrics",
188
+ template: "{comparison}-token-savings-percent",
189
+ pattern: /^.+-token-savings-percent$/,
190
+ row: { name: "Token savings (comparison)", def: "(rawTotalTokens - myDevKitTotalTokens) / rawTotalTokens * 100 for one raw-versus-guided comparison.", unit: "percent", interp: "Positive means my-dev-kit used fewer tokens; negative means it used more.", caveats: ["Only valid when both paired runs expose total tokens; otherwise unavailable."], basis: "derived" }
191
+ },
192
+ {
193
+ pluginId: "context-strategy-comparison",
194
+ docsSection: "Experiment and run metrics",
195
+ template: "{comparison}-correctness-delta",
196
+ pattern: /^.+-correctness-delta$/,
197
+ row: { name: "Correctness delta (comparison)", def: "Guided correctnessScore minus raw correctnessScore for one comparison.", unit: "score", interp: "Positive means the guided strategy scored higher.", caveats: ["Unavailable when either run lacks a correctness score; deterministic answer-key scoring."], basis: "derived" }
198
+ }
199
+ ];