Context
src/signals/registration-readiness.ts:95-99 and src/signals/repo-policy-readiness.ts:234-238 each independently define their own private resolveIssuePolicy(lane, settings) function with byte-identical logic:
function resolveIssuePolicy(lane: LaneAdvice, settings: RepositorySettings): IssuePolicy /* or: string */ {
if (lane.lane === "issue_discovery") return "issue_discovery_enabled";
if (lane.lane === "split") return "split_pr_and_issue_discovery_enabled";
return settings.requireLinkedIssue ? "direct_pr_requires_linked_issue" : "direct_pr_no_issue_required";
}
The only difference between the two copies is the declared return type: registration-readiness.ts uses its own IssuePolicy union type (src/signals/registration-readiness.ts:10), while repo-policy-readiness.ts types its copy as a bare string. registration-readiness.ts already imports buildRepoPolicyReadiness from ./repo-policy-readiness (line 7), so the dependency direction between the two files is already established: registration-readiness.ts depends on repo-policy-readiness.ts, not the reverse.
This codebase has an existing, explicit precedent for exactly this situation. src/signals/repo-policy-compiler.ts exports labelPolicyNote (lines 118-122) specifically so src/signals/onboarding-pack.ts can reuse the identical logic instead of re-declaring it, with the doc comment (lines 115-117): "Shared by focusManifestPolicyToCompilerOutput (onboarding-pack.ts) so both adapters compile the same manifest to the same labelPolicy.note (#5943)." resolveIssuePolicy is the same class of two-independently-hand-maintained-copies drift risk that precedent was created to prevent — it just hasn't been applied here yet.
Requirements
- Move the
IssuePolicy type (currently src/signals/registration-readiness.ts:10) and the resolveIssuePolicy function definition (currently src/signals/registration-readiness.ts:95-99) into src/signals/repo-policy-readiness.ts — the module registration-readiness.ts already depends on — and export both. Keep the IssuePolicy union return type (not the bare string signature currently used in repo-policy-readiness.ts).
- Update
repo-policy-readiness.ts's own internal call at line 73 (const issuePolicy = resolveIssuePolicy(input.lane, input.settings);) to use the newly-shared, exported version instead of its private local copy.
- Update
src/signals/registration-readiness.ts to import IssuePolicy and resolveIssuePolicy from ./repo-policy-readiness instead of declaring its own local copies, removing the local function and re-pointing every existing usage (the IssuePolicy type reference at line 50, and the call site at line 224) to the imported versions.
- Do not introduce a circular import:
repo-policy-readiness.ts must not import anything from registration-readiness.ts.
- This is a pure refactor: the output of both
buildRegistrationReadiness and buildRepoPolicyReadiness must be byte-identical to before this change.
Deliverables
Test Coverage Requirements
Touches src/signals/registration-readiness.ts and src/signals/repo-policy-readiness.ts, both under src/** — this repo's Codecov patch gate (99%+ on changed lines) applies. No new test file should be required since both call paths are already covered by test/unit/registration-readiness.test.ts and the existing repo-policy-readiness test suite — this is a pure internal move, and existing assertions on issuePolicy/ownerContext.issuePolicy values must continue to pass unchanged.
Expected Outcome
Exactly one implementation of the direct-PR/issue-discovery issue-policy classification exists under src/signals/, imported wherever it's needed, instead of two independently hand-maintained copies that can silently drift apart.
Links & Resources
Context
src/signals/registration-readiness.ts:95-99andsrc/signals/repo-policy-readiness.ts:234-238each independently define their own privateresolveIssuePolicy(lane, settings)function with byte-identical logic:The only difference between the two copies is the declared return type:
registration-readiness.tsuses its ownIssuePolicyunion type (src/signals/registration-readiness.ts:10), whilerepo-policy-readiness.tstypes its copy as a barestring.registration-readiness.tsalready importsbuildRepoPolicyReadinessfrom./repo-policy-readiness(line 7), so the dependency direction between the two files is already established:registration-readiness.tsdepends onrepo-policy-readiness.ts, not the reverse.This codebase has an existing, explicit precedent for exactly this situation.
src/signals/repo-policy-compiler.tsexportslabelPolicyNote(lines 118-122) specifically sosrc/signals/onboarding-pack.tscan reuse the identical logic instead of re-declaring it, with the doc comment (lines 115-117): "Shared byfocusManifestPolicyToCompilerOutput(onboarding-pack.ts) so both adapters compile the same manifest to the samelabelPolicy.note(#5943)."resolveIssuePolicyis the same class of two-independently-hand-maintained-copies drift risk that precedent was created to prevent — it just hasn't been applied here yet.Requirements
IssuePolicytype (currentlysrc/signals/registration-readiness.ts:10) and theresolveIssuePolicyfunction definition (currentlysrc/signals/registration-readiness.ts:95-99) intosrc/signals/repo-policy-readiness.ts— the moduleregistration-readiness.tsalready depends on — and export both. Keep theIssuePolicyunion return type (not the barestringsignature currently used inrepo-policy-readiness.ts).repo-policy-readiness.ts's own internal call at line 73 (const issuePolicy = resolveIssuePolicy(input.lane, input.settings);) to use the newly-shared, exported version instead of its private local copy.src/signals/registration-readiness.tsto importIssuePolicyandresolveIssuePolicyfrom./repo-policy-readinessinstead of declaring its own local copies, removing the local function and re-pointing every existing usage (theIssuePolicytype reference at line 50, and the call site at line 224) to the imported versions.repo-policy-readiness.tsmust not import anything fromregistration-readiness.ts.buildRegistrationReadinessandbuildRepoPolicyReadinessmust be byte-identical to before this change.Deliverables
resolveIssuePolicy/IssuePolicyhas exactly one implementation, exported fromsrc/signals/repo-policy-readiness.tssrc/signals/registration-readiness.tsimports it instead of re-declaring itbuildRegistrationReadinessandbuildRepoPolicyReadinesspass unmodified (output is unchanged)Test Coverage Requirements
Touches
src/signals/registration-readiness.tsandsrc/signals/repo-policy-readiness.ts, both undersrc/**— this repo's Codecov patch gate (99%+ on changed lines) applies. No new test file should be required since both call paths are already covered bytest/unit/registration-readiness.test.tsand the existing repo-policy-readiness test suite — this is a pure internal move, and existing assertions onissuePolicy/ownerContext.issuePolicyvalues must continue to pass unchanged.Expected Outcome
Exactly one implementation of the direct-PR/issue-discovery issue-policy classification exists under
src/signals/, imported wherever it's needed, instead of two independently hand-maintained copies that can silently drift apart.Links & Resources
src/signals/registration-readiness.ts:9-10,50,95-99,224src/signals/repo-policy-readiness.ts:70-73,234-238src/signals/repo-policy-compiler.ts:115-122(labelPolicyNote, fix(signals): focusManifestPolicyToCompilerOutput drops labelPolicy.note, unlike its sibling adapter #5943)