Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .claude/SURGERY_BLACKBOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ started: "2026-03-12"
orchestration_prompt: ".claude/prompts/18_brain_surgery_orchestration.md"

surgeon:
S1_delete_P1: PENDING
S2_delete_P3: PENDING
S1_delete_P1: DONE # Deleted src/query/cypher.rs (1560 lines)
S2_delete_P3: SKIPPED # P3 (lance_parser) is the PRODUCTION parser — kept
S3_stale_prs: PENDING
S4_ci_green: PENDING
S5_rename_p4: PENDING
S5_rename_p4: DONE # CypherOp → CypherInstruction in cam_ops.rs

locksmith:
L1_project_out: PENDING
Expand All @@ -22,7 +22,7 @@ bridge:
B1_match_spo: PENDING
B2_merge_spo: PENDING
B3_edge_spo: PENDING
B4_server_cypher: PENDING
B4_server_cypher: DONE # /cypher now: parse_cypher_query → execute_cypher → BindSpace
B5_crystal_state: PENDING

bouncer:
Expand All @@ -40,7 +40,11 @@ seal:
K5_register: PENDING

blocking_issues: []
decisions_made: []
decisions_made:
- "P3 (lance_parser) is the production parser. S2_delete_P3 skipped — it was misnamed."
- "cypher_bridge.rs rewritten: CypherOp/NodeRef/WhereClause/CypherValue removed. Takes P3 AST directly."
- "CypherResult.rows changed from HashMap<String, CypherValue> to HashMap<String, serde_json::Value> (no more CypherValue)."
- "server.rs /cypher now EXECUTES against BindSpace (was: transpile-only stub)."
notes: |
Read .claude/prompts/18_brain_surgery_orchestration.md for full context.
Read prompts 15, 16, 17, 17a BEFORE starting any work.
53 changes: 13 additions & 40 deletions .github/workflows/ci-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
components: clippy, rustfmt

- uses: Swatinem/rust-cache@v2
Expand Down Expand Up @@ -70,9 +73,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"

- uses: Swatinem/rust-cache@v2

Expand Down Expand Up @@ -107,9 +113,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
components: clippy, rustfmt

- uses: Swatinem/rust-cache@v2
Expand All @@ -120,47 +129,12 @@ jobs:
- name: Rustfmt
run: cargo fmt --all -- --check

# ---------------------------------------------------------------------------
# Miri — catches UB in unsafe code (split_at_mut, raw pointers, etc.)
# ---------------------------------------------------------------------------
miri:
name: Miri (unsafe validation)
needs: build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

- name: Init vendor submodules
run: git submodule update --init vendor/rustynum

- name: Clone sibling repos (OBLIGATORY deps)
run: |
git clone --depth 1 https://github.com/AdaWorldAPI/rustynum.git ../rustynum
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- uses: dtolnay/rust-toolchain@nightly
with:
components: miri

- uses: Swatinem/rust-cache@v2
with:
prefix-key: miri

# 5 min timeout per target — Miri can run 1-3h without timeout
- name: Miri — lib tests (5 min timeout)
run: timeout 300 cargo miri test --lib -- --test-threads=1
env:
MIRIFLAGS: "-Zmiri-disable-isolation"
continue-on-error: true

# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
ci-summary:
name: CI Summary
needs: [build, test, lint, miri]
needs: [build, test, lint]
runs-on: ubuntu-latest
if: always()
steps:
Expand All @@ -173,7 +147,6 @@ jobs:
echo " Build: ${{ needs.build.result }}"
echo " Test: ${{ needs.test.result }}"
echo " Lint: ${{ needs.lint.result }}"
echo " Miri: ${{ needs.miri.result }}"
echo ""
PASS=true
for r in "${{ needs.build.result }}" "${{ needs.test.result }}" "${{ needs.lint.result }}"; do
Expand Down
30 changes: 24 additions & 6 deletions .github/workflows/proof.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: cargo check
run: cargo check --lib --tests
Expand All @@ -91,9 +94,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: Run foundation proofs
run: cargo test --test proof_foundation -- --test-threads=1 --show-output
Expand All @@ -117,9 +123,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: Run reasoning ladder proofs
run: cargo test --test proof_reasoning_ladder -- --test-threads=1 --show-output
Expand All @@ -143,9 +152,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: Run tactics proofs
run: cargo test --test proof_tactics -- --test-threads=1 --show-output
Expand All @@ -169,9 +181,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: Run level A gap proofs
run: cargo test --test proof_level_a_gaps -- --test-threads=1
Expand All @@ -195,9 +210,12 @@ jobs:
git clone --depth 1 https://github.com/AdaWorldAPI/crewai-rust.git ../crewai-rust
git clone --depth 1 https://github.com/AdaWorldAPI/n8n-rs.git ../n8n-rs

