feat(related): RelatedProgressions — loop-relative suggestions replace the generic table (task L-51)

KB-sourced ranking per the one-screen spec: same canonical degree
shape +100 (with the mandatory collapse of consecutive equal
degree/quality pairs — a real detector-produced 12-bar fires its blues
relatives despite the raw-index matcher bug), same style +40, shared
transitions +12 capped 36, degree-set Jaccard, length penalty; floor
24, max 5, never padded; entries render name/style/level/annotation +
the chord chain realized in the live key as real buttons ->
ChordDetailModal. match.js: additive exports only. Ride-along: dead
currentChord prop dropped from the banner mount. Critic PASS (both
hand-verified scores recomputed independently; full entry lists
byte-matched on three scenarios; collapse counterfactual = 39).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
vadimwit
2026-07-11 16:52:52 +01:00
parent d5a45f433a
commit 6e8d8f40fc
3 changed files with 339 additions and 8 deletions
+12 -6
View File
@@ -10,6 +10,7 @@ import DebugView from './components/DebugView'
import DrumView from './components/DrumView'
import { NOTES, detectKey, detectTopKeys, matchChordFromChroma, detectRepeatingProgression, getChordTones, getChordCandidates, getNoteHistoryAnalysis } from './lib/theory'
import ChordDetailModal from './components/ChordDetailModal'
import RelatedProgressions from './components/RelatedProgressions'
import LoopStation from './components/LoopStation'
import JamGuide, { KnowledgeDock } from './components/JamGuide'
import { useLoopEngine } from './services/loopEngine'
@@ -653,17 +654,16 @@ export default function App() {
chordHistory={chordHistory}
keyInfo={effectiveKey}
detectedProgression={detectedProgression}
currentChord={currentChord}
onChordClick={setSelectedChord}
/>
{/* ── The jam dashboard grid (task L-50, one-screen.md §1/§6): JamGuide
owns the two-column layout — LEFT: compact instrument view (chosen
here, passed as the mainView slot) + licks strip + the related-
progressions slot (null until L-51); RIGHT: the suggested-voicings
rail. ProgressionSuggestions is unmounted (file kept) — its job
split into the rail + RelatedProgressions per the user directive.
Follows the one global instrument selector. ── */}
progressions slot (RelatedProgressions, task L-51); RIGHT: the
suggested-voicings rail. ProgressionSuggestions is unmounted (file
kept) — its job split into the rail + RelatedProgressions per the
user directive. Follows the one global instrument selector. ── */}
<JamGuide
detectedProgression={detectedProgression}
keyInfo={effectiveKey}
@@ -678,7 +678,13 @@ export default function App() {
{instrument === 'piano' && <Piano keyInfo={effectiveKey} currentChord={currentChord} monoColor={monoColor} compact />}
</>
}
relatedSlot={null}
relatedSlot={
<RelatedProgressions
loop={detectedProgression}
keyInfo={effectiveKey}
onChordClick={setSelectedChord}
/>
}
fill={jamView}
/>