Skip to content

Commit 2e13a5a

Browse files
Roo Coderuvnet
andcommitted
Add ADR-014 and DDD for semantic pipeline unification sprint
ADR-014: Wire all existing components in a single pass. No backward compat, no fallback paths, no duplicate systems. Delete dead code. Edge colour = domain gradient weighted by relationship power. DDD: 4 bounded contexts (Ingestion, Physics, Analytics, Rendering) with shared EdgeType enum (8 types, weight table, spring multipliers). Every OntologyBlock relationship becomes a Neo4j edge. Every edge type flows to the GPU. Every analytics result flows to the client. Co-Authored-By: claude-flow <[email protected]>
1 parent 530e146 commit 2e13a5a

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# ADR-014: Semantic Pipeline Unification
2+
3+
**Date**: 2026-03-25
4+
**Status**: ACCEPTED
5+
**Decision**: Wire all existing semantic pipeline components into a single data flow. No new systems. No backward compatibility. No fallbacks.
6+
7+
## Context
8+
9+
The VisionFlow codebase contains a complete semantic pipeline — parsers, Neo4j adapters, GPU actors, CUDA kernels, constraint translators, binary protocol fields — built over months but never connected end-to-end. 7 data loss points cause 8/9 relationship types to be dropped, 110K axioms to sit isolated, and GPU analytics to return zeros.
10+
11+
## Decision
12+
13+
**Single pass, single sprint. Delete all fallback paths. Wire source→sink directly.**
14+
15+
### Principles
16+
1. **Markdown is truth** — every relationship in an OntologyBlock becomes a Neo4j edge
17+
2. **No fallback edge generation** — delete `generate_edges_from_metadata()` and `generate_edges_from_labels()`
18+
3. **No dual-path loading**`load_graph()` loads ONE unified graph (GraphNode + OwlClass edges combined)
19+
4. **Edge type flows to GPU** — CSR carries edge_type buffer alongside col_indices
20+
5. **Analytics flow back** — ClusteringActor writes results, binary protocol carries them, client reads them
21+
6. **Edge colour = relationship power** — gradient from source domain colour to target domain colour, weighted by relationship strength
22+
23+
### What We Delete
24+
- `generate_edges_from_metadata()` (dead code, never called)
25+
- `generate_edges_from_labels()` (fallback that produces low-quality namespace-only edges)
26+
- `if iri_to_id.is_empty()` branch in `load_graph()` (either/or path selection)
27+
- `app_state.node_analytics` empty HashMap pattern (replace with write path)
28+
29+
### What We Wire
30+
- `neo4j_ontology_repository::add_owl_class()` → store ALL relationship types as `:RELATES` edges
31+
- `neo4j_adapter::load_graph()` → single query: EDGE + SUBCLASS_OF + RELATES
32+
- `force_compute_actor` → CSR with `edge_types: DeviceBuffer<u8>`
33+
- `clustering_actor``ClientCoordinatorActor``node_analytics` → binary V3
34+
- `ontology_constraint_actor``apply_ontology_constraints()` (remove dead_code annotation)
35+
- `semantic_forces_actor` → receive `source_domain` as type_id, activate type clustering
36+
37+
### Edge Colour Model
38+
Edges render as gradient tubes:
39+
- **Source end**: domain colour of source node (AI=#4FC3F7, BC=#81C784, MV=#CE93D8, etc.)
40+
- **Target end**: domain colour of target node
41+
- **Width**: `edge.weight` (hierarchical=2.5, structural=1.5, dependency=1.5, associative=1.0, bridge=1.0)
42+
- **Opacity**: `relationship_power = weight * (1 + log(edge_count_between_pair))`
43+
- Cross-domain edges create visible colour gradients between domain clusters
44+
45+
## Consequences
46+
47+
- Breaking change: edge count jumps from ~490 to ~1,500+
48+
- Physics will need re-settle with new edge topology
49+
- Cluster hulls will naturally separate into domain groups
50+
- Client edge rendering needs gradient material (already partially in `GlassEdges` with `useGradient: true`)
51+
52+
## Alternatives Considered
53+
54+
- **Keep fallback paths**: Rejected — they mask data quality issues and add code complexity
55+
- **New edge storage system**: Rejected — existing `:EDGE`/`:RELATES`/`:SUBCLASS_OF` pattern is sufficient
56+
- **Client-side edge synthesis**: Rejected — server is source of truth, client should only render

docs/ddd-semantic-pipeline.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# DDD: Semantic Pipeline — Bounded Contexts
2+
3+
**Date**: 2026-03-25
4+
5+
## Domain Model
6+
7+
```
8+
Markdown Source (Aggregate Root)
9+
├── OntologyBlock (Value Object)
10+
│ ├── is_subclass_of: Vec<String> → "hierarchical" edge, weight 2.5
11+
│ ├── has_part: Vec<String> → "structural" edge, weight 1.5
12+
│ ├── is_part_of: Vec<String> → "structural" edge, weight 1.5
13+
│ ├── requires: Vec<String> → "dependency" edge, weight 1.5
14+
│ ├── depends_on: Vec<String> → "dependency" edge, weight 1.5
15+
│ ├── enables: Vec<String> → "dependency" edge, weight 1.5
16+
│ ├── relates_to: Vec<String> → "associative" edge, weight 1.0
17+
│ ├── bridges_to: Vec<String> → "bridge" edge, weight 1.0
18+
│ └── bridges_from: Vec<String> → "bridge" edge, weight 1.0
19+
├── Wikilinks (Value Object) → "explicit_link" edge, weight 1.0
20+
└── Namespace Prefix (derived) → "namespace" edge, weight 0.3
21+
22+
Neo4j (Repository)
23+
├── :GraphNode — display nodes
24+
├── :OwlClass — ontology concepts
25+
├── :EDGE — wikilink + relationship edges (with relation_type, owl_property_iri)
26+
├── :SUBCLASS_OF — hierarchy edges
27+
├── :RELATES — non-hierarchical ontology edges (NEW)
28+
└── :OwlAxiom — reasoner output (materialised → SUBCLASS_OF)
29+
30+
GPU Physics (Domain Service)
31+
├── ForceComputeActor — Barnes-Hut forces + edge springs
32+
│ └── CSR graph: (col_indices, edge_weights, edge_types)
33+
├── SemanticForcesActor — type clustering + DAG layout
34+
│ └── Reads: node type_id (domain), edge_type for spring differentiation
35+
├── OntologyConstraintActor — axiom-derived constraints
36+
│ └── Reads: DisjointWith → separation, SubClassOf → clustering
37+
├── ClusteringActor — k-means / louvain
38+
│ └── Writes: cluster_id per node → app_state.node_analytics
39+
└── AnomalyDetectionActor — LOF / z-score
40+
└── Writes: anomaly_score per node → app_state.node_analytics
41+
42+
Client Rendering (Presentation)
43+
├── Binary Protocol V3: [pos, vel, cluster_id, anomaly_score, community_id]
44+
├── Node colour: domain palette (AI=#4FC3F7, BC=#81C784, MV=#CE93D8, ...)
45+
├── Edge gradient: source_domain_colour → target_domain_colour
46+
├── Edge width: relationship weight (2.5 hierarchical → 1.0 associative)
47+
└── ClusterHulls: convex hull per domain/cluster with domain colour
48+
```
49+
50+
## Bounded Contexts
51+
52+
### 1. Ingestion Context
53+
**Responsibility**: Markdown → Neo4j
54+
**Files**: github_sync_service.rs, ontology_parser.rs, knowledge_graph_parser.rs, neo4j_ontology_repository.rs, neo4j_adapter.rs
55+
**Invariant**: Every relationship in an OntologyBlock becomes a Neo4j edge with type and weight
56+
57+
### 2. Physics Context
58+
**Responsibility**: Neo4j → GPU forces → settled positions
59+
**Files**: graph_state_actor.rs, force_compute_actor.rs, semantic_forces_actor.rs, ontology_constraint_actor.rs
60+
**Invariant**: Edge type influences spring strength. Domain membership influences clustering force.
61+
62+
### 3. Analytics Context
63+
**Responsibility**: GPU clustering/anomaly → client metadata
64+
**Files**: clustering_actor.rs, anomaly_detection_actor.rs, app_state.rs, binary_protocol.rs
65+
**Invariant**: Every node has a cluster_id and anomaly_score delivered to the client
66+
67+
### 4. Rendering Context
68+
**Responsibility**: Positions + metadata → visual output
69+
**Files**: GraphManager.tsx, ClusterHulls.tsx, GlassEdges, binaryProtocol.ts, graph.worker.ts
70+
**Invariant**: Edges are coloured by domain gradient. Nodes are coloured by cluster. Hulls wrap clusters.
71+
72+
## Anti-Corruption Layer
73+
74+
None. Single codebase, single data flow. The markdown parser IS the domain model. Everything downstream is a projection of the parsed OntologyBlock.
75+
76+
## Edge Type Enum (shared across all contexts)
77+
78+
```rust
79+
#[repr(u8)]
80+
pub enum EdgeType {
81+
ExplicitLink = 0, // [[wikilink]]
82+
Hierarchical = 1, // is-subclass-of (rdfs:subClassOf)
83+
Structural = 2, // has-part, is-part-of
84+
Dependency = 3, // requires, depends-on, enables
85+
Associative = 4, // relates-to
86+
Bridge = 5, // bridges-to, bridges-from
87+
Namespace = 6, // shared prefix grouping
88+
Inferred = 7, // whelk reasoner output
89+
}
90+
```
91+
92+
Weight table:
93+
| EdgeType | Default Weight | Spring Multiplier | Colour Influence |
94+
|----------|---------------|-------------------|-----------------|
95+
| Hierarchical | 2.5 | 2.0x | Strong domain pull |
96+
| Structural | 1.5 | 1.5x | Medium clustering |
97+
| Dependency | 1.5 | 1.5x | Medium clustering |
98+
| Associative | 1.0 | 1.0x | Gentle grouping |
99+
| Bridge | 1.0 | 0.5x | Cross-domain (weaker) |
100+
| ExplicitLink | 1.0 | 1.0x | Standard spring |
101+
| Namespace | 0.3 | 0.3x | Weak grouping |
102+
| Inferred | 0.8 | 0.8x | Reasoner-derived |

0 commit comments

Comments
 (0)