typed "no X" / "less X" goes on the avoid list and marks X as a disliked tag; "more X" the reverse; the DJ is told the avoid list is a hard rule that outranks the taste model
shots / shots (push) Failing after 2m21s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6897PWcUEwHiqZMcVnNF6
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-21 06:20:49 +00:00
1 parent a4dab59f21
commit 582e5bebf2
1 file changed
+12 -1
+12 -1
View File
@@ -783,6 +783,7 @@ Recent tracks:
${prev || "- (none yet: this is the first)"}
Write the NEXT track. Keep continuity with the recent tracks unless the listener pushed away; vary something each time so the station moves.
Hard rule: nothing from "Listener asked to avoid" appears in the tags, the instruments, the lyrics or the mood — not even a hint, whatever any taste note above suggests. This is a different station from the listener's others: its seed is the law here, not what they liked elsewhere.
Return JSON:
- title: a short evocative song title (never reuse one)
- tags: the music prompt for a song model, 60-300 characters, comma-separated descriptors: genre, instruments, vocal style or "instrumental", mood, era, production notes. Concrete and musical, no sentences.
@@ -1389,7 +1390,17 @@ async function react(emoji, text) {
if (m.notes) state.taste.notes.push(`${m.notes} (an emoji, re ${who})`);
cur.reaction = (cur.reaction ? cur.reaction + " " : "") + emoji;
}
if (text) { state.taste.notes.push(`${text} (re ${who})`); cur.reaction = (cur.reaction ? cur.reaction + " " : "") + `"${text}"`; }
if (text) {
state.taste.notes.push(`${text} (re ${who})`); cur.reaction = (cur.reaction ? cur.reaction + " " : "") + `"${text}"`;
// "no violin", "less classical, no strings", "more guitar": the named things go on the lists and on the sheet as tag signals
const parts = text.toLowerCase().split(/[,;.!]|\band\b/).map((s) => s.trim()).filter(Boolean);
for (const p of parts) {
const neg = p.match(/^(?:no|not|less|stop|without|drop|enough|never|skip|hate|lose|kill|cut|no more)\s+(?:the\s+|any\s+)?([a-z][a-z0-9 -]{1,30}?)$/);
const pos = p.match(/^(?:more|love|loving|keep|add|give me|i like|i love)\s+(?:the\s+|this\s+|that\s+)?([a-z][a-z0-9 -]{1,30}?)$/);
if (neg) { state.taste.avoid.push(`${neg[1]} (listener said: "${p}")`); cur.reaction += " -" + neg[1].replace(/\s+/g, "_"); }
else if (pos) { state.taste.like.push(`${pos[1]} (listener said: "${p}")`); cur.reaction += " +" + pos[1].replace(/\s+/g, "_"); }
}
}
for (const k of ["like", "avoid", "notes"]) state.taste[k] = state.taste[k].slice(-8);
// a next track already in the oven was planned before this was said: it still plays (it's paid for), the one after hears it
const later = !!state.nextJob;