Skip to content
Closed
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
3,498 changes: 988 additions & 2,510 deletions Cargo.lock

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions crates/mcb-ast-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "mcb-ast-utils"
description = "AST traversal and analysis utilities for MCP Context Browser"
homepage.workspace = true
authors.workspace = true
repository.workspace = true
license.workspace = true
version.workspace = true
edition.workspace = true
rust-version.workspace = true


[lints]
workspace = true

[dependencies]
# Language support for language identification
mcb-language-support = { path = "../mcb-language-support" }

# Tree-sitter for direct AST access
tree-sitter.workspace = true
tree-sitter-rust.workspace = true
tree-sitter-python.workspace = true
tree-sitter-javascript.workspace = true
tree-sitter-typescript.workspace = true
tree-sitter-go.workspace = true
tree-sitter-java.workspace = true
tree-sitter-c.workspace = true
tree-sitter-cpp.workspace = true
tree-sitter-kotlin-ng.workspace = true

# Error handling
thiserror.workspace = true

[dev-dependencies]
tempfile.workspace = true
rstest = { workspace = true }
mockall = { workspace = true }
insta = { workspace = true }
72 changes: 50 additions & 22 deletions crates/mcb-infrastructure/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,34 @@ path = "src/lib.rs"
[dependencies]
# Domain layer - core business logic and port traits
mcb-domain = { path = "../mcb-domain" }
mcb-utils = { path = "../mcb-utils" }

# Application layer - use cases and business logic orchestration
mcb-application = { path = "../mcb-application" }
Comment on lines +22 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Disallowed deps in mcb-infrastructure 📘 Rule violation ⌂ Architecture

crates/mcb-infrastructure/Cargo.toml adds internal dependencies on mcb-application and
mcb-validate, which are not in the allowed set for mcb-infrastructure. This violates the crate
dependency restriction for mcb-infrastructure and risks layering/architecture drift.
Agent Prompt
## Issue description
`mcb-infrastructure` depends on disallowed internal crates (`mcb-application`, `mcb-validate`).

## Issue Context
Per the dependency restriction rule, `mcb-infrastructure` may only depend on `mcb-domain`, `mcb-providers`, and `mcb-utils` among `mcb-*` crates.

## Fix Focus Areas
- crates/mcb-infrastructure/Cargo.toml[18-24]
- crates/mcb-infrastructure/Cargo.toml[151-156]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


# Core async runtime
tokio = { workspace = true }

# Serialization
serde = { workspace = true }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
Comment on lines 27 to -29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Infrastructure deps removed used 🐞 Bug ≡ Correctness

crates/mcb-infrastructure/Cargo.toml removes serde_yaml, mcb-utils, and linkme, but
mcb-infrastructure still uses serde_yaml for YAML parsing and uses a domain macro that expands
to #[linkme::distributed_slice], causing compilation failures.
Agent Prompt
### Issue description
`mcb-infrastructure` still references `serde_yaml`, `mcb_utils`, and uses `mcb_domain::register_config_provider!` (which expands to `#[linkme::distributed_slice(...)]`), but its `Cargo.toml` no longer declares these crates. This will fail to compile.

### Issue Context
- `serde_yaml` is used to parse YAML config.
- `mcb_utils` constants are still referenced.
- `register_config_provider!` expands to `#[linkme::distributed_slice]`, which requires the `linkme` crate in the consuming crate.

### Fix Focus Areas
- crates/mcb-infrastructure/Cargo.toml[18-125]

### What to change
- Add back:
  - `serde_yaml = { workspace = true }`
  - `mcb-utils = { path = "../mcb-utils" }` (or replace usages with the new canonical constants location if a migration is intended)
  - `linkme = { workspace = true }`
- If the intent is to remove any of these crates, update the corresponding code paths (`src/config/loader.rs`, `src/config/provider.rs`, and any macro-based registrations) to stop referencing them.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


# Error handling
anyhow = { workspace = true }
thiserror = { workspace = true }

# Logging
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

