Skip to content
All posts
Behind the Scenes2026-04-277 min readBy Todd Nigro

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:

  1. 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.
  2. 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.ts branches: when primaryId === 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."
  3. 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 @keyframes blocks
  • The rounded-3xl utility (use canonical rounded-card instead)

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

MetricValue
Builds in the run6 (B1571—B1576)
New CI ratchets installed2 (design-canon + capability-surface)
Ghost voices added2 (Verdi, Hildegard)
Genres surfaced+6 (opera, chant, gospel, punk, reggaeton, dancehall)
Surface gaps closed2 (chant capability, language picker on V2)
Operator critiques in the loop1 (the original chant question)
Operator critiques structurally preventedThe 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.