Accept wrapped Scattermind thread fallbacks

This commit is contained in:
OpenClaw Bot
2026-05-27 16:58:31 +02:00
parent 0271bfcbf6
commit 85c8067185
2 changed files with 53 additions and 6 deletions
+29
View File
@@ -290,6 +290,35 @@ try {
assert.match(storedRowPaste.handoff.copyableText, /CM-STORED-77/);
assert.equal(storedRowPaste.ranked.find(item => /Account dashboard/i.test(item.title)).lane.id, 'park');
const wrappedThreadFallbackResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
mode: 'mvp',
rankerInput: {
sourceName: 'Scattermind',
artifactId: 'CM-WRAPPED-THREADS',
snapshotTitle: 'Wrapped thread fallback Concept Map',
originalPrompt: 'I clarified an idea, but the bridge envelope only has action threads so far.',
context: 'Solo builder. Manual proof first. Avoid saved workspaces and account dashboards before evidence.',
threadsToHold: [
'Start by manually turning one Concept Map into a source-traced build order preview',
'Validate with three tired non-AI-native users before adding product UI',
'Hold for later: polished saved workspace after proof',
],
},
}),
});
assert.equal(wrappedThreadFallbackResponse.status, 200);
const wrappedThreadFallback = await wrappedThreadFallbackResponse.json();
assert.equal(wrappedThreadFallback.input.provenance.artifactId, 'CM-WRAPPED-THREADS');
assert.equal(wrappedThreadFallback.input.optionCount, 3);
assert.equal(wrappedThreadFallback.ranked[0].provenance.sourceSection, 'ranker-input.threadsToHold');
assert.match(wrappedThreadFallback.brief.quickGlance.topPick, /source-traced build order preview/i);
assert.equal(wrappedThreadFallback.ranked.find(item => /saved workspace/i.test(item.title)).lane.id, 'defer');
assert.equal(wrappedThreadFallback.handoff.readiness.status, 'ready');
assert.deepEqual(wrappedThreadFallback.handoff.warnings, []);
const snapshotOnlyResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
+24 -6
View File
@@ -1116,6 +1116,10 @@ function optionsFromSnapshotReading(source = {}, sourceSection = 'snapshot') {
return options.length >= 2 ? options : [];
}
function firstArraySource(entries = []) {
return entries.find(entry => Array.isArray(entry.items) && entry.items.length > 0) || { items: [], sourceSection: '' };
}
function optionsFromBody(body = {}) {
const envelope = bridgeEnvelopeFrom(body);
const featureSet = featureSetFrom(body);
@@ -1221,18 +1225,32 @@ function optionsFromBody(body = {}) {
);
const buildOrderOptions = optionsFromBuildOrderText(buildOrderText, 'concept-map.lenses.channel', buildOrderSourceTitle);
if (buildOrderOptions.length) return normalizeCandidateGroup([{ items: buildOrderOptions, sourceSection: 'concept-map.lenses.channel' }]);
const actionThreadSource = firstArraySource([
{ 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: envelope.threads_to_hold || envelope.threadsToHold || envelope.actionThreads || envelope.action_threads, sourceSection: 'ranker-input.threadsToHold' },
{ items: featureSet.threads_to_hold || featureSet.threadsToHold || featureSet.actionThreads || featureSet.action_threads, sourceSection: 'feature-set.threadsToHold' },
{ items: body.threads_to_hold || body.threadsToHold || body.actionThreads || body.action_threads, sourceSection: 'threadsToHold' },
]);
const actionThreadOptions = optionsFromActionThreads(
body.threads_to_hold || body.threadsToHold || body.actionThreads || body.action_threads || conceptMap.threads_to_hold || conceptMap.threadsToHold || conceptMap.actionThreads || conceptMap.action_threads,
conceptMap.threads_to_hold || conceptMap.threadsToHold || conceptMap.actionThreads || conceptMap.action_threads ? 'concept-map.threadsToHold' : 'threadsToHold',
actionThreadSource.items,
actionThreadSource.sourceSection || 'threadsToHold',
'Thread to hold'
);
if (actionThreadOptions.length >= 2) return normalizeCandidateGroup([{ items: actionThreadOptions, sourceSection: 'concept-map.threadsToHold' }]);
if (actionThreadOptions.length >= 2) return normalizeCandidateGroup([{ items: actionThreadOptions, sourceSection: actionThreadSource.sourceSection || 'threadsToHold' }]);
const questionSource = firstArraySource([
{ items: conceptMap.questions_to_sit_with || conceptMap.questionsToSitWith || conceptMap.openQuestions || conceptMap.open_questions, sourceSection: 'concept-map.questionsToSitWith' },
{ items: snapshot.questions_to_sit_with || snapshot.questionsToSitWith || snapshot.openQuestions || snapshot.open_questions, sourceSection: 'snapshot.questionsToSitWith' },
{ items: envelope.questions_to_sit_with || envelope.questionsToSitWith || envelope.openQuestions || envelope.open_questions, sourceSection: 'ranker-input.questionsToSitWith' },
{ items: featureSet.questions_to_sit_with || featureSet.questionsToSitWith || featureSet.openQuestions || featureSet.open_questions, sourceSection: 'feature-set.questionsToSitWith' },
{ items: body.questions_to_sit_with || body.questionsToSitWith || body.openQuestions || body.open_questions, sourceSection: 'questionsToSitWith' },
]);
const questionOptions = optionsFromQuestionsToSitWith(
body.questions_to_sit_with || body.questionsToSitWith || body.openQuestions || body.open_questions || conceptMap.questions_to_sit_with || conceptMap.questionsToSitWith || conceptMap.openQuestions || conceptMap.open_questions,
conceptMap.questions_to_sit_with || conceptMap.questionsToSitWith || conceptMap.openQuestions || conceptMap.open_questions ? 'concept-map.questionsToSitWith' : 'questionsToSitWith',
questionSource.items,
questionSource.sourceSection || 'questionsToSitWith',
'Question to sit with'
);
if (questionOptions.length >= 2) return normalizeCandidateGroup([{ items: questionOptions, sourceSection: 'concept-map.questionsToSitWith', defaultLane: 'validate-next' }]);
if (questionOptions.length >= 2) return normalizeCandidateGroup([{ items: questionOptions, sourceSection: questionSource.sourceSection || 'questionsToSitWith', defaultLane: 'validate-next' }]);
const nestedSnapshotReadingOptions = optionsFromSnapshotReading(snapshot, 'snapshot');
const snapshotReadingOptions = nestedSnapshotReadingOptions.length
? nestedSnapshotReadingOptions