Skip to content

Feature/phase2 di centralization - #149

Open
marlon-costa-dc wants to merge 3 commits into
mainfrom
feature/phase2-di-centralization
Open

Feature/phase2 di centralization#149
marlon-costa-dc wants to merge 3 commits into
mainfrom
feature/phase2-di-centralization

Conversation

@marlon-costa-dc

Copy link
Copy Markdown
Collaborator

No description provided.

- Remove reqwest dependency from Cargo.lock and related files.
- Implement FileSystemProvider trait for file operations.
- Add TaskRunnerProvider trait for background task management.
- Update IndexingServiceImpl to utilize new providers for file system access and task execution.
- Refactor indexing logic to improve directory traversal and error handling.
- Introduce new event bus and file system provider configurations in the DI layer.
- Replace `println!` with `writeln!` to ensure proper output flushing.
- Update the `print_json` and `print_summary` methods for consistent output formatting.
- Enhance readability by restructuring output statements in the validation report.
- Removed mcb-providers from Cargo.lock and related files to streamline dependencies.
- Introduced new bypass rules in mcb-validate-internal.toml for handling provider imports across various crates.
- Updated tests and code to ensure compliance with the new dependency structure and validation rules.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@marlon-costa-dc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c648796d-0bf6-47a0-8110-4c791a7697cb

📥 Commits

Reviewing files that changed from the base of the PR and between a6d22a3 and b931063.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (70)
  • config/mcb-validate-internal.toml
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-application/tests/unit/registry_tests.rs
  • crates/mcb-application/tests/utils/shared_context.rs
  • crates/mcb-domain/Cargo.toml
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/ports/infrastructure/database.rs
  • crates/mcb-domain/src/ports/mod.rs
  • crates/mcb-domain/src/ports/providers/fs.rs
  • crates/mcb-domain/src/ports/providers/http.rs
  • crates/mcb-domain/src/ports/providers/mod.rs
  • crates/mcb-domain/src/ports/providers/task.rs
  • crates/mcb-domain/src/registry/event_bus.rs
  • crates/mcb-domain/src/registry/fs.rs
  • crates/mcb-domain/src/registry/mod.rs
  • crates/mcb-domain/src/registry/project_detection.rs
  • crates/mcb-domain/src/registry/task_runner.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-infrastructure/Cargo.toml
  • crates/mcb-infrastructure/src/config/types/system.rs
  • crates/mcb-infrastructure/src/di/bootstrap.rs
  • crates/mcb-infrastructure/src/di/database_resolver.rs
  • crates/mcb-infrastructure/src/di/mod.rs
  • crates/mcb-infrastructure/src/di/modules/domain_services.rs
  • crates/mcb-infrastructure/src/di/provider_resolvers.rs
  • crates/mcb-infrastructure/src/di/repositories.rs
  • crates/mcb-infrastructure/src/di/test_factory.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-infrastructure/src/infrastructure/factory.rs
  • crates/mcb-infrastructure/src/lib.rs
  • crates/mcb-infrastructure/src/project/service.rs
  • crates/mcb-infrastructure/src/provider_linker.rs
  • crates/mcb-infrastructure/tests/integration/di/architecture_validation_tests.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs
  • crates/mcb-infrastructure/tests/unit/config/config_figment_tests.rs
  • crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs
  • crates/mcb-infrastructure/tests/unit/routing/router_tests.rs
  • crates/mcb-infrastructure/tests/utils/fs_guards.rs
  • crates/mcb-infrastructure/tests/utils/real_providers.rs
  • crates/mcb-infrastructure/tests/utils/shared_context.rs
  • crates/mcb-infrastructure/tests/utils/workspace.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/events/nats.rs
  • crates/mcb-providers/src/events/tokio.rs
  • crates/mcb-providers/src/fs/mod.rs
  • crates/mcb-providers/src/lib.rs
  • crates/mcb-providers/src/project_detection/registry.rs
  • crates/mcb-providers/src/task/mod.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb-server/Cargo.toml
  • crates/mcb-server/tests/integration/browse_api_integration.rs
  • crates/mcb-server/tests/integration/error_recovery_integration.rs
  • crates/mcb-server/tests/integration/full_stack_integration.rs
  • crates/mcb-server/tests/integration/golden_acceptance_integration.rs
  • crates/mcb-server/tests/integration/operating_modes_integration.rs
  • crates/mcb-server/tests/unit/mcp_server/mcp_contract_tests.rs
  • crates/mcb-server/tests/unit/mcp_server/mcp_protocol_tests.rs
  • crates/mcb-server/tests/unit/tools/tool_invariant_matrix_tests.rs
  • crates/mcb-server/tests/utils/real_providers.rs
  • crates/mcb-server/tests/utils/shared_context.rs
  • crates/mcb-validate/src/validators/dependency/bypass.rs
  • crates/mcb-validate/src/validators/dependency/violation.rs
  • crates/mcb-validate/src/validators/macros/violations.rs
  • crates/mcb-validate/tests/integration/integration_architecture.rs
  • crates/mcb-validate/tests/unit/validators/dependency/dependency_tests.rs
  • crates/mcb/Cargo.toml
  • crates/mcb/src/cli/validate.rs
  • crates/mcb/src/main.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/phase2-di-centralization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Centralize DI via domain provider ports (FS, task runner, event bus, DB, VCS)

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Introduce domain-level provider ports/registries to decouple app/infrastructure from concrete
 providers.
• Refactor DI bootstrap and indexing to use FileSystemProvider + TaskRunnerProvider abstractions.
• Remove direct mcb-providers imports, update validation rules/tests, and streamline dependencies.
Diagram

graph TD
  A["mcb CLI/Server"] --> B["Infrastructure DI bootstrap"] --> E["Domain services factory"] --> F["IndexingServiceImpl"]
  B --> C["Domain registries (linkme)"] --> D["mcb-providers implementations"]
  F --> C
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Keep runtime-specific adapters in infrastructure only (no new domain ports)
  • ➕ Smaller domain surface area (fewer ports/registries/types)
  • ➕ Avoids adding generic FS/task abstractions to the domain layer
  • ➖ Application layer still ends up coupled to tokio/std::fs unless carefully contained
  • ➖ Harder to test indexing traversal/task spawning without reaching into runtime details
2. Use a generic runtime/spawn abstraction (e.g., tower::Service-like or Spawn trait) instead of TaskRunnerProvider registry
  • ➕ Potentially standardizes on existing ecosystem patterns
  • ➕ May reduce bespoke registry/config boilerplate
  • ➖ Still needs a concrete wiring mechanism for linkme/DI in this architecture
  • ➖ May not cover FS abstraction needs; would still require a FileSystemProvider-like port

Recommendation: Proceed with this PR’s approach: moving file-system access, task spawning, event bus, VCS, and DB repository creation behind domain ports/registries is consistent with the clean-architecture goal and removes concrete provider leakage into application/server. The added ports increase surface area, but they directly enable the dependency boundary enforcement (DEP006) and simplify testing through injection.

Files changed (70) +1420 / -471

Enhancement (21) +733 / -20
database.rsExpand DatabaseProvider to construct repositories +46/-0

Expand DatabaseProvider to construct repositories

• Extends DatabaseProvider beyond connect() to include factory methods for all DB-backed repositories (memory, agent, project, file-hash, VCS/org/plan/issue). Shifts repository instantiation behind the provider boundary.

crates/mcb-domain/src/ports/infrastructure/database.rs

fs.rsIntroduce FileSystemProvider port +23/-0

Introduce FileSystemProvider port

• Defines a DirEntry DTO and FileSystemProvider trait for async file reads, directory listing, and path canonicalization.

crates/mcb-domain/src/ports/providers/fs.rs

task.rsIntroduce TaskRunnerProvider port +12/-0

Introduce TaskRunnerProvider port

• Defines a small port for spawning background tasks via a BoxFuture, enabling runtime-agnostic task execution from application code.

crates/mcb-domain/src/ports/providers/task.rs

event_bus.rsAdd event bus provider registry/config +20/-0

Add event bus provider registry/config

• Creates EventBusProviderConfig and linkme-based registry wiring for resolving/listing EventBusProvider implementations.

crates/mcb-domain/src/registry/event_bus.rs

fs.rsAdd filesystem provider registry/config +20/-0

Add filesystem provider registry/config

• Creates FileSystemProviderConfig and registry plumbing for resolving/listing FileSystemProvider implementations via linkme.

crates/mcb-domain/src/registry/fs.rs

mod.rsRegister new provider registries (event bus/fs/task/VCS/project) +5/-0

Register new provider registries (event bus/fs/task/VCS/project)

• Extends the domain registry module set to include event bus, filesystem, task runner, VCS, and project detection registries.

crates/mcb-domain/src/registry/mod.rs

task_runner.rsAdd task runner provider registry/config +20/-0

Add task runner provider registry/config

• Creates TaskRunnerProviderConfig and linkme registry boilerplate for resolving/listing TaskRunnerProvider implementations.

crates/mcb-domain/src/registry/task_runner.rs

vcs.rsAdd VCS provider registry/config +20/-0

Add VCS provider registry/config

• Creates VcsProviderConfig and registry wiring for resolving/listing VcsProvider implementations via linkme.

crates/mcb-domain/src/registry/vcs.rs

provider_resolvers.rsAdd resolvers for event bus, VCS, filesystem, and task runner +165/-1

Add resolvers for event bus, VCS, filesystem, and task runner

• Introduces resolver structs that build provider configs from AppConfig and resolve them via domain registries. Includes listing helpers for available providers.

crates/mcb-infrastructure/src/di/provider_resolvers.rs

vcs.rsImplement DynamicVcsProvider resolved from registry +123/-16

Implement DynamicVcsProvider resolved from registry

• Replaces the previous default VCS provider passthrough with a dynamic provider that tries all registered VCS providers and delegates operations to the one that can open a repository. Provides deduped repository listing across providers.

crates/mcb-infrastructure/src/di/vcs.rs

lib.rsExpose provider_linker module +2/-0

Expose provider_linker module

• Adds the provider_linker module to allow crates/tests to force-link mcb-providers for linkme registration without direct extern crate usage.

crates/mcb-infrastructure/src/lib.rs

provider_linker.rsAdd ensure_linked() to force provider registration +4/-0

Add ensure_linked() to force provider registration

• Introduces a tiny module that references mcb_providers to ensure linkme inventories are linked when needed (especially tests).

crates/mcb-infrastructure/src/provider_linker.rs

provider.rsImplement expanded DatabaseProvider repository factories for SQLite +67/-3

Implement expanded DatabaseProvider repository factories for SQLite

• Implements the new DatabaseProvider factory methods, returning concrete SQLite-backed repositories for all supported repository types including file-hash and entity repositories.

crates/mcb-providers/src/database/sqlite/provider.rs

nats.rsRegister NATS event bus provider in linkme registry +34/-0

Register NATS event bus provider in linkme registry

• Adds a registry entry and build function that constructs NatsEventBusProvider from EventBusProviderConfig extras (url/subject/client_name) and registers it in EVENT_BUS_PROVIDERS.

crates/mcb-providers/src/events/nats.rs

tokio.rsRegister Tokio event bus provider in linkme registry +22/-0

Register Tokio event bus provider in linkme registry

• Adds a registry entry and build function that reads capacity from config extras and registers a TokioEventBusProvider in EVENT_BUS_PROVIDERS.

crates/mcb-providers/src/events/tokio.rs

mod.rsAdd LocalFileSystemProvider and register it +73/-0

Add LocalFileSystemProvider and register it

• Introduces a local filesystem provider implementing FileSystemProvider using std::fs and registers it into the FILE_SYSTEM_PROVIDERS distributed slice.

crates/mcb-providers/src/fs/mod.rs

lib.rsExpose fs and task provider modules +6/-0

Expose fs and task provider modules

• Adds fs and task modules to the providers crate public API for linkme registration and DI resolution.

crates/mcb-providers/src/lib.rs

mod.rsAdd Tokio task runner provider and register it +38/-0

Add Tokio task runner provider and register it

• Introduces TokioTaskRunnerProvider implementing TaskRunnerProvider (tokio::spawn) and registers it in TASK_RUNNER_PROVIDERS.

crates/mcb-providers/src/task/mod.rs

git2_provider.rsRegister git2 VCS provider in linkme registry +15/-0

Register git2 VCS provider in linkme registry

• Adds a build function and distributed slice registration for Git2Provider in VCS_PROVIDERS, enabling DI-based resolution.

crates/mcb-providers/src/vcs/git2_provider.rs

