Skip to content

Commit 4128b6b

Browse files
hyperpolymathclaude
andcommitted
feat: git-reticulator — semantic lattice builder for git repos in AffineScript
Rust CLI + library for building semantic lattices from git repository structure. AffineScript engine integration, REST API, PostgreSQL backing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 parents  commit 4128b6b

13 files changed

Lines changed: 471 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Rust build artifacts
2+
/target/
3+
Cargo.lock

Cargo.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[package]
2+
name = "git-reticulator"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Semantic lattice builder for git repositories in AffineScript"
6+
license = "PMPL-1.0-or-later"
7+
authors = ["Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>"]
8+
repository = "https://github.com/hyperpolymath/git-reticulator"
9+
readme = "README.md"
10+
11+
[dependencies]
12+
# Core dependencies (always required)
13+
tokio = { version = "1.0", features = ["full"] }
14+
actix-web = "4.0" # REST API server
15+
serde = { version = "1.0", features = ["derive"] }
16+
serde_json = "1.0"
17+
log = "0.4"
18+
env_logger = "0.10"
19+
clap = { version = "4.0", features = ["derive"] }
20+
21+
# Optional dependencies (feature-gated)
22+
affinescript = { package = "affinescript-runtime", path = "../nextgen-languages/affinescript/runtime", optional = true }
23+
reqwest = { version = "0.12", features = ["json"], optional = true }
24+
git2 = { version = "0.19", optional = true } # Git repo parsing
25+
postgres = { version = "0.19", features = ["with-uuid-1"], optional = true }
26+
tch = { version = "0.13", optional = true } # PyTorch embeddings
27+
28+
[features]
29+
default = []
30+
affinescript-engine = ["affinescript"] # AffineScript runtime integration
31+
git-integration = ["git2"] # Git repository parsing
32+
db = ["postgres"] # PostgreSQL connectivity
33+
embeddings = ["tch"] # PyTorch-based embeddings
34+
http-client = ["reqwest"] # HTTP client for external calls
35+
full = ["affinescript-engine", "git-integration", "db", "embeddings", "http-client"]
36+
37+
[dev-dependencies]
38+
criterion = "0.4" # For benchmarks
39+
40+
[lib]
41+
name = "git_reticulator"
42+
path = "src/lib.rs"
43+
44+
[[bin]]
45+
name = "reticulate"
46+
path = "src/cli/main.rs"

Justfile

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Justfile for Git-Reticulator orchestration
2+
3+
DB_URL := "postgresql://hyper:password@localhost/git_reticulator"
4+
5+
# Build all components
6+
build:
7+
cargo build
8+
9+
# Build the semantic lattice of the current repository
10+
reticulate repo_path=".":
11+
cargo run -- build --repo {{repo_path}} --db {{DB_URL}}
12+
13+
# Start the REST API server
14+
serve:
15+
cargo run -- api --db {{DB_URL}}
16+
17+
# Run a sample query to zoom into the auth module
18+
query-auth:
19+
cargo run -- query --zoom "auth" --db {{DB_URL}}
20+
21+
# Run tests (AffineScript and Rust)
22+
test:
23+
cargo test
24+
# Add AffineScript-specific tests if available
25+
# as-test tests/lattice_tests.as
26+
27+
# Clean up build artifacts
28+
clean:
29+
cargo clean
30+
31+
# Format code
32+
fmt:
33+
cargo fmt --all
34+
35+
# Check formatting without modifying
36+
fmt-check:
37+
cargo fmt --all --check
38+
39+
# Run panic-attacker pre-commit scan
40+
assail:
41+
@command -v panic-attack >/dev/null 2>&1 && panic-attack assail . || echo "panic-attack not found — install from https://github.com/hyperpolymath/panic-attacker"

README.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
= git-reticulator
2+
:toc: preamble
3+
:icons: font
4+
5+

