Carry Scattermind proof lens into ranking
This commit is contained in:
@@ -775,6 +775,7 @@ try {
|
|||||||
lenses: {
|
lenses: {
|
||||||
audience: 'First buyers are local neighbors who already attend small food events and want a low-pressure dinner plan.',
|
audience: 'First buyers are local neighbors who already attend small food events and want a low-pressure dinner plan.',
|
||||||
risk: 'Avoid accounts and saved workspaces before anyone pays. Do not build subscriptions or an app dashboard yet.',
|
risk: 'Avoid accounts and saved workspaces before anyone pays. Do not build subscriptions or an app dashboard yet.',
|
||||||
|
question: 'Proof steps: Ask ten neighbors if they would pay for the first dinner before building software. Run one manual invite script and collect yes/no replies.',
|
||||||
channel: 'Build first: One manual supper-club offer page - collect three real yes/no replies before building machinery. Test manually: Concierge invitation script - ask ten neighbors if they would pay for the first dinner. Defer: Pretty event calendar after the first paid table. Probably noise: Saved member workspace with accounts, billing, subscriptions, and team dashboard.',
|
channel: 'Build first: One manual supper-club offer page - collect three real yes/no replies before building machinery. Test manually: Concierge invitation script - ask ten neighbors if they would pay for the first dinner. Defer: Pretty event calendar after the first paid table. Probably noise: Saved member workspace with accounts, billing, subscriptions, and team dashboard.',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -784,10 +785,14 @@ try {
|
|||||||
assert.equal(scattermindPaidShape.input.provenance.artifactId, 'SM-PAID1');
|
assert.equal(scattermindPaidShape.input.provenance.artifactId, 'SM-PAID1');
|
||||||
assert.equal(scattermindPaidShape.input.provenance.snapshotTitle, 'Neighborhood Supper Club');
|
assert.equal(scattermindPaidShape.input.provenance.snapshotTitle, 'Neighborhood Supper Club');
|
||||||
assert.match(scattermindPaidShape.input.provenance.originalPrompt, /supper club/);
|
assert.match(scattermindPaidShape.input.provenance.originalPrompt, /supper club/);
|
||||||
assert.equal(scattermindPaidShape.input.optionCount, 4);
|
assert.equal(scattermindPaidShape.input.optionCount, 6);
|
||||||
assert.equal(scattermindPaidShape.ranked[0].id, 'build-order-1');
|
assert.equal(scattermindPaidShape.ranked[0].id, 'build-order-1');
|
||||||
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceId, 'concept-map.lenses.channel#1');
|
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceId, 'concept-map.lenses.channel#1');
|
||||||
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceTitle, 'Build Order');
|
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceTitle, 'Build Order');
|
||||||
|
assert.equal(scattermindPaidShape.ranked.find(item => item.id === 'proof-step-1').lane.id, 'test');
|
||||||
|
assert.equal(scattermindPaidShape.ranked.find(item => item.id === 'proof-step-1').provenance.sourceSection, 'concept-map.lenses.question');
|
||||||
|
assert.match(scattermindPaidShape.ranked.find(item => item.id === 'proof-step-1').factors.evidenceNeeded, /Ask ten neighbors/);
|
||||||
|
assert.equal(scattermindPaidShape.handoff.itemTrace.find(item => item.id === 'proof-step-1').sourceTitle, 'Proof Steps');
|
||||||
assert.match(scattermindPaidShape.handoff.itemTrace.find(item => item.id === 'build-order-1').sourceQuote, /One manual supper-club offer page/);
|
assert.match(scattermindPaidShape.handoff.itemTrace.find(item => item.id === 'build-order-1').sourceQuote, /One manual supper-club offer page/);
|
||||||
assert.equal(scattermindPaidShape.ranked.find(item => item.id === 'build-order-4').lane.id, 'park');
|
assert.equal(scattermindPaidShape.ranked.find(item => item.id === 'build-order-4').lane.id, 'park');
|
||||||
assert.ok(scattermindPaidShape.input.decisionContext.nonGoals.includes('Avoid accounts and saved workspaces before anyone pays'));
|
assert.ok(scattermindPaidShape.input.decisionContext.nonGoals.includes('Avoid accounts and saved workspaces before anyone pays'));
|
||||||
|
|||||||
@@ -1059,6 +1059,41 @@ function optionsFromBuildOrderText(text = '', sourceSection = 'concept-map.lense
|
|||||||
}).filter(item => item.action);
|
}).filter(item => item.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function proofStepFragments(text = '') {
|
||||||
|
return cleanMultiline(text, 2600)
|
||||||
|
.replace(/\b(proof steps?|test manually|validate|evidence to collect|next proof)\s*:/gi, '\n')
|
||||||
|
.split(/\n|;|\s+[•-]\s+|\.\s+/)
|
||||||
|
.map(part => part.replace(/^\s*[-*•\d.)]+\s*/, '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
.filter(part => /\b(run|ask|show|send|test|validate|prototype|mock|observe|interview|collect|measure|prove|disprove|manual|concierge|offer|script|reply|signal)\b/i.test(part))
|
||||||
|
.filter(part => !/^\s*(avoid|no|do not|don't|defer|probably noise|park|hold for later)\b/i.test(part))
|
||||||
|
.slice(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function proofTitleFromFragment(fragment = '') {
|
||||||
|
const cleaned = cleanText(fragment
|
||||||
|
.replace(/^(run|ask|show|send|test|validate|prototype|mock|observe|interview|collect|measure)\b\s*/i, match => match.trim()[0].toUpperCase() + match.trim().slice(1).toLowerCase() + ' ')
|
||||||
|
.replace(/\s+before\s+.*$/i, '')
|
||||||
|
.replace(/\s+and\s+record\s+.*$/i, ''), 180);
|
||||||
|
return cleanText(cleaned.split(/\s[-–—:]\s/)[0] || cleaned, 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
function optionsFromProofLensText(text = '', sourceSection = 'concept-map.lenses.question', sourceTitle = 'Proof Steps') {
|
||||||
|
return proofStepFragments(text).map((fragment, index) => ({
|
||||||
|
id: `proof-step-${index + 1}`,
|
||||||
|
action: proofTitleFromFragment(fragment),
|
||||||
|
why: 'Scattermind named this as proof to collect before promoting more build surface.',
|
||||||
|
evidence: fragment,
|
||||||
|
validationSteps: [fragment],
|
||||||
|
suggestedLane: 'validate-next',
|
||||||
|
rankerHints: { value: 7, effort: 2, confidence: 6, urgency: 6, risk: 3 },
|
||||||
|
sourceSection,
|
||||||
|
sourceItemId: `${sourceSection}#${index + 1}`,
|
||||||
|
sourceTitle,
|
||||||
|
sourceExcerpt: fragment,
|
||||||
|
})).filter(item => item.action);
|
||||||
|
}
|
||||||
|
|
||||||
function laneFromActionThread(text = '') {
|
function laneFromActionThread(text = '') {
|
||||||
if (/\b(probably noise|set aside|park|parking lot|do not build|don't build|not worth|distraction)\b/i.test(text)) return 'park';
|
if (/\b(probably noise|set aside|park|parking lot|do not build|don't build|not worth|distraction)\b/i.test(text)) return 'park';
|
||||||
if (/\b(defer|not yet|later|hold for later|after proof|wait until)\b/i.test(text)) return 'defer';
|
if (/\b(defer|not yet|later|hold for later|after proof|wait until)\b/i.test(text)) return 'defer';
|
||||||
@@ -1283,7 +1318,20 @@ function optionsFromBody(body = {}) {
|
|||||||
140
|
140
|
||||||
);
|
);
|
||||||
const buildOrderOptions = optionsFromBuildOrderText(buildOrderText, 'concept-map.lenses.channel', buildOrderSourceTitle);
|
const buildOrderOptions = optionsFromBuildOrderText(buildOrderText, 'concept-map.lenses.channel', buildOrderSourceTitle);
|
||||||
if (buildOrderOptions.length) return normalizeCandidateGroup([{ items: buildOrderOptions, sourceSection: 'concept-map.lenses.channel' }]);
|
if (buildOrderOptions.length) {
|
||||||
|
const proofLens = objectFrom(conceptMapLenses.question || conceptMapLenses.proof || conceptMapLenses.validation || conceptMapLenses.evidence);
|
||||||
|
const proofLensText = lensContent(conceptMapLenses.question)
|
||||||
|
|| lensContent(conceptMapLenses.proof)
|
||||||
|
|| lensContent(conceptMapLenses.validation)
|
||||||
|
|| lensContent(conceptMapLenses.evidence)
|
||||||
|
|| '';
|
||||||
|
const proofSourceTitle = cleanText(proofLens.title || 'Proof Steps', 140);
|
||||||
|
const proofOptions = optionsFromProofLensText(proofLensText, 'concept-map.lenses.question', proofSourceTitle);
|
||||||
|
return normalizeCandidateGroup([
|
||||||
|
{ items: buildOrderOptions, sourceSection: 'concept-map.lenses.channel' },
|
||||||
|
...(proofOptions.length ? [{ items: proofOptions, sourceSection: 'concept-map.lenses.question', defaultLane: 'validate-next' }] : []),
|
||||||
|
]);
|
||||||
|
}
|
||||||
const actionThreadSource = firstArraySource([
|
const actionThreadSource = firstArraySource([
|
||||||
{ items: conceptMap.threads_to_hold || conceptMap.threadsToHold || conceptMap.actionThreads || conceptMap.action_threads, sourceSection: 'concept-map.threadsToHold' },
|
{ items: conceptMap.threads_to_hold || conceptMap.threadsToHold || conceptMap.actionThreads || conceptMap.action_threads, sourceSection: 'concept-map.threadsToHold' },
|
||||||
{ items: snapshot.threads_to_hold || snapshot.threadsToHold || snapshot.actionThreads || snapshot.action_threads, sourceSection: 'snapshot.threadsToHold' },
|
{ items: snapshot.threads_to_hold || snapshot.threadsToHold || snapshot.actionThreads || snapshot.action_threads, sourceSection: 'snapshot.threadsToHold' },
|
||||||
|
|||||||
Reference in New Issue
Block a user