# Configuration
figment = { workspace = true }
toml = { workspace = true }
notify = { workspace = true }
arc-swap = { workspace = true }

# HTTP client
reqwest = { workspace = true }

# CLI
clap = { workspace = true }

Expand All @@ -47,10 +58,15 @@ regex = { workspace = true }
# Date/time handling
chrono = { workspace = true }

# Template engines
handlebars = { workspace = true }

# Cryptography
aes-gcm = { workspace = true }
rand = { workspace = true }

# Authentication
bcrypt = { workspace = true }
argon2 = { workspace = true }
base64 = { workspace = true }

Expand All @@ -60,6 +76,10 @@ hex = { workspace = true }

# Utilities
uuid = { workspace = true }
dirs = { workspace = true }

# System metrics
sysinfo = { workspace = true }

# Fast globbing
glob = { workspace = true }
Expand All @@ -69,31 +89,48 @@ ignore = { workspace = true }

# Concurrent processing
futures = { workspace = true }
rayon = { workspace = true }

# Text processing
unicode-segmentation = { workspace = true }

# Schema generation for MCP
schemars = { workspace = true }

# Input validation
validator = { workspace = true }

# MCP SDK
rmcp = { workspace = true }

# Advanced Multi-Provider Strategy
tokio-util = { workspace = true }
dashmap = { workspace = true }
health = { workspace = true }

# Additional dependencies
async-trait = { workspace = true }
downcast-rs = { workspace = true }
futures-util = { workspace = true }
linkme.workspace = true

# Dependency Injection (IoC Container)
dill = { workspace = true }

# Service layer improvements
itertools = { workspace = true }
humantime = { workspace = true }

# Shell expansion
shellexpand = { workspace = true }

# Additional crypto
pbkdf2 = { workspace = true }
hmac = { workspace = true }

# SeaORM (database layer)
sea-orm = { workspace = true }
sea-orm-migration = { workspace = true }
# Logging appenders
tracing-appender = { workspace = true }

# SQLite for legacy/direct queries
# SQLite for file hash storage (Phase 3: Incremental indexing)
sqlx = { workspace = true }

# Syntax highlighting
Expand All @@ -111,29 +148,20 @@ tree-sitter-ruby = { workspace = true }
tree-sitter-php = { workspace = true }
tree-sitter-swift = { workspace = true }

[features]
default = []
test-utils = []
# Links providers into linkme distributed slices
mcb-providers = { path = "../mcb-providers" }

# Architecture validation (for MCP tool integration)
mcb-validate = { path = "../mcb-validate" }

[lints]
workspace = true

[dev-dependencies]
mcb-domain = { path = "../mcb-domain", features = ["test-utils"] }
mcb-utils = { path = "../mcb-utils" }
tempfile = { workspace = true }
toml = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
serial_test = { workspace = true }
rstest = { workspace = true }
mockall = { workspace = true }
mcb-validate = { path = "../mcb-validate" }
insta = { workspace = true }

[[test]]
name = "unit"
path = "tests/unit/mod.rs"

[[test]]
name = "integration"
path = "tests/integration/mod.rs"
mcb-domain = { path = "../mcb-domain", features = ["test-utils"] }
202 changes: 202 additions & 0 deletions crates/mcb-infrastructure/tests/unit/config_figment_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//! Tests verifying Figment configuration pattern compliance (ADR-025)
//!
//! These tests ensure the configuration system adheres to ADR-025 principles:
//! - All configuration flows through Figment
//! - Environment variables use `MCP__` prefix (double underscore)
//! - No implicit fallbacks or legacy `MCB_` prefix support
//! - Fail-fast on missing required configuration
//!
//! # Safety
//!
//! Tests use `unsafe` blocks for `env::set_var`/`env::remove_var` because
//! Rust 2024 edition requires this for environment variable mutations.
//! Tests use `#[serial]` to prevent data races between env var mutations.

use std::env;

use mcb_infrastructure::config::loader::ConfigLoader;
use serial_test::serial;

