Skip to content

Commit 650d2a5

Browse files
authored
Merge pull request #68 from AdaWorldAPI/claude/v3-substrate-migration-review-o0yoxv
OSINT cockpit: wire the full 11-dim V3 stacked cascade + runtime bake
2 parents d888761 + dda56d8 commit 650d2a5

8 files changed

Lines changed: 13730 additions & 6391 deletions

File tree

cockpit/public/aiwar_graph.json

Lines changed: 7266 additions & 6385 deletions
Large diffs are not rendered by default.

crates/cockpit-server/src/main.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,57 @@ use include_dir::{include_dir, Dir};
5151
#[cfg(feature = "embed-cockpit")]
5252
static COCKPIT_DIST: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/../../cockpit/dist");
5353

54-
/// Pre-baked enriched OSINT SoA wire buffer (`osint_gotham::osint_soa_bytes`,
55-
/// baked offline via the `bake_osint_soa` test). Served at `/osint.soa` and
56-
/// decoded to a 3D scene client-side — no cypher at runtime, no JSON.
57-
static OSINT_SOA: &[u8] =
54+
/// Committed fallback OSINT SoA (used only if the harvest graph isn't on disk).
55+
static OSINT_SOA_FALLBACK: &[u8] =
5856
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/osint_scene.soa"));
5957

60-
/// Serve the pre-baked OSINT SoA as raw bytes (octet-stream) for `/osint3d`.
58+
/// OSINT SoA wire buffer, **baked at startup from the on-disk enriched harvest**
59+
/// (`osint_gotham::osint_soa_bytes`) so the served 3D scene reflects the CURRENT
60+
/// `aiwar_graph.json` — the full 11-dim V3 stacked cascade (militaryUse:civicUse,
61+
/// MLTask:MLType, purpose:capacity, output:impact, currentStatus:type,
62+
/// stakeholder:airo:type), not a stale pre-bake. Falls back to the committed
63+
/// asset when the harvest is absent (e.g. a minimal image). One-time init.
64+
static OSINT_SOA: std::sync::LazyLock<Vec<u8>> = std::sync::LazyLock::new(|| {
65+
let candidates = [
66+
// Anchored on the crate dir (baked at build) so it resolves regardless
67+
// of the runtime cwd Railway launches from.
68+
concat!(env!("CARGO_MANIFEST_DIR"), "/../../cockpit/public/aiwar_graph.json"),
69+
"cockpit/public/aiwar_graph.json",
70+
"/home/user/aiwar-neo4j-harvest/data/aiwar_graph.json",
71+
"../aiwar-neo4j-harvest/data/aiwar_graph.json",
72+
];
73+
let Some(path) = candidates.iter().find(|p| std::path::Path::new(p).exists()) else {
74+
tracing::warn!("osint: no aiwar_graph.json on disk — serving committed fallback asset");
75+
return OSINT_SOA_FALLBACK.to_vec();
76+
};
77+
let cypher = std::path::Path::new(path)
78+
.parent()
79+
.and_then(|p| p.parent())
80+
.map(|r| r.join("cypher"));
81+
let graph = match &cypher {
82+
Some(d) if d.is_dir() => aiwar_ingest::load_with_enrichment(path, d),
83+
_ => aiwar_ingest::load_from_file(path),
84+
};
85+
let Ok(graph) = graph else {
86+
tracing::warn!("osint: harvest at {path} failed to load — serving fallback asset");
87+
return OSINT_SOA_FALLBACK.to_vec();
88+
};
89+
let rounds = cypher
90+
.as_ref()
91+
.and_then(|d| aiwar_ingest::encounter_round::load_encounter_rounds(d).ok())
92+
.unwrap_or_default();
93+
tracing::info!(
94+
"osint: baked {} nodes from {path} (11-dim V3 stacked cascade)",
95+
graph.nodes.len()
96+
);
97+
osint_gotham::osint_soa_bytes(&graph, &rounds)
98+
});
99+
100+
/// Serve the baked OSINT SoA as raw bytes (octet-stream) for `/osint3d`.
61101
async fn osint_soa_handler() -> impl axum::response::IntoResponse {
62102
(
63103
[(axum::http::header::CONTENT_TYPE, "application/octet-stream")],
64-
OSINT_SOA,
104+
OSINT_SOA.as_slice(),
65105
)
66106
}
67107

