Skip to content

Commit 0e38b5b

Browse files
committed
feat: add SPEC-11 sister integration bridges
Add bridge traits for all 6 sister integrations (Memory, Identity, Time, Contract, Vision, Comm) with no-op defaults, BridgeConfig toggle, HydraAdapter trait, and comprehensive tests. Lives in MCP crate since codebase has no core library crate. Enables Hydra-compatible cross-sister integration without direct imports.
1 parent ed2fc85 commit 0e38b5b

2 files changed

Lines changed: 253 additions & 0 deletions

File tree

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
//! Sister integration bridge traits for AgenticCodebase.
2+
//!
3+
//! Each bridge defines the interface for integrating with another Agentra sister.
4+
//! Default implementations are no-ops, allowing gradual adoption.
5+
//! Trait-based design ensures Hydra compatibility — swap implementors without refactoring.
6+
//!
7+
//! Note: Codebase has no core library crate, so bridges live in the MCP crate.
8+
9+
/// Bridge to agentic-memory for persisting code analysis in memory.
10+
pub trait MemoryBridge: Send + Sync {
11+
/// Store a code analysis result as a memory node
12+
fn store_analysis(&self, analysis_type: &str, details: &str) -> Result<u64, String> {
13+
let _ = (analysis_type, details);
14+
Err("Memory bridge not connected".to_string())
15+
}
16+
17+
/// Recall past code analyses from memory
18+
fn recall_analyses(&self, topic: &str, max_results: usize) -> Vec<String> {
19+
let _ = (topic, max_results);
20+
Vec::new()
21+
}
22+
23+
/// Link a code unit to a memory decision node
24+
fn link_unit_to_memory(&self, unit_id: u64, node_id: u64) -> Result<(), String> {
25+
let _ = (unit_id, node_id);
26+
Err("Memory bridge not connected".to_string())
27+
}
28+
}
29+
30+
/// Bridge to agentic-identity for code attribution and signing.
31+
pub trait IdentityBridge: Send + Sync {
32+
/// Attribute a code change to an agent identity
33+
fn attribute_change(&self, unit_id: u64, agent_id: &str) -> Result<(), String> {
34+
let _ = (unit_id, agent_id);
35+
Err("Identity bridge not connected".to_string())
36+
}
37+
38+
/// Verify the author of a code unit
39+
fn verify_author(&self, unit_id: u64, claimed_author: &str) -> bool {
40+
let _ = (unit_id, claimed_author);
41+
true // Default: trust all
42+
}
43+
44+
/// Sign a code review action
45+
fn sign_review(&self, unit_id: u64, verdict: &str) -> Result<String, String> {
46+
let _ = (unit_id, verdict);
47+
Err("Identity bridge not connected".to_string())
48+
}
49+
}
50+
51+
/// Bridge to agentic-time for temporal code context.
52+
pub trait TimeBridge: Send + Sync {
53+
/// Get when a code unit was last modified
54+
fn last_modified(&self, unit_id: u64) -> Option<u64> {
55+
let _ = unit_id;
56+
None
57+
}
58+
59+
/// Schedule a code review at a future time
60+
fn schedule_review(&self, unit_id: u64, review_at: u64) -> Result<String, String> {
61+
let _ = (unit_id, review_at);
62+
Err("Time bridge not connected".to_string())
63+
}
64+
65+
/// Create a deadline for a code change
66+
fn create_code_deadline(&self, label: &str, due_at: u64) -> Result<String, String> {
67+
let _ = (label, due_at);
68+
Err("Time bridge not connected".to_string())
69+
}
70+
}
71+
72+
/// Bridge to agentic-contract for policy-governed code operations.
73+
pub trait ContractBridge: Send + Sync {
74+
/// Check if a code operation is allowed by policies
75+
fn check_policy(&self, operation: &str, unit_id: u64) -> Result<bool, String> {
76+
let _ = (operation, unit_id);
77+
Ok(true) // Default: allow all
78+
}
79+
80+
/// Record a code operation for audit trail
81+
fn record_operation(&self, operation: &str, unit_id: u64) -> Result<(), String> {
82+
let _ = (operation, unit_id);
83+
Err("Contract bridge not connected".to_string())
84+
}
85+
86+
/// Get risk assessment for a code change
87+
fn risk_assessment(&self, unit_id: u64) -> Option<f64> {
88+
let _ = unit_id;
89+
None
90+
}
91+
}
92+
93+
/// Bridge to agentic-vision for code-visual bindings.
94+
pub trait VisionBridge: Send + Sync {
95+
/// Link a code unit to a visual capture
96+
fn link_to_capture(&self, unit_id: u64, capture_id: u64, binding_type: &str) -> Result<(), String> {
97+
let _ = (unit_id, capture_id, binding_type);
98+
Err("Vision bridge not connected".to_string())
99+
}
100+
101+
/// Find visual captures related to a code component
102+
fn find_visual_for_code(&self, symbol: &str) -> Vec<u64> {
103+
let _ = symbol;
104+
Vec::new()
105+
}
106+
}
107+
108+
/// Bridge to agentic-comm for code-aware messaging.
109+
pub trait CommBridge: Send + Sync {
110+
/// Broadcast a code change notification
111+
fn broadcast_change(&self, unit_id: u64, change_type: &str, channel_id: u64) -> Result<(), String> {
112+
let _ = (unit_id, change_type, channel_id);
113+
Err("Comm bridge not connected".to_string())
114+
}
115+
116+
/// Notify of a regression prediction
117+
fn notify_regression(&self, unit_id: u64, affected_tests: &[String]) -> Result<(), String> {
118+
let _ = (unit_id, affected_tests);
119+
Err("Comm bridge not connected".to_string())
120+
}
121+
}
122+
123+
/// No-op implementation of all bridges for standalone use.
124+
#[derive(Debug, Clone, Default)]
125+
pub struct NoOpBridges;
126+
127+
impl MemoryBridge for NoOpBridges {}
128+
impl IdentityBridge for NoOpBridges {}
129+
impl TimeBridge for NoOpBridges {}
130+
impl ContractBridge for NoOpBridges {}
131+
impl VisionBridge for NoOpBridges {}
132+
impl CommBridge for NoOpBridges {}
133+
134+
/// Configuration for which bridges are active.
135+
#[derive(Debug, Clone)]
136+
pub struct BridgeConfig {
137+
pub memory_enabled: bool,
138+
pub identity_enabled: bool,
139+
pub time_enabled: bool,
140+
pub contract_enabled: bool,
141+
pub vision_enabled: bool,
142+
pub comm_enabled: bool,
143+
}
144+
145+
impl Default for BridgeConfig {
146+
fn default() -> Self {
147+
Self {
148+
memory_enabled: false,
149+
identity_enabled: false,
150+
time_enabled: false,
151+
contract_enabled: false,
152+
vision_enabled: false,
153+
comm_enabled: false,
154+
}
155+
}
156+
}
157+
158+
/// Hydra adapter trait — future orchestrator discovery interface.
159+
pub trait HydraAdapter: Send + Sync {
160+
fn adapter_id(&self) -> &str;
161+
fn capabilities(&self) -> Vec<String>;
162+
fn handle_request(&self, method: &str, params: &str) -> Result<String, String>;
163+
}
164+
165+
// ---------------------------------------------------------------------------
166+
// Tests
167+
// ---------------------------------------------------------------------------
168+
169+
#[cfg(test)]
170+
mod tests {
171+
use super::*;
172+
173+
#[test]
174+
fn noop_bridges_implements_all_traits() {
175+
let b = NoOpBridges;
176+
let _: &dyn MemoryBridge = &b;
177+
let _: &dyn IdentityBridge = &b;
178+
let _: &dyn TimeBridge = &b;
179+
let _: &dyn ContractBridge = &b;
180+
let _: &dyn VisionBridge = &b;
181+
let _: &dyn CommBridge = &b;
182+
}
183+
184+
#[test]
185+
fn memory_bridge_defaults() {
186+
let b = NoOpBridges;
187+
assert!(b.store_analysis("impact", "details").is_err());
188+
assert!(b.recall_analyses("topic", 10).is_empty());
189+
assert!(b.link_unit_to_memory(1, 2).is_err());
190+
}
191+
192+
#[test]
193+
fn identity_bridge_defaults() {
194+
let b = NoOpBridges;
195+
assert!(b.attribute_change(1, "agent-1").is_err());
196+
assert!(b.verify_author(1, "agent-1"));
197+
assert!(b.sign_review(1, "approved").is_err());
198+
}
199+
200+
#[test]
201+
fn time_bridge_defaults() {
202+
let b = NoOpBridges;
203+
assert!(b.last_modified(1).is_none());
204+
assert!(b.schedule_review(1, 1000).is_err());
205+
assert!(b.create_code_deadline("label", 1000).is_err());
206+
}
207+
208+
#[test]
209+
fn contract_bridge_defaults() {
210+
let b = NoOpBridges;
211+
assert!(b.check_policy("analyze", 1).unwrap());
212+
assert!(b.record_operation("analyze", 1).is_err());
213+
assert!(b.risk_assessment(1).is_none());
214+
}
215+
216+
#[test]
217+
fn vision_bridge_defaults() {
218+
let b = NoOpBridges;
219+
assert!(b.link_to_capture(1, 2, "rendered_by").is_err());
220+
assert!(b.find_visual_for_code("Button").is_empty());
221+
}
222+
223+
#[test]
224+
fn comm_bridge_defaults() {
225+
let b = NoOpBridges;
226+
assert!(b.broadcast_change(1, "behavior", 1).is_err());
227+
assert!(b.notify_regression(1, &["test_1".to_string()]).is_err());
228+
}
229+
230+
#[test]
231+
fn bridge_config_defaults_all_false() {
232+
let cfg = BridgeConfig::default();
233+
assert!(!cfg.memory_enabled);
234+
assert!(!cfg.identity_enabled);
235+
assert!(!cfg.time_enabled);
236+
assert!(!cfg.contract_enabled);
237+
assert!(!cfg.vision_enabled);
238+
assert!(!cfg.comm_enabled);
239+
}
240+
241+
#[test]
242+
fn noop_bridges_is_send_sync() {
243+
fn assert_send_sync<T: Send + Sync>() {}
244+
assert_send_sync::<NoOpBridges>();
245+
}
246+
247+
#[test]
248+
fn noop_bridges_default_and_clone() {
249+
let b = NoOpBridges::default();
250+
let _b2 = b.clone();
251+
}
252+
}

crates/agentic-codebase-mcp/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::time::SystemTime;
1515
use agentic_codebase::engine::compile::{CompileOptions, CompilePipeline};
1616
use walkdir::WalkDir;
1717

18+
mod bridges;
1819
mod ghost_bridge;
1920

2021
#[derive(Parser)]

0 commit comments

Comments
 (0)