(payload, files)
| 400 | } |
| 401 | |
| 402 | async function classifyPr(payload, files) { |
| 403 | const pr = payload.pull_request; |
| 404 | const title = pr.title || ""; |
| 405 | const prType = parsePrType(title); |
| 406 | const filenames = files.map((item) => item.filename || ""); |
| 407 | const impactedPaths = files.flatMap((item) => { |
| 408 | const paths = [item.filename || ""]; |
| 409 | if (item.status === "renamed" && item.previous_filename) { |
| 410 | paths.push(item.previous_filename); |
| 411 | } |
| 412 | return paths.filter(Boolean); |
| 413 | }); |
| 414 | |
| 415 | // Filter out docs, tests, and other low-risk paths so the size label tracks business impact. |
| 416 | const effectiveChanges = files.reduce( |
| 417 | (sum, item) => sum + (isLowRiskPath(item.filename) ? 0 : (item.changes || 0)), |
| 418 | 0, |
| 419 | ); |
| 420 | const totalChanges = files.reduce((sum, item) => sum + (item.changes || 0), 0); |
| 421 | |
| 422 | const domains = new Set(); |
| 423 | const businessDomains = new Set(); |
| 424 | |
| 425 | for (const name of impactedPaths) { |
| 426 | const businessDomain = getBusinessDomain(name); |
| 427 | if (businessDomain) { |
| 428 | businessDomains.add(businessDomain); |
| 429 | domains.add(businessDomain); |
| 430 | continue; |
| 431 | } |
| 432 | |
| 433 | const shortcutDomain = shortcutDomainForPath(name); |
| 434 | if (shortcutDomain) domains.add(shortcutDomain); |
| 435 | |
| 436 | const skillDomain = skillDomainForPath(name); |
| 437 | if (skillDomain) domains.add(skillDomain); |
| 438 | } |
| 439 | |
| 440 | const coreAreas = collectCoreAreas(impactedPaths); |
| 441 | const newShortcutDomain = await detectNewShortcutDomain(files); |
| 442 | |
| 443 | const lowRiskOnly = impactedPaths.length > 0 && impactedPaths.every(isLowRiskPath); |
| 444 | const singleDomain = domains.size <= 1; |
| 445 | const multiDomain = domains.size >= 2; |
| 446 | const headDomains = [...domains].filter((domain) => HEAD_BUSINESS_DOMAINS.has(domain)); |
| 447 | const coreSignals = [...coreAreas].sort(); |
| 448 | const sensitiveKeywords = collectSensitiveKeywords(impactedPaths); |
| 449 | const sensitive = coreSignals.length > 0 || sensitiveKeywords.length > 0; |
| 450 | |
| 451 | const context = { |
| 452 | prType, effectiveChanges, lowRiskOnly, |
| 453 | domains, headDomains, coreAreas, coreSignals, |
| 454 | sensitiveKeywords, sensitive, newShortcutDomain, |
| 455 | singleDomain, multiDomain, filenames: impactedPaths |
| 456 | }; |
| 457 | |
| 458 | const { label, reasons } = evaluateRules(context); |
| 459 |
no test coverage detected