Skip to content

feat: series-level voice-drift finding for uniformly off-register corpus (#2248)#2250

Open
atomantic wants to merge 1 commit into
mainfrom
claim/issue-2248
Open

feat: series-level voice-drift finding for uniformly off-register corpus (#2248)#2250
atomantic wants to merge 1 commit into
mainfrom
claim/issue-2248

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Closes #2248

Summary

Follow-up to the #2179 voice-fingerprint baseline hook (PR #2247). Closes the residual boundary in the style.voice-drift check'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.

computeVoiceDrift now 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.jscomputeVoiceDrift returns a new seriesFindings[] (sorted by distance desc), populated only under an active exemplar/blended baseline at the σ≈0 skip site (drafted mode never trips it — center === mean). New describeSeriesDrift() 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.jsstyle.voice-drift run() maps seriesFindings into findings with issueNumber: null (a corpus-wide observation, not an outlier), a Series-wide location, escalated one severity rank above the per-issue floor. They share the maxFindings cap, listed first so a series-wide finding is never starved by a flood of per-issue ones.
  • server/services/pipeline/voiceFingerprint.js — surfaces seriesFindings on 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).
  • Docs: server/lib/editorial/README.md row + .changelog/NEXT.md entry under Creative Writing Quality Engine.

Test plan

  • server/lib/editorial/voiceFingerprint.test.js — new computeVoiceDrift 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; describeSeriesDrift text) + a registry-wiring test asserting issueNumber: null / Series-wide / escalated severity.
  • client/src/pages/PipelineVoiceFingerprint.test.jsx — banner renders for seriesFindings, absent when empty.
  • server editorial suite (912 passed) + client fingerprint page suite (8 passed) green.
  • Pre-existing DB-backed pipeline suites (series.test.js, issues.test.js, etc.) fail locally on both this branch and clean main because the portos_test DB isn't provisioned in this environment — unrelated to this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CWQE: voice-drift exemplar baseline — flag a metric where the WHOLE corpus is uniformly off the chosen voice (series-level finding)

1 participant