diff --git a/scripts/check-rank-feedback.mjs b/scripts/check-rank-feedback.mjs index 4e7bd7f..92d25fa 100644 --- a/scripts/check-rank-feedback.mjs +++ b/scripts/check-rank-feedback.mjs @@ -554,6 +554,36 @@ try { assert.match(commaLabel.handoff.itemTrace.find(item => item.id === 'build-order-1').sourceQuote, /Build first, run a concierge preview/); assert.deepEqual(commaLabel.handoff.warnings, []); + const paidFriendlyLabelResponse = await fetch(`${base}/api/rank-feedback`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + sourceName: 'Scattermind', + artifactId: 'concept_map_paid_friendly_labels', + originalPrompt: 'Scattermind wrote a paid Concept Map with less mechanical but still labelled Build Order wording.', + context: 'Solo builder. Manual proof first. No account dashboards or billing until evidence exists.', + conceptMap: { + working_name: 'Reader paid build order', + lenses: { + channel: { + title: 'Build Order', + content: 'Next 48 hours — mock one private Concept Map handoff and ask a tired builder where they would click first. Manual proof first — run the handoff from a real paid-style reading before adding app chrome. What not to build yet — saved account dashboards and billing settings. Do not let this become — another roadmap workspace with collaboration before proof.', + }, + }, + }, + }), + }); + assert.equal(paidFriendlyLabelResponse.status, 200); + const paidFriendlyLabel = await paidFriendlyLabelResponse.json(); + assert.equal(paidFriendlyLabel.input.optionCount, 4, 'reader-friendly paid labels should still split into build-order lanes'); + assert.equal(paidFriendlyLabel.ranked.find(item => item.id === 'build-order-1').lane.id, 'do'); + assert.equal(paidFriendlyLabel.ranked.find(item => item.id === 'build-order-2').lane.id, 'test'); + assert.equal(paidFriendlyLabel.ranked.find(item => item.id === 'build-order-3').lane.id, 'defer'); + assert.equal(paidFriendlyLabel.ranked.find(item => item.id === 'build-order-4').lane.id, 'park'); + assert.equal(paidFriendlyLabel.ranked.find(item => item.id === 'build-order-1').title, 'mock one private Concept Map handoff and ask a tired builder where they would click first'); + assert.match(paidFriendlyLabel.handoff.activeSlice.rule, /Only this active slice is build-ready/); + assert.deepEqual(paidFriendlyLabel.handoff.warnings, []); + const hintedResponse = await fetch(`${base}/api/rank-feedback`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, diff --git a/server.js b/server.js index 7f6a8a9..eb60546 100644 --- a/server.js +++ b/server.js @@ -1202,7 +1202,7 @@ function normalizeCandidateGroup(group = []) { } const buildOrderLabelSeparator = '\\s*(?::|,|[-–—])\\s*'; -const buildOrderLabelPattern = '(build first|build this first|start here|start with|start by|ship first|ship this first|first week|week one|first-week build order|continue first|make tangible first|make tangible|try next|test first|test this first|prove first|prove manually|evidence next|learn next|test manually|test this manually|validate manually|validate this manually|manual proof|validate next|hold for later|leave out|skip for now|not yet|defer|set aside|out of scope|probably noise|park|do not build yet|do not build this yet|don\'t build yet)'; +const buildOrderLabelPattern = '(build first|build this first|start here|start with|start by|ship first|ship this first|first week|week one|first-week build order|next 48 hours|first 48 hours|48-hour move|continue first|make tangible first|make tangible next|make tangible|try next|test first|test this first|prove first|proof first|manual proof first|prove manually|evidence next|learn next|test manually|test this manually|validate manually|validate this manually|manual proof|validate next|hold for later|leave out for now|leave out|skip for now|wait until proof|do later|not yet|what not to build yet|defer|set aside|out of scope|probably noise|distraction|do not let this become|don\'t let this become|park|do not build yet|do not build this yet|don\'t build yet)'; const buildOrderLabelRegex = new RegExp(`^${buildOrderLabelPattern}${buildOrderLabelSeparator}`, 'i'); function normalizeBuildOrderFragment(fragment = '') { @@ -1228,10 +1228,10 @@ function titleFromBuildOrderFragment(value = '') { } function laneFromBuildOrderLabel(fragment = '') { - if (new RegExp(`^(build first|build this first|start here|start with|start by|ship first|ship this first|first week|week one|first-week build order|continue first|make tangible first|make tangible)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'do-first'; - if (new RegExp(`^(try next|test first|test this first|prove first|prove manually|evidence next|learn next|test manually|test this manually|validate manually|validate this manually|manual proof|validate next)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'validate-next'; - if (new RegExp(`^(hold for later|leave out|skip for now|not yet|defer|do not build yet|do not build this yet|don't build yet|don't build this yet)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'defer'; - if (new RegExp(`^(set aside|out of scope|probably noise|park)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'park'; + if (new RegExp(`^(build first|build this first|start here|start with|start by|ship first|ship this first|first week|week one|first-week build order|next 48 hours|first 48 hours|48-hour move|continue first|make tangible first|make tangible next|make tangible)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'do-first'; + if (new RegExp(`^(try next|test first|test this first|prove first|proof first|manual proof first|prove manually|evidence next|learn next|test manually|test this manually|validate manually|validate this manually|manual proof|validate next)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'validate-next'; + if (new RegExp(`^(hold for later|leave out for now|leave out|skip for now|wait until proof|do later|not yet|what not to build yet|defer|do not build yet|do not build this yet|don't build yet|don't build this yet)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'defer'; + if (new RegExp(`^(set aside|out of scope|probably noise|distraction|do not let this become|don't let this become|park)${buildOrderLabelSeparator}`, 'i').test(fragment)) return 'park'; return ''; }