Jev was asked "Is this item about any of these: ai, development, programming?", so any one broad word was enough and the Signal's name, which says what is meant, was never sent. AI Development held 54 items: Sega pulling a game remaster (0.90), a Surface Laptop review (0.95), a C++ tutorial (0.99). It is now asked about one subject, called by the Signal's name, described by its words and, when those are not enough, its owner's sentence. Scored against TypeSafe over those 54 with "AI tools for writing software: coding agents, AI dev tools, vibe coding, AI in code review.": 5 stay, Linux kernel AI code review highest at 0.94; the remaster 0.06, the C++ tutorial 0.02. signals.description is new, and its first start deletes every verdict, which answered the old question, so everything in the window is asked again. Any change to a Signal's name, words or sentence drops its verdicts, since each is part of the question. The queue query names the Signal's sentence apart from the item's description, which share a row; the test caught the item's winning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
2.5 KiB
JavaScript
52 lines
2.5 KiB
JavaScript
// Serves the fixture feeds so the daemon under test has something real to scan.
|
|
const http = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const dir = __dirname;
|
|
const port = Number(process.env.FIXTURE_PORT || 8792);
|
|
|
|
http.createServer((req, res) => {
|
|
// Jev, for Signals (#254): sure of an item whose text has a question's keyword in it. Refuses
|
|
// a request in the shape TypeSafe refuses, as it does (#257).
|
|
if (req.method === 'POST' && req.url === '/jev') {
|
|
let body = '';
|
|
req.on('data', c => { body += c; });
|
|
req.on('end', () => {
|
|
const { state, questions } = JSON.parse(body);
|
|
if (Object.values(questions).some(q => typeof q.criteria !== 'object')) {
|
|
res.writeHead(422, { 'content-type': 'application/json' }).end('{"detail":"criteria should be an object"}');
|
|
return;
|
|
}
|
|
const title = String(state).toLowerCase();
|
|
const answers = {};
|
|
for (const [id, q] of Object.entries(questions)) {
|
|
const words = /described by these words: ([^.]*)\./.exec(q.instructions)[1].split(', ');
|
|
answers[id] = { noul: words.some(w => title.includes(w.toLowerCase())) ? 0.9 : 0.1 };
|
|
}
|
|
res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify({ answers }));
|
|
});
|
|
return;
|
|
}
|
|
// Dated today, since a Signal looks at the last 30 days only.
|
|
if (req.url === '/signal.xml') {
|
|
const now = new Date().toUTCString();
|
|
const item = (guid, title) => `<item><guid>${guid}</guid><title>${title}</title><pubDate>${now}</pubDate></item>`;
|
|
res.writeHead(200, { 'content-type': 'application/xml' }).end(`<?xml version="1.0"?><rss version="2.0"><channel>
|
|
<title>Signal Show</title>${item('sig-1', 'Monaco qualifying')}${item('sig-2', 'Baking bread')}</channel></rss>`);
|
|
return;
|
|
}
|
|
const name = decodeURIComponent(req.url.split('?')[0].replace(/^\//, '')) || 'index';
|
|
const file = path.join(dir, path.basename(name));
|
|
fs.readFile(file, (err, body) => {
|
|
if (err) { res.writeHead(404).end('no'); return; }
|
|
const type = file.endsWith('.mp3') ? 'audio/mpeg'
|
|
: file.endsWith('.opml') ? 'text/x-opml'
|
|
// Artwork as an image, or /api/art refuses it as not one.
|
|
: file.endsWith('.jpg') ? 'image/jpeg'
|
|
: file.endsWith('.html') ? 'text/html' : 'application/xml';
|
|
res.writeHead(200, { 'content-type': type, 'content-length': body.length });
|
|
res.end(body);
|
|
});
|
|
}).listen(port, '127.0.0.1', () => console.log(`fixtures on ${port}`));
|