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

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 (44) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +9 -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/evaluation/affectedNeighborhood.js +140 -32
  12. package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +23 -1
  13. package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +7 -2
  14. package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +29 -4
  15. package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +1 -1
  16. package/dist/src/experiments/plugins/agentSuccessRate/config.js +47 -2
  17. package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +4 -2
  18. package/dist/src/experiments/plugins/agentSuccessRate/promptContract.js +121 -0
  19. package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +32 -2
  20. package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +105 -11
  21. package/dist/src/experiments/plugins/agentSuccessRate/strictDiffResponse.js +312 -0
  22. package/dist/src/gallery/galleryPathSafety.js +111 -0
  23. package/dist/src/gallery/readGallerySources.js +542 -0
  24. package/dist/src/gallery/writeGalleryManifest.js +57 -3
  25. package/dist/src/report/experiments/buildPluginExperimentReport.js +4 -16
  26. package/dist/src/report/experiments/htmlSafety.js +32 -0
  27. package/dist/src/report/experiments/metricEvidence.js +389 -0
  28. package/dist/src/report/experiments/metricGlossary.js +227 -0
  29. package/dist/src/report/experiments/metricGlossaryCatalog.js +199 -0
  30. package/dist/src/report/experiments/promptTemplateComparisonEvidence.js +377 -0
  31. package/dist/src/report/experiments/promptTemplateComparisonModel.js +436 -0
  32. package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +158 -64
  33. package/dist/src/report/experiments/renderPluginExperimentReportText.js +22 -677
  34. package/dist/src/report/experiments/renderPromptTemplateComparison.js +192 -0
  35. package/dist/src/report/experiments/renderSpecializedTextSections.js +633 -0
  36. package/dist/src/report/experiments/reportSectionRegistry.js +79 -0
  37. package/dist/src/report/experiments/specializedReportSections.js +142 -0
  38. package/dist/src/report/experiments/writePluginExperimentReports.js +4 -2
  39. package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +146 -133
  40. package/dist/src/securityValidation/attackScenarios/scenarios/subprocessInjectionScenario.js +54 -36
  41. package/dist/src/securityValidation/commandRunner.js +13 -6
  42. package/dist/src/securityValidation/packageChecks/parseNpmPackDryRun.js +46 -21
  43. package/docs/METRICS.md +11 -1
  44. package/package.json +3 -3