docs/setup.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Git-Reticulator: Database Setup
2+
3+
To support topological queries and hierarchical LOD (Level of Detail) zooming, Git-Reticulator uses **PostgreSQL** with the **pgRouting** extension.
4+
5+
## 🐘 Prerequisites
6+
- PostgreSQL 14+
7+
- PostGIS
8+
- pgRouting
9+
10+
## 🛠️ Schema Initialization
11+
12+
Run the following SQL to set up the semantic lattice environment:
13+
14+
```sql
15+
-- Enable necessary extensions
16+
CREATE EXTENSION IF NOT EXISTS postgis;
17+
CREATE EXTENSION IF NOT EXISTS pgrouting;
18+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
19+
20+
-- 1. Keywords Table (Nodes in the Lattice)
21+
-- Stores semantic identities at multiple LOD levels.
22+
CREATE TABLE IF NOT EXISTS keywords (
23+
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
24+
name TEXT NOT NULL,
25+
file_path TEXT NOT NULL,
26+
level TEXT NOT NULL CHECK (level IN ('Module', 'File', 'Definition', 'Block')),
27+
parent_id UUID REFERENCES keywords(id),
28+
embedding VECTOR(1536), -- Optional: requires pgvector for semantic search
29+
cluster TEXT,
30+
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
31+
UNIQUE(name, file_path, level)
32+
);
33+
34+
-- Index for hierarchical lookups (Zooming)
35+
CREATE INDEX idx_keywords_parent ON keywords(parent_id);
36+
CREATE INDEX idx_keywords_level ON keywords(level);
37+
38+
-- 2. Relationships Table (Edges in the Lattice)
39+
-- Optimized for pgRouting topological queries.
40+
CREATE TABLE IF NOT EXISTS relationships (
41+
id SERIAL PRIMARY KEY,
42+
source_id UUID NOT NULL REFERENCES keywords(id),
43+
target_id UUID NOT NULL REFERENCES keywords(id),
44+
weight FLOAT DEFAULT 1.0,
45+
rel_type TEXT NOT NULL, -- e.g., 'calls', 'contains', 'depends_on'
46+
47+
-- pgRouting required columns for topology
48+
source INTEGER,
49+
target INTEGER,
50+
cost FLOAT DEFAULT 1.0,
51+
reverse_cost FLOAT DEFAULT -1.0 -- -1 means one-way
52+
);
53+
54+
-- 3. Topology Generation
55+
-- After inserting relationships, run pgr_createTopology to populate source/target integers.
56+
-- This is handled by the Git-Reticulator storage engine.
57+
```
58+
59+
## 🔍 Example pgRouting Query (Shortest Semantic Path)
60+
61+
To find the most direct semantic relationship between two nodes:
62+
63+
```sql
64+
SELECT * FROM pgr_dijkstra(
65+
'SELECT id, source, target, cost FROM relationships',
66+
1, -- Start node ID (integer)
67+
50, -- End node ID (integer)
68+
directed := true
69+
);
70+
```

src/api/app.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use crate::lattice::affine;
2+
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
3+
use serde::{Deserialize, Serialize};
4+
5+
#[derive(Deserialize)]
6+
pub struct BuildRequest {
7+
pub repo: String,
8+
pub db: String,
9+
}
10+
11+
#[derive(Deserialize)]
12+
pub struct QueryRequest {
13+
pub zoom: String,
14+
pub db: String,
15+
}
16+
17+
#[derive(Serialize)]
18+
pub struct ApiResponse {
19+
pub status: String,
20+
pub message: String,
21+
}
22+
23+
#[post("/build")]
24+
async fn build_lattice(req: web::Json<BuildRequest>) -> impl Responder {
25+
println!("🚀 API: Reticulating repo: {}", req.repo);
26+
affine::build_lattice(&req.repo, &req.db);
27+
HttpResponse::Ok().json(ApiResponse {
28+
status: "success".to_string(),
29+
message: format!("Lattice built for {}", req.repo),
30+
})
31+
}
32+
33+
#[get("/zoom/{node_id}")]
34+
async fn zoom_node(path: web::Path<String>, query: web::Query<QueryRequest>) -> impl Responder {
35+
let node_id = path.into_inner();
36+
println!("🔍 API: Zooming into node: {}", node_id);
37+
affine::query_lattice(&node_id, &query.db);
38+
HttpResponse::Ok().json(ApiResponse {
39+
status: "success".to_string(),
40+
message: format!("Zoomed into node {}", node_id),
41+
})
42+
}
43+
44+
#[get("/health")]
45+
async fn health() -> impl Responder {
46+
HttpResponse::Ok().body("Git-Reticulator API is healthy.")
47+
}
48+
49+
pub async fn start_server(db_uri: String) -> std::io::Result<()> {
50+
println!("🌐 Git-Reticulator API starting on http://localhost:8080");
51+
52+
HttpServer::new(move || {
53+
App::new()
54+
.app_data(web::Data::new(db_uri.clone()))
55+
.service(build_lattice)
56+
.service(zoom_node)
57+
.service(health)
58+
})
59+
.bind(("127.0.0.1", 8080))?
60+
.run()
61+
.await
62+
}

