Context
buildRegistrationReadiness in src/signals/registration-readiness.ts computes a RegistrationReadinessReport with separate blockers, warnings, and ready fields for a repo owner evaluating registration:
const configNeedsAttention = configQuality.level === "needs_attention"; // line 151
...
const blockers = [ // lines 177-181
...(!isRegistered ? ["Repository is not registered in the latest LoopOver registry snapshot."] : []),
...(configFragile ? ["Repository config quality is fragile."] : []),
...(intakeBlocked ? ["Contributor intake health is blocked."] : []),
];
...
const warnings = [ // lines 202-209
...(configNeedsAttention ? ["Repository config quality needs attention before registration promotion."] : []),
...
];
const ready = blockers.length === 0 && !configFragile && !configNeedsAttention; // line 212
ready factors in configNeedsAttention (a third condition beyond blockers.length === 0), but blockers itself never includes a configNeedsAttention entry — only warnings does. So when configQuality.level === "needs_attention" and every other input (isRegistered, configFragile, intakeBlocked) is healthy, RegistrationReadinessReport.ready is false while RegistrationReadinessReport.blockers is [] — an empty list, which contradicts the field's implied contract as "the reasons registration isn't ready" (a caller can't render "why isn't this ready?" from blockers alone in this scenario; they'd have to separately cross-reference warnings and re-derive that one of them is actually gating ready).
test/unit/registration-readiness.test.ts (~lines 199-213, "warns about config attention and strained intake") already builds exactly this scenario and asserts report.ready === false and report.warnings contents, but never asserts anything about report.blockers — so this empty-blockers-while-not-ready gap is untested and unnoticed.
Requirements
- Add a
configNeedsAttention-derived entry to the blockers array (not just warnings), so blockers.length === 0 is equivalent to ready === true (modulo the same conditions ready already checks) — i.e. ready should be derivable as blockers.length === 0 without a separate !configNeedsAttention/!configFragile re-check, OR (alternative acceptable approach) keep configNeedsAttention as warning-only but change ready's definition to only depend on blockers.length === 0 — pick whichever resolves the inconsistency; the maintainer-facing contract is that blockers must fully explain any ready === false, whichever field is the source of truth.
- Given
configFragile is already both a blocker (line 179) and implicitly part of ready's calculation, prefer the first approach (add configNeedsAttention to blockers, matching how configFragile is already handled) for internal consistency between the two "needs attention" tiers of the same configQuality.level field, rather than removing configFragile's existing blocker treatment.
- If
configNeedsAttention becomes a blocker, decide whether to keep or remove its existing warnings entry (avoid the same fact being flagged as both, unless the codebase has a clear existing precedent for a condition appearing in both — check configFragile isn't also duplicated in warnings today before deciding, and follow that same precedent for consistency).
- Do not change
directPrReadiness, issueDiscoveryReadiness, or any other field's logic — this fix is scoped strictly to blockers/ready consistency for the configNeedsAttention condition.
Deliverables
Test Coverage Requirements
Aim for 99%+ Codecov patch coverage (100% including the new branch/invariant) on the touched lines in src/signals/registration-readiness.ts. This is a fix for a real contract inconsistency between two fields of the same report, so the blockers-assertion regression test is required, not just incidental line coverage.
Expected Outcome
RegistrationReadinessReport.blockers fully explains every case where ready === false, so a caller rendering "why can't I register" can rely on blockers alone instead of needing to separately reconcile warnings against ready.
Links & Resources
src/signals/registration-readiness.ts (buildRegistrationReadiness, configNeedsAttention at line 151, blockers at lines 177-181, warnings at lines 202-209, ready at line 212)
test/unit/registration-readiness.test.ts (existing "warns about config attention and strained intake" test, ~lines 199-213 — the test to extend)
Context
buildRegistrationReadinessinsrc/signals/registration-readiness.tscomputes aRegistrationReadinessReportwith separateblockers,warnings, andreadyfields for a repo owner evaluating registration:readyfactors inconfigNeedsAttention(a third condition beyondblockers.length === 0), butblockersitself never includes aconfigNeedsAttentionentry — onlywarningsdoes. So whenconfigQuality.level === "needs_attention"and every other input (isRegistered,configFragile,intakeBlocked) is healthy,RegistrationReadinessReport.readyisfalsewhileRegistrationReadinessReport.blockersis[]— an empty list, which contradicts the field's implied contract as "the reasons registration isn't ready" (a caller can't render "why isn't this ready?" fromblockersalone in this scenario; they'd have to separately cross-referencewarningsand re-derive that one of them is actually gatingready).test/unit/registration-readiness.test.ts(~lines 199-213, "warns about config attention and strained intake") already builds exactly this scenario and assertsreport.ready === falseandreport.warningscontents, but never asserts anything aboutreport.blockers— so this empty-blockers-while-not-ready gap is untested and unnoticed.Requirements
configNeedsAttention-derived entry to theblockersarray (not justwarnings), soblockers.length === 0is equivalent toready === true(modulo the same conditionsreadyalready checks) — i.e.readyshould be derivable asblockers.length === 0without a separate!configNeedsAttention/!configFragilere-check, OR (alternative acceptable approach) keepconfigNeedsAttentionas warning-only but changeready's definition to only depend onblockers.length === 0— pick whichever resolves the inconsistency; the maintainer-facing contract is thatblockersmust fully explain anyready === false, whichever field is the source of truth.configFragileis already both a blocker (line 179) and implicitly part ofready's calculation, prefer the first approach (addconfigNeedsAttentiontoblockers, matching howconfigFragileis already handled) for internal consistency between the two "needs attention" tiers of the sameconfigQuality.levelfield, rather than removingconfigFragile's existing blocker treatment.configNeedsAttentionbecomes a blocker, decide whether to keep or remove its existingwarningsentry (avoid the same fact being flagged as both, unless the codebase has a clear existing precedent for a condition appearing in both — checkconfigFragileisn't also duplicated inwarningstoday before deciding, and follow that same precedent for consistency).directPrReadiness,issueDiscoveryReadiness, or any other field's logic — this fix is scoped strictly toblockers/readyconsistency for theconfigNeedsAttentioncondition.Deliverables
blockers.length === 0andready === trueare consistent — a repo withconfigQuality.level === "needs_attention"and no other blocking condition either has a non-emptyblockersarray explaining why, orreadyno longer depends onconfigNeedsAttentionindependently ofblockers.test/unit/registration-readiness.test.tsexplicitly assertingreport.blockerscontents (not justreport.ready/report.warnings) for the "config needs attention, everything else healthy" scenario, closing the gap in the existing test at ~line 199-213.configFragile/intakeBlocked/!isRegisteredblocker scenarios remain passing unmodified.Test Coverage Requirements
Aim for 99%+ Codecov patch coverage (100% including the new branch/invariant) on the touched lines in
src/signals/registration-readiness.ts. This is a fix for a real contract inconsistency between two fields of the same report, so theblockers-assertion regression test is required, not just incidental line coverage.Expected Outcome
RegistrationReadinessReport.blockersfully explains every case whereready === false, so a caller rendering "why can't I register" can rely onblockersalone instead of needing to separately reconcilewarningsagainstready.Links & Resources
src/signals/registration-readiness.ts(buildRegistrationReadiness,configNeedsAttentionat line 151,blockersat lines 177-181,warningsat lines 202-209,readyat line 212)test/unit/registration-readiness.test.ts(existing "warns about config attention and strained intake" test, ~lines 199-213 — the test to extend)