Harden Scattermind rank feedback bridge

This commit is contained in:
OpenClaw Bot
2026-05-26 22:12:27 +02:00
parent f75dbb1a10
commit e532c6d910
3 changed files with 135 additions and 17 deletions
+72
View File
@@ -0,0 +1,72 @@
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',
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.' },
{ id: 'build-order-preview', title: 'Build order preview', description: 'Show do first, validate next, defer, and park with reasons.' },
{ id: 'workspace', title: 'Accounts and saved workspaces', description: 'Full dashboard with auth, workspace collaboration, team voting, and sync.' },
{ id: 'billing', title: 'Subscription billing layer', description: 'Pricing, checkout, invoices, account plans, and admin controls.' },
{ id: 'export', title: 'Exportable decision brief', description: 'Simple brief for sharing the defended build order.' },
],
},
}),
});
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.equal(data.ranked.length, 5);
assert.deepEqual(Object.keys(data.buildOrder), ['doFirst', 'validateNext', 'defer', 'park']);
assert.equal(data.ranked[0].id, data.buildOrder.doFirst[0]);
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.match(data.brief.summary, /nearest follow-up|strongest signal/i);
console.log(JSON.stringify({ ok: true, top: data.ranked[0].id, provenance: data.input.provenance, buildOrder: data.buildOrder }, null, 2));
} finally {
server.kill('SIGTERM');
}