src/cli/main.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use clap::{Parser, Subcommand};
2+
use git_reticulator::lattice::affine;
3+
4+
#[derive(Parser)]
5+
#[command(name = "reticulate")]
6+
#[command(about = "Git-Reticulator: Build and query semantic lattices from git repos", long_about = None)]
7+
struct Cli {
8+
#[command(subcommand)]
9+
command: Commands,
10+
}
11+
12+
#[derive(Subcommand)]
13+
enum Commands {
14+
/// Build a semantic lattice from a git repo
15+
Build {
16+
/// Path to the git repository
17+
#[arg(short, long)]
18+
repo: String,
19+
/// PostgreSQL database URI
20+
#[arg(short, long)]
21+
db: String,
22+
},
23+
/// Query the lattice with a zoom level to minimize token cost
24+
Query {
25+
/// Semantic node or keyword to zoom into
26+
#[arg(short, long)]
27+
zoom: String,
28+
/// PostgreSQL database URI
29+
#[arg(short, long)]
30+
db: String,
31+
},
32+
/// Start the REST API server for LLM integration
33+
Api {
34+
/// PostgreSQL database URI
35+
#[arg(short, long)]
36+
db: String,
37+
},
38+
}
39+
40+
#[tokio::main]
41+
async fn main() {
42+
let cli = Cli::parse();
43+
env_logger::init();
44+
45+
match &cli.command {
46+
Commands::Build { repo, db } => {
47+
println!("🚀 Starting reticulation process...");
48+
affine::build_lattice(repo, db);
49+
println!("✅ Semantic lattice built and stored.");
50+
}
51+
Commands::Query { zoom, db } => {
52+
println!("🔍 Querying lattice for context: {}", zoom);
53+
affine::query_lattice(zoom, db);
54+
}
55+
Commands::Api { db } => {
56+
println!("🌐 Starting Git-Reticulator API on http://localhost:8080");
57+
println!("Using database: {}", db);
58+
git_reticulator::api::app::start_server(db.clone())
59+
.await
60+
.expect("Failed to start API server");
61+
}
62+
}
63+
}

src/lattice/affine/lattice.as

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Lattice builder in AffineScript
2+
// Implements Level-of-Detail (LOD) zooming logic to minimize LLM token cost.
3+
4+
import models::{Lattice, Keyword, SemanticLevel, Relationship};
5+
6+
@doc "Builds the semantic lattice of a Git repository."
7+
def build_lattice(repo_path: String, db_uri: String) -> Lattice {
8+
// 1. Repository Ingestion
9+
let repo = git2::Repository::open(repo_path).unwrap();
10+
let files = repo.list_files();
11+
12+
// 2. Multilevel Keyword Extraction (Hierarchical Zooming)
13+
// Extract keywords at different LOD levels
14+
let keywords = files.flat_map(|file| {
15+
[
16+
extract_file_level_keyword(file),
17+
extract_definition_level_keywords(file)
18+
].flatten()
19+
});
20+
21+
// 3. Topological Relationship Discovery
22+
// Preserves semantics: "login" -> "session" -> "database"
23+
let relationships = build_relationships(keywords);
24+
25+
// 4. Persistence with pgRouting Topography
26+
let storage = LatticeStorage::new(db_uri);
27+
storage.store_keywords(keywords);
28+
storage.store_relationships(relationships);
29+
30+
return Lattice { keywords, relationships };
31+
}
32+
33+
@doc "Exposes a zoomed-in view of a semantic node to reduce LLM token noise."
34+
def zoom_to_node(node_id: UUID, target_level: SemanticLevel) -> Lattice {
35+
// Queries pgRouting for children nodes at target_level
36+
let children = storage.get_children(node_id, target_level);
37+
let internal_edges = storage.get_edges_between(children);
38+
39+
return Lattice { keywords: children, relationships: internal_edges };
40+
}

src/lattice/affine/models.as

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Data models for the Git-Reticulator semantic lattice.
2+
// Supports Hierarchical LOD (Level of Detail) to minimize LLM token cost.
3+
4+
type SemanticLevel =
5+
| Module // Root/Subsystem level
6+
| File // File-system object level
7+
| Definition // Class/Function/Constant level
8+
| Block // Granular logical/control-flow level
9+
10+
type Keyword = {
11+
name: String,
12+
file: String,
13+
level: SemanticLevel,
14+
parent_id: Option<UUID>, // For hierarchical "zooming"
15+
embedding: Option<Vec<f64>>,
16+
cluster: Option<String>
17+
}
18+
19+
type Relationship = {
20+
source_id: UUID,
21+
target_id: UUID,
22+
weight: f64,
23+
rel_type: String // e.g., "calls", "contains", "inherits", "depends_on"
24+
}
25+
26+
type Lattice = {
27+
keywords: Vec<Keyword>,
28+
relationships: Vec<Relationship>
29+
}

0 commit comments

Comments
 (0)