- name: Install protoc
run: sudo apt-get install -y protobuf-compiler

- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.93.0"
toolchain: "1.93.1"
- uses: Swatinem/rust-cache@v2
- name: Run all unit tests
run: cargo test --lib -- --test-threads=4
Expand Down
26 changes: 2 additions & 24 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ simd = [] # AVX-512 SIMD for Hamming operations
parallel = ["rayon"] # Parallel processing

# Storage backends
lancedb = ["lance"] # LanceDB vector storage
lancedb = ["dep:lancedb"] # LanceDB vector storage (SDK wrapping Lance)
lance-zero-copy = [] # Zero-copy integration (uses Arrow buffers)
neo4j = ["neo4rs"] # Neo4j graph database
redis = ["dep:redis"] # Redis caching
Expand Down Expand Up @@ -75,7 +75,7 @@ ladybug-contract = { path = "crates/ladybug-contract" }
# Lance 2.0.0 released on crates.io (Feb 5, 2026).
# Arrow 57.x aligns with DataFusion 52 / Lance 2.x.
# -----------------------------------------------------------------------------
lance = { version = "2.0", optional = true, default-features = false }
lancedb = { version = "0.26", optional = true }
arrow = { version = "57", features = ["ffi"] }
arrow-array = "57"
arrow-schema = "57"
Expand Down Expand Up @@ -306,28 +306,6 @@ opt-level = 3
# =============================================================================

# [patch.crates-io]
# Lance vendor patches — UNCOMMENT when lancedb feature is ready and
# submodules are checked out (git submodule update --init --recursive).
# These are NOT needed for production builds (simd,parallel,flight).
# Cargo validates ALL patch paths at resolution time regardless of features,
# so keeping these active breaks builds where submodules aren't cloned
# (Railway, GitHub Actions without actions/checkout submodules: true).
#
# lance = { path = "vendor/lance/rust/lance", default-features = false }
# lance-core = { path = "vendor/lance/rust/lance-core" }
# lance-io = { path = "vendor/lance/rust/lance-io", default-features = false }
# lance-file = { path = "vendor/lance/rust/lance-file" }
# lance-encoding = { path = "vendor/lance/rust/lance-encoding" }
# lance-table = { path = "vendor/lance/rust/lance-table" }
# lance-index = { path = "vendor/lance/rust/lance-index" }
# lance-arrow = { path = "vendor/lance/rust/lance-arrow" }
# lance-linalg = { path = "vendor/lance/rust/lance-linalg" }
# lance-datafusion = { path = "vendor/lance/rust/lance-datafusion" }
# lance-namespace = { path = "vendor/lance/rust/lance-namespace" }
# lance-geo = { path = "vendor/lance/rust/lance-geo" }
# lance-bitpacking = { path = "vendor/lance/rust/compression/bitpacking" }
# fsst = { path = "vendor/lance/rust/compression/fsst" }
# datafusion = { git = "https://github.com/AdaWorldAPI/datafusion", branch = "liblzma-fix" }
#
# n8n-rs vendor overrides — use local clones instead of git fetch:
# git clone https://github.com/AdaWorldAPI/n8n-rs vendor/n8n-rs
Expand Down
81 changes: 49 additions & 32 deletions src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ fn route(
("POST", "/api/v1/sql") | ("POST", "/sql") => handle_sql(body, state, format),

// Cypher endpoint
("POST", "/api/v1/cypher") | ("POST", "/cypher") => handle_cypher(body, format),
("POST", "/api/v1/cypher") | ("POST", "/cypher") => handle_cypher(body, state, format),

// CogRedis text protocol - always uses Redis wire protocol
("POST", "/redis") => handle_redis_command(body, state),
Expand Down Expand Up @@ -1618,39 +1618,56 @@ fn handle_sql(body: &str, _state: &SharedState, format: ResponseFormat) -> Vec<u
}
}

