Follow-up from the #2179 voice-fingerprint baseline hook (PR #2247).
Gap
computeVoiceDrift in server/lib/editorial/voiceFingerprint.js measures each issue's z-score against a baseline center and skips any metric whose drafted σ is ~0 (if (std < 1e-9) continue;) — correct for drafted mode (no spread = no per-issue outlier), and it does NOT affect the common exemplar-baseline case (issues that drifted the same direction still have natural per-metric variance, so σ>0 and they flag).
But there's a residual boundary in exemplars/blended mode: when EVERY drafted issue shares the same value on a metric (σ≈0) and that shared value sits far from the chosen-voice center, the per-issue z-score model emits nothing — there is no per-issue outlier, the whole corpus is uniformly off-register. Example: a dialogue-free series (dialogueRatio = 0 on every issue) with a dialogue-heavy chosen voice.
Proposed work
Emit a distinct series-level finding (not a per-issue outlier) when, under an active exemplar/blended baseline, a metric has σ≈0 AND |center − mean| exceeds a distance threshold — "the whole series sits at X on vs the chosen voice's Y." This is a different finding shape than the per-issue outliers (no issueNumber), so it needs its own branch in the style.voice-drift run() and a scope/anchor decision — deferred out of the baseline-hook slice to keep that PR focused.
The boundary is documented inline at the skip site in voiceFingerprint.js.
Related to #2179
Follow-up from the #2179 voice-fingerprint baseline hook (PR #2247).
Gap
computeVoiceDriftinserver/lib/editorial/voiceFingerprint.jsmeasures each issue's z-score against a baseline center and skips any metric whose drafted σ is ~0 (if (std < 1e-9) continue;) — correct fordraftedmode (no spread = no per-issue outlier), and it does NOT affect the common exemplar-baseline case (issues that drifted the same direction still have natural per-metric variance, so σ>0 and they flag).But there's a residual boundary in
exemplars/blendedmode: when EVERY drafted issue shares the same value on a metric (σ≈0) and that shared value sits far from the chosen-voice center, the per-issue z-score model emits nothing — there is no per-issue outlier, the whole corpus is uniformly off-register. Example: a dialogue-free series (dialogueRatio= 0 on every issue) with a dialogue-heavy chosen voice.Proposed work
Emit a distinct series-level finding (not a per-issue outlier) when, under an active exemplar/blended baseline, a metric has σ≈0 AND
|center − mean|exceeds a distance threshold — "the whole series sits at X on vs the chosen voice's Y." This is a different finding shape than the per-issue outliers (noissueNumber), so it needs its own branch in thestyle.voice-driftrun()and a scope/anchor decision — deferred out of the baseline-hook slice to keep that PR focused.The boundary is documented inline at the skip site in
voiceFingerprint.js.Related to #2179