const sample = { idea: 'Scattermind clarified a messy course idea. Now I need feedback on the feature/functionality order, not a dashboard.', optionsText: `- Manual build-order preview from one Concept Map - Copyable decision brief with Do first / Validate next / Defer / Park - Evidence questions beside each next move - Accounts and saved workspaces - Team voting on roadmap priority - Subscription billing layer - Polished export for sharing the defended order`, context: 'Snapshot / Concept Map handoff, solo builder, tired non-AI-native user, avoid auth/workspaces/billing before proof.', mode: 'mvp', }; const laneMeta = { do: { title: 'Do first', note: 'The active first move. Give it oxygen before the backlog starts breeding.' }, test: { title: 'Validate next', note: 'Strong candidates, but they need evidence before build time.' }, defer: { title: 'Defer', note: 'Useful later. Dangerous if it steals attention now.' }, park: { title: 'Park / cut', note: 'Not in the active decision. Reopen only if new evidence earns it.' }, }; const form = document.querySelector('#rankForm'); const results = document.querySelector('#results'); const toastEl = document.querySelector('#toast'); let lastResult = null; function escapeHtml(value) { return String(value ?? '').replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char])); } function metricPct(value) { return Math.max(0, Math.min(100, Math.round(Number(value || 0) * 10))); } function toast(message) { toastEl.textContent = message; toastEl.hidden = false; clearTimeout(toastEl._timer); toastEl._timer = setTimeout(() => { toastEl.hidden = true; }, 2600); } function fillSample() { form.idea.value = sample.idea; form.optionsText.value = sample.optionsText; form.context.value = sample.context; form.mode.value = sample.mode; document.querySelector('#try')?.scrollIntoView({ behavior: 'smooth', block: 'start' }); toast('Sample loaded. Run it or replace it with your own mess.'); } function laneClass(lane) { return `lane-${lane?.id || 'defer'}`; } function parsePastedJsonPayload(value) { const text = String(value || '').trim() .replace(/^```(?:json)?\s*/i, '') .replace(/```$/i, '') .trim(); const jsonText = text.startsWith('{') && text.endsWith('}') ? text : extractFirstJsonObject(text); if (!jsonText) return null; try { const parsed = JSON.parse(jsonText); const looksLikeBridgePayload = parsed && typeof parsed === 'object' && !Array.isArray(parsed) && ( parsed.schema || parsed.featureSet || parsed.feature_set || parsed.candidateSet || parsed.candidate_set || parsed.candidateFeatureSet || parsed.candidate_feature_set || parsed.rankReadyFeatureSet || parsed.rank_ready_feature_set || parsed.snapshot || parsed.conceptMap || parsed.concept_map || parsed.buildOrder || parsed.build_order || parsed.rankedBuildOrder || parsed.ranked_build_order || parsed.rankReadyBuildOrder || parsed.rank_ready_build_order || parsed.lenses || parsed.payload || parsed.rankPayload || parsed.rank_payload || parsed.scattermindPayload || parsed.scattermind_payload || parsed.conceptMapJson || parsed.rankerInput || parsed.ranker_input || parsed.rankerHandoff || parsed.ranker_handoff || parsed.rankerBridge || parsed.ranker_bridge || parsed.rankReady || parsed.rank_ready || parsed.bridge || parsed.bridgePayload || parsed.bridge_payload || parsed.continuation || parsed.continuationPlan || parsed.continuation_plan || parsed.concept_map_json || parsed.fullReadingJson || parsed.full_reading_json || parsed.fullReading || parsed.full_reading || parsed.glimpseJson || parsed.glimpse_json || parsed.snapshotJson || parsed.snapshot_json || parsed.buildOrderPreview || parsed.build_order_preview || parsed.rankedBuildOrder || parsed.ranked_build_order || parsed.rankReadyBuildOrder || parsed.rank_ready_build_order || parsed.reference_code || parsed.referenceCode || parsed.artifactId || parsed.sourceArtifactId || parsed.source_artifact_id || parsed.ideaText || parsed.idea_text || parsed.originalPrompt || parsed.original_prompt || parsed.sourceSummary || parsed.source_summary || parsed.opening_reflection || parsed.restated_idea || Array.isArray(parsed.features) || Array.isArray(parsed.actions) || Array.isArray(parsed.candidates) || Array.isArray(parsed.candidateActions) || Array.isArray(parsed.candidate_actions) || Array.isArray(parsed.candidateMoves) || Array.isArray(parsed.candidate_moves) || Array.isArray(parsed.rankReadyActions) || Array.isArray(parsed.rank_ready_actions) || Array.isArray(parsed.recommendedActions) || Array.isArray(parsed.recommended_actions) || Array.isArray(parsed.suggestedActions) || Array.isArray(parsed.suggested_actions) || Array.isArray(parsed.nextActions) || Array.isArray(parsed.next_actions) || Array.isArray(parsed.nextMoves) || Array.isArray(parsed.next_moves) || Array.isArray(parsed.possibleNextMoves) || Array.isArray(parsed.possible_next_moves) || Array.isArray(parsed.suggestedNextMoves) || Array.isArray(parsed.suggested_next_moves) || Array.isArray(parsed.recommendations) || Array.isArray(parsed.opportunities) || Array.isArray(parsed.doFirst) || Array.isArray(parsed.do_first) || Array.isArray(parsed.continueFirst) || Array.isArray(parsed.continue_first) || Array.isArray(parsed.makeTangible) || Array.isArray(parsed.make_tangible) || Array.isArray(parsed.validateNext) || Array.isArray(parsed.validate_next) || Array.isArray(parsed.evidenceNext) || Array.isArray(parsed.evidence_next) || Array.isArray(parsed.tryNext) || Array.isArray(parsed.try_next) || Array.isArray(parsed.next48Hours) || Array.isArray(parsed.next_48_hours) || Array.isArray(parsed.first48Hours) || Array.isArray(parsed.first_48_hours) || Array.isArray(parsed.nextTwoDays) || Array.isArray(parsed.next_two_days) || Array.isArray(parsed.deferred) || Array.isArray(parsed.holdForLater) || Array.isArray(parsed.hold_for_later) || Array.isArray(parsed.parkingLot) || Array.isArray(parsed.parking_lot) || Array.isArray(parsed.setAside) || Array.isArray(parsed.set_aside) || Array.isArray(parsed.threads_to_hold) || Array.isArray(parsed.threadsToHold) || Array.isArray(parsed.actionThreads) || Array.isArray(parsed.action_threads) || Array.isArray(parsed.questions_to_sit_with) || Array.isArray(parsed.questionsToSitWith) || Array.isArray(parsed.evidenceQuestions) || Array.isArray(parsed.evidence_questions) || Array.isArray(parsed.proofQuestions) || Array.isArray(parsed.proof_questions) || Array.isArray(parsed.proofPlan) || Array.isArray(parsed.proof_plan) || Array.isArray(parsed.validationQuestions) || Array.isArray(parsed.validation_questions) || Array.isArray(parsed.validationPlan) || Array.isArray(parsed.validation_plan) || Array.isArray(parsed.decisionQuestions) || Array.isArray(parsed.decision_questions) || Array.isArray(parsed.questionsToAnswer) || Array.isArray(parsed.questions_to_answer) || Array.isArray(parsed.followupQuestions) || Array.isArray(parsed.followup_questions) || Array.isArray(parsed.openQuestions) || Array.isArray(parsed.open_questions) || Array.isArray(parsed.assumptionTests) || Array.isArray(parsed.assumption_tests) || Array.isArray(parsed.riskiestAssumptions) || Array.isArray(parsed.riskiest_assumptions) || Array.isArray(parsed.risksToTest) || Array.isArray(parsed.risks_to_test) ); return looksLikeBridgePayload ? parsed : null; } catch { return null; } } function extractFirstJsonObject(text = '') { const start = text.indexOf('{'); if (start < 0) return ''; let depth = 0; let inString = false; let escaped = false; for (let index = start; index < text.length; index += 1) { const char = text[index]; if (escaped) { escaped = false; continue; } if (char === '\\' && inString) { escaped = true; continue; } if (char === '"') { inString = !inString; continue; } if (inString) continue; if (char === '{') depth += 1; if (char === '}') { depth -= 1; if (depth === 0) return text.slice(start, index + 1); } } return ''; } function payloadFromForm(formPayload) { const ideaJson = parsePastedJsonPayload(formPayload.idea); const optionsJson = parsePastedJsonPayload(formPayload.optionsText); const embedded = ideaJson || optionsJson; if (!embedded) return formPayload; const unwrapCandidate = embedded.payload || embedded.rankPayload || embedded.rank_payload || embedded.scattermindPayload || embedded.scattermind_payload || embedded.rankerBridge || embedded.ranker_bridge || embedded.continuation || embedded.continuationPlan || embedded.continuation_plan || embedded; const unwrapped = unwrapCandidate && typeof unwrapCandidate === 'object' && !Array.isArray(unwrapCandidate) ? unwrapCandidate : embedded; const merged = { ...unwrapped }; if (!merged.mode && formPayload.mode) merged.mode = formPayload.mode; if (String(formPayload.context || '').trim() && !merged.context) merged.context = formPayload.context; return merged; } function renderMetrics(metrics = {}) { const items = [ ['Value', metrics.value], ['Feasible', metrics.feasibility], ['Confidence', metrics.confidence], ['Revenue', metrics.revenue], ['Risk', metrics.risk], ]; return `
${items.map(([label, value]) => `
${label}
`).join('')}
`; } function renderPills(items = []) { if (!items.length) return ''; return `
${items.map((item) => `${escapeHtml(item)}`).join('')}
`; } function renderSourceTrace(sourceTrace = {}) { const hasTrace = sourceTrace.sourceSection || sourceTrace.sourceId || sourceTrace.sourceQuote; if (!hasTrace) return ''; const label = [sourceTrace.sourceTitle, sourceTrace.sourceId || sourceTrace.sourceSection].filter(Boolean).join(' · '); return `
Source trace ${label ? `${escapeHtml(label)}` : ''} ${sourceTrace.sourceQuote ? `

${escapeHtml(sourceTrace.sourceQuote)}

` : ''}
`; } function renderItemSourceTrace(item = {}) { const provenance = item.provenance || {}; const trace = { sourceSection: provenance.sourceSection || '', sourceId: provenance.sourceId || '', sourceTitle: provenance.sourceTitle || '', sourceQuote: provenance.sourceQuote || '', }; const hasTrace = trace.sourceSection || trace.sourceId || trace.sourceQuote; if (!hasTrace) return ''; const label = [trace.sourceTitle, trace.sourceId || trace.sourceSection].filter(Boolean).join(' · '); return `
Source trace${label ? ` · ${escapeHtml(label)}` : ''} ${trace.sourceQuote ? `

${escapeHtml(trace.sourceQuote)}

` : ''} ${trace.sourceSection ? `Section: ${escapeHtml(trace.sourceSection)}` : ''}
`; } function readinessTone(status = '') { if (status === 'ready') return 'ready'; if (status === 'usable-with-warnings') return 'warn'; return status ? 'blocked' : ''; } function renderHandoffStatus(handoff = {}) { const readiness = handoff.readiness || {}; if (!readiness.status) return ''; const warnings = handoff.warnings || []; return `
Bridge handoff readiness

${escapeHtml(readiness.label || readiness.status)} — ${escapeHtml(readiness.summary || '')}

${(readiness.nextChecks || []).length ? `
    ${readiness.nextChecks.map((step) => `
  1. ${escapeHtml(step)}
  2. `).join('')}
` : ''} ${warnings.length ? `
${warnings.map((warning) => `${escapeHtml(warning)}`).join('')}
` : ''}
`; } function renderBridgeHandoffStrip(data = {}) { const handoff = data.handoff || {}; const readiness = handoff.readiness || {}; if (!readiness.status) return ''; const source = handoff.source || data.brief?.source || data.input?.provenance || {}; const activeSlice = handoff.activeSlice || {}; const activeItem = activeSlice.item || {}; const sourceBits = [source.snapshotTitle, source.artifactId].filter(Boolean).join(' · '); const tone = readinessTone(readiness.status); return `
Bridge readiness ${escapeHtml(readiness.label || readiness.status)}

${escapeHtml(readiness.summary || 'Read the warnings before handing this back to Scattermind.')}

Active slice ${escapeHtml(activeItem.title || data.brief?.decisionReceipt?.activeMove || 'No active move')}

${escapeHtml(activeSlice.rule || data.brief?.decisionReceipt?.handoffRule || 'Only the Do first item is active.')}

Source ${escapeHtml(sourceBits || 'No artifact attached')}

${escapeHtml(source.originalPromptExcerpt || source.sourceSummaryExcerpt || 'Add source context before treating this as a durable bridge handoff.')}

${(readiness.nextChecks || []).length ? `Next check: ${escapeHtml(readiness.nextChecks[0])}` : ''}
`; } function renderFirstScreen(firstScreen = {}) { if (!firstScreen.headline) return ''; const held = firstScreen.holdBack || []; const guardrails = firstScreen.guardrails || []; return `
One thing to do now

${escapeHtml(firstScreen.headline)}

${escapeHtml(firstScreen.primaryAction || 'Run the smallest manual proof before product machinery.')}

Proof question

${escapeHtml(firstScreen.proofQuestion || 'What evidence would change this order?')}

Why this wins

${escapeHtml(firstScreen.why || 'It is the best first proof slice.')}

Pass / stop signals

Pass: ${escapeHtml(firstScreen.passSignal || 'A real user can name why this should be first.')}

Stop: ${escapeHtml(firstScreen.stopSignal || 'The proof creates no clear action, request, or value signal.')}

${escapeHtml(firstScreen.proofCadence || 'Run one tiny proof cycle, then rerank.')}
${held.length ? `
Hold back
` : ''} ${firstScreen.sourceQuote ? `
${escapeHtml(firstScreen.sourceTitle || 'Source quote')}${escapeHtml(firstScreen.sourceQuote)}
` : ''} ${guardrails.length ? `Guardrails: ${guardrails.map(escapeHtml).join(' · ')}` : ''} ${firstScreen.sourceAnchor ? `Source anchor: ${escapeHtml(firstScreen.sourceAnchor)}` : ''} ${escapeHtml(firstScreen.rule || 'One active move. Everything else waits.')}
`; } function renderDecisionReceipt(receipt = {}) { if (!receipt.activeMove) return ''; const held = receipt.doNotStartYet || []; return `
Active move ${escapeHtml(receipt.activeMove)}
First proof step

${escapeHtml(receipt.firstProofStep || 'Run the smallest manual proof before product machinery.')}

Evidence question

${escapeHtml(receipt.evidenceQuestion || 'What would make this ranking obviously right or wrong?')}

Proof gate

Pass: ${escapeHtml(receipt.passSignal || 'Evidence makes the active move obviously worth keeping first.')}

Stop: ${escapeHtml(receipt.stopSignal || 'Evidence does not create a clear next action.')}

${held.length ? `
Do not start yet
` : ''} ${receipt.sourceAnchor ? `Source anchor: ${escapeHtml(receipt.sourceAnchor)}` : ''} ${escapeHtml(receipt.handoffRule || 'Only the Do first item is active.')}
`; } function renderRankCard(item) { return `
#${item.rank} ${escapeHtml(item.lane?.label || 'Ranked')} ${item.metrics.score}

${escapeHtml(item.title)}

${item.description && item.description !== item.title ? `

${escapeHtml(item.description)}

` : ''} ${renderPills(item.scoreDrivers || [])}

Why: ${escapeHtml(item.reason)}.

Concern: ${escapeHtml(item.concern)}

Next step

${escapeHtml(item.nextStep || 'Run the smallest proof step.')}

Evidence question

${escapeHtml(item.evidenceQuestion || 'What proof would change this ranking?')}

Success / kill signal

${escapeHtml(item.successSignal || '')} ${escapeHtml(item.killSignal ? `Kill if: ${item.killSignal}` : '')}

${renderItemSourceTrace(item)} ${renderMetrics(item.metrics)} ${renderPills(item.scoringNotes || [])}
`; } function renderLane(ranked, laneId) { const items = ranked.filter((item) => item.lane?.id === laneId); const meta = laneMeta[laneId]; return `
${escapeHtml(meta.title)} ${items.length}

${escapeHtml(meta.note)}

${items.length ? items.map(renderRankCard).join('') : 'No items landed here.'}
`; } function sourceCitation(data) { const brief = data.brief || {}; const source = brief.source || data.handoff?.source || data.input?.provenance || {}; const trace = brief.quickGlance?.sourceTrace || {}; const parts = [ source.artifactId ? `Artifact: ${source.artifactId}` : '', source.conceptMapId ? `Concept Map: ${source.conceptMapId}` : '', source.snapshotTitle ? `Title: ${source.snapshotTitle}` : '', trace.sourceSection ? `Source section: ${trace.sourceSection}` : '', trace.sourceId ? `Source item: ${trace.sourceId}` : '', trace.sourceTitle ? `Source title: ${trace.sourceTitle}` : '', trace.sourceQuote ? `Source quote: ${trace.sourceQuote}` : '', source.originalPromptExcerpt ? `Original prompt: ${source.originalPromptExcerpt}` : '', source.sourceSummaryExcerpt ? `Source summary: ${source.sourceSummaryExcerpt}` : '', ].filter(Boolean); return parts.length ? parts.join('\n') : 'No source citation was carried into this result.'; } function markdownBrief(data) { if (data.handoff?.copyableText) return data.handoff.copyableText; const brief = data.brief || {}; const glance = brief.quickGlance || {}; const ranked = data.ranked || []; const citation = sourceCitation(data); const hasCitation = !citation.startsWith('No source citation'); const lanes = ['do', 'test', 'defer', 'park']; const laneTitles = { do: 'Do first', test: 'Validate next', defer: 'Defer', park: 'Park / cut' }; return [ `# ${brief.headline || 'Ranked feedback map'}`, '', brief.summary || '', '', '## Quick judgement', `- Top pick: ${glance.topPick || 'n/a'}`, `- Why it wins: ${glance.whyThisWins || 'n/a'}`, `- Next action: ${glance.nextAction || 'n/a'}`, `- Evidence question: ${glance.evidenceQuestion || 'n/a'}`, `- Biggest trap: ${glance.biggestTrap || 'n/a'}`, `- Confidence: ${data.rankConfidence?.level || 'n/a'} — ${data.rankConfidence?.reason || ''}`, ...(hasCitation ? ['', '## Source citation', citation] : []), '', ...lanes.flatMap((lane) => { const items = ranked.filter((item) => item.lane?.id === lane); return [`## ${laneTitles[lane]}`, ...(items.length ? items.map((item) => `- #${item.rank} ${item.title}: ${item.reason}. Next: ${item.nextStep}`) : ['- None']) , '']; }), '## Next 48 hours', ...(brief.next48Hours || []).map((step) => `- ${step}`), ].join('\n'); } async function copyText(text, label) { try { await navigator.clipboard.writeText(text); toast(`${label} copied.`); } catch { toast('Clipboard blocked. Browser being a tiny tyrant.'); } } function attachResultActions(data) { document.querySelector('#copyBrief')?.addEventListener('click', () => copyText(markdownBrief(data), 'Decision brief')); document.querySelector('#copyActions')?.addEventListener('click', () => copyText((data.brief?.next48Hours || []).map((step, index) => `${index + 1}. ${step}`).join('\n'), '48h actions')); document.querySelector('#copyJson')?.addEventListener('click', () => copyText(JSON.stringify({ brief: data.brief, ranked: data.ranked, buildOrder: data.buildOrderDetails, handoff: data.handoff }, null, 2), 'JSON handoff')); document.querySelector('#copySource')?.addEventListener('click', () => copyText(sourceCitation(data), 'Source citation')); } function renderResults(data) { lastResult = data; const ranked = data.ranked || []; const brief = data.brief || {}; const glance = brief.quickGlance || {}; results.hidden = false; results.innerHTML = `

${escapeHtml(data.mode?.label || 'Ranked feedback')} · first-pass judgement memo

${escapeHtml(brief.headline || 'Ranked feedback map')}

${escapeHtml(brief.summary || '')}

Not an oracle${escapeHtml(data.rankConfidence?.level || 'first pass')} confidence${ranked.length} items judged${escapeHtml(data.handoff?.readiness?.label || data.handoff?.readiness?.status || 'handoff unchecked')}
${renderBridgeHandoffStrip(data)}
Do this first${escapeHtml(glance.topPick || 'Add options')}
Why it wins

${escapeHtml(glance.whyThisWins || 'The list needs more comparable options.')}

Proof to collect

${escapeHtml(glance.evidenceQuestion || 'Name the evidence that would change the ranking.')}

Trap to avoid

${escapeHtml(glance.biggestTrap || brief.caution || 'Do not treat first-pass judgement as final truth.')}

${renderFirstScreen(brief.firstScreen || {})} ${renderDecisionReceipt(brief.decisionReceipt || {})} ${renderSourceTrace(glance.sourceTrace || {})}
${['do', 'test', 'defer', 'park'].map((lane) => renderLane(ranked, lane)).join('')}
Next 48 hours
    ${(brief.next48Hours || []).map((step) => `
  1. ${escapeHtml(step)}
  2. `).join('')}
Rank confidence

${escapeHtml(data.rankConfidence?.level || 'First pass')} — ${escapeHtml(data.rankConfidence?.reason || brief.caution || '')}

${renderHandoffStatus(data.handoff || {})} ${(brief.whatWouldChangeRanking || []).length ? `
What would change the order
    ${brief.whatWouldChangeRanking.map((change) => `
  1. ${escapeHtml(change)}
  2. `).join('')}
` : ''} ${(data.closeCalls || []).length ? `
Close calls
    ${data.closeCalls.map((call) => `
  1. ${escapeHtml(call.note)}
  2. `).join('')}
` : ''} ${(brief.assumptions || []).length ? `
Context carried forward
` : ''} ${(brief.expertReflections || []).map((reflection) => `
${escapeHtml(reflection.lens)}

${escapeHtml(reflection.text)}

`).join('')}

${escapeHtml(brief.caution || 'First-pass judgement, not an oracle.')}

`; attachResultActions(data); results.scrollIntoView({ behavior: 'smooth', block: 'start' }); } async function createFeedbackMap(event) { event.preventDefault(); const submit = form.querySelector('button[type="submit"]'); const payload = payloadFromForm(Object.fromEntries(new FormData(form).entries())); if (!String(payload.optionsText || '').trim() && !payload.conceptMap && !payload.featureSet && !payload.lenses) payload.optionsText = payload.idea; submit.disabled = true; submit.textContent = 'Judging…'; try { const response = await fetch('/api/rank-feedback', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const data = await response.json(); if (!response.ok) throw new Error(data.error || 'Could not rank this list.'); renderResults(data); } catch (error) { toast(error.message); } finally { submit.disabled = false; submit.textContent = 'Create ranked feedback map'; } } form.addEventListener('submit', createFeedbackMap); document.querySelector('#loadSample')?.addEventListener('click', fillSample); document.querySelector('#loadSampleTop')?.addEventListener('click', fillSample);