Expose active source quote in Ranker receipt

This commit is contained in:
OpenClaw Bot
2026-05-27 20:51:03 +02:00
parent 77b5395962
commit 2fa061120c
4 changed files with 11 additions and 1 deletions
+1
View File
@@ -253,6 +253,7 @@ function renderFirstScreen(firstScreen = {}) {
<p>${escapeHtml(firstScreen.why || 'It is the best first proof slice.')}</p>
</div>
${held.length ? `<div><span>Hold back</span><ul>${held.map((item) => `<li><b>${escapeHtml(item.title)}</b>${item.lane ? `${escapeHtml(item.lane)}` : ''}</li>`).join('')}</ul></div>` : ''}
${firstScreen.sourceQuote ? `<blockquote class="active-source-quote"><span>${escapeHtml(firstScreen.sourceTitle || 'Source quote')}</span>${escapeHtml(firstScreen.sourceQuote)}</blockquote>` : ''}
${guardrails.length ? `<small>Guardrails: ${guardrails.map(escapeHtml).join(' · ')}</small>` : ''}
${firstScreen.sourceAnchor ? `<small>Source anchor: ${escapeHtml(firstScreen.sourceAnchor)}</small>` : ''}
<small>${escapeHtml(firstScreen.rule || 'One active move. Everything else waits.')}</small>
+1 -1
View File
@@ -45,6 +45,6 @@ button,input,textarea{font:inherit} button{cursor:pointer} a{color:inherit;text-
.active-slice-strip{display:grid;grid-template-columns:1.25fr 1fr 1fr 1fr;gap:10px;margin:-6px 0 22px;padding:12px;border:3px solid var(--ink);background:#15130f;color:#fff;box-shadow:8px 8px 0 var(--blue)}
.handoff-strip{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin:22px 0 -6px;padding:12px;border:3px solid var(--ink);background:#fffaf1;box-shadow:7px 7px 0 rgba(21,19,15,.20)}.handoff-strip.status-ready{box-shadow:7px 7px 0 var(--green)}.handoff-strip.status-warn{box-shadow:7px 7px 0 var(--amber)}.handoff-strip.status-blocked{box-shadow:7px 7px 0 var(--red)}.handoff-strip>div{border:1.5px solid var(--hair);background:#fff6e5;padding:13px}.handoff-strip span{display:block;margin-bottom:7px;color:var(--blue2);text-transform:uppercase;letter-spacing:.12em;font-size:10px;font-weight:1000}.handoff-strip strong{display:block;margin-bottom:7px;font-size:clamp(18px,2vw,26px);line-height:1;letter-spacing:-.04em}.handoff-strip p{margin:0;color:var(--ink2);line-height:1.35}.handoff-strip small{grid-column:1/-1;color:var(--muted);font-weight:850}
.active-slice-strip>div{border:1.5px solid rgba(255,255,255,.32);background:linear-gradient(145deg,rgba(255,255,255,.10),rgba(255,255,255,.03));padding:13px}
.active-slice-strip span{display:block;margin-bottom:7px;color:#bfcaff;text-transform:uppercase;letter-spacing:.12em;font-size:10px;font-weight:1000}.active-slice-strip h3{margin:0 0 8px;font-size:clamp(24px,3vw,42px);line-height:.9;letter-spacing:-.06em}.active-slice-strip p{margin:0;color:#f7efe1;line-height:1.38}.active-slice-strip ul{margin:0;padding-left:18px;color:#f7efe1}.active-slice-strip small{grid-column:1/-1;color:#d9ddff;font-weight:850}.active-slice-main{background:linear-gradient(145deg,rgba(36,92,255,.28),rgba(255,255,255,.04))!important}
.active-slice-strip span{display:block;margin-bottom:7px;color:#bfcaff;text-transform:uppercase;letter-spacing:.12em;font-size:10px;font-weight:1000}.active-slice-strip h3{margin:0 0 8px;font-size:clamp(24px,3vw,42px);line-height:.9;letter-spacing:-.06em}.active-slice-strip p{margin:0;color:#f7efe1;line-height:1.38}.active-slice-strip ul{margin:0;padding-left:18px;color:#f7efe1}.active-slice-strip small{grid-column:1/-1;color:#d9ddff;font-weight:850}.active-source-quote{grid-column:1/-1;margin:0;padding:12px 14px;border:1.5px dashed rgba(255,255,255,.42);background:rgba(255,255,255,.06);color:#f7efe1;line-height:1.42}.active-source-quote span{color:#d9ddff}.active-slice-main{background:linear-gradient(145deg,rgba(36,92,255,.28),rgba(255,255,255,.04))!important}
@media (max-width:1100px){.active-slice-strip,.handoff-strip{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:700px){.active-slice-strip,.handoff-strip{grid-template-columns:1fr;box-shadow:5px 5px 0 var(--blue)}}
+3
View File
@@ -2247,6 +2247,9 @@ try {
assert.equal(gameRouteGuardrail.input.decisionContext.ideaRoute, 'game');
assert.ok(gameRouteGuardrail.input.decisionContext.nonGoals.some(item => /accounts, dashboards, workspaces/i.test(item)), 'game route should add anti-SaaS guardrails even if Scattermind did not spell them out as avoid text');
assert.equal(gameRouteGuardrail.ranked[0].id, 'build-order-1');
assert.match(gameRouteGuardrail.brief.firstScreen.sourceQuote, /Five-minute playable driving arena/i, 'first-screen receipt should carry the exact source quote behind the active move');
assert.equal(gameRouteGuardrail.brief.firstScreen.sourceTitle, 'Build Order');
assert.match(gameRouteGuardrail.brief.decisionReceipt.sourceQuote, /Five-minute playable driving arena/i);
assert.equal(gameRouteGuardrail.ranked.find(item => /Player account dashboard/i.test(item.title)).lane.source, 'source-non-goal');
assert.equal(gameRouteGuardrail.handoff.decisionContext.ideaRoute, 'game');
assert.match(gameRouteGuardrail.handoff.copyableText, /Idea route: game/);
+6
View File
@@ -1829,12 +1829,16 @@ function createDecisionBrief({ idea, context, mode, ranked, provenance, decision
},
} : null;
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 firstScreen = top ? {
headline: `Build only this first: ${top.title}`,
primaryAction: nextStepFor(top),
proofQuestion: evidenceQuestionFor(top),
why: reasonFor(top),
sourceAnchor: activeSourceAnchor,
sourceTitle: activeSourceTitle,
sourceQuote: activeSourceQuote,
holdBack: deferred.slice(0, 3).map(item => ({ title: item.title, lane: item.lane?.label || 'Not now', reason: reasonFor(item) })),
guardrails: (decisionContext?.nonGoals || []).slice(0, 3),
rule: 'One active move. Everything else waits until this proof produces evidence.',
@@ -1846,6 +1850,8 @@ function createDecisionBrief({ idea, context, mode, ranked, provenance, decision
evidenceQuestion: evidenceQuestionFor(top),
doNotStartYet: deferred.slice(0, 3).map(item => item.title),
sourceAnchor: activeSourceAnchor,
sourceTitle: activeSourceTitle,
sourceQuote: activeSourceQuote,
handoffRule: 'Only the Do first item is active. Validate one proof, then re-rank before promoting anything else.',
} : null;
const assumptions = [