@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
@@ -1,9 +1,6 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
1
  import { makeEvidence } from "../exploitEvidence.js";
5
2
  import { getPayloadsForGroup } from "../payloadCorpus.js";
6
- import { runSecurityCommand } from "../../commandRunner.js";
3
+ import { buildSecurityCommandSpawnOptions, runSecurityCommand } from "../../commandRunner.js";
7
4
  // ---------------------------------------------------------------------------
8
5
  // v0.2.2 Batch 3 — subprocess injection scenario.
9
6
  //
@@ -15,31 +12,60 @@ import { runSecurityCommand } from "../../commandRunner.js";
15
12
  // that no shell re-interpreted it (splitting it into multiple commands via
16
13
  // `;`, `&&`, backticks, `$()`, or `|`).
17
14
  //
18
- // Also does a bounded static check that the command-execution helpers this
19
- // scenario exercises are built with `shell: false` (source inspection of a
20
- // known-small file, not a general code scanner).
15
+ // Also verifies at runtime, through the compiled command runner's own
16
+ // spawn-options builder (the exact value runSecurityCommand() spawns with),
17
+ // that the runner does not enable a shell. No TypeScript source is read, so
18
+ // the proof works from an installed package.
21
19
  // ---------------------------------------------------------------------------
22
20
  const ECHO_ARGV_SCRIPT = "process.stdout.write(JSON.stringify(process.argv.slice(1)))";
