Atticus · Voice Lab · Implementation plan

Voice Lab — the room

Replanned 2026-09-27 around the revised spec. Source: .claude/plans/voice-lab-room.md

Status: Phases A–C complete (2026-08-27). Replanned 2026-09-27 around the revised spec; Phase G (prerequisite fixes) is next. Awaiting approval. Written 2026-08-27 · replanned 2026-09-27. Canonical design: Docs/mockups/voice-lab-spec.md (revised 2026-09-27) and the nine-screen prototype Docs/mockups/Voice-Lab.html. Review of the spec and every revision: Docs/reviews/2026-09-27-voice-lab-spec-review.html. Supersedes: this plan's own Phases D–F (a single LLM prose analysis over uploaded samples), and the 2026-04-10 mockup at .claude/handoffs/atticus/mockups/voice-lab-60F26437/mockup.html.

Section numbers such as §6.4 refer to the spec.

What changed on 2026-09-27

The spec replaces the design Phases D–F were built for. Five LLM-authored prose dimensions with a preserve / neutral / challenge stance give way to three things:

Most measurement is deterministic. The model writes margin notes, judges whether a hedge is justified, and drafts Say it first suggestions. It never places a mark.

Decision: retire the five-dimension analysis (approve or reverse)

The spec left this to the plan. Recommended: retire.

Scope decisions

DecisionResolution
Which surfaces use the portraitComposition (threads, drift), Critique (thread, Leave, and won't anchors), generation (actual-voice directive plus won'ts), and Discovery (one question per piece). The 2026-08-27 exclusion of Discovery is reversed by spec §7.1.
Deterministic register gates (RegisterBaseline → VoiceCeiling)Still untouched. The portrait feeds prompts and its own marks, never the ceiling's score. The reasoning in "Why the gates stay untouched" below still holds.
Register selectorMode only. Project.stakes is never set (spec §7.1).
Step-back eventSend to Critique, Save, or Start new version (spec §7.2).
Backup folderOut of this plan. It moves to its own spec (spec §8.4).
Model-judgment axes (Cool/Warm, Direct/Oblique, Earnest/Wry)Fed by the pairs only. No LLM classifier in this plan. They stay pencil and can't host a hunch.
Spec Open (55): Direct/Oblique core vs. registerStays core until validation data says otherwise. It is model-judgment and pencil-only, so it can't drive a thread either way.
Spec Open (65): two marks per draftTwo, plus Show them anyway. Revisit in Phase N.
Thread placement in CompositionPinned section above the margin tab strip, not a fifth tab. The margin is at four tabs after a 2026-05 complaint about six.
Register filter / diff banner / "include my drafts" (2026-08-27 deferrals)Moot. Mode tabs replace the register filter. "Learned from Thursday's draft" replaces the diff banner. Drafts feed the portrait by design, with generated text excluded.

Why the gates stay untouched

RegisterBaseline → VoiceCeiling ships today and actively regenerates drafts that score above the writer's register. Two reasons not to feed Voice Lab material into it:

  1. The scorer's own calibration finding is that chat and prose do not compare — which is why RegisterScorer.Weights.sentenceComplexity is zero. A polished uploaded chapter sits far above a Discovery chat answer on lexical rarity too.
  2. Mixing samples in would silently raise the baseline and change which drafts get regenerated, with no visible cause. A regression in shipped synthesis behavior that only manifests as "the drafts feel different."
Phases A–C and their implementation notes (complete, 2026-08-27)

Phase A: Sample model + true multi-sample store (no LLM, no UI)

The current model holds one sample and actively deletes the others.

Deliverable: a sample library that can hold many samples without losing any.

Phase B: The room (renders what exists; no analysis yet)

Deliverable: the Voice Lab room, managing a real sample library.

Phase C: Analysis contract + quote verification (pure, no LLM)

Built and fully tested before anything calls a model — the sequencing that synthesis-quality-gates.md used for RegisterScorer.

Rationale: this is the writer's own prose. A quote that is subtly paraphrased — the failure mode LLMs are most prone to — is immediately visible to the one person who wrote it, and it destroys trust in the whole profile. Reuse or mirror the existing substring-anchoring logic in EditorHighlightResolver / the critique quotedSubstring degradation pattern rather than writing a third one.

Deliverable: a verified analysis contract that cannot display prose the writer did not write.

Implementation notes — Phases A–C (2026-08-27)

Where the build departed from the plan above, and why.

Suite: 730 tests, 0 failures (was 671 before this work).

Phase G: Prerequisite fixes in shipped code (no Voice Lab UI)

These are bugs against the spec's generation policy (§7.4), and they exist today. They land first because every later phase measures drafts, and a draft measured before this phase includes Atticus's own sentences.

Deliverable: Atticus can tell its sentences from the writer's, and students get no rewrites.

Phase H: Axes, evidence, and the portrait engine (pure, no UI, no LLM)

Deliverable: give it evidence and it returns a correct portrait and hunch, with no screen yet.

Phase I: Retire the five-dimension analysis (if approved)

Deliverable: one model of voice in the codebase.

Phase J: The room and the sitting (UI)

Deliverable: a writer can sit, bring pages, or bring an admired paragraph, and all three feed the portrait.

Phase K: The portrait and the hunch (UI)

Deliverable: the sitting ends honestly, on a hunch or on "nothing yet."

Phase L: Listening, the mirror, and the fork

Deliverable: a hunch is confirmed or withdrawn on real writing, and the writer decides what it means.

Gate: the first Phase N item (Sarah's writer validation) runs here. Phase M starts only after it passes.

Phase M: Threads, Review, Discovery, and generation

Deliverable: the writer's decisions reach every surface that comments on or writes their prose.

Phase N: Validation and documentation

Migrations (per the migration safety protocol)

ChangeRisk
Project.authorshipLedgerJSON (nullable)LOW
VoiceEvidence tableLOW
VoiceProfile.decisionsJSON (nullable)LOW
VoiceSample.modeRaw (nullable) + one-time backfill from workTypeRawLOW. Additive: the source column is untouched
Draft.notInMyVoice (Bool, default false)LOW
Retiring analysisJSON / dimensionStancesJSONNone. The columns stay and are only unread. No DROP

No HIGH-risk migration is planned.

Model calls and cost

CallWhenBound
Paragraph edit notesKeep it1 per sitting
Short-page noteKeep this page≤ 5 per sitting
Justified-hedge judgmentStep-back, only while a gap is listening or threaded1 per step-back
Say it firstPer thread mark2 per draft, plus Show them anyway

Portrait computation, uploads, and draft learning make no model calls. This replaces the old plan's unbounded "rebuild on every sample change" analysis.

Open questions

References

Final review

Pros

Cons and where the plan may be inadequate

Adjustments worth considering

  1. Exclude quoted dialogue from measurement in Phase H. This is cheap and removes the worst fiction skew. I'd add it at approval.
  2. Split Phase M into M1 (threads + Review anchor) and M2 (Discovery question, generation directive, drift). M1 completes the loop the sitting promises. M2 extends it.
  3. Ship Phases G–K as a first release. The sitting and portrait stand on their own, and Phase G fixes bugs regardless.

Confidence: 74 / 100

I'm confident in the sequencing, the ledger, deterministic measurement, the retirement call, and keeping migrations additive. The doubt sits in three empirical places: whether lexicon detectors plus provisional bands produce a portrait the writer recognises; whether the mirror's median-instance rule reads as fair; and how much the editor work costs. Phase N answers the first two, and it is placed early enough that a bad answer stops the plan before Phase M.