feat(kb): schema + validator + smoke for progression levels and licks (task C-20)
Optional additive shapes: progression level (foundation|intermediate) and per-pack licks (8-word technique vocab, tab notes string 1-6 / fret 0-15, ids global with progressions). Validator exports checkLick as a lib for smoke (KB_VALIDATE_AS_LIB guard); smoke 776 -> 787. Critic PASS (independent sabotage re-proof: injected bad lick exit 1 with named errors; weakened vocab fails smoke 2/787). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -46,12 +46,20 @@ export default [
|
||||
mode: 'major', // major | minor | dorian | mixolydian | …
|
||||
songs: ['Autumn Leaves'],
|
||||
tip: 'One transferable idea.',
|
||||
level: 'intermediate', // OPTIONAL: 'foundation' | 'intermediate'
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
4–8 progressions per style. Cross-check `docs/progression-repertoire.md` §1.
|
||||
|
||||
**`level` (optional).** Tags the progression's difficulty for the in-app level
|
||||
filter. Allowed values: `'foundation'` (the style's bread-and-butter loops) or
|
||||
`'intermediate'` (secondary dominants, chained ii–Vs, backdoor, borrowed
|
||||
chords…). **Omitting the field is fine and means `'foundation'`** — consumers
|
||||
treat an absent `level` as foundation, so existing styles need no edits. The
|
||||
validator only checks the value when the field is present.
|
||||
|
||||
## Instrument packs
|
||||
|
||||
Common envelope:
|
||||
@@ -126,6 +134,76 @@ Voicings are degree recipes resolved through the chord quality. Degrees: `'1' '3
|
||||
}
|
||||
```
|
||||
|
||||
## Licks (optional, guitar first)
|
||||
|
||||
A style's instrument pack may also teach short, named licks — the ordered-note
|
||||
phrases the Licks & Techniques cards render. **The whole section is optional**:
|
||||
a pack without licks is complete and valid.
|
||||
|
||||
**How to register licks:** add a `licks` array as one more top-level key on the
|
||||
instrument pack's default export (next to `styleIntro`/`comping`/`plays`/`improv`
|
||||
in e.g. `src/data/kb/blues/guitar.js`). No change to `src/data/kb/index.js` is
|
||||
needed — the registry already exposes the whole pack, and the UI reads
|
||||
`kb[style].instruments.guitar.licks ?? []`.
|
||||
|
||||
```js
|
||||
export default {
|
||||
styleIntro: '…',
|
||||
comping: [ /* … */ ],
|
||||
plays: { /* … */ },
|
||||
improv: { /* … */ },
|
||||
|
||||
// OPTIONAL — structured licks (this section):
|
||||
licks: [
|
||||
{
|
||||
id: 'blues-box1-bb-answer', // '<style>-<slug>', globally unique
|
||||
// (shares ONE id namespace with progression ids)
|
||||
name: 'B.B. box answer phrase',
|
||||
level: 'foundation', // 'foundation' | 'intermediate' (required)
|
||||
chordContext: 'over the I7', // which chord/station it fits — free text,
|
||||
// e.g. 'dom7' or 'over the V7 turnaround'
|
||||
techniques: ['bend', 'vibrato'], // summary tags, from the fixed vocabulary below
|
||||
source: 'the B.B. King box, e.g. "The Thrill Is Gone" fills', // recommended attribution
|
||||
tab: [ // the ORDERED note sequence (played first → last)
|
||||
{ string: 2, fret: 8 }, // string 1 = high e … 6 = low E
|
||||
{ string: 1, fret: 8, technique: 'bend' }, // fret 0 (open) … 15
|
||||
{ string: 1, fret: 10, technique: 'vibrato' }, // technique is optional per note
|
||||
{ string: 2, fret: 8 },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Field rules (all enforced by `node scripts/validate-kb.mjs` when `licks` is present):
|
||||
|
||||
| Field | Rule |
|
||||
|---|---|
|
||||
| `id` | starts with `'<style>-'`; globally unique across **all** progression and lick ids in the whole KB |
|
||||
| `name` | required, non-empty |
|
||||
| `level` | `'foundation'` or `'intermediate'` — nothing else |
|
||||
| `chordContext` | required, non-empty string — tells the player *where* the lick lands |
|
||||
| `techniques` | array; every entry from the fixed vocabulary below (empty array = plain-picked) |
|
||||
| `tab` | non-empty ordered array of `{string, fret, technique?}` |
|
||||
| `tab[].string` | integer 1–6 (**1 = high e, 6 = low E** — standard tab convention; note `rootStr` in shapes counts the same way) |
|
||||
| `tab[].fret` | integer 0–15 (0 = open string) |
|
||||
| `tab[].technique` | optional; from the vocabulary; must **also** appear in the lick's `techniques[]` summary so card tags stay honest |
|
||||
| `source` | optional but recommended — name where the lick comes from (checklist: nothing invented) |
|
||||
|
||||
**Fixed technique vocabulary** (both for `techniques[]` and per-note `technique`
|
||||
— the validator rejects anything else):
|
||||
|
||||
`hammer-on` · `pull-off` · `slide` · `bend` · `double-stop` · `ghost-note` · `chromatic-approach` · `vibrato`
|
||||
|
||||
Like shapes, licks are **key-agnostic in spirit**: write them where they sit in
|
||||
the style's home position and say in `chordContext` which chord they fit; the
|
||||
tab renders as absolute string/fret positions.
|
||||
|
||||
Note: the older freeform `improv.licks` (prose `{tab/notation, description,
|
||||
over, source}`) is unchanged and still welcome — it feeds the improv text
|
||||
section. This top-level `licks` array is the *structured* shape that the lick
|
||||
cards render and the validator checks.
|
||||
|
||||
## Musician checklist (self-review before committing)
|
||||
|
||||
- [ ] Plays per progression genuinely differ in register/density/technique
|
||||
@@ -133,4 +211,5 @@ Voicings are degree recipes resolved through the chord quality. Degrees: `'1' '3
|
||||
- [ ] The style is recognizable from the rhythm descriptions alone (bossa ≠ jazz with new labels)
|
||||
- [ ] Every tip teaches a transferable idea (voice leading, register, space), not just "play this"
|
||||
- [ ] Songs/licks have sources; nothing invented
|
||||
- [ ] Lick techniques use only the fixed vocabulary; every lick is playable as written (strings 1–6, frets 0–15, in order)
|
||||
- [ ] `node scripts/validate-kb.mjs` green; `npm run build` green
|
||||
|
||||
Reference in New Issue
Block a user