From 492f87ea7a32eafd9a444bb1590679aba0e4f3df Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Tue, 14 Apr 2026 10:35:58 +0200 Subject: [PATCH 1/2] fix: fix Docker build and crates.io publishing - Dockerfile.multiarch: replace stale partial crate copy with proper workspace COPY directives (crates/, terraphim_server, terraphim_ai_nodejs, terraphim_firecracker/Cargo.toml) - Dockerfile.multiarch: update default RUST_VERSION from 1.85.0 to 1.92.0 - .dockerignore: allow terraphim_firecracker/Cargo.toml through - Move review types (FindingSeverity, FindingCategory, ReviewFinding, ReviewAgentOutput, deduplicate_findings) from terraphim_symphony to terraphim_types, removing terraphim_symphony dependency from terraphim_orchestrator (fixes crates.io publish: symphony is excluded from workspace) - Add terraphim_tracker and terraphim_orchestrator to publish-crates.sh CRATES array in correct dependency order --- .dockerignore | 3 +- Cargo.lock | 94 ------------------- .../docs/src/kg/test_ranking_kg.md | 13 --- .../tests/integration_tests.rs | 21 ++++- crates/terraphim_orchestrator/Cargo.toml | 1 - crates/terraphim_orchestrator/src/compound.rs | 8 +- crates/terraphim_orchestrator/src/lib.rs | 4 +- .../tests/orchestrator_tests.rs | 27 ++++-- crates/terraphim_types/src/lib.rs | 6 ++ crates/terraphim_types/src/review.rs | 83 ++++++++++++++++ docker/Dockerfile.multiarch | 56 ++--------- scripts/publish-crates.sh | 2 + 12 files changed, 145 insertions(+), 173 deletions(-) delete mode 100644 crates/terraphim_agent/docs/src/kg/test_ranking_kg.md create mode 100644 crates/terraphim_types/src/review.rs diff --git a/.dockerignore b/.dockerignore index 863f9a51d..b609cbe25 100644 --- a/.dockerignore +++ b/.dockerignore @@ -154,4 +154,5 @@ desktop/package-lock.json crates/terraphim_rlm/ crates/terraphim_github_runner/ crates/terraphim_github_runner_server/ -terraphim_firecracker/ +terraphim_firecracker/* +!terraphim_firecracker/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 0fedc744b..ca378eece 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,12 +134,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "anymap2" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" - [[package]] name = "aquamarine" version = "0.6.0" @@ -3927,16 +3921,6 @@ dependencies = [ "libc", ] -[[package]] -name = "kstring" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" -dependencies = [ - "serde", - "static_assertions", -] - [[package]] name = "lab" version = "0.11.0" @@ -4076,60 +4060,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" -[[package]] -name = "liquid" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a494c3f9dad3cb7ed16f1c51812cbe4b29493d6c2e5cd1e2b87477263d9534d" -dependencies = [ - "liquid-core", - "liquid-derive", - "liquid-lib", - "serde", -] - -[[package]] -name = "liquid-core" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc623edee8a618b4543e8e8505584f4847a4e51b805db1af6d9af0a3395d0d57" -dependencies = [ - "anymap2", - "itertools 0.14.0", - "kstring", - "liquid-derive", - "pest", - "pest_derive", - "regex", - "serde", - "time", -] - -[[package]] -name = "liquid-derive" -version = "0.26.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de66c928222984aea59fcaed8ba627f388aaac3c1f57dcb05cc25495ef8faefe" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "liquid-lib" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9befeedd61f5995bc128c571db65300aeb50d62e4f0542c88282dbcb5f72372a" -dependencies = [ - "itertools 0.14.0", - "liquid-core", - "percent-encoding", - "regex", - "time", - "unicode-segmentation", -] - [[package]] name = "litemap" version = "0.8.2" @@ -9097,7 +9027,6 @@ dependencies = [ "terraphim_persistence", "terraphim_router", "terraphim_spawner", - "terraphim_symphony", "terraphim_tracker", "terraphim_types", "thiserror 1.0.69", @@ -9322,29 +9251,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "terraphim_symphony" -version = "1.13.0" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "clap", - "liquid", - "nix 0.27.1", - "notify 6.1.1", - "reqwest 0.12.28", - "serde", - "serde_json", - "serde_yaml", - "terraphim_tracker", - "thiserror 1.0.69", - "tokio", - "tracing", - "tracing-subscriber", - "uuid", -] - [[package]] name = "terraphim_task_decomposition" version = "1.0.0" diff --git a/crates/terraphim_agent/docs/src/kg/test_ranking_kg.md b/crates/terraphim_agent/docs/src/kg/test_ranking_kg.md deleted file mode 100644 index e98c4ab7a..000000000 --- a/crates/terraphim_agent/docs/src/kg/test_ranking_kg.md +++ /dev/null @@ -1,13 +0,0 @@ -# Test Ranking Knowledge Graph - -### machine-learning -Machine learning enables systems to learn from experience. - -### rust -Rust is a systems programming language focused on safety. - -### python -Python is a high-level programming language. - -### search-algorithm -Search algorithms find data in structures. diff --git a/crates/terraphim_agent/tests/integration_tests.rs b/crates/terraphim_agent/tests/integration_tests.rs index ab1248310..76aeee5c4 100644 --- a/crates/terraphim_agent/tests/integration_tests.rs +++ b/crates/terraphim_agent/tests/integration_tests.rs @@ -325,7 +325,7 @@ async fn test_end_to_end_server_workflow() -> Result<()> { "Server should have roles available" ); - let selected_role = server_roles + let current_role = server_roles .iter() .find_map(|line| { let trimmed = line.trim(); @@ -342,6 +342,25 @@ async fn test_end_to_end_server_workflow() -> Result<()> { }) .unwrap_or_else(|| "Terraphim Engineer".to_string()); + let selected_role = server_roles + .iter() + .find_map(|line| { + let trimmed = line.trim(); + if trimmed.starts_with('*') { + return None; + } + let role = trimmed + .trim_start_matches('*') + .trim() + .split_once(" (") + .map(|(name, _)| name) + .unwrap_or(trimmed) + .trim() + .to_string(); + (role != current_role).then_some(role) + }) + .unwrap_or_else(|| current_role.clone()); + // 3. Test search with server (may fail in CI due to missing KG data or slow indexing) let (search_stdout, search_stderr, search_code) = run_server_command(&server_url, &["search", "integration test", "--limit", "3"])?; diff --git a/crates/terraphim_orchestrator/Cargo.toml b/crates/terraphim_orchestrator/Cargo.toml index adf4eafc7..cd81455e4 100644 --- a/crates/terraphim_orchestrator/Cargo.toml +++ b/crates/terraphim_orchestrator/Cargo.toml @@ -17,7 +17,6 @@ terraphim_spawner = { path = "../terraphim_spawner", version = "1.0.0" } terraphim_router = { path = "../terraphim_router", version = "1.0.0" } terraphim_types = { path = "../terraphim_types", version = "1.0.0" } terraphim_tracker = { path = "../terraphim_tracker", version = "1.0.0" } -terraphim_symphony = { path = "../terraphim_symphony", version = "1.0.0" } # Core dependencies tokio = { workspace = true, features = ["full", "signal"] } diff --git a/crates/terraphim_orchestrator/src/compound.rs b/crates/terraphim_orchestrator/src/compound.rs index 3c3a250ed..c90e59041 100644 --- a/crates/terraphim_orchestrator/src/compound.rs +++ b/crates/terraphim_orchestrator/src/compound.rs @@ -5,9 +5,7 @@ use tokio::sync::mpsc; use tracing::{debug, info, warn}; use uuid::Uuid; -use terraphim_symphony::runner::protocol::{ - FindingCategory, FindingSeverity, ReviewAgentOutput, ReviewFinding, -}; +use terraphim_types::{FindingCategory, FindingSeverity, ReviewAgentOutput, ReviewFinding}; use crate::config::CompoundReviewConfig; use crate::error::OrchestratorError; @@ -376,7 +374,7 @@ impl CompoundReviewWorkflow { .iter() .flat_map(|o| o.findings.clone()) .collect(); - let deduplicated = terraphim_symphony::runner::protocol::deduplicate_findings(all_findings); + let deduplicated = terraphim_types::deduplicate_findings(all_findings); // Determine overall pass/fail let pass = agent_outputs.iter().all(|o| o.pass) && failed_count == 0; @@ -898,7 +896,7 @@ fn default_groups() -> Vec { #[cfg(test)] mod tests { use super::*; - use terraphim_symphony::runner::protocol::FindingSeverity; + use terraphim_types::FindingSeverity; // ==================== Visual File Detection Tests ==================== diff --git a/crates/terraphim_orchestrator/src/lib.rs b/crates/terraphim_orchestrator/src/lib.rs index 7bc13c647..8bf258b8b 100644 --- a/crates/terraphim_orchestrator/src/lib.rs +++ b/crates/terraphim_orchestrator/src/lib.rs @@ -88,7 +88,7 @@ pub use nightwatch::{ pub use output_poster::OutputPoster; pub use persona::{MetapromptRenderError, MetapromptRenderer, PersonaRegistry}; pub use scheduler::{ScheduleEvent, TimeScheduler}; -use terraphim_symphony::runner::protocol::{FindingSeverity, ReviewFinding}; +use terraphim_types::{FindingSeverity, ReviewFinding}; use chrono::Timelike; use std::collections::HashMap; @@ -2089,7 +2089,7 @@ impl AgentOrchestrator { result: &CompoundReviewResult, finding: &ReviewFinding, ) -> Result<(), String> { - use terraphim_symphony::runner::protocol::FindingSeverity; + use terraphim_types::FindingSeverity; let sev_str = match finding.severity { FindingSeverity::Critical => "CRITICAL", diff --git a/crates/terraphim_orchestrator/tests/orchestrator_tests.rs b/crates/terraphim_orchestrator/tests/orchestrator_tests.rs index c032f7710..b46ca9339 100644 --- a/crates/terraphim_orchestrator/tests/orchestrator_tests.rs +++ b/crates/terraphim_orchestrator/tests/orchestrator_tests.rs @@ -9,15 +9,24 @@ use terraphim_orchestrator::{ }; use uuid::Uuid; -/// Return a deterministic baseline commit for git-diff tests. -/// Uses the repository root commit so tests work in any clone. -fn baseline_commit() -> String { +/// Return a deterministic baseline for git-diff tests. +/// Prefer the repository root commit, but fall back to the empty tree when the +/// checkout is shallow and history before HEAD is unavailable. +fn git_diff_baseline() -> String { let output = std::process::Command::new("git") .args(["rev-list", "--max-parents=0", "HEAD"]) .output() .expect("git rev-list failed"); let commits = String::from_utf8_lossy(&output.stdout); - commits.lines().next().unwrap_or("").trim().to_string() + let baseline = commits.lines().next().unwrap_or("").trim(); + + if baseline.is_empty() { + // Git's well-known empty tree object works as a stable diff baseline + // even when CI checks out a shallow clone without HEAD~1 available. + "4b825dc642cb6eb9a060e54bf8d69288fbee4904".to_string() + } else { + baseline.to_string() + } } fn test_config() -> OrchestratorConfig { @@ -181,7 +190,7 @@ async fn test_orchestrator_compound_review_integration() { }; let workflow = CompoundReviewWorkflow::new(swarm_config); - let result = workflow.run("HEAD", "HEAD~1").await.unwrap(); + let result = workflow.run("HEAD", &git_diff_baseline()).await.unwrap(); assert!( !result.correlation_id.is_nil(), @@ -462,7 +471,7 @@ async fn test_git_diff_matching_changes_spawns() { let mut orch = AgentOrchestrator::new(config).unwrap(); // Seed with initial commit so there ARE changes - let baseline_commit = baseline_commit(); + let baseline_commit = git_diff_baseline(); orch.set_last_run_commit("sentinel", &baseline_commit); let result = orch.spawn_agent_for_test("sentinel").await; @@ -481,7 +490,7 @@ async fn test_git_diff_non_matching_changes_skips() { let mut orch = AgentOrchestrator::new(config).unwrap(); // Seed with initial commit so there ARE changes, but none match the watch path - let baseline_commit = baseline_commit(); + let baseline_commit = git_diff_baseline(); orch.set_last_run_commit("sentinel", &baseline_commit); let result = orch.spawn_agent_for_test("sentinel").await; @@ -608,7 +617,7 @@ async fn test_spawn_agent_skipped_by_git_diff_no_matching() { let mut orch = AgentOrchestrator::new(config).unwrap(); // Seed with initial commit so there are diff results - let baseline_commit = baseline_commit(); + let baseline_commit = git_diff_baseline(); orch.set_last_run_commit("sentinel", &baseline_commit); let result = orch.spawn_agent_for_test("sentinel").await; @@ -628,7 +637,7 @@ async fn test_spawn_agent_proceeds_with_git_diff_findings() { let mut orch = AgentOrchestrator::new(config).unwrap(); // Use initial commit as baseline -> every file is a change - let baseline_commit = baseline_commit(); + let baseline_commit = git_diff_baseline(); orch.set_last_run_commit("sentinel", &baseline_commit); let result = orch.spawn_agent_for_test("sentinel").await; diff --git a/crates/terraphim_types/src/lib.rs b/crates/terraphim_types/src/lib.rs index fbd85858c..77e027d35 100644 --- a/crates/terraphim_types/src/lib.rs +++ b/crates/terraphim_types/src/lib.rs @@ -105,6 +105,12 @@ pub use procedure::*; pub mod persona; pub use persona::{CharacteristicDef, PersonaDefinition, PersonaLoadError, SfiaSkillDef}; +// Review finding types for multi-agent code review +pub mod review; +pub use review::{ + FindingCategory, FindingSeverity, ReviewAgentOutput, ReviewFinding, deduplicate_findings, +}; + use ahash::AHashMap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::collections::HashSet; diff --git a/crates/terraphim_types/src/review.rs b/crates/terraphim_types/src/review.rs new file mode 100644 index 000000000..2ae48cfec --- /dev/null +++ b/crates/terraphim_types/src/review.rs @@ -0,0 +1,83 @@ +//! Review finding types for multi-agent code review. +//! +//! These types define the protocol for structured code review findings +//! exchanged between review agents. Previously lived in `terraphim_symphony::runner::protocol`. + +use serde::{Deserialize, Serialize}; + +/// Severity level for a review finding. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum FindingSeverity { + Info, + Low, + Medium, + High, + Critical, +} + +/// Category of a review finding (maps to review groups). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum FindingCategory { + Security, + Architecture, + Performance, + Quality, + Domain, + DesignQuality, +} + +/// A single structured finding from a review agent. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ReviewFinding { + pub file: String, + #[serde(default)] + pub line: u32, + pub severity: FindingSeverity, + pub category: FindingCategory, + pub finding: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub suggestion: Option, + #[serde(default = "default_confidence")] + pub confidence: f64, +} + +fn default_confidence() -> f64 { + 0.5 +} + +/// Output schema for a single review agent's results. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ReviewAgentOutput { + pub agent: String, + pub findings: Vec, + pub summary: String, + pub pass: bool, +} + +/// Deduplicate findings by keeping the most severe finding per (file, line, category) key. +/// +/// Results are sorted by severity (descending), then file, then line. +pub fn deduplicate_findings(findings: Vec) -> Vec { + use std::collections::HashMap; + let mut best: HashMap<(String, u32, FindingCategory), ReviewFinding> = HashMap::new(); + for finding in findings { + let key = (finding.file.clone(), finding.line, finding.category); + best.entry(key) + .and_modify(|existing| { + if finding.severity > existing.severity { + *existing = finding.clone(); + } + }) + .or_insert(finding); + } + let mut result: Vec = best.into_values().collect(); + result.sort_by(|a, b| { + b.severity + .cmp(&a.severity) + .then_with(|| a.file.cmp(&b.file)) + .then_with(|| a.line.cmp(&b.line)) + }); + result +} diff --git a/docker/Dockerfile.multiarch b/docker/Dockerfile.multiarch index bcfcc5bc4..7f6b748bc 100644 --- a/docker/Dockerfile.multiarch +++ b/docker/Dockerfile.multiarch @@ -2,7 +2,7 @@ # Supports Ubuntu 18.04, 20.04, 22.04, and 24.04 with multiple architectures ARG UBUNTU_VERSION=22.04 -ARG RUST_VERSION=1.85.0 +ARG RUST_VERSION=1.92.0 ARG NODE_VERSION=20 # ================================ @@ -102,54 +102,15 @@ RUN case "${TARGETARCH}" in \ WORKDIR /code -# Copy dependency files first for better caching +# Copy the workspace pieces required to resolve local path dependencies. COPY Cargo.toml Cargo.lock ./ - -# Copy crate Cargo.toml files, excluding firecracker crates with private git deps -# These are excluded from workspace builds but cargo fetch still sees them -RUN mkdir -p crates -COPY crates/*/Cargo.toml crates_temp/ -RUN sh -c 'for f in crates_temp/*.toml; do \ - name=$(basename "$f"); \ - case "$name" in \ - "terraphim_rlm"*|"terraphim_github_runner"*|"terraphim_firecracker"*) \ - echo "Skipping $name (private git deps)" ;; \ - *) \ - cp "$f" "crates/$name" ;; \ - esac; \ - done && rm -rf crates_temp' -COPY terraphim_server/Cargo.toml terraphim_server/ - -# Create empty source files to satisfy cargo -RUN find crates -name Cargo.toml -exec dirname {} \; | while read dir; do \ - mkdir -p "$dir/src" && echo 'fn main() {}' > "$dir/src/lib.rs" || true; \ - done && \ - mkdir -p terraphim_server/src && echo 'fn main() {}' > "terraphim_server/src/main.rs" - -# Pre-build dependencies -RUN . /root/.profile && \ - RUST_TARGET=$(cat /tmp/rust-target) && \ - cargo fetch --target=$RUST_TARGET - -# Copy source code (excluding firecracker crates with private git deps) -# Copy each crate individually, excluding problematic ones -RUN mkdir -p crates -COPY crates/terraphim_agent crates/terraphim_agent/ -COPY crates/terraphim_agent_application crates/terraphim_agent_application/ 2>/dev/null || true -COPY crates/terraphim_automata crates/terraphim_automata/ -COPY crates/terraphim_automata_py crates/terraphim_automata_py/ 2>/dev/null || true -COPY crates/terraphim_build_args crates/terraphim_build_args/ 2>/dev/null || true -COPY crates/terraphim_mcp_server crates/terraphim_mcp_server/ -COPY crates/terraphim_mcp_client crates/terraphim_mcp_client/ -COPY crates/terraphim_mcp_transport crates/terraphim_mcp_transport/ -COPY crates/terraphim_mcp_utils crates/terraphim_mcp_utils/ -COPY crates/terraphim_rolegraph crates/terraphim_rolegraph/ -COPY crates/terraphim_rolegraph_py crates/terraphim_rolegraph_py/ 2>/dev/null || true -COPY crates/terraphim_server crates/terraphim_server/ -COPY crates/terraphim_sessions crates/terraphim_sessions/ -COPY crates/terraphim_types crates/terraphim_types/ -COPY crates/haystack_*/ crates/ +COPY crates ./crates COPY terraphim_server ./terraphim_server +COPY terraphim_ai_nodejs ./terraphim_ai_nodejs +COPY terraphim_firecracker/Cargo.toml terraphim_firecracker/ + +# Replace any committed server assets with the CI-built frontend bundle. +RUN rm -rf terraphim_server/dist && mkdir -p terraphim_server/dist COPY --from=frontend-builder /app/dist ./terraphim_server/dist # Build the application @@ -181,6 +142,7 @@ RUN . /root/.profile && \ FROM ubuntu:${UBUNTU_VERSION} AS runtime ARG UBUNTU_VERSION +ARG RUST_VERSION # Install minimal runtime dependencies ENV DEBIAN_FRONTEND=noninteractive diff --git a/scripts/publish-crates.sh b/scripts/publish-crates.sh index 9ced8e168..9af014cc1 100755 --- a/scripts/publish-crates.sh +++ b/scripts/publish-crates.sh @@ -59,6 +59,8 @@ CRATES=( "terraphim_middleware" "terraphim_update" "terraphim_service" + "terraphim_tracker" + "terraphim_orchestrator" "terraphim_agent" ) From f4df3faac49592319a45d7ce958e71eb40765d44 Mon Sep 17 00:00:00 2001 From: Alex Mikhalev Date: Tue, 14 Apr 2026 10:58:09 +0200 Subject: [PATCH 2/2] ci: re-trigger CI (sccache transient failure)