Files
rank/scripts/check-rank-feedback.mjs
T
2026-05-27 15:45:55 +02:00

1134 lines
81 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
const port = 43045 + Math.floor(Math.random() * 1000);
const base = `http://127.0.0.1:${port}`;
const server = spawn(process.execPath, ['server.js'], {
cwd: new URL('..', import.meta.url),
env: { ...process.env, PORT: String(port), APPWRITE_ENDPOINT: '', APPWRITE_PROJECT_ID: '', APPWRITE_API_KEY: '' },
stdio: ['ignore', 'pipe', 'pipe'],
});
let output = '';
server.stdout.on('data', chunk => { output += chunk; });
server.stderr.on('data', chunk => { output += chunk; });
async function waitForServer() {
const deadline = Date.now() + 6000;
while (Date.now() < deadline) {
try {
const response = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ optionsText: '- one\n- two' }),
});
if (response.status < 500) return;
} catch {
await new Promise(resolve => setTimeout(resolve, 120));
}
}
throw new Error(`server did not become ready:\n${output}`);
}
try {
await waitForServer();
const response = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
artifactId: 'snapshot_123',
snapshotTitle: 'Tiny shop idea clarity pass',
originalPrompt: 'I have a tiny shop idea and do not know what to build first.',
idea: 'Scattermind clarified a small product idea. Ranker must defend the next build order, not create a dashboard.',
context: 'Solo builder. Need a rank-ready build order after Snapshot / Concept Map. Avoid accounts, workspaces, and team voting.',
mode: 'mvp',
featureSet: {
features: [
{ id: 'bridge-contract', title: 'Snapshot to Ranker feature-set contract', description: 'Convert Concept Map next moves into a rank-ready feature set with provenance.', userValue: 'Preserves the user prompt and source artifact so build order is defensible.', evidenceNeeded: 'Can one generated Concept Map create 3-7 sane next moves with source sections?', proofSteps: ['Run one fixture through /api/rank-feedback'], recommendedLane: 'do-first', sourceSection: 'concept-map.nextMoves' },
{ id: 'build-order-preview', title: 'Build order preview', description: 'Show do first, validate next, defer, and park with reasons.', userValue: 'A tired builder sees the next move without opening a dashboard.', evidenceNeeded: 'Can 3 non-AI-native users understand the first recommended action?', proofSteps: ['Show a static result screen to 3 people'], recommendedLane: 'validate-next', sourceSection: 'concept-map.nextMoves' },
{ id: 'workspace', title: 'Accounts and saved workspaces', description: 'Full dashboard with auth, workspace collaboration, team voting, and sync.', dependencies: ['auth', 'teams', 'saved projects', 'sync'], risk: 'Turns the bridge into generic dashboard swamp before the first proof.', sourceSection: 'concept-map.deferred' },
{ id: 'billing', title: 'Subscription billing layer', description: 'Pricing, checkout, invoices, account plans, and admin controls.', dependencies: ['account model', 'checkout', 'fulfillment'], risk: 'Payment machinery before the continuation value is proven.', sourceSection: 'concept-map.deferred' },
{ id: 'export', title: 'Exportable decision brief', description: 'Simple brief for sharing the defended build order.', evidenceNeeded: 'Does a plain brief help a user act within 48 hours?', recommendedLane: 'validate-next', sourceSection: 'concept-map.nextMoves' },
{ id: 'parked-bridge-dashboard', title: 'Saved Snapshot dashboard with provenance', description: 'Looks bridge-adjacent, but Scattermind already marked it as not-now because it needs auth, saved workspaces, and collaboration first.', recommendedLane: 'park', sourceSection: 'concept-map.parkingLot' },
],
},
}),
});
assert.equal(response.status, 200);
const data = await response.json();
assert.equal(data.ok, true);
assert.equal(data.input.provenance.artifactId, 'snapshot_123');
assert.equal(data.input.provenance.source, 'Scattermind');
assert.match(data.input.provenance.originalPrompt, /tiny shop idea/);
assert.equal(data.ranked.length, 6);
assert.deepEqual(Object.keys(data.buildOrder), ['doFirst', 'validateNext', 'defer', 'park']);
assert.equal(data.ranked[0].id, data.buildOrder.doFirst[0]);
assert.equal(data.buildOrderDetails.doFirst[0].id, data.buildOrder.doFirst[0]);
assert.equal(data.buildOrderDetails.doFirst[0].sourceSection, 'concept-map.nextMoves');
assert.equal(data.buildOrderDetails.doFirst[0].laneSource, 'ranked');
assert.equal(typeof data.buildOrderDetails.doFirst[0].score, 'number');
assert.equal(typeof data.buildOrderDetails.doFirst[0].confidence, 'number');
assert.match(data.buildOrderDetails.doFirst[0].nextStep, /manual proof/i);
assert.notEqual(data.ranked[0].id, 'workspace', 'dashboard swamp must not win the bridge fixture');
assert.ok(['defer', 'park'].includes(data.ranked.find(item => item.id === 'workspace').lane.id));
assert.ok(['defer', 'park'].includes(data.ranked.find(item => item.id === 'billing').lane.id));
assert.equal(data.ranked.find(item => item.id === 'parked-bridge-dashboard').lane.id, 'park', 'explicit Scattermind park hints must stay out of the active proof slice');
assert.equal(data.ranked.find(item => item.id === 'parked-bridge-dashboard').lane.source, 'hint');
assert.equal(data.ranked.find(item => item.id === 'bridge-contract').provenance.sourceSection, 'concept-map.nextMoves');
assert.match(data.ranked.find(item => item.id === 'bridge-contract').factors.evidenceNeeded, /Concept Map/);
assert.ok(data.ranked.find(item => item.id === 'bridge-contract').factors.metricHints.value === undefined);
assert.equal(data.brief.source.artifactId, 'snapshot_123');
assert.match(data.brief.source.originalPromptExcerpt, /tiny shop idea/);
assert.match(data.brief.summary, /Source: Tiny shop idea clarity pass · snapshot_123/);
assert.equal(data.handoff.schema, 'rank-feedback-result-v1');
assert.equal(data.handoff.source.artifactId, 'snapshot_123');
assert.match(data.handoff.source.originalPromptExcerpt, /tiny shop idea/);
assert.equal(data.handoff.source.hasOriginalPrompt, true);
assert.equal(data.handoff.source.requiresSourceTrace, true);
assert.equal(data.handoff.itemTrace.length, data.ranked.length);
assert.equal(data.handoff.itemTrace.find(item => item.id === 'bridge-contract').sourceSection, 'concept-map.nextMoves');
assert.ok(data.input.decisionContext.constraints.includes('Solo builder'));
assert.ok(data.input.decisionContext.nonGoals.includes('Avoid accounts, workspaces, and team voting'));
assert.deepEqual(data.handoff.decisionContext.nonGoals, ['Avoid accounts, workspaces, and team voting']);
assert.ok(data.ranked.find(item => item.id === 'workspace').metrics.nonGoalConflicts.length >= 1, 'flat text avoid guardrails should protect against workspace candidates');
assert.deepEqual(data.handoff.warnings, []);
assert.ok(data.brief.next48Hours.some(item => /Open the source artifact \(snapshot_123\)/i.test(item)));
assert.ok(data.brief.next48Hours.some(item => /Evidence to collect/i.test(item)));
assert.match(data.brief.summary, /nearest follow-up|strongest signal/i);
assert.ok(data.brief.whatWouldChangeRanking.some(item => /evidence fails|re-run the order/i.test(item)));
assert.ok(Array.isArray(data.brief.assumptions));
assert.equal(data.handoff.readiness.status, 'ready');
assert.equal(data.handoff.readiness.sourceComplete, true);
assert.ok(data.handoff.readiness.nextChecks.some(item => /Do first item/i.test(item)));
assert.match(data.handoff.copyableText, /# Ranker build-order handoff/);
assert.match(data.handoff.copyableText, /## Do first\n\n- Snapshot to Ranker feature-set contract/);
assert.match(data.handoff.copyableText, /Source: concept-map\.nextMoves/);
assert.match(data.handoff.copyableText, /Rule: only the Do first item is active/);
const messyIdeaOnlyResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
idea: 'Im building a tool that helps freelancers package their services and decide what to sell first. Maybe offer critique, pricing calculator, proposal generator, landing page copywriter, client persona mapper, and some kind of dashboard later? I only have a week and want the fastest useful proof.',
context: 'Solo builder. Manual proof first. Avoid dashboards, accounts, and saved workspaces before evidence.',
mode: 'validation',
}),
});
assert.equal(messyIdeaOnlyResponse.status, 200);
const messyIdeaOnly = await messyIdeaOnlyResponse.json();
assert.equal(messyIdeaOnly.input.optionCount, 6, 'idea-only messy dumps should be split into rank-ready candidates');
assert.ok(messyIdeaOnly.ranked.some(item => /Offer critique/i.test(item.title)));
assert.ok(messyIdeaOnly.ranked.some(item => /Pricing calculator/i.test(item.title)));
assert.equal(messyIdeaOnly.ranked.find(item => /dashboard/i.test(item.title)).lane.source, 'source-non-goal');
assert.ok(!/dashboard/i.test(messyIdeaOnly.ranked[0].title), 'dashboard-flavored candidate must not win tired-user first pass');
assert.equal(messyIdeaOnly.handoff.source.requiresSourceTrace, false);
assert.ok(!messyIdeaOnly.handoff.warnings.some(item => /missing source section|missing original prompt/.test(item)));
assert.ok(messyIdeaOnly.handoff.warnings.includes('missing source artifact id'));
assert.equal(messyIdeaOnly.handoff.readiness.status, 'usable-with-warnings');
assert.ok(messyIdeaOnly.handoff.readiness.nextChecks.some(item => /source artifact id if this came from Scattermind/i.test(item)));
const hardRailResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
artifactId: 'concept_map_hard_rails',
snapshotTitle: 'Continuation engine guardrail pass',
originalPrompt: 'Clarify the continuation engine without turning it into a dashboard.',
idea: 'Scattermind clarified that Ranker should defend build order after a Concept Map.',
context: 'Solo builder. Avoid dashboards, accounts, saved workspaces, billing, and collaboration before proof.',
mode: 'mvp',
featureSet: {
features: [
{ id: 'workspace-autopilot', title: 'Saved workspace autopilot', description: 'A dashboard with accounts, saved workspaces, billing, collaboration, and AI-generated roadmaps.', evidenceNeeded: 'Would teams pay for this later?', rankerHints: { value: 10, effort: 1, confidence: 10, urgency: 10, risk: 1 }, sourceSection: 'concept-map.parkingLot' },
{ id: 'manual-source-preview', title: 'Manual source-traced build order preview', description: 'Take one Concept Map and produce a defended do-first / validate-next / defer / park result with source quotes.', evidenceNeeded: 'Can one tired non-AI-native user explain why the first move wins?', rankerHints: { value: 8, effort: 3, confidence: 7, urgency: 7, risk: 3 }, sourceSection: 'concept-map.nextActions' },
{ id: 'copyable-brief', title: 'Copyable bridge brief', description: 'Let the user copy the defended order and provenance into notes.', evidenceNeeded: 'Does the copied brief preserve the reason and source trace?', recommendedLane: 'validate-next', sourceSection: 'concept-map.nextActions' },
],
},
}),
});
assert.equal(hardRailResponse.status, 200);
const hardRail = await hardRailResponse.json();
assert.equal(hardRail.ranked.find(item => item.id === 'workspace-autopilot').lane.source, 'source-non-goal');
assert.equal(hardRail.buildOrder.doFirst[0], 'manual-source-preview', 'first eligible bridge item should become Do first even when a hard-railed candidate scores loudly');
assert.equal(hardRail.brief.quickGlance.topPick, 'Manual source-traced build order preview');
assert.equal(hardRail.handoff.readiness.status, 'ready');
const hintedResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
artifactId: 'concept_map_metric_hints',
idea: 'A concept map produced possible next moves for an overwhelmed solo builder.',
context: 'Defend build order from explicit Scattermind scoring hints plus text; do not let flashy platform language win.',
mode: 'mvp',
featureSet: {
features: [
{ id: 'manual-concierge-proof', title: 'Manual concierge proof', description: 'Personally rank three real idea snapshots and turn each into a small build order preview.', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 }, evidenceNeeded: 'Will a tired user act on the first recommended move?', proofSteps: ['Run 3 manual previews'] },
{ id: 'ai-autopilot-roadmap', title: 'AI autopilot roadmap platform', description: 'Generate a full automated roadmap with dashboard, workspace, team voting, sync, and integrations.', rankerHints: { value: 5, effort: 9, confidence: 3, urgency: 3, risk: 9 } },
{ id: 'nice-export', title: 'Clean export of the build order', description: 'Turn the defended order into a shareable text brief.', scoring: { impact: 7, complexity: 3, certainty: 7, timing: 5, assumptionRisk: 3 }, recommendedLane: 'validate-next' },
],
},
}),
});
assert.equal(hintedResponse.status, 200);
const hinted = await hintedResponse.json();
assert.equal(hinted.ranked[0].id, 'manual-concierge-proof', 'explicit low-effort/high-confidence Scattermind hints should defend the manual proof slice');
assert.ok(hinted.ranked[0].factors.metricHints.value >= 9);
assert.ok(hinted.ranked.find(item => item.id === 'ai-autopilot-roadmap').metrics.risk > hinted.ranked[0].metrics.risk);
const actionsResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
artifactId: 'concept_map_actions',
snapshotTitle: 'Workshop idea continuation',
originalPrompt: 'I want to turn a workshop idea into the first useful thing to build.',
idea: 'Scattermind emitted next actions rather than feature objects; Ranker should still defend build order.',
context: 'Candidate action set from Concept Map. Keep it action-first and avoid generic workspace layers.',
mode: 'mvp',
featureSet: {
actions: [
{ id: 'manual-preview', action: 'Manual build-order preview', why: 'A user sees one defended next move before any app machinery exists.', evidence: 'Can two tired users explain what to do next?', validationSteps: ['Create one static preview from the Concept Map'], suggestedLane: 'do-first', sourceSection: 'concept-map.nextActions' },
{ id: 'saved-workspace', action: 'Saved project workspace', why: 'Keep every idea and roadmap in an account dashboard.', dependencies: ['auth', 'database permissions', 'workspace model', 'sync'], risk: 'Dashboard swamp before the continuation proof.', suggestedLane: 'park', sourceSection: 'concept-map.parkingLot' },
{ id: 'text-export', action: 'Copyable decision brief', why: 'Let the user paste the defended order into notes or a chat.', evidence: 'Does a plain text brief help them act within 48 hours?', validationSteps: ['Export the top lane and concerns as text'], suggestedLane: 'validate-next', sourceSection: 'concept-map.nextActions' },
],
},
}),
});
assert.equal(actionsResponse.status, 200);
const actions = await actionsResponse.json();
assert.equal(actions.input.optionCount, 3);
assert.equal(actions.ranked[0].id, 'manual-preview', 'action-shaped Concept Map next moves should be rankable without a features wrapper');
assert.equal(actions.ranked.find(item => item.id === 'manual-preview').provenance.sourceSection, 'concept-map.nextActions');
assert.equal(actions.ranked.find(item => item.id === 'saved-workspace').lane.id, 'park');
assert.deepEqual(actions.handoff.warnings, []);
const nestedConceptResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
idea: 'Scattermind produced a Concept Map artifact with nested next actions.',
context: 'Ranker should consume the artifact directly and preserve provenance without asking Scattermind to rename actions as features.',
mode: 'mvp',
conceptMap: {
id: 'concept_map_nested_42',
snapshotTitle: 'Course idea continuation',
originalPrompt: 'I have a course idea and need the first build step.',
nextActions: [
{ id: 'landing-proof', action: 'One-page promise test', why: 'A tired creator can see whether the promise lands before building lessons.', evidence: 'Can 5 target users describe the promised outcome?', validationSteps: ['Write the promise', 'Ask 5 target users'], rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 }, suggestedLane: 'do-first' },
{ id: 'lesson-library', action: 'Full lesson library', why: 'Build every module, account dashboard, saved progress, and team workspace.', dependencies: ['auth', 'content system', 'progress tracking', 'workspace'], risk: 'Large platform before promise proof.', suggestedLane: 'park' },
{ id: 'copyable-plan', action: 'Copyable build order brief', why: 'Gives the creator a concrete next step to paste into notes.', evidence: 'Does the brief trigger one real follow-up action?', suggestedLane: 'validate-next' },
],
},
}),
});
assert.equal(nestedConceptResponse.status, 200);
const nestedConcept = await nestedConceptResponse.json();
assert.equal(nestedConcept.input.provenance.artifactId, 'concept_map_nested_42');
assert.equal(nestedConcept.input.provenance.conceptMapId, 'concept_map_nested_42');
assert.equal(nestedConcept.handoff.source.hasOriginalPrompt, true);
assert.equal(nestedConcept.ranked[0].id, 'landing-proof');
assert.equal(nestedConcept.ranked.find(item => item.id === 'landing-proof').provenance.sourceSection, 'concept-map.nextActions');
assert.equal(nestedConcept.ranked.find(item => item.id === 'lesson-library').lane.id, 'park');
assert.deepEqual(nestedConcept.handoff.warnings, []);
const snapshotOnlyResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
schema: 'scattermind-snapshot-v1',
idea: 'Scattermind has only produced a Snapshot so far; Ranker should still defend a tiny continuation order.',
mode: 'mvp',
snapshot: {
id: 'snapshot_only_17',
title: 'Tiny audit service snapshot',
originalPrompt: 'I want to sell a small audit service but do not know what to build first.',
targetAudience: 'Overwhelmed solo service seller',
context: {
constraints: ['Manual proof before product shell'],
nonGoals: ['Avoid accounts and saved workspaces'],
assumptions: ['A one-page promise can validate demand'],
},
nextActions: [
{ id: 'promise-proof', action: 'One-page promise proof', why: 'Use the Snapshot to test whether buyers understand the outcome before building app machinery.', evidence: 'Can 3 service sellers say what they would buy and why?', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'copyable-brief', action: 'Copyable audit brief', why: 'Turn the defended order into a plain text follow-up.', evidence: 'Does the copied brief make the next move obvious?', suggestedLane: 'validate-next' },
],
parkingLot: [
{ id: 'seller-workspace', action: 'Saved seller workspace', why: 'Accounts, saved projects, dashboard, billing, and collaboration for each service seller.', evidence: 'No proof yet' },
],
},
}),
});
assert.equal(snapshotOnlyResponse.status, 200);
const snapshotOnly = await snapshotOnlyResponse.json();
assert.equal(snapshotOnly.input.provenance.artifactId, 'snapshot_only_17');
assert.equal(snapshotOnly.input.provenance.snapshotTitle, 'Tiny audit service snapshot');
assert.equal(snapshotOnly.input.decisionContext.targetAudience, 'Overwhelmed solo service seller');
assert.deepEqual(snapshotOnly.input.decisionContext.nonGoals, ['Avoid accounts and saved workspaces']);
assert.equal(snapshotOnly.ranked[0].id, 'promise-proof', 'Snapshot nextActions should rank without waiting for a Concept Map wrapper');
assert.equal(snapshotOnly.ranked.find(item => item.id === 'promise-proof').provenance.sourceSection, 'snapshot.nextActions');
assert.equal(snapshotOnly.ranked.find(item => item.id === 'copyable-brief').lane.id, 'test');
assert.equal(snapshotOnly.ranked.find(item => item.id === 'seller-workspace').lane.id, 'park');
assert.equal(snapshotOnly.handoff.source.requiresSourceTrace, true);
assert.deepEqual(snapshotOnly.handoff.warnings, []);
const sectionedConceptResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_sectioned_lanes',
originalPrompt: 'The Concept Map separates next actions, validation ideas, deferred items, and a parking lot.',
idea: 'Ranker should combine a sectioned Concept Map into one defended build order without losing lane provenance.',
mode: 'mvp',
conceptMap: {
nextActions: [
{ id: 'one-source-preview', action: 'One-source build-order preview', why: 'Turn the Concept Map into a defended first move.', evidence: 'Can one tired user say what to do next?', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
],
validateNext: [
{ id: 'copyable-handoff', action: 'Copyable handoff brief', why: 'Let the user paste the build order into notes.', evidence: 'Does the copied brief preserve the action and reason?' },
],
deferred: [
{ id: 'visual-polish-pass', action: 'Visual polish pass', why: 'Improve the result screen after the bridge proves useful.', evidence: 'Do users understand the rough brief first?' },
],
parkingLot: [
{ id: 'saved-team-workspace', action: 'Saved team workspace', why: 'Accounts, auth dashboard, collaboration, and sync for every idea.', evidence: 'No proof yet' },
],
},
}),
});
assert.equal(sectionedConceptResponse.status, 200);
const sectionedConcept = await sectionedConceptResponse.json();
assert.equal(sectionedConcept.input.optionCount, 4);
assert.equal(sectionedConcept.ranked[0].id, 'one-source-preview');
assert.equal(sectionedConcept.ranked.find(item => item.id === 'copyable-handoff').lane.id, 'test', 'validateNext sections should default into Validate next');
assert.equal(sectionedConcept.ranked.find(item => item.id === 'copyable-handoff').lane.source, 'hint');
assert.equal(sectionedConcept.ranked.find(item => item.id === 'visual-polish-pass').lane.id, 'defer', 'deferred sections should not enter the active proof slice');
assert.equal(sectionedConcept.ranked.find(item => item.id === 'saved-team-workspace').lane.id, 'park', 'parking lot sections should stay parked');
assert.equal(sectionedConcept.handoff.itemTrace.find(item => item.id === 'saved-team-workspace').sourceSection, 'concept-map.parkingLot');
assert.deepEqual(sectionedConcept.handoff.warnings, []);
const experimentSectionResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_experiments',
originalPrompt: 'The Concept Map emitted experiments separately from next actions.',
idea: 'Ranker should merge validation experiments into the build order instead of dropping them.',
mode: 'mvp',
conceptMap: {
nextActions: [
{ id: 'manual-build-order', action: 'Manual build-order preview', why: 'Create one defended first move from the source artifact.', evidence: 'Can one tired user act on it?', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
],
experiments: [
{ id: 'five-user-comprehension', hypothesis: 'Five-user comprehension test', question: 'Can 3 of 5 non-AI-native users explain the first recommended move?', method: ['Show the copyable brief', 'Ask what they would do next'], sourceItemId: 'experiment-1', sourceTitle: 'Validation experiments' },
],
proofTests: [
{ id: 'copy-paste-proof', testName: 'Copy-paste proof test', learningGoal: 'Does the build-order brief survive being pasted into notes?', steps: ['Copy the brief', 'Read it the next morning'], sourceItemId: 'experiment-2', sourceTitle: 'Validation experiments' },
],
parkingLot: [
{ id: 'accounted-test-library', action: 'Saved experiment library', why: 'Auth dashboard for every validation test.', evidence: 'No bridge proof yet' },
],
},
}),
});
assert.equal(experimentSectionResponse.status, 200);
const experimentSection = await experimentSectionResponse.json();
assert.equal(experimentSection.input.optionCount, 4);
assert.equal(experimentSection.ranked[0].id, 'manual-build-order');
assert.equal(experimentSection.ranked.find(item => item.id === 'five-user-comprehension').lane.id, 'test');
assert.equal(experimentSection.ranked.find(item => item.id === 'five-user-comprehension').lane.source, 'hint');
assert.equal(experimentSection.ranked.find(item => item.id === 'five-user-comprehension').provenance.sourceSection, 'concept-map.experiments');
assert.equal(experimentSection.ranked.find(item => item.id === 'five-user-comprehension').factors.evidenceNeeded, 'Can 3 of 5 non-AI-native users explain the first recommended move?');
assert.equal(experimentSection.ranked.find(item => item.id === 'copy-paste-proof').lane.id, 'test');
assert.equal(experimentSection.ranked.find(item => item.id === 'copy-paste-proof').provenance.sourceSection, 'concept-map.experiments');
assert.equal(experimentSection.ranked.find(item => item.id === 'accounted-test-library').lane.id, 'park');
assert.deepEqual(experimentSection.handoff.warnings, []);
const emptyWrapperResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_empty_wrappers',
originalPrompt: 'The export includes empty feature arrays plus real Concept Map next actions.',
idea: 'Ranker should ignore empty wrapper arrays and rank the real nested action set.',
mode: 'mvp',
features: [],
featureSet: { features: [], actions: [] },
conceptMap: {
nextActions: [
{ id: 'source-action-proof', action: 'Source action proof', why: 'Use the Concept Map next action directly to defend the first move.', evidence: 'Can this action become a build-order preview?', suggestedLane: 'do-first' },
{ id: 'source-action-export', action: 'Copyable action brief', why: 'Keep the handoff artifact-shaped for a tired user.', evidence: 'Can the brief be pasted into notes?', suggestedLane: 'validate-next' },
{ id: 'empty-shadow-dashboard', action: 'Saved dashboard after empty import', why: 'Auth workspace dashboard that should not win.', suggestedLane: 'park' },
],
},
}),
});
assert.equal(emptyWrapperResponse.status, 200);
const emptyWrapper = await emptyWrapperResponse.json();
assert.equal(emptyWrapper.input.optionCount, 3);
assert.equal(emptyWrapper.ranked[0].id, 'source-action-proof', 'empty top-level arrays must not shadow nested Concept Map actions');
assert.equal(emptyWrapper.ranked.find(item => item.id === 'source-action-proof').provenance.sourceSection, 'concept-map.nextActions');
assert.deepEqual(emptyWrapper.handoff.warnings, []);
const nonGoalResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_non_goals',
originalPrompt: 'I clarified a tiny service idea and need the first build order.',
idea: 'Ranker should respect Concept Map non-goals when defending the build order.',
mode: 'mvp',
conceptMap: {
snapshotTitle: 'Non-goal guarded continuation',
context: {
targetAudience: 'Tired non-AI-native solo operator',
constraints: ['No account before first value', 'Manual proof is acceptable'],
nonGoals: ['Avoid saved workspaces', 'No auth dashboard', 'No billing layer before proof'],
},
nextActions: [
{ id: 'workspace-autopilot', action: 'Saved workspace autopilot', why: 'Build accounts, auth dashboard, saved workspaces, and team sync.', evidence: 'None yet', suggestedLane: 'do-first', rankerHints: { value: 10, effort: 2, confidence: 9, urgency: 9, risk: 2 } },
{ id: 'manual-next-move', action: 'Manual next-move build order preview', why: 'Turn one source artifact into a defended first action and rank-ready build order without accounts.', evidence: 'Can 3 users act on the first move?', validationSteps: ['Create one static brief'], suggestedLane: 'do-first', rankerHints: { value: 9, effort: 1, confidence: 9, urgency: 9, risk: 1 } },
{ id: 'copy-brief', action: 'Copyable build-order brief', why: 'Give the user a plain artifact they can paste into notes.', evidence: 'Does copy/paste preserve the next step?', suggestedLane: 'validate-next' },
],
},
}),
});
assert.equal(nonGoalResponse.status, 200);
const nonGoal = await nonGoalResponse.json();
assert.equal(nonGoal.input.decisionContext.targetAudience, 'Tired non-AI-native solo operator');
assert.deepEqual(nonGoal.input.decisionContext.nonGoals, ['Avoid saved workspaces', 'No auth dashboard', 'No billing layer before proof']);
assert.ok(nonGoal.brief.assumptions.includes('Constraint: No account before first value'));
assert.ok(nonGoal.brief.assumptions.includes('Non-goal: Avoid saved workspaces'));
assert.equal(nonGoal.ranked[0].id, 'manual-next-move', 'non-goal conflicts should beat flashy positive hints');
const workspace = nonGoal.ranked.find(item => item.id === 'workspace-autopilot');
assert.ok(workspace.metrics.nonGoalConflicts.length >= 2);
assert.match(workspace.concern, /Source context says not to do this yet/);
assert.equal(workspace.lane.id, 'defer', 'source non-goals should keep conflicted candidates out of the active proof slice even with strong hints');
assert.equal(workspace.lane.source, 'source-non-goal');
assert.deepEqual(nonGoal.handoff.itemTrace.find(item => item.id === 'workspace-autopilot').nonGoalConflicts, workspace.metrics.nonGoalConflicts);
assert.ok(!nonGoal.buildOrder.doFirst.includes('workspace-autopilot'));
assert.ok(!nonGoal.buildOrder.validateNext.includes('workspace-autopilot'));
assert.ok(!nonGoal.handoff.warnings.some(item => /active item workspace-autopilot conflicts/.test(item)), 'conflicted candidates should be demoted before handoff warnings need to flag active-lane conflict');
const mixedWrapperResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_mixed_wrappers',
originalPrompt: 'The bridge export contains a normalized feature list plus Concept Map validation and parking sections.',
idea: 'Ranker should not lose sectioned Concept Map context when a partial feature wrapper is also present.',
mode: 'mvp',
featureSet: {
features: [
{ id: 'manual-bridge-proof', title: 'Manual bridge proof', description: 'Turn one Concept Map into a defended build-order preview.', evidenceNeeded: 'Can one tired user act on the preview?', recommendedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
],
},
conceptMap: {
validateNext: [
{ id: 'copyable-brief-test', action: 'Copyable brief test', why: 'Check whether the decision brief remains useful outside Ranker.', evidence: 'Can the user paste it into notes and still know the next move?' },
],
parkingLot: [
{ id: 'account-dashboard', action: 'Account dashboard', why: 'Saved workspaces, auth, billing, and team sync.', evidence: 'No bridge proof yet' },
],
},
}),
});
assert.equal(mixedWrapperResponse.status, 200);
const mixedWrapper = await mixedWrapperResponse.json();
assert.equal(mixedWrapper.input.optionCount, 3);
assert.equal(mixedWrapper.ranked[0].id, 'manual-bridge-proof');
assert.equal(mixedWrapper.handoff.itemTrace.find(item => item.id === 'manual-bridge-proof').sourceSection, 'feature-set.features');
assert.equal(mixedWrapper.ranked.find(item => item.id === 'copyable-brief-test').lane.id, 'test');
assert.equal(mixedWrapper.handoff.itemTrace.find(item => item.id === 'copyable-brief-test').sourceSection, 'concept-map.validateNext');
assert.equal(mixedWrapper.ranked.find(item => item.id === 'account-dashboard').lane.id, 'park');
assert.equal(mixedWrapper.handoff.itemTrace.find(item => item.id === 'account-dashboard').sourceSection, 'concept-map.parkingLot');
assert.deepEqual(mixedWrapper.handoff.warnings, []);
const laneAliasResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_lane_aliases',
originalPrompt: 'Scattermind exported normalized candidates with lane fields instead of recommendedLane.',
idea: 'Ranker should treat lane as a lane hint when it is do-first/validate-next/defer/park, while preserving the actual source section.',
mode: 'mvp',
featureSet: {
features: [
{ id: 'manual-bridge-proof', title: 'Manual bridge proof', description: 'Defended build order from one Concept Map.', evidenceNeeded: 'Can one tired user act on the first move?', lane: 'do-first', sourceSection: 'concept-map.nextActions', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'copyable-decision-brief', title: 'Copyable decision brief', description: 'Export the defended order as plain text.', evidenceNeeded: 'Does the copied brief preserve the next action?', lane: 'validate-next', sourceSection: 'concept-map.nextActions' },
{ id: 'auth-workspace', title: 'Auth workspace', description: 'Accounts, dashboard, saved projects, and collaboration.', evidenceNeeded: 'No bridge proof yet.', lane: 'park', sourceSection: 'concept-map.parkingLot' },
],
},
}),
});
assert.equal(laneAliasResponse.status, 200);
const laneAlias = await laneAliasResponse.json();
assert.equal(laneAlias.ranked[0].id, 'manual-bridge-proof');
assert.equal(laneAlias.ranked.find(item => item.id === 'manual-bridge-proof').provenance.sourceSection, 'concept-map.nextActions');
assert.equal(laneAlias.ranked.find(item => item.id === 'copyable-decision-brief').lane.id, 'test');
assert.equal(laneAlias.ranked.find(item => item.id === 'copyable-decision-brief').lane.source, 'hint');
assert.equal(laneAlias.ranked.find(item => item.id === 'auth-workspace').lane.id, 'park');
assert.equal(laneAlias.ranked.find(item => item.id === 'auth-workspace').lane.source, 'hint');
assert.equal(laneAlias.handoff.itemTrace.find(item => item.id === 'auth-workspace').sourceSection, 'concept-map.parkingLot');
assert.deepEqual(laneAlias.handoff.warnings, []);
const duplicateIdResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_duplicate_ids',
originalPrompt: 'I pasted duplicated next-move IDs from a messy Concept Map export.',
idea: 'Ranker should keep handoff rows addressable even if Scattermind sends duplicate candidate IDs.',
mode: 'mvp',
conceptMap: {
nextActions: [
{ id: 'preview', action: 'Manual build-order preview', why: 'Defends the first move without dashboard machinery.', evidence: 'Can one user act on the preview?', suggestedLane: 'do-first' },
{ id: 'preview', action: 'Copyable preview brief', why: 'Exports the defended order into notes.', evidence: 'Does the copied brief preserve the next action?', suggestedLane: 'validate-next' },
{ id: 'workspace', action: 'Saved workspace', why: 'Auth dashboard for every idea.', evidence: 'None yet', suggestedLane: 'park' },
],
},
}),
});
assert.equal(duplicateIdResponse.status, 200);
const duplicateIds = await duplicateIdResponse.json();
assert.deepEqual(duplicateIds.ranked.map(item => item.id).sort(), ['preview', 'preview-2', 'workspace']);
assert.equal(new Set(duplicateIds.handoff.itemTrace.map(item => item.id)).size, duplicateIds.handoff.itemTrace.length);
assert.equal(duplicateIds.handoff.itemTrace.find(item => item.id === 'preview-2').originalId, 'preview');
assert.equal(duplicateIds.handoff.itemTrace.find(item => item.id === 'preview-2').idNormalized, true);
assert.ok(duplicateIds.handoff.warnings.some(item => /duplicate source id preview normalized to preview-2/.test(item)));
assert.ok(Object.values(duplicateIds.buildOrder).flat().includes('preview-2'));
assert.equal(duplicateIds.handoff.readiness.status, 'usable-with-warnings');
assert.ok(duplicateIds.handoff.readiness.nextChecks.some(item => /duplicate IDs/i.test(item)));
const structuredContextResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_structured_context',
originalPrompt: 'Scattermind sent a structured context object instead of a flat context string.',
idea: 'Ranker should preserve structured context and not turn it into [object Object].',
mode: 'mvp',
context: {
summary: 'Bridge proof for a tired non-AI-native user.',
targetAudience: 'Tired non-AI-native solo builder',
constraints: ['No account before first value', 'Use a copyable artifact first'],
nonGoals: ['Avoid auth dashboard', 'Avoid saved workspaces'],
assumptions: ['Manual proof is acceptable for the first pass'],
},
conceptMap: {
nextActions: [
{ id: 'copyable-bridge-artifact', action: 'Copyable bridge artifact', why: 'Turn the source Concept Map into one defended build-order brief.', evidence: 'Can the user paste it and know the first move?', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'auth-dashboard', action: 'Auth dashboard', why: 'Accounts and saved workspaces for every idea.', evidence: 'None yet', suggestedLane: 'do-first', rankerHints: { value: 10, effort: 2, confidence: 9, urgency: 9, risk: 2 } },
],
},
}),
});
assert.equal(structuredContextResponse.status, 200);
const structuredContext = await structuredContextResponse.json();
assert.doesNotMatch(structuredContext.input.context, /\[object Object\]/);
assert.match(structuredContext.input.context, /Target audience: Tired non-AI-native solo builder/);
assert.deepEqual(structuredContext.input.decisionContext.constraints, ['No account before first value', 'Use a copyable artifact first']);
assert.deepEqual(structuredContext.handoff.decisionContext.nonGoals, ['Avoid auth dashboard', 'Avoid saved workspaces']);
assert.equal(structuredContext.ranked[0].id, 'copyable-bridge-artifact');
assert.equal(structuredContext.ranked.find(item => item.id === 'auth-dashboard').lane.source, 'source-non-goal');
const lensOnlyResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_lens_only',
originalPrompt: 'Scattermind produced the current paid Concept Map shape: lenses with a Build Order paragraph, not arrays.',
idea: 'Ranker should turn the Build Order lens into rank-ready candidates without asking Scattermind to rename fields first.',
mode: 'mvp',
conceptMap: {
snapshotTitle: 'Lens-only Concept Map bridge',
lenses: {
risk: {
title: 'What Can Mislead You',
content: 'Avoid saved workspaces and auth dashboard before one manual proof. Do not build billing yet.',
},
channel: {
title: 'Build Order',
content: 'Build first: Manual build-order preview from one Concept Map - prove the bridge before adding product machinery. Test manually: Copyable decision brief - show it to 3 tired users and ask what they would do next. Defer: Visual polish pass after the rough artifact is understood. Probably noise: Saved workspace dashboard with auth, billing, and team collaboration.',
},
},
},
}),
});
assert.equal(lensOnlyResponse.status, 200);
const lensOnly = await lensOnlyResponse.json();
assert.equal(lensOnly.input.optionCount, 4);
assert.equal(lensOnly.ranked[0].id, 'build-order-1');
assert.equal(lensOnly.ranked[0].provenance.sourceSection, 'concept-map.lenses.channel');
assert.equal(lensOnly.ranked[0].provenance.sourceId, 'concept-map.lenses.channel#1');
assert.equal(lensOnly.ranked[0].provenance.sourceTitle, 'Build Order');
assert.match(lensOnly.ranked[0].provenance.sourceQuote, /Build first: Manual build-order preview/);
assert.equal(lensOnly.brief.quickGlance.sourceTrace.sourceId, 'concept-map.lenses.channel#1');
assert.equal(lensOnly.ranked.find(item => item.id === 'build-order-2').lane.id, 'test');
assert.equal(lensOnly.ranked.find(item => item.id === 'build-order-3').lane.id, 'defer');
assert.equal(lensOnly.ranked.find(item => item.id === 'build-order-4').lane.id, 'park');
assert.ok(lensOnly.input.decisionContext.nonGoals.includes('Avoid saved workspaces and auth dashboard before one manual proof'));
assert.ok(lensOnly.input.decisionContext.nonGoals.includes('Do not build billing yet'));
assert.ok(lensOnly.ranked.find(item => item.id === 'build-order-4').metrics.nonGoalConflicts.length >= 1);
assert.deepEqual(lensOnly.handoff.warnings, []);
const scattermindPaidShapeResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
reference_code: 'SM-PAID1',
working_name: 'Neighborhood Supper Club',
ideaText: 'I want to test a tiny paid supper club before building a food community app.',
context: 'Solo operator. Manual proof first. Avoid accounts and saved workspaces before anyone pays.',
mode: 'mvp',
lenses: {
audience: 'First buyers are local neighbors who already attend small food events and want a low-pressure dinner plan.',
risk: 'Avoid accounts and saved workspaces before anyone pays. Do not build subscriptions or an app dashboard yet.',
channel: 'Build first: One manual supper-club offer page - collect three real yes/no replies before building machinery. Test manually: Concierge invitation script - ask ten neighbors if they would pay for the first dinner. Defer: Pretty event calendar after the first paid table. Probably noise: Saved member workspace with accounts, billing, subscriptions, and team dashboard.',
},
}),
});
assert.equal(scattermindPaidShapeResponse.status, 200);
const scattermindPaidShape = await scattermindPaidShapeResponse.json();
assert.equal(scattermindPaidShape.input.provenance.artifactId, 'SM-PAID1');
assert.equal(scattermindPaidShape.input.provenance.snapshotTitle, 'Neighborhood Supper Club');
assert.match(scattermindPaidShape.input.provenance.originalPrompt, /supper club/);
assert.equal(scattermindPaidShape.input.optionCount, 4);
assert.equal(scattermindPaidShape.ranked[0].id, 'build-order-1');
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceId, 'concept-map.lenses.channel#1');
assert.equal(scattermindPaidShape.ranked[0].provenance.sourceTitle, 'Build Order');
assert.match(scattermindPaidShape.handoff.itemTrace.find(item => item.id === 'build-order-1').sourceQuote, /One manual supper-club offer page/);
assert.equal(scattermindPaidShape.ranked.find(item => item.id === 'build-order-4').lane.id, 'park');
assert.ok(scattermindPaidShape.input.decisionContext.nonGoals.includes('Avoid accounts and saved workspaces before anyone pays'));
assert.ok(scattermindPaidShape.ranked.find(item => item.id === 'build-order-4').metrics.nonGoalConflicts.length >= 1);
assert.deepEqual(scattermindPaidShape.handoff.warnings, []);
const softLabelLensResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
reference_code: 'SM-SOFT-LABELS',
working_name: 'Gentle Scattermind continuation labels',
ideaText: 'Scattermind avoids verdict language but still needs to hand Ranker a continuation order.',
context: 'Solo builder. Manual proof before machinery. Avoid accounts and saved workspaces.',
mode: 'mvp',
lenses: {
risk: 'Avoid accounts and saved workspaces until one copyable result makes sense.',
channel: 'Continue first: One source-traced build order preview - make the next move tangible without a dashboard. Try next: Copyable decision brief - ask three tired users what they would do next. Hold for later: Visual polish after the rough brief works. Set aside: Saved workspace with accounts, auth, billing, and collaboration.',
},
}),
});
assert.equal(softLabelLensResponse.status, 200);
const softLabelLens = await softLabelLensResponse.json();
assert.equal(softLabelLens.input.optionCount, 4);
assert.equal(softLabelLens.ranked[0].id, 'build-order-1');
assert.equal(softLabelLens.ranked[0].lane.id, 'do');
assert.match(softLabelLens.ranked[0].provenance.sourceQuote, /Continue first: One source-traced build order preview/);
assert.equal(softLabelLens.ranked.find(item => item.id === 'build-order-2').lane.id, 'test');
assert.equal(softLabelLens.ranked.find(item => item.id === 'build-order-3').lane.id, 'defer');
assert.equal(softLabelLens.ranked.find(item => item.id === 'build-order-4').lane.id, 'park');
assert.equal(softLabelLens.handoff.readiness.status, 'ready');
assert.deepEqual(softLabelLens.handoff.warnings, []);
const summaryOnlyConceptMapResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
reference_code: 'SM-SUMMARY-ONLY',
working_name: 'Tired maker continuation pass',
opening_reflection: 'A tired maker needs one defended first-week build order after Scattermind clarified the idea.',
context: 'Solo builder. Manual proof first. Avoid accounts and saved workspaces before the build order lands.',
mode: 'mvp',
lenses: {
risk: 'Avoid accounts and saved workspaces before the first copyable result works.',
channel: 'First week: Manual source-traced preview - turn the Concept Map into one defended next move. Evidence next: Copyable handoff comprehension check - ask three tired users what they would do next. Hold for later: Visual polish after comprehension proof. Set aside: Saved account workspace with dashboard and billing.',
},
}),
});
assert.equal(summaryOnlyConceptMapResponse.status, 200);
const summaryOnlyConceptMap = await summaryOnlyConceptMapResponse.json();
assert.equal(summaryOnlyConceptMap.input.idea, 'A tired maker needs one defended first-week build order after Scattermind clarified the idea.');
assert.equal(summaryOnlyConceptMap.input.provenance.artifactId, 'SM-SUMMARY-ONLY');
assert.equal(summaryOnlyConceptMap.input.provenance.snapshotTitle, 'Tired maker continuation pass');
assert.match(summaryOnlyConceptMap.input.provenance.sourceSummary, /first-week build order/);
assert.equal(summaryOnlyConceptMap.input.provenance.originalPrompt, '');
assert.equal(summaryOnlyConceptMap.ranked[0].lane.id, 'do');
assert.match(summaryOnlyConceptMap.ranked[0].provenance.sourceQuote, /First week: Manual source-traced preview/);
assert.equal(summaryOnlyConceptMap.ranked.find(item => item.id === 'build-order-2').lane.id, 'test');
assert.equal(summaryOnlyConceptMap.handoff.source.hasOriginalPrompt, false);
assert.equal(summaryOnlyConceptMap.handoff.source.hasSourceSummary, true);
assert.equal(summaryOnlyConceptMap.handoff.readiness.status, 'ready');
assert.equal(summaryOnlyConceptMap.handoff.readiness.sourceComplete, true);
assert.deepEqual(summaryOnlyConceptMap.handoff.warnings, []);
assert.match(summaryOnlyConceptMap.handoff.copyableText, /Source summary: A tired maker needs one defended first-week build order/);
assert.match(summaryOnlyConceptMap.brief.source.sourceSummaryExcerpt, /first-week build order/);
const softLabelObjectResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_soft_object_labels',
originalPrompt: 'Scattermind exported soft continuation sections instead of verdict-named lanes.',
idea: 'Ranker should accept softer object keys without forcing Scattermind to say build/kill.',
mode: 'mvp',
conceptMap: {
buildOrder: {
continueFirst: [{ id: 'manual-preview', move: 'Manual continuation preview', evidenceQuestion: 'Can one user explain the next move?' }],
evidenceNext: ['Copyable brief comprehension check'],
holdForLater: ['Visual system polish'],
setAside: ['Account workspace dashboard'],
},
},
}),
});
assert.equal(softLabelObjectResponse.status, 200);
const softLabelObject = await softLabelObjectResponse.json();
assert.equal(softLabelObject.input.optionCount, 4);
assert.equal(softLabelObject.buildOrder.doFirst[0], 'manual-preview');
assert.equal(softLabelObject.ranked.find(item => item.id === 'feature-1').lane.id, 'test');
assert.equal(softLabelObject.ranked.find(item => item.title === 'Visual system polish').lane.id, 'defer');
assert.equal(softLabelObject.ranked.find(item => item.title === 'Account workspace dashboard').lane.id, 'park');
const mergedContextResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_merged_context',
originalPrompt: 'Scattermind sent summary context plus deeper Concept Map guardrails and lenses.',
idea: 'Ranker should merge context sources instead of letting a shallow top-level summary shadow Concept Map constraints.',
mode: 'mvp',
context: { summary: 'Continuation bridge for an overwhelmed non-AI-native operator.' },
conceptMap: {
context: {
targetAudience: 'Overwhelmed non-AI-native shop owner',
constraints: ['Manual proof before saved projects'],
nonGoals: ['Avoid workspace dashboard'],
assumptions: ['A copyable brief is enough for the first pass'],
},
lenses: {
constraints: { content: 'No auth before first value. Keep the result copyable.' },
assumptions: { content: 'The user is tired; one defended next move beats a backlog.' },
},
nextActions: [
{ id: 'defended-next-move', action: 'Defended next-move brief', why: 'One copyable build order with source provenance.', evidence: 'Can the shop owner name the next step?', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'workspace-dashboard', action: 'Workspace dashboard', why: 'Accounts, saved projects, and auth dashboard.', evidence: 'No proof yet', suggestedLane: 'do-first', rankerHints: { value: 10, effort: 2, confidence: 9, urgency: 9, risk: 2 } },
],
},
}),
});
assert.equal(mergedContextResponse.status, 200);
const mergedContext = await mergedContextResponse.json();
assert.equal(mergedContext.input.decisionContext.targetAudience, 'Overwhelmed non-AI-native shop owner');
assert.ok(mergedContext.input.decisionContext.constraints.includes('Manual proof before saved projects'));
assert.ok(mergedContext.input.decisionContext.constraints.includes('No auth before first value'));
assert.ok(mergedContext.input.decisionContext.nonGoals.includes('Avoid workspace dashboard'));
assert.ok(mergedContext.input.decisionContext.assumptions.includes('A copyable brief is enough for the first pass'));
assert.ok(mergedContext.input.decisionContext.assumptions.includes('The user is tired'));
assert.equal(mergedContext.ranked[0].id, 'defended-next-move');
assert.equal(mergedContext.ranked.find(item => item.id === 'workspace-dashboard').lane.source, 'source-non-goal');
assert.deepEqual(mergedContext.handoff.warnings, []);
const objectBuildOrderResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_object_build_order',
originalPrompt: 'Scattermind exported a Build Order object instead of nextActions arrays.',
idea: 'Ranker should accept an already-laned Build Order object and preserve lane provenance.',
mode: 'mvp',
conceptMap: {
snapshotTitle: 'Object Build Order bridge',
context: {
targetAudience: 'Tired local service seller',
constraints: ['Copyable output before accounts'],
nonGoals: ['Avoid saved workspace dashboard'],
},
buildOrder: {
doFirst: [
{ id: 'manual-offer-proof', move: 'Manual offer proof', why: 'Send one copyable offer to three real prospects.', questionToAnswer: 'Will one prospect ask for the next step?', nextAction: 'Send the offer to three named prospects before touching the page design.', successSignal: 'One prospect asks how to buy or book the next step.', killSignal: 'All three prospects understand it but none want the next step.' },
],
validateNext: [
'Copyable follow-up script',
],
deferred: [
{ id: 'visual-refresh', title: 'Visual refresh after proof', evidenceQuestion: 'Does the rough brief work first?' },
],
parkingLot: [
{ id: 'workspace-dashboard', title: 'Saved workspace dashboard', description: 'Accounts, auth, and saved projects for every offer.', evidenceNeeded: 'No proof yet' },
],
},
},
}),
});
assert.equal(objectBuildOrderResponse.status, 200);
const objectBuildOrder = await objectBuildOrderResponse.json();
assert.equal(objectBuildOrder.input.optionCount, 4);
assert.equal(objectBuildOrder.ranked[0].id, 'manual-offer-proof');
assert.equal(objectBuildOrder.ranked[0].factors.evidenceNeeded, 'Will one prospect ask for the next step?');
assert.equal(objectBuildOrder.ranked[0].nextStep, 'Send the offer to three named prospects before touching the page design.');
assert.equal(objectBuildOrder.ranked[0].successSignal, 'One prospect asks how to buy or book the next step.');
assert.equal(objectBuildOrder.ranked[0].killSignal, 'All three prospects understand it but none want the next step.');
assert.equal(objectBuildOrder.buildOrderDetails.doFirst[0].successSignal, 'One prospect asks how to buy or book the next step.');
assert.equal(objectBuildOrder.handoff.itemTrace.find(item => item.id === 'manual-offer-proof').nextStep, 'Send the offer to three named prospects before touching the page design.');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'feature-1').title, 'Copyable follow-up script');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'feature-1').provenance.sourceId, 'concept-map.buildOrder.validateNext#1');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'feature-1').provenance.sourceQuote, 'Copyable follow-up script');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'feature-1').lane.id, 'test');
assert.equal(objectBuildOrder.handoff.itemTrace.find(item => item.id === 'feature-1').sourceSection, 'concept-map.buildOrder.validateNext');
assert.equal(objectBuildOrder.handoff.itemTrace.find(item => item.id === 'feature-1').sourceId, 'concept-map.buildOrder.validateNext#1');
assert.equal(objectBuildOrder.handoff.itemTrace.find(item => item.id === 'feature-1').sourceQuote, 'Copyable follow-up script');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'workspace-dashboard').lane.id, 'park');
assert.equal(objectBuildOrder.ranked.find(item => item.id === 'workspace-dashboard').lane.source, 'hint');
assert.ok(objectBuildOrder.handoff.warnings.includes('missing evidence needed for active item feature-1'));
assert.equal(objectBuildOrder.handoff.readiness.status, 'needs-source-context');
assert.ok(objectBuildOrder.handoff.readiness.blockers.includes('missing evidence needed for active item feature-1'));
assert.ok(objectBuildOrder.handoff.readiness.nextChecks.some(item => /evidenceNeeded/i.test(item)));
const embeddedJsonResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
idea: JSON.stringify({
reference_code: 'SM-PASTE1',
working_name: 'Raw pasted Concept Map',
ideaText: 'The user pasted a raw Scattermind Concept Map JSON blob into the public Ranker form.',
lenses: {
risk: 'Avoid saved workspaces before one copyable result lands.',
channel: 'Build first: Raw JSON handoff detection - turn the pasted Concept Map into a defended build order. Test manually: Copy the decision brief into notes and check whether the first move survives. Probably noise: Saved workspace with accounts and team dashboard.',
},
}),
mode: 'mvp',
context: '',
}),
});
assert.equal(embeddedJsonResponse.status, 200);
const embeddedJson = await embeddedJsonResponse.json();
assert.equal(embeddedJson.input.embeddedPayloadSource, 'idea');
assert.equal(embeddedJson.input.provenance.artifactId, 'SM-PASTE1');
assert.equal(embeddedJson.input.optionCount, 3);
assert.equal(embeddedJson.ranked[0].id, 'build-order-1');
assert.equal(embeddedJson.ranked.find(item => item.id === 'build-order-3').lane.id, 'park');
assert.ok(embeddedJson.input.decisionContext.nonGoals.includes('Avoid saved workspaces before one copyable result lands'));
assert.deepEqual(embeddedJson.handoff.warnings, []);
const fencedJsonResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
optionsText: `Here is the Scattermind export I copied:\n\n\`\`\`json\n${JSON.stringify({
reference_code: 'SM-FENCE1',
working_name: 'Fenced pasted Concept Map',
ideaText: 'The user copied a fenced Scattermind JSON export plus surrounding prose.',
lenses: {
risk: 'Avoid saved workspaces and account dashboard before the first manual proof.',
channel: 'Build first: Fenced JSON handoff - detect the pasted Concept Map and defend the first move. Test manually: Copy the ranked brief into notes and ask one user what they would do next. Probably noise: Account dashboard with saved workspaces and collaboration.',
},
})}\n\`\`\`\n\nPlease rank this.`,
mode: 'mvp',
}),
});
assert.equal(fencedJsonResponse.status, 200);
const fencedJson = await fencedJsonResponse.json();
assert.equal(fencedJson.input.embeddedPayloadSource, 'optionsText');
assert.equal(fencedJson.input.provenance.artifactId, 'SM-FENCE1');
assert.equal(fencedJson.input.optionCount, 3);
assert.equal(fencedJson.ranked[0].id, 'build-order-1');
assert.equal(fencedJson.ranked.find(item => item.id === 'build-order-3').lane.id, 'park');
assert.ok(fencedJson.input.decisionContext.nonGoals.includes('Avoid saved workspaces and account dashboard before the first manual proof'));
assert.deepEqual(fencedJson.handoff.warnings, []);
const embeddedSnapshotResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
idea: `Pasted Snapshot export:\n${JSON.stringify({
snapshot: {
id: 'SNAP-PASTE1',
title: 'Schema-light Snapshot paste',
originalPrompt: 'I only have a Snapshot JSON export and need the first continuation move.',
context: {
targetAudience: 'Tired solo builder',
constraints: ['Manual proof first'],
nonGoals: ['Avoid account dashboard'],
},
nextActions: [
{ id: 'manual-snapshot-preview', action: 'Manual Snapshot build-order preview', why: 'Turn the Snapshot into one defended first move.', evidence: 'Can one user say what to do next?', suggestedLane: 'do-first' },
{ id: 'copyable-snapshot-brief', action: 'Copyable Snapshot brief', why: 'Let the user paste the defended order elsewhere.', evidence: 'Does the pasted brief preserve the next action?', suggestedLane: 'validate-next' },
{ id: 'account-dashboard', action: 'Account dashboard', why: 'Saved projects and auth dashboard for every Snapshot.', evidence: 'No proof yet', suggestedLane: 'park' },
],
},
})}`,
mode: 'mvp',
}),
});
assert.equal(embeddedSnapshotResponse.status, 200);
const embeddedSnapshot = await embeddedSnapshotResponse.json();
assert.equal(embeddedSnapshot.input.embeddedPayloadSource, 'idea');
assert.equal(embeddedSnapshot.input.provenance.artifactId, 'SNAP-PASTE1');
assert.equal(embeddedSnapshot.input.provenance.snapshotTitle, 'Schema-light Snapshot paste');
assert.equal(embeddedSnapshot.input.optionCount, 3);
assert.equal(embeddedSnapshot.ranked[0].id, 'manual-snapshot-preview');
assert.equal(embeddedSnapshot.ranked.find(item => item.id === 'manual-snapshot-preview').provenance.sourceSection, 'snapshot.nextActions');
assert.equal(embeddedSnapshot.ranked.find(item => item.id === 'account-dashboard').lane.id, 'park');
assert.deepEqual(embeddedSnapshot.handoff.warnings, []);
const sourceExcerptResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_source_excerpts',
originalPrompt: 'Scattermind exported source excerpts for each recommended move.',
idea: 'Ranker should preserve source excerpts so the defended build order can point back to the exact Concept Map note.',
mode: 'mvp',
conceptMap: {
nextActions: [
{ id: 'manual-proof', action: 'Manual proof preview', why: 'Show one defended next move before adding machinery.', evidence: 'Can one tired user act on it?', sourceItemId: 'lens-channel-1', sourceTitle: 'Build Order', sourceExcerpt: 'Build first: show one defended next move before adding machinery.', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'copyable-brief', action: 'Copyable brief', why: 'Let the user paste the defended order into notes.', evidence: 'Does the pasted brief preserve the first move?', sourceItemId: 'lens-channel-2', lensTitle: 'Build Order', quote: 'Test manually: copy the ranked brief into notes.', suggestedLane: 'validate-next' },
{ id: 'saved-workspace', action: 'Saved workspace', why: 'Accounts and saved projects for every idea.', evidence: 'No bridge proof yet.', sourceItemId: 'lens-risk-1', sourceHeading: 'What can mislead you', sourceQuote: 'Probably noise: saved workspace with accounts before proof.', suggestedLane: 'park' },
],
},
}),
});
assert.equal(sourceExcerptResponse.status, 200);
const sourceExcerpt = await sourceExcerptResponse.json();
assert.equal(sourceExcerpt.ranked[0].id, 'manual-proof');
assert.equal(sourceExcerpt.ranked[0].provenance.sourceId, 'lens-channel-1');
assert.equal(sourceExcerpt.ranked[0].provenance.sourceTitle, 'Build Order');
assert.match(sourceExcerpt.ranked[0].provenance.sourceQuote, /Build first/);
assert.match(sourceExcerpt.brief.source.originalPromptExcerpt, /source excerpts/);
assert.equal(sourceExcerpt.brief.quickGlance.sourceTrace.sourceId, 'lens-channel-1');
assert.match(sourceExcerpt.brief.quickGlance.sourceTrace.sourceQuote, /defended next move/);
assert.equal(sourceExcerpt.handoff.source.originalPromptExcerpt, 'Scattermind exported source excerpts for each recommended move.');
assert.equal(sourceExcerpt.buildOrderDetails.doFirst[0].sourceId, 'lens-channel-1');
assert.match(sourceExcerpt.buildOrderDetails.doFirst[0].sourceQuote, /defended next move/);
assert.equal(sourceExcerpt.handoff.itemTrace.find(item => item.id === 'copyable-brief').sourceTitle, 'Build Order');
assert.match(sourceExcerpt.handoff.itemTrace.find(item => item.id === 'saved-workspace').sourceQuote, /Probably noise/);
assert.deepEqual(sourceExcerpt.handoff.warnings, []);
assert.equal(sourceExcerpt.handoff.readiness.status, 'ready');
assert.equal(sourceExcerpt.handoff.readiness.activeItemCount, 2);
const snakeCaseBridgeResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'scattermind-concept-map-v1',
source_name: 'Scattermind',
source_artifact_id: 'SM-SNAKE-1',
snapshot_title: 'Snake-case bridge export',
concept_map_id: 'CM-SNAKE-1',
original_prompt: 'I have a clarified idea and need a defended build order from snake_case JSON.',
context: {
target_audience: 'Tired non-AI-native solo builder',
constraints: ['Manual proof before product shell'],
non_goals: ['Avoid account dashboards and saved workspaces before proof'],
},
concept_map: {
next_actions: [
{ id: 'manual-bridge-proof', action: 'Manual bridge proof preview', why: 'Show the defended first move with source trace.', evidence_needed: 'Can one tired user say why this wins?', proof_steps: ['Create one static preview'], recommended_lane: 'do-first', source_section: 'concept-map.next_actions', source_item_id: 'snake-next-1', source_title: 'Build order lens', source_excerpt: 'Build first: manual bridge proof preview.', ranker_hints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'copyable-handoff', action: 'Copyable handoff brief', why: 'Preserve the decision outside the app.', evidence_needed: 'Does the copied brief retain source and next step?', suggested_lane: 'validate-next', source_section: 'concept-map.next_actions', source_item_id: 'snake-next-2' },
],
parking_lot: [
{ id: 'saved-workspace', action: 'Saved workspace dashboard', why: 'Accounts, dashboards, and saved projects for every idea.', evidence_needed: 'No proof yet.', suggested_lane: 'park', source_section: 'concept-map.parking_lot', source_item_id: 'snake-park-1' },
],
},
}),
});
assert.equal(snakeCaseBridgeResponse.status, 200);
const snakeCaseBridge = await snakeCaseBridgeResponse.json();
assert.equal(snakeCaseBridge.input.provenance.artifactId, 'SM-SNAKE-1');
assert.equal(snakeCaseBridge.input.provenance.snapshotTitle, 'Snake-case bridge export');
assert.equal(snakeCaseBridge.input.provenance.conceptMapId, 'CM-SNAKE-1');
assert.equal(snakeCaseBridge.input.provenance.originalPrompt, 'I have a clarified idea and need a defended build order from snake_case JSON.');
assert.equal(snakeCaseBridge.input.decisionContext.targetAudience, 'Tired non-AI-native solo builder');
assert.ok(snakeCaseBridge.input.decisionContext.nonGoals.includes('Avoid account dashboards and saved workspaces before proof'));
assert.equal(snakeCaseBridge.ranked[0].id, 'manual-bridge-proof');
assert.equal(snakeCaseBridge.ranked[0].provenance.sourceSection, 'concept-map.next_actions');
assert.equal(snakeCaseBridge.ranked[0].provenance.sourceId, 'snake-next-1');
assert.equal(snakeCaseBridge.ranked[0].factors.evidenceNeeded, 'Can one tired user say why this wins?');
assert.ok(snakeCaseBridge.ranked[0].factors.proofSteps.includes('Create one static preview'));
assert.ok(snakeCaseBridge.ranked[0].factors.metricHints.value >= 9);
assert.equal(snakeCaseBridge.ranked.find(item => item.id === 'saved-workspace').lane.id, 'park');
assert.equal(snakeCaseBridge.handoff.readiness.status, 'ready');
assert.deepEqual(snakeCaseBridge.handoff.warnings, []);
const nextStepsAliasResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'concept_map_next_steps_aliases',
originalPrompt: 'Scattermind exported next_steps / recommended_next_steps rather than nextActions.',
idea: 'Ranker should keep schema-light continuation exports rankable without asking Scattermind to rename step fields.',
mode: 'mvp',
context: {
targetAudience: 'Tired non-AI-native solo builder',
nonGoals: ['Avoid saved workspaces before the first proof'],
},
concept_map: {
next_steps: [
{ id: 'manual-step-preview', next_step: 'Manual step preview', why: 'Turn one Concept Map into a defended first move.', evidence_needed: 'Can one tired user explain the first move?', suggested_lane: 'do-first', ranker_hints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'copyable-followup', recommended_next_step: 'Copyable follow-up brief', why: 'Let the build order survive outside Ranker.', evidence_needed: 'Does the brief preserve reason and source?', suggested_lane: 'validate-next' },
],
recommended_next_steps: [],
parking_lot: [
{ id: 'saved-workspace', next_step: 'Saved workspace dashboard', why: 'Accounts and saved projects for every idea.', evidence_needed: 'No proof yet.', suggested_lane: 'park' },
],
},
}),
});
assert.equal(nextStepsAliasResponse.status, 200);
const nextStepsAlias = await nextStepsAliasResponse.json();
assert.equal(nextStepsAlias.input.optionCount, 3);
assert.equal(nextStepsAlias.ranked[0].id, 'manual-step-preview');
assert.equal(nextStepsAlias.ranked[0].title, 'Manual step preview');
assert.equal(nextStepsAlias.ranked[0].provenance.sourceSection, 'concept-map.nextActions');
assert.equal(nextStepsAlias.ranked.find(item => item.id === 'copyable-followup').title, 'Copyable follow-up brief');
assert.equal(nextStepsAlias.ranked.find(item => item.id === 'copyable-followup').lane.id, 'test');
assert.equal(nextStepsAlias.ranked.find(item => item.id === 'saved-workspace').lane.id, 'park');
assert.deepEqual(nextStepsAlias.handoff.warnings, []);
const summaryGuardrailResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sourceName: 'Scattermind',
artifactId: 'context_summary_guardrails',
originalPrompt: 'Scattermind exported guardrails in context summaries rather than explicit nonGoals arrays.',
mode: 'mvp',
context: {
summary: 'Solo builder. Manual proof first. No account dashboards or saved workspaces before the first useful result.',
targetAudience: 'Tired non-AI-native solo builder',
},
conceptMap: {
context: {
summary: 'Avoid billing, auth, and collaboration until the bridge proves one defended build order.',
},
nextActions: [
{ id: 'manual-summary-proof', action: 'Manual summary-context build order preview', why: 'Show one defended first move from a Concept Map.', evidenceNeeded: 'Can one tired user name the first move?', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'workspace-summary', action: 'Saved workspace and account dashboard', why: 'Auth-backed saved projects and collaboration for every idea.', evidenceNeeded: 'No bridge proof yet.', rankerHints: { value: 10, effort: 1, confidence: 10, urgency: 10, risk: 1 } },
{ id: 'billing-summary', action: 'Billing and subscription admin', why: 'Checkout, invoices, plans, and account controls.', evidenceNeeded: 'No buyer proof yet.', rankerHints: { value: 10, effort: 1, confidence: 10, urgency: 10, risk: 1 } },
],
},
}),
});
assert.equal(summaryGuardrailResponse.status, 200);
const summaryGuardrail = await summaryGuardrailResponse.json();
assert.ok(summaryGuardrail.input.decisionContext.nonGoals.includes('No account dashboards or saved workspaces before the first useful result'));
assert.ok(summaryGuardrail.input.decisionContext.nonGoals.includes('Avoid billing, auth, and collaboration until the bridge proves one defended build order'));
assert.equal(summaryGuardrail.ranked[0].id, 'manual-summary-proof');
assert.equal(summaryGuardrail.ranked.find(item => item.id === 'workspace-summary').lane.source, 'source-non-goal');
assert.equal(summaryGuardrail.ranked.find(item => item.id === 'billing-summary').lane.source, 'source-non-goal');
assert.deepEqual(summaryGuardrail.handoff.warnings, []);
const bridgeEnvelopeResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'scattermind-ranker-bridge-v1',
rankerInput: {
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
reference_code: 'SM-BRIDGE-ENV-1',
working_name: 'Envelope bridge export',
ideaText: 'A paid Concept Map clarified the idea and now needs a defended build order.',
context: {
targetAudience: 'Tired solo operator',
constraints: ['Manual source-traced proof before product shell'],
avoid: ['Avoid accounts, billing, and saved workspaces before bridge proof'],
},
candidateSet: {
nextActions: [
{ id: 'envelope-manual-proof', action: 'Envelope manual build-order proof', why: 'Proves a wrapped Scattermind export can become a Rank-ready first move.', evidenceNeeded: 'Can one wrapped Concept Map produce a traceable Do first lane?', sourceSection: 'rankerInput.candidateSet.nextActions', sourceItemId: 'env-next-1', suggestedLane: 'do-first', rankerHints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
{ id: 'envelope-copyable-brief', action: 'Envelope copyable decision brief', why: 'Keeps provenance and next step portable.', evidenceNeeded: 'Does the copyable handoff include the wrapped source?', sourceSection: 'rankerInput.candidateSet.nextActions', sourceItemId: 'env-next-2', suggestedLane: 'validate-next' },
],
parkingLot: [
{ id: 'envelope-workspace', action: 'Envelope saved workspace dashboard', why: 'Accounts, billing, saved projects, and collaboration.', evidenceNeeded: 'No bridge proof yet.', suggestedLane: 'park', sourceSection: 'rankerInput.candidateSet.parkingLot', sourceItemId: 'env-park-1' },
],
},
},
}),
});
assert.equal(bridgeEnvelopeResponse.status, 200);
const bridgeEnvelope = await bridgeEnvelopeResponse.json();
assert.equal(bridgeEnvelope.input.provenance.artifactId, 'SM-BRIDGE-ENV-1');
assert.equal(bridgeEnvelope.input.provenance.snapshotTitle, 'Envelope bridge export');
assert.equal(bridgeEnvelope.input.provenance.originalPrompt, 'A paid Concept Map clarified the idea and now needs a defended build order.');
assert.equal(bridgeEnvelope.input.decisionContext.targetAudience, 'Tired solo operator');
assert.ok(bridgeEnvelope.input.decisionContext.nonGoals.includes('Avoid accounts, billing, and saved workspaces before bridge proof'));
assert.equal(bridgeEnvelope.input.context, 'Target audience: Tired solo operator\nConstraint: Manual source-traced proof before product shell\nNon-goal: Avoid accounts, billing, and saved workspaces before bridge proof');
assert.equal(bridgeEnvelope.ranked[0].id, 'envelope-manual-proof');
assert.equal(bridgeEnvelope.ranked[0].provenance.sourceSection, 'rankerInput.candidateSet.nextActions');
assert.equal(bridgeEnvelope.ranked.find(item => item.id === 'envelope-workspace').lane.id, 'park');
assert.equal(bridgeEnvelope.handoff.readiness.status, 'ready');
assert.match(bridgeEnvelope.handoff.copyableText, /SM-BRIDGE-ENV-1/);
assert.deepEqual(bridgeEnvelope.handoff.warnings, []);
const directEnvelopeSectionsResponse = await fetch(`${base}/api/rank-feedback`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
schema: 'scattermind-ranker-bridge-v1',
rankReady: {
schema: 'prioritix-feature-set-v1',
sourceName: 'Scattermind',
reference_code: 'SM-DIRECT-ENV-1',
working_name: 'Direct envelope sections',
ideaText: 'Scattermind wrapped the Ranker handoff but put the lanes directly under the envelope.',
context: {
targetAudience: 'Tired non-AI-native maker',
constraints: ['Use the source artifact before building UI layers'],
nonGoals: ['Avoid account dashboard before the first manual proof'],
},
next_actions: [
{ id: 'direct-envelope-preview', next_step: 'Direct envelope preview', why: 'Rank one wrapped next_actions array without requiring a candidateSet wrapper.', evidence_needed: 'Can a direct envelope become a traceable Do first item?', suggested_lane: 'do-first', source_item_id: 'direct-next-1', source_section: 'rankReady.next_actions', ranker_hints: { value: 9, effort: 2, confidence: 8, urgency: 8, risk: 2 } },
],
validate_next: [
{ id: 'direct-envelope-copy', next_step: 'Direct envelope copyable handoff', why: 'Keep the decision portable.', evidence_needed: 'Does copyable text preserve direct envelope source?', source_item_id: 'direct-test-1', source_section: 'rankReady.validate_next' },
],
parking_lot: [
{ id: 'direct-envelope-dashboard', next_step: 'Direct envelope account dashboard', why: 'Saved workspaces, auth, and dashboards before proof.', evidence_needed: 'No proof yet.', source_item_id: 'direct-park-1', source_section: 'rankReady.parking_lot' },
],
},
}),
});
assert.equal(directEnvelopeSectionsResponse.status, 200);
const directEnvelopeSections = await directEnvelopeSectionsResponse.json();
assert.equal(directEnvelopeSections.input.provenance.artifactId, 'SM-DIRECT-ENV-1');
assert.equal(directEnvelopeSections.input.optionCount, 3);
assert.equal(directEnvelopeSections.ranked[0].id, 'direct-envelope-preview');
assert.equal(directEnvelopeSections.ranked[0].provenance.sourceSection, 'rankReady.next_actions');
assert.equal(directEnvelopeSections.ranked.find(item => item.id === 'direct-envelope-copy').lane.id, 'test');
assert.equal(directEnvelopeSections.ranked.find(item => item.id === 'direct-envelope-copy').lane.source, 'hint');
assert.equal(directEnvelopeSections.ranked.find(item => item.id === 'direct-envelope-dashboard').lane.id, 'park');
assert.equal(directEnvelopeSections.handoff.itemTrace.find(item => item.id === 'direct-envelope-copy').sourceId, 'direct-test-1');
assert.equal(directEnvelopeSections.handoff.readiness.status, 'ready');
assert.deepEqual(directEnvelopeSections.handoff.warnings, []);
console.log(JSON.stringify({ ok: true, top: data.ranked[0].id, hintedTop: hinted.ranked[0].id, actionTop: actions.ranked[0].id, nestedConceptTop: nestedConcept.ranked[0].id, nonGoalTop: nonGoal.ranked[0].id, structuredContextTop: structuredContext.ranked[0].id, lensOnlyTop: lensOnly.ranked[0].id, scattermindPaidShapeTop: scattermindPaidShape.ranked[0].id, mergedContextTop: mergedContext.ranked[0].id, embeddedJsonTop: embeddedJson.ranked[0].id, fencedJsonTop: fencedJson.ranked[0].id, embeddedSnapshotTop: embeddedSnapshot.ranked[0].id, sourceExcerptTop: sourceExcerpt.ranked[0].id, snakeCaseBridgeTop: snakeCaseBridge.ranked[0].id, nextStepsAliasTop: nextStepsAlias.ranked[0].id, summaryGuardrailTop: summaryGuardrail.ranked[0].id, bridgeEnvelopeTop: bridgeEnvelope.ranked[0].id, directEnvelopeSectionsTop: directEnvelopeSections.ranked[0].id, duplicateIds: duplicateIds.ranked.map(item => item.id), readiness: data.handoff.readiness.status, provenance: data.input.provenance, buildOrder: data.buildOrder }, null, 2));
} finally {
server.kill('SIGTERM');
}