bypass.rsMap DEP006 to new provider bypass violation type +6/-0

Map DEP006 to new provider bypass violation type

• Adds handling for DEP006 so bypass boundary validation can emit a distinct ProviderBypassImport violation.

crates/mcb-validate/src/validators/dependency/bypass.rs

violation.rsDefine DEP006 ProviderBypassImport violation +12/-0

Define DEP006 ProviderBypassImport violation

• Introduces a new violation type for forbidden provider imports outside mcb-providers with an explicit suggestion to use DI/registry.

crates/mcb-validate/src/validators/dependency/violation.rs

Refactor (19) +388 / -313
indexing_service.rsInject filesystem + task runner ports into indexing +47/-34

Inject filesystem + task runner ports into indexing

• Replaces ignore::WalkBuilder traversal with FileSystemProvider-based directory walking and switches tokio::spawn to TaskRunnerProvider::spawn. Also uses FileSystemProvider for file reads and simplifies incremental hash skip logic.

crates/mcb-application/src/use_cases/indexing_service.rs

di.rsAdd arc_getters! macro for DI containers +41/-0

Add arc_getters! macro for DI containers

• Introduces a macro to generate Arc-cloning getters for container fields, supporting optional field name overrides. Used to reduce repetitive boilerplate in AppContext.

crates/mcb-domain/src/macros/di.rs

mod.rsExport new DI macros module +2/-0

Export new DI macros module

• Registers the new di macro module so arc_getters! is available across crates.

crates/mcb-domain/src/macros/mod.rs

mod.rsExpose FS/task ports and remove HTTP client exports +5/-5

Expose FS/task ports and remove HTTP client exports

• Adds FileSystemProvider/DirEntry and TaskRunnerProvider to the public ports surface while removing HttpClientProvider/HttpClientConfig exports.

crates/mcb-domain/src/ports/mod.rs

mod.rsWire new fs/task provider ports into provider module +5/-3

Wire new fs/task provider ports into provider module

• Removes the HTTP provider module and adds fs + task provider modules/exports. Updates re-exports accordingly.

crates/mcb-domain/src/ports/providers/mod.rs

project_detection.rsMove project detector distributed slice into domain +15/-0

Move project detector distributed slice into domain

• Defines PROJECT_DETECTORS slice and a list helper in the domain registry, centralizing detector registration away from providers.

crates/mcb-domain/src/registry/project_detection.rs

system.rsStop importing provider constants for event bus defaults +4/-5

Stop importing provider constants for event bus defaults

• Replaces use of mcb_providers event-bus capacity constant with an infrastructure constant. Keeps config defaults inside infrastructure instead of providers.

crates/mcb-infrastructure/src/config/types/system.rs

bootstrap.rsResolve providers via registries and inject FS/task runner +73/-200

Resolve providers via registries and inject FS/task runner

• Refactors AppContext to use arc_getters! and adds FileSystemProvider + TaskRunnerProvider fields. init_app() now ensures providers are linked, resolves event bus/FS/task runner/VCS via resolvers, and uses DatabaseProvider factory methods for repository construction.

crates/mcb-infrastructure/src/di/bootstrap.rs

database_resolver.rsExpose DatabaseProvider resolution separately from connect +12/-4

Expose DatabaseProvider resolution separately from connect

• Adds resolve_provider() to obtain the configured DatabaseProvider instance. Reuses it in resolve_and_connect() and improves separation of concerns.

crates/mcb-infrastructure/src/di/database_resolver.rs

mod.rsExport new provider resolvers and gate test_factory +6/-2

Export new provider resolvers and gate test_factory

• Adds feature gating for test_factory and re-exports new resolvers (event bus, FS, task runner, VCS). Also adds missing-doc allowances for vcs module.

crates/mcb-infrastructure/src/di/mod.rs

domain_services.rsThread FS/task runner dependencies into domain service wiring +19/-5

Thread FS/task runner dependencies into domain service wiring

• Extends ServiceDependencies and indexing service construction to inject FileSystemProvider and TaskRunnerProvider, ensuring indexing can run without direct runtime/fs coupling.

crates/mcb-infrastructure/src/di/modules/domain_services.rs

repositories.rsConstruct repositories via resolved DatabaseProvider +37/-7

Construct repositories via resolved DatabaseProvider

• Removes direct mcb_providers database factory calls and resolves the default DatabaseProvider from the registry. Uses provider factory methods to build repositories and to connect executors.

crates/mcb-infrastructure/src/di/repositories.rs

factory.rsResolve event bus/vector store via registries with adapters +77/-19

Resolve event bus/vector store via registries with adapters

• Switches standalone factory helpers to resolve providers via domain registries, adding a NullEventBusProvider fallback and a VectorStoreBrowser adapter for provider-only implementations.

crates/mcb-infrastructure/src/infrastructure/factory.rs

service.rsDetect projects via domain registry slice instead of provider helper +22/-3

Detect projects via domain registry slice instead of provider helper

• Replaces mcb_providers project detection helper with direct iteration over the domain PROJECT_DETECTORS slice, using marker files and detector build/detect calls.

crates/mcb-infrastructure/src/project/service.rs

registry.rsRe-export domain project detector registry slice +1/-8

Re-export domain project detector registry slice

• Removes providers-owned distributed slice definition and re-exports the domain registry slice to avoid duplication and centralize registry ownership.

crates/mcb-providers/src/project_detection/registry.rs

milvus.rsSimplify Milvus ID parsing without clippy allow +3/-6

Simplify Milvus ID parsing without clippy allow

• Refactors int-id mapping to use 'to_string()' directly on i64 references and removes a clippy allow.

crates/mcb-providers/src/vector_store/milvus.rs

violations.rsAllow missing docs on generated violation enums +1/-0

Allow missing docs on generated violation enums

• Adds a missing_docs allow to the generated violation enum to reduce lint noise from macro-generated code.

crates/mcb-validate/src/validators/macros/violations.rs

validate.rsSwitch validation CLI output to writeln!(stdout) for flushing +18/-10

Switch validation CLI output to writeln!(stdout) for flushing

• Replaces println! with writeln!(stdout, ...) across JSON, summary, and violation output to improve output consistency and flushing behavior.

crates/mcb/src/cli/validate.rs

main.rsRemove explicit provider crate force-linking +0/-2

Remove explicit provider crate force-linking

• Deletes 'extern crate mcb_providers' from the main binary now that provider linking is handled through infrastructure and test utilities.

crates/mcb/src/main.rs

Tests (24) +232 / -126
registry_tests.rsRemove explicit mcb_providers link forcing +0/-3

Remove explicit mcb_providers link forcing

• Drops 'extern crate mcb_providers' used solely for linkme registration; tests now rely on infrastructure linking utilities instead.

crates/mcb-application/tests/unit/registry_tests.rs

shared_context.rsForce provider linking via infrastructure linker +1/-2

Force provider linking via infrastructure linker

• Replaces direct 'extern crate mcb_providers' with a constant reference to provider_linker::ensure_linked to ensure linkme registration in tests.

crates/mcb-application/tests/utils/shared_context.rs

test_factory.rsBuild test repositories via DatabaseProvider factories +28/-19

Build test repositories via DatabaseProvider factories

• Replaces direct Sqlite repository instantiation with DatabaseProvider-based construction resolved from the registry. Also injects FS/task runner dependencies from AppContext.

crates/mcb-infrastructure/src/di/test_factory.rs

architecture_validation_tests.rsRemove explicit provider crate linking in DI integration tests +0/-2

Remove explicit provider crate linking in DI integration tests

• Drops 'extern crate mcb_providers' usage; tests rely on the infrastructure linker approach for provider registration.

crates/mcb-infrastructure/tests/integration/di/architecture_validation_tests.rs

dispatch_tests.rsAdd VCS provider resolver test and remove extern linking +55/-3

Add VCS provider resolver test and remove extern linking

• Adds an integration test that initializes a git repo and validates VcsProviderResolver/open_repository/list_repositories behavior. Removes direct provider crate linking.

crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs

config_figment_tests.rsConsolidate crate-level allow attributes +1/-3

Consolidate crate-level allow attributes

• Simplifies lint allow attributes into a single line while preserving unsafe-code allowance required by linkme behavior in tests.

crates/mcb-infrastructure/tests/unit/config/config_figment_tests.rs

error_ext_tests.rsRefactor infra error assertions for clarity +13/-7

Refactor infra error assertions for clarity

• Replaces wildcard match arm with a matches! assertion plus explicit field checks, improving failure messages and readability.

crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs

router_tests.rsAvoid clippy Arc clone lint in router setup +2/-2

Avoid clippy Arc clone lint in router setup

• Introduces an explicit Arc<dyn HealthMonitor> clone for router construction, removing a clippy allow and clarifying intent.

crates/mcb-infrastructure/tests/unit/routing/router_tests.rs

fs_guards.rsDocument error behavior for filesystem test guards +8/-2

Document error behavior for filesystem test guards

• Adds rustdoc comments describing guard behavior and error cases; removes clippy missing-errors-doc allows.

crates/mcb-infrastructure/tests/utils/fs_guards.rs

real_providers.rsRemove explicit provider crate link forcing +0/-3

Remove explicit provider crate link forcing

• Drops 'extern crate mcb_providers' as the mechanism for linkme registration in these utilities.

crates/mcb-infrastructure/tests/utils/real_providers.rs

shared_context.rsForce provider linking via provider_linker in infra shared context +1/-2

Force provider linking via provider_linker in infra shared context

• Replaces 'extern crate mcb_providers' with a constant reference to provider_linker::ensure_linked to ensure providers are registered in test binaries.

crates/mcb-infrastructure/tests/utils/shared_context.rs

workspace.rsAdd documentation for workspace_root helper +4/-1

Add documentation for workspace_root helper

• Documents behavior and error cases for discovering the workspace root via Cargo.lock ancestry.

crates/mcb-infrastructure/tests/utils/workspace.rs

browse_api_integration.rsResolve real vector store via DI utilities and randomize collections +96/-43

Resolve real vector store via DI utilities and randomize collections

• Removes direct EdgeVec provider construction, resolves a real vector store via test utilities, adapts provider to VectorStoreBrowser, and uses unique collection names to reduce cross-test coupling.

crates/mcb-server/tests/integration/browse_api_integration.rs

error_recovery_integration.rsRemove explicit mcb_providers link forcing +0/-3

Remove explicit mcb_providers link forcing

• Drops 'extern crate mcb_providers' now that provider linking is centralized elsewhere.

crates/mcb-server/tests/integration/error_recovery_integration.rs

full_stack_integration.rsRemove explicit mcb_providers link forcing +0/-3

Remove explicit mcb_providers link forcing

• Removes direct provider linking statement and relies on centralized linker mechanisms for linkme registration.

crates/mcb-server/tests/integration/full_stack_integration.rs

golden_acceptance_integration.rsRemove explicit provider linking comment/code +0/-5

Remove explicit provider linking comment/code

• Removes provider-linking documentation and the 'extern crate mcb_providers' statement from the golden acceptance integration test.

crates/mcb-server/tests/integration/golden_acceptance_integration.rs

operating_modes_integration.rsRemove extern crate provider link forcing +0/-1

Remove extern crate provider link forcing

• Removes 'extern crate mcb_providers' from operating mode tests, aligning with the new centralized provider linking approach.

crates/mcb-server/tests/integration/operating_modes_integration.rs

mcp_contract_tests.rsRemove explicit provider crate linking in unit tests +0/-1

Remove explicit provider crate linking in unit tests

• Drops 'extern crate mcb_providers' usage from MCP contract unit tests.

crates/mcb-server/tests/unit/mcp_server/mcp_contract_tests.rs

mcp_protocol_tests.rsRemove explicit provider crate linking in protocol tests +0/-1

Remove explicit provider crate linking in protocol tests

• Drops 'extern crate mcb_providers' usage from MCP protocol unit tests.

crates/mcb-server/tests/unit/mcp_server/mcp_protocol_tests.rs

tool_invariant_matrix_tests.rsRemove explicit provider crate linking in tool matrix tests +0/-1

Remove explicit provider crate linking in tool matrix tests

• Drops 'extern crate mcb_providers' usage from tool invariant matrix unit tests.

crates/mcb-server/tests/unit/tools/tool_invariant_matrix_tests.rs

real_providers.rsRemove explicit provider link forcing in server test utilities +0/-3

Remove explicit provider link forcing in server test utilities

• Removes 'extern crate mcb_providers' from server test helpers; provider linking is handled via infrastructure linker.

