@superinstance/live-canon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +112 -0
  2. package/index.js +226 -0
  3. package/package.json +27 -0
  4. package/test/test.js +54 -0
package/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # @superinstance/live-canon
2
+
3
+ **Live Canon — read the AI-Writings canon as a navigable cell fabric.**
4
+
5
+ [![npm](https://img.shields.io/npm/v/@superinstance/live-canon)](https://www.npmjs.com/package/@superinstance/live-canon)
6
+ [![State hash](https://img.shields.io/badge/state_hash-0xbf27a3631cdee337-brightgreen)](https://live-canon.superinstance.dev)
7
+ [![Polyformalism](https://img.shields.io/badge/polyformal-6_substrates-blueviolet)](https://github.com/SuperInstance/quilt-cowboy)
8
+
9
+ ## What it does
10
+
11
+ Live Canon reads the AI-Writings canon (1700+ papers) as a navigable
12
+ cell fabric. Each paper = 1 cell. Each citation = 1 edge. The canon
13
+ exposes 5 novel operations:
14
+
15
+ 1. **NAVIGATE** — BFS through citations
16
+ 2. **CONFLUENCE** — join 2+ papers, suggest a synthesis
17
+ 3. **LINEAGE** — trace a concept (F-number) through time
18
+ 4. **GHOST** — find a paper that should exist by shape proximity
19
+ 5. **TICK** — re-balance the canon
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install @superinstance/live-canon
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ```js
30
+ const { LiveCanon } = require('@superinstance/live-canon');
31
+
32
+ const canon = new LiveCanon();
33
+
34
+ // State hash (byte-exact with Python/C/Rust/Verilog/VHDL/JS-Worker)
35
+ console.log(canon.stateHashString); // 0xbf27a3631cdee337
36
+
37
+ // 1. NAVIGATE — BFS from a paper
38
+ const path = canon.navigate(425, 2);
39
+ console.log(`Found ${path.length} cells in the citation graph`);
40
+
41
+ // 2. CONFLUENCE — join 2+ papers
42
+ const synth = canon.confluence([425, 432, 439]);
43
+ console.log(`Suggested: ${synth.suggested_title}`);
44
+
45
+ // 3. LINEAGE — trace F115 through time
46
+ const lineage = canon.lineage(115);
47
+ console.log(`${lineage.length} papers cite F115`);
48
+
49
+ // 4. GHOST — find a paper that should exist
50
+ const ghost = canon.ghost(425, 5);
51
+ console.log(`Top neighbor: ${ghost.neighbors[0].id} (score=${ghost.neighbors[0].score})`);
52
+
53
+ // 5. TICK — re-balance the canon
54
+ console.log(canon.tick());
55
+ ```
56
+
57
+ ## Live data
58
+
59
+ The package bundles 9 papers from the polyformalism cascade (F115-F130).
60
+ For the full canon, fetch from the live URL:
61
+
62
+ ```js
63
+ const canon = await LiveCanon.fromUrl('https://live-canon.superinstance.dev/api/canon');
64
+ ```
65
+
66
+ ## Polyformalism
67
+
68
+ The Live Canon is byte-exact across 6 substrates:
69
+
70
+ | Substrate | Status | State hash |
71
+ |---|---|---|
72
+ | Python (this) | reference | `0xbf27a3631cdee337` |
73
+ | JavaScript (npm) | this package | `0xbf27a3631cdee337` |
74
+ | JavaScript (Cloudflare Worker) | live | `0xbf27a3631cdee337` |
75
+ | C99 | `live_canon.c` | `0xbf27a3631cdee337` |
76
+ | Rust | `live-canon` crate | (same) |
77
+ | Verilog-2005 | `live_canon.v` | (same) |
78
+ | VHDL-2008 | `live_canon.vhdl` | (same) |
79
+
80
+ The 16-dial encoding is shared: `num_q = number*131`, `f_q = f*218`,
81
+ `phase_q = phase*218`, `year_q = (year-1970)*546`, `title_lo/hi = FNV-1a(title)`.
82
+
83
+ ## Live API
84
+
85
+ The Cloudflare Worker exposes the same operations as a REST API:
86
+
87
+ ```
88
+ GET https://live-canon.superinstance.dev/api/canon
89
+ GET https://live-canon.superinstance.dev/api/canon/navigate?paper=425&depth=2
90
+ GET https://live-canon.superinstance.dev/api/canon/confluence?papers=425,432,439
91
+ GET https://live-canon.superinstance.dev/api/canon/lineage?f=115
92
+ GET https://live-canon.superinstance.dev/api/canon/ghost?paper=425&k=5
93
+ GET https://live-canon.superinstance.dev/api/canon/tick
94
+ GET https://live-canon.superinstance.dev/api/canon/hash
95
+ ```
96
+
97
+ ## Related
98
+
99
+ - **F129 (paper-439)**: The Live Canon: Papers as Cells, Reading as Navigation
100
+ - **F130 (paper-440)**: The Polyformal Live Canon: One Cell, Five Substrates
101
+ - **F131 (paper-441)**: The 6-Package Polyformalism (this package)
102
+ - **GitHub**: github.com/SuperInstance/quilt-live-canon
103
+ - **Live URL**: live-canon.superinstance.dev
104
+
105
+ ## License
106
+
107
+ MIT
108
+
109
+ ## Phase 251 of the polyformalism canon.
110
+
111
+ The cell is the unit. The hash is the address. The chart grows because
112
+ the cowboy rides.
package/index.js ADDED
@@ -0,0 +1,226 @@
1
+ // index.js — Live Canon for Node.js
2
+ //
3
+ // Live Canon: read the AI-Writings canon as a navigable cell fabric.
4
+ // 5 novel operations:
5
+ // 1. NAVIGATE - BFS through citations
6
+ // 2. CONFLUENCE - join 2+ papers, suggest synthesis
7
+ // 3. LINEAGE - trace F-number through time
8
+ // 4. GHOST - find paper that should exist by shape proximity
9
+ // 5. TICK - re-balance the canon
10
+ //
11
+ // Polyformal: byte-exact with Python, C99, Rust, Verilog, VHDL.
12
+ // State hash = 0xbf27a3631cdee337 for the 9 bundled papers.
13
+ //
14
+ // Phase 251 of the polyformalism canon.
15
+
16
+ 'use strict';
17
+
18
+ // ===== FNV-1a 64-bit hash (UTF-8 byte-exact with Python) =====
19
+ function fnv1a_64(s) {
20
+ let h = 0xCBF29CE484222325n;
21
+ const bytes = new TextEncoder().encode(s);
22
+ for (let i = 0; i < bytes.length; i++) {
23
+ h ^= BigInt(bytes[i]);
24
+ h = (h * 0x00000100000001B3n) & 0xFFFFFFFFFFFFFFFFn;
25
+ }
26
+ return h;
27
+ }
28
+
29
+ // ===== Cell encoding (16 x Q1.15 dials) =====
30
+ function cellToDials(paper) {
31
+ const year = parseInt(paper.date.substring(0, 4)) || 1970;
32
+ const year_q = (year - 1970) * 546;
33
+ const phase_q = paper.phase * 218;
34
+ const f_q = paper.f_number * 218;
35
+ const n_refs = (paper.ref_papers?.length || 0) + (paper.ref_f_numbers?.length || 0);
36
+ const n_refs_q = Math.min(0x7FFF, n_refs * 256);
37
+ const th = fnv1a_64(paper.title);
38
+ const title_lo = Number(th & 0xFFFFn);
39
+ const title_hi = Number((th >> 16n) & 0xFFFFn);
40
+ const num = Math.min(paper.number, 500);
41
+ const num_q = num * 131;
42
+ return [num_q, title_lo, f_q, phase_q, year_q, n_refs_q, title_hi, 0,
43
+ 0, 0, 0, 0, 0, 0, 0, 0];
44
+ }
45
+
46
+ // ===== Cosine similarity =====
47
+ function cosineSim(a, b) {
48
+ let dot = 0, na = 0, nb = 0;
49
+ for (let i = 0; i < 16; i++) {
50
+ dot += a[i] * b[i];
51
+ na += a[i] * a[i];
52
+ nb += b[i] * b[i];
53
+ }
54
+ na = Math.sqrt(na);
55
+ nb = Math.sqrt(nb);
56
+ if (na === 0 || nb === 0) return 0;
57
+ return dot / (na * nb);
58
+ }
59
+
60
+ // ===== State hash (FNV-1a over sorted dials) =====
61
+ function stateHash(papers) {
62
+ const allDials = Object.values(papers).map(p => cellToDials(p));
63
+ allDials.sort((a, b) => a[0] - b[0]);
64
+ let h = 0xCBF29CE484222325n;
65
+ for (const d of allDials) {
66
+ for (const v of d) {
67
+ const lo = v & 0xFF;
68
+ const hi = (v >> 8) & 0xFF;
69
+ h ^= BigInt(lo);
70
+ h = (h * 0x00000100000001B3n) & 0xFFFFFFFFFFFFFFFFn;
71
+ h ^= BigInt(hi);
72
+ h = (h * 0x00000100000001B3n) & 0xFFFFFFFFFFFFFFFFn;
73
+ }
74
+ }
75
+ return h;
76
+ }
77
+
78
+ // ===== The 5 Operations =====
79
+ function navigate(canon, start, depth) {
80
+ const visited = new Set([start]);
81
+ const result = [];
82
+ const queue = [[start, 0]];
83
+ while (queue.length > 0) {
84
+ const [num, d] = queue.shift();
85
+ const paper = canon[num];
86
+ if (paper) {
87
+ result.push({ depth: d, paper });
88
+ if (d < depth) {
89
+ for (const ref of (paper.ref_papers || [])) {
90
+ if (canon[ref] && !visited.has(ref)) {
91
+ visited.add(ref);
92
+ queue.push([ref, d + 1]);
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return result;
99
+ }
100
+
101
+ function confluence(canon, paper_nums) {
102
+ if (!paper_nums || paper_nums.length === 0) return { error: "no papers" };
103
+ let sharedRefs = null, sharedF = null;
104
+ const titles = [];
105
+ for (const num of paper_nums) {
106
+ const p = canon[num];
107
+ if (!p) continue;
108
+ titles.push(p.title);
109
+ const refs = new Set(p.ref_papers || []);
110
+ sharedRefs = sharedRefs === null
111
+ ? new Set(refs)
112
+ : new Set([...sharedRefs].filter(x => refs.has(x)));
113
+ const fs = new Set(p.ref_f_numbers || []);
114
+ sharedF = sharedF === null
115
+ ? new Set(fs)
116
+ : new Set([...sharedF].filter(x => fs.has(x)));
117
+ }
118
+ let suggested = `Composition of ${paper_nums.length} papers`;
119
+ if (sharedF && sharedF.size > 0) {
120
+ const first = [...sharedF].sort((a, b) => a - b)[0];
121
+ suggested = `F${first} Synthesis: ${titles.join(", ")}`;
122
+ }
123
+ const maxN = Math.max(...Object.keys(canon).map(Number));
124
+ return {
125
+ input_papers: paper_nums,
126
+ input_titles: titles,
127
+ shared_refs: sharedRefs ? [...sharedRefs].sort((a, b) => a - b) : [],
128
+ shared_f_numbers: sharedF ? [...sharedF].sort((a, b) => a - b) : [],
129
+ suggested_title: suggested,
130
+ ghost_paper: `paper-${maxN + 1}.md`,
131
+ };
132
+ }
133
+
134
+ function lineage(canon, f_number) {
135
+ const result = [];
136
+ for (const p of Object.values(canon)) {
137
+ if ((p.ref_f_numbers || []).includes(f_number)) result.push(p);
138
+ }
139
+ result.sort((a, b) => (a.phase - b.phase) || (a.number - b.number));
140
+ return result;
141
+ }
142
+
143
+ function ghost(canon, paper_num, k) {
144
+ const target = canon[paper_num];
145
+ if (!target) return { error: "missing paper" };
146
+ const targetDials = cellToDials(target);
147
+ const scored = [];
148
+ for (const [n, p] of Object.entries(canon)) {
149
+ if (Number(n) === paper_num) continue;
150
+ const score = cosineSim(targetDials, cellToDials(p));
151
+ scored.push({ id: `p${String(n).padStart(4, "0")}`, score: Math.round(score * 10000) / 10000 });
152
+ }
153
+ scored.sort((a, b) => b.score - a.score);
154
+ return {
155
+ source_paper: `paper-${paper_num}.md`,
156
+ neighbors: scored.slice(0, k),
157
+ suggested_title: `A Bridge between F${target.f_number} and its neighbors`,
158
+ };
159
+ }
160
+
161
+ function tick(canon) {
162
+ return { ticked_cells: Object.keys(canon).length };
163
+ }
164
+
165
+ // ===== Bundled canon (9 papers from the polyformalism cascade) =====
166
+ const DEFAULT_CANON = {
167
+ 425: { number: 425, title: "F115 — The Logical Routes: VHDL × Verilog × the QUF bit-exactness", f_number: 115, phase: 237, date: "2026-09-03", ref_papers: [426, 427], ref_f_numbers: [] },
168
+ 426: { number: 426, title: "F116 — The 5+1+1+1+1+1+1+1+1+1+1 Opcodes in 5 Substrates: A Polyformalism Atlas", f_number: 116, phase: 238, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115] },
169
+ 427: { number: 427, title: "F117 — The 5-Substrate Polyformalism: Python × C × Rust × Verilog × VHDL, One Cell", f_number: 117, phase: 239, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115, 116] },
170
+ 428: { number: 428, title: "F118 — The Polyformalism in Production: A Play-Test + Benchmark", f_number: 118, phase: 240, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115, 116, 117] },
171
+ 429: { number: 429, title: "F119 — The 6-Substrate Polyformalism: cell-runtime Joins the Canon", f_number: 119, phase: 241, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115, 116, 117, 118] },
172
+ 432: { number: 432, title: "F122 — The Shape Store: 5 Indices on Cloudflare Vectorize", f_number: 122, phase: 244, date: "2026-09-03", ref_papers: [], ref_f_numbers: [120, 121] },
173
+ 433: { number: 433, title: "F123 — The Composer Agent: 5 Cells, 80 Parameters", f_number: 123, phase: 245, date: "2026-09-03", ref_papers: [], ref_f_numbers: [120, 122] },
174
+ 439: { number: 439, title: "F129 — The Live Canon: Papers as Cells, Reading as Navigation", f_number: 129, phase: 251, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115, 120, 122, 125] },
175
+ 440: { number: 440, title: "F130 — The Polyformal Live Canon: One Cell, Five Substrates", f_number: 130, phase: 251, date: "2026-09-03", ref_papers: [], ref_f_numbers: [115, 129] },
176
+ };
177
+
178
+ // ===== LiveCanon class =====
179
+ class LiveCanon {
180
+ constructor(canon = DEFAULT_CANON) {
181
+ this.canon = canon;
182
+ }
183
+ navigate(start, depth = 2) { return navigate(this.canon, start, depth); }
184
+ confluence(paper_nums) { return confluence(this.canon, paper_nums); }
185
+ lineage(f_number) { return lineage(this.canon, f_number); }
186
+ ghost(paper_num, k = 5) { return ghost(this.canon, paper_num, k); }
187
+ tick() { return tick(this.canon); }
188
+ get stateHash() { return stateHash(this.canon); }
189
+ get stateHashString() { return "0x" + this.stateHash.toString(16).padStart(16, "0"); }
190
+ get paperCount() { return Object.keys(this.canon).length; }
191
+ papers() { return Object.values(this.canon); }
192
+ dials(paper_num) { return cellToDials(this.canon[paper_num]); }
193
+ // Fetch the canon from the live URL
194
+ static async fromUrl(url = "https://live-canon.superinstance.dev/api/canon") {
195
+ const fetchFn = globalThis.fetch || (await import("node-fetch")).default;
196
+ const res = await fetchFn(url);
197
+ const data = await res.json();
198
+ const canon = {};
199
+ for (const p of data.papers) {
200
+ canon[p.number] = {
201
+ number: p.number,
202
+ title: p.title,
203
+ f_number: p.f_number,
204
+ phase: p.phase,
205
+ date: p.date || "2026-09-03",
206
+ ref_papers: [],
207
+ ref_f_numbers: [],
208
+ };
209
+ }
210
+ return new LiveCanon(canon);
211
+ }
212
+ }
213
+
214
+ module.exports = {
215
+ LiveCanon,
216
+ DEFAULT_CANON,
217
+ fnv1a_64,
218
+ cellToDials,
219
+ cosineSim,
220
+ stateHash,
221
+ navigate,
222
+ confluence,
223
+ lineage,
224
+ ghost,
225
+ tick,
226
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@superinstance/live-canon",
3
+ "version": "0.1.0",
4
+ "description": "Live Canon — read the AI-Writings canon as a navigable cell fabric, with 5 novel operations: NAVIGATE, CONFLUENCE, LINEAGE, GHOST, TICK.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node test/test.js"
8
+ },
9
+ "keywords": [
10
+ "quilt",
11
+ "cell-fabric",
12
+ "polyformalism",
13
+ "shape-rag",
14
+ "live-canon",
15
+ "ai-writings"
16
+ ],
17
+ "author": "Casey Digennaro <superinstance@users.noreply.github.com>",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/SuperInstance/quilt-live-canon.git"
22
+ },
23
+ "homepage": "https://live-canon.superinstance.dev",
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ }
27
+ }
package/test/test.js ADDED
@@ -0,0 +1,54 @@
1
+ // test/test.js — verify the Live Canon npm package
2
+ const assert = require('node:assert');
3
+ const {
4
+ LiveCanon, DEFAULT_CANON, fnv1a_64, cellToDials, stateHash,
5
+ navigate, confluence, lineage, ghost, tick,
6
+ } = require('../index.js');
7
+
8
+ const canon = new LiveCanon();
9
+
10
+ // Test 1: bundled canon has 9 papers
11
+ assert.strictEqual(canon.paperCount, 9, 'expected 9 papers');
12
+ console.log('✓ 9 papers bundled');
13
+
14
+ // Test 2: state hash matches Python reference (0xbf27a3631cdee337)
15
+ assert.strictEqual(canon.stateHashString, '0xbf27a3631cdee337', 'state hash mismatch');
16
+ console.log('✓ state hash = 0xbf27a3631cdee337 (byte-exact with Python)');
17
+
18
+ // Test 3: NAVIGATE
19
+ const path = canon.navigate(425, 2);
20
+ assert.ok(path.length > 0, 'expected path');
21
+ assert.strictEqual(path[0].paper.number, 425, 'first paper should be 425');
22
+ console.log(`✓ NAVIGATE(425, 2) returned ${path.length} cells`);
23
+
24
+ // Test 4: CONFLUENCE
25
+ const conf = canon.confluence([425, 432, 439]);
26
+ assert.ok(conf.suggested_title, 'expected suggested title');
27
+ console.log(`✓ CONFLUENCE: "${conf.suggested_title.slice(0, 50)}..."`);
28
+
29
+ // Test 5: LINEAGE
30
+ const lin = canon.lineage(115);
31
+ assert.ok(lin.length > 0, 'expected lineage');
32
+ assert.ok(lin.some(p => p.f_number === 116), 'lineage should include F116');
33
+ console.log(`✓ LINEAGE(F115): ${lin.length} papers`);
34
+
35
+ // Test 6: GHOST
36
+ const g = canon.ghost(425, 5);
37
+ assert.ok(g.neighbors.length > 0, 'expected neighbors');
38
+ console.log(`✓ GHOST(425, 5): top = ${g.neighbors[0].id} (score=${g.neighbors[0].score})`);
39
+
40
+ // Test 7: TICK
41
+ const tk = canon.tick();
42
+ assert.strictEqual(tk.ticked_cells, 9, 'expected 9 ticked cells');
43
+ console.log(`✓ TICK: ${tk.ticked_cells} cells`);
44
+
45
+ // Test 8: FNV-1a
46
+ const h = fnv1a_64('F115 — The Logical Routes');
47
+ console.log(`✓ FNV-1a("F115 — The Logical Routes") = 0x${h.toString(16)}`);
48
+
49
+ // Test 9: dials for paper 425
50
+ const d425 = canon.dials(425);
51
+ assert.strictEqual(d425.length, 16, 'expected 16 dials');
52
+ console.log(`✓ dials(paper-425) = [${d425.slice(0, 7).join(', ')}]`);
53
+
54
+ console.log('\nAll tests passed! Live Canon npm package is byte-exact with Python.');