23
- function commandRunnerSourcePath() {
24
- const here = path.dirname(fileURLToPath(import.meta.url));
25
- return path.resolve(here, "..", "..", "commandRunner.ts");
21
+ const DEFAULT_DEPS = {
22
+ runCommand: runSecurityCommand,
23
+ readSpawnOptions: (cwd) => buildSecurityCommandSpawnOptions({ cwd, env: process.env }),
24
+ };
25
+ // Runtime proof that the applicable runner's spawn path does not use a shell.
26
+ // A strict `shell === false` is the only accepted safe value; a truthy shell
27
+ // is demonstrated unsafe; a missing/malformed/throwing proof is unavailable.
28
+ function proveShellDisabled(deps, cwd) {
29
+ let options;
30
+ try {
31
+ options = deps.readSpawnOptions(cwd);
32
+ }
33
+ catch (error) {
34
+ return {
35
+ state: "unavailable",
36
+ detail: `Spawn options could not be obtained from the command runner: ${error instanceof Error ? error.message : String(error)}`,
37
+ };
38
+ }
39
+ if (typeof options !== "object" || options === null || !("shell" in options)) {
40
+ return { state: "unavailable", detail: "Command runner spawn options did not declare a shell setting." };
41
+ }
42
+ const shell = options.shell;
43
+ if (shell === false)
44
+ return { state: "safe" };
45
+ if (shell === true || typeof shell === "string") {
46
+ return { state: "unsafe", detail: "Command runner spawn options enable a shell." };
47
+ }
48
+ return { state: "unavailable", detail: "Command runner spawn options declared a malformed shell setting." };
26
49
  }
27
50
  export const SUBPROCESS_INJECTION_SCENARIO = {
28
51
  id: "subprocess-injection-safety",
29
52
  title: "Subprocess injection: shell metacharacter payloads are passed as literal arguments",
30
- description: "Runs a benign argv-echoing Node script with each subprocess-injection payload as a single spawn argument and confirms it round-trips as exactly one literal string, proving no shell re-interpretation. Also checks that commandRunner.ts constructs subprocess calls with shell:false.",
53
+ description: "Runs a benign argv-echoing Node script with each subprocess-injection payload as a single spawn argument and confirms it round-trips as exactly one literal string, proving no shell re-interpretation. Also verifies at runtime that the command runner constructs subprocess calls with shell:false.",
31
54
  checkId: "subprocess",
32
55
  applicableProfiles: [],
33
56
  severityBaseline: "blocker",
34
57
  verdictImpact: "tool-framework-blocker",
35
- expectedSafeBehavior: "Every subprocess-injection payload round-trips as a single literal argv element; commandRunner.ts uses shell:false.",
58
+ expectedSafeBehavior: "Every subprocess-injection payload round-trips as a single literal argv element; the command runner uses shell:false.",
36
59
  evidenceRequirements: ["command", "observation"],
37
- run: async (ctx) => {
60
+ run: (ctx) => runSubprocessInjectionScenario(ctx, DEFAULT_DEPS),
61
+ };
62
+ export async function runSubprocessInjectionScenario(ctx, deps) {
63
+ {
38
64
  const evidence = [];
39
65
  const notLiteral = [];
40
66
  const payloads = getPayloadsForGroup("subprocess-injection");
41
67
  for (const p of payloads) {
42
- const cmd = await runSecurityCommand({
68
+ const cmd = await deps.runCommand({
43
69
  command: process.execPath,
44
70
  args: ["-e", ECHO_ARGV_SCRIPT, p.value],
45
71
  cwd: ctx.toolRoot,
@@ -70,42 +96,34 @@ export const SUBPROCESS_INJECTION_SCENARIO = {
70
96
  rawPreview: p.value,
71
97
  }));
72
98
  }
73
- let shellFalseConfirmed = false;
74
- let sourceReadable = true;
75
- try {
76
- const source = fs.readFileSync(commandRunnerSourcePath(), "utf8");
77
- shellFalseConfirmed = /shell:\s*false/.test(source);
78
- }
79
- catch {
80
- sourceReadable = false;
81
- }
99
+ const proof = proveShellDisabled(deps, ctx.toolRoot);
82
100
  evidence.push(makeEvidence({
83
101
  kind: "observation",
84
- source: "static check: src/securityValidation/commandRunner.ts",
85
- confidence: sourceReadable ? "high" : "low",
102
+ source: "runtime check: buildSecurityCommandSpawnOptions() used by runSecurityCommand()",
103
+ confidence: proof.state === "unavailable" ? "low" : "high",
86
104
  expectedBehavior: "Subprocess calls should be constructed with shell:false.",
87
- observedBehavior: sourceReadable
88
- ? shellFalseConfirmed
89
- ? "shell:false found in commandRunner.ts."
90
- : "shell:false NOT found in commandRunner.ts."
91
- : "Could not read commandRunner.ts source for static confirmation.",
105
+ observedBehavior: proof.state === "safe"
106
+ ? "Command runner spawn options declare shell:false."
107
+ : proof.state === "unsafe"
108
+ ? proof.detail
109
+ : `Could not verify shell:false: ${proof.detail}`,
92
110
  }));
93
- if (notLiteral.length > 0 || (sourceReadable && !shellFalseConfirmed)) {
111
+ if (notLiteral.length > 0 || proof.state === "unsafe") {
94
112
  return {
95
113
  status: "failed",
96
114
  confidence: "high",
97
115
  evidence,
98
116
  recommendation: notLiteral.length > 0
99
117
  ? `Investigate argument construction — payload(s) did not round-trip literally: ${notLiteral.join(", ")}.`
100
- : "commandRunner.ts must construct child_process.spawn calls with shell:false.",
118
+ : "The command runner must construct child_process.spawn calls with shell:false.",
101
119
  };
102
120
  }
103
- if (!sourceReadable) {
121
+ if (proof.state === "unavailable") {
104
122
  return {
105
123
  status: "blocked",
106
124
  confidence: "low",
107
125
  evidence,
108
- recommendation: "Could not statically confirm shell:false; argv round-trip evidence is still valid.",
126
+ recommendation: "Could not verify shell:false for the command runner; argv round-trip evidence alone is not sufficient.",
109
127
  };
110
128
  }
111
129
  return {
@@ -113,5 +131,5 @@ export const SUBPROCESS_INJECTION_SCENARIO = {
113
131
  confidence: "high",
114
132
  evidence,
115
133
  };
116
- },
117
- };
134
+ }
135
+ }
@@ -5,6 +5,18 @@ import { resolveCommand } from "../core/resolveCommand.js";
5
5
  export function resolveNpmCommand() {
6
6
  return "npm";
7
7
  }
8
+ // The single place the spawn options for security commands are constructed.
9
+ // runSecurityCommand() spawns with exactly this result, so the subprocess
10
+ // injection scenario can verify `shell: false` at runtime from the compiled
11
+ // module instead of reading unshipped TypeScript source.
12
+ export function buildSecurityCommandSpawnOptions(options) {
13
+ return {
14
+ cwd: options.cwd,
15
+ shell: false,
16
+ stdio: ["ignore", "pipe", "pipe"],
17
+ env: options.env,
18
+ };
19
+ }
8
20
  // Runs a security check command with an argument array (no shell interpolation).
9
21
  // npm audit returns exit code 1 on vulnerabilities; that is not treated as a
10
22
  // spawn failure here — the caller inspects the output and exit code separately.
@@ -52,12 +64,7 @@ export async function runSecurityCommand(options) {
52
64
  let timeout;
53
65
  let child;
54
66
  try {
55
- child = spawn(command, args, {
56
- cwd: options.cwd,
57
- shell: false,
58
- stdio: ["ignore", "pipe", "pipe"],
59
- env,
60
- });
67
+ child = spawn(command, args, buildSecurityCommandSpawnOptions({ cwd: options.cwd, env }));
61
68
  }
62
69
  catch (spawnErr) {
63
70
  resolve({
package/docs/METRICS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Metrics
2
2
 
3
- This document is the canonical metric glossary for my-dev-kit-lab. It defines every metric that appears in benchmark profiles, prompt variants, controlled experiment artifacts, and rendered reports across the registered experiment plugins (`context-strategy-comparison`, `warm-index-reuse`, `incremental-change-staleness`, `context-window-scaling`, `retrieval-precision-recall`, `retrieval-query-strategy-comparison`, and `context-pack-generation`).
3
+ This document is the canonical metric glossary for my-dev-kit-lab. It defines every metric that appears in benchmark profiles, prompt variants, controlled experiment artifacts, and rendered reports across the registered experiment plugins (`context-strategy-comparison`, `warm-index-reuse`, `incremental-change-staleness`, `context-window-scaling`, `retrieval-precision-recall`, `retrieval-query-strategy-comparison`, `context-pack-generation`, and `agent-success-rate`).
4
4
 
5
5
  Related documentation:
6
6
  - [ARCHITECTURE.md](ARCHITECTURE.md) — how metrics flow through the pipeline
@@ -947,3 +947,13 @@ Token evidence levels are `provider-reported`, `cli-reported`, `parsed-from-outp
947
947
  The plugin-specific progress projection is `my-dev-kit-lab-agent-success-rate-campaign-progress-v1`; the writer publishes immutable revision-specific JSON, HTML, and text snapshots. It accounts for selected cases and treatment units, while execution epochs remain recovery history and are excluded from selected-unit counts. Paused provider-limit and uncertain states remain distinct. Partial progress is not a complete scientific analysis artifact.
948
948
 
949
949
  Evidence quality is metric-specific: task outcomes, token measurements, and duration can have different strong, partial, or unavailable classifications. Strong describes the completeness and provenance of the available evidence; it is not statistical significance. Partial evidence does not mean task failure. Missing token evidence is unavailable, never zero; estimated tokens are not provider usage. Duration is Lab-measured wall-clock time, not provider-native time. Comparisons use matched completed cases and state their denominator and exclusions; the existing paired direction is preserved. These descriptive comparisons establish no causal effect, winner, p-value, or general provider performance.
950
+
951
+ ## v0.9.3 report metric evidence and prompt comparison
952
+
953
+ The plugin report projects recorded metrics into value, unit, availability, reason, provenance, reliability, variant/case identity, and an optional glossary anchor. It adds deterministic caveats for unavailable/not-applicable values, estimates, unknown token provenance/reliability, partial campaign telemetry, and missing glossary metadata. These are presentation projections; the existing JSON report and scientific formulas are unchanged, and the text report presents the same caveats from the same projection. Unknown provenance remains unknown and is never promoted to high reliability.
954
+
955
+ The descriptor registry in `src/report/experiments/metricGlossary.ts` covers every metric id emitted by the registered experiment plugins. Static ids are listed explicitly; bounded dynamic families are matched by closed patterns: context-window per-budget ids (`budget-<tokens>-utilization/correctness/success` and `context-budget-<tokens>-fit/too-large/successful/success-rate/mean-correctness/mean-utilization`), per task-locality scope ids (`overall|localized|cross-module|broad-change`-prefixed comparison counts and means for retrieval-query-strategy-comparison and context-pack-generation) and per-comparison `-token-savings-percent` / `-correctness-delta` ids of the context-strategy comparison. Definitions are condensed from the calculation owners (and this document); an id outside the static and family sets is rendered without a definition or link and produces a missing-metadata caveat. Agent-success attempt, repair, optional-fact, `patchApplied` and per-attempt provider duration/token metrics are defined from `analysis.ts`/`repairAnalysis.ts`; incremental-change count, upstream-refresh and run-level metrics are defined from the plugin's own metric descriptions. Per-attempt token provenance and reliability are preserved where the execution evidence records them, an aggregate (`totalProviderTokens`, cumulative warm-index tokens) is attributed a provenance only when every contributing measurement is known and agrees, and an aggregate reliability only when all contributors share it.
956
+
957
+ The standalone prompt comparison reports matched first-attempt invalid-output rates and reports persisted task-success verdicts separately. The numerator requires persisted proof of a completed provider response that violated the prompt/response contract (`OUTPUT_CONTRACT_*` error, verified malformed diff, or a named empty completed answer); a bare provider `invalid-output` status, provider timeouts, unavailability, limits, transport/infrastructure failures, policy rejections and failed task checks are excluded or valid, never counted as invalid. The denominator is eligible completed first-attempt responses on units matched on both sides. Evidence is verified through the campaign store's complete checkpoint validation against trusted task definitions authenticated by task digest. A V1 identity does not bind a template, and a self-validating sidecar plus intact prompt hash is insufficient to prove historical template identity. Without independent trustworthy run-bound proof, V1 identity is `UNVERIFIED` and the comparison is `INCOMPARABLE`.
958
+
959
+ No matched paid Codex/Claude smoke was authorized. Observed invalid-output-rate improvement remains **NOT_VERIFIED** and must not be claimed. The v0.9.3 release gate covers reproducible prompt-contract classification, trustworthy run-bound identity, matched comparison eligibility/denominators, and report correctness; real-provider effectiveness is a separate, opt-in empirical objective, not a readiness dependency. Prompt comparison capability alone does not demonstrate improvement.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dailephd/my-dev-kit-lab",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "type": "module",
5
5
  "description": "Evidence, benchmark, and evaluation companion for my-dev-kit.",
6
6
  "bin": {