crates/mcb-server/tests/utils/real_providers.rs

shared_context.rsForce provider linking via infrastructure provider_linker +2/-0

Force provider linking via infrastructure provider_linker

• Adds a constant reference to provider_linker::ensure_linked to ensure providers are registered in server test binaries.

crates/mcb-server/tests/utils/shared_context.rs

integration_architecture.rsHarden provider-import tests against direct literal matching +17/-14

Harden provider-import tests against direct literal matching

• Builds provider import strings dynamically and updates expected import_path construction, making tests resilient to direct substring/literal patterns.

crates/mcb-validate/tests/integration/integration_architecture.rs

dependency_tests.rsUpdate dependency validator unit tests for DEP006 +4/-2

Update dependency validator unit tests for DEP006

• Extends match arms to treat ProviderBypassImport as an unexpected violation in tests that assert other violation types.

crates/mcb-validate/tests/unit/validators/dependency/dependency_tests.rs

Other (6) +67 / -12
Cargo.lockDrop reqwest and mcb-providers transitive lock entries +0/-3

Drop reqwest and mcb-providers transitive lock entries

• Removes locked dependencies tied to prior provider imports (e.g., reqwest, mcb-providers references). Aligns the lockfile with the new DI/provider resolution structure.

Cargo.lock

mcb-validate-internal.tomlAdd DEP006 bypass rules for provider import scanning +66/-0

Add DEP006 bypass rules for provider import scanning

• Introduces bypass boundary entries for the new provider-import violation id across multiple crate scan roots. Keeps internal validation usable while refactoring provider boundaries.

config/mcb-validate-internal.toml

Cargo.tomlRemove reqwest from domain dependencies +0/-2

Remove reqwest from domain dependencies

• Eliminates the HTTP-client dependency from the domain crate as HTTP provider ports are removed/relocated.

crates/mcb-domain/Cargo.toml

Cargo.tomlMove mcb-providers to dev-dependencies for infrastructure +1/-1

Move mcb-providers to dev-dependencies for infrastructure

• Removes mcb-providers from main dependencies and adds it to dev-dependencies. Production code now resolves providers via domain registries; tests still link providers directly.

crates/mcb-infrastructure/Cargo.toml

Cargo.tomlRemove mcb-providers dependency from server crate +0/-3

Remove mcb-providers dependency from server crate

• Drops direct dependency on mcb-providers now that provider registration is handled via infrastructure linking and registry resolution.

crates/mcb-server/Cargo.toml

Cargo.tomlRemove mcb-providers dependency from main binary crate +0/-3

Remove mcb-providers dependency from main binary crate

• Drops direct dependency on mcb-providers now that provider registration is mediated by infrastructure/provider_linker.

crates/mcb/Cargo.toml

@qodo-code-review

qodo-code-review Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (9) 📘 Rule violations (4) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 36 rules

Grey Divider


Action required

1. Wrong VCS repository_id 🐞 Bug ≡ Correctness ⭐ New
Description
DynamicVcsProvider::repository_id() delegates to the first registered provider rather than the
provider that produced the VcsRepository, so repository identity can change depending on registry
ordering. This can cause inconsistent IDs vs VcsRepository::id() and mis-associate repository-scoped
data.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R62-66]

+    fn repository_id(&self, repo: &VcsRepository) -> RepositoryId {
+        self.providers
+            .first()
+            .map_or_else(|| *repo.id(), |provider| provider.repository_id(repo))
+    }
Evidence
The dynamic provider discards which concrete provider opened the repo, then computes IDs using the
first provider, even though VcsRepository already contains an ID field.

crates/mcb-infrastructure/src/di/vcs.rs[55-66]
crates/mcb-domain/src/entities/vcs/vcs_repo.rs[10-18]
crates/mcb-domain/src/entities/vcs/vcs_repo.rs[39-43]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DynamicVcsProvider::repository_id()` currently calls `repository_id()` on `self.providers.first()`, which is unrelated to the provider that created the `VcsRepository`. That can return a different `RepositoryId` than the one embedded in the `VcsRepository`, breaking identity stability.

### Issue Context
`VcsRepository` already stores an `id` computed at open time by the concrete provider. The dynamic wrapper should not recompute identity using an arbitrary provider.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[55-66]
- crates/mcb-domain/src/entities/vcs/vcs_repo.rs[10-18]

### Suggested fix
- Change `DynamicVcsProvider::repository_id()` to return `*repo.id()`.
- (Optional, if you truly need recomputation) store the selected provider alongside the repo (e.g., wrap `VcsRepository` in a provider-tagged struct) so subsequent calls use the same provider.

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


2. NATS factory blocks runtime 🐞 Bug ☼ Reliability ⭐ New
Description
The NATS event bus registry factory uses Handle::block_on to perform async connection during
provider resolution, which can deadlock or panic depending on the Tokio runtime context (e.g.,
current-thread) and will block a runtime thread during network connect. This can hang or crash app
startup when the NATS provider is selected.
Code

crates/mcb-providers/src/events/nats.rs[R49-70]

+fn create_nats_event_bus_provider(
+    config: &EventBusProviderConfig,
+) -> std::result::Result<Arc<dyn EventBusProvider>, String> {
+    let url = config
+        .extra
+        .get("url")
+        .cloned()
+        .unwrap_or_else(|| "nats://127.0.0.1:4222".to_owned());
+    let subject = config
+        .extra
+        .get("subject")
+        .cloned()
+        .unwrap_or_else(|| NATS_DEFAULT_SUBJECT.to_owned());
+    let client_name = config.extra.get("client_name").cloned();
+
+    let rt = tokio::runtime::Handle::try_current().map_err(|e| e.to_string())?;
+    rt.block_on(async move {
+        NatsEventBusProvider::with_options(&url, &subject, client_name.as_deref())
+            .await
+            .map(|provider| Arc::new(provider) as Arc<dyn EventBusProvider>)
+            .map_err(|e| e.to_string())
+    })
Evidence
The NATS provider is constructed via a sync registry factory that blocks on async connection;
init_app resolves the provider in async bootstrap code.

crates/mcb-providers/src/events/nats.rs[49-70]
crates/mcb-providers/src/events/nats.rs[122-130]
crates/mcb-infrastructure/src/di/bootstrap.rs[303-309]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The linkme registry `build` function for the NATS event bus performs async initialization by calling `tokio::runtime::Handle::block_on(...)`. This is risky inside an already-running Tokio runtime: it blocks a runtime thread and may deadlock/panic in some runtime flavors/contexts.

### Issue Context
Provider resolution happens during `init_app()` (async), so registry factories can execute while running on Tokio. The registry factory signature is sync (`fn(&Config) -> Result<Arc<...>, String>`), so it should not do async I/O.

### Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-70]
- crates/mcb-providers/src/events/nats.rs[122-130]
- crates/mcb-infrastructure/src/di/bootstrap.rs[303-309]

### Suggested fix
- Make the registry factory return a provider that connects lazily in async methods:
 - Store `url/subject/client_name` in the provider.
 - Hold a `tokio::sync::OnceCell<Arc<Client>>` (or similar) and `get_or_try_init().await` inside `publish/subscribe`.
- Alternatively, redesign the registry/DI path to support async factories (larger change), and await provider creation during bootstrap instead of blocking.

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


3. Ignored writeln! results 📘 Rule violation ☼ Reliability
Description
The PR adds let _ = writeln!(...) calls that discard I/O errors when printing validation output.
This can silently hide failures (e.g., broken pipe) and violates the requirement to propagate, log,
or explicitly justify ignoring errors.
Code

crates/mcb/src/cli/validate.rs[R170-181]

+        let _ = writeln!(
+            std::io::stdout(),
            "[{}] {}: {} ({}:{})",
-            violation.severity, violation.id, violation.message, file_display, line
+            violation.severity,
+            violation.id,
+            violation.message,
+            file_display,
+            line
        );
        if let Some(ref suggestion) = violation.suggestion {
-            println!("  → {suggestion}");
+            let _ = writeln!(std::io::stdout(), "  → {suggestion}");
        }
Evidence
PR Compliance ID 1208751 disallows silently swallowing errors such as by assigning a fallible call
result to _. The cited code uses let _ = writeln!(...), which discards std::io::Result without
handling or justification.

Rule 1208751: Errors must not be silently swallowed or hidden
crates/mcb/src/cli/validate.rs[152-181]
crates/mcb/src/cli/validate.rs[184-199]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Several `writeln!` results are discarded via `let _ = ...`, silently swallowing potential output errors.

## Issue Context
The compliance rule requires errors to be propagated, logged, or explicitly handled with justification.

## Fix Focus Areas
- crates/mcb/src/cli/validate.rs[140-199]

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


View more (4)
4. Spawn failure stalls indexing 🐞 Bug ☼ Reliability
Description
IndexingServiceImpl::index_codebase returns STARTED even when TaskRunnerProvider::spawn fails; in
that case the background task never runs, so the operation is never completed and polling clients
can wait forever. The only spawn failure handling is a warn log.
Code

crates/mcb-application/src/use_cases/indexing_service.rs[R301-307]

+        let task = Box::pin(async move {
            Self::run_indexing_task(service, files, workspace_root, collection_id, op_id).await;
        });

+        if let Err(e) = self.task_runner_provider.spawn(task) {
+            warn!("Failed to spawn indexing background task: {}", e);
+        }
Evidence
The code logs spawn failure but still returns STARTED; complete_operation() is only called within
run_indexing_task(), so a task that never spawns can never complete the operation.

crates/mcb-application/src/use_cases/indexing_service.rs[277-318]
crates/mcb-application/src/use_cases/indexing_service.rs[379-385]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`index_codebase()` creates an operation and returns `INDEXING_STATUS_STARTED` even if spawning the background task fails. Since completion happens only inside `run_indexing_task()`, a spawn failure leaves the operation permanently active.

### Issue Context
This regression is introduced by replacing infallible `tokio::spawn` with a fallible `TaskRunnerProvider::spawn` and not translating spawn failure into a terminal operation state.

### Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[277-318]
- crates/mcb-application/src/use_cases/indexing_service.rs[345-395]

### Suggested fix approach
- If `spawn()` returns an error:
 - Immediately finalize/cleanup the operation (e.g., call `indexing_ops.complete_operation(&operation_id)` or a new `fail_operation(...)` API if you have one).
 - Return an error from `index_codebase()` so callers don’t receive an operation_id that will never progress.
 - Optionally publish a failure/completed-with-error event (if your domain events model supports it).

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


5. Null event bus fallback 🐞 Bug ☼ Reliability
Description
default_event_bus() silently falls back to a no-op NullEventBusProvider when registry resolution
fails, so admin/server flows can drop all events without any hard failure. This is particularly
risky now that mcb-server no longer depends on mcb-providers and default admin state uses
default_event_bus without calling init_app().
Code

crates/mcb-infrastructure/src/infrastructure/factory.rs[R81-84]

pub fn default_event_bus() -> Arc<dyn EventBusProvider> {
-    Arc::new(mcb_providers::events::TokioEventBusProvider::new())
+    resolve_event_bus_provider(&EventBusProviderConfig::new("tokio"))
+        .unwrap_or_else(|_| Arc::new(NullEventBusProvider))
}
Evidence
default_event_bus swallows resolution errors and returns a no-op provider; server admin’s default
state calls default_event_bus directly, while provider linking is only guaranteed by init_app’s
ensure_linked call. The server crate no longer declares mcb-providers as a dependency, increasing
sensitivity to linker/registration issues.

crates/mcb-infrastructure/src/infrastructure/factory.rs[75-85]
crates/mcb-server/src/admin/web/router.rs[25-36]
crates/mcb-infrastructure/src/di/bootstrap.rs[209-217]
crates/mcb-infrastructure/src/provider_linker.rs[1-4]
crates/mcb-server/Cargo.toml[118-131]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`default_event_bus()` converts provider-resolution failure into a successful no-op event bus. This can hide missing linkme registration / provider-linking problems and causes events to be dropped silently.

### Issue Context
- `init_app()` explicitly calls `provider_linker::ensure_linked()`, but callers that bypass `init_app()` (e.g., server admin default state) still use `default_event_bus()`.
- `mcb-server` no longer has a direct dependency on `mcb-providers`, increasing the likelihood that linkme registrations aren’t present unless the linker hook is referenced.

### Fix Focus Areas
- crates/mcb-infrastructure/src/infrastructure/factory.rs[75-85]
- crates/mcb-server/src/admin/web/router.rs[25-36]
- crates/mcb-infrastructure/src/di/bootstrap.rs[209-217]
- crates/mcb-infrastructure/src/provider_linker.rs[1-4]
- crates/mcb-server/Cargo.toml[118-131]

### Suggested fix approach
- Prefer fail-fast over silent fallback:
 - Change `default_event_bus()` to return `Result<Arc<dyn EventBusProvider>>` and make callers handle failure explicitly, OR
 - At minimum, emit an `error!`/`warn!` that clearly indicates events will be dropped.
- Ensure providers are linked for this path:
 - Call `crate::provider_linker::ensure_linked()` inside `default_event_bus()` before resolving.
- Only use a NullEventBusProvider when explicitly requested (e.g., config setting), not as an error fallback.

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


6. #![allow(unsafe_code)] added 📘 Rule violation ⛨ Security
Description
The PR introduces crate-level unsafe_code suppression, weakening enforcement of the no-unsafe
policy and potentially permitting unsafe expansions without review. This violates the requirement to
disallow unsafe code and keep unsafe_code denied.
Code

crates/mcb-domain/src/registry/project_detection.rs[2]

+#![allow(unsafe_code)]
Evidence
PR Compliance ID 1208417 forbids unsafe code and requires the unsafe_code deny posture to remain
in place. The changed code adds #![allow(unsafe_code)] (and keeps it in another changed file),
which explicitly suppresses that enforcement.

Rule 1208417: No unsafe code blocks
crates/mcb-domain/src/registry/project_detection.rs[1-3]
crates/mcb-infrastructure/tests/unit/config/config_figment_tests.rs[1-3]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`#![allow(unsafe_code)]` was added, which disables the project’s no-unsafe enforcement for that module.