crates/cockpit-server/src/osint_gotham.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ const FACET_AIRO_ROLE: usize = 3; // airo:type — u8 bitset (compound)
114114
const FACET_MLTYPE: usize = 4; // MLTask/MLTasks primary token, u8 code
115115
const FACET_PURPOSE: usize = 5; // purpose / purpose:vair — u8 code
116116
const FACET_CAPACITY: usize = 6; // capacity / capacity:airo — u8 code
117+
// V3 6×(8:8) completion (bytes 7..=11) — the six AIRO/VAIR dimensions the
118+
// original 1..=6 tenant left unstacked. Additive: byte offsets 1..=6 are
119+
// unchanged (client + committed asset stay compatible); these complete the
120+
// stacked cascade so every used property is a hot, groupable value tenant —
121+
// HEEL currentStatus:type · family output:impact · identity stakeholder:airo.
122+
const FACET_STATUS: usize = 7; // currentStatus — lifecycle stage, u8 code
123+
const FACET_TYPE: usize = 8; // type — system/actor class, u8 code
124+
const FACET_OUTPUT: usize = 9; // output:airo — u8 code
125+
const FACET_IMPACT: usize = 10; // impact:vair — primary harm token, u8 code
126+
const FACET_STAKEHOLDER: usize = 11; // stakeholder — owning actor class, u8 code
117127

118128
/// `airo:type` actor roles in bit order. The four canonical AIRO players plus
119129
/// the two rare variants the harvest carries; the game-theory structure is
@@ -275,6 +285,107 @@ const CAPACITY_AIRO: &[&str] = &[
275285
"SignalTracking",
276286
];
277287

288+
/// `currentStatus:airo` lifecycle stage (aiwc.ods column ∪ actor states).
289+
const CURRENT_STATUS: &[&str] = &[
290+
"Development",
291+
"Deployment",
292+
"Operation",
293+
"Retirement",
294+
"Active",
295+
"Historical",
296+
"Deployed",
297+
];
298+
299+
/// `type` — the system/actor class (aiwc.ods `type` column ∪ common instances).
300+
const NODE_TYPE: &[&str] = &[
301+
"IntelligentControlSystem",
302+
"GenerativeModel",
303+
"GenerativeAI",
304+
"NarrowAI",
305+
"TrainingDatabase",
306+
"ExpertSystem",
307+
"ServiceRobot",
308+
"MultiAgentSystem",
309+
"Dashboard",
310+
"Nation",
311+
"TechCompany",
312+
"DefenseCompany",
313+
"Military",
314+
"Investor",
315+
"Institution",
316+
"Utility",
317+
"SurveillanceSystem",
318+
"FacialRecognition",
319+
"AutonomousWeapon",
320+
"TargetingSystem",
321+
"RoboticSystem",
322+
"LoiteringMunition",
323+
"PredictiveAnalytics",
324+
"DataAnalyticsCompany",
325+
"FoundationModelProvider",
326+
"SurveillanceVendor",
327+
"AILab",
328+
"GovernmentOfficial",
329+
"HeadOfState",
330+
"Investor",
331+
"Financier",
332+
"TechExecutive",
333+
];
334+
335+
/// `output:airo` — what the system emits.
336+
const OUTPUT_AIRO: &[&str] = &[
337+
"Action",
338+
"Content",
339+
"Decision",
340+
"Prediction",
341+
"Recommendation",
342+
"Detection",
343+
"Monitoring",
344+
"Identification",
345+
"Generation",
346+
"Investment",
347+
"Influence",
348+
];
349+
350+
/// `impact:vair` — the VAIR harm (primary token of a compound list).
351+
const IMPACT_VAIR: &[&str] = &[
352+
"PsychologicalHarm",
353+
"PhysicalInjury",
354+
"PhysicalHarm",
355+
"WellbeingImpact",
356+
"DistortionInHumanBehavior",
357+
"Overreliance",
358+
"UnderminingFreedom",
359+
"LossOfLife",
360+
"LossOfHumanControl",
361+
"LossOfPrivacy",
362+
"DiscriminationBias",
363+
"ConcentrationOfPower",
364+
"Escalation",
365+
"PolicyCapture",
366+
"Deregulation",
367+
];
368+
369+
/// `stakeholder` — the owning/operating actor class.
370+
const STAKEHOLDER: &[&str] = &[
371+
"Nation",
372+
"TechCompany",
373+
"DefenseCompany",
374+
"Institution",
375+
"Military",
376+
"Police",
377+
"Company",
378+
"Investor",
379+
"Utility",
380+
"Owner",
381+
"CEO",
382+
"Politician",
383+
"Agency",
384+
"Person",
385+
"NGO",
386+
"Consortium",
387+
];
388+
278389
/// Code a single facet value (`1 + index` in its codebook; `0` = absent /
279390
/// unknown). Compound axes (comma-joined) code their **primary** token; the
280391
/// match is case-insensitive so harvest casing drift never silently drops.
@@ -326,6 +437,22 @@ fn write_facet_tenant(value: &mut [u8; 480], props: &HashMap<String, Value>) {
326437
if let Some(v) = s("capacity").or_else(|| s("capacity:airo")) {
327438
value[FACET_CAPACITY] = facet_code(CAPACITY_AIRO, v);
328439
}
440+
// V3 6×(8:8) completion — the six dims the original tenant left unstacked.
441+
if let Some(v) = s("currentStatus").or_else(|| s("currentStatus:airo")) {
442+
value[FACET_STATUS] = facet_code(CURRENT_STATUS, v);
443+
}
444+
if let Some(v) = s("type") {
445+
value[FACET_TYPE] = facet_code(NODE_TYPE, v);
446+
}
447+
if let Some(v) = s("output").or_else(|| s("output:airo")) {
448+
value[FACET_OUTPUT] = facet_code(OUTPUT_AIRO, v);
449+
}
450+
if let Some(v) = s("impact").or_else(|| s("impact:vair")) {
451+
value[FACET_IMPACT] = facet_code(IMPACT_VAIR, v);
452+
}
453+
if let Some(v) = s("stakeholder") {
454+
value[FACET_STAKEHOLDER] = facet_code(STAKEHOLDER, v);
455+
}
329456
}
330457

