Reference implementation for the Phoenix Architecture. Work in progress.
aicoding.leaflet.pub/
ai
coding
crazy
1/**
2 * Negative Knowledge Store — persists what was tried and failed.
3 * Preserved across compaction. The system's immune memory.
4 */
5
6import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
7import { join } from 'node:path';
8import type { NegativeKnowledge } from '../models/negative-knowledge.js';
9
10interface NKIndex {
11 records: NegativeKnowledge[];
12}
13
14export class NegativeKnowledgeStore {
15 private indexPath: string;
16
17 constructor(phoenixRoot: string) {
18 const dir = join(phoenixRoot, 'provenance');
19 mkdirSync(dir, { recursive: true });
20 this.indexPath = join(dir, 'negative-knowledge.json');
21 }
22
23 private load(): NKIndex {
24 if (!existsSync(this.indexPath)) return { records: [] };
25 return JSON.parse(readFileSync(this.indexPath, 'utf8'));
26 }
27
28 private save(index: NKIndex): void {
29 writeFileSync(this.indexPath, JSON.stringify(index, null, 2), 'utf8');
30 }
31
32 add(record: NegativeKnowledge): void {
33 const index = this.load();
34 const existing = index.records.findIndex(r => r.nk_id === record.nk_id);
35 if (existing >= 0) {
36 index.records[existing] = record;
37 } else {
38 index.records.push(record);
39 }
40 this.save(index);
41 }
42
43 getBySubject(subjectId: string): NegativeKnowledge[] {
44 return this.load().records.filter(r => r.subject_id === subjectId && r.active);
45 }
46
47 getAll(): NegativeKnowledge[] {
48 return this.load().records;
49 }
50
51 getActive(): NegativeKnowledge[] {
52 return this.load().records.filter(r => r.active);
53 }
54
55 markStale(nkId: string): boolean {
56 const index = this.load();
57 const record = index.records.find(r => r.nk_id === nkId);
58 if (record) {
59 record.active = false;
60 this.save(index);
61 return true;
62 }
63 return false;
64 }
65}