Skip to content

Commit 61772c7

Browse files
authored
Merge pull request #47 from AdaWorldAPI/claude/osint-soa-rebake
osint: global-category pole — aiwar ceiling axes + FMA dual-membership (post-#46)
2 parents 5fdb53c + deae195 commit 61772c7

8 files changed

Lines changed: 709 additions & 12 deletions

File tree

cockpit/public/fma.soa

5.05 KB
Binary file not shown.

cockpit/src/FmaGraph.tsx

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// FMA anatomy slice — the "real test" of the dual-membership lattice. Decodes
2+
// the baked /fma.soa (heart organ subtree + cross-cutting tissue TYPES) into the
3+
// same vis-network renderer, and on click shows a node resolving to BOTH:
4+
// · its part-of position (basin-local: organ → chamber → wall → structure)
5+
// · its leaf-limited global TYPE (the 0xFFFF ceiling pole — cross-cutting,
6+
// the same "Cardiac muscle tissue" shared by every chamber).
7+
import { useEffect, useMemo, useRef, useState } from 'react';
8+
import { Network, type Options } from 'vis-network';
9+
import { DataSet } from 'vis-data';
10+
import { decodeSoa, type Soa } from './OsintGraph';
11+
12+
const PAGE_BG = '#0a0e17';
13+
const CEILING_COLOR = '#ffd166';
14+
15+
// class byte → colour/name (mirrors the C_* consts in src/bin/fma.rs).
16+
const FMA_CLASS = [
17+
{ name: 'Organ', color: '#ff637d' },
18+
{ name: 'Chamber', color: '#ffb547' },
19+
{ name: 'Wall', color: '#4dd0e1' },
20+
{ name: 'Tissue', color: '#35d07f' },
21+
{ name: 'Cell', color: '#9b8cff' },
22+
{ name: 'Type · global', color: CEILING_COLOR },
23+
];
24+
const classColor = (c: number) => FMA_CLASS[c]?.color ?? '#8899aa';
25+
26+
// rel byte → name (REL_* in src/bin/fma.rs). 2 part-of, 3 is-a(global type).
27+
const REL = ['member-of', 'interfaces', 'part-of', 'is-a'];
28+
const REL_COLOR = ['#223040', '#223040', '#7fa6c4', CEILING_COLOR];
29+
30+
const OPTIONS: Options = {
31+
nodes: { shape: 'dot', borderWidth: 2.5, font: { color: '#d9e9f9', size: 13, strokeWidth: 3, strokeColor: PAGE_BG } },
32+
edges: {
33+
color: { color: 'rgba(125,162,186,0.3)', inherit: false },
34+
font: { color: 'rgba(147,169,191,0.55)', size: 9, strokeWidth: 0, align: 'middle' },
35+
width: 1.1,
36+
smooth: { enabled: true, type: 'continuous', roundness: 0.2 },
37+
arrows: { to: { enabled: true, scaleFactor: 0.45 } },
38+
},
39+
physics: {
40+
solver: 'forceAtlas2Based',
41+
forceAtlas2Based: { gravitationalConstant: -70, centralGravity: 0.008, springLength: 130, springConstant: 0.04, damping: 0.5, avoidOverlap: 0.5 },
42+
stabilization: { iterations: 180, fit: true },
43+
},
44+
interaction: { hover: true, tooltipDelay: 90, dragNodes: true },
45+
layout: { improvedLayout: false },
46+
};
47+
48+
interface Trace {
49+
node: string;
50+
partOf: string[];
51+
type: string | null;
52+
shared: number;
53+
}
54+
55+
export function FmaGraph() {
56+
const hostRef = useRef<HTMLDivElement>(null);
57+
const [soa, setSoa] = useState<Soa | null>(null);
58+
const [error, setError] = useState<string | null>(null);
59+
const [status, setStatus] = useState('loading FMA slice…');
60+
const [trace, setTrace] = useState<Trace | null>(null);
61+
62+
useEffect(() => {
63+
let cancelled = false;
64+
fetch('/fma.soa')
65+
.then((r) => {
66+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
67+
return r.arrayBuffer();
68+
})
69+
.then((buf) => !cancelled && setSoa(decodeSoa(buf)))
70+
.catch((e: unknown) => !cancelled && setError(String(e)));
71+
return () => {
72+
cancelled = true;
73+
};
74+
}, []);
75+
76+
// part-of parent (rel 2) and is-a global type (rel 3) per node, + how many
77+
// tissues each type gathers (its cross-cutting reach).
78+
const rel = useMemo(() => {
79+
if (!soa) return null;
80+
const parentOf = new Map<number, number>();
81+
const typeOf = new Map<number, number>();
82+
const typeMembers = new Map<number, number>();
83+
for (const e of soa.edges) {
84+
if (e.r === 2) parentOf.set(e.s, e.t);
85+
else if (e.r === 3) {
86+
typeOf.set(e.s, e.t);
87+
typeMembers.set(e.t, (typeMembers.get(e.t) ?? 0) + 1);
88+
}
89+
}
90+
return { parentOf, typeOf, typeMembers };
91+
}, [soa]);
92+
93+
useEffect(() => {
94+
if (!hostRef.current || !soa || !rel) return;
95+
const ceiling = (i: number) => soa.ceiling[i] === 1 || soa.cls[i] === 5;
96+
const baseNode = (i: number) => ({
97+
id: i,
98+
label: soa.labels[i] || `#${i}`,
99+
shape: ceiling(i) ? 'diamond' : 'dot',
100+
color: {
101+
background: ceiling(i) ? 'rgba(255,209,102,0.14)' : 'rgba(10,14,23,0.88)',
102+
border: ceiling(i) ? CEILING_COLOR : classColor(soa.cls[i]),
103+
},
104+
size: ceiling(i) ? 22 : 13,
105+
font: { color: ceiling(i) ? '#ffe9b0' : '#d9e9f9' },
106+
title: `${soa.labels[i]}\n${ceiling(i) ? '◈ global type (leaf-limited, cross-cutting)' : FMA_CLASS[soa.cls[i]]?.name}`,
107+
});
108+
const baseEdge = (e: { s: number; t: number; r: number }, id: number) => ({
109+
id,
110+
from: e.s,
111+
to: e.t,
112+
label: REL[e.r] ?? '',
113+
color: { color: `${REL_COLOR[e.r] ?? '#8fa6c4'}66`, highlight: REL_COLOR[e.r] ?? '#4dd0e1' },
114+
dashes: e.r === 3 ? [4, 3] : false,
115+
});
116+
117+
const visNodes = new DataSet<any>(Array.from({ length: soa.nodeCount }, (_, i) => baseNode(i)));
118+
const visEdges = new DataSet<any>(soa.edges.map((e, id) => baseEdge(e, id)));
119+
const net = new Network(hostRef.current, { nodes: visNodes, edges: visEdges }, OPTIONS);
120+
net.once('stabilizationIterationsDone', () => {
121+
net.setOptions({ physics: { enabled: false } });
122+
setStatus(`${soa.nodeCount} nodes · ${soa.edgeCount} edges — click a tissue to see its dual membership`);
123+
});
124+
125+
const dim = () => {
126+
visNodes.update(Array.from({ length: soa.nodeCount }, (_, i) => ({ id: i, color: { background: 'rgba(10,14,23,0.5)', border: '#26323f' }, font: { color: '#566779' } })));
127+
visEdges.update(soa.edges.map((_, id) => ({ id, color: { color: 'rgba(50,66,84,0.1)' } })));
128+
};
129+
const restore = () => {
130+
visNodes.update(Array.from({ length: soa.nodeCount }, (_, i) => baseNode(i)));
131+
visEdges.update(soa.edges.map((e, id) => baseEdge(e, id)));
132+
setTrace(null);
133+
};
134+
const bright = (i: number) => visNodes.update({ id: i, color: { background: 'rgba(10,14,23,0.96)', border: ceiling(i) ? CEILING_COLOR : '#9fe8ff' }, font: { color: '#eaf4ff' } });
135+
const litEdge = (from: number, to: number, c: string) => {
136+
const hit = soa.edges.findIndex((e) => e.s === from && e.t === to);
137+
if (hit >= 0) visEdges.update({ id: hit, color: { color: c }, width: 3 });
138+
};
139+
140+
net.on('click', (p: { nodes: unknown[] }) => {
141+
if (!p.nodes.length) {
142+
restore();
143+
return;
144+
}
145+
const seed = p.nodes[0] as number;
146+
dim();
147+
bright(seed);
148+
// part-of chain up to the organ (basin-local position).
149+
const partOf: string[] = [];
150+
let cur = seed;
151+
for (let hop = 0; hop < 12; hop++) {
152+
const parent = rel.parentOf.get(cur);
153+
if (parent == null) break;
154+
litEdge(cur, parent, '#6cf0ff');
155+
bright(parent);
156+
partOf.push(soa.labels[parent]);
157+
cur = parent;
158+
}
159+
// is-a leaf-limited global type (the ceiling pole, cross-cutting).
160+
const ty = rel.typeOf.get(seed);
161+
if (ty != null) {
162+
litEdge(seed, ty, CEILING_COLOR);
163+
bright(ty);
164+
}
165+
setTrace({
166+
node: soa.labels[seed],
167+
partOf,
168+
type: ty != null ? soa.labels[ty] : null,
169+
shared: ty != null ? rel.typeMembers.get(ty) ?? 0 : 0,
170+
});
171+
});
172+
173+
return () => net.destroy();
174+
}, [soa, rel]);
175+
176+
return (
177+
<div style={{ position: 'relative', width: '100%', height: '100vh', background: PAGE_BG, overflow: 'hidden' }}>
178+
<div ref={hostRef} style={{ position: 'absolute', inset: 0, zIndex: 0 }} />
179+
<div style={{ position: 'absolute', top: 16, left: 16, zIndex: 10, fontFamily: 'monospace', color: '#93a9bf', fontSize: 12, pointerEvents: 'none', textShadow: '0 0 4px #0a0e17' }}>
180+
<div style={{ fontSize: 14, color: '#cfe7ff' }}>FMA heart slice · part-of basin × leaf-limited global type</div>
181+
<div>{error ? <span style={{ color: '#ff637d' }}>load error: {error}</span> : status}</div>
182+
</div>
183+
184+
{trace && (
185+
<div style={{ position: 'absolute', left: 16, bottom: 56, width: 380, zIndex: 10, fontFamily: 'monospace', fontSize: 11, color: '#cfe7ff', background: 'rgba(8,12,20,0.88)', border: '1px solid #2a4a6a', borderRadius: 8, padding: '10px 12px' }}>
186+
<div style={{ color: '#7fd1ff', marginBottom: 6 }}>{trace.node}</div>
187+
<div style={{ color: '#9fb4c8' }}>part-of (basin-local position):</div>
188+
<div style={{ marginBottom: 6 }}>{trace.partOf.length ? trace.partOf.join(' › ') : '(root)'}</div>
189+
<div style={{ color: '#9fb4c8' }}>is-a (leaf-limited global type — ceiling pole):</div>
190+
<div style={{ color: trace.type ? CEILING_COLOR : '#566779' }}>
191+
{trace.type ? `◈ ${trace.type} · cross-cuts ${trace.shared} chambers` : '— (no global type at this grain)'}
192+
</div>
193+
</div>
194+
)}
195+
196+
{/* legend */}
197+
<div style={{ position: 'absolute', bottom: 16, left: 16, zIndex: 10, fontFamily: 'monospace', fontSize: 11, color: '#93a9bf', pointerEvents: 'none', textShadow: '0 0 4px #0a0e17' }}>
198+
{FMA_CLASS.map((k) => (
199+
<span key={k.name} style={{ marginRight: 12, whiteSpace: 'nowrap' }}>
200+
<span style={{ display: 'inline-block', width: 9, height: 9, borderRadius: k.name.startsWith('Type') ? 0 : 9, border: `2px solid ${k.color}`, marginRight: 5, verticalAlign: 'middle', transform: k.name.startsWith('Type') ? 'rotate(45deg)' : 'none' }} />
201+
{k.name}
202+
</span>
203+
))}
204+
</div>
205+
206+
<a href="/osint" style={{ position: 'absolute', top: 14, right: 16, zIndex: 10, fontFamily: 'monospace', fontSize: 12, color: '#cfe7ff', background: 'rgba(17,32,48,0.7)', border: '1px solid #2a4a6a', borderRadius: 6, padding: '6px 12px', textDecoration: 'none' }}>
207+
← OSINT graph
208+
</a>
209+
</div>
210+
);
211+
}

cockpit/src/OsintGraph.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,11 @@ const facetColor = (code: number) =>
7474
const DIM_NODE = { background: 'rgba(10,14,23,0.55)', border: '#26323f' };
7575
const DIM_EDGE = 'rgba(50,66,84,0.12)';
7676
const ACTIVE = '#6cf0ff';
77+
// the cross-cutting global-category hubs (HEEL=HIP=0xFFFF ceiling pole) — drawn
78+
// as bright diamonds so the dual-use axes read as global, not basin-local.
79+
const CEILING_COLOR = '#ffd166';
7780

78-
interface Soa {
81+
export interface Soa {
7982
nodeCount: number;
8083
edgeCount: number;
8184
cls: Uint8Array;
@@ -84,6 +87,8 @@ interface Soa {
8487
// per-node facet tenant: 6 codes (value[1..=6]) × nodeCount, or null if the
8588
// asset predates the tenant tail. The dynamic attribute the facet lens groups by.
8689
tenants: Uint8Array | null;
90+
// per-node global-category flag (HEEL=HIP=0xFFFF ceiling pole): 1 = cross-cutting.
91+
ceiling: Uint8Array;
8792
}
8893

8994
/** One readable step of the reasoning traversal, streamed into the readout. */
@@ -110,7 +115,7 @@ interface GraphApi {
110115
// Decode the OSO1 wire: magic(4) | nodeCount u32 | edgeCount u32 |
111116
// nodeCount×[guid:16|class:1] | edgeCount×[src:u16|tgt:u16|rel:u8] |
112117
// nodeCount×[len:u8|utf8 name] (the label tail is additive / may be absent).
113-
function decodeSoa(buf: ArrayBuffer): Soa {
118+
export function decodeSoa(buf: ArrayBuffer): Soa {
114119
const dv = new DataView(buf);
115120
const magicOk =
116121
dv.getUint8(0) === 0x4f && dv.getUint8(1) === 0x53 &&
@@ -120,7 +125,14 @@ function decodeSoa(buf: ArrayBuffer): Soa {
120125
const edgeCount = dv.getUint32(8, true);
121126
let off = 12;
122127
const cls = new Uint8Array(nodeCount);
128+
// ceiling[i] = 1 when the GUID's HEEL and HIP are both the 0xFFFF sentinel —
129+
// the node is a cross-cutting GLOBAL category (the dual-use axes), not
130+
// basin-local. Read straight off the 16-byte GUID (HEEL @4, HIP @6).
131+
const ceiling = new Uint8Array(nodeCount);
123132
for (let i = 0; i < nodeCount; i++) {
133+
const heel = dv.getUint16(off + 4, true);
134+
const hip = dv.getUint16(off + 6, true);
135+
if (heel === 0xffff && hip === 0xffff) ceiling[i] = 1;
124136
cls[i] = dv.getUint8(off + 16);
125137
off += 17;
126138
}
@@ -150,7 +162,7 @@ function decodeSoa(buf: ArrayBuffer): Soa {
150162
tenants = new Uint8Array(buf, off, nodeCount * 6);
151163
off += nodeCount * 6;
152164
}
153-
return { nodeCount, edgeCount, cls, edges, labels, tenants };
165+
return { nodeCount, edgeCount, cls, edges, labels, tenants, ceiling };
154166
}
155167

156168
// vis-network options tuned to the Palantir look: hollow ring nodes (dark fill
@@ -348,22 +360,26 @@ export function OsintGraph() {
348360
// code on that axis (categorical, computed live across every node); else the
349361
// class colour. This is the dynamic group-by — no baked edges involved.
350362
const nodeBorder = (i: number) => {
363+
if (soa.ceiling[i]) return CEILING_COLOR; // global hubs stay prominent in every mode
351364
const ax = facetAxisRef.current;
352365
if (ax != null && soa.tenants) return facetColor(soa.tenants[i * 6 + ax]);
353366
return classColor(soa.cls[i]);
354367
};
368+
const nodeKind = (i: number) =>
369+
soa.ceiling[i] ? '◈ global category (cross-cutting)' : CLASS[soa.cls[i]]?.name ?? 'concept';
355370
const baseNode = (i: number) => ({
356371
id: i,
357372
label: soa.labels[i] || `#${i}`,
373+
shape: soa.ceiling[i] ? 'diamond' : 'dot',
358374
color: {
359-
background: 'rgba(10,14,23,0.88)',
375+
background: soa.ceiling[i] ? 'rgba(255,209,102,0.14)' : 'rgba(10,14,23,0.88)',
360376
border: nodeBorder(i),
361377
highlight: { background: 'rgba(10,14,23,0.96)', border: '#9fe8ff' },
362378
hover: { background: 'rgba(10,14,23,0.82)', border: nodeBorder(i) },
363379
},
364-
size: baseSize(i),
365-
font: { color: '#d9e9f9' },
366-
title: `${soa.labels[i] || `#${i}`}\n${CLASS[soa.cls[i]]?.name ?? 'concept'} · ${degree.get(i) ?? 0} links`,
380+
size: baseSize(i) + (soa.ceiling[i] ? 7 : 0),
381+
font: { color: soa.ceiling[i] ? '#ffe9b0' : '#d9e9f9' },
382+
title: `${soa.labels[i] || `#${i}`}\n${nodeKind(i)} · ${degree.get(i) ?? 0} links`,
367383
});
368384
const baseEdge = (e: { id: number; s: number; t: number; r: number }) => ({
369385
id: e.id,
@@ -993,6 +1009,20 @@ export function OsintGraph() {
9931009
{k.name}
9941010
</span>
9951011
))}
1012+
<span style={{ marginRight: 12, whiteSpace: 'nowrap' }}>
1013+
<span
1014+
style={{
1015+
display: 'inline-block',
1016+
width: 9,
1017+
height: 9,
1018+
border: `2px solid ${CEILING_COLOR}`,
1019+
marginRight: 5,
1020+
verticalAlign: 'middle',
1021+
transform: 'rotate(45deg)',
1022+
}}
1023+
/>
1024+
◈ global axis
1025+
</span>
9961026
</div>
9971027

9981028
{/* alternative-view link */}

cockpit/src/main.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { NeuralDebuggerPage } from './NeuralDebuggerPage';
77
import { RenderPage, OrbitPage, FlightPage } from './RenderPage';
88
import { OsintScene3D } from './OsintScene3D';
99
import { OsintGraph } from './OsintGraph';
10+
import { FmaGraph } from './FmaGraph';
1011
import { ReasoningPage } from './ReasoningPage';
1112
import { ErrorBoundary } from './components/ErrorBoundary';
1213
import './styles/cockpit.css';
@@ -76,6 +77,8 @@ createRoot(document.getElementById('root')!).render(
7677
<Route path="/" element={<OsintGraph />} />
7778
<Route path="/osint" element={<OsintGraph />} />
7879
<Route path="/osint3d" element={<OsintScene3D />} />
80+
{/* FMA anatomy slice — part-of basin × leaf-limited global type (dual membership) */}
81+
<Route path="/fma" element={<FmaGraph />} />
7982
{/* The Palantir JSON-graph cockpit (221 aiwar nodes) stays reachable
8083
at /palantir and as the catch-all for its own sub-routes. */}
8184
<Route path="/palantir" element={<PalantirApp />} />
0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)