331458
/// Golden angle (radians) — the φ-spiral / Vogel constant.
@@ -858,6 +985,14 @@ const REL_FACET_AIRO: u8 = 12;
858985
const REL_FACET_MLTYPE: u8 = 13;
859986
const REL_FACET_PURPOSE: u8 = 14;
860987
const REL_FACET_CAPACITY: u8 = 15;
988+
// V3 6×(8:8) completion — the remaining stacked dimensions as traversable facet
989+
// edges (rel 16..20). Same distinct-layer contract as 10..15 (toggleable in the
990+
// client); together the eleven rels put the WHOLE cascade in the schema graph.
991+
const REL_FACET_STATUS: u8 = 16;
992+
const REL_FACET_TYPE: u8 = 17;
993+
const REL_FACET_OUTPUT: u8 = 18;
994+
const REL_FACET_IMPACT: u8 = 19;
995+
const REL_FACET_STAKEHOLDER: u8 = 20;
861996

862997
/// (property-key candidates, facet rel) per dual-use axis. First matching key
863998
/// wins (e.g. `MLTask` before `MLTasks`). Mirrors the facet tenant axes.
@@ -868,6 +1003,11 @@ const FACET_AXES: &[(&[&str], u8)] = &[
8681003
(&["MLTask", "MLTasks"], REL_FACET_MLTYPE),
8691004
(&["purpose", "purpose:vair"], REL_FACET_PURPOSE),
8701005
(&["capacity", "capacity:airo"], REL_FACET_CAPACITY),
1006+
(&["currentStatus", "currentStatus:airo"], REL_FACET_STATUS),
1007+
(&["type"], REL_FACET_TYPE),
1008+
(&["output", "output:airo"], REL_FACET_OUTPUT),
1009+
(&["impact", "impact:vair"], REL_FACET_IMPACT),
1010+
(&["stakeholder"], REL_FACET_STAKEHOLDER),
8711011
];
8721012

8731013
/// Entity → `SchemaValue` facet edges: for each node carrying a dual-use facet
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# OSINT-V3 SoA Bake — aiwar-neo4j-harvest → 6×(8:8) substrate
2+
3+
Baked from `AdaWorldAPI/aiwar-neo4j-harvest` (611 nodes) into the V3 SoA
4+
`6×(8:8) part_of:is_a` centroid cascade (CAM-PQ-shaped) under classid
5+
`0x1000_0700` (System) / `0x1000_0701` (Person, McClelland).
6+
7+
## Assets
8+
- `osint_v3.soa` — binary SoA. Header `OSINTV3\0` + `ver(u16=3) count(u32) stride(u16=36)`,
9+
then `count` records of `row_id(u32) | GUID1(16B) | GUID2(16B)` little-endian.
10+
- `osint_v3_codebook.json` — the aiwc.ods controlled vocab → byte map, tier layout, classids.
11+
- `osint_v3_nodes.json` — per-node index (row, id, name, guid1/guid2 hex, is_person).
12+
13+
## GUID1 (identity+location, 6×(8:8), CAM-PQ-shaped)
14+
`[classid u32][HEEL|HIP|TWIG|LEAF|family|identity]` — each tier `hi:lo` byte-pair:
15+
HEEL currentStatus:type · HIP militaryUse:civicUse · TWIG MLTask:MLType ·
16+
LEAF purpose:capacity · family output:impact · identity stakeholder:airo_type.
17+
18+
## GUID2 (relationships, McClelland, persons)
19+
`[classid u32][stage:need|receptor:rubicon|motive:_|...]`.
20+
21+
## Findings (this bake)
22+
- Codebook max cardinality = 30 → every dim fits u8 (u16 is 8× overkill).
23+
- 45 dual-use HIP basins (militaryUse:civicUse prefix) emerge with zero hub nodes —
24+
the AIRO "island" axes collapse into prefix-adjacency.
25+
- 78 collision groups = same-type basins (finance/politician/…), u32 row = local identity.
26+
- 133 persons carry a McClelland GUID2.
27+
28+
Provisional: enrichment for unwired dims was inferred by a 15-agent sweep against
29+
the aiwc.ods controlled vocabulary; base dims (type/currentStatus/stakeholder) are
30+
from the harvest. Reconcile against ground-truth cypher enrichments before locking.

0 commit comments

Comments
 (0)