// Cypher handler
fn handle_cypher(body: &str, format: ResponseFormat) -> Vec<u8> {
// Cypher handler — parses via lance_parser, executes against BindSpace via cypher_bridge
fn handle_cypher(body: &str, state: &SharedState, format: ResponseFormat) -> Vec<u8> {
let query = extract_json_str(body, "query").unwrap_or_default();

match ladybug::query::cypher_to_sql(&query) {
Ok(sql) => match format {
ResponseFormat::Arrow => {
let schema = Arc::new(Schema::new(vec![
Field::new("cypher", DataType::Utf8, false),
Field::new("transpiled_sql", DataType::Utf8, false),
Field::new("status", DataType::Utf8, false),
]));
let batch = RecordBatch::try_new(
schema,
vec![
Arc::new(StringArray::from(vec![query.as_str()])) as ArrayRef,
Arc::new(StringArray::from(vec![sql.as_str()])) as ArrayRef,
Arc::new(StringArray::from(vec!["transpiled"])) as ArrayRef,
],
)
.unwrap();
http_arrow(200, &batch)
}
ResponseFormat::Json => {
let json = format!(
r#"{{"cypher":"{}","transpiled_sql":"{}","status":"transpiled"}}"#,
query.replace('"', "'"),
sql.replace('"', "'")
);
http_json(200, &json)
// Parse with lance_parser (P3)
let ast = match ladybug::query::parse_cypher_query(&query) {
Ok(ast) => ast,
Err(e) => return http_error(400, "cypher_parse_error", &format!("{}", e), format),
};

// Execute against BindSpace via cypher_bridge
let mut db = state.write().unwrap();
let bs = db.cog_redis.bind_space_mut();
match ladybug::cypher_bridge::execute_cypher(bs, &ast) {
Ok(result) => {
let result_json = serde_json::json!({
"columns": result.columns,
"rows": result.rows,
"stats": {
"nodes_created": result.nodes_created,
"relationships_created": result.relationships_created,
"properties_set": result.properties_set,
}
});
match format {
ResponseFormat::Arrow => {
let schema = Arc::new(Schema::new(vec![
Field::new("cypher", DataType::Utf8, false),
Field::new("result", DataType::Utf8, false),
Field::new("status", DataType::Utf8, false),
]));
let result_str = serde_json::to_string(&result_json).unwrap_or_default();
let batch = RecordBatch::try_new(
schema,
vec![
Arc::new(StringArray::from(vec![query.as_str()])) as ArrayRef,
Arc::new(StringArray::from(vec![result_str.as_str()])) as ArrayRef,
Arc::new(StringArray::from(vec!["executed"])) as ArrayRef,
],
)
.unwrap();
http_arrow(200, &batch)
}
ResponseFormat::Json => {
let json = serde_json::to_string(&result_json).unwrap_or_default();
http_json(200, &json)
}
}
},
Err(e) => http_error(400, "cypher_parse_error", &e.to_string(), format),
}
Err(e) => http_error(400, "cypher_execution_error", &e, format),
}
}

Expand Down Expand Up @@ -3065,7 +3082,7 @@ fn handle_unified_command(body: &str, state: &SharedState, format: ResponseForma
handle_sql(query, state, format)
} else if first_word == "CYPHER" {
let query = cmd.get(7..).unwrap_or("").trim();
handle_cypher(query, format)
handle_cypher(query, state, format)
} else if first_word.starts_with("CREW.") || first_word.starts_with("AGENT.") {
handle_crew_command(cmd, format)
} else if first_word.starts_with("WF.") || first_word.starts_with("EXEC.") {
Expand Down
Loading
Loading