How we shipped Italian opera + Gregorian chant in six builds.
One operator question ("I don’t see how to do a Gregorian chant") exposed a bug class: capabilities registered in code with no user click-path. Six builds later: two new ghost voices, six new genres, a pure-mode prompt path, and two CI ratchets that prevent the bug class from shipping again.
The question
Operator: "How are we doing to be able to make an Italian opera piece or Gregorian Chant?"
Honest answer at the time: the platform could mimic them by genre-bend, but it didn't really do them. There was no Italian-opera-aware ghost in the war room. There was no Gregorian-chant-aware ghost. The 12-metric scoring rubric was calibrated against popular song; modal monophony or operatic recitative would silently mis-score. And the picker carried the assumption that every song is verse-chorus-bridge.
So the answer was: not really. Let's fix that.
The first build (B1571) — surface the capability
Two ghost voices added to src/lib/ghost-collaborators.ts:
- Verdi — The Maestro (🎼): operatic dramatist. Argues big-arc tension, character motive, libretto economy. Pet peeve: decorative grandeur without stakes.
- Hildegard — The Mystic (☧): liturgical chant tradition. Argues modal phrasing, sacred restraint, breath as architecture. Pet peeve: pop-section thinking applied to devotion.
Two new entries in the splice-genre catalog: opera and chant. The picker grew from 14 to 16. A calibration caveat surfaced when either was in the splice, telling the user honestly that the 12-metric rubric was calibrated for popular song and the score should be treated as a creative-prompt aid, not a definitive grade.
The second build (B1572) — fix the underlying bug
Five minutes after B1571 deployed, the operator opened the page: "I don't see how to do a Gregorian chant."
Investigation surfaced a deeper problem. The picker tiles were live, but the GENRE_DNA_DATABASE didn't have entries for opera or chant. The spliceGenres() lookup returned null, the prompt path silently skipped the splice fragment, and the user got nothing chant-specific. The capability claim and the actual capability had diverged.
The fix touched three files:
- Full DNA entries for opera + chant. Antiphonal/responsorial form. Modal scales (Dorian, Phrygian, Lydian, Mixolydian) — not major/minor. Latin vocabulary. Stone-room acoustic. No drum-pulse. For opera: aria/recitative/chorus architecture, mythic imagery, hall acoustic, bel canto vocal approach.
- A pure-genre prompt path. The standard splice fragment talks about "TENSION between two genres" — nonsense for pure-mode. New
buildPureGenrePrompt(genreId)emits a single-genre fidelity fragment instead.single-phase-prompt.tsbranches: whenprimaryId === secondaryId, it uses pure-mode. The pure-mode prompt explicitly forbids pop-ifying the tradition: "If a listener immersed in this tradition would not recognize the form, you have failed." - Persona tiles on V1. Sacred Chant + Operatic added to the always-visible Songwriter Persona grid. One click sets ghost + voltage + splice with primary === secondary.
That fixed it on V1. There was a second bug.
The Deep Audit + the punch list
A planned Deep Audit ran the same evening. It surfaced the deeper pattern: of the last 50 commits, ~14 were operator-flagged bugfixes. The reactive ratio was too high. The audit produced a 10-item punch list designed to convert reactive cycles into structural prevention.
Three of those items shipped that night and they are the actual story.
B1573 — The design-canon ratchet
The recent design-cycle drift (B1557—B1567) cost ~10 builds because the unified design-system spec was discipline-only. scripts/check-design-canon.ts greps src/**/*.{ts,tsx} for kill-list violations from docs/SITE-DESIGN-SYSTEM.md §2:
- Inline custom
linear-gradient(135deg)in style attrs - Glass-with-mask-gradient-border panels
- HeroFire on disallowed surfaces (allowlist: homepage + ForgeV2Composing)
- Off-palette color hues (rose / pink / fuchsia / emerald / amber / slate)
- Inline
@keyframesblocks - The
rounded-3xl utility (use canonicalrounded-cardinstead)
Initial captured count: 662 violations. Largest cleanup target: 449 amber (the canonical token is gold). The ratchet ceiling can only move down. Future sweeps tighten it. Same compounding property as console-log, explicit-any, bundle-size.
B1574 — Surface the language picker
The LyricLanguage enum (en/la/it/es/fr/ja) had been in the prompt path since B1420. V1 forge surfaced the picker. V2 (production since B1536) didn't. Capability claimed end-to-end with no user click-path. Same shape as the chant bug.
Fix: a compact LanguageControl dropdown in the V2 toggle row. Native <select> for keyboard nav + screen-reader support. Hover title surfaces RFC-0009 Phase 1 calibration advisory: non-English picks generate in the target language, but scoring still uses the English-calibrated rubric. Honest disclosure.
Now Latin chant is reachable. Italian opera is reachable. The capability matches the surface.
B1575 — The capability-surface ratchet
The bug class needs structural prevention. scripts/check-capability-surface.ts cross-checks every entry in LyricLanguage, GENRE_DNA_DATABASE, and GHOST_PROFILES against user-facing surface code. A capability that's registered but not reachable in any .tsx under src/app or src/components fails CI.
The first run found 4 gaps: gospel, punk, reggaeton, dancehall — all had full DNA entries but no picker tile. Closed in the same build by adding them to the GENRES array. Now: 21/21 genres surfaced. 6/6 languages surfaced. 10/10 ghosts surfaced. Allowlist starts empty.
The next time someone adds a capability without a click-path, the gate fails the PR. The chant-style bug class is structurally impossible.
B1576 — The honest correction
The B1572 persona tiles I added to V1 weren't visible on V2. V2's Customize panel could reach Hildegard + chant + Latin, but it took five clicks. The operator's original complaint was only fully addressed for users on the rollback path, not the production path.
B1576 added a TraditionPresets row to V2 — slim, palette-canon compliant, two tiles. One click sets ghost + voltage + splice (primary === secondary, triggering pure-mode) + language. Click-again clears everything.
Plus 4 unit tests asserting the click contract: Operatic sets verdi+opera+65+Italian, Sacred Chant sets hildegard+chant+25+Latin, clear resets everything, and the pure-mode contract holds (primary === secondary so single-phase-prompt picks buildPureGenrePrompt not the splice fragment).
The numbers
| Metric | Value |
|---|---|
| Builds in the run | 6 (B1571—B1576) |
| New CI ratchets installed | 2 (design-canon + capability-surface) |
| Ghost voices added | 2 (Verdi, Hildegard) |
| Genres surfaced | +6 (opera, chant, gospel, punk, reggaeton, dancehall) |
| Surface gaps closed | 2 (chant capability, language picker on V2) |
| Operator critiques in the loop | 1 (the original chant question) |
| Operator critiques structurally prevented | The whole bug class going forward |
The principle
Every bug the operator catches is a bug the system didn't catch. The fix isn't "be more careful next time." The fix is a CI ratchet that catches the bug class going forward. Every time.
This run shipped two of those: design-canon enforcement and capability/surface enforcement. Both convert human-vigilance into compile-time enforcement. Both have the same compounding property as the existing ratchets (explicit-any at zero, console-log at 19, golden-eval prompt drift snapshots, bundle-size, lighthouse). Future drift caught at PR time, not post-merge operator critique.
The chant question started this. The honest answer was that the platform didn't really do chant. The end state: the platform does Italian opera + Gregorian chant via two ghost voices, two new DNA entries, a pure-mode prompt path, a one-click tradition row, and two new ratchets that prevent the next capability from shipping with the same gap.
Next thing surfaced is the next thing fixed. That's the whole loop.