feat: series-level voice-drift finding for uniformly off-register corpus (#2248)#2250
Open
atomantic wants to merge 1 commit into
Open
feat: series-level voice-drift finding for uniformly off-register corpus (#2248)#2250atomantic wants to merge 1 commit into
atomantic wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2248
Summary
Follow-up to the #2179 voice-fingerprint baseline hook (PR #2247). Closes the residual boundary in the
style.voice-driftcheck's exemplar/blended baseline mode: when every drafted issue shares the same value on a metric (drafted σ≈0) but that shared value sits far from the chosen-voice center, the per-issue z-score model emits nothing — there's no per-issue outlier, the whole corpus is uniformly off-register. The canonical case is a dialogue-free series (dialogueRatio= 0 on every issue) measured against a dialogue-heavy chosen voice.computeVoiceDriftnow emits a distinct series-level finding for exactly this case: a metric with σ≈0 whose|center − mean|exceeds a scale-free relative distance threshold (|center − mean| / max(|center|, |mean|)≥ 0.5 by default). Scale-free because the metric vector mixes wildly different scales (dialogueRatio 0–100, sentence-length CV ~0.4, em-dash rate per-1k) — a single absolute gap threshold would be meaningless.What shipped
server/lib/editorial/voiceFingerprint.js—computeVoiceDriftreturns a newseriesFindings[](sorted by distance desc), populated only under an active exemplar/blended baseline at the σ≈0 skip site (drafted mode never trips it — center === mean). NewdescribeSeriesDrift()renders the finding sentence ("The whole series sits at 0% on dialogue ratio vs the style guide's chosen voice of 42.5% — the corpus is uniformly below the chosen voice here").server/lib/editorial/checks/proseStyle.js—style.voice-driftrun()mapsseriesFindingsinto findings withissueNumber: null(a corpus-wide observation, not an outlier), aSeries-widelocation, escalated one severity rank above the per-issue floor. They share themaxFindingscap, listed first so a series-wide finding is never starved by a flood of per-issue ones.server/services/pipeline/voiceFingerprint.js— surfacesseriesFindingson the matrix-view payload.client/src/pages/PipelineVoiceFingerprint.jsx— renders a banner above the matrix for series-wide mismatches (they have no single cell to highlight since the column is uniform).server/lib/editorial/README.mdrow +.changelog/NEXT.mdentry under Creative Writing Quality Engine.Test plan
server/lib/editorial/voiceFingerprint.test.js— newcomputeVoiceDrift series-level findings (#2248)block (drafted-mode never emits; flags the uniformly-off-register metric; distance-desc sort; custom threshold; σ>0 metrics stay in the per-issue path; gatedOff → empty;describeSeriesDrifttext) + a registry-wiring test assertingissueNumber: null/Series-wide/ escalated severity.client/src/pages/PipelineVoiceFingerprint.test.jsx— banner renders forseriesFindings, absent when empty.servereditorial suite (912 passed) +clientfingerprint page suite (8 passed) green.series.test.js,issues.test.js, etc.) fail locally on both this branch and cleanmainbecause theportos_testDB isn't provisioned in this environment — unrelated to this change.