@@ -1,13 +1,25 @@
1
- import { renderContextStrategyComparisonV043Html } from "./renderContextStrategyComparisonV043Html.js";
2
- import { renderWarmIndexReuseHtml } from "./renderWarmIndexReuseHtml.js";
3
- import { renderIncrementalChangeStalenessHtml } from "./renderIncrementalChangeStalenessHtml.js";
4
- import { renderContextWindowScalingHtml } from "./renderContextWindowScalingHtml.js";
5
- import { renderRetrievalPrecisionRecallHtml } from "./renderRetrievalPrecisionRecallHtml.js";
6
- import { renderRetrievalQueryStrategyComparisonHtml } from "./renderRetrievalQueryStrategyComparisonHtml.js";
7
- import { renderContextPackGenerationHtml } from "./renderContextPackGenerationHtml.js";
8
- import { renderAgentSuccessRateHtml } from "./renderAgentSuccessRateHtml.js";
9
- export function renderPluginExperimentReportHtml(report) {
1
+ import { getSpecializedReportSectionRegistry } from "./specializedReportSections.js";
2
+ import { escapeHtml, safeInternalHref, sectionAnchorId } from "./htmlSafety.js";
3
+ import { buildMetricEvidenceCaveats, buildMetricEvidenceViews } from "./metricEvidence.js";
4
+ import { listMetricDescriptors } from "./metricGlossary.js";
5
+ export function renderPluginExperimentReportHtml(report, evidence) {
10
6
  const redactedTarget = report.target.privacyProjection === "external-local-redacted";
7
+ const views = buildMetricEvidenceViews(report, evidence);
8
+ const caveats = buildMetricEvidenceCaveats(report, views);
9
+ const glossary = glossaryDescriptorsFor(views);
10
+ const family = renderSpecializedSections(report);
11
+ const nav = [
12
+ { anchor: "section-plugin-target", label: "Plugin And Target" },
13
+ { anchor: "section-interpretation", label: "Interpretation" },
14
+ { anchor: "section-variants", label: "Variants" },
15
+ { anchor: "section-cases", label: "Cases" },
16
+ { anchor: "section-metrics", label: "Metrics" },
17
+ { anchor: "section-evidence-caveats", label: "Evidence Caveats" },
18
+ ...family.nav,
19
+ { anchor: "section-findings", label: "Findings" },
20
+ { anchor: "section-artifacts", label: "Artifacts" },
21
+ ...(glossary.length > 0 ? [{ anchor: "section-metric-glossary", label: "Metric Glossary" }] : []),
22
+ ];
11
23
  return `<!DOCTYPE html>
12
24
  <html lang="en">
13
25
  <head>
@@ -22,11 +34,19 @@ export function renderPluginExperimentReportHtml(report) {
22
34
  h2 { margin: 0 0 12px; font-size: 22px; }
23
35
  h3 { margin: 14px 0 8px; font-size: 17px; }
24
36
  p, li { line-height: 1.45; }
25
- section, .hero { background: #fff; border: 1px solid #d7e0ea; border-radius: 8px; padding: 18px; margin-bottom: 16px; }
26
- table { width: 100%; border-collapse: collapse; margin: 8px 0 16px; background: #fff; }
37
+ a { color: #0b4f9c; }
38
+ a:focus-visible, .table-wrap:focus-visible { outline: 3px solid #1a5fb4; outline-offset: 2px; }
39
+ .skip-link { position: absolute; left: -9999px; top: 0; background: #fff; padding: 8px 12px; z-index: 10; }
40
+ .skip-link:focus { left: 8px; top: 8px; }
41
+ section, .hero, .report-nav { background: #fff; border: 1px solid #d7e0ea; border-radius: 8px; padding: 18px; margin-bottom: 16px; }
42
+ .report-nav h2 { font-size: 17px; }
43
+ .report-nav ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px 16px; }
44
+ .table-wrap { overflow-x: auto; margin: 8px 0 16px; }
45
+ table { width: 100%; border-collapse: collapse; background: #fff; }
46
+ caption { text-align: left; font-weight: 700; padding: 4px 0; }
27
47
  th, td { border: 1px solid #d9e1ea; padding: 8px 10px; text-align: left; vertical-align: top; }
28
48
  th { background: #edf2f7; }
29
- code { font-family: Consolas, "Courier New", monospace; }
49
+ code { font-family: Consolas, "Courier New", monospace; overflow-wrap: anywhere; }
30
50
  .muted { color: #5b6b7b; }
31
51
  .grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
32
52
  .card { border: 1px solid #d9e1ea; border-radius: 6px; padding: 12px; background: #fbfcfe; }
@@ -34,11 +54,21 @@ export function renderPluginExperimentReportHtml(report) {
34
54
  .status-completed { background: #dcf4e4; color: #0c6b30; }
35
55
  .status-partial, .status-skipped { background: #fff0ce; color: #785400; }
36
56
  .status-failed { background: #fde2e2; color: #8a1f1f; }
57
+ .availability-available { background: #dcf4e4; color: #0c6b30; }
58
+ .availability-unavailable { background: #fde2e2; color: #8a1f1f; border: 1px solid #8a1f1f; }
59
+ .availability-not-applicable { background: #e8eef7; color: #26405e; border: 1px dashed #26405e; }
60
+ .caveat-warning { border-left: 4px solid #a06a00; }
61
+ .caveat-info { border-left: 4px solid #3c6ea8; }
62
+ .caveats { list-style: none; margin: 0; padding: 0; }
63
+ .caveats li { padding: 6px 10px; margin-bottom: 8px; background: #fbfcfe; }
64
+ dt { font-weight: 700; margin-top: 12px; }
65
+ dd { margin: 4px 0 0 16px; }
37
66
  @media (max-width: 860px) { main { padding: 14px; } .grid { grid-template-columns: 1fr; } table { font-size: 13px; } }
38
67
  </style>
39
68
  </head>
40
69
  <body>
41
- <main>
70
+ <a class="skip-link" href="#main-content">Skip to report content</a>
71
+ <main id="main-content">
42
72
  <header class="hero">
43
73
  <h1>${escapeHtml(report.plugin.name)}</h1>
44
74
  <p>${escapeHtml(report.plugin.description)}</p>
@@ -46,9 +76,14 @@ export function renderPluginExperimentReportHtml(report) {
46
76
  <p class="muted">Generated ${escapeHtml(report.metadata.generatedAt)} for run <code>${escapeHtml(report.metadata.runId)}</code></p>
47
77
  </header>
48
78
 
49
- <section>
50
- <h2>Plugin And Target</h2>
51
- ${table(["Field", "Value"], [
79
+ <nav class="report-nav" aria-labelledby="report-nav-title">
80
+ <h2 id="report-nav-title">Report sections</h2>
81
+ <ul>${nav.map(renderNavLink).join("")}</ul>
82
+ </nav>
83
+
84
+ <section id="section-plugin-target" aria-labelledby="section-plugin-target-title">
85
+ <h2 id="section-plugin-target-title">Plugin And Target</h2>
86
+ ${table("Plugin and target", ["Field", "Value"], [
52
87
  ["Plugin ID", report.plugin.id],
53
88
  ["Plugin schema", report.plugin.schemaVersion],
54
89
  ["Mode", report.target.mode],
@@ -62,15 +97,15 @@ export function renderPluginExperimentReportHtml(report) {
62
97
  ])}
63
98
  </section>
64
99
 
65
- <section>
66
- <h2>Interpretation</h2>
100
+ <section id="section-interpretation" aria-labelledby="section-interpretation-title">
101
+ <h2 id="section-interpretation-title">Interpretation</h2>
67
102
  <p>${escapeHtml(report.interpretation.summary)}</p>
68
103
  <p><strong>Recommended next step:</strong> ${escapeHtml(report.interpretation.recommendedNextStep)}</p>
69
104
  </section>
70
105
 
71
- <section>
72
- <h2>Variants</h2>
73
- ${table(["Variant", "Completed", "Partial", "Failed", "Skipped"], report.variants.map((variant) => [
106
+ <section id="section-variants" aria-labelledby="section-variants-title">
107
+ <h2 id="section-variants-title">Variants</h2>
108
+ ${table("Variant outcome counts", ["Variant", "Completed", "Partial", "Failed", "Skipped"], report.variants.map((variant) => [
74
109
  `${variant.name} (${variant.id})`,
75
110
  String(variant.completedOutcomes),
76
111
  String(variant.partialOutcomes),
@@ -79,9 +114,9 @@ export function renderPluginExperimentReportHtml(report) {
79
114
  ]))}
80
115
  </section>
81
116
 
82
- <section>
83
- <h2>Cases</h2>
84
- ${table(["Case", "Status", "Completed", "Partial", "Failed", "Skipped"], report.cases.map((experimentCase) => [
117
+ <section id="section-cases" aria-labelledby="section-cases-title">
118
+ <h2 id="section-cases-title">Cases</h2>
119
+ ${table("Case outcome counts", ["Case", "Status", "Completed", "Partial", "Failed", "Skipped"], report.cases.map((experimentCase) => [
85
120
  `${experimentCase.name} (${experimentCase.id})`,
86
121
  experimentCase.status,
87
122
  String(experimentCase.completedOutcomes),
@@ -91,36 +126,22 @@ export function renderPluginExperimentReportHtml(report) {
91
126
  ]))}
92
127
  </section>
93
128
 
94
- <section>
95
- <h2>Metrics</h2>
96
- ${table(["Metric", "Value", "Unit", "Variant", "Case"], report.metrics.map((metric) => [
97
- `${metric.name} (${metric.id})`,
98
- String(metric.value ?? ""),
99
- metric.unit ?? "",
100
- metric.variantId ?? "",
101
- metric.caseId ?? "",
102
- ]))}
129
+ <section id="section-metrics" aria-labelledby="section-metrics-title">
130
+ <h2 id="section-metrics-title">Metrics</h2>
131
+ <p class="muted">Availability, provenance and reliability are taken from recorded evidence only. <em>Unavailable</em> and <em>not applicable</em> are never zero; <em>unknown</em> means the report does not record that information.</p>
132
+ ${renderMetricsTable(views)}
103
133
  </section>
104
134
 
105
- ${renderContextStrategyComparisonV043Html(report.contextStrategyComparisonV043)}
106
-
107
- ${renderWarmIndexReuseHtml(report.warmIndexReuse)}
108
-
109
- ${renderIncrementalChangeStalenessHtml(report.incrementalChangeStaleness)}
110
-
111
- ${renderContextWindowScalingHtml(report.contextWindowScaling)}
112
-
113
- ${renderRetrievalPrecisionRecallHtml(report.retrievalPrecisionRecall ?? null)}
114
-
115
- ${renderRetrievalQueryStrategyComparisonHtml(report.retrievalQueryStrategyComparison ?? null)}
116
-
117
- ${renderContextPackGenerationHtml(report.contextPackGeneration ?? null)}
135
+ <section id="section-evidence-caveats" aria-labelledby="section-evidence-caveats-title">
136
+ <h2 id="section-evidence-caveats-title">Evidence Caveats</h2>
137
+ ${renderCaveats(caveats)}
138
+ </section>
118
139
 
119
- ${renderAgentSuccessRateHtml(report.agentSuccessRate ?? null)}
140
+ ${family.html}
120
141
 
121
- <section>
122
- <h2>Warnings, Skips, And Failures</h2>
123
- ${report.findings.length ? table(["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
142
+ <section id="section-findings" aria-labelledby="section-findings-title">
143
+ <h2 id="section-findings-title">Warnings, Skips, And Failures</h2>
144
+ ${report.findings.length ? table("Warnings, skips, and failures", ["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
124
145
  finding.severity,
125
146
  finding.code,
126
147
  finding.message,
@@ -129,9 +150,9 @@ export function renderPluginExperimentReportHtml(report) {
129
150
  ])) : "<p>No warnings, skips, or failures.</p>"}
130
151
  </section>
131
152
 
132
- <section>
133
- <h2>Artifacts</h2>
134
- ${table(["Artifact", "Kind", "Path", "Variant", "Case"], report.artifacts.map((artifact) => [
153
+ <section id="section-artifacts" aria-labelledby="section-artifacts-title">
154
+ <h2 id="section-artifacts-title">Artifacts</h2>
155
+ ${table("Artifacts", ["Artifact", "Kind", "Path", "Variant", "Case"], report.artifacts.map((artifact) => [
135
156
  `${artifact.label} (${artifact.id})`,
136
157
  artifact.kind,
137
158
  artifact.path ?? "",
@@ -139,20 +160,93 @@ export function renderPluginExperimentReportHtml(report) {
139
160
  artifact.caseId ?? "",
140
161
  ]))}
141
162
  </section>
163
+ ${renderGlossary(glossary)}
142
164
  </main>
143
165
  </body>
144
166
  </html>`;
145
167
  }
146
- function table(headers, rows) {
147
- return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
148
- .map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
149
- .join("")}</tbody></table>`;
168
+ function renderNavLink(entry) {
169
+ const href = safeInternalHref(`#${entry.anchor}`);
170
+ return href === null ? "" : `<li><a href="${escapeHtml(href)}">${escapeHtml(entry.label)}</a></li>`;
171
+ }
172
+ // Specialized family sections come from the shared registry, in its deterministic order. Each non-empty
173
+ // section is wrapped in an anchored container so the compact navigation can target it.
174
+ function renderSpecializedSections(report) {
175
+ const nav = [];
176
+ const parts = [];
177
+ const used = new Set();
178
+ for (const descriptor of getSpecializedReportSectionRegistry().descriptors) {
179
+ const rendered = descriptor.renderHtml(report[descriptor.projectionKey] ?? null);
180
+ if (rendered.trim() === "")
181
+ continue;
182
+ let anchor = sectionAnchorId(descriptor.id);
183
+ for (let suffix = 2; used.has(anchor); suffix += 1)
184
+ anchor = `${sectionAnchorId(descriptor.id)}-${suffix}`;
185
+ used.add(anchor);
186
+ nav.push({ anchor, label: descriptor.text.title ?? descriptor.id });
187
+ parts.push(`<div id="${anchor}" class="family-section">${rendered}</div>`);
188
+ }
189
+ return { html: parts.join("\n\n "), nav };
190
+ }
191
+ function glossaryDescriptorsFor(views) {
192
+ const anchors = new Set(views.map((view) => view.glossaryAnchor).filter((anchor) => anchor !== null));
193
+ return listMetricDescriptors().filter((descriptor) => anchors.has(descriptor.anchor));
194
+ }
195
+ function renderMetricsTable(views) {
196
+ const headers = ["Metric", "Value", "Availability", "Unit", "Provenance", "Reliability", "Evidence note", "Variant", "Case"];
197
+ const rows = views.map((view) => {
198
+ const href = safeInternalHref(view.glossaryAnchor === null ? null : `#${view.glossaryAnchor}`);
199
+ const name = href === null
200
+ ? `${escapeHtml(view.displayName)} <code>${escapeHtml(view.metricId)}</code> <span class="muted">(no glossary entry)</span>`
201
+ : `<a href="${escapeHtml(href)}">${escapeHtml(view.displayName)}</a> <code>${escapeHtml(view.metricId)}</code>`;
202
+ const value = view.availability === "available" ? escapeHtml(view.value === null ? "" : String(view.value)) : escapeHtml(view.availability);
203
+ const note = view.availability === "available" ? "" : view.reason ?? "no reason recorded";
204
+ return [
205
+ name,
206
+ value,
207
+ `<span class="badge availability-${escapeHtml(view.availability)}">${escapeHtml(view.availability.replace("-", " "))}</span>`,
208
+ escapeHtml(view.unit ?? ""),
209
+ escapeHtml(view.provenance),
210
+ escapeHtml(view.reliability),
211
+ escapeHtml(note),
212
+ escapeHtml(view.variantId ?? ""),
213
+ escapeHtml(view.caseId ?? ""),
214
+ ];
215
+ });
216
+ return wrapTable("Metrics with availability, provenance and reliability", headers, rows);
217
+ }
218
+ function renderCaveats(caveats) {
219
+ if (caveats.length === 0)
220
+ return "<p>No metric evidence caveats were derived from the recorded evidence.</p>";
221
+ return `<ul class="caveats">${caveats
222
+ .map((caveat) => `<li class="caveat-${escapeHtml(caveat.severity)}"><span class="badge">${escapeHtml(caveat.severity)}</span> ${escapeHtml(caveat.text)}</li>`)
223
+ .join("")}</ul>`;
224
+ }
225
+ function renderGlossary(descriptors) {
226
+ if (descriptors.length === 0)
227
+ return "";
228
+ return `
229
+ <section id="section-metric-glossary" aria-labelledby="section-metric-glossary-title">
230
+ <h2 id="section-metric-glossary-title">Metric Glossary</h2>
231
+ <p class="muted">Definitions are condensed from docs/METRICS.md, the authoritative metric reference. They are descriptive and are not combined into a single figure.</p>
232
+ <dl>${descriptors
233
+ .map((descriptor) => {
234
+ const caveats = descriptor.caveats.length ? `<br /><strong>Caveats:</strong> ${descriptor.caveats.map((c) => escapeHtml(c)).join(" ")}` : "";
235
+ return `
236
+ <dt id="${escapeHtml(descriptor.anchor)}">${escapeHtml(descriptor.displayName)} <code>${escapeHtml(descriptor.metricId)}</code></dt>
237
+ <dd>${escapeHtml(descriptor.definition)}<br /><strong>Unit:</strong> ${escapeHtml(descriptor.unit ?? "not specified")}<br /><strong>Interpretation:</strong> ${escapeHtml(descriptor.interpretation)}${caveats}<br /><span class="muted">Plugin <code>${escapeHtml(descriptor.pluginId)}</code> · docs/METRICS.md: ${escapeHtml(descriptor.docsSection)}</span></dd>`;
238
+ })
239
+ .join("")}
240
+ </dl>
241
+ </section>
242
+ `;
243
+ }
244
+ function table(caption, headers, rows) {
245
+ return wrapTable(caption, headers, rows.map((row) => row.map((cell) => escapeHtml(cell))));
150
246
  }
151
- function escapeHtml(value) {
152
- return String(value ?? "")
153
- .replaceAll("&", "&amp;")
154
- .replaceAll("<", "&lt;")
155
- .replaceAll(">", "&gt;")
156
- .replaceAll('"', "&quot;")
157
- .replaceAll("'", "&#39;");
247
+ /** Cells are already safe HTML. */
248
+ function wrapTable(caption, headers, rows) {
249
+ return `<div class="table-wrap" role="region" aria-label="${escapeHtml(caption)}" tabindex="0"><table><caption>${escapeHtml(caption)}</caption><thead><tr>${headers
250
+ .map((header) => `<th scope="col">${escapeHtml(header)}</th>`)
251
+ .join("")}</tr></thead><tbody>${rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("")}</tbody></table></div>`;
158
252
  }