sela-core 1.0.6 → 1.0.8
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/README.md +1 -1
- package/dist/cli/ErrorHandler.d.ts +1 -1
- package/dist/cli/ErrorHandler.js +14 -14
- package/dist/cli/commands/bulk.d.ts +1 -1
- package/dist/cli/commands/bulk.d.ts.map +1 -1
- package/dist/cli/commands/bulk.js +51 -33
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +26 -18
- package/dist/cli/commands/showReport.d.ts +1 -1
- package/dist/cli/commands/showReport.d.ts.map +1 -1
- package/dist/cli/commands/showReport.js +12 -10
- package/dist/cli/commands/status.d.ts +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +8 -8
- package/dist/cli/commands/sync.d.ts +1 -1
- package/dist/cli/commands/sync.d.ts.map +1 -1
- package/dist/cli/commands/sync.js +38 -30
- package/dist/cli/index.js +9 -9
- package/dist/cli/ui/DnaTable.d.ts +1 -1
- package/dist/cli/ui/DnaTable.d.ts.map +1 -1
- package/dist/cli/ui/DnaTable.js +23 -16
- package/dist/cli/ui/RefactorWizard.d.ts +3 -3
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -1
- package/dist/cli/ui/RefactorWizard.js +108 -82
- package/dist/config/ConfigLoader.d.ts +3 -2
- package/dist/config/ConfigLoader.d.ts.map +1 -1
- package/dist/config/ConfigLoader.js +23 -13
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +79 -0
- package/dist/config/SelaConfig.d.ts +26 -16
- package/dist/config/SelaConfig.d.ts.map +1 -1
- package/dist/config/SelaConfig.js +54 -22
- package/dist/engine/HealingRegistry.d.ts +1 -1
- package/dist/engine/HealingRegistry.js +3 -3
- package/dist/engine/SelaEngine.d.ts +13 -10
- package/dist/engine/SelaEngine.d.ts.map +1 -1
- package/dist/engine/SelaEngine.js +292 -101
- package/dist/errors/SelaError.d.ts +132 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +154 -0
- package/dist/fixtures/expectProxy.d.ts +1 -1
- package/dist/fixtures/expectProxy.d.ts.map +1 -1
- package/dist/fixtures/expectProxy.js +17 -10
- package/dist/fixtures/index.d.ts +7 -1
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +26 -11
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/reporter/SelaReporter.d.ts +143 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +428 -0
- package/dist/services/ASTSourceUpdater.d.ts +20 -1
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -1
- package/dist/services/ASTSourceUpdater.js +257 -89
- package/dist/services/ArgumentTypeAnalyzer.js +7 -7
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -1
- package/dist/services/BlastRadiusAnalyzer.js +5 -2
- package/dist/services/ChainValidator.js +22 -22
- package/dist/services/CrossFileHealer.js +7 -7
- package/dist/services/DefinitionTracer.d.ts +1 -1
- package/dist/services/DefinitionTracer.d.ts.map +1 -1
- package/dist/services/DefinitionTracer.js +28 -13
- package/dist/services/DnaEditorService.d.ts +2 -2
- package/dist/services/DnaEditorService.d.ts.map +1 -1
- package/dist/services/DnaEditorService.js +81 -62
- package/dist/services/DnaIndexService.d.ts +1 -1
- package/dist/services/DnaIndexService.d.ts.map +1 -1
- package/dist/services/DnaIndexService.js +14 -14
- package/dist/services/HealReportService.d.ts +43 -1
- package/dist/services/HealReportService.d.ts.map +1 -1
- package/dist/services/HealReportService.js +108 -25
- package/dist/services/HealingAdvisory.js +1 -1
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +460 -0
- package/dist/services/HealthReportService.d.ts +1 -1
- package/dist/services/HealthReportService.d.ts.map +1 -1
- package/dist/services/HealthReportService.js +25 -25
- package/dist/services/InitializerUpdater.d.ts.map +1 -1
- package/dist/services/InitializerUpdater.js +20 -1
- package/dist/services/IntentAuditor.d.ts +18 -1
- package/dist/services/IntentAuditor.d.ts.map +1 -1
- package/dist/services/IntentAuditor.js +50 -26
- package/dist/services/InteractiveReview.d.ts +15 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +125 -0
- package/dist/services/LLMService.d.ts +18 -2
- package/dist/services/LLMService.d.ts.map +1 -1
- package/dist/services/LLMService.js +33 -23
- package/dist/services/PRAutomationService.d.ts +14 -1
- package/dist/services/PRAutomationService.d.ts.map +1 -1
- package/dist/services/PRAutomationService.js +287 -92
- package/dist/services/PendingPromptLedger.d.ts +44 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +180 -0
- package/dist/services/ReportGenerator.d.ts +70 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +191 -0
- package/dist/services/SafetyGuard.d.ts +30 -7
- package/dist/services/SafetyGuard.d.ts.map +1 -1
- package/dist/services/SafetyGuard.js +85 -116
- package/dist/services/SnapshotService.js +5 -5
- package/dist/services/SourceLinkService.d.ts +2 -2
- package/dist/services/SourceLinkService.d.ts.map +1 -1
- package/dist/services/SourceLinkService.js +31 -22
- package/dist/services/SourceUpdater.d.ts +1 -1
- package/dist/services/SourceUpdater.d.ts.map +1 -1
- package/dist/services/SourceUpdater.js +66 -34
- package/dist/services/TemplateDiffService.d.ts.map +1 -1
- package/dist/services/TemplateDiffService.js +18 -15
- package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +200 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +382 -0
- package/package.json +71 -67
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// src/services/HealReportService.ts
|
|
3
3
|
//
|
|
4
|
-
// Sela Insights
|
|
4
|
+
// Sela Insights - Transparent Healing Report.
|
|
5
5
|
// Buffers a HealEvent per heal attempt (HEALED, FAILED, PROTECTED), then renders
|
|
6
6
|
// a single self-contained HTML file (sela-report.html) at commitUpdates() time.
|
|
7
7
|
//
|
|
@@ -47,9 +47,9 @@ const path = __importStar(require("path"));
|
|
|
47
47
|
// Time-saved heuristics (minutes). Tuned for "fair midpoint" engineer estimate.
|
|
48
48
|
const TIME_PER_HEAL = 12;
|
|
49
49
|
const TIME_PER_PROTECT = 18; // catching a masked bug is worth more
|
|
50
|
-
const TIME_PER_FAILED = 4; // partial value
|
|
50
|
+
const TIME_PER_FAILED = 4; // partial value - narrows debug scope
|
|
51
51
|
// ═══════════════════════════════════════════════════════════════════
|
|
52
|
-
// SUMMARISERS
|
|
52
|
+
// SUMMARISERS - ElementSnapshotV2 → DnaSummary
|
|
53
53
|
// ═══════════════════════════════════════════════════════════════════
|
|
54
54
|
function summariseSnapshot(s) {
|
|
55
55
|
if (!s)
|
|
@@ -96,6 +96,52 @@ class HealReportService {
|
|
|
96
96
|
getProtectedEvents() {
|
|
97
97
|
return this.events.filter((e) => e.kind === "PROTECTED");
|
|
98
98
|
}
|
|
99
|
+
/** All FAILED events accumulated in this session. */
|
|
100
|
+
getFailedEvents() {
|
|
101
|
+
return this.events.filter((e) => e.kind === "FAILED");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Inject a Playwright failure screenshot (Base64 PNG, no data: prefix)
|
|
105
|
+
* onto the FAILED event(s) matching the supplied identity hints. Used
|
|
106
|
+
* by `SelaReporter` to enrich engine-emitted FAILED events with the
|
|
107
|
+
* Playwright-captured screenshot from `TestResult.attachments`.
|
|
108
|
+
*
|
|
109
|
+
* Match policy (all supplied hints must agree):
|
|
110
|
+
* • `sourceFile` - suffix-matched both directions to absorb
|
|
111
|
+
* project-relative vs absolute path mismatches between the engine
|
|
112
|
+
* (relative) and Playwright TestInfo.location.file (absolute).
|
|
113
|
+
* • `testTitle` - exact match.
|
|
114
|
+
* • `stableId` - exact match.
|
|
115
|
+
*
|
|
116
|
+
* Returns the number of events updated. Zero means the caller should
|
|
117
|
+
* keep the screenshot pending until the matching FailedEvent is
|
|
118
|
+
* recorded (worker-crash edge case where the heal pipeline did not
|
|
119
|
+
* run before the Reporter received the failure).
|
|
120
|
+
*/
|
|
121
|
+
attachFailureScreenshot(match, base64Png) {
|
|
122
|
+
if (!base64Png)
|
|
123
|
+
return 0;
|
|
124
|
+
const norm = (p) => (p ?? "").replace(/\\/g, "/").toLowerCase();
|
|
125
|
+
const targetFile = norm(match.sourceFile);
|
|
126
|
+
let touched = 0;
|
|
127
|
+
for (const ev of this.events) {
|
|
128
|
+
if (ev.kind !== "FAILED")
|
|
129
|
+
continue;
|
|
130
|
+
const evFile = norm(ev.sourceFile);
|
|
131
|
+
const fileMatch = !targetFile ||
|
|
132
|
+
!evFile ||
|
|
133
|
+
evFile === targetFile ||
|
|
134
|
+
evFile.endsWith(targetFile) ||
|
|
135
|
+
targetFile.endsWith(evFile);
|
|
136
|
+
const titleMatch = !match.testTitle || ev.testTitle === match.testTitle;
|
|
137
|
+
const idMatch = !match.stableId || ev.stableId === match.stableId;
|
|
138
|
+
if (fileMatch && titleMatch && idMatch) {
|
|
139
|
+
ev.failureScreenshotPng = base64Png;
|
|
140
|
+
touched++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return touched;
|
|
144
|
+
}
|
|
99
145
|
/**
|
|
100
146
|
* Write `sela-report.html` (+ `sela-report.json` debug dump) to cwd.
|
|
101
147
|
* Returns the absolute path of the HTML report, or null if nothing to write.
|
|
@@ -107,7 +153,11 @@ class HealReportService {
|
|
|
107
153
|
const htmlPath = path.join(targetDir, "sela-report.html");
|
|
108
154
|
const jsonPath = path.join(targetDir, "sela-report.json");
|
|
109
155
|
fs.writeFileSync(htmlPath, html, "utf8");
|
|
110
|
-
fs.writeFileSync(jsonPath, JSON.stringify({
|
|
156
|
+
fs.writeFileSync(jsonPath, JSON.stringify({
|
|
157
|
+
generatedAt: new Date().toISOString(),
|
|
158
|
+
startedAt: this.startedAt,
|
|
159
|
+
events: this.events,
|
|
160
|
+
}, null, 2), "utf8");
|
|
111
161
|
return htmlPath;
|
|
112
162
|
}
|
|
113
163
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -170,14 +220,14 @@ function htmlEscape(s) {
|
|
|
170
220
|
}
|
|
171
221
|
function renderHtmlTemplate(input) {
|
|
172
222
|
// Embed structured data into the page so the front-end script renders it
|
|
173
|
-
//
|
|
223
|
+
// - keeps the template compact and avoids server-side template branching.
|
|
174
224
|
const payload = JSON.stringify(input).replace(/</g, "\\u003c");
|
|
175
225
|
return `<!DOCTYPE html>
|
|
176
226
|
<html lang="en" data-theme="dark">
|
|
177
227
|
<head>
|
|
178
228
|
<meta charset="UTF-8" />
|
|
179
229
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
|
180
|
-
<title>Sela Insights
|
|
230
|
+
<title>Sela Insights - Healing Report</title>
|
|
181
231
|
<style>
|
|
182
232
|
${REPORT_CSS}
|
|
183
233
|
</style>
|
|
@@ -266,7 +316,7 @@ ${REPORT_JS}
|
|
|
266
316
|
</html>`;
|
|
267
317
|
}
|
|
268
318
|
// ═══════════════════════════════════════════════════════════════════
|
|
269
|
-
// CSS
|
|
319
|
+
// CSS - dark, immersive, animated
|
|
270
320
|
// ═══════════════════════════════════════════════════════════════════
|
|
271
321
|
const REPORT_CSS = `
|
|
272
322
|
:root {
|
|
@@ -684,6 +734,23 @@ a:hover { text-decoration: underline; }
|
|
|
684
734
|
.tag.ghost { color: var(--warn); border-color: rgba(251,191,36,0.3); }
|
|
685
735
|
.tag.occlude { color: var(--bad); border-color: rgba(248,113,113,0.3); }
|
|
686
736
|
|
|
737
|
+
/* Failure context - Playwright screenshot rendered inline on FAILED cards */
|
|
738
|
+
.failure-img {
|
|
739
|
+
background: var(--bg-1); border: 1px solid var(--line);
|
|
740
|
+
border-radius: var(--radius-sm); padding: 8px;
|
|
741
|
+
overflow: hidden;
|
|
742
|
+
}
|
|
743
|
+
.failure-img img {
|
|
744
|
+
display: block; max-width: 100%; height: auto;
|
|
745
|
+
border-radius: 4px;
|
|
746
|
+
background: #000;
|
|
747
|
+
outline: 1px solid var(--line);
|
|
748
|
+
}
|
|
749
|
+
.failure-img .img-meta {
|
|
750
|
+
font-family: var(--mono); font-size: 11px; color: var(--text-muted);
|
|
751
|
+
padding: 6px 4px 0;
|
|
752
|
+
}
|
|
753
|
+
|
|
687
754
|
/* Empty state */
|
|
688
755
|
.empty {
|
|
689
756
|
margin-top: 32px; padding: 60px 20px; text-align: center;
|
|
@@ -734,7 +801,7 @@ a:hover { text-decoration: underline; }
|
|
|
734
801
|
}
|
|
735
802
|
`;
|
|
736
803
|
// ═══════════════════════════════════════════════════════════════════
|
|
737
|
-
// FRONT-END JS
|
|
804
|
+
// FRONT-END JS - renders the embedded payload
|
|
738
805
|
// ═══════════════════════════════════════════════════════════════════
|
|
739
806
|
const REPORT_JS = String.raw `
|
|
740
807
|
(function() {
|
|
@@ -752,7 +819,7 @@ const REPORT_JS = String.raw `
|
|
|
752
819
|
try { return new Date(iso).toLocaleString(); } catch { return iso; }
|
|
753
820
|
}
|
|
754
821
|
function pluralize(n, one, many) { return n === 1 ? one : (many || one + "s"); }
|
|
755
|
-
// Graceful percentage formatter
|
|
822
|
+
// Graceful percentage formatter - mirrors the server-side helper in
|
|
756
823
|
// PRAutomationService. Anything that is not a finite number renders "n/a"
|
|
757
824
|
// instead of being coerced to 0%. Used in every spot that previously
|
|
758
825
|
// hard-coded a plain "x + %" concatenation.
|
|
@@ -777,7 +844,7 @@ const REPORT_JS = String.raw `
|
|
|
777
844
|
if (s.protectedCnt > 0 && s.healed === 0) {
|
|
778
845
|
return [
|
|
779
846
|
"Sela protected your suite from " + s.protectedCnt + " masked " + pluralize(s.protectedCnt, "regression") + ".",
|
|
780
|
-
"These changes looked safe
|
|
847
|
+
"These changes looked safe - but the Intent Auditor caught semantic mismatch and blocked the auto-fix.",
|
|
781
848
|
"Bugs caught"
|
|
782
849
|
];
|
|
783
850
|
}
|
|
@@ -797,7 +864,7 @@ const REPORT_JS = String.raw `
|
|
|
797
864
|
}
|
|
798
865
|
return [
|
|
799
866
|
"Sela couldn't heal " + s.failed + " " + pluralize(s.failed, "selector") + ".",
|
|
800
|
-
"These need a human hand
|
|
867
|
+
"These need a human hand - but you've got context, candidates, and direct links ready below.",
|
|
801
868
|
"Needs attention"
|
|
802
869
|
];
|
|
803
870
|
})();
|
|
@@ -863,15 +930,15 @@ const REPORT_JS = String.raw `
|
|
|
863
930
|
const same = oldStr === newStr;
|
|
864
931
|
const hasAfter = !!after;
|
|
865
932
|
let cls, icon;
|
|
866
|
-
if (!hasAfter) { cls = "missing"; icon = "
|
|
933
|
+
if (!hasAfter) { cls = "missing"; icon = "-"; }
|
|
867
934
|
else if (same) { cls = "matched"; icon = "✅"; }
|
|
868
935
|
else { cls = "changed"; icon = "⚠️"; }
|
|
869
936
|
const valCell = !hasAfter
|
|
870
|
-
? '<td class="val">' + esc(oldStr || "
|
|
937
|
+
? '<td class="val">' + esc(oldStr || "-") + '</td>'
|
|
871
938
|
: same
|
|
872
|
-
? '<td class="val" colspan="1">' + esc(oldStr || "
|
|
873
|
-
: '<td class="val"><span class="val old">' + esc(oldStr || "
|
|
874
|
-
'<br><span class="val new">' + esc(newStr || "
|
|
939
|
+
? '<td class="val" colspan="1">' + esc(oldStr || "-") + '</td>'
|
|
940
|
+
: '<td class="val"><span class="val old">' + esc(oldStr || "-") + '</span>' +
|
|
941
|
+
'<br><span class="val new">' + esc(newStr || "-") + '</span></td>';
|
|
875
942
|
rows.push(
|
|
876
943
|
'<tr class="' + cls + '">' +
|
|
877
944
|
'<td class="field">' + esc(field) + '</td>' +
|
|
@@ -1002,10 +1069,10 @@ const REPORT_JS = String.raw `
|
|
|
1002
1069
|
if (ev.kind !== "HEALED") {
|
|
1003
1070
|
if (ev.kind === "PROTECTED") {
|
|
1004
1071
|
return (
|
|
1005
|
-
'<div class="muted-block">No source code was modified. Sela blocked the AI-proposed change because the Intent Auditor flagged a semantic mismatch
|
|
1072
|
+
'<div class="muted-block">No source code was modified. Sela blocked the AI-proposed change because the Intent Auditor flagged a semantic mismatch - your test file is untouched and the underlying behaviour change is still surfacing in CI.</div>'
|
|
1006
1073
|
);
|
|
1007
1074
|
}
|
|
1008
|
-
return '<div class="muted-block">No source code was modified
|
|
1075
|
+
return '<div class="muted-block">No source code was modified - the AI could not propose a confident fix for this selector.</div>';
|
|
1009
1076
|
}
|
|
1010
1077
|
|
|
1011
1078
|
const raw = ev.gitUnifiedDiff || "";
|
|
@@ -1061,6 +1128,19 @@ const REPORT_JS = String.raw `
|
|
|
1061
1128
|
);
|
|
1062
1129
|
}
|
|
1063
1130
|
|
|
1131
|
+
function renderFailureContext(ev) {
|
|
1132
|
+
if (ev.kind !== "FAILED" || !ev.failureScreenshotPng) return "";
|
|
1133
|
+
return (
|
|
1134
|
+
'<div class="section">' +
|
|
1135
|
+
'<div class="section-title"><span class="marker"></span>Failure Context · Playwright Screenshot</div>' +
|
|
1136
|
+
'<div class="failure-img">' +
|
|
1137
|
+
'<img src="data:image/png;base64,' + ev.failureScreenshotPng + '" alt="Playwright failure screenshot" loading="lazy" />' +
|
|
1138
|
+
'<div class="img-meta">captured by SelaReporter at test failure · base64-embedded · survives Playwright temp cleanup</div>' +
|
|
1139
|
+
'</div>' +
|
|
1140
|
+
'</div>'
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1064
1144
|
function renderAlts(ev) {
|
|
1065
1145
|
if (ev.kind !== "HEALED" || !ev.aiAlternatives || !ev.aiAlternatives.length) return "";
|
|
1066
1146
|
return (
|
|
@@ -1075,7 +1155,7 @@ const REPORT_JS = String.raw `
|
|
|
1075
1155
|
|
|
1076
1156
|
function renderActions(ev) {
|
|
1077
1157
|
const issueUrl = "https://github.com/anthropics/sela-core/issues/new?title=" +
|
|
1078
|
-
encodeURIComponent("[Sela Report] " + ev.kind + "
|
|
1158
|
+
encodeURIComponent("[Sela Report] " + ev.kind + " - " + ev.stableId) +
|
|
1079
1159
|
"&body=" + encodeURIComponent(
|
|
1080
1160
|
"Stable ID: " + ev.stableId + "\n" +
|
|
1081
1161
|
"File: " + ev.sourceFile + ":" + ev.sourceLine + "\n" +
|
|
@@ -1095,7 +1175,7 @@ const REPORT_JS = String.raw `
|
|
|
1095
1175
|
if (ev.kind === "PROTECTED") {
|
|
1096
1176
|
return (
|
|
1097
1177
|
'<div class="actions">' +
|
|
1098
|
-
'<button class="btn primary" data-action="acknowledge" data-id="' + ev.stableId + '">✓ Acknowledge
|
|
1178
|
+
'<button class="btn primary" data-action="acknowledge" data-id="' + ev.stableId + '">✓ Acknowledge - I will investigate</button>' +
|
|
1099
1179
|
'<a class="btn ghost" target="_blank" rel="noopener" href="' + issueUrl + '">⚑ File regression</a>' +
|
|
1100
1180
|
'</div>'
|
|
1101
1181
|
);
|
|
@@ -1150,7 +1230,10 @@ const REPORT_JS = String.raw `
|
|
|
1150
1230
|
'</div>'
|
|
1151
1231
|
);
|
|
1152
1232
|
|
|
1153
|
-
// 5.
|
|
1233
|
+
// 5. Failure Context - Playwright screenshot (FAILED only)
|
|
1234
|
+
blocks.push(renderFailureContext(ev));
|
|
1235
|
+
|
|
1236
|
+
// 6. Alternatives
|
|
1154
1237
|
blocks.push(renderAlts(ev));
|
|
1155
1238
|
|
|
1156
1239
|
// 6. Actions
|
|
@@ -1163,14 +1246,14 @@ const REPORT_JS = String.raw `
|
|
|
1163
1246
|
const steps = [];
|
|
1164
1247
|
steps.push("AI proposed a replacement selector for the broken locator.");
|
|
1165
1248
|
if (ev.aiExplanation) steps.push("Reasoning given: " + ev.aiExplanation);
|
|
1166
|
-
if (ev.candidateNewSelector) steps.push("Candidate selector resolved live
|
|
1249
|
+
if (ev.candidateNewSelector) steps.push("Candidate selector resolved live - but its semantics changed.");
|
|
1167
1250
|
if (ev.auditor) steps.push("Intent Auditor verdict: " + ev.auditor.verdict + " · " + ev.auditor.reason);
|
|
1168
1251
|
steps.push("Sela blocked the auto-fix. Source code untouched. Investigate this as a real regression.");
|
|
1169
1252
|
return steps;
|
|
1170
1253
|
}
|
|
1171
1254
|
function buildFailedSteps(ev) {
|
|
1172
1255
|
const steps = [];
|
|
1173
|
-
steps.push("Locator failed to resolve at runtime
|
|
1256
|
+
steps.push("Locator failed to resolve at runtime - Sela attempted to heal it.");
|
|
1174
1257
|
if (ev.aiExplanation) steps.push("AI response: " + ev.aiExplanation);
|
|
1175
1258
|
steps.push("No confident replacement was produced. Reason: " + (ev.reason || "unknown"));
|
|
1176
1259
|
steps.push("Open the file directly in your editor to inspect the original locator.");
|
|
@@ -1203,7 +1286,7 @@ const REPORT_JS = String.raw `
|
|
|
1203
1286
|
// ── Mount ─────────────────────────────────────────────────────
|
|
1204
1287
|
const $events = $("#events");
|
|
1205
1288
|
if (DATA.events.length === 0) {
|
|
1206
|
-
$events.innerHTML = '<div class="muted-block">Sela was idle on this run
|
|
1289
|
+
$events.innerHTML = '<div class="muted-block">Sela was idle on this run - no broken selectors encountered.</div>';
|
|
1207
1290
|
} else {
|
|
1208
1291
|
$events.innerHTML = DATA.events.map(eventCard).join("");
|
|
1209
1292
|
|
|
@@ -1239,7 +1322,7 @@ const REPORT_JS = String.raw `
|
|
|
1239
1322
|
if (action === "keep" || action === "acknowledge") {
|
|
1240
1323
|
btn.dataset.state = "approved";
|
|
1241
1324
|
btn.textContent = action === "keep" ? "✓ Approved" : "✓ Acknowledged";
|
|
1242
|
-
showToast("Saved locally
|
|
1325
|
+
showToast("Saved locally - Sela noted your decision for " + id);
|
|
1243
1326
|
} else if (action === "rollback") {
|
|
1244
1327
|
const cmd = "npx sela dna refactor --rollback " + id;
|
|
1245
1328
|
navigator.clipboard?.writeText(cmd).catch(() => {});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// Typed advisory system for definition-site mutations.
|
|
6
6
|
// Advisories are buffered during the AST phase and flushed AFTER
|
|
7
|
-
// Playwright prints its test result
|
|
7
|
+
// Playwright prints its test result - prevents interleaving.
|
|
8
8
|
// ═══════════════════════════════════════════════════════════════════
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.AdvisoryBuffer = exports.AdvisoryCode = void 0;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { SmartChainSegment, SelectorSegment } from "./LLMService";
|
|
2
|
+
export type HealCacheMode = "action" | "assertion";
|
|
3
|
+
export interface CacheKeyInput {
|
|
4
|
+
/** Absolute or project-relative spec path containing the failing locator. */
|
|
5
|
+
filePath: string;
|
|
6
|
+
/** 1-based line of the failing call site - paired with filePath as a fingerprint. */
|
|
7
|
+
line: number;
|
|
8
|
+
/**
|
|
9
|
+
* Stable content hash of the DNA snapshot used to drive the heal. When the
|
|
10
|
+
* DNA rewrites, this changes - every cached entry for the old DNA
|
|
11
|
+
* silently stops matching and ages out via TTL.
|
|
12
|
+
*/
|
|
13
|
+
dnaContentHash: string;
|
|
14
|
+
/** The original broken selector. */
|
|
15
|
+
brokenSelector: string;
|
|
16
|
+
/** Heal mode the cached fix was produced under. */
|
|
17
|
+
mode: HealCacheMode;
|
|
18
|
+
}
|
|
19
|
+
export interface HealingCacheEntry {
|
|
20
|
+
/** Full SHA1 of the CacheKeyInput tuple. */
|
|
21
|
+
key: string;
|
|
22
|
+
filePath: string;
|
|
23
|
+
line: number;
|
|
24
|
+
dnaContentHash: string;
|
|
25
|
+
brokenSelector: string;
|
|
26
|
+
mode: HealCacheMode;
|
|
27
|
+
healedSelector: string;
|
|
28
|
+
chainSegments?: SmartChainSegment[];
|
|
29
|
+
segments?: SelectorSegment[];
|
|
30
|
+
confidence?: number;
|
|
31
|
+
contentChange?: {
|
|
32
|
+
oldText: string;
|
|
33
|
+
newText: string;
|
|
34
|
+
};
|
|
35
|
+
explanation?: string;
|
|
36
|
+
alternatives?: string[];
|
|
37
|
+
/**
|
|
38
|
+
* True when SafetyGuard cleared this fix in the originating session
|
|
39
|
+
* (SAFE or REQUIRES_REVIEW). Unsafe entries are never replayed.
|
|
40
|
+
*/
|
|
41
|
+
safe: boolean;
|
|
42
|
+
createdAt: string;
|
|
43
|
+
/** ISO timestamp after which the entry is considered expired and ignored. */
|
|
44
|
+
ttlExpiresAt: string;
|
|
45
|
+
lastHitAt?: string;
|
|
46
|
+
hitCount: number;
|
|
47
|
+
}
|
|
48
|
+
interface CacheFile {
|
|
49
|
+
schemaVersion: 1;
|
|
50
|
+
entries: Record<string, HealingCacheEntry>;
|
|
51
|
+
}
|
|
52
|
+
export interface HealingCacheSetInput extends CacheKeyInput {
|
|
53
|
+
healedSelector: string;
|
|
54
|
+
chainSegments?: SmartChainSegment[];
|
|
55
|
+
segments?: SelectorSegment[];
|
|
56
|
+
confidence?: number;
|
|
57
|
+
contentChange?: {
|
|
58
|
+
oldText: string;
|
|
59
|
+
newText: string;
|
|
60
|
+
};
|
|
61
|
+
explanation?: string;
|
|
62
|
+
alternatives?: string[];
|
|
63
|
+
safe: boolean;
|
|
64
|
+
/** Optional override for entry-level TTL (ms). Defaults to env / 7d. */
|
|
65
|
+
ttlMs?: number;
|
|
66
|
+
}
|
|
67
|
+
export declare function computeCacheKey(input: CacheKeyInput): string;
|
|
68
|
+
/**
|
|
69
|
+
* Convenience helper - fingerprint the JSON form of a DNA snapshot so
|
|
70
|
+
* the cache key changes whenever the DNA is rewritten.
|
|
71
|
+
*/
|
|
72
|
+
export declare function hashDnaContent(dna: unknown): string;
|
|
73
|
+
export declare class HealingCacheService {
|
|
74
|
+
private readonly cacheDir;
|
|
75
|
+
private readonly filename;
|
|
76
|
+
private readonly lockDir;
|
|
77
|
+
private readonly tmpSuffix;
|
|
78
|
+
private readonly bakSuffix;
|
|
79
|
+
private inFlight;
|
|
80
|
+
constructor(opts?: {
|
|
81
|
+
cacheDir?: string;
|
|
82
|
+
filename?: string;
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Read-only lookup. Bumps `hitCount` / `lastHitAt` IN-MEMORY only on
|
|
86
|
+
* the returned entry reference - never persisted on the get() path.
|
|
87
|
+
* This eliminates the read-write race where parallel workers each
|
|
88
|
+
* call get() and serialise full entries-map snapshots back to disk,
|
|
89
|
+
* overwriting peer set() operations.
|
|
90
|
+
*/
|
|
91
|
+
get(input: CacheKeyInput): HealingCacheEntry | null;
|
|
92
|
+
/**
|
|
93
|
+
* Persist a new cache entry. Serialises against in-process writers
|
|
94
|
+
* AND across processes via the mkdir lock. The in-process chain
|
|
95
|
+
* isolates each `await` so a rejected upstream Promise never
|
|
96
|
+
* deadlocks downstream callers.
|
|
97
|
+
*/
|
|
98
|
+
set(input: HealingCacheSetInput): Promise<void>;
|
|
99
|
+
/** Synchronous variant - used by teardown / CLI paths. */
|
|
100
|
+
setSync(input: HealingCacheSetInput): void;
|
|
101
|
+
/** Wipe every entry. Used by tests and the (forthcoming) `sela cache clear` CLI. */
|
|
102
|
+
clear(): void;
|
|
103
|
+
/** Returns the parsed cache file (or an empty shell on failure). */
|
|
104
|
+
read(): CacheFile;
|
|
105
|
+
/** Absolute path of the on-disk cache file. */
|
|
106
|
+
filePath(): string;
|
|
107
|
+
/**
|
|
108
|
+
* Prunes expired entries. Cheap O(n) scan - intended for engine
|
|
109
|
+
* startup. Returns the number of entries removed.
|
|
110
|
+
*/
|
|
111
|
+
pruneExpired(): number;
|
|
112
|
+
private isExpired;
|
|
113
|
+
private lockedWrite;
|
|
114
|
+
private buildEntry;
|
|
115
|
+
/**
|
|
116
|
+
* Reads + parses the cache file. Tolerates every realistic failure
|
|
117
|
+
* (missing, empty, malformed, wrong schema version) by returning a
|
|
118
|
+
* fresh empty shell - the contract is "cache misses are silent".
|
|
119
|
+
*/
|
|
120
|
+
private safeRead;
|
|
121
|
+
/**
|
|
122
|
+
* Atomic write: write to `<file>.tmp`, copy current to `<file>.bak`,
|
|
123
|
+
* then rename `<file>.tmp` → `<file>`. The rename is the single
|
|
124
|
+
* commit point - a crash between bak and rename leaves the bak file
|
|
125
|
+
* intact for future recovery.
|
|
126
|
+
*/
|
|
127
|
+
private atomicWrite;
|
|
128
|
+
private acquireLockWithBackoff;
|
|
129
|
+
private tryAcquireLock;
|
|
130
|
+
private withFileLockSync;
|
|
131
|
+
private releaseLock;
|
|
132
|
+
}
|
|
133
|
+
export declare const sharedHealingCache: HealingCacheService;
|
|
134
|
+
export {};
|
|
135
|
+
//# sourceMappingURL=HealingCacheService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HealingCacheService.d.ts","sourceRoot":"","sources":["../../src/services/HealingCacheService.ts"],"names":[],"mappings":"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"}
|