## Issue Context
This rule requires that `unsafe` is not permitted and that the `unsafe_code` deny policy remains effective.

## Fix Focus Areas
- crates/mcb-domain/src/registry/project_detection.rs[1-3]
- crates/mcb-infrastructure/tests/unit/config/config_figment_tests.rs[1-3]

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


7. Ignore rules not applied 🐞 Bug ≡ Correctness
Description
IndexingServiceImpl::discover_files no longer uses an ignore-aware walker and now only skips a
hard-coded SKIP_DIRS list, so files excluded by .gitignore/.ignore may be indexed (changed results +
potentially large perf regression). This contradicts the module’s stated “respecting ignore
patterns” behavior.
Code

crates/mcb-application/src/use_cases/indexing_service.rs[R199-220]

+        let mut dirs = vec![path.to_path_buf()];
+
+        while let Some(dir) = dirs.pop() {
+            match self.file_system_provider.read_dir_entries(&dir).await {
+                Ok(entries) => {
+                    for entry in entries {
+                        if entry.is_dir {
+                            let should_skip = entry
+                                .path
+                                .file_name()
+                                .and_then(|n| n.to_str())
+                                .is_some_and(|name| SKIP_DIRS.contains(&name));
+
+                            if !should_skip {
+                                dirs.push(entry.path);
+                            }
+                            continue;
+                        }
+
+                        if entry.is_file && self.is_supported_file(&entry.path) {
+                            files.push(entry.path);
+                        }
Evidence
The indexing service docs still promise ignore-pattern support, but the new traversal only checks
SKIP_DIRS and uses read_dir_entries() without any ignore-file evaluation.

crates/mcb-application/src/use_cases/indexing_service.rs[11-16]
crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
crates/mcb-application/src/constants.rs[22-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`IndexingServiceImpl::discover_files()` switched from an ignore-aware walker to a raw directory traversal, but the new implementation does not apply `.gitignore`/`.ignore` (or similar) rules. This can cause the indexer to ingest files that were previously ignored, changing indexing results and potentially dramatically increasing the amount of work.

### Issue Context
The module documentation claims file discovery respects ignore patterns, but the current implementation only filters directory names via `SKIP_DIRS`.

### Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[11-16]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
- crates/mcb-application/src/constants.rs[22-24]

### Suggested fix approach
- Re-introduce ignore-aware traversal semantics (equivalent to the prior `ignore::WalkBuilder` behavior) while keeping DI goals:
 - Option A (minimal): use `ignore::WalkBuilder` again inside `discover_files()` and keep `FileSystemProvider` only for file reads.
 - Option B (more architectural): extend the filesystem port to provide an ignore-aware walk/list API (or ignore-matching helper) and implement it in the local provider.
- Add a unit/integration test that asserts files under an ignored path (e.g. a temp dir with `.gitignore` ignoring `target/` or `dist/`) are not discovered.

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



Remediation recommended

8. Hardcoded NATS URL default 📘 Rule violation ☼ Reliability ⭐ New
Description
create_nats_event_bus_provider silently falls back to a hardcoded nats://127.0.0.1:4222 when
config.extra.url is missing, masking misconfiguration and potentially connecting to the wrong
broker in non-local environments. Required external connection settings should be provided via
configuration and missing values should cause a clear failure instead of using an in-code default.
Code

crates/mcb-providers/src/events/nats.rs[R49-56]

+fn create_nats_event_bus_provider(
+    config: &EventBusProviderConfig,
+) -> std::result::Result<Arc<dyn EventBusProvider>, String> {
+    let url = config
+        .extra
+        .get("url")
+        .cloned()
+        .unwrap_or_else(|| "nats://127.0.0.1:4222".to_owned());
Evidence
PR Compliance ID 1206783 requires failing fast when required external configuration is missing and
forbids substituting hard-coded defaults. PR Compliance ID 1208701 disallows hardcoding
environment-dependent values; the new code defaults url to a localhost NATS endpoint when not
configured.

Rule 1206783: Fail fast when required external configuration is missing at startup
Rule 1208701: Do not hardcode values that should be configurable
crates/mcb-providers/src/events/nats.rs[49-56]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`create_nats_event_bus_provider()` uses a hardcoded default NATS URL when `config.extra["url"]` is missing. This prevents fail-fast behavior for required external configuration and hardcodes an environment-dependent value.

## Issue Context
The NATS URL determines which external broker the provider connects to; defaulting to localhost can hide configuration mistakes and cause unexpected behavior outside local development.

## Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-71]

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


9. Async FS blocks Tokio 🐞 Bug ➹ Performance ⭐ New
Description
LocalFileSystemProvider implements async FileSystemProvider methods using blocking std::fs
operations, so indexing can block Tokio worker threads while scanning directories and reading files.
This can cause runtime starvation and latency spikes during indexing, especially on large workspaces
or slow disks.
Code

crates/mcb-providers/src/fs/mod.rs[R58-72]

+#[async_trait]
+impl FileSystemProvider for LocalFileSystemProvider {
+    async fn read_to_string(&self, path: &Path) -> Result<String, Error> {
+        std::fs::read_to_string(path)
+            .map_err(|e| Error::internal(format!("Failed to read file {}: {e}", path.display())))
+    }
+
+    async fn read_dir_entries(&self, path: &Path) -> Result<Vec<DirEntry>, Error> {
+        Self::read_dir_sync(path)
+    }
+
+    async fn canonicalize_path(&self, path: &Path) -> Result<PathBuf, Error> {
+        std::fs::canonicalize(path)
+            .map_err(|e| Error::internal(format!("Failed to canonicalize {}: {e}", path.display())))
+    }
Evidence
The new async filesystem provider uses std::fs internally, and the indexing service now awaits these
methods on the Tokio task path.

crates/mcb-providers/src/fs/mod.rs[34-72]
crates/mcb-application/src/use_cases/indexing_service.rs[192-227]
crates/mcb-application/src/use_cases/indexing_service.rs[427-444]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`LocalFileSystemProvider` exposes async APIs but performs synchronous filesystem I/O (`std::fs::*`) inside those async fns. Awaiting these methods does not make the I/O non-blocking; it still runs on the async executor thread.

### Issue Context
Indexing uses `file_system_provider.read_dir_entries().await` during discovery and `read_to_string().await` per file, so this blocking behavior can materially impact end-to-end indexing.

### Fix Focus Areas
- crates/mcb-providers/src/fs/mod.rs[34-72]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-227]
- crates/mcb-application/src/use_cases/indexing_service.rs[427-444]

### Suggested fix
- Switch to `tokio::fs::read_to_string`.
- Implement `read_dir_entries` using `tokio::fs::read_dir` and iterate via `next_entry().await`.
- For operations without async equivalents (or for simplicity), wrap blocking calls in `tokio::task::spawn_blocking` (or `block_in_place` where appropriate) and `await` the join handle.
- Apply the same approach to `canonicalize_path`.

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


10. Blocking marker scan 🐞 Bug ☼ Reliability ⭐ New
Description
ProjectService::detect_all() does synchronous Path::exists checks inside an async method for each
detector marker file, which can block the async runtime and also collapses I/O errors into "not
found". This can hide permission/IO problems and silently skip project detection.
Code

crates/mcb-infrastructure/src/project/service.rs[R52-55]

+        for entry in PROJECT_DETECTORS {
+            let has_marker = entry.marker_files.iter().any(|f| path.join(f).exists());
+            if !has_marker {
+                continue;
Evidence
The async project detection loop performs blocking filesystem existence checks for marker files, and
exists() cannot report I/O errors distinctly.

crates/mcb-infrastructure/src/project/service.rs[43-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`detect_all()` is async but calls `path.join(f).exists()` (blocking metadata) per marker file; `exists()` also returns `false` on many errors, so detection silently ignores I/O failures.

### Issue Context
Project detection may run during request handling or other concurrent async workloads; blocking calls reduce throughput and can make failures non-obvious.

### Fix Focus Areas
- crates/mcb-infrastructure/src/project/service.rs[43-63]

### Suggested fix
- Use an async check (e.g., `tokio::fs::try_exists` if available in your Tokio version) or `tokio::fs::metadata(...).await`.
- If you keep a sync check, run it via `tokio::task::spawn_blocking`.
- Distinguish between "missing" and "error" so permission/IO problems can be logged or surfaced appropriately.

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


View more (3)
11. Unstable default DB provider 🐞 Bug ☼ Reliability
Description
Infrastructure helper resolve_default_database_provider() selects the first provider returned by
list_database_providers(), but the registry list preserves linkme slice iteration order and does
not define a default selection policy. Adding another database provider can unpredictably change
which backend these helpers use.
Code

crates/mcb-infrastructure/src/di/repositories.rs[R23-32]

+fn resolve_default_database_provider() -> Result<Arc<dyn DatabaseProvider>> {
+    let provider_name = list_database_providers()
+        .first()
+        .map(|(name, _)| *name)
+        .ok_or_else(|| {
+            Error::configuration("Database: no providers registered in linkme registry")
+        })?;
+
+    resolve_database_provider(&DatabaseProviderConfig::new(provider_name))
+}
Evidence
The helper explicitly picks the first registry entry, and the registry’s list function preserves
slice iteration order without sorting or a default policy.

crates/mcb-infrastructure/src/di/repositories.rs[23-32]
crates/mcb-domain/src/macros/registry.rs[64-67]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`resolve_default_database_provider()` chooses `.first()` from the registered database providers list, which is not an explicit default policy and can change based on registration/link order.

### Issue Context
This affects the infrastructure helper constructors in `di/repositories.rs` (not the main `init_app()` path, which resolves from configured provider).

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/repositories.rs[23-37]
- crates/mcb-domain/src/macros/registry.rs[64-67]

### Suggested fix approach
- Define a deterministic selection policy:
 - Prefer an explicit known name (e.g. try `"sqlite"` first, else error), OR
 - Accept a provider name (or config) parameter in these helper functions and resolve that, OR
 - Fail with a clear configuration error when multiple providers are registered and no explicit provider is specified.

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


12. VCS repo reopened per call 🐞 Bug ➹ Performance
Description
DynamicVcsProvider re-runs provider detection and re-opens the repository for operations like
list_files/read_file/diff_refs, even when given an already-opened VcsRepository. For Git2Provider,
open_repository computes the root commit and branch list, so repeated calls add avoidable overhead.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R68-91]

+    async fn list_branches(&self, repo: &VcsRepository) -> Result<Vec<VcsBranch>> {
+        let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
+        provider.list_branches(&concrete_repo).await
+    }
+
+    async fn commit_history(
+        &self,
+        repo: &VcsRepository,
+        branch: &str,
+        limit: Option<usize>,
+    ) -> Result<Vec<VcsCommit>> {
+        let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
+        provider.commit_history(&concrete_repo, branch, limit).await
+    }
+
+    async fn list_files(&self, repo: &VcsRepository, branch: &str) -> Result<Vec<PathBuf>> {
+        let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
+        provider.list_files(&concrete_repo, branch).await
+    }
+
+    async fn read_file(&self, repo: &VcsRepository, branch: &str, path: &Path) -> Result<String> {
+        let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
+        provider.read_file(&concrete_repo, branch, path).await
+    }
Evidence
DynamicVcsProvider methods re-call provider selection/opening based on repo.path; Git2Provider’s
open_repository performs non-trivial work (revwalk to find root commit and listing branches) which
will be repeated under the current dynamic strategy.

crates/mcb-infrastructure/src/di/vcs.rs[33-105]
crates/mcb-providers/src/vcs/git2_provider.rs[57-75]
crates/mcb-providers/src/vcs/git2_provider.rs[117-135]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DynamicVcsProvider` calls `provider_and_repo_for_path(repo.path())` inside most operations, which repeatedly calls `open_repository()` (and tries multiple providers). This discards any benefit of opening once and can be expensive for providers that scan repository history/branches during open.

### Issue Context
`VcsRepository` is a pure-data entity; to avoid reopening, the dynamic provider needs an internal cache that maps a repo identity (e.g., `RepositoryId` or canonicalized path) to the chosen concrete provider.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[33-105]
- crates/mcb-providers/src/vcs/git2_provider.rs[57-107]
- crates/mcb-providers/src/vcs/git2_provider.rs[117-135]

### Suggested fix approach
- In `DynamicVcsProvider::open_repository()`, after resolving a provider, cache the chosen provider keyed by `repo.id()` (or canonical path).
- In subsequent methods (list_branches, list_files, read_file, diff_refs, etc.), pick the provider from the cache instead of re-probing all providers.
- Keep a fallback path (probe providers) if the cache misses, but avoid doing it on every call.

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


13. Unjustified #[allow(...)] suppressions 📘 Rule violation ⚙ Maintainability
Description
Multiple #[allow(...)] directives were added without an inline justification comment on the same
line or immediately following line. This reduces auditability and makes it unclear why suppressions
are safe/necessary.
Code

crates/mcb-application/src/use_cases/indexing_service.rs[112]

+#[allow(missing_docs)]
Evidence
PR Compliance ID 1206895 requires an inline justification comment for each suppression directive.
The cited lines add #[allow(missing_docs)] / #[allow(clippy::...)] without a same-line or
next-line justification comment.

Rule 1206895: Require inline justification comments for all static-analysis suppression directives
crates/mcb-application/src/use_cases/indexing_service.rs[111-114]
crates/mcb-infrastructure/src/di/test_factory.rs[34-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New static-analysis suppression directives were added without required inline justification.

## Issue Context
The compliance rule requires a specific justification comment on the same line or the immediately following line for each suppression.

## Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[111-114]
- crates/mcb-infrastructure/src/di/test_factory.rs[34-41]

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


Grey Divider

Qodo Logo

@@ -0,0 +1,15 @@
#![allow(missing_docs)]
#![allow(unsafe_code)]

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

1. #![allow(unsafe_code)] added 📘 Rule violation ⛨ Security

The PR introduces crate-level unsafe_code suppression, weakening enforcement of the no-unsafe
policy and potentially permitting unsafe expansions without review. This violates the requirement to
disallow unsafe code and keep unsafe_code denied.
Agent Prompt
## Issue description
`#![allow(unsafe_code)]` was added, which disables the project’s no-unsafe enforcement for that module.

## Issue Context
This rule requires that `unsafe` is not permitted and that the `unsafe_code` deny policy remains effective.

## Fix Focus Areas
- crates/mcb-domain/src/registry/project_detection.rs[1-3]
- crates/mcb-infrastructure/tests/unit/config/config_figment_tests.rs[1-3]

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

}

/// `IndexingServiceDeps` struct.
#[allow(missing_docs)]

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. Unjustified #[allow(...)] suppressions 📘 Rule violation ⚙ Maintainability

Multiple #[allow(...)] directives were added without an inline justification comment on the same
line or immediately following line. This reduces auditability and makes it unclear why suppressions
are safe/necessary.
Agent Prompt
## Issue description
New static-analysis suppression directives were added without required inline justification.

## Issue Context
The compliance rule requires a specific justification comment on the same line or the immediately following line for each suppression.

## Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[111-114]
- crates/mcb-infrastructure/src/di/test_factory.rs[34-41]

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

Comment on lines +170 to 181
let _ = writeln!(
std::io::stdout(),
"[{}] {}: {} ({}:{})",
violation.severity, violation.id, violation.message, file_display, line
violation.severity,
violation.id,
violation.message,
file_display,
line
);
if let Some(ref suggestion) = violation.suggestion {
println!(" → {suggestion}");
let _ = writeln!(std::io::stdout(), " → {suggestion}");
}

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

3. Ignored writeln! results 📘 Rule violation ☼ Reliability

The PR adds let _ = writeln!(...) calls that discard I/O errors when printing validation output.
This can silently hide failures (e.g., broken pipe) and violates the requirement to propagate, log,
or explicitly justify ignoring errors.
Agent Prompt
## Issue description
Several `writeln!` results are discarded via `let _ = ...`, silently swallowing potential output errors.

## Issue Context
The compliance rule requires errors to be propagated, logged, or explicitly handled with justification.

## Fix Focus Areas
- crates/mcb/src/cli/validate.rs[140-199]

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

Comment on lines +199 to +220
let mut dirs = vec![path.to_path_buf()];

while let Some(dir) = dirs.pop() {
match self.file_system_provider.read_dir_entries(&dir).await {
Ok(entries) => {
for entry in entries {
if entry.is_dir {
let should_skip = entry
.path
.file_name()
.and_then(|n| n.to_str())
.is_some_and(|name| SKIP_DIRS.contains(&name));

if !should_skip {
dirs.push(entry.path);
}
continue;
}

if entry.is_file && self.is_supported_file(&entry.path) {
files.push(entry.path);
}

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

4. Ignore rules not applied 🐞 Bug ≡ Correctness

IndexingServiceImpl::discover_files no longer uses an ignore-aware walker and now only skips a
hard-coded SKIP_DIRS list, so files excluded by .gitignore/.ignore may be indexed (changed results +
potentially large perf regression). This contradicts the module’s stated “respecting ignore
patterns” behavior.
Agent Prompt
### Issue description
`IndexingServiceImpl::discover_files()` switched from an ignore-aware walker to a raw directory traversal, but the new implementation does not apply `.gitignore`/`.ignore` (or similar) rules. This can cause the indexer to ingest files that were previously ignored, changing indexing results and potentially dramatically increasing the amount of work.

### Issue Context
The module documentation claims file discovery respects ignore patterns, but the current implementation only filters directory names via `SKIP_DIRS`.

### Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[11-16]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
- crates/mcb-application/src/constants.rs[22-24]

### Suggested fix approach
- Re-introduce ignore-aware traversal semantics (equivalent to the prior `ignore::WalkBuilder` behavior) while keeping DI goals:
  - Option A (minimal): use `ignore::WalkBuilder` again inside `discover_files()` and keep `FileSystemProvider` only for file reads.
  - Option B (more architectural): extend the filesystem port to provide an ignore-aware walk/list API (or ignore-matching helper) and implement it in the local provider.
- Add a unit/integration test that asserts files under an ignored path (e.g. a temp dir with `.gitignore` ignoring `target/` or `dist/`) are not discovered.

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

Comment on lines +301 to +307
let task = Box::pin(async move {
Self::run_indexing_task(service, files, workspace_root, collection_id, op_id).await;
});

if let Err(e) = self.task_runner_provider.spawn(task) {
warn!("Failed to spawn indexing background task: {}", e);
}

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. Spawn failure stalls indexing 🐞 Bug ☼ Reliability

IndexingServiceImpl::index_codebase returns STARTED even when TaskRunnerProvider::spawn fails; in
that case the background task never runs, so the operation is never completed and polling clients
can wait forever. The only spawn failure handling is a warn log.
Agent Prompt
### Issue description
`index_codebase()` creates an operation and returns `INDEXING_STATUS_STARTED` even if spawning the background task fails. Since completion happens only inside `run_indexing_task()`, a spawn failure leaves the operation permanently active.

### Issue Context
This regression is introduced by replacing infallible `tokio::spawn` with a fallible `TaskRunnerProvider::spawn` and not translating spawn failure into a terminal operation state.

### Fix Focus Areas
- crates/mcb-application/src/use_cases/indexing_service.rs[277-318]
- crates/mcb-application/src/use_cases/indexing_service.rs[345-395]

### Suggested fix approach
- If `spawn()` returns an error:
  - Immediately finalize/cleanup the operation (e.g., call `indexing_ops.complete_operation(&operation_id)` or a new `fail_operation(...)` API if you have one).
  - Return an error from `index_codebase()` so callers don’t receive an operation_id that will never progress.
  - Optionally publish a failure/completed-with-error event (if your domain events model supports it).

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

Comment on lines 81 to 84
pub fn default_event_bus() -> Arc<dyn EventBusProvider> {
Arc::new(mcb_providers::events::TokioEventBusProvider::new())
resolve_event_bus_provider(&EventBusProviderConfig::new("tokio"))
.unwrap_or_else(|_| Arc::new(NullEventBusProvider))
}

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

6. Null event bus fallback 🐞 Bug ☼ Reliability

default_event_bus() silently falls back to a no-op NullEventBusProvider when registry resolution
fails, so admin/server flows can drop all events without any hard failure. This is particularly
risky now that mcb-server no longer depends on mcb-providers and default admin state uses
default_event_bus without calling init_app().
Agent Prompt
### Issue description
`default_event_bus()` converts provider-resolution failure into a successful no-op event bus. This can hide missing linkme registration / provider-linking problems and causes events to be dropped silently.

### Issue Context
- `init_app()` explicitly calls `provider_linker::ensure_linked()`, but callers that bypass `init_app()` (e.g., server admin default state) still use `default_event_bus()`.
- `mcb-server` no longer has a direct dependency on `mcb-providers`, increasing the likelihood that linkme registrations aren’t present unless the linker hook is referenced.

### Fix Focus Areas
- crates/mcb-infrastructure/src/infrastructure/factory.rs[75-85]
- crates/mcb-server/src/admin/web/router.rs[25-36]
- crates/mcb-infrastructure/src/di/bootstrap.rs[209-217]
- crates/mcb-infrastructure/src/provider_linker.rs[1-4]
- crates/mcb-server/Cargo.toml[118-131]

### Suggested fix approach
- Prefer fail-fast over silent fallback:
  - Change `default_event_bus()` to return `Result<Arc<dyn EventBusProvider>>` and make callers handle failure explicitly, OR
  - At minimum, emit an `error!`/`warn!` that clearly indicates events will be dropped.
- Ensure providers are linked for this path:
  - Call `crate::provider_linker::ensure_linked()` inside `default_event_bus()` before resolving.
- Only use a NullEventBusProvider when explicitly requested (e.g., config setting), not as an error fallback.

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

Comment on lines +23 to +32
fn resolve_default_database_provider() -> Result<Arc<dyn DatabaseProvider>> {
let provider_name = list_database_providers()
.first()
.map(|(name, _)| *name)
.ok_or_else(|| {
Error::configuration("Database: no providers registered in linkme registry")
})?;

resolve_database_provider(&DatabaseProviderConfig::new(provider_name))
}

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

7. Unstable default db provider 🐞 Bug ☼ Reliability

Infrastructure helper resolve_default_database_provider() selects the first provider returned by
list_database_providers(), but the registry list preserves linkme slice iteration order and does
not define a default selection policy. Adding another database provider can unpredictably change
which backend these helpers use.
Agent Prompt
### Issue description
`resolve_default_database_provider()` chooses `.first()` from the registered database providers list, which is not an explicit default policy and can change based on registration/link order.

### Issue Context
This affects the infrastructure helper constructors in `di/repositories.rs` (not the main `init_app()` path, which resolves from configured provider).

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/repositories.rs[23-37]
- crates/mcb-domain/src/macros/registry.rs[64-67]

### Suggested fix approach
- Define a deterministic selection policy:
  - Prefer an explicit known name (e.g. try `"sqlite"` first, else error), OR
  - Accept a provider name (or config) parameter in these helper functions and resolve that, OR
  - Fail with a clear configuration error when multiple providers are registered and no explicit provider is specified.

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

Comment on lines +68 to +91
async fn list_branches(&self, repo: &VcsRepository) -> Result<Vec<VcsBranch>> {
let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
provider.list_branches(&concrete_repo).await
}

async fn commit_history(
&self,
repo: &VcsRepository,
branch: &str,
limit: Option<usize>,
) -> Result<Vec<VcsCommit>> {
let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
provider.commit_history(&concrete_repo, branch, limit).await
}

async fn list_files(&self, repo: &VcsRepository, branch: &str) -> Result<Vec<PathBuf>> {
let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
provider.list_files(&concrete_repo, branch).await
}

async fn read_file(&self, repo: &VcsRepository, branch: &str, path: &Path) -> Result<String> {
let (provider, concrete_repo) = self.provider_and_repo_for_path(repo.path()).await?;
provider.read_file(&concrete_repo, branch, path).await
}

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

8. Vcs repo reopened per call 🐞 Bug ➹ Performance

DynamicVcsProvider re-runs provider detection and re-opens the repository for operations like
list_files/read_file/diff_refs, even when given an already-opened VcsRepository. For Git2Provider,
open_repository computes the root commit and branch list, so repeated calls add avoidable overhead.
Agent Prompt
### Issue description
`DynamicVcsProvider` calls `provider_and_repo_for_path(repo.path())` inside most operations, which repeatedly calls `open_repository()` (and tries multiple providers). This discards any benefit of opening once and can be expensive for providers that scan repository history/branches during open.

### Issue Context
`VcsRepository` is a pure-data entity; to avoid reopening, the dynamic provider needs an internal cache that maps a repo identity (e.g., `RepositoryId` or canonicalized path) to the chosen concrete provider.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[33-105]
- crates/mcb-providers/src/vcs/git2_provider.rs[57-107]
- crates/mcb-providers/src/vcs/git2_provider.rs[117-135]

### Suggested fix approach
- In `DynamicVcsProvider::open_repository()`, after resolving a provider, cache the chosen provider keyed by `repo.id()` (or canonical path).
- In subsequent methods (list_branches, list_files, read_file, diff_refs, etc.), pick the provider from the cache instead of re-probing all providers.
- Keep a fallback path (probe providers) if the cache misses, but avoid doing it on every call.

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

Comment on lines +49 to +56
fn create_nats_event_bus_provider(
config: &EventBusProviderConfig,
) -> std::result::Result<Arc<dyn EventBusProvider>, String> {
let url = config
.extra
.get("url")
.cloned()
.unwrap_or_else(|| "nats://127.0.0.1:4222".to_owned());

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. Hardcoded nats url default 📘 Rule violation ☼ Reliability

create_nats_event_bus_provider silently falls back to a hardcoded nats://127.0.0.1:4222 when
config.extra.url is missing, masking misconfiguration and potentially connecting to the wrong
broker in non-local environments. Required external connection settings should be provided via
configuration and missing values should cause a clear failure instead of using an in-code default.
Agent Prompt
## Issue description
`create_nats_event_bus_provider()` uses a hardcoded default NATS URL when `config.extra["url"]` is missing. This prevents fail-fast behavior for required external configuration and hardcodes an environment-dependent value.

## Issue Context
The NATS URL determines which external broker the provider connects to; defaulting to localhost can hide configuration mistakes and cause unexpected behavior outside local development.

## Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-71]

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

Comment on lines +62 to +66
fn repository_id(&self, repo: &VcsRepository) -> RepositoryId {
self.providers
.first()
.map_or_else(|| *repo.id(), |provider| provider.repository_id(repo))
}

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

2. Wrong vcs repository_id 🐞 Bug ≡ Correctness

DynamicVcsProvider::repository_id() delegates to the first registered provider rather than the
provider that produced the VcsRepository, so repository identity can change depending on registry
ordering. This can cause inconsistent IDs vs VcsRepository::id() and mis-associate repository-scoped
data.
Agent Prompt
### Issue description
`DynamicVcsProvider::repository_id()` currently calls `repository_id()` on `self.providers.first()`, which is unrelated to the provider that created the `VcsRepository`. That can return a different `RepositoryId` than the one embedded in the `VcsRepository`, breaking identity stability.

### Issue Context
`VcsRepository` already stores an `id` computed at open time by the concrete provider. The dynamic wrapper should not recompute identity using an arbitrary provider.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[55-66]
- crates/mcb-domain/src/entities/vcs/vcs_repo.rs[10-18]

### Suggested fix
- Change `DynamicVcsProvider::repository_id()` to return `*repo.id()`.
- (Optional, if you truly need recomputation) store the selected provider alongside the repo (e.g., wrap `VcsRepository` in a provider-tagged struct) so subsequent calls use the same provider.

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

Comment on lines +49 to +70
fn create_nats_event_bus_provider(
config: &EventBusProviderConfig,
) -> std::result::Result<Arc<dyn EventBusProvider>, String> {
let url = config
.extra
.get("url")
.cloned()
.unwrap_or_else(|| "nats://127.0.0.1:4222".to_owned());
let subject = config
.extra
.get("subject")
.cloned()
.unwrap_or_else(|| NATS_DEFAULT_SUBJECT.to_owned());
let client_name = config.extra.get("client_name").cloned();

let rt = tokio::runtime::Handle::try_current().map_err(|e| e.to_string())?;
rt.block_on(async move {
NatsEventBusProvider::with_options(&url, &subject, client_name.as_deref())
.await
.map(|provider| Arc::new(provider) as Arc<dyn EventBusProvider>)
.map_err(|e| e.to_string())
})

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

3. Nats factory blocks runtime 🐞 Bug ☼ Reliability

The NATS event bus registry factory uses Handle::block_on to perform async connection during
provider resolution, which can deadlock or panic depending on the Tokio runtime context (e.g.,
current-thread) and will block a runtime thread during network connect. This can hang or crash app
startup when the NATS provider is selected.
Agent Prompt
### Issue description
The linkme registry `build` function for the NATS event bus performs async initialization by calling `tokio::runtime::Handle::block_on(...)`. This is risky inside an already-running Tokio runtime: it blocks a runtime thread and may deadlock/panic in some runtime flavors/contexts.

### Issue Context
Provider resolution happens during `init_app()` (async), so registry factories can execute while running on Tokio. The registry factory signature is sync (`fn(&Config) -> Result<Arc<...>, String>`), so it should not do async I/O.

### Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-70]
- crates/mcb-providers/src/events/nats.rs[122-130]
- crates/mcb-infrastructure/src/di/bootstrap.rs[303-309]

### Suggested fix
- Make the registry factory return a provider that connects lazily in async methods:
  - Store `url/subject/client_name` in the provider.
  - Hold a `tokio::sync::OnceCell<Arc<Client>>` (or similar) and `get_or_try_init().await` inside `publish/subscribe`.
- Alternatively, redesign the registry/DI path to support async factories (larger change), and await provider creation during bootstrap instead of blocking.

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

Comment on lines +58 to +72
#[async_trait]
impl FileSystemProvider for LocalFileSystemProvider {
async fn read_to_string(&self, path: &Path) -> Result<String, Error> {
std::fs::read_to_string(path)
.map_err(|e| Error::internal(format!("Failed to read file {}: {e}", path.display())))
}

async fn read_dir_entries(&self, path: &Path) -> Result<Vec<DirEntry>, Error> {
Self::read_dir_sync(path)
}

async fn canonicalize_path(&self, path: &Path) -> Result<PathBuf, Error> {
std::fs::canonicalize(path)
.map_err(|e| Error::internal(format!("Failed to canonicalize {}: {e}", path.display())))
}

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

4. Async fs blocks tokio 🐞 Bug ➹ Performance

LocalFileSystemProvider implements async FileSystemProvider methods using blocking std::fs
operations, so indexing can block Tokio worker threads while scanning directories and reading files.
This can cause runtime starvation and latency spikes during indexing, especially on large workspaces
or slow disks.
Agent Prompt
### Issue description
`LocalFileSystemProvider` exposes async APIs but performs synchronous filesystem I/O (`std::fs::*`) inside those async fns. Awaiting these methods does not make the I/O non-blocking; it still runs on the async executor thread.

### Issue Context
Indexing uses `file_system_provider.read_dir_entries().await` during discovery and `read_to_string().await` per file, so this blocking behavior can materially impact end-to-end indexing.

### Fix Focus Areas
- crates/mcb-providers/src/fs/mod.rs[34-72]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-227]
- crates/mcb-application/src/use_cases/indexing_service.rs[427-444]

### Suggested fix
- Switch to `tokio::fs::read_to_string`.
- Implement `read_dir_entries` using `tokio::fs::read_dir` and iterate via `next_entry().await`.
- For operations without async equivalents (or for simplicity), wrap blocking calls in `tokio::task::spawn_blocking` (or `block_in_place` where appropriate) and `await` the join handle.
- Apply the same approach to `canonicalize_path`.

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

Comment on lines +52 to +55
for entry in PROJECT_DETECTORS {
let has_marker = entry.marker_files.iter().any(|f| path.join(f).exists());
if !has_marker {
continue;

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

5. Blocking marker scan 🐞 Bug ☼ Reliability

ProjectService::detect_all() does synchronous Path::exists checks inside an async method for each
detector marker file, which can block the async runtime and also collapses I/O errors into "not
found". This can hide permission/IO problems and silently skip project detection.
Agent Prompt
### Issue description
`detect_all()` is async but calls `path.join(f).exists()` (blocking metadata) per marker file; `exists()` also returns `false` on many errors, so detection silently ignores I/O failures.

### Issue Context
Project detection may run during request handling or other concurrent async workloads; blocking calls reduce throughput and can make failures non-obvious.

### Fix Focus Areas
- crates/mcb-infrastructure/src/project/service.rs[43-63]

### Suggested fix
- Use an async check (e.g., `tokio::fs::try_exists` if available in your Tokio version) or `tokio::fs::metadata(...).await`.
- If you keep a sync check, run it via `tokio::task::spawn_blocking`.
- Distinguish between "missing" and "error" so permission/IO problems can be logged or surfaced appropriately.

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

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit b931063

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

40 issues found across 71 files

Confidence score: 2/5

  • crates/mcb-providers/src/events/nats.rs and crates/mcb-infrastructure/src/di/bootstrap.rs can panic during normal startup when event_bus.provider = "nats" because provider resolution calls Handle::block_on from inside an active Tokio runtime, turning a valid config into a hard crash—make NATS provider construction async end-to-end (or move blocking work off-runtime) so bootstrap returns an error instead of panicking.
  • crates/mcb-providers/src/events/tokio.rs and crates/mcb-infrastructure/src/di/bootstrap.rs do not reliably validate capacity, so non-numeric values silently fall back and 0 can panic in broadcast::channel(0) during resolution—parse and enforce a positive capacity before provider creation and surface a configuration error.
  • crates/mcb-application/src/use_cases/indexing_service.rs regresses indexing behavior by dropping ignore-file matching, which can ingest git-ignored/generated/vendor content, and it can also report started after spawn failure, leaving status stuck as active—restore WalkBuilder ignore semantics and roll back operation state when spawn fails.
  • crates/mcb-providers/src/task/mod.rs and crates/mcb-providers/src/fs/mod.rs can fail abruptly under common runtime conditions: task spawning panics without an entered Tokio runtime and async FS APIs block worker threads via std::fs calls—use Handle::try_current() to return Err and move file I/O to tokio::fs/spawn_blocking to avoid runtime instability.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/mcb-providers/src/events/nats.rs">

<violation number="1" location="crates/mcb-providers/src/events/nats.rs:65">
P0: Selecting the NATS event bus during normal startup will panic: `init_app` is async and resolves this provider from inside the Tokio runtime, while this synchronous factory calls `Handle::block_on` on that same runtime thread. The NATS connection needs an async-aware construction path (for example, an async registry constructor/resolver) rather than nesting `block_on`; otherwise the advertised NATS provider cannot be initialized through DI.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/bootstrap.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/bootstrap.rs:308">
P0: Starting the app with `event_bus.provider = "nats"` panics during bootstrap. `resolve_from_config()` invokes the synchronous registry builder, while the NATS builder calls `Handle::block_on`; this call occurs inside async `init_app`, where Tokio forbids nested `block_on`. NATS resolution needs an async construction path (or a provider that connects lazily) so startup can await it and propagate connection errors normally.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/bootstrap.rs:308">
P1: A configured event-bus `capacity = 0` now panics during startup instead of returning a configuration error. Validate positive capacity before resolving the provider.</violation>

<violation number="3" location="crates/mcb-infrastructure/src/di/bootstrap.rs:339">
P2: Database provider construction now runs twice, and repositories are created by a different provider instance from the one that connected the executor. Reusing `db_provider.connect(...)` avoids duplicate initialization and future state mismatches for stateful providers.</violation>
</file>

<file name="crates/mcb-application/src/use_cases/indexing_service.rs">

<violation number="1" location="crates/mcb-application/src/use_cases/indexing_service.rs:199">
P1: Git-ignored files and directories are now indexed because the manual DFS applies only `SKIP_DIRS`, dropping `WalkBuilder`'s ignore-file matching. This can ingest generated/vendor code or intentionally excluded source; preserve ignore matching in the filesystem abstraction or retain an ignore-aware walker.</violation>

<violation number="2" location="crates/mcb-application/src/use_cases/indexing_service.rs:202">
P2: One unreadable or concurrently removed entry now prevents every other file and subdirectory in that directory from being indexed. Consider exposing per-entry results or partial entries so discovery can record the bad entry and continue with its siblings.</violation>

<violation number="3" location="crates/mcb-application/src/use_cases/indexing_service.rs:305">
P2: A spawn failure is returned as a successful `started` result and leaves `get_status()` reporting an active operation forever. Roll back the operation and propagate the spawn error instead of continuing with the success response.</violation>
</file>

<file name="crates/mcb-providers/src/task/mod.rs">

<violation number="1" location="crates/mcb-providers/src/task/mod.rs:35">
P1: Calling this provider from a thread without an entered Tokio runtime panics instead of returning the `Err` promised by `TaskRunnerProvider::spawn`. Using `Handle::try_current()` preserves the provider boundary and lets callers handle the spawn failure.</violation>
</file>

<file name="crates/mcb-providers/src/events/tokio.rs">

<violation number="1" location="crates/mcb-providers/src/events/tokio.rs:52">
P1: Invalid `capacity` configuration is not reported: non-numeric values silently select the default, while `0` reaches `broadcast::channel(0)` and panics during provider resolution. Parsing and validating the value through the constructor's `Result` would turn both cases into configuration errors.</violation>
</file>

<file name="config/mcb-validate-internal.toml">

<violation number="1" location="config/mcb-validate-internal.toml:169">
P2: DEP006 can still be bypassed from independently compiled Rust targets, notably the existing `crates/mcb/tests` directory and examples/benches omitted from these scan roots. Consider covering all non-provider crate targets (or scanning each crate root with an explicit exclusion for `mcb-providers`) so the boundary applies consistently.</violation>

<violation number="2" location="config/mcb-validate-internal.toml:170">
P1: DEP006 does not enforce the stated ban on provider imports: it only searches for the exact text `mcb_providers::`. Rust imports such as the existing `crates/mcb-infrastructure/src/provider_linker.rs` line `use mcb_providers as _;` (and any aliased or `extern crate` import) pass this boundary despite the infrastructure scan having no allowed files. Parsing `use`/`extern crate` items, with an explicit composition-root exception if `provider_linker.rs` is intentional, would prevent these bypasses and avoid matching fixture strings as imports.</violation>
</file>

<file name="crates/mcb/src/cli/validate.rs">

<violation number="1" location="crates/mcb/src/cli/validate.rs:153">
P2: Text-mode validation can report success after stdout fails, leaving callers with missing or truncated output; every new text `writeln!` result is discarded while JSON output propagates the error. Consider returning `Result` from the text-printing helpers and propagating each write failure through `execute`.</violation>
</file>

<file name="crates/mcb-providers/src/fs/mod.rs">

<violation number="1" location="crates/mcb-providers/src/fs/mod.rs:36">
P2: Filesystem failures are reported as internal application faults and lose their source chain, preventing callers and diagnostics from recognizing I/O errors. Mapping each failure with `Error::io_with_source(context, e)` would preserve the intended error category and source.</violation>

<violation number="2" location="crates/mcb-providers/src/fs/mod.rs:61">
P2: Codebase discovery and file processing can block Tokio worker threads because every async filesystem method performs synchronous `std::fs` I/O without yielding. Using `tokio::fs` (or `spawn_blocking`) for directory iteration, reads, metadata, and canonicalization would keep indexing from stalling unrelated async work.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/vcs.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/vcs.rs:21">
P2: A construction failure in any registered VCS backend now prevents the entire application VCS service from starting, even if another backend is usable. Consider retaining successfully constructed providers, reporting skipped failures, and failing only when none can be initialized.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/vcs.rs:40">
P2: Branch search now performs a full repository open, root-history walk, and branch enumeration for every file read, which can make large repositories prohibitively slow. Preserve the provider selected by the initial open or add a cheap provider probe/cache instead of using `open_repository` for every delegated call.</violation>

<violation number="3" location="crates/mcb-infrastructure/src/di/vcs.rs:63">
P2: Repositories opened by any provider except the first can receive an ID computed by the wrong backend, risking incorrect request/context attribution. Return the ID embedded in `VcsRepository`, or retain and dispatch to the provider that created it.</violation>

<violation number="4" location="crates/mcb-infrastructure/src/di/vcs.rs:112">
P2: Repository discovery becomes unavailable if any one registered backend fails, even when other backends already found valid repositories. Consider isolating per-provider scan failures and returning an error only when no provider can complete, while retaining successful results.</violation>
</file>

<file name="crates/mcb-infrastructure/src/infrastructure/factory.rs">

<violation number="1" location="crates/mcb-infrastructure/src/infrastructure/factory.rs:83">
P2: When Tokio provider resolution fails, callers receive a successful-looking bus that silently drops every event, so admin event workflows can appear healthy while doing nothing. Preserve the documented default-bus contract by surfacing the configuration/linking failure instead of falling back to `NullEventBusProvider`.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/test_factory.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/test_factory.rs:41">
P2: Test repositories can be created by a non-SQLite provider while still receiving the caller’s SQLite executor because the factory selects the first registry entry. Selecting the `sqlite` entry explicitly (or passing the provider alongside the executor) keeps repository and executor implementations paired.</violation>
</file>

<file name="crates/mcb-providers/src/lib.rs">

<violation number="1" location="crates/mcb-providers/src/lib.rs:92">
P2: Indexing now performs blocking `std::fs` operations inside async provider methods, potentially stalling Tokio workers throughout directory discovery and per-file processing. Consider implementing this provider with `tokio::fs` or isolating synchronous filesystem work with `spawn_blocking`.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/repositories.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/repositories.rs:25">
P2: Standalone repository factories become registration-order-dependent as soon as another database backend is linked, because the first registry entry is treated as the default without any default semantics. Consider accepting a provider/configuration or explicitly preserving the intended SQLite fallback rather than selecting `.first()`.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/repositories.rs:35">
P2: Memory repositories can be created by a different provider instance from the one that opened their executor, since `connect_executor()` resolves the provider again. Passing the already-resolved provider into the connection helper (or calling `provider.connect(...)` directly) keeps connection and repository construction within one provider instance.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/provider_resolvers.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/provider_resolvers.rs:367">
P2: Switching only the provider to NATS passes the default config's empty `nats_url` to the client instead of using the builder's `nats://127.0.0.1:4222` fallback, so startup cannot connect. Treat a blank optional URL as absent or reject it during config validation.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/provider_resolvers.rs:374">
P2: Configured NATS connection timeout and reconnect-attempt values are silently ignored by the new resolver. Users can set `connection_timeout_ms` and `max_reconnect_attempts`, but neither reaches the provider, so runtime behavior remains at library defaults. The registry config and NATS constructor should accept and apply both settings, with this resolver forwarding them.</violation>
</file>

<file name="crates/mcb-infrastructure/src/project/service.rs">

<violation number="1" location="crates/mcb-infrastructure/src/project/service.rs:52">
P3: Project detection now has two orchestration implementations, while `detect_all_projects` becomes an unused public implementation and has already diverged in logging behavior. Consider retaining one shared implementation in a dependency-neutral layer or removing the obsolete provider facade in this migration.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/project/service.rs:58">
P2: Detector construction and execution failures now disappear silently, so `detect_all` returns an incomplete result with no diagnostic identifying the failed detector. Preserve the prior warning paths while centralizing this orchestration.</violation>
</file>

<file name="crates/mcb-domain/src/ports/providers/fs.rs">

<violation number="1" location="crates/mcb-domain/src/ports/providers/fs.rs:12">
P2: Indexing cannot distinguish symlinks through this port, so `follow_symlinks = true` cannot be honored and the local adapter silently drops every symlink. A file-kind enum including symlinks would preserve the needed state and prevent contradictory boolean combinations.</violation>
</file>

<file name="crates/mcb-domain/src/registry/project_detection.rs">

<violation number="1" location="crates/mcb-domain/src/registry/project_detection.rs:2">
P2: This module disables the workspace's `unsafe_code = "deny"` policy for all code added here, even though the module only declares and iterates a `linkme` slice and the other registries do not need this exception. Keeping the deny lint active avoids creating an unreviewed pocket where future unsafe code can compile; this allowance can be removed.</violation>
</file>

<file name="crates/mcb-infrastructure/src/config/types/system.rs">

<violation number="1" location="crates/mcb-infrastructure/src/config/types/system.rs:123">
P2: Fallback-created Tokio event buses now use capacity 1000, while `TokioEventBusProvider::new()` and `config/default.toml` still use 1024. Centralizing this default would preserve one shared value (or update all remaining defaults together) to avoid construction-path-dependent behavior.</violation>
</file>

<file name="crates/mcb-domain/src/ports/infrastructure/database.rs">

<violation number="1" location="crates/mcb-domain/src/ports/infrastructure/database.rs:87">
P2: Once another database backend is registered, a repository can be created by one provider around another provider's executor, producing runtime SQL-dialect failures. Encoding provider/executor affinity—such as having the provider own its connection or returning a provider-specific repository bundle from `connect`—would make invalid composition impossible.</violation>
</file>

<file name="crates/mcb-validate/src/validators/dependency/bypass.rs">

<violation number="1" location="crates/mcb-validate/src/validators/dependency/bypass.rs:45">
P3: DEP006 dispatch can regress back to the `_` fallback without any focused test failing. A bypass-boundary test mirroring the DEP004/DEP005 cases and asserting `DependencyViolation::ProviderBypassImport` would protect this new behavior.</violation>
</file>

<file name="crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs">

<violation number="1" location="crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs:210">
P2: This integration test now requires the external `git` executable even though the resolved provider uses libgit2. On a test runner without the CLI, `Command::new("git").output()?` fails before `open_repository` is exercised. Building the repository fixture through a library/helper (or explicitly detecting and skipping when the CLI is unavailable) would keep this DI test from adding an unrelated environment dependency.</violation>
</file>

<file name="crates/mcb-server/tests/integration/browse_api_integration.rs">

<violation number="1" location="crates/mcb-server/tests/integration/browse_api_integration.rs:394">
P3: These browse tests now duplicate the infrastructure layer's `VectorStoreBrowserAdapter`, including all three forwarding methods and repeated wrapping at every setup. `mcb_infrastructure::infrastructure::factory::create_test_vector_store_for_e2e` already returns the provider/browser pair backed by one store specifically for this workflow; reusing that factory would keep the adapter behavior in one place and avoid the local copy drifting when `VectorStoreBrowser` changes.</violation>
</file>

<file name="crates/mcb-validate/src/validators/macros/violations.rs">

<violation number="1" location="crates/mcb-validate/src/validators/macros/violations.rs:183">
P3: The new blanket `#[allow(missing_docs)]` hides missing documentation across every enum generated by `define_violations!`, not just the new DEP006 variant. The macro already supports variant doc comments, so documenting `ProviderBypassImport` and removing this suppression would preserve the workspace lint for all 46 macro call sites instead of disabling that quality check globally.</violation>
</file>

<file name="crates/mcb-validate/src/validators/dependency/violation.rs">

<violation number="1" location="crates/mcb-validate/src/validators/dependency/violation.rs:97">
P3: DEP006's ID, message, severity, and validator mapping have no behavioral regression test, so this new rule can stop emitting the intended variant unnoticed. Consider adding a dependency-validator test with a forbidden `mcb_providers::` import and asserting `ProviderBypassImport` plus its metadata.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/modules/domain_services.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/modules/domain_services.rs:100">
P3: Public `ServiceDependencies` documentation is weakened by exempting the entire struct from the workspace `missing_docs` lint for two new undocumented fields. Prefer documenting `file_system_provider` and `task_runner_provider` (including the existing `# Fields` list) and leaving the lint active for this API.</violation>
</file>

<file name="crates/mcb-infrastructure/src/lib.rs">

<violation number="1" location="crates/mcb-infrastructure/src/lib.rs:56">
P3: The new public `provider_linker` module bypasses the workspace documentation lint instead of documenting its purpose. A module doc comment would preserve API documentation and keep the lint effective.</violation>
</file>

<file name="crates/mcb-infrastructure/src/di/mod.rs">

<violation number="1" location="crates/mcb-infrastructure/src/di/mod.rs:50">
P3: The public `di::vcs` module remains undocumented, and the module-wide allowance also hides missing documentation on future exports. A short module doc comment would preserve the workspace lint instead of suppressing it.</violation>
</file>

<file name="crates/mcb-domain/src/macros/di.rs">

<violation number="1" location="crates/mcb-domain/src/macros/di.rs:6">
P3: The macro accepts `= ImplementationType` annotations but silently ignores them, so an incorrect or stale implementation type still compiles and misrepresents the DI declaration. Consider removing this unused grammar or using it in generated/validation code.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.
Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

let client_name = config.extra.get("client_name").cloned();

let rt = tokio::runtime::Handle::try_current().map_err(|e| e.to_string())?;
rt.block_on(async move {

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0: Selecting the NATS event bus during normal startup will panic: init_app is async and resolves this provider from inside the Tokio runtime, while this synchronous factory calls Handle::block_on on that same runtime thread. The NATS connection needs an async-aware construction path (for example, an async registry constructor/resolver) rather than nesting block_on; otherwise the advertised NATS provider cannot be initialized through DI.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-providers/src/events/nats.rs, line 65:

<comment>Selecting the NATS event bus during normal startup will panic: `init_app` is async and resolves this provider from inside the Tokio runtime, while this synchronous factory calls `Handle::block_on` on that same runtime thread. The NATS connection needs an async-aware construction path (for example, an async registry constructor/resolver) rather than nesting `block_on`; otherwise the advertised NATS provider cannot be initialized through DI.</comment>

<file context>
@@ -37,12 +37,46 @@ use futures::{StreamExt, stream};
+    let client_name = config.extra.get("client_name").cloned();
+
+    let rt = tokio::runtime::Handle::try_current().map_err(|e| e.to_string())?;
+    rt.block_on(async move {
+        NatsEventBusProvider::with_options(&url, &subject, client_name.as_deref())
+            .await
</file context>
Fix with cubic


let event_bus: Arc<dyn EventBusProvider> = Arc::new(TokioEventBusProvider::new());
let event_bus_resolver = EventBusProviderResolver::new(Arc::clone(&config));
let event_bus: Arc<dyn EventBusProvider> = event_bus_resolver.resolve_from_config()?;

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0: Starting the app with event_bus.provider = "nats" panics during bootstrap. resolve_from_config() invokes the synchronous registry builder, while the NATS builder calls Handle::block_on; this call occurs inside async init_app, where Tokio forbids nested block_on. NATS resolution needs an async construction path (or a provider that connects lazily) so startup can await it and propagate connection errors normally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-infrastructure/src/di/bootstrap.rs, line 308:

<comment>Starting the app with `event_bus.provider = "nats"` panics during bootstrap. `resolve_from_config()` invokes the synchronous registry builder, while the NATS builder calls `Handle::block_on`; this call occurs inside async `init_app`, where Tokio forbids nested `block_on`. NATS resolution needs an async construction path (or a provider that connects lazily) so startup can await it and propagate connection errors normally.</comment>

<file context>
@@ -435,13 +304,19 @@ pub async fn init_app(config: AppConfig) -> Result<AppContext> {
 
-    let event_bus: Arc<dyn EventBusProvider> = Arc::new(TokioEventBusProvider::new());
+    let event_bus_resolver = EventBusProviderResolver::new(Arc::clone(&config));
+    let event_bus: Arc<dyn EventBusProvider> = event_bus_resolver.resolve_from_config()?;
     let shutdown_coordinator: Arc<dyn ShutdownCoordinator> =
         Arc::new(DefaultShutdownCoordinator::new());
</file context>
Fix with cubic

&& self.is_supported_file(entry.path())
{
files.push(entry.path().to_path_buf());
let mut dirs = vec![path.to_path_buf()];

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Git-ignored files and directories are now indexed because the manual DFS applies only SKIP_DIRS, dropping WalkBuilder's ignore-file matching. This can ingest generated/vendor code or intentionally excluded source; preserve ignore matching in the filesystem abstraction or retain an ignore-aware walker.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-application/src/use_cases/indexing_service.rs, line 199:

<comment>Git-ignored files and directories are now indexed because the manual DFS applies only `SKIP_DIRS`, dropping `WalkBuilder`'s ignore-file matching. This can ingest generated/vendor code or intentionally excluded source; preserve ignore matching in the filesystem abstraction or retain an ignore-aware walker.</comment>

<file context>
@@ -185,31 +196,32 @@ impl IndexingServiceImpl {
-                        && self.is_supported_file(entry.path())
-                    {
-                        files.push(entry.path().to_path_buf());
+        let mut dirs = vec![path.to_path_buf()];
+
+        while let Some(dir) = dirs.pop() {
</file context>
Fix with cubic

Comment on lines +35 to +36
tokio::spawn(task);
Ok(())

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Calling this provider from a thread without an entered Tokio runtime panics instead of returning the Err promised by TaskRunnerProvider::spawn. Using Handle::try_current() preserves the provider boundary and lets callers handle the spawn failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-providers/src/task/mod.rs, line 35:

<comment>Calling this provider from a thread without an entered Tokio runtime panics instead of returning the `Err` promised by `TaskRunnerProvider::spawn`. Using `Handle::try_current()` preserves the provider boundary and lets callers handle the spawn failure.</comment>

<file context>
@@ -0,0 +1,38 @@
+
+impl TaskRunnerProvider for TokioTaskRunnerProvider {
+    fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
+        tokio::spawn(task);
+        Ok(())
+    }
</file context>
Suggested change
tokio::spawn(task);
Ok(())
let runtime = tokio::runtime::Handle::try_current().map_err(|error| {
mcb_domain::error::Error::internal(format!(
"Failed to access Tokio runtime: {error}"
))
})?;
drop(runtime.spawn(task));
Ok(())
Fix with cubic

Comment on lines +52 to +56
let capacity = config
.extra
.get("capacity")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(EVENTS_TOKIO_DEFAULT_CAPACITY);

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Invalid capacity configuration is not reported: non-numeric values silently select the default, while 0 reaches broadcast::channel(0) and panics during provider resolution. Parsing and validating the value through the constructor's Result would turn both cases into configuration errors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-providers/src/events/tokio.rs, line 52:

<comment>Invalid `capacity` configuration is not reported: non-numeric values silently select the default, while `0` reaches `broadcast::channel(0)` and panics during provider resolution. Parsing and validating the value through the constructor's `Result` would turn both cases into configuration errors.</comment>

<file context>
@@ -37,12 +37,34 @@ use futures::stream;
+fn create_tokio_event_bus_provider(
+    config: &EventBusProviderConfig,
+) -> std::result::Result<Arc<dyn EventBusProvider>, String> {
+    let capacity = config
+        .extra
+        .get("capacity")
</file context>
Suggested change
let capacity = config
.extra
.get("capacity")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(EVENTS_TOKIO_DEFAULT_CAPACITY);
let capacity = match config.extra.get("capacity") {
Some(value) => {
let capacity = value
.parse::<usize>()
.map_err(|e| format!("Invalid Tokio event bus capacity '{value}': {e}"))?;
if capacity == 0 {
return Err("Tokio event bus capacity must be greater than zero".to_owned());
}
capacity
}
None => EVENTS_TOKIO_DEFAULT_CAPACITY,
};
Fix with cubic

message = "Forbidden provider import outside mcb-providers: {file}:{line} - {context}",
suggestion = "Resolve providers via DI/registry and import only domain ports outside mcb-providers"
)]
ProviderBypassImport {

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: DEP006's ID, message, severity, and validator mapping have no behavioral regression test, so this new rule can stop emitting the intended variant unnoticed. Consider adding a dependency-validator test with a forbidden mcb_providers:: import and asserting ProviderBypassImport plus its metadata.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-validate/src/validators/dependency/violation.rs, line 97:

<comment>DEP006's ID, message, severity, and validator mapping have no behavioral regression test, so this new rule can stop emitting the intended variant unnoticed. Consider adding a dependency-validator test with a forbidden `mcb_providers::` import and asserting `ProviderBypassImport` plus its metadata.</comment>

<file context>
@@ -88,5 +88,17 @@ define_violations! {
+            message = "Forbidden provider import outside mcb-providers: {file}:{line} - {context}",
+            suggestion = "Resolve providers via DI/registry and import only domain ports outside mcb-providers"
+        )]
+        ProviderBypassImport {
+            file: PathBuf,
+            line: usize,
</file context>
Fix with cubic

/// * `agent_repository` - Repository for agent session data
/// * `vcs_provider` - Version control system provider
/// * `project_service` - Service for project detection and management
#[allow(missing_docs)]

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Public ServiceDependencies documentation is weakened by exempting the entire struct from the workspace missing_docs lint for two new undocumented fields. Prefer documenting file_system_provider and task_runner_provider (including the existing # Fields list) and leaving the lint active for this API.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-infrastructure/src/di/modules/domain_services.rs, line 100:

<comment>Public `ServiceDependencies` documentation is weakened by exempting the entire struct from the workspace `missing_docs` lint for two new undocumented fields. Prefer documenting `file_system_provider` and `task_runner_provider` (including the existing `# Fields` list) and leaving the lint active for this API.</comment>

<file context>
@@ -96,6 +97,7 @@ pub struct DomainServicesContainer {
 /// * `agent_repository` - Repository for agent session data
 /// * `vcs_provider` - Version control system provider
 /// * `project_service` - Service for project detection and management
+#[allow(missing_docs)]
 pub struct ServiceDependencies {
     /// Unique identifier for the current project
</file context>
Fix with cubic

Comment on lines +56 to +57
#[allow(missing_docs)]
pub mod provider_linker;

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new public provider_linker module bypasses the workspace documentation lint instead of documenting its purpose. A module doc comment would preserve API documentation and keep the lint effective.

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

<comment>The new public `provider_linker` module bypasses the workspace documentation lint instead of documenting its purpose. A module doc comment would preserve API documentation and keep the lint effective.</comment>

<file context>
@@ -53,6 +53,8 @@ pub mod error_ext;
 pub mod health;
 pub mod logging;
 pub mod project;
+#[allow(missing_docs)]
+pub mod provider_linker;
 pub mod routing;
</file context>
Suggested change
#[allow(missing_docs)]
pub mod provider_linker;
/// Forces the provider crate to be linked so distributed registrations are retained.
pub mod provider_linker;
Fix with cubic

pub mod resolver;
#[cfg(feature = "test-utils")]
pub mod test_factory;
#[allow(missing_docs)]

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The public di::vcs module remains undocumented, and the module-wide allowance also hides missing documentation on future exports. A short module doc comment would preserve the workspace lint instead of suppressing it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-infrastructure/src/di/mod.rs, line 50:

<comment>The public `di::vcs` module remains undocumented, and the module-wide allowance also hides missing documentation on future exports. A short module doc comment would preserve the workspace lint instead of suppressing it.</comment>

<file context>
@@ -45,7 +45,9 @@ pub mod modules;
 pub mod resolver;
+#[cfg(feature = "test-utils")]
 pub mod test_factory;
+#[allow(missing_docs)]
 pub mod vcs;
 
</file context>
Suggested change
#[allow(missing_docs)]
/// VCS provider resolution and dynamic provider dispatch.
Fix with cubic

macro_rules! arc_getters {
() => {};

($name:ident : $ty:ty => $field:ident $(= $impl:ty)? , $($rest:tt)*) => {

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The macro accepts = ImplementationType annotations but silently ignores them, so an incorrect or stale implementation type still compiles and misrepresents the DI declaration. Consider removing this unused grammar or using it in generated/validation code.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/mcb-domain/src/macros/di.rs, line 6:

<comment>The macro accepts `= ImplementationType` annotations but silently ignores them, so an incorrect or stale implementation type still compiles and misrepresents the DI declaration. Consider removing this unused grammar or using it in generated/validation code.</comment>

<file context>
@@ -0,0 +1,41 @@
+macro_rules! arc_getters {
+    () => {};
+
+    ($name:ident : $ty:ty => $field:ident $(= $impl:ty)? , $($rest:tt)*) => {
+        #[doc = concat!("Get `", stringify!($name), "`.")]
+        #[must_use]
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants