sela-core 1.0.4 โ 1.0.5
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.
|
@@ -14,6 +14,14 @@ export declare class SelaEngine {
|
|
|
14
14
|
heal(page: Page, fullSelector: string, elementSelectorOnly: string, stableId: string, filePath: string, line: number, healMode?: HealMode): Promise<string>;
|
|
15
15
|
private _toReportFile;
|
|
16
16
|
private _safeReadLine;
|
|
17
|
+
/**
|
|
18
|
+
* Snapshot the entire file as a string[] of lines. Returns an empty array
|
|
19
|
+
* if the file is missing or unreadable. Used to preserve the PRE-mutation
|
|
20
|
+
* source so the diff in HealedEvent reflects the line that was actually
|
|
21
|
+
* mutated (which may differ from the failure line when JumpToDef lands on
|
|
22
|
+
* a const declaration above).
|
|
23
|
+
*/
|
|
24
|
+
private _safeReadAllLines;
|
|
17
25
|
private _auditorBlockFromDecision;
|
|
18
26
|
private _buildAlternatives;
|
|
19
27
|
private _buildReasoningSteps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAMxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAe,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAgChE,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAanD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiDnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"SelaEngine.d.ts","sourceRoot":"","sources":["../../src/engine/SelaEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAMxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,EAAe,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAgChE,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,cAAc,CAAoC;IAC1D,OAAO,CAAC,eAAe,CAAkC;;IAanD,YAAY,CAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAiDnB,IAAI,CACR,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,MAAM,EACpB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,QAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC;IAwclB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,aAAa;IAarB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,kBAAkB;IA8B1B,OAAO,CAAC,oBAAoB;IAuC5B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrD,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YA4FtB,aAAa;IAsB3B,OAAO,CAAC,0BAA0B;IAiClC,OAAO,CAAC,WAAW;IA6Bb,wBAAwB,CAC5B,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;CAqBnB;AAED,OAAO,EAAE,CAAC"}
|
|
@@ -262,7 +262,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
262
262
|
reason: safetyDecision.reason,
|
|
263
263
|
safetyLevel: safetyDecision.level,
|
|
264
264
|
auditor: this._auditorBlockFromDecision(safetyDecision.meta),
|
|
265
|
-
aiConfidence: aiFix.confidence,
|
|
265
|
+
aiConfidence: typeof aiFix.confidence === "number" ? aiFix.confidence : 0,
|
|
266
266
|
aiExplanation: aiFix.explanation ?? "",
|
|
267
267
|
dnaBefore: (0, HealReportService_1.summariseSnapshot)(lastKnownState),
|
|
268
268
|
dnaAfter: (0, HealReportService_1.summariseSnapshot)(liveSnapshot ?? null),
|
|
@@ -286,12 +286,37 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
286
286
|
`(raw ${breakdown.rawConfidence}%, ${parts.join(", ")})`);
|
|
287
287
|
}
|
|
288
288
|
console.log(`[Sela] ๐ Rebuilt Full Runtime Selector (Clean): ${newFullSelector}`);
|
|
289
|
-
//
|
|
290
|
-
|
|
289
|
+
// Snapshot the ENTIRE source file BEFORE the AST mutation.
|
|
290
|
+
// We don't know which line will actually be mutated until update()
|
|
291
|
+
// returns (JumpToDef may land on the const-decl line, not the failure
|
|
292
|
+
// line) โ so we keep the pre-image around and slice into it after.
|
|
293
|
+
const preMutationLines = this._safeReadAllLines(filePath);
|
|
291
294
|
const updateResult = SourceUpdater_1.SourceUpdater.update({ filePath, line }, elementSelectorOnly, newFullSelector, neighborhoodDom, undefined, fullSelector, aiFix.segments, aiFix.contentChange, aiFix.chainSegments, aiFix.originalChainHint, this.config.updateStrategy, this.config.autoCommit);
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
+
// Resolve the line that was actually mutated. Order of precedence:
|
|
296
|
+
// 1. updateResult.lineUpdated (the strategy's literal write location)
|
|
297
|
+
// 2. updateResult.definitionSite.line (semantic JumpToDef target,
|
|
298
|
+
// used when the mutation crossed into a const/variable declaration)
|
|
299
|
+
// 3. line (the original failure line โ last-resort fallback)
|
|
300
|
+
// For cross-file definitionSite.file, we still read the new line from
|
|
301
|
+
// the SAME file that was mutated.
|
|
302
|
+
const defSite = updateResult.definitionSite;
|
|
303
|
+
const sameFileDef = defSite && defSite.file
|
|
304
|
+
? path.resolve(defSite.file) ===
|
|
305
|
+
(SourceUpdater_1.SourceUpdater.resolveFilePath(filePath) ?? "")
|
|
306
|
+
: true;
|
|
307
|
+
const writtenLine = updateResult.lineUpdated ??
|
|
308
|
+
(sameFileDef ? defSite?.line : undefined) ??
|
|
309
|
+
line;
|
|
310
|
+
const mutatedFilePath = !sameFileDef && defSite ? defSite.file : filePath;
|
|
311
|
+
// Old line: PRE-mutation content at the line that ended up being mutated.
|
|
312
|
+
// If the mutation was cross-file, read the pre-image of THAT file by
|
|
313
|
+
// recovering it from disk minus our own change (we don't track that โ
|
|
314
|
+
// fall back to current pre-image if same file, else empty string).
|
|
315
|
+
const oldCodeLine = sameFileDef && preMutationLines.length > 0
|
|
316
|
+
? (preMutationLines[writtenLine - 1] ?? "")
|
|
317
|
+
: this._safeReadLine(mutatedFilePath, writtenLine);
|
|
318
|
+
// New line: POST-mutation content at the same line in the mutated file.
|
|
319
|
+
const newCodeLine = this._safeReadLine(mutatedFilePath, writtenLine);
|
|
295
320
|
const canonicalSelector = updateResult.healedLocatorString ?? newFullSelector;
|
|
296
321
|
console.log(`[Sela] โ
Canonical selector (disk == runtime): "${canonicalSelector}"`);
|
|
297
322
|
// Buffer CLI-ready metadata โ merged into DNA at commitUpdates() time.
|
|
@@ -339,7 +364,7 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
339
364
|
oldCodeLine,
|
|
340
365
|
newCodeLine,
|
|
341
366
|
newLineNumber: writtenLine,
|
|
342
|
-
aiConfidence: aiFix.confidence,
|
|
367
|
+
aiConfidence: typeof aiFix.confidence === "number" ? aiFix.confidence : 0,
|
|
343
368
|
aiExplanation: aiFix.explanation ?? "",
|
|
344
369
|
aiAlternatives: this._buildAlternatives(aiFix.chainSegments, aiFix.segments),
|
|
345
370
|
reasoningSteps: this._buildReasoningSteps({
|
|
@@ -435,6 +460,25 @@ If the text has NOT changed, omit the "contentChange" field entirely.`;
|
|
|
435
460
|
return "";
|
|
436
461
|
}
|
|
437
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* Snapshot the entire file as a string[] of lines. Returns an empty array
|
|
465
|
+
* if the file is missing or unreadable. Used to preserve the PRE-mutation
|
|
466
|
+
* source so the diff in HealedEvent reflects the line that was actually
|
|
467
|
+
* mutated (which may differ from the failure line when JumpToDef lands on
|
|
468
|
+
* a const declaration above).
|
|
469
|
+
*/
|
|
470
|
+
_safeReadAllLines(filePath) {
|
|
471
|
+
try {
|
|
472
|
+
const cleaned = this._toReportFile(filePath);
|
|
473
|
+
const abs = path.isAbsolute(cleaned) ? cleaned : path.resolve(process.cwd(), cleaned);
|
|
474
|
+
if (!fs.existsSync(abs))
|
|
475
|
+
return [];
|
|
476
|
+
return fs.readFileSync(abs, "utf8").split(/\r?\n/);
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
return [];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
438
482
|
_auditorBlockFromDecision(meta) {
|
|
439
483
|
if (!meta || !meta.auditorVerdict)
|
|
440
484
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PRAutomationService.d.ts","sourceRoot":"","sources":["../../src/services/PRAutomationService.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,SAAS,EACV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACf,MAAM,qBAAqB,CAAC;AAM7B,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,eAAe,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,aAAa,GAAG,IAAI,CAAC;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"PRAutomationService.d.ts","sourceRoot":"","sources":["../../src/services/PRAutomationService.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,oBAAoB,EACpB,UAAU,EACV,SAAS,EACV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACf,MAAM,qBAAqB,CAAC;AAM7B,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,UAAU,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,eAAe,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,aAAa,GAAG,IAAI,CAAC;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AA2ED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAsB,GAAG,UAAU,CA8EpE;AAMD,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,oBAAoB,EACzB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,WAAW,EAAE,GACnB,gBAAgB,CAuDlB;AA6DD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,UAAU,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,OAAO,CAC3B,GAAG,EAAE,oBAAoB,EACzB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,eAAe,CAAC,CA4J1B;AAMD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,cAAc,EAAE,EACxB,UAAU,EAAE,UAAU,EACtB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA0Ff"}
|
|
@@ -82,6 +82,30 @@ function gitAvailable(cwd) {
|
|
|
82
82
|
function isDetachedHead(cwd) {
|
|
83
83
|
return !sh("git symbolic-ref HEAD", cwd).ok;
|
|
84
84
|
}
|
|
85
|
+
const SELA_BRANCH_PREFIX = "sela/heal-";
|
|
86
|
+
function isSelaHealBranch(branch) {
|
|
87
|
+
return !!branch && branch.startsWith(SELA_BRANCH_PREFIX);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the repo's default integration branch.
|
|
91
|
+
* 1. origin/HEAD symbolic-ref (set by `git clone`).
|
|
92
|
+
* 2. First existing local ref among: main, master, develop, dev.
|
|
93
|
+
* Returns null if none found โ caller must handle that.
|
|
94
|
+
*/
|
|
95
|
+
function findDefaultBranch(cwd) {
|
|
96
|
+
const headRef = sh("git symbolic-ref refs/remotes/origin/HEAD", cwd);
|
|
97
|
+
if (headRef.ok) {
|
|
98
|
+
const m = headRef.stdout.trim().match(/refs\/remotes\/origin\/(.+)$/);
|
|
99
|
+
if (m)
|
|
100
|
+
return m[1];
|
|
101
|
+
}
|
|
102
|
+
for (const candidate of ["main", "master", "develop", "dev"]) {
|
|
103
|
+
if (sh(`git rev-parse --verify --quiet ${candidate}`, cwd).ok) {
|
|
104
|
+
return candidate;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
85
109
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
86
110
|
// TEMPLATING
|
|
87
111
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -98,14 +122,17 @@ function detectBranch(cwd = process.cwd()) {
|
|
|
98
122
|
const ghEventName = process.env.GITHUB_EVENT_NAME;
|
|
99
123
|
const ghRef = process.env.GITHUB_REF; // "refs/pull/123/merge"
|
|
100
124
|
// Prefer HEAD_REF on PRs โ REF_NAME is the synthetic merge ref.
|
|
101
|
-
if (ghHeadRef && ghHeadRef.length > 0) {
|
|
125
|
+
if (ghHeadRef && ghHeadRef.length > 0 && !isSelaHealBranch(ghHeadRef)) {
|
|
102
126
|
const prNumber = (() => {
|
|
103
127
|
const m = ghRef?.match(/refs\/pull\/(\d+)\//);
|
|
104
128
|
return m ? Number(m[1]) : null;
|
|
105
129
|
})();
|
|
106
130
|
return { branch: ghHeadRef, isPR: true, prNumber, source: "env" };
|
|
107
131
|
}
|
|
108
|
-
if (ghRefName &&
|
|
132
|
+
if (ghRefName &&
|
|
133
|
+
ghRefName.length > 0 &&
|
|
134
|
+
!ghRefName.endsWith("/merge") &&
|
|
135
|
+
!isSelaHealBranch(ghRefName)) {
|
|
109
136
|
return {
|
|
110
137
|
branch: ghRefName,
|
|
111
138
|
isPR: ghEventName === "pull_request",
|
|
@@ -115,18 +142,16 @@ function detectBranch(cwd = process.cwd()) {
|
|
|
115
142
|
}
|
|
116
143
|
// GitLab CI fallback
|
|
117
144
|
const gitlabRef = process.env.CI_COMMIT_REF_NAME;
|
|
118
|
-
if (gitlabRef && gitlabRef.length > 0) {
|
|
145
|
+
if (gitlabRef && gitlabRef.length > 0 && !isSelaHealBranch(gitlabRef)) {
|
|
119
146
|
return { branch: gitlabRef, isPR: false, prNumber: null, source: "env" };
|
|
120
147
|
}
|
|
121
148
|
// Generic Jenkins / local fallback
|
|
122
149
|
const jenkinsBranch = process.env.GIT_BRANCH;
|
|
123
150
|
if (jenkinsBranch && jenkinsBranch.length > 0) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
isPR: false,
|
|
127
|
-
|
|
128
|
-
source: "env",
|
|
129
|
-
};
|
|
151
|
+
const cleaned = jenkinsBranch.replace(/^origin\//, "");
|
|
152
|
+
if (!isSelaHealBranch(cleaned)) {
|
|
153
|
+
return { branch: cleaned, isPR: false, prNumber: null, source: "env" };
|
|
154
|
+
}
|
|
130
155
|
}
|
|
131
156
|
// Local: git CLI
|
|
132
157
|
if (!gitAvailable(cwd)) {
|
|
@@ -135,11 +160,27 @@ function detectBranch(cwd = process.cwd()) {
|
|
|
135
160
|
const r = sh("git rev-parse --abbrev-ref HEAD", cwd);
|
|
136
161
|
if (!r.ok)
|
|
137
162
|
return { branch: null, isPR: false, prNumber: null, source: "none" };
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
163
|
+
const localBranch = r.stdout.trim();
|
|
164
|
+
if (!localBranch || localBranch === "HEAD") {
|
|
140
165
|
return { branch: null, isPR: false, prNumber: null, source: "none" };
|
|
141
166
|
}
|
|
142
|
-
|
|
167
|
+
// โโ Sela self-branch guard โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
168
|
+
// If we're sitting on a previously-created `sela/heal-*` branch (typical
|
|
169
|
+
// local-rerun scenario), do NOT use it as the PR base. Fall back to the
|
|
170
|
+
// repo's default integration branch (main / master / develop) so the new
|
|
171
|
+
// PR targets a real branch, not another sela patch branch.
|
|
172
|
+
if (isSelaHealBranch(localBranch)) {
|
|
173
|
+
const fallback = findDefaultBranch(cwd);
|
|
174
|
+
if (fallback) {
|
|
175
|
+
console.warn(`[Sela PR] โ ๏ธ HEAD is on '${localBranch}' (sela patch branch) โ ` +
|
|
176
|
+
`using '${fallback}' as PR base instead.`);
|
|
177
|
+
return { branch: fallback, isPR: false, prNumber: null, source: "git" };
|
|
178
|
+
}
|
|
179
|
+
console.warn(`[Sela PR] โ ๏ธ HEAD is on '${localBranch}' and no default branch found โ ` +
|
|
180
|
+
`skipping PR automation to avoid self-targeting.`);
|
|
181
|
+
return { branch: null, isPR: false, prNumber: null, source: "none" };
|
|
182
|
+
}
|
|
183
|
+
return { branch: localBranch, isPR: false, prNumber: null, source: "git" };
|
|
143
184
|
}
|
|
144
185
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
145
186
|
// STRATEGY DECISION
|
|
@@ -191,6 +232,9 @@ function decideEffectiveStrategy(cfg, branch, heals) {
|
|
|
191
232
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
192
233
|
// PR BODY RENDERING
|
|
193
234
|
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
235
|
+
function fmtPct(v) {
|
|
236
|
+
return typeof v === "number" && Number.isFinite(v) ? `${v}%` : "n/a";
|
|
237
|
+
}
|
|
194
238
|
function buildDefaultBody(heals, branchInfo, reportLink, decision) {
|
|
195
239
|
const lines = [];
|
|
196
240
|
lines.push(`# ๐ค Sela Insights โ Automation Suite Healed!`);
|
|
@@ -199,8 +243,8 @@ function buildDefaultBody(heals, branchInfo, reportLink, decision) {
|
|
|
199
243
|
lines.push("");
|
|
200
244
|
lines.push(`## ๐ Executive Summary`);
|
|
201
245
|
lines.push(`* **Heals:** ${heals.length}`);
|
|
202
|
-
lines.push(`* **Min AI Confidence:** ${decision.minAiConfidence}
|
|
203
|
-
lines.push(`* **Min Auditor Confidence:** ${decision.minAuditorConfidence}
|
|
246
|
+
lines.push(`* **Min AI Confidence:** ${fmtPct(decision.minAiConfidence)}`);
|
|
247
|
+
lines.push(`* **Min Auditor Confidence:** ${fmtPct(decision.minAuditorConfidence)}`);
|
|
204
248
|
lines.push(`* **Branch:** \`${branchInfo.branch ?? "unknown"}\``);
|
|
205
249
|
if (decision.downgradeReason) {
|
|
206
250
|
lines.push(`* **โ ๏ธ Strategy Downgrade:** \`${decision.configured}\` โ \`${decision.effective}\` (${decision.downgradeReason})`);
|
|
@@ -208,16 +252,17 @@ function buildDefaultBody(heals, branchInfo, reportLink, decision) {
|
|
|
208
252
|
lines.push("");
|
|
209
253
|
lines.push(`## ๐ป Per-Heal Diff`);
|
|
210
254
|
for (const h of heals) {
|
|
211
|
-
|
|
255
|
+
const lineRef = h.newLineNumber ?? h.sourceLine;
|
|
256
|
+
lines.push(`### \`${h.sourceFile}:${lineRef}\` โ ${h.testTitle ?? "(no test title)"}`);
|
|
212
257
|
lines.push("");
|
|
213
258
|
lines.push("```diff");
|
|
214
|
-
lines.push(`- ${h.oldCodeLine.trim()}`);
|
|
215
|
-
lines.push(`+ ${h.newCodeLine.trim()}`);
|
|
259
|
+
lines.push(`- ${(h.oldCodeLine ?? "").trim()}`);
|
|
260
|
+
lines.push(`+ ${(h.newCodeLine ?? "").trim()}`);
|
|
216
261
|
lines.push("```");
|
|
217
262
|
if (h.aiExplanation) {
|
|
218
263
|
lines.push(`> ๐ง ${h.aiExplanation}`);
|
|
219
264
|
}
|
|
220
|
-
lines.push(`> AI Confidence: **${h.aiConfidence}
|
|
265
|
+
lines.push(`> AI Confidence: **${fmtPct(h.aiConfidence)}** ยท Auditor: **${fmtPct(h.auditor?.confidence)}**`);
|
|
221
266
|
lines.push("");
|
|
222
267
|
}
|
|
223
268
|
lines.push(`## ๐ Visual Evidence`);
|