@dailephd/my-dev-kit-lab 0.6.1 → 0.6.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.
- package/CHANGELOG.md +32 -0
- package/README.md +9 -7
- package/benchmarks/contracts/incremental-change-staleness-scenarios.json +360 -0
- package/dist/src/cli/help.js +16 -9
- package/dist/src/commands/runExperimentDescribeCommand.js +5 -2
- package/dist/src/commands/runExperimentRunCommand.js +4 -7
- package/dist/src/evaluation/affectedNeighborhoodMetrics.js +51 -0
- package/dist/src/evaluation/benchmarkMetadata.js +5 -1
- package/dist/src/evaluation/parseAgentAnswer.js +172 -24
- package/dist/src/evaluation/runMyDevKitRetrieval.js +120 -7
- package/dist/src/experiments/defaultRegistry.js +2 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/comparison.js +101 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/comparisonV2.js +49 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/config.js +67 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/disposableTarget.js +125 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/execution.js +430 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionArtifact.js +160 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionArtifactV2.js +289 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/executionV2.js +195 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/index.js +13 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/indexClone.js +76 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecycle.js +308 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecyclePolicy.js +154 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecyclePolicyV2.js +78 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/lifecycleV2.js +298 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/mutationExecution.js +144 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/plugin.js +475 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/scenarioCatalog.js +42 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/scenarioMutation.js +116 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/scenarioSelection.js +79 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/scenarioTypes.js +37 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/scenarioValidation.js +168 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/sourceState.js +145 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/treatmentSession.js +6 -0
- package/dist/src/experiments/plugins/incrementalChangeStaleness/treatmentSessionV2.js +5 -0
- package/dist/src/experiments/plugins/index.js +1 -0
- package/dist/src/experiments/plugins/warmIndexReuse/metrics.js +17 -20
- package/dist/src/experiments/plugins/warmIndexReuse/warmIndexSession.js +3 -1
- package/dist/src/mobile/android/advancedSecurity/candidateEvidence.js +9 -2
- package/dist/src/mobile/android/advancedSecurity/redaction.js +19 -10
- package/dist/src/mobile/android/advancedSecurity/secretCandidates/analyzeSecretCandidates.js +12 -2
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/analyzeSigningConfiguration.js +1 -1
- package/dist/src/mobile/android/advancedSecurity/signingConfiguration/extractSigningConfigurations.js +7 -7
- package/dist/src/report/experiments/buildIncrementalChangeStalenessPluginReport.js +33 -0
- package/dist/src/report/experiments/buildIncrementalChangeStalenessReport.js +112 -0
- package/dist/src/report/experiments/buildIncrementalChangeStalenessReportV2.js +80 -0
- package/dist/src/report/experiments/buildPluginExperimentReport.js +3 -0
- package/dist/src/report/experiments/incrementalChangeStalenessReportModel.js +4 -0
- package/dist/src/report/experiments/incrementalChangeStalenessReportModelV2.js +3 -0
- package/dist/src/report/experiments/incrementalChangeStalenessReportRowsV2.js +146 -0
- package/dist/src/report/experiments/index.js +6 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessHtml.js +243 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessHtmlV2.js +111 -0
- package/dist/src/report/experiments/renderIncrementalChangeStalenessTextV2.js +96 -0
- package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +3 -0
- package/dist/src/report/experiments/renderPluginExperimentReportText.js +144 -0
- package/dist/src/securityValidation/staticScans/codeql.js +8 -8
- package/docs/METRICS.md +67 -1
- package/examples/tutorial-browser/prepare.mjs +13 -13
- package/examples/tutorial-browser/server.mjs +13 -13
- package/package.json +1 -1
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// v0.6.2 Batch 5 -- plugin-specific HTML section rendering only. Renders exactly the persisted
|
|
2
|
+
// report-model fields; performs no classification, no metric arithmetic, and no percent
|
|
3
|
+
// calculation beyond formatting an already-persisted number.
|
|
4
|
+
import { STALE_RISK_CLASSIFICATION_EXPLANATIONS } from "./buildIncrementalChangeStalenessReport.js";
|
|
5
|
+
import { REINDEX_RECOMMENDATION_EXPLANATIONS } from "./buildWarmIndexReuseReport.js";
|
|
6
|
+
import { isV2ReportSection } from "./buildIncrementalChangeStalenessReportV2.js";
|
|
7
|
+
import { renderIncrementalChangeStalenessHtmlV2 } from "./renderIncrementalChangeStalenessHtmlV2.js";
|
|
8
|
+
const MAX_DISPLAY_ITEMS = 20;
|
|
9
|
+
export function renderIncrementalChangeStalenessHtml(section) {
|
|
10
|
+
if (section !== null && isV2ReportSection(section))
|
|
11
|
+
return renderIncrementalChangeStalenessHtmlV2(section);
|
|
12
|
+
if (section === null) {
|
|
13
|
+
return `<section>
|
|
14
|
+
<h2>Incremental-Change And Staleness Evidence</h2>
|
|
15
|
+
<p>Not applicable to this plugin.</p>
|
|
16
|
+
</section>`;
|
|
17
|
+
}
|
|
18
|
+
return `<section>
|
|
19
|
+
<h2>Incremental-Change And Staleness Evidence</h2>
|
|
20
|
+
<p class="muted">Scoped experimental evidence: results are limited to the executed scenarios below, not a general safety verdict.</p>
|
|
21
|
+
${table(["Field", "Value"], [
|
|
22
|
+
["Scenarios", String(section.scenarioCount)],
|
|
23
|
+
["Ready", String(section.readyScenarioCount)],
|
|
24
|
+
["Failed", String(section.failedScenarioCount)],
|
|
25
|
+
["Observed stale regression", String(section.observedStaleRegressionCount)],
|
|
26
|
+
["No observed stale regression", String(section.noObservedStaleRegressionCount)],
|
|
27
|
+
["Inconclusive", String(section.inconclusiveCount)]
|
|
28
|
+
])}
|
|
29
|
+
<h3>Scenario Comparison</h3>
|
|
30
|
+
${table([
|
|
31
|
+
"Scenario",
|
|
32
|
+
"Category",
|
|
33
|
+
"Status",
|
|
34
|
+
"Relationship",
|
|
35
|
+
"Reindex recommendation",
|
|
36
|
+
"Stale correctness",
|
|
37
|
+
"Full-refresh correctness",
|
|
38
|
+
"Correctness relation",
|
|
39
|
+
"Stale required files",
|
|
40
|
+
"Full-refresh required files",
|
|
41
|
+
"Required-file relation",
|
|
42
|
+
"Stale-risk classification"
|
|
43
|
+
], section.scenarios.map(comparisonRow))}
|
|
44
|
+
${section.scenarios.map(renderScenarioDetail).join("\n")}
|
|
45
|
+
<h3>Limitations</h3>
|
|
46
|
+
${list(section.limitations)}
|
|
47
|
+
</section>`;
|
|
48
|
+
}
|
|
49
|
+
function symmetricAffectedNeighborhood(scenario) {
|
|
50
|
+
const stale = scenario.staleTreatment?.affectedNeighborhood ?? null;
|
|
51
|
+
const fullRefresh = scenario.fullRefreshTreatment?.affectedNeighborhood ?? null;
|
|
52
|
+
if (!stale || !fullRefresh)
|
|
53
|
+
return null;
|
|
54
|
+
if (stale.relationship !== fullRefresh.relationship || stale.reindexRecommendation !== fullRefresh.reindexRecommendation) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return stale;
|
|
58
|
+
}
|
|
59
|
+
function comparisonRow(scenario) {
|
|
60
|
+
const stale = scenario.staleTreatment?.affectedNeighborhood ?? null;
|
|
61
|
+
const fullRefresh = scenario.fullRefreshTreatment?.affectedNeighborhood ?? null;
|
|
62
|
+
const symmetric = symmetricAffectedNeighborhood(scenario);
|
|
63
|
+
const inconsistent = stale !== null && fullRefresh !== null && symmetric === null;
|
|
64
|
+
return [
|
|
65
|
+
`${scenario.scenarioId}`,
|
|
66
|
+
scenario.category,
|
|
67
|
+
scenario.status,
|
|
68
|
+
inconsistent ? "inconsistent persisted evidence" : symmetric?.relationship ?? "not available",
|
|
69
|
+
inconsistent ? "inconsistent persisted evidence" : symmetric?.reindexRecommendation ?? "not available",
|
|
70
|
+
formatCorrectness(scenario.staleTreatment),
|
|
71
|
+
formatCorrectness(scenario.fullRefreshTreatment),
|
|
72
|
+
scenario.comparison.correctnessRelation,
|
|
73
|
+
scenario.staleTreatment?.requiredFileEvidence.status ?? "not available",
|
|
74
|
+
scenario.fullRefreshTreatment?.requiredFileEvidence.status ?? "not available",
|
|
75
|
+
scenario.comparison.requiredFileEvidenceRelation,
|
|
76
|
+
scenario.comparison.staleRiskClassification
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
function formatCorrectness(treatment) {
|
|
80
|
+
if (!treatment || !treatment.fakeAgent)
|
|
81
|
+
return "unavailable";
|
|
82
|
+
const correctness = treatment.fakeAgent.correctness;
|
|
83
|
+
return correctness.available && correctness.score !== null ? String(correctness.score) : "unavailable";
|
|
84
|
+
}
|
|
85
|
+
function renderScenarioDetail(scenario) {
|
|
86
|
+
return `<h3>Scenario: ${escapeHtml(scenario.scenarioId)}</h3>
|
|
87
|
+
${renderDefinition(scenario)}
|
|
88
|
+
${renderLifecycle(scenario)}
|
|
89
|
+
${renderAffectedNeighborhoodSection(scenario)}
|
|
90
|
+
${renderTreatment("Stale-Index Treatment", scenario.staleTreatment)}
|
|
91
|
+
${renderTreatment("Full-Refresh Treatment", scenario.fullRefreshTreatment)}
|
|
92
|
+
${renderComparison(scenario)}`;
|
|
93
|
+
}
|
|
94
|
+
function renderDefinition(scenario) {
|
|
95
|
+
return `<h4>Scenario Definition</h4>
|
|
96
|
+
${table(["Field", "Value"], [
|
|
97
|
+
["Scenario ID", scenario.scenarioId],
|
|
98
|
+
["Category", scenario.category],
|
|
99
|
+
["Benchmark project", scenario.benchmarkProjectId],
|
|
100
|
+
["Base case", scenario.baseCaseId],
|
|
101
|
+
["Answer policy", scenario.answerPolicy],
|
|
102
|
+
["Query", scenario.query ?? "unavailable"],
|
|
103
|
+
["Status", scenario.status],
|
|
104
|
+
["Failure reason", scenario.failureReason ?? "not applicable"]
|
|
105
|
+
])}
|
|
106
|
+
<p>Expected files:</p>
|
|
107
|
+
${boundedList(scenario.expectedFiles)}
|
|
108
|
+
<p>Expected symbols:</p>
|
|
109
|
+
${boundedList(scenario.expectedSymbols)}`;
|
|
110
|
+
}
|
|
111
|
+
function renderLifecycle(scenario) {
|
|
112
|
+
const lifecycle = scenario.lifecycle;
|
|
113
|
+
if (!lifecycle) {
|
|
114
|
+
return `<h4>Lifecycle Integrity</h4><p>Not available for this scenario.</p>`;
|
|
115
|
+
}
|
|
116
|
+
return `<h4>Lifecycle Integrity</h4>
|
|
117
|
+
${table(["Field", "Value"], [
|
|
118
|
+
["Pre-mutation equivalence", lifecycle.preMutationEquivalence],
|
|
119
|
+
["Post-mutation equivalence", lifecycle.postMutationEquivalence],
|
|
120
|
+
["my-dev-kit version", lifecycle.myDevKitVersion ?? "unavailable"],
|
|
121
|
+
["Stale baseline freshness", lifecycle.staleBaselineFreshnessStatus],
|
|
122
|
+
["Full-refresh baseline freshness", lifecycle.fullRefreshBaselineFreshnessStatus],
|
|
123
|
+
["Full-refresh refreshed freshness", lifecycle.fullRefreshRefreshedFreshnessStatus ?? "unavailable"]
|
|
124
|
+
])}
|
|
125
|
+
<p>Controlled file paths:</p>
|
|
126
|
+
${boundedList(lifecycle.controlledFilePaths)}
|
|
127
|
+
<p>Source roots:</p>
|
|
128
|
+
${boundedList(lifecycle.sourceRoots)}`;
|
|
129
|
+
}
|
|
130
|
+
function renderAffectedNeighborhoodSection(scenario) {
|
|
131
|
+
const stale = scenario.staleTreatment?.affectedNeighborhood ?? null;
|
|
132
|
+
const fullRefresh = scenario.fullRefreshTreatment?.affectedNeighborhood ?? null;
|
|
133
|
+
if (!stale && !fullRefresh) {
|
|
134
|
+
return `<h4>Affected-Neighborhood Evidence (Changed Baseline Neighborhood Relative To The Task)</h4><p>Not available.</p>`;
|
|
135
|
+
}
|
|
136
|
+
const symmetric = symmetricAffectedNeighborhood(scenario);
|
|
137
|
+
if (!symmetric) {
|
|
138
|
+
return `<h4>Affected-Neighborhood Evidence (Changed Baseline Neighborhood Relative To The Task)</h4><p>inconsistent persisted evidence</p>`;
|
|
139
|
+
}
|
|
140
|
+
const explanation = REINDEX_RECOMMENDATION_EXPLANATIONS[symmetric.reindexRecommendation];
|
|
141
|
+
return `<h4>Affected-Neighborhood Evidence (Changed Baseline Neighborhood Relative To The Task)</h4>
|
|
142
|
+
${table(["Field", "Value"], [
|
|
143
|
+
["Relationship", symmetric.relationship],
|
|
144
|
+
["Reindex recommendation", symmetric.reindexRecommendation],
|
|
145
|
+
["Changed indexed files", formatAffectedNumber(symmetric.changedFileCount)],
|
|
146
|
+
["Changed baseline symbols", formatAffectedNumber(symmetric.changedSymbolCount)],
|
|
147
|
+
["Affected graph nodes", formatAffectedNumber(symmetric.affectedNodeCount)],
|
|
148
|
+
["Affected graph edges", formatAffectedNumber(symmetric.affectedEdgeCount)],
|
|
149
|
+
["Task-overlap nodes", formatAffectedNumber(symmetric.taskOverlapCount)],
|
|
150
|
+
["Task-overlap percent", formatAffectedNumber(symmetric.taskOverlapPercent)]
|
|
151
|
+
])}
|
|
152
|
+
<p>${escapeHtml(explanation)}</p>`;
|
|
153
|
+
}
|
|
154
|
+
function renderTreatment(title, treatment) {
|
|
155
|
+
if (!treatment) {
|
|
156
|
+
return `<h4>${escapeHtml(title)}</h4><p>Not available.</p>`;
|
|
157
|
+
}
|
|
158
|
+
const retrieval = treatment.retrieval;
|
|
159
|
+
const fakeAgent = treatment.fakeAgent;
|
|
160
|
+
const requiredFileEvidence = treatment.requiredFileEvidence;
|
|
161
|
+
return `<h4>${escapeHtml(title)}</h4>
|
|
162
|
+
${table(["Field", "Value"], [
|
|
163
|
+
["Treatment status", treatment.status],
|
|
164
|
+
["Failure reason", treatment.failureReason ?? "not applicable"],
|
|
165
|
+
["Active index phase", treatment.activeIndexPhase],
|
|
166
|
+
["Baseline freshness status", treatment.baselineFreshness.status]
|
|
167
|
+
])}
|
|
168
|
+
<p><strong>Retrieval</strong> (bounded evidence; full retrieved context body is never rendered here)</p>
|
|
169
|
+
${table(["Field", "Value"], [
|
|
170
|
+
["Status", retrieval.status],
|
|
171
|
+
["Selected node ID", retrieval.selectedNodeId ?? "unavailable"],
|
|
172
|
+
["Selected file", retrieval.selectedFile ?? "unavailable"],
|
|
173
|
+
["Selected symbol", retrieval.selectedSymbol ?? "unavailable"],
|
|
174
|
+
["Context characters", String(retrieval.totalChars)],
|
|
175
|
+
["Estimated context tokens", String(retrieval.totalEstimatedTokens)],
|
|
176
|
+
["Duration (ms)", String(retrieval.durationMs)]
|
|
177
|
+
])}
|
|
178
|
+
<p>Files read:</p>
|
|
179
|
+
${boundedList(retrieval.filesRead)}
|
|
180
|
+
<p>Retrieval warnings:</p>
|
|
181
|
+
${boundedList(retrieval.warnings)}
|
|
182
|
+
<p><strong>Fake-Agent Evidence</strong> (deterministic fake-agent / simulated harness evidence, not a provider or real-model measurement)</p>
|
|
183
|
+
${fakeAgent
|
|
184
|
+
? table(["Field", "Value"], [
|
|
185
|
+
["Status", fakeAgent.status],
|
|
186
|
+
["Correctness score", fakeAgent.correctness.available && fakeAgent.correctness.score !== null ? String(fakeAgent.correctness.score) : "unavailable"],
|
|
187
|
+
["Passed", fakeAgent.correctness.passed === null ? "unavailable" : String(fakeAgent.correctness.passed)],
|
|
188
|
+
["Duration (ms)", fakeAgent.durationMs === null ? "unavailable" : String(fakeAgent.durationMs)]
|
|
189
|
+
])
|
|
190
|
+
: "<p>Not run for this treatment.</p>"}
|
|
191
|
+
<p><strong>Required-File Evidence</strong> (bounded answer-key file-presence check, not retrieval precision/recall)</p>
|
|
192
|
+
${table(["Field", "Value"], [
|
|
193
|
+
["Status", requiredFileEvidence.status],
|
|
194
|
+
["Reason", requiredFileEvidence.reason ?? "not applicable"]
|
|
195
|
+
])}
|
|
196
|
+
<p>Required files:</p>
|
|
197
|
+
${boundedList(requiredFileEvidence.requiredFiles)}
|
|
198
|
+
<p>Observed files:</p>
|
|
199
|
+
${boundedList(requiredFileEvidence.observedFiles)}
|
|
200
|
+
<p>Missing files:</p>
|
|
201
|
+
${boundedList(requiredFileEvidence.missingFiles)}`;
|
|
202
|
+
}
|
|
203
|
+
function renderComparison(scenario) {
|
|
204
|
+
const comparison = scenario.comparison;
|
|
205
|
+
const explanation = STALE_RISK_CLASSIFICATION_EXPLANATIONS[comparison.staleRiskClassification];
|
|
206
|
+
return `<h4>Matched Comparison</h4>
|
|
207
|
+
${table(["Field", "Value"], [
|
|
208
|
+
["Correctness relation", comparison.correctnessRelation],
|
|
209
|
+
["Required-file evidence relation", comparison.requiredFileEvidenceRelation],
|
|
210
|
+
["Stale-risk classification", comparison.staleRiskClassification]
|
|
211
|
+
])}
|
|
212
|
+
<p>Reason codes:</p>
|
|
213
|
+
${list(comparison.reasonCodes)}
|
|
214
|
+
<p>${escapeHtml(explanation)}</p>`;
|
|
215
|
+
}
|
|
216
|
+
function formatAffectedNumber(value) {
|
|
217
|
+
return value === null ? "unavailable" : String(value);
|
|
218
|
+
}
|
|
219
|
+
function boundedList(items) {
|
|
220
|
+
if (items.length === 0)
|
|
221
|
+
return "<p>none</p>";
|
|
222
|
+
const shown = items.slice(0, MAX_DISPLAY_ITEMS);
|
|
223
|
+
const omitted = items.length - shown.length;
|
|
224
|
+
return `${list(shown)}${omitted > 0 ? `<p class="muted">... ${omitted} more</p>` : ""}`;
|
|
225
|
+
}
|
|
226
|
+
function list(items) {
|
|
227
|
+
if (items.length === 0)
|
|
228
|
+
return "<ul><li>none</li></ul>";
|
|
229
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`;
|
|
230
|
+
}
|
|
231
|
+
function table(headers, rows) {
|
|
232
|
+
return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
|
|
233
|
+
.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
|
|
234
|
+
.join("")}</tbody></table>`;
|
|
235
|
+
}
|
|
236
|
+
function escapeHtml(value) {
|
|
237
|
+
return String(value ?? "")
|
|
238
|
+
.replaceAll("&", "&")
|
|
239
|
+
.replaceAll("<", "<")
|
|
240
|
+
.replaceAll(">", ">")
|
|
241
|
+
.replaceAll('"', """)
|
|
242
|
+
.replaceAll("'", "'");
|
|
243
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// v0.6.3 -- V2 HTML section for the incremental-change-staleness plugin report. Renders the same persisted
|
|
2
|
+
// facts as the text renderer through the shared row builders; every persisted string is HTML-escaped.
|
|
3
|
+
import { COMPARISON_PRESENTATION_ORDER, EVIDENCE_FAMILY_NOTE, SCENARIO_SUMMARY_ROWS, TREATMENT_PRESENTATION_ORDER, affectedNeighborhoodSummary, comparisonBlock, treatmentBlock } from "./incrementalChangeStalenessReportRowsV2.js";
|
|
4
|
+
const MAX_DISPLAY_ITEMS = 20;
|
|
5
|
+
export function renderIncrementalChangeStalenessHtmlV2(section) {
|
|
6
|
+
return `<section>
|
|
7
|
+
<h2>Incremental-Change And Staleness Evidence</h2>
|
|
8
|
+
<p class="muted">Scoped experimental evidence: results are limited to the executed scenarios below. Full refresh is a comparison reference, not a declared best treatment.</p>
|
|
9
|
+
${table(["Field", "Value"], SCENARIO_SUMMARY_ROWS(section.summary))}
|
|
10
|
+
${section.scenarios.map(renderScenario).join("\n")}
|
|
11
|
+
<h3>Limitations</h3>
|
|
12
|
+
${list(section.limitations)}
|
|
13
|
+
</section>`;
|
|
14
|
+
}
|
|
15
|
+
function renderScenario(scenario) {
|
|
16
|
+
const lifecycle = scenario.lifecycle;
|
|
17
|
+
const affected = affectedNeighborhoodSummary(scenario);
|
|
18
|
+
return `<h3>Scenario: ${escapeHtml(scenario.scenarioId)}</h3>
|
|
19
|
+
<h4>Scenario Definition</h4>
|
|
20
|
+
${table(["Field", "Value"], [
|
|
21
|
+
["Scenario ID", scenario.scenarioId],
|
|
22
|
+
["Category", scenario.category],
|
|
23
|
+
["Benchmark project", scenario.benchmarkProjectId],
|
|
24
|
+
["Base case", scenario.baseCaseId],
|
|
25
|
+
["Answer policy", scenario.answerPolicy],
|
|
26
|
+
["Query", scenario.query ?? "unavailable"],
|
|
27
|
+
["Status", scenario.status],
|
|
28
|
+
["Failure reason", scenario.failureReason ?? "not applicable"]
|
|
29
|
+
])}
|
|
30
|
+
<p>Expected files:</p>
|
|
31
|
+
${boundedList(scenario.expectedFiles)}
|
|
32
|
+
<p>Expected symbols:</p>
|
|
33
|
+
${boundedList(scenario.expectedSymbols)}
|
|
34
|
+
<h4>Lifecycle Integrity</h4>
|
|
35
|
+
${lifecycle
|
|
36
|
+
? `${table(["Field", "Value"], [
|
|
37
|
+
["my-dev-kit version", lifecycle.myDevKitVersion ?? "unavailable"],
|
|
38
|
+
["Index invocations (total)", String(lifecycle.totalIndexInvocationCount)],
|
|
39
|
+
["Freshness assessments (total)", String(lifecycle.totalFreshnessAssessmentCount)],
|
|
40
|
+
...Object.entries(lifecycle.preMutationEquivalence).map(([id, result]) => [`Pre-mutation equivalence (${id} vs stale-index reference)`, result]),
|
|
41
|
+
...Object.entries(lifecycle.postMutationEquivalence).map(([id, result]) => [`Post-mutation equivalence (${id} vs stale-index reference)`, result])
|
|
42
|
+
])}<p>Controlled file paths:</p>${boundedList(lifecycle.controlledFilePaths)}<p>Source roots:</p>${boundedList(lifecycle.sourceRoots)}`
|
|
43
|
+
: "<p>Not available for this scenario.</p>"}
|
|
44
|
+
<h4>Affected-Neighborhood Evidence (Changed Baseline Neighborhood Relative To The Task)</h4>
|
|
45
|
+
${affected.state === "none"
|
|
46
|
+
? "<p>Not available.</p>"
|
|
47
|
+
: affected.state === "inconsistent"
|
|
48
|
+
? "<p>inconsistent persisted evidence</p>"
|
|
49
|
+
: `${table(["Field", "Value"], affected.rows)}<p>${escapeHtml(affected.explanation)}</p>`}
|
|
50
|
+
<p class="muted">${escapeHtml(EVIDENCE_FAMILY_NOTE)}</p>
|
|
51
|
+
${scenario.treatments.length === 0
|
|
52
|
+
? "<h4>Treatments</h4><p>Not produced: the scenario failed before any treatment evidence existed.</p>"
|
|
53
|
+
: TREATMENT_PRESENTATION_ORDER.map((entry) => renderTreatment(scenario, entry)).join("\n")}
|
|
54
|
+
<h4>Reference Comparisons (Against The Full-Refresh Reference)</h4>
|
|
55
|
+
${COMPARISON_PRESENTATION_ORDER.map((entry) => renderComparison(scenario, entry)).join("\n")}`;
|
|
56
|
+
}
|
|
57
|
+
function renderTreatment(scenario, entry) {
|
|
58
|
+
const block = treatmentBlock(scenario, entry.id, entry.label);
|
|
59
|
+
const title = `${block.label} Treatment (${entry.id})`;
|
|
60
|
+
if (!block.available)
|
|
61
|
+
return `<h4>${escapeHtml(title)}</h4><p>Not available.</p>`;
|
|
62
|
+
return `<h4>${escapeHtml(title)}</h4>
|
|
63
|
+
${table(["Field", "Value"], block.rows)}
|
|
64
|
+
<p><strong>Refresh Execution</strong> (upstream evidence of what my-dev-kit actually did)</p>
|
|
65
|
+
${table(["Field", "Value"], block.refreshRows)}
|
|
66
|
+
${block.fallbackStatement ? `<p class="fallback"><strong>${escapeHtml(block.fallbackStatement)}</strong></p>` : ""}
|
|
67
|
+
${block.forcedNeighborSample ? `<p>Forced-neighbor sample:</p>${boundedList(block.forcedNeighborSample)}` : ""}
|
|
68
|
+
<p>Files read:</p>
|
|
69
|
+
${boundedList(block.filesRead)}
|
|
70
|
+
<p>Required files:</p>
|
|
71
|
+
${boundedList(block.requiredFiles)}
|
|
72
|
+
<p>Observed files:</p>
|
|
73
|
+
${boundedList(block.observedFiles)}
|
|
74
|
+
<p>Missing files:</p>
|
|
75
|
+
${boundedList(block.missingFiles)}`;
|
|
76
|
+
}
|
|
77
|
+
function renderComparison(scenario, entry) {
|
|
78
|
+
const block = comparisonBlock(scenario, entry);
|
|
79
|
+
if (!block.available)
|
|
80
|
+
return `<h5>${escapeHtml(block.label)}</h5><p>Not available.</p>`;
|
|
81
|
+
return `<h5>${escapeHtml(block.label)}</h5>
|
|
82
|
+
${table(["Field", "Value"], block.rows)}
|
|
83
|
+
<p>Reason codes:</p>
|
|
84
|
+
${list(block.reasonCodes)}
|
|
85
|
+
<p>${escapeHtml(block.explanation)}</p>`;
|
|
86
|
+
}
|
|
87
|
+
function boundedList(items) {
|
|
88
|
+
if (items.length === 0)
|
|
89
|
+
return "<p>none</p>";
|
|
90
|
+
const shown = items.slice(0, MAX_DISPLAY_ITEMS);
|
|
91
|
+
const omitted = items.length - shown.length;
|
|
92
|
+
return `${list(shown)}${omitted > 0 ? `<p class="muted">... ${omitted} more</p>` : ""}`;
|
|
93
|
+
}
|
|
94
|
+
function list(items) {
|
|
95
|
+
if (items.length === 0)
|
|
96
|
+
return "<ul><li>none</li></ul>";
|
|
97
|
+
return `<ul>${items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`;
|
|
98
|
+
}
|
|
99
|
+
function table(headers, rows) {
|
|
100
|
+
return `<table><thead><tr>${headers.map((header) => `<th>${escapeHtml(header)}</th>`).join("")}</tr></thead><tbody>${rows
|
|
101
|
+
.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
|
|
102
|
+
.join("")}</tbody></table>`;
|
|
103
|
+
}
|
|
104
|
+
function escapeHtml(value) {
|
|
105
|
+
return String(value ?? "")
|
|
106
|
+
.replaceAll("&", "&")
|
|
107
|
+
.replaceAll("<", "<")
|
|
108
|
+
.replaceAll(">", ">")
|
|
109
|
+
.replaceAll('"', """)
|
|
110
|
+
.replaceAll("'", "'");
|
|
111
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// v0.6.3 -- V2 text section for the incremental-change-staleness plugin report. Renders only persisted
|
|
2
|
+
// V2 report-model fields (via the shared row builders); no recomputation. Lists are bounded.
|
|
3
|
+
import { COMPARISON_PRESENTATION_ORDER, EVIDENCE_FAMILY_NOTE, SCENARIO_SUMMARY_ROWS, TREATMENT_PRESENTATION_ORDER, affectedNeighborhoodSummary, comparisonBlock, treatmentBlock } from "./incrementalChangeStalenessReportRowsV2.js";
|
|
4
|
+
function renderScenario(lines, scenario, h) {
|
|
5
|
+
h.pushSection(lines, `Scenario: ${scenario.scenarioId}`);
|
|
6
|
+
lines.push(h.fieldLine("Category", scenario.category));
|
|
7
|
+
lines.push(h.fieldLine("Status", scenario.status));
|
|
8
|
+
lines.push(h.fieldLine("Failure Reason", scenario.failureReason));
|
|
9
|
+
lines.push(h.fieldLine("Benchmark Project", scenario.benchmarkProjectId));
|
|
10
|
+
lines.push(h.fieldLine("Base Case", scenario.baseCaseId));
|
|
11
|
+
lines.push(h.fieldLine("Answer Policy", scenario.answerPolicy));
|
|
12
|
+
lines.push(h.fieldLine("Query", scenario.query));
|
|
13
|
+
h.pushBoundedStringList(lines, "Expected Files", scenario.expectedFiles);
|
|
14
|
+
h.pushBoundedStringList(lines, "Expected Symbols", scenario.expectedSymbols);
|
|
15
|
+
h.pushSection(lines, "Lifecycle");
|
|
16
|
+
const lifecycle = scenario.lifecycle;
|
|
17
|
+
if (!lifecycle) {
|
|
18
|
+
lines.push("Not available.");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
lines.push(h.fieldLine("my-dev-kit Version", lifecycle.myDevKitVersion));
|
|
22
|
+
lines.push(h.fieldLine("Index Invocations (total)", lifecycle.totalIndexInvocationCount));
|
|
23
|
+
lines.push(h.fieldLine("Freshness Assessments (total)", lifecycle.totalFreshnessAssessmentCount));
|
|
24
|
+
for (const [treatmentId, result] of Object.entries(lifecycle.preMutationEquivalence)) {
|
|
25
|
+
lines.push(h.fieldLine(`Pre-Mutation Equivalence (${treatmentId} vs stale-index reference)`, result));
|
|
26
|
+
}
|
|
27
|
+
for (const [treatmentId, result] of Object.entries(lifecycle.postMutationEquivalence)) {
|
|
28
|
+
lines.push(h.fieldLine(`Post-Mutation Equivalence (${treatmentId} vs stale-index reference)`, result));
|
|
29
|
+
}
|
|
30
|
+
h.pushBoundedStringList(lines, "Controlled File Paths", lifecycle.controlledFilePaths);
|
|
31
|
+
h.pushBoundedStringList(lines, "Source Roots", lifecycle.sourceRoots);
|
|
32
|
+
}
|
|
33
|
+
h.pushSection(lines, "Affected-Neighborhood Evidence (changed baseline neighborhood relative to the task)");
|
|
34
|
+
const affected = affectedNeighborhoodSummary(scenario);
|
|
35
|
+
if (affected.state === "none") {
|
|
36
|
+
lines.push("Not available.");
|
|
37
|
+
}
|
|
38
|
+
else if (affected.state === "inconsistent") {
|
|
39
|
+
lines.push("inconsistent persisted evidence");
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
for (const [label, value] of affected.rows)
|
|
43
|
+
lines.push(h.fieldLine(label, value));
|
|
44
|
+
lines.push(h.fieldLine("Reindex Recommendation Explanation", affected.explanation));
|
|
45
|
+
}
|
|
46
|
+
lines.push(EVIDENCE_FAMILY_NOTE);
|
|
47
|
+
if (scenario.treatments.length === 0) {
|
|
48
|
+
h.pushSection(lines, "Treatments");
|
|
49
|
+
lines.push("Not produced: the scenario failed before any treatment evidence existed.");
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
for (const entry of TREATMENT_PRESENTATION_ORDER) {
|
|
53
|
+
const block = treatmentBlock(scenario, entry.id, entry.label);
|
|
54
|
+
h.pushSection(lines, `${block.label} Treatment (${entry.id})`);
|
|
55
|
+
if (!block.available) {
|
|
56
|
+
lines.push("Not available.");
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const [label, value] of block.rows)
|
|
60
|
+
lines.push(h.fieldLine(label, value));
|
|
61
|
+
lines.push("Refresh Execution (upstream evidence of what my-dev-kit actually did):");
|
|
62
|
+
for (const [label, value] of block.refreshRows)
|
|
63
|
+
lines.push(h.fieldLine(label, value));
|
|
64
|
+
if (block.fallbackStatement)
|
|
65
|
+
lines.push(block.fallbackStatement);
|
|
66
|
+
if (block.forcedNeighborSample)
|
|
67
|
+
h.pushBoundedStringList(lines, "Forced-Neighbor Sample", block.forcedNeighborSample);
|
|
68
|
+
h.pushBoundedStringList(lines, "Files Read", block.filesRead);
|
|
69
|
+
h.pushBoundedStringList(lines, "Required Files", block.requiredFiles);
|
|
70
|
+
h.pushBoundedStringList(lines, "Observed Files", block.observedFiles);
|
|
71
|
+
h.pushBoundedStringList(lines, "Missing Files", block.missingFiles);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
h.pushSection(lines, "Reference Comparisons (against the full-refresh reference)");
|
|
75
|
+
for (const entry of COMPARISON_PRESENTATION_ORDER) {
|
|
76
|
+
const block = comparisonBlock(scenario, entry);
|
|
77
|
+
lines.push(block.label);
|
|
78
|
+
if (!block.available) {
|
|
79
|
+
lines.push("Not available.");
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (const [label, value] of block.rows)
|
|
83
|
+
lines.push(h.fieldLine(label, value));
|
|
84
|
+
h.pushDashList(lines, block.reasonCodes);
|
|
85
|
+
lines.push(block.explanation);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function renderIncrementalChangeStalenessSectionV2(lines, section, h) {
|
|
89
|
+
h.pushSection(lines, "Summary");
|
|
90
|
+
for (const [label, value] of SCENARIO_SUMMARY_ROWS(section.summary))
|
|
91
|
+
lines.push(h.fieldLine(label, value));
|
|
92
|
+
for (const scenario of section.scenarios)
|
|
93
|
+
renderScenario(lines, scenario, h);
|
|
94
|
+
h.pushSection(lines, "Limitations");
|
|
95
|
+
h.pushDashList(lines, section.limitations);
|
|
96
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { renderContextStrategyComparisonV043Html } from "./renderContextStrategyComparisonV043Html.js";
|
|
2
2
|
import { renderWarmIndexReuseHtml } from "./renderWarmIndexReuseHtml.js";
|
|
3
|
+
import { renderIncrementalChangeStalenessHtml } from "./renderIncrementalChangeStalenessHtml.js";
|
|
3
4
|
export function renderPluginExperimentReportHtml(report) {
|
|
4
5
|
return `<!DOCTYPE html>
|
|
5
6
|
<html lang="en">
|
|
@@ -99,6 +100,8 @@ export function renderPluginExperimentReportHtml(report) {
|
|
|
99
100
|
|
|
100
101
|
${renderWarmIndexReuseHtml(report.warmIndexReuse)}
|
|
101
102
|
|
|
103
|
+
${renderIncrementalChangeStalenessHtml(report.incrementalChangeStaleness)}
|
|
104
|
+
|
|
102
105
|
<section>
|
|
103
106
|
<h2>Warnings, Skips, And Failures</h2>
|
|
104
107
|
${report.findings.length ? table(["Severity", "Code", "Message", "Variant", "Case"], report.findings.map((finding) => [
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { STALE_RISK_CLASSIFICATION_EXPLANATIONS } from "./buildIncrementalChangeStalenessReport.js";
|
|
2
|
+
import { isV2ReportSection } from "./buildIncrementalChangeStalenessReportV2.js";
|
|
3
|
+
import { renderIncrementalChangeStalenessSectionV2 } from "./renderIncrementalChangeStalenessTextV2.js";
|
|
4
|
+
import { REINDEX_RECOMMENDATION_EXPLANATIONS } from "./buildWarmIndexReuseReport.js";
|
|
1
5
|
function sanitizeScalar(value) {
|
|
2
6
|
const text = String(value);
|
|
3
7
|
let result = "";
|
|
@@ -489,6 +493,144 @@ function renderTaskFreshnessDetail(lines, freshness) {
|
|
|
489
493
|
lines.push(`- ${entry.path === null ? "run-level" : sanitizeScalar(entry.path)} [${sanitizeScalar(entry.reasonCode)}] ${sanitizeScalar(entry.message)}`);
|
|
490
494
|
}
|
|
491
495
|
}
|
|
496
|
+
const INCREMENTAL_CHANGE_STALENESS_MAX_DISPLAY_ITEMS = 20;
|
|
497
|
+
function pushBoundedStringList(lines, label, items) {
|
|
498
|
+
lines.push(`${label}:`);
|
|
499
|
+
if (items.length === 0) {
|
|
500
|
+
lines.push("- none");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const shown = items.slice(0, INCREMENTAL_CHANGE_STALENESS_MAX_DISPLAY_ITEMS);
|
|
504
|
+
for (const item of shown)
|
|
505
|
+
lines.push(`- ${sanitizeScalar(item)}`);
|
|
506
|
+
const omitted = items.length - shown.length;
|
|
507
|
+
if (omitted > 0)
|
|
508
|
+
lines.push(`... ${omitted} more`);
|
|
509
|
+
}
|
|
510
|
+
function symmetricIncrementalAffectedNeighborhood(scenario) {
|
|
511
|
+
const stale = scenario.staleTreatment?.affectedNeighborhood ?? null;
|
|
512
|
+
const fullRefresh = scenario.fullRefreshTreatment?.affectedNeighborhood ?? null;
|
|
513
|
+
if (!stale || !fullRefresh)
|
|
514
|
+
return null;
|
|
515
|
+
if (stale.relationship !== fullRefresh.relationship || stale.reindexRecommendation !== fullRefresh.reindexRecommendation) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
return stale;
|
|
519
|
+
}
|
|
520
|
+
function renderIncrementalChangeStalenessTreatment(lines, label, treatment) {
|
|
521
|
+
pushSection(lines, label);
|
|
522
|
+
if (!treatment) {
|
|
523
|
+
lines.push("Not available.");
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
lines.push(fieldLine("Treatment Status", treatment.status));
|
|
527
|
+
lines.push(fieldLine("Failure Reason", treatment.failureReason));
|
|
528
|
+
lines.push(fieldLine("Active Index Phase", treatment.activeIndexPhase));
|
|
529
|
+
lines.push(fieldLine("Baseline Freshness Status", treatment.baselineFreshness.status));
|
|
530
|
+
lines.push("Retrieval (deterministic fake-agent / simulated harness evidence label applies to fakeAgent below):");
|
|
531
|
+
lines.push(fieldLine("Retrieval Status", treatment.retrieval.status));
|
|
532
|
+
lines.push(fieldLine("Selected Node ID", treatment.retrieval.selectedNodeId));
|
|
533
|
+
lines.push(fieldLine("Selected File", treatment.retrieval.selectedFile));
|
|
534
|
+
lines.push(fieldLine("Selected Symbol", treatment.retrieval.selectedSymbol));
|
|
535
|
+
lines.push(fieldLine("Context Characters", treatment.retrieval.totalChars));
|
|
536
|
+
lines.push(fieldLine("Estimated Context Tokens", treatment.retrieval.totalEstimatedTokens));
|
|
537
|
+
lines.push(fieldLine("Retrieval Duration (ms)", treatment.retrieval.durationMs));
|
|
538
|
+
pushBoundedStringList(lines, "Files Read", treatment.retrieval.filesRead);
|
|
539
|
+
pushBoundedStringList(lines, "Retrieval Warnings", treatment.retrieval.warnings);
|
|
540
|
+
lines.push("Fake-Agent Evidence (deterministic fake-agent / simulated harness evidence):");
|
|
541
|
+
if (treatment.fakeAgent) {
|
|
542
|
+
lines.push(fieldLine("Fake-Agent Status", treatment.fakeAgent.status));
|
|
543
|
+
lines.push(fieldLine("Correctness Score", treatment.fakeAgent.correctness.available ? treatment.fakeAgent.correctness.score : null));
|
|
544
|
+
lines.push(fieldLine("Passed", treatment.fakeAgent.correctness.passed));
|
|
545
|
+
lines.push(fieldLine("Fake-Agent Duration (ms)", treatment.fakeAgent.durationMs));
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
lines.push("Not run for this treatment.");
|
|
549
|
+
}
|
|
550
|
+
lines.push("Required-File Evidence (bounded answer-key file-presence check, not retrieval precision/recall):");
|
|
551
|
+
lines.push(fieldLine("Required-File Status", treatment.requiredFileEvidence.status));
|
|
552
|
+
lines.push(fieldLine("Required-File Reason", treatment.requiredFileEvidence.reason));
|
|
553
|
+
pushBoundedStringList(lines, "Required Files", treatment.requiredFileEvidence.requiredFiles);
|
|
554
|
+
pushBoundedStringList(lines, "Observed Files", treatment.requiredFileEvidence.observedFiles);
|
|
555
|
+
pushBoundedStringList(lines, "Missing Files", treatment.requiredFileEvidence.missingFiles);
|
|
556
|
+
}
|
|
557
|
+
function renderIncrementalChangeStalenessScenario(lines, scenario) {
|
|
558
|
+
pushSection(lines, `Scenario: ${scenario.scenarioId}`);
|
|
559
|
+
lines.push(fieldLine("Category", scenario.category));
|
|
560
|
+
lines.push(fieldLine("Status", scenario.status));
|
|
561
|
+
lines.push(fieldLine("Failure Reason", scenario.failureReason));
|
|
562
|
+
lines.push(fieldLine("Benchmark Project", scenario.benchmarkProjectId));
|
|
563
|
+
lines.push(fieldLine("Base Case", scenario.baseCaseId));
|
|
564
|
+
lines.push(fieldLine("Answer Policy", scenario.answerPolicy));
|
|
565
|
+
lines.push(fieldLine("Query", scenario.query));
|
|
566
|
+
pushBoundedStringList(lines, "Expected Files", scenario.expectedFiles);
|
|
567
|
+
pushBoundedStringList(lines, "Expected Symbols", scenario.expectedSymbols);
|
|
568
|
+
pushSection(lines, "Lifecycle");
|
|
569
|
+
const lifecycle = scenario.lifecycle;
|
|
570
|
+
if (!lifecycle) {
|
|
571
|
+
lines.push("Not available.");
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
lines.push(fieldLine("Pre-Mutation Equivalence", lifecycle.preMutationEquivalence));
|
|
575
|
+
lines.push(fieldLine("Post-Mutation Equivalence", lifecycle.postMutationEquivalence));
|
|
576
|
+
lines.push(fieldLine("my-dev-kit Version", lifecycle.myDevKitVersion));
|
|
577
|
+
lines.push(fieldLine("Stale Baseline Freshness Status", lifecycle.staleBaselineFreshnessStatus));
|
|
578
|
+
lines.push(fieldLine("Full-Refresh Baseline Freshness Status", lifecycle.fullRefreshBaselineFreshnessStatus));
|
|
579
|
+
lines.push(fieldLine("Full-Refresh Refreshed Freshness Status", lifecycle.fullRefreshRefreshedFreshnessStatus));
|
|
580
|
+
pushBoundedStringList(lines, "Controlled File Paths", lifecycle.controlledFilePaths);
|
|
581
|
+
pushBoundedStringList(lines, "Source Roots", lifecycle.sourceRoots);
|
|
582
|
+
}
|
|
583
|
+
pushSection(lines, "Affected-Neighborhood Evidence (changed baseline neighborhood relative to the task)");
|
|
584
|
+
const symmetric = symmetricIncrementalAffectedNeighborhood(scenario);
|
|
585
|
+
const bothPresent = scenario.staleTreatment?.affectedNeighborhood && scenario.fullRefreshTreatment?.affectedNeighborhood;
|
|
586
|
+
if (!scenario.staleTreatment?.affectedNeighborhood && !scenario.fullRefreshTreatment?.affectedNeighborhood) {
|
|
587
|
+
lines.push("Not available.");
|
|
588
|
+
}
|
|
589
|
+
else if (bothPresent && !symmetric) {
|
|
590
|
+
lines.push("inconsistent persisted evidence");
|
|
591
|
+
}
|
|
592
|
+
else if (symmetric) {
|
|
593
|
+
lines.push(fieldLine("Relationship", symmetric.relationship));
|
|
594
|
+
lines.push(fieldLine("Reindex Recommendation", symmetric.reindexRecommendation));
|
|
595
|
+
lines.push(fieldLine("Reindex Recommendation Explanation", REINDEX_RECOMMENDATION_EXPLANATIONS[symmetric.reindexRecommendation]));
|
|
596
|
+
lines.push(fieldLine("Changed Indexed Files", symmetric.changedFileCount));
|
|
597
|
+
lines.push(fieldLine("Changed Baseline Symbols", symmetric.changedSymbolCount));
|
|
598
|
+
lines.push(fieldLine("Affected Graph Nodes", symmetric.affectedNodeCount));
|
|
599
|
+
lines.push(fieldLine("Affected Graph Edges", symmetric.affectedEdgeCount));
|
|
600
|
+
lines.push(fieldLine("Task-Overlap Nodes", symmetric.taskOverlapCount));
|
|
601
|
+
lines.push(fieldLine("Task-Overlap Percent", symmetric.taskOverlapPercent));
|
|
602
|
+
}
|
|
603
|
+
renderIncrementalChangeStalenessTreatment(lines, "Stale-Index Treatment", scenario.staleTreatment);
|
|
604
|
+
renderIncrementalChangeStalenessTreatment(lines, "Full-Refresh Treatment", scenario.fullRefreshTreatment);
|
|
605
|
+
pushSection(lines, "Matched Comparison");
|
|
606
|
+
lines.push(fieldLine("Correctness Relation", scenario.comparison.correctnessRelation));
|
|
607
|
+
lines.push(fieldLine("Required-File Evidence Relation", scenario.comparison.requiredFileEvidenceRelation));
|
|
608
|
+
lines.push(fieldLine("Stale-Risk Classification", scenario.comparison.staleRiskClassification));
|
|
609
|
+
pushDashList(lines, scenario.comparison.reasonCodes);
|
|
610
|
+
lines.push(STALE_RISK_CLASSIFICATION_EXPLANATIONS[scenario.comparison.staleRiskClassification]);
|
|
611
|
+
}
|
|
612
|
+
function renderIncrementalChangeStalenessSection(lines, section) {
|
|
613
|
+
if (section === null) {
|
|
614
|
+
lines.push("Not applicable to this plugin.");
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (isV2ReportSection(section)) {
|
|
618
|
+
renderIncrementalChangeStalenessSectionV2(lines, section, { pushSection, fieldLine, pushDashList, pushBoundedStringList });
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
pushSection(lines, "Summary");
|
|
622
|
+
lines.push(fieldLine("Scenarios", section.scenarioCount));
|
|
623
|
+
lines.push(fieldLine("Ready", section.readyScenarioCount));
|
|
624
|
+
lines.push(fieldLine("Failed", section.failedScenarioCount));
|
|
625
|
+
lines.push(fieldLine("Observed Stale Regression", section.observedStaleRegressionCount));
|
|
626
|
+
lines.push(fieldLine("No Observed Stale Regression", section.noObservedStaleRegressionCount));
|
|
627
|
+
lines.push(fieldLine("Inconclusive", section.inconclusiveCount));
|
|
628
|
+
for (const scenario of section.scenarios) {
|
|
629
|
+
renderIncrementalChangeStalenessScenario(lines, scenario);
|
|
630
|
+
}
|
|
631
|
+
pushSection(lines, "Limitations");
|
|
632
|
+
pushDashList(lines, section.limitations);
|
|
633
|
+
}
|
|
492
634
|
export function renderPluginExperimentReportText(report) {
|
|
493
635
|
const lines = [];
|
|
494
636
|
pushSection(lines, "Plugin Experiment Report");
|
|
@@ -519,6 +661,8 @@ export function renderPluginExperimentReportText(report) {
|
|
|
519
661
|
renderV043Section(lines, report.contextStrategyComparisonV043);
|
|
520
662
|
pushSection(lines, "Warm Index Reuse Evidence");
|
|
521
663
|
renderWarmIndexReuseSection(lines, report.warmIndexReuse);
|
|
664
|
+
pushSection(lines, "Incremental-Change And Staleness Evidence");
|
|
665
|
+
renderIncrementalChangeStalenessSection(lines, report.incrementalChangeStaleness);
|
|
522
666
|
pushSection(lines, "Warnings, Skips, And Failures");
|
|
523
667
|
pushDashList(lines, report.findings.map((finding) => `${finding.severity} ${finding.code} ${finding.message} variant=${finding.variantId ?? ""} case=${finding.caseId ?? ""}`));
|
|
524
668
|
pushSection(lines, "Artifacts");
|