Accept Scattermind first-48-hour handoffs

This commit is contained in:
OpenClaw Bot
2026-05-27 20:13:21 +02:00
parent 9f712a3b93
commit 0447e89ca6
3 changed files with 59 additions and 9 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ Candidate items may include optional 110 `rankerHints` (`value`, `effort`, `c
Candidate trace note: candidate-level `sourceItemId` / `traceId`, `sourceTitle` / `lensTitle`, and `sourceExcerpt` / `sourceQuote` are preserved in ranked items, `buildOrderDetails`, and `handoff.itemTrace`. Lens-only Build Order text is also split into deterministic `concept-map.lenses.channel#N` source IDs with the original labelled sentence carried as `sourceQuote`, so pasted paid Concept Maps remain traceable even without explicit candidate objects. String items in laned Build Order arrays now also receive deterministic section-local source IDs such as `concept-map.buildOrder.validateNext#1` and carry the original string as `sourceQuote`, so simple Scattermind exports stay addressable downstream instead of becoming anonymous `feature-1` rows. Ranker also accepts the current Scattermind storage-row shape with `referenceCode`, `ideaText`, `context`, and string-valued `fullReadingJson` / `full_reading_json`; it expands the saved paid Concept Map before ranking so operators do not have to hand-copy lenses out of Appwrite rows. It also accepts private reading/API envelopes with object-valued `reading` / `fullReading` and `glimpse`, preserving `referenceCode` and `initialPrompt` while treating `reading` as the paid Concept Map. Stringified bridge envelopes in fields such as `rankerInput`, `rankerBridge`, `rankReady`, `bridgePayload`, or `continuationPlan` are expanded the same way, so Appwrite/string-copy handoffs do not have to be manually unwrapped before ranking. If the row only has free Snapshot data (`glimpseJson` / `glimpse_json` / `snapshotJson`), Ranker expands that Snapshot into a minimal continuation order: one manual proof plus the first evidence question, with the Snapshot reference code/title preserved for provenance. The public paste form mirrors this: a prose-wrapped/fenced Appwrite row paste stays intact for the API instead of unwrapping the stringified reading into nonsense fields. The decision `brief.quickGlance.sourceTrace` now repeats the winning item's source section/id/title/quote, and both `brief.source.originalPromptExcerpt` / `handoff.source.originalPromptExcerpt` or, when the original prompt is unavailable, `sourceSummaryExcerpt` carry the source context into the bridge handoff.
Soft Scattermind labels are accepted at the bridge boundary so Scattermind does not need to use harsh verdict copy in its own product surface. Lens text can say `Continue first`, `Make tangible`, `Try next`, `Evidence next`, `Hold for later`, or `Set aside`, with either colon or reader-friendly dash separators (`Continue first: …`, `Continue first — …`, `Evidence next - …`). Build Order objects and direct bridge/envelope sections can use matching camel/snake-case keys such as `continueFirst`, `evidenceNext`, `holdForLater`, and `setAside`. Ranker maps those to `doFirst / validateNext / defer / park` while preserving the softer original label in `sourceQuote` or candidate source trace. Ranker also accepts softer continuation envelopes named `rankerBridge`, `continuation`, or `continuationPlan`, candidate arrays named `possibleNextMoves`, `suggestedNextMoves`, `recommendations`, or `opportunities`, laned `buildOrderPreview` / `build_order_preview` objects, and evidence-question fallback arrays (`evidenceQuestions` / `evidence_questions`, `decisionQuestions`, `questionsToAnswer`, `followupQuestions`). Direct candidate objects may use reader-friendly prose keys like `text`, `content`, `summary`, `why_it_matters`, `evidence_to_collect`, `first_proof_step`, `green_flag`, and `red_flag`; Ranker normalizes those into title, value, evidence, next-step, success, and kill-signal fields so Scattermind does not have to rename paid Concept Map language into software-feature jargon. If a paid Concept Map has no labelled Build Order/action threads but does include `closing_note` / `closingNote` plus decision questions, Ranker treats the closing note as the active 48-hour Do first move and keeps the questions in Validate next. This lets Scattermind pass reader-friendly Concept Map copy without renaming everything into software-feature language.
Soft Scattermind labels are accepted at the bridge boundary so Scattermind does not need to use harsh verdict copy in its own product surface. Lens text can say `Continue first`, `Make tangible`, `Try next`, `Evidence next`, `Hold for later`, or `Set aside`, with either colon or reader-friendly dash separators (`Continue first: …`, `Continue first — …`, `Evidence next - …`). Build Order objects and direct bridge/envelope sections can use matching camel/snake-case keys such as `continueFirst`, `evidenceNext`, `holdForLater`, and `setAside`. Ranker maps those to `doFirst / validateNext / defer / park` while preserving the softer original label in `sourceQuote` or candidate source trace. Ranker also accepts softer continuation envelopes named `rankerBridge`, `continuation`, or `continuationPlan`, candidate arrays named `possibleNextMoves`, `suggestedNextMoves`, `recommendations`, or `opportunities`, laned `buildOrderPreview` / `build_order_preview` objects, first-48-hour action arrays (`next48Hours`, `next_48_hours`, `first48Hours`, `first_48_hours`, `nextTwoDays`, `next_two_days`), and evidence-question fallback arrays (`evidenceQuestions` / `evidence_questions`, `proofQuestions` / `proof_questions`, `validationQuestions` / `validation_questions`, `decisionQuestions`, `questionsToAnswer`, `followupQuestions`). Direct candidate objects may use reader-friendly prose keys like `text`, `content`, `summary`, `step`, `task`, `instruction`, `why_it_matters`, `evidence_to_collect`, `first_proof_step`, `green_flag`, and `red_flag`; Ranker normalizes those into title, value, evidence, next-step, success, and kill-signal fields so Scattermind does not have to rename paid Concept Map language into software-feature jargon. If a paid Concept Map has no labelled Build Order/action threads but does include `closing_note` / `closingNote` plus decision questions, Ranker treats the closing note as the active 48-hour Do first move and keeps the questions in Validate next. This lets Scattermind pass reader-friendly Concept Map copy without renaming everything into software-feature language.
Lane safety note: explicit Scattermind `defer` / `park` hints are hard rails, not mild suggestions. Source `nonGoals` / `avoid` guardrails are also hard enough to keep conflicting candidates out of Do first / Validate next even when their local scoring hints look attractive; soft guardrail language such as “this is not a dashboard” or “keep auth/billing/workspaces out until proof” is promoted into non-goals, not merely background context. Ranker now also infers a light `ideaRoute` from the Scattermind source text and carries it in `input.decisionContext` / `handoff.decisionContext`; for game concepts it automatically adds anti-SaaS non-goals so account/dashboard/workspace/subscription candidates cannot win a playable-prototype build order just because they were phrased loudly. The result will mark the lane source as `source-non-goal` so the handoff can explain that the candidate needs guardrail resolution before active work. Handoff `source.requiresSourceTrace` is true only when a real source artifact/title is present; plain idea-only ranking still warns about a missing artifact ID when it carries prompt provenance, but it does not spam source-section/evidence warnings meant for Scattermind artifacts. Handoff `readiness` now gives downstream bridge consumers a deterministic gate: `ready`, `usable-with-warnings`, `needs-source-context`, or `blocked`, with blockers and next checks for missing evidence, source trace, duplicate IDs, or active source-non-goal conflicts. Handoff `activeSlice` (`ranker-active-slice-v1`) is the compact machine-readable continuation unit: one active item, its proof/evidence/success/kill signals, source anchor, held-back items, readiness status, and the rule that only this slice is build-ready. For tired first-screen users, `brief.decisionReceipt` repeats the one active move, first proof step, evidence question, held-back items, source anchor, and the handoff rule that only Do first is active; use it as the compact result strip before showing the full lane board. For low-friction handoff, `/api/rank-feedback` also detects a raw Scattermind/Concept Map JSON object pasted into `idea`, `ideaText`, `optionsText`, or wrapper keys such as `payload`; it expands that object before ranking and reports `input.embeddedPayloadSource` so the public form can accept copy/paste exports without a custom import screen. Exact free Snapshot JSON (`working_name`, `restated_idea`, `lenses.shape`, `questions_to_sit_with`, `reference_code`) is rankable too: Ranker derives a manual proof active slice plus evidence questions, carrying the Snapshot reference code/title into provenance so a Snapshot-only handoff does not need a paid Concept Map before it can produce a useful build order. If a Concept Map only carries `questions_to_sit_with` / `questionsToSitWith` / `openQuestions` and no explicit build-order lanes or action threads, Ranker converts those questions into Validate-next evidence actions with source trace instead of pretending they are software features.
+35
View File
@@ -374,6 +374,41 @@ try {
assert.match(singleThreadQuestion.brief.decisionReceipt.firstProofStep, /manual proof/i);
assert.equal(singleThreadQuestion.handoff.readiness.status, 'ready');
const first48HoursResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_first_48_hours',
originalPrompt: 'Clarify a tiny offer idea and produce the first 48 hours of continuation work.',
context: 'Solo builder. Manual proof first. Avoid saved workspace dashboards before evidence.',
conceptMap: {
working_name: 'First 48 hour handoff',
first_48_hours: [
{
id: 'send-manual-preview',
step: 'Send one manual build-order preview to a real freelancer',
why_it_matters: 'This proves whether the continuation helps before UI work.',
proof_question: 'Can they name the first move without another explanation?',
},
{
id: 'workspace-dashboard',
task: 'Create a saved workspace dashboard for every Concept Map',
evidence_to_collect: 'Would users want saved boards later?',
},
],
},
}),
});
assert.equal(first48HoursResponse.status, 200);
const first48Hours = await first48HoursResponse.json();
assert.equal(first48Hours.input.optionCount, 2, 'first_48_hours arrays should become rankable continuation candidates');
assert.equal(first48Hours.buildOrder.doFirst[0], 'send-manual-preview');
assert.equal(first48Hours.ranked.find(item => item.id === 'send-manual-preview').provenance.sourceSection, 'concept-map.next48Hours');
assert.match(first48Hours.handoff.activeSlice.proof.evidenceQuestion, /first move/);
assert.equal(first48Hours.ranked.find(item => item.id === 'workspace-dashboard').lane.source, 'source-non-goal');
assert.equal(first48Hours.handoff.readiness.status, 'ready');
const softDashLabelResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
+23 -8
View File
@@ -599,6 +599,12 @@ function looksLikeRankPayload(value = {}) {
|| Array.isArray(value.suggested_actions)
|| Array.isArray(value.nextActions)
|| Array.isArray(value.next_actions)
|| Array.isArray(value.next48Hours)
|| Array.isArray(value.next_48_hours)
|| Array.isArray(value.first48Hours)
|| Array.isArray(value.first_48_hours)
|| Array.isArray(value.nextTwoDays)
|| Array.isArray(value.next_two_days)
|| Array.isArray(value.nextMoves)
|| Array.isArray(value.next_moves)
|| Array.isArray(value.possibleNextMoves)
@@ -641,6 +647,10 @@ function looksLikeRankPayload(value = {}) {
|| Array.isArray(value.questionsToSitWith)
|| Array.isArray(value.evidenceQuestions)
|| Array.isArray(value.evidence_questions)
|| Array.isArray(value.proofQuestions)
|| Array.isArray(value.proof_questions)
|| Array.isArray(value.validationQuestions)
|| Array.isArray(value.validation_questions)
|| Array.isArray(value.decisionQuestions)
|| Array.isArray(value.decision_questions)
|| Array.isArray(value.questionsToAnswer)
@@ -1046,13 +1056,13 @@ function nonGoalConflicts(optionText, decisionContext = {}) {
function normalizeFeatureOption(item, index, fallbackId = 'feature', defaultSourceSection = '', defaultRecommendedLane = '') {
const rawValue = typeof item === 'string' || typeof item === 'number' ? String(item) : '';
const raw = rawValue ? { action: rawValue } : objectFrom(item);
const title = cleanText(raw.title || raw.name || raw.action || raw.move || raw.nextMove || raw.next_move || raw.nextStep || raw.next_step || raw.recommendedNextStep || raw.recommended_next_step || raw.experiment || raw.testName || raw.test_name || raw.hypothesis || raw.label || raw.text || raw.content || raw.summary || '', 140);
const title = cleanText(raw.title || raw.name || raw.action || raw.move || raw.step || raw.task || raw.instruction || raw.nextMove || raw.next_move || raw.nextStep || raw.next_step || raw.recommendedNextStep || raw.recommended_next_step || raw.experiment || raw.testName || raw.test_name || raw.hypothesis || raw.label || raw.text || raw.content || raw.summary || '', 140);
const proofSteps = cleanTextList(raw.proofSteps || raw.proof_steps || raw.proof || raw.firstProof || raw.first_proof || raw.validationSteps || raw.validation_steps || raw.steps || raw.method, 5, 180);
const dependencies = cleanTextList(raw.dependencies || raw.blockedBy || raw.blocked_by, 5, 120);
const evidenceNeeded = cleanText(raw.evidenceNeeded || raw.evidence_needed || raw.evidenceToCollect || raw.evidence_to_collect || raw.evidence || raw.test || raw.proofQuestion || raw.proof_question || raw.evidenceQuestion || raw.evidence_question || raw.questionToAnswer || raw.question_to_answer || raw.question || raw.learningGoal || raw.learning_goal || '', 260);
const userValue = cleanText(raw.userValue || raw.user_value || raw.value || raw.outcome || raw.why || raw.whyItMatters || raw.why_it_matters || raw.whyNow || raw.why_now, 260);
const risk = cleanText(raw.risk || raw.assumption || raw.unknown || raw.watchOut || raw.watch_out || '', 220);
const nextStep = cleanText(raw.nextStep || raw.next_step || raw.nextAction || raw.next_action || raw.firstStep || raw.first_step || raw.manualStep || raw.manual_step || raw.actionToTake || raw.action_to_take || raw.firstProofStep || raw.first_proof_step || '', 260);
const nextStep = cleanText(raw.nextStep || raw.next_step || raw.nextAction || raw.next_action || raw.firstStep || raw.first_step || raw.manualStep || raw.manual_step || raw.actionToTake || raw.action_to_take || raw.firstProofStep || raw.first_proof_step || raw.step || raw.task || raw.instruction || '', 260);
const successSignal = cleanText(raw.successSignal || raw.success_signal || raw.successCriteria || raw.success_criteria || raw.successMetric || raw.success_metric || raw.greenLight || raw.green_light || raw.greenFlag || raw.green_flag || raw.signalToSee || raw.signal_to_see || '', 260);
const killSignal = cleanText(raw.killSignal || raw.kill_signal || raw.stopSignal || raw.stop_signal || raw.redFlag || raw.red_flag || raw.failureSignal || raw.failure_signal || raw.cutIf || raw.cut_if || raw.stopIf || raw.stop_if || '', 260);
const rawLane = cleanText(raw.lane || '', 40);
@@ -1328,7 +1338,7 @@ function optionsFromSnapshotReading(source = {}, sourceSection = 'snapshot') {
const shapeText = cleanMultiline(lensContent(rawShapeLens), 700);
const workingName = cleanText(reading.working_name || reading.workingName || reading.snapshotTitle || reading.snapshot_title || reading.title || reading.name || '', 90);
const restatedIdea = cleanText(reading.restated_idea || reading.restatedIdea || reading.ideaText || reading.idea_text || reading.idea || '', 260);
const questions = cleanFlexibleTextList(reading.questions_to_sit_with || reading.questionsToSitWith || reading.evidenceQuestions || reading.evidence_questions || reading.decisionQuestions || reading.decision_questions || reading.questionsToAnswer || reading.questions_to_answer || reading.followupQuestions || reading.followup_questions || reading.openQuestions || reading.open_questions, 4, 240);
const questions = cleanFlexibleTextList(reading.questions_to_sit_with || reading.questionsToSitWith || reading.evidenceQuestions || reading.evidence_questions || reading.proofQuestions || reading.proof_questions || reading.validationQuestions || reading.validation_questions || reading.decisionQuestions || reading.decision_questions || reading.questionsToAnswer || reading.questions_to_answer || reading.followupQuestions || reading.followup_questions || reading.openQuestions || reading.open_questions, 4, 240);
const hasSnapshotShape = Boolean(workingName || restatedIdea || shapeText || questions.length);
if (!hasSnapshotShape || (!shapeText && !restatedIdea && questions.length < 1)) return [];
@@ -1383,6 +1393,9 @@ function optionsFromBody(body = {}) {
{ items: body.nextActions || body.next_actions || body.nextSteps || body.next_steps || body.recommendedNextSteps || body.recommended_next_steps || body.recommendedActions || body.recommended_actions || body.suggestedActions || body.suggested_actions, sourceSection: 'nextActions' },
{ items: envelope.nextActions || envelope.next_actions || envelope.nextSteps || envelope.next_steps || envelope.recommendedNextSteps || envelope.recommended_next_steps || envelope.recommendedActions || envelope.recommended_actions || envelope.suggestedActions || envelope.suggested_actions, sourceSection: 'ranker-input.nextActions' },
{ items: featureSet.nextActions || featureSet.next_actions || featureSet.nextSteps || featureSet.next_steps || featureSet.recommendedNextSteps || featureSet.recommended_next_steps || featureSet.recommendedActions || featureSet.recommended_actions || featureSet.suggestedActions || featureSet.suggested_actions, sourceSection: 'feature-set.nextActions' },
{ items: body.next48Hours || body.next_48_hours || body.first48Hours || body.first_48_hours || body.nextTwoDays || body.next_two_days, sourceSection: 'next48Hours', defaultLane: 'do-first' },
{ items: envelope.next48Hours || envelope.next_48_hours || envelope.first48Hours || envelope.first_48_hours || envelope.nextTwoDays || envelope.next_two_days, sourceSection: 'ranker-input.next48Hours', defaultLane: 'do-first' },
{ items: featureSet.next48Hours || featureSet.next_48_hours || featureSet.first48Hours || featureSet.first_48_hours || featureSet.nextTwoDays || featureSet.next_two_days, sourceSection: 'feature-set.next48Hours', defaultLane: 'do-first' },
{ items: body.nextMoves || body.next_moves, sourceSection: 'nextMoves' },
{ items: envelope.nextMoves || envelope.next_moves, sourceSection: 'ranker-input.nextMoves' },
{ items: featureSet.nextMoves || featureSet.next_moves, sourceSection: 'feature-set.nextMoves' },
@@ -1419,6 +1432,7 @@ function optionsFromBody(body = {}) {
]);
const conceptMapCandidateGroup = compactCandidateGroup([
{ items: conceptMap.nextActions || conceptMap.next_actions || conceptMap.nextSteps || conceptMap.next_steps || conceptMap.recommendedNextSteps || conceptMap.recommended_next_steps || conceptMap.recommendedActions || conceptMap.recommended_actions || conceptMap.suggestedActions || conceptMap.suggested_actions, sourceSection: 'concept-map.nextActions' },
{ items: conceptMap.next48Hours || conceptMap.next_48_hours || conceptMap.first48Hours || conceptMap.first_48_hours || conceptMap.nextTwoDays || conceptMap.next_two_days, sourceSection: 'concept-map.next48Hours', defaultLane: 'do-first' },
{ items: conceptMap.nextMoves || conceptMap.next_moves, sourceSection: 'concept-map.nextMoves' },
{ items: conceptMap.possibleNextMoves || conceptMap.possible_next_moves || conceptMap.suggestedNextMoves || conceptMap.suggested_next_moves || conceptMap.recommendations || conceptMap.opportunities, sourceSection: 'concept-map.possibleNextMoves' },
{ items: conceptMap.features, sourceSection: 'concept-map.features' },
@@ -1434,6 +1448,7 @@ function optionsFromBody(body = {}) {
]);
const snapshotCandidateGroup = compactCandidateGroup([
{ items: snapshot.nextActions || snapshot.next_actions || snapshot.nextSteps || snapshot.next_steps || snapshot.recommendedNextSteps || snapshot.recommended_next_steps || snapshot.recommendedActions || snapshot.recommended_actions || snapshot.suggestedActions || snapshot.suggested_actions, sourceSection: 'snapshot.nextActions' },
{ items: snapshot.next48Hours || snapshot.next_48_hours || snapshot.first48Hours || snapshot.first_48_hours || snapshot.nextTwoDays || snapshot.next_two_days, sourceSection: 'snapshot.next48Hours', defaultLane: 'do-first' },
{ items: snapshot.nextMoves || snapshot.next_moves, sourceSection: 'snapshot.nextMoves' },
{ items: snapshot.possibleNextMoves || snapshot.possible_next_moves || snapshot.suggestedNextMoves || snapshot.suggested_next_moves || snapshot.recommendations || snapshot.opportunities, sourceSection: 'snapshot.possibleNextMoves' },
{ items: snapshot.actions, sourceSection: 'snapshot.actions' },
@@ -1506,11 +1521,11 @@ function optionsFromBody(body = {}) {
'Thread to hold'
);
const questionSource = firstArraySource([
{ items: conceptMap.questions_to_sit_with || conceptMap.questionsToSitWith || conceptMap.evidenceQuestions || conceptMap.evidence_questions || conceptMap.decisionQuestions || conceptMap.decision_questions || conceptMap.questionsToAnswer || conceptMap.questions_to_answer || conceptMap.followupQuestions || conceptMap.followup_questions || conceptMap.openQuestions || conceptMap.open_questions, sourceSection: 'concept-map.questionsToSitWith' },
{ items: snapshot.questions_to_sit_with || snapshot.questionsToSitWith || snapshot.evidenceQuestions || snapshot.evidence_questions || snapshot.decisionQuestions || snapshot.decision_questions || snapshot.questionsToAnswer || snapshot.questions_to_answer || snapshot.followupQuestions || snapshot.followup_questions || snapshot.openQuestions || snapshot.open_questions, sourceSection: 'snapshot.questionsToSitWith' },
{ items: envelope.questions_to_sit_with || envelope.questionsToSitWith || envelope.evidenceQuestions || envelope.evidence_questions || envelope.decisionQuestions || envelope.decision_questions || envelope.questionsToAnswer || envelope.questions_to_answer || envelope.followupQuestions || envelope.followup_questions || envelope.openQuestions || envelope.open_questions, sourceSection: 'ranker-input.questionsToSitWith' },
{ items: featureSet.questions_to_sit_with || featureSet.questionsToSitWith || featureSet.evidenceQuestions || featureSet.evidence_questions || featureSet.decisionQuestions || featureSet.decision_questions || featureSet.questionsToAnswer || featureSet.questions_to_answer || featureSet.followupQuestions || featureSet.followup_questions || featureSet.openQuestions || featureSet.open_questions, sourceSection: 'feature-set.questionsToSitWith' },
{ items: body.questions_to_sit_with || body.questionsToSitWith || body.evidenceQuestions || body.evidence_questions || body.decisionQuestions || body.decision_questions || body.questionsToAnswer || body.questions_to_answer || body.followupQuestions || body.followup_questions || body.openQuestions || body.open_questions, sourceSection: 'questionsToSitWith' },
{ items: conceptMap.questions_to_sit_with || conceptMap.questionsToSitWith || conceptMap.evidenceQuestions || conceptMap.evidence_questions || conceptMap.proofQuestions || conceptMap.proof_questions || conceptMap.validationQuestions || conceptMap.validation_questions || conceptMap.decisionQuestions || conceptMap.decision_questions || conceptMap.questionsToAnswer || conceptMap.questions_to_answer || conceptMap.followupQuestions || conceptMap.followup_questions || conceptMap.openQuestions || conceptMap.open_questions, sourceSection: 'concept-map.questionsToSitWith' },
{ items: snapshot.questions_to_sit_with || snapshot.questionsToSitWith || snapshot.evidenceQuestions || snapshot.evidence_questions || snapshot.proofQuestions || snapshot.proof_questions || snapshot.validationQuestions || snapshot.validation_questions || snapshot.decisionQuestions || snapshot.decision_questions || snapshot.questionsToAnswer || snapshot.questions_to_answer || snapshot.followupQuestions || snapshot.followup_questions || snapshot.openQuestions || snapshot.open_questions, sourceSection: 'snapshot.questionsToSitWith' },
{ items: envelope.questions_to_sit_with || envelope.questionsToSitWith || envelope.evidenceQuestions || envelope.evidence_questions || envelope.proofQuestions || envelope.proof_questions || envelope.validationQuestions || envelope.validation_questions || envelope.decisionQuestions || envelope.decision_questions || envelope.questionsToAnswer || envelope.questions_to_answer || envelope.followupQuestions || envelope.followup_questions || envelope.openQuestions || envelope.open_questions, sourceSection: 'ranker-input.questionsToSitWith' },
{ items: featureSet.questions_to_sit_with || featureSet.questionsToSitWith || featureSet.evidenceQuestions || featureSet.evidence_questions || featureSet.proofQuestions || featureSet.proof_questions || featureSet.validationQuestions || featureSet.validation_questions || featureSet.decisionQuestions || featureSet.decision_questions || featureSet.questionsToAnswer || featureSet.questions_to_answer || featureSet.followupQuestions || featureSet.followup_questions || featureSet.openQuestions || featureSet.open_questions, sourceSection: 'feature-set.questionsToSitWith' },
{ items: body.questions_to_sit_with || body.questionsToSitWith || body.evidenceQuestions || body.evidence_questions || body.proofQuestions || body.proof_questions || body.validationQuestions || body.validation_questions || body.decisionQuestions || body.decision_questions || body.questionsToAnswer || body.questions_to_answer || body.followupQuestions || body.followup_questions || body.openQuestions || body.open_questions, sourceSection: 'questionsToSitWith' },
]);
const questionOptions = optionsFromQuestionsToSitWith(
questionSource.items,