diff --git a/scripts/check-rank-feedback.mjs b/scripts/check-rank-feedback.mjs index 92d25fa..e9a57cd 100644 --- a/scripts/check-rank-feedback.mjs +++ b/scripts/check-rank-feedback.mjs @@ -84,6 +84,9 @@ try { assert.match(data.brief.firstScreen.headline, /Build only this first/); assert.equal(data.brief.firstScreen.primaryAction, data.brief.decisionReceipt.firstProofStep); assert.equal(data.brief.firstScreen.proofQuestion, data.brief.decisionReceipt.evidenceQuestion); + assert.equal(data.brief.firstScreen.proofScript, data.brief.decisionReceipt.proofScript); + assert.match(data.brief.firstScreen.proofScript, /I have a Scattermind build-order recommendation/); + assert.match(data.brief.firstScreen.proofScript, /Snapshot to Ranker feature-set contract/); assert.deepEqual(data.brief.firstScreen.guardrails, ['Avoid accounts, workspaces, and team voting']); assert.ok(data.brief.firstScreen.holdBack.some(item => item.title === 'Subscription billing layer')); assert.match(data.brief.firstScreen.rule, /One active move/); @@ -116,9 +119,11 @@ try { assert.match(data.handoff.activeSlice.proof.nextStep, /manual proof/i); assert.equal(data.handoff.activeSlice.source.artifactId, 'snapshot_123'); assert.equal(data.handoff.activeSlice.source.sourceSection, 'concept-map.nextMoves'); + assert.match(data.handoff.activeSlice.proof.proofScript, /The thing I need to learn is/); assert.ok(data.handoff.activeSlice.notNow.some(item => item.id === 'export')); assert.match(data.handoff.activeSlice.rule, /Only this active slice is build-ready/); assert.match(data.handoff.copyableText, /## Active slice/); + assert.match(data.handoff.copyableText, /- Proof script:/); assert.match(data.handoff.copyableText, /- Do not start yet:/); assert.equal(data.handoff.readiness.sourceComplete, true); assert.ok(data.handoff.readiness.nextChecks.some(item => /Do first item/i.test(item))); diff --git a/server.js b/server.js index eb60546..ac0ad2e 100644 --- a/server.js +++ b/server.js @@ -1778,6 +1778,18 @@ function evidenceQuestionFor(option) { return `Can 3–5 target users understand and act on “${option.title}” without extra explanation?`; } +function proofScriptFor(option, provenance = {}) { + if (!option) return ''; + const sourceName = cleanText(provenance?.source || 'Scattermind', 60); + const sourceLabel = cleanText(provenance?.snapshotTitle || provenance?.artifactId || '', 120); + const evidence = evidenceQuestionFor(option); + const next = nextStepFor(option); + const setup = sourceLabel + ? `I have a ${sourceName} build-order recommendation for “${sourceLabel}”.` + : 'I am checking the next build move before adding more scope.'; + return cleanText(`${setup} The proposed first move is “${option.title}”. ${next} The thing I need to learn is: ${evidence}`, 520); +} + function nextStepFor(option) { if (!option) return ''; if (option.factors?.nextStep) return option.factors.nextStep; @@ -1854,10 +1866,12 @@ function createDecisionBrief({ idea, context, mode, ranked, provenance, decision const activeSourceAnchor = [top?.provenance?.sourceSection, top?.provenance?.sourceId || top?.provenance?.sourceTitle].filter(Boolean).join(' · '); const activeSourceQuote = cleanText(top?.provenance?.sourceQuote || '', 260); const activeSourceTitle = cleanText(top?.provenance?.sourceTitle || '', 140); + const activeProofScript = top ? proofScriptFor(top, provenance) : ''; const firstScreen = top ? { headline: `Build only this first: ${top.title}`, primaryAction: nextStepFor(top), proofQuestion: evidenceQuestionFor(top), + proofScript: activeProofScript, why: reasonFor(top), sourceAnchor: activeSourceAnchor, sourceTitle: activeSourceTitle, @@ -1871,6 +1885,7 @@ function createDecisionBrief({ idea, context, mode, ranked, provenance, decision activeLane: top.lane?.label || 'Do first', firstProofStep: nextStepFor(top), evidenceQuestion: evidenceQuestionFor(top), + proofScript: activeProofScript, doNotStartYet: deferred.slice(0, 3).map(item => item.title), sourceAnchor: activeSourceAnchor, sourceTitle: activeSourceTitle, @@ -2036,6 +2051,7 @@ function activeSliceFor({ ranked = [], provenance = {}, readiness = {} }) { proof: { nextStep: nextStepFor(active), evidenceQuestion: evidenceQuestionFor(active), + proofScript: proofScriptFor(active, provenance), successSignal: successSignalFor(active), killSignal: killSignalFor(active), }, @@ -2102,6 +2118,7 @@ function copyableHandoffText({ ranked = [], provenance = {}, decisionContext = { `- Move: ${activeSlice.item.title}`, `- Proof: ${activeSlice.proof.nextStep}`, `- Evidence question: ${activeSlice.proof.evidenceQuestion}`, + activeSlice.proof.proofScript ? `- Proof script: ${activeSlice.proof.proofScript}` : '', activeSlice.source.sourceSection ? `- Source: ${[activeSlice.source.sourceSection, activeSlice.source.sourceId].filter(Boolean).join(' · ')}` : '', activeSlice.notNow.length ? `- Do not start yet: ${activeSlice.notNow.map(item => item.title).join('; ')}` : '', ].filter(Boolean).join('\n') : '';