/// Helper to set env var safely.
#[allow(unsafe_code)]
fn set_env(key: &str, value: &str) {
Comment on lines +20 to 22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. #[allow(unsafe_code)] lacks justification 📘 Rule violation ⚙ Maintainability

#[allow(unsafe_code)] is used without an inline justification comment on the same line or
immediately following line. This violates the requirement to document why a static-analysis
suppression is necessary, making future audits and maintenance harder.
Agent Prompt
## Issue description
`#[allow(unsafe_code)]` is a suppression directive but it has no inline justification comment on the same line or the next line, as required.

## Issue Context
This appears to be needed due to Rust 2024 requiring `unsafe` for `env::set_var`/`env::remove_var` in tests, but that rationale must be recorded inline next to the suppression.

## Fix Focus Areas
- crates/mcb-infrastructure/tests/unit/config_figment_tests.rs[20-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

// SAFETY: Tests run serially (#[serial]) — no concurrent env mutation.

@cubic-dev-ai cubic-dev-ai Bot Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: These unsafe calls are not made sound merely by serializing the tagged tests: #[serial] does not make the shared unit test binary single-threaded, and the Rust safety contract also forbids concurrent environment reads outside std::env, not only concurrent mutations. Since this binary includes many ordinary and Tokio tests, this new safety rationale does not establish the required invariant on non-Windows targets. Consider running these cases in an actually single-threaded process (and enforcing that in the test command), or isolating environment mutation in subprocesses; the safety comment should state the full invariant that the implementation enforces.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-infrastructure/tests/unit/config_figment_tests.rs, line 23:

<comment>These unsafe calls are not made sound merely by serializing the tagged tests: `#[serial]` does not make the shared `unit` test binary single-threaded, and the Rust safety contract also forbids concurrent environment reads outside `std::env`, not only concurrent mutations. Since this binary includes many ordinary and Tokio tests, this new safety rationale does not establish the required invariant on non-Windows targets. Consider running these cases in an actually single-threaded process (and enforcing that in the test command), or isolating environment mutation in subprocesses; the safety comment should state the full invariant that the implementation enforces.</comment>

<file context>
@@ -17,19 +17,19 @@ use std::env;
-/// Helper to set env var safely
 fn set_env(key: &str, value: &str) {
-    // SAFETY: Tests must run with --test-threads=1
+    // SAFETY: Tests run serially (#[serial]) — no concurrent env mutation.
     unsafe {
         env::set_var(key, value);
</file context>
Fix with cubic

unsafe {
env::set_var(key, value);
}
}

/// Helper to remove env var safely.
#[allow(unsafe_code)]
fn remove_env(key: &str) {
// SAFETY: Tests run serially (#[serial]) — no concurrent env mutation.
unsafe {
env::remove_var(key);
}
}

/// Helper to disable auth to avoid JWT secret validation
fn disable_auth() {
set_env("MCP__AUTH__ENABLED", "false");
}

/// Helper cleanup for auth
fn cleanup_auth() {
remove_env("MCP__AUTH__ENABLED");
}

/// Verify env vars with MCP__ prefix are loaded correctly
#[test]
#[serial]
fn test_mcp_prefix_env_vars_loaded() {
// Disable auth to avoid JWT validation
disable_auth();
set_env("MCP__PROVIDERS__EMBEDDING__PROVIDER", "test-provider");

let config = ConfigLoader::new().load().expect("Should load config");

// Verify the provider value was loaded from env
assert_eq!(
config.providers.embedding.provider,
Some("test-provider".to_string()),
"MCP__ prefixed env vars should be loaded by Figment"
);

remove_env("MCP__PROVIDERS__EMBEDDING__PROVIDER");
cleanup_auth();
}

/// Verify old MCB_ prefix is NOT loaded (breaking change per ADR-025)
#[test]
#[serial]
fn test_old_mcb_prefix_not_loaded() {
// Disable auth to avoid JWT validation
disable_auth();
// Set admin key with OLD prefix (should be ignored)
set_env("MCB_ADMIN_API_KEY", "old-key-value");

let config = ConfigLoader::new().load().expect("Should load config");

// Old prefix should NOT be recognized - key should be None
assert_eq!(
config.auth.admin.key, None,
"Old MCB_ prefix should NOT be recognized (ADR-025 breaking change)"
);

remove_env("MCB_ADMIN_API_KEY");
cleanup_auth();
}

/// Verify new MCP__ admin key IS loaded correctly
#[test]
#[serial]
fn test_new_admin_key_loaded() {
// Disable auth to avoid JWT validation
disable_auth();
// Set admin key with NEW prefix
set_env("MCP__AUTH__ADMIN__KEY", "new-key-value");

let config = ConfigLoader::new().load().expect("Should load config");

// New prefix should be recognized
assert_eq!(
config.auth.admin.key,
Some("new-key-value".to_string()),
"MCP__AUTH__ADMIN__KEY should be loaded by Figment"
);

remove_env("MCP__AUTH__ADMIN__KEY");
cleanup_auth();
}

/// Verify JWT secret validation fails when empty and auth is enabled
#[test]
#[serial]
fn test_jwt_secret_required_when_auth_enabled() {
// Enable auth but don't set JWT secret
set_env("MCP__AUTH__ENABLED", "true");
// Deliberately NOT setting MCP__AUTH__JWT__SECRET

let result = ConfigLoader::new().load();

// Should fail validation
assert!(
result.is_err(),
"Config should fail validation when auth.enabled=true but JWT secret is empty"
);

let err = result.unwrap_err().to_string();
assert!(
err.contains("JWT") || err.contains("secret"),
"Error message should mention JWT secret requirement, got: {}",
err
);

remove_env("MCP__AUTH__ENABLED");
}

/// Verify watching_enabled config is loaded from Figment, not direct env::var
#[test]
#[serial]
fn test_watching_enabled_via_figment() {
// Disable auth to avoid JWT validation
disable_auth();
set_env("MCP__SYSTEM__DATA__SYNC__WATCHING_ENABLED", "false");

let config = ConfigLoader::new().load().expect("Should load config");

// Should be false (default is true)
assert!(
!config.system.data.sync.watching_enabled,
"watching_enabled should be loaded from MCP__SYSTEM__DATA__SYNC__WATCHING_ENABLED"
);

remove_env("MCP__SYSTEM__DATA__SYNC__WATCHING_ENABLED");
cleanup_auth();
}

/// Verify that DISABLE_CONFIG_WATCHING env var is NOT supported (legacy removal)
#[test]
#[serial]
fn test_legacy_disable_watching_not_supported() {
// Disable auth to avoid JWT validation
disable_auth();
// Set OLD env var that should be ignored
set_env("DISABLE_CONFIG_WATCHING", "true");

let config = ConfigLoader::new().load().expect("Should load config");

// Old env var should be ignored, watching_enabled should be default (true)
assert!(
config.system.data.sync.watching_enabled,
"DISABLE_CONFIG_WATCHING should NOT affect watching_enabled (use MCP__SYSTEM__DATA__SYNC__WATCHING_ENABLED)"
);

remove_env("DISABLE_CONFIG_WATCHING");
cleanup_auth();
}

// ============================================================================
// Tests that need clean env state (also serial to avoid interference)
// ============================================================================

/// Verify auth is DISABLED by default (changed from ADR-025 for local development ease)
/// When auth.enabled=false, JWT secret is not required
#[test]
#[serial]
fn test_auth_disabled_by_default_loads_without_jwt_secret() {
// Ensure clean state - remove any auth env vars from previous tests
remove_env("MCP__AUTH__ENABLED");
remove_env("MCP__AUTH__JWT__SECRET");

// Load config without any env vars set - should SUCCEED because auth is disabled by default
let result = ConfigLoader::new().load();

// Config should load successfully because auth.enabled=false by default
// No JWT secret is required when auth is disabled
assert!(
result.is_ok(),
"Config should load when auth.enabled=false (default), got error: {:?}",
result.err()
);
}
Loading