Lossless Changelog
product

In-play classifier ranking lit, audits schema migrated to dual-surface, TOC redesign planned

A++.2 closed: `/play/[slot].astro` now consumes `<DeckOverlay--Play-UI>`, replacing the inline calmstorm-port chrome bars with the themable `<DeckChrome>` floating capsule and lighting up the in-play `<SlideRankPill>` via the section-wrap adapter. Phase 1 of the TOC-redesign plan also shipped: the audits storage migrated from one-rating-per-(deck, variant, slot) (schema 1) to up-to-two-ratings-per-row keyed by surface (schema 2 — `{scroll?, play?}`), with an auto-migrating loader, a one-shot CLI migrator, and the chroma audits file rewritten in place against its 18-row backlog. And the workflow-shaped TOC the founder asked for — variants as columns, slots as rows, paired scroll-rating + play-rating chips per cell, per-column shippability rollup, per-row drift indicator — is now a written plan ready for execution.

Michael StatonMusic Editoraugmented with Claude Code on Claude Opus 4.7 (1M context)

In-play classifier ranking lit, audits schema migrated to dual-surface, TOC redesign planned

Why care?

Two of the four shipped chunks here are agent-and-architecture work (the schema migration and the redesign plan), but they exist because of one workflow gap the founder named explicitly: today's /toc/[deck]/[variant]/ answers a build-system question ("which slot has a Play-UI file?") when the daily question is a workflow one — "which design variant is closest to shippable, and what's left to fix in it, on both surfaces?" Calmstorm-decks' /index shows the right shape; dididecks' didn't. The plan written today closes that gap, and the schema migration is the foundation it needs.

The other shipped piece — A++.2 — is the kind of small refactor that's loud only to the founder using it: every slide in /play/ now carries the in-play ranking pill so judgments form and persist while presenting, not just while scroll-reading. Combined with today's schema migration that means dididecks now has the capacity to track scroll-rating and play-rating independently per slot, which is what the new TOC will surface.

What's new

A++.2 — <DeckOverlay--Play-UI> consumed by the /play/[slot] route

  • /play/[deckSlug]/[variantSlug]/[slot].astro rebuilt to compose <DeckOverlay--Play-UI> instead of the inline calmstorm-port chrome bars + inline keyboard handler.

  • In-play <SlideRankPill> now mounts on every /play/ slot (top-right, per the position prop wired 2026-05-15) — the section-wrap adapter inside the overlay makes its IntersectionObserver pick up exactly one active slot without a play-mode code path.

  • <DeckChrome> (the floating bottom-right themable capsule) replaces the heavy top + bottom bars; the slide canvas now uses the full viewport at 16:9 letterbox instead of subtracting 7rem for chrome.

  • Keyboard contract preserved verbatim — ← / → / Space / PageUp / PageDown / Home / End / F / C / T / Esc — all wired through <DeckChrome>'s inline handler.

  • Closes the A++.2 row of the Phase-A-Plus-Plus-Play-Fidelity-In-Play-Ranking-and-Variant-URL-Safety plan. A++.1 (variant URL safety) and A++.3 (ContentFit clipping) still pending.

Audits schema v1 → v2 — dual-surface ratings

  • Storage shape grew from {schema: 1, ranks: { key → {status, rankedAt, ...} }} to {schema: 2, ranks: { key → {scroll?: {status, ...}, play?: {status, ...}} }}. Same key (deck/variant/slot); up to two nested entries per row, one per surface.

  • loadAuditRegistry auto-migrates schema-1 files on read — v1 entries land under .scroll (since the Phase A+ pill was scroll-mounted only). The migration happens in memory; the rewrite persists on the next write.

  • apps/deck-shell/scripts/migrate-audits-v1-to-v2.ts — idempotent one-shot CLI that the founder can run deliberately. Used today against client-sites/chroma-decks/data/audits/slides.json; 18 rows migrated cleanly, backed up to /tmp/slides.v1.backup.json.

  • New types: Surface = "scroll" | "play", SurfaceRankEntry (inner shape), RankEntryV2 = Partial<Record<Surface, SurfaceRankEntry>>, AuditRegistry = {schema: 2, ranks}. RankEntry retained as a transitional alias for SurfaceRankEntry so unrelated callsites typecheck unchanged.

  • preferredSurfaceEntry helper resolves a RankEntryV2 down to a single most-recent surface (prefers .play over .scroll) for legacy callsites that haven't yet been split — used today by /toc/[deck]/[variant]/, which deprecates to a redirect in Phase 6 of the redesign anyway.

  • /api/slide-rank POST writes into .scroll for now (Phase 1 of redesign keeps surface implicit; Phase 2 adds the explicit surface field on the request body and removes the default). <SlideRankPill> hydration reads v.scroll?.status ?? v.play?.status so the pill still shows the previously-set rating after migration.

