Python Guides — Phase 2: Voice & Content Rewrite
Date: 2026-06-24 Status: Design approved, ready for implementation planning Builds on: 2026-06-23-python-guides-personification-design.md (Phase 1, visual-only — shipped) Scope of this phase: Rewrite the per-level teaching content and recap bubbles of one pilot chapter (docs/python/variables.md) so each reading level reads in its guide's voice. English only.
Summary
Phase 1 turned the three Python reading levels into three named guides (Juno / Ace / Sol) and made every recap callout show the selected guide's avatar + name. It deliberately left all body and recap copy untouched.
Phase 2 writes the voice. For the pilot chapter, each <ForLevel> teaching block is rewritten in its guide's temperament, and each section's single shared recap is split into three voiced recaps (one per level, inside the matching <ForLevel>). The Deep Dive (advanced) blocks are simultaneously rebalanced from low-level trivia to production-professional Python, per the style guide's updated "Deep Dive is production depth, not academic depth" guardrail.
Decisions (locked during brainstorming)
- Frame: personalities, no station lore. Juno/Ace/Sol appear as three Scrimba Python mentors. Their Crooked Orbit / space-station backstory stays in the How AI Works track. In Python, any personal anecdote is a coding/teaching one ("the first time this clicked for me"), never a station incident.
- Reach: full. Both the teaching body and the recap bubbles are voiced per level.
- Approach A: tone in the body, character in the recap. Teaching blocks carry each guide's phrasing and temperament but stay clean and anecdote-free. The personal voice and any war-story lives only in the recap bubble. This is the existing style-guide rule 4 ("lore lives in the callouts, not the body").
- Guide mode needed wiring (correction). The Phase 1 spec assumed
CharacterMessagealready resolved the speaker from the selected level. It did not — guide mode was never built, and the component's hardcoded avatar map was stale and cross-wired againstguides.ts. Phase 2 therefore wires guide mode intoCharacterMessage: inshowLevelchapters the speaker name + avatar come fromGUIDES[level](single source of truth =guides.ts, so the bubble image matches the "pick your guide" card), and thenameprop renders as a topic eyebrow. A recap inside<ForLevel level="beginner">then shows Juno automatically. Non-leveled pages (bughouse, how-ai-works) are unchanged. - Casual recap voice. The recap copy is a casual, fun, TLDR personal-message from the guide, not a mini-lecture. Each guide has a light verbal style (Juno friendly, Ace chill-practical, Sol dry). Banlist still holds.
Goals
docs/python/variables.mdreads in three distinct guide voices across all<ForLevel>blocks and all recaps, with depth still climbing (the persona is voice on top of the depth ladder, never a replacement for it).- The
advancedblocks become production-professional (QC findings 1–4 below), folded into Sol's voice in the same pass. - A short Python-specific persona note is added to the style guide's
### Pythonappendix so future chapters and translators have the rule. - The pilot is a clean template that the remaining 12 chapters and 6 translations can follow in a later rollout.
Non-Goals
- Any other Python chapter (rollout is a separate plan).
- Translations (English pilot only; the per-level recap structure is what translators will later mirror).
- Any change to
CharacterMessage.vue,guides.ts, or the pickers (Phase 1 covered them). - The How AI Works track (its own crew lore is unchanged).
python/bughouse.md(its own named characters; noshowLevel, so unaffected).
Voice mapping
| Level | Guide | Body treatment | Recap treatment |
|---|---|---|---|
| beginner | Juno (she/her) | Warm, patient, analogy-led, occasionally self-deprecating about her own learning. The body is already close; tune warmth, keep it clean. | Voiced warm + one light, self-aware coding aside is allowed here. |
| intermediate | Ace (they/them) | Canonical, practical, build-first. Lightest touch — mostly today's copy, tightened. Cleanest to translate. | Voiced practical; minimal lore. The quotable, search-friendly one. |
| advanced | Sol (she/her) | Dry, jaded, senior-engineer. Production depth, never academic. Roasts the tech and herself, never the reader, never cryptic. | Voiced dry; war-story is a real-codebase one, not a station incident. |
Deep Dive rebalance (folds into Sol's rewrite)
From the QC of variables.md, the advanced blocks to fix:
variables.mdfloat block (~334–341): drop "IEEE 754 binary64, 53-bit mantissa, relative precision 2^-52". Lead with the rule: floats can't represent most decimal fractions exactly, so never use them for money or exact comparisons; reach fordecimal.Decimalorfractions.Fraction.- str block (~250): cut the CPython string-interning sentence. Keep Unicode-code-points (
len("café")is 4) and hashable-so-valid-as-dict-keys. If anything replaces interning, make it the rule: compare strings with==, neveris. - int block (~292): lead with "compare numbers with
==, neveris"; demote small-integer caching to a one-clause reason. Keep arbitrary precision. - type-intro block (~211): drop the "no primitives like C/Java" flex and the
(255).bit_length()party trick. Keep only the practical upshot: every value is a full object, so methods live on the values themselves.
Also fold the two should-fix items: remove the cross-block repetition of "the type is on the value, not the name" (state it once at full strength), and reframe id() away from "memory address" toward the aliasing consequence (two names, one mutable object).
Structure change (per section)
Before:
## Storing a value
<ForLevel level="beginner"> ...body... </ForLevel>
<ForLevel level="intermediate"> ...body... </ForLevel>
<ForLevel level="advanced"> ...body... </ForLevel>
<CharacterMessage name="Storing a value"> ...one shared recap... </CharacterMessage>After:
## Storing a value
<ForLevel level="beginner">
...Juno-voiced body...
<CharacterMessage name="Storing a value"> ...Juno recap... </CharacterMessage>
</ForLevel>
<ForLevel level="intermediate">
...Ace-voiced body...
<CharacterMessage name="Storing a value"> ...Ace recap... </CharacterMessage>
</ForLevel>
<ForLevel level="advanced">
...Sol-voiced body...
<CharacterMessage name="Storing a value"> ...Sol recap... </CharacterMessage>
</ForLevel>Sections that currently share code blocks across levels keep doing so where the code is identical; only the recap and the level prose are per-guide. name= keeps the topic title (translatable, drives the eyebrow). Speaker name/avatar come from guide mode, not from name=.
Voice sample (approved) — "Storing a value" recap trio
Juno: "
=isn't the 'equals' from maths class, it means 'store this value under this name'. Reassign whenever you like and Python keeps the newest one. The first time this clicked for me I stopped fighting it: there's no declare-first step, you assign and the variable exists."Ace: "
=binds a name to a value and you can rebind it any time; Python uses the latest. No declare-then-assign step, the binding is what creates the variable."Sol: "
=binds a name to an object, it never copies a value into a box. Reassigning rebinds the name and leaves the old object alone. Hold onto that before we hit mutable types, where forgetting it is how two variables quietly end up sharing one list."
Constraints (style guide, must hold)
- Depth still climbs. Voice never replaces a real depth difference. Juno explains with analogies, Ace gives the mechanism and what to do, Sol gives the production reality. If two levels differ only in jokes, it's wrong.
- Banlist applies even in dialogue: no em-dashes; no "just", "simply", "easy", "obvious", "of course", "genuine", "honestly".
- Each level stands alone as a complete path, and the trio at each spot deepens the same sub-point (no topic drift across levels).
- Personality never costs clarity. If a joke makes a sentence harder to read, cut it.
Style guide change
Add to the ### Python appendix: Python reuses the Juno/Ace/Sol guides as personalities only — no Crooked Orbit lore. Anecdotes in recaps are coding/teaching moments. Everything else in the existing "Personas" rules (depth ladder, lore-in-callout, banlist, translation-safety) still applies.
Affected files
- Edit:
docs/python/variables.md(per-level body voicing + per-level recaps + Deep Dive rebalance). - Edit:
data/style-guide.md(Python persona note in the### Pythonappendix). - No
.vue/ noguides.tsedits.
Risks / edge cases
- Recap reactivity: with recaps inside
<ForLevel>, switching level swaps the whole bubble (different voice), which is the intended richer behavior. Verify a live level switch re-renders cleanly. - Translation debt: per-level recaps triple the translatable bubble count at rollout. Acknowledged; out of scope for the pilot. The pilot sets the structure translators mirror.
- Voice drift toward filler: the banlist and "personality never costs clarity" rule are the guard. QC the pilot against the style guide before declaring it the template.
Verification
- Build the site; confirm
variables.mdrenders for all three levels with no broken tags. - Switch levels live; confirm each level shows its own voiced body + recap and the correct guide avatar/name.
- Re-run the chapter QC: depth still climbs, no banlist hits, no topic drift, advanced blocks read production-not-academic.
Future phases (out of scope)
- Roll the pilot's pattern across the other 12 leveled Python chapters.
- Fan out all per-level recaps to the 6 translations.

