Reference implementation for the Phoenix Architecture. Work in progress.
aicoding.leaflet.pub/
ai
coding
crazy
1import { readFileSync } from 'node:fs';
2import { resolve } from 'node:path';
3import { parseSpec } from '../src/spec-parser.js';
4import { extractCanonicalNodes } from '../src/canonicalizer.js';
5
6const ROOT = resolve(import.meta.dirname, '..');
7
8const specs = [
9 { name: 'Settlements', path: 'examples/settle-up/spec/settlements.md', docId: 'spec/settlements.md',
10 gold: [
11 { statement: 'minimum number of payments', type: 'CONSTRAINT' },
12 { statement: 'same net effect', type: 'REQUIREMENT' },
13 { statement: 'cycles', type: 'REQUIREMENT' },
14 { statement: 'all balances are zero', type: 'REQUIREMENT' },
15 { statement: 'exceeds', type: 'REQUIREMENT' },
16 { statement: 'settled up', type: 'REQUIREMENT' },
17 ]},
18 { name: 'TicTacToe', path: 'examples/tictactoe/spec/game-engine.md', docId: 'spec/game-engine.md',
19 gold: [
20 { statement: '3x3 grid', type: 'REQUIREMENT' },
21 { statement: 'already occupied', type: 'REQUIREMENT' },
22 { statement: 'x always moves first', type: 'INVARIANT' },
23 { statement: 'win detection', type: 'CONTEXT' },
24 { statement: 'draw', type: 'REQUIREMENT' },
25 { statement: 'game must track the current status', type: 'REQUIREMENT' },
26 ]},
27 { name: 'Pixel Wars', path: 'examples/pixel-wars/spec/game.md', docId: 'spec/game.md',
28 gold: [
29 { statement: '20 columns', type: 'CONTEXT' },
30 { statement: 'cooldown', type: 'CONSTRAINT' },
31 { statement: 'rejected', type: 'REQUIREMENT' },
32 { statement: 'broadcast', type: 'REQUIREMENT' },
33 { statement: '120 seconds', type: 'CONTEXT' },
34 { statement: 'round-robin', type: 'CONTEXT' },
35 ]},
36 { name: 'User Service', path: 'examples/microservices/spec/user-service.md', docId: 'spec/user-service.md',
37 gold: [
38 { statement: 'system of record', type: 'CONTEXT' },
39 { statement: 'email addresses must be unique', type: 'REQUIREMENT' },
40 { statement: 'never store or return plaintext passwords', type: 'INVARIANT' },
41 { statement: 'soft delete', type: 'REQUIREMENT' },
42 { statement: '100 characters', type: 'CONSTRAINT' },
43 { statement: 'locked for 1 hour', type: 'REQUIREMENT' },
44 { statement: 'parameterized statements', type: 'CONSTRAINT' },
45 { statement: 'event payloads must never contain passwords', type: 'INVARIANT' },
46 { statement: '50 results per page', type: 'CONSTRAINT' },
47 { statement: 'usercreated', type: 'REQUIREMENT' },
48 ]},
49];
50
51for (const spec of specs) {
52 const text = readFileSync(resolve(ROOT, spec.path), 'utf8');
53 const clauses = parseSpec(text, spec.docId);
54 const nodes = extractCanonicalNodes(clauses);
55 console.log(`\n=== ${spec.name} (${nodes.length} nodes) ===`);
56 for (const g of spec.gold) {
57 const match = nodes.find(n => n.statement.toLowerCase().includes(g.statement.toLowerCase()));
58 if (match) {
59 const ok = match.type === g.type ? 'OK ' : 'MISS';
60 console.log(`${ok} "${g.statement}" expected=${g.type} got=${match.type} conf=${match.confidence?.toFixed(2)} stmt="${match.statement.substring(0, 80)}"`);
61 } else {
62 console.log(`GONE "${g.statement}"`);
63 }
64 }
65}