TOC redesign plan — Redesign-TOC-as-Deck-Level-Dual-Surface-Review-Matrix.md

  • Authored against the founder's articulated 10-step deck-iteration-workflow: PDF → generate variants → scroll-rate → pick variant → iterate U/C → re-confirm scroll → port to play → play-rate → ship-when-column-all-≥P. The TOC is the answer to "where am I in this loop, right now?"

  • Scope: deck-level matrix at /toc/[deck]/ with variants as columns and slots as rows. Per-cell: two paired affordances — <ScrollIcon> + rating chip → /scroll/{variant}/#slot-NN, <PlayIcon> + rating chip → /play/{variant}/{slot}/. Rating chips are read-only display; rating happens via the floating <SlideRankPill> on the surface itself (one source of truth for the rate action). Per-column shippability rollup at column header (counts + READY badge when every cell ≥ passable on both surfaces). Per-row drift indicator (≠) at row right-edge when scroll-rating ≠ play-rating for any variant in that row. Old /toc/[deck]/[variant]/ becomes a redirect to /toc/[deck]/?variant=X.

  • Seven phases: (1) audits schema v2 — shipped today, (2) /api/slide-rank surface field, (3) <SlideRankPill> surface prop, (4) <ScrollIcon> + <PlayIcon> SVG pair (founder picks from three drafts), (5) /toc/[deck]/ matrix route, (6) per-variant route → redirect, (7) cross-cutting verify + version bump.

  • Three explicit stop-and-show points: post-migration sanity, icon-pair pick, matrix-shape verification.

  • Architecture decisions locked: schema migration rule (lazy on-read + one-shot script), surface-as-explicit-prop (no auto-detect magic), READY-badge semantics, drift-indicator scope, route shape. Open questions deferred to in-flight resolution: anchor format for #slot-NN, never-rated treatment in READY, narrow-viewport collapse, drift severity gradation, the "remix favorite slots across variants" affordance as follow-up.

The story

Morning: A++.2

The Plans-Inventory-2026-05-16.md cold-start doc named three reasonable next iterations; the founder picked Path A. The refactor was small in scope — replace the route's top/bottom chrome bars + inline keyboard handler with <DeckOverlay--Play-UI>, retarget the load-bearing letterbox CSS at the new structure (.ddd-overlay__stage > .slide-canvas), and let <DeckChrome> and <SlideRankPill> mount via the overlay's defaults. The build went clean on the first try: 46 static slot pages, all four chroma variants, no regressions. The pill now appears top-right on every /play/ slot and writes ranks through to the same audits file the scroll surface and TOC read.

The smoke test surfaced the next gap — the founder opened /toc/pitch/enhanced-v2/ next to calmstorm-decks' /index and articulated the workflow-vs-build-system framing mismatch. Calmstorm's TOC shows review-status as the headline; dididecks' shows port-status. The two-axis problem the workflow actually faces (scroll-review × play-review) is even richer than calmstorm's one-axis solution.

Afternoon: convergence + plan + Phase 1

A converged design fell out of a clarifying back-and-forth. Each TOC cell carries two ratings — one per surface — because porting to Play-UI is itself a re-review step, and ratings can drift if the port loses fidelity. The drift signal (scroll ≠ play) becomes a workflow primitive of its own: "this slot may have lost something in translation." Cell halves navigate; rating happens only on the surfaces (one source of truth). The dominant rollup is the per-column shippability banner; the per-row drift indicator is the subtle one. /toc/[deck]/ becomes primary; per-variant routes redirect.

That converged shape became Redesign-TOC-as-Deck-Level-Dual-Surface-Review-Matrix.md — seven phases, three stop-and-show points, scope boundary explicit on both sides. Phase 1 shipped immediately after: the audits schema split into surface-keyed entries, the in-memory v1→v2 migrator wired into loadAuditRegistry, the one-shot CLI for deliberate migration, and the chroma audits file rewritten in place. loadAuditRegistry no longer returns schema 1; every reader (TOC, SlideRankPill, future /api/slide-rank callers) reads the v2 shape natively.

