diff --git a/src/signals/registration-readiness.ts b/src/signals/registration-readiness.ts index 43f3687704..9605a6b0c9 100644 --- a/src/signals/registration-readiness.ts +++ b/src/signals/registration-readiness.ts @@ -177,6 +177,7 @@ export function buildRegistrationReadiness(input: RegistrationReadinessInput): R const blockers = [ ...(!isRegistered ? ["Repository is not registered in the latest LoopOver registry snapshot."] : []), ...(configFragile ? ["Repository config quality is fragile."] : []), + ...(configNeedsAttention ? ["Repository config quality needs attention before registration promotion."] : []), ...(intakeBlocked ? ["Contributor intake health is blocked."] : []), ]; @@ -200,7 +201,9 @@ export function buildRegistrationReadiness(input: RegistrationReadinessInput): R }; const warnings = [ - ...(configNeedsAttention ? ["Repository config quality needs attention before registration promotion."] : []), + // configNeedsAttention is a blocker (not a warning), mirroring how configFragile is treated — the two + // needs-attention tiers of the same configQuality.level stay consistent, and blockers fully explains + // ready === false without double-flagging the same fact as a warning (#5946). ...(contributorIntakeHealth.level === "strained" ? ["Contributor intake is strained; expect more maintainer triage."] : []), ...(settings.publicSurface === "off" ? ["GitHub App public surface is disabled; maintainers will not get comment/label assistance."] : []), ...testCoverageHealth.warnings, @@ -209,7 +212,9 @@ export function buildRegistrationReadiness(input: RegistrationReadinessInput): R ...upstreamRegistryDriftWarnings, ]; - const ready = blockers.length === 0 && !configFragile && !configNeedsAttention; + // configFragile and configNeedsAttention are now both blockers, so blockers.length === 0 alone fully + // determines readiness — blockers is the single source of truth for every ready === false reason (#5946). + const ready = blockers.length === 0; return { repoFullName, diff --git a/test/unit/policy-sanitizer.test.ts b/test/unit/policy-sanitizer.test.ts index d3f440bcc5..69537d8054 100644 --- a/test/unit/policy-sanitizer.test.ts +++ b/test/unit/policy-sanitizer.test.ts @@ -650,7 +650,9 @@ describe("readiness warnings sanitizer", () => { contributorIntakeHealth: { ...base.contributorIntakeHealth, level: "strained" }, }); - expect(report.warnings).toEqual(expect.arrayContaining(["Repository config quality needs attention before registration promotion.", "Contributor intake is strained; expect more maintainer triage."])); + // config-attention is a blocker (not a warning) since #5946; strained-intake stays a warning. + expect(report.blockers).toEqual(expect.arrayContaining(["Repository config quality needs attention before registration promotion."])); + expect(report.warnings).toEqual(expect.arrayContaining(["Contributor intake is strained; expect more maintainer triage."])); expect(report.warnings.join(" ")).not.toMatch(PRIVATE_TERMS_PATTERN); expect(report.blockers.join(" ")).not.toMatch(PRIVATE_TERMS_PATTERN); }); diff --git a/test/unit/registration-readiness.test.ts b/test/unit/registration-readiness.test.ts index bf3b65b408..d82da7076c 100644 --- a/test/unit/registration-readiness.test.ts +++ b/test/unit/registration-readiness.test.ts @@ -210,9 +210,30 @@ describe("buildRegistrationReadiness", () => { }); expect(report.ready).toBe(false); - expect(report.warnings).toEqual( - expect.arrayContaining(["Repository config quality needs attention before registration promotion.", "Contributor intake is strained; expect more maintainer triage."]), - ); + // "needs attention" is a blocker (mirroring "fragile"), not a warning; the strained-intake note stays a warning. + expect(report.blockers).toContain("Repository config quality needs attention before registration promotion."); + expect(report.warnings).toEqual(expect.arrayContaining(["Contributor intake is strained; expect more maintainer triage."])); + expect(report.warnings).not.toContain("Repository config quality needs attention before registration promotion."); + }); + + // #5946: with config "needs attention" as the ONLY gating condition (everything else healthy), the report was + // ready === false while blockers was []. blockers must now fully explain ready === false on its own. + it("lists config-needs-attention in blockers when it is the only gating condition (regression for #5946)", () => { + const repo = repoFor("octo/attention", configFor({ repo: "octo/attention" })); + const base = signalsFor(repo, [], [], [label("bug")]); + const report = buildRegistrationReadiness({ + repoFullName: repo.fullName, + repo, + settings: settingsFor(repo.fullName), + installation: healthyInstall, + ...base, + configQuality: { ...base.configQuality, level: "needs_attention" }, + }); + + expect(report.ready).toBe(false); + expect(report.blockers).toContain("Repository config quality needs attention before registration promotion."); + expect(report.blockers.length).toBeGreaterThan(0); + expect(report.warnings).not.toContain("Repository config quality needs attention before registration promotion."); }); it("keeps the report free of forbidden public language", () => {