Context
deriveContributionLanes in src/signals/focus-manifest.ts builds contributor-facing PR guidance from a maintainer's focus manifest. It filters manifest.wantedPaths for public-safety first (line 826):
const safeWanted = manifest.wantedPaths.filter(isFocusManifestPublicSafe);
...but then, when deciding what text to actually push into prEntryGuidance, it gates on the filtered list but interpolates the raw, unfiltered list (lines 859-860):
if (safeWanted.length > 0) {
prEntryGuidance.push(`Focus changes on maintainer-wanted areas: ${manifest.wantedPaths.slice(0, 5).join(", ")}.`);
}
prEntryGuidance (along with other accumulated strings) is later collapsed through an all-or-nothing safety filter: const safeprEntryGuidance = [...new Set(prEntryGuidance)].filter(isFocusManifestPublicSafe);. Because the interpolated string embeds manifest.wantedPaths (raw) rather than safeWanted (filtered), a single public-unsafe entry anywhere in wantedPaths makes the entire joined sentence fail isFocusManifestPublicSafe, and the whole guidance line — including the legitimate safe paths that were supposed to be surfaced — is silently dropped.
This is a known anti-pattern already fixed once in this codebase's own history: the sibling function buildPolicyEntryGuidance in packages/loopover-engine/src/focus-manifest.ts documents it explicitly:
"Joining the raw wantedPaths means a single reserved-word path... fails the all-or-nothing public-safety filter at the end and silently drops the entire focus-areas guidance line instead of surfacing the safe paths."
and correctly uses safeWantedPaths.slice(0, 5) instead of the raw list. deriveContributionLanes in src/signals/focus-manifest.ts never received the equivalent fix.
test/unit/policy-sanitizer.test.ts (which exercises deriveContributionLanes) has an extensive property test asserting no forbidden term ever leaks through, but no case exercises a mixed wantedPaths list (one safe entry + one unsafe entry) checking that the safe guidance still surfaces — so this is a real, provable regression (guidance is silently over-dropped) that the existing negative-only property test can't catch.
Requirements
- In
deriveContributionLanes, change the prEntryGuidance.push(...) call at line 860 to interpolate safeWanted instead of the raw manifest.wantedPaths, mirroring packages/loopover-engine/src/focus-manifest.ts's buildPolicyEntryGuidance fix.
- Do not change the
safeWanted.length > 0 gating condition itself — only the interpolated content.
- Do not change any other branch in
deriveContributionLanes (the preferred-labels guidance, safePublicNotes handling, directPrLane/issueDiscoveryLane computation) unless it exhibits the identical raw-vs-filtered mismatch (verify each other interpolation site in this function against its filtered counterpart while you're in there, but keep the fix narrowly scoped to genuine instances of this bug, not a speculative rewrite).
Deliverables
Test Coverage Requirements
Aim for 99%+ Codecov patch coverage (100% including the new branch/invariant) on the touched lines in src/signals/focus-manifest.ts. This is a fix for silently-dropped legitimate contributor guidance, so the mixed-safe/unsafe regression test is required, not just incidental line coverage.
Expected Outcome
A maintainer focus manifest with a mix of public-safe and public-unsafe wantedPaths entries still surfaces PR guidance built from the safe entries, instead of the entire "Focus changes on maintainer-wanted areas" guidance line being silently dropped because one unrelated unsafe path happened to be present in the raw list.
Links & Resources
src/signals/focus-manifest.ts (deriveContributionLanes, ~lines 815-880; safeWanted at line 826, the raw-interpolation bug at line 860)
packages/loopover-engine/src/focus-manifest.ts (buildPolicyEntryGuidance — the already-fixed sibling pattern to mirror, including its explanatory comment)
test/unit/policy-sanitizer.test.ts (existing property-test suite covering deriveContributionLanes)
Context
deriveContributionLanesinsrc/signals/focus-manifest.tsbuilds contributor-facing PR guidance from a maintainer's focus manifest. It filtersmanifest.wantedPathsfor public-safety first (line 826):...but then, when deciding what text to actually push into
prEntryGuidance, it gates on the filtered list but interpolates the raw, unfiltered list (lines 859-860):prEntryGuidance(along with other accumulated strings) is later collapsed through an all-or-nothing safety filter:const safeprEntryGuidance = [...new Set(prEntryGuidance)].filter(isFocusManifestPublicSafe);. Because the interpolated string embedsmanifest.wantedPaths(raw) rather thansafeWanted(filtered), a single public-unsafe entry anywhere inwantedPathsmakes the entire joined sentence failisFocusManifestPublicSafe, and the whole guidance line — including the legitimate safe paths that were supposed to be surfaced — is silently dropped.This is a known anti-pattern already fixed once in this codebase's own history: the sibling function
buildPolicyEntryGuidanceinpackages/loopover-engine/src/focus-manifest.tsdocuments it explicitly:and correctly uses
safeWantedPaths.slice(0, 5)instead of the raw list.deriveContributionLanesinsrc/signals/focus-manifest.tsnever received the equivalent fix.test/unit/policy-sanitizer.test.ts(which exercisesderiveContributionLanes) has an extensive property test asserting no forbidden term ever leaks through, but no case exercises a mixedwantedPathslist (one safe entry + one unsafe entry) checking that the safe guidance still surfaces — so this is a real, provable regression (guidance is silently over-dropped) that the existing negative-only property test can't catch.Requirements
deriveContributionLanes, change theprEntryGuidance.push(...)call at line 860 to interpolatesafeWantedinstead of the rawmanifest.wantedPaths, mirroringpackages/loopover-engine/src/focus-manifest.ts'sbuildPolicyEntryGuidancefix.safeWanted.length > 0gating condition itself — only the interpolated content.deriveContributionLanes(the preferred-labels guidance,safePublicNoteshandling,directPrLane/issueDiscoveryLanecomputation) unless it exhibits the identical raw-vs-filtered mismatch (verify each other interpolation site in this function against its filtered counterpart while you're in there, but keep the fix narrowly scoped to genuine instances of this bug, not a speculative rewrite).Deliverables
deriveContributionLanes'sprEntryGuidance"Focus changes on maintainer-wanted areas" line interpolates the public-safety-filteredsafeWantedlist, not the rawmanifest.wantedPaths.test/unit/policy-sanitizer.test.ts(ortest/unit/focus-manifest.test.ts, whichever already coversderiveContributionLanesmost directly) with awantedPathslist containing one public-safe entry and one public-unsafe entry, asserting the resultingprEntryGuidance/guidanceTextstill surfaces guidance built from the safe entry instead of dropping the whole line.Test Coverage Requirements
Aim for 99%+ Codecov patch coverage (100% including the new branch/invariant) on the touched lines in
src/signals/focus-manifest.ts. This is a fix for silently-dropped legitimate contributor guidance, so the mixed-safe/unsafe regression test is required, not just incidental line coverage.Expected Outcome
A maintainer focus manifest with a mix of public-safe and public-unsafe
wantedPathsentries still surfaces PR guidance built from the safe entries, instead of the entire "Focus changes on maintainer-wanted areas" guidance line being silently dropped because one unrelated unsafe path happened to be present in the raw list.Links & Resources
src/signals/focus-manifest.ts(deriveContributionLanes, ~lines 815-880;safeWantedat line 826, the raw-interpolation bug at line 860)packages/loopover-engine/src/focus-manifest.ts(buildPolicyEntryGuidance— the already-fixed sibling pattern to mirror, including its explanatory comment)test/unit/policy-sanitizer.test.ts(existing property-test suite coveringderiveContributionLanes)