The Phase 1 boundary was kept tight on purpose. The API still writes implicitly to .scroll. The pill still doesn't know which surface it's on. The new matrix route doesn't exist yet. Those are Phases 2, 3, and 5 respectively. What Phase 1 buys is storage that can hold two ratings per row, with v1 data intact and reachable — the foundation everything else builds on.

Shell version

Stayed at 0.1.0-rc.0. The next bump (0.1.0-rc.2 or 0.1.0-rc.1 depending on what lands first) is reserved for the TOC redesign's Phase 7 cross-cutting verification, since Phase 1 alone doesn't yet change any user-facing surface.

How it works (or "Under the hood")

The audits v1 → v2 migration

JSONC
// v1 — what the chroma audits file looked like before today
{
  "schema": 1,
  "ranks": {
    "pitch/enhanced-v2/01": {
      "status": "perfect",
      "rankedAt": "2026-05-12T10:37:27.882Z",
      "rankedBy": "founder",
      "notes": null
    }
  }
}

// v2 — what it looks like after today's migration
{
  "schema": 2,
  "ranks": {
    "pitch/enhanced-v2/01": {
      "scroll": {
        "status": "perfect",
        "rankedAt": "2026-05-12T10:37:27.882Z",
        "rankedBy": "founder",
        "notes": null
      }
      // play is absent until the founder rates this slot from /play/
    }
  }
}

loadAuditRegistry does the lift transparently — if the file is schema: 1, every entry gets re-homed under .scroll in memory. On the next write (any POST to /api/slide-rank, even with status: "pending"), the rewrite persists. The one-shot CLI does the same transform on disk; running it preemptively means the founder sees the v2 shape immediately rather than after the first write. Both paths are idempotent — running them on a v2 file is a no-op.

Why surface-keyed, not flat

A flat schema ({status_scroll, status_play}) would type-check fine but loses orthogonality: every consumer would have to special-case which surface they care about. Surface-keyed with Partial<Record<Surface, SurfaceRankEntry>> makes "this surface was never rated" structurally distinct from "this surface was rated pending" (which deletes the surface entry entirely) — a distinction the TOC's READY badge needs.

It also leaves the door open for additional surfaces later (thumbnail, print, narration-audio) without another migration: just another optional key on the same row.

What's next

The TOC redesign plan's Phase 2 — /api/slide-rank surface-aware POST. Small, contained, ~30 minutes. Adds an explicit surface: "scroll" | "play" field to the POST body, refuses requests without it (after a one-release transitional warning), and the route's pending-delete logic scopes to the surface (already in place from today). Phase 3 then makes <SlideRankPill> take a surface prop, wired through to both the GET hydrate and the POST. After that, Phase 4 (icon-pair drafts for founder approval) and Phase 5 (the new matrix route itself) — the visible work.

Live partial plans unchanged by today's work: A++.1 variant URL safety (the /play/{deck}/{variant}/ index redirect still 404s for variants without SLOTS[] entries), A++.3 ContentFit (slides exceeding 1920×1080 in Play-UI clip), Restore-Calmstorm-Nav-Elegance Steps 3+4+7+10+11, Phase A.7 publish (still blocked on the dididecks org-name decision).

  • [[../context-v/plans/Redesign-TOC-as-Deck-Level-Dual-Surface-Review-Matrix]] — the plan today's Phase 1 executes against; Phases 2–7 still ahead.

  • [[../context-v/plans/Phase-A-Plus-Plus-Play-Fidelity-In-Play-Ranking-and-Variant-URL-Safety]] — A++.2 row closed by today's /play/[slot] refactor.

  • [[../context-v/plans/Restore-Calmstorm-Nav-Elegance-as-Themable-Shell-Primitives]] — Step 6 (/play route uses DeckChrome) closed structurally today via the <DeckOverlay--Play-UI> consumption.

  • [[../context-v/explorations/Plans-Inventory-2026-05-16]] — the cold-start doc that named Path A as the next move.

  • [[2026-05-16_01]] — yesterday's bundled four-day entry; today builds on its DeckOverlay paired components + audits / SlideRankPill foundations.

  • context-v/skills/deck-iteration-workflow/SKILL.md (in lossless-agent-skills) — the workflow the new TOC operationalizes into a UI.