({ capsule, event, mutation, gene, blastRadius })
| 293 | } |
| 294 | |
| 295 | async function maybeCreatePR({ capsule, event, mutation, gene, blastRadius }) { |
| 296 | if (String(process.env.EVOLVER_SELF_PR || '').toLowerCase() !== 'true') return null; |
| 297 | |
| 298 | // User has explicitly opted into self-PR. Ensure we have the obfuscate |
| 299 | // list so we don't accidentally leak obfuscated source via a "non-obf" PR. |
| 300 | if (loadObfuscatedFromManifest() === null) { |
| 301 | if (!_warnedAboutMissingManifest) { |
| 302 | console.warn('[SelfPR] public.manifest.json not found at ' + getEvolverInstallRoot() + ' — rejecting all self-PRs (manifest is required to identify obfuscated files). Error: ' + _manifestLoadError); |
| 303 | _warnedAboutMissingManifest = true; |
| 304 | } |
| 305 | return null; |
| 306 | } |
| 307 | |
| 308 | const score = capsule && capsule.outcome ? (capsule.outcome.score || 0) : 0; |
| 309 | const streak = capsule ? (capsule.success_streak || 0) : 0; |
| 310 | |
| 311 | if (score < SELF_PR_MIN_SCORE) return null; |
| 312 | if (streak < SELF_PR_MIN_STREAK) return null; |
| 313 | |
| 314 | if (!mutation || mutation.category !== 'optimize') return null; |
| 315 | if (!mutation || mutation.risk !== 'low') return null; |
| 316 | |
| 317 | const filesChanged = blastRadius ? (blastRadius.files || 0) : 0; |
| 318 | const linesChanged = blastRadius ? (blastRadius.lines || 0) : 0; |
| 319 | if (filesChanged > SELF_PR_MAX_FILES || filesChanged === 0) return null; |
| 320 | if (linesChanged > SELF_PR_MAX_LINES || linesChanged === 0) return null; |
| 321 | |
| 322 | const changedFiles = (blastRadius && Array.isArray(blastRadius.all_changed_files) |
| 323 | ? blastRadius.all_changed_files |
| 324 | : []).map(normalizeRel).filter(Boolean); |
| 325 | |
| 326 | if (changedFiles.length === 0) return null; |
| 327 | if (!changedFiles.every(isPublicNonObfuscated)) return null; |
| 328 | |
| 329 | if (isInCooldown()) { |
| 330 | console.log('[SelfPR] Skipping: cooldown active.'); |
| 331 | return { attempted: false, reason: 'cooldown' }; |
| 332 | } |
| 333 | |
| 334 | const repoRoot = getRepoRoot(); |
| 335 | const diffHash = computeDiffHash(changedFiles, repoRoot); |
| 336 | if (isDuplicateDiff(diffHash)) { |
| 337 | console.log('[SelfPR] Skipping: duplicate diff ' + diffHash); |
| 338 | return { attempted: false, reason: 'duplicate_diff' }; |
| 339 | } |
| 340 | |
| 341 | const diffContent = getGitDiff(changedFiles, repoRoot); |
| 342 | if (!diffContent) { |
| 343 | console.log('[SelfPR] Skipping: no diff content.'); |
| 344 | return { attempted: false, reason: 'no_diff' }; |
| 345 | } |
| 346 | const leakResult = fullLeakCheck(diffContent); |
| 347 | if (leakResult.found) { |
| 348 | const leakSummary = leakResult.leaks.map(function (l) { return l.type; }).join(', '); |
| 349 | console.warn('[SelfPR] Skipping: leak detected in diff (' + leakSummary + ')'); |
| 350 | return { attempted: false, reason: 'leak_detected', leaks: leakResult.leaks.length }; |
| 351 | } |
| 352 |
nothing calls this directly
no test coverage detected