Skip to content

Feature/mcb loc reduction - #148

Open
marlon-costa-dc wants to merge 7 commits into
mainfrom
feature/mcb-loc-reduction
Open

Feature/mcb loc reduction#148
marlon-costa-dc wants to merge 7 commits into
mainfrom
feature/mcb-loc-reduction

Conversation

@marlon-costa-dc

Copy link
Copy Markdown
Collaborator

No description provided.

- Add Error::not_found_or() helper to consolidate ok_or_else(|| Error::not_found(...)) pattern
- Replace 18 instances across 5 SQLite repository files with the new helper
- Improves error handling consistency and reduces boilerplate
- All production unwrap/expect remain in test code only (8 instances, acceptable)
- Zero clippy warnings, all tests pass
- 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.
- Create comprehensive sync tests in tests/unit/schema_entity_sync_tests.rs
- Detect schema-only fields: Repository.origin_context, Worktree.{org_id, project_id, origin_context}, Branch.origin_context
- Report full schema structure for manual review
- All 7 tests pass, documents divergence mitigation strategy
- Foundation for derive(TableSchema) migration in Phase 1
…schema

- Introduced `impl_table_schema!` macro for defining table schemas directly in entity files.
- Migrated existing entities (ApiKey, Organization, Team, TeamMember, User) to use the new macro.
- Removed legacy schema modules for these entities, consolidating schema definitions.
- Added SQLite backend integration with SeaORM for simplified CRUD operations.
- Updated repository implementations to utilize SeaORM where applicable.
- Added tests to ensure schema synchronization between entities and database definitions.
- Simplified the `tables` function in `definition.rs` by removing unnecessary line breaks.
- Enhanced readability of column name assertions in `schema_entity_sync_tests.rs` by formatting them into multi-line vectors.
- Cleaned up struct initialization in `backend.rs` for a more concise representation.
- Standardized SQL statement formatting in `org_entity_repository.rs`.
- Improved import formatting in `row_convert.rs` for better clarity.
- Adjusted column type import in `mod.rs` to maintain consistent style.
@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

Review Change Stack

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: 29 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: 11952fe0-0ef5-4a92-87f1-f8375059f0a7

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (63)
  • Cargo.toml
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/Cargo.toml
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/macros/schema.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/task_runner.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-domain/src/schema/api_keys.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-domain/src/schema/organizations.rs
  • crates/mcb-domain/src/schema/team_members.rs
  • crates/mcb-domain/src/schema/teams.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-domain/src/schema/users.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • 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/test_factory.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_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/workspace.rs
  • crates/mcb-providers/Cargo.toml
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.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/task/mod.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb/src/cli/validate.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mcb-loc-reduction

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

Refactor DI providers (FS/Task/VCS/EventBus), SeaORM org repo, error helper

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Adds FileSystemProvider/TaskRunnerProvider ports to decouple indexing from std::fs and tokio.
• Centralizes provider selection via registries/resolvers for event bus, VCS, FS, and task runner.
• Introduces SeaORM-backed organization CRUD via a SqliteBackend, plus row/schema macros to reduce
 boilerplate.
Diagram

graph TD
    APP["IndexingServiceImpl"] --> FSP["FileSystemProvider"] --> LOCALFS["LocalFileSystemProvider"]
    APP --> TRP["TaskRunnerProvider"] --> TOKIOTASK["TokioTaskRunnerProvider"]
    BOOT["DI Bootstrap"] --> RESOLVERS["Provider Resolvers"] --> REGISTRY[("Provider Registries")]
    BOOT --> DBPROV["DatabaseProvider"] --> SQLBACKEND["SqliteBackend"]
    SQLBACKEND --> EXEC[("SQLite Executor")]
    SQLBACKEND --> SEAORM[("SeaORM Connection")]
    subgraph Legend
      direction LR
      _svc([Service]) ~~~ _db[(Database)]
    end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Full SeaORM migration for organizations (no dual path)
  • ➕ Removes per-method branching between SeaORM/raw SQL
  • ➕ Single persistence path to test and maintain
  • ➖ Bigger immediate migration surface
  • ➖ Harder rollback if ORM behavior differs from raw SQL
2. Defer SeaORM until broader/batched migration
  • ➕ Avoids mixed executor/ORM complexity in one repository
  • ➕ Keeps dependency graph/stability during refactor
  • ➖ Delays the boilerplate reduction and type-safety benefits
  • ➖ Larger future migration PR with higher risk
3. Keep incremental dual-backend (as implemented)
  • ➕ Low-risk step: limits ORM usage to org CRUD initially
  • ➕ Provides a migration template for other repositories
  • ➖ Temporary code duplication and extra testing surface
  • ➖ Increases cognitive load until migration completes

Recommendation: The incremental dual-backend approach (executor + optional SeaORM) is a reasonable stepping stone for LOC reduction with bounded blast radius, but it should be treated as transitional. If the intent is long-term SeaORM usage, plan a follow-up to remove the raw-SQL org CRUD path once validated to avoid permanent branching/duplication.

Files changed (64) +2388 / -815

Enhancement (11) +527 / -87
database.rsExtend DatabaseProvider with repository factory methods +46/-0

Extend DatabaseProvider with repository factory methods

• Adds 'create_*_repository' methods to 'DatabaseProvider' to centralize repo construction behind the selected DB provider implementation.

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

fs.rsAdd FileSystemProvider port +23/-0

Add FileSystemProvider port

• Introduces 'FileSystemProvider' (read_to_string, read_dir_entries, canonicalize_path) and a simple 'DirEntry' representation for provider implementations.

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

task.rsAdd TaskRunnerProvider port +12/-0

Add TaskRunnerProvider port

• Introduces a 'TaskRunnerProvider' trait for spawning background tasks without directly depending on Tokio from application code.

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

types.rsIntroduce HasTableSchema trait +22/-0

Introduce HasTableSchema trait

• Adds 'HasTableSchema' so entities can be a schema source of truth during schema assembly.

crates/mcb-domain/src/schema/types.rs

backend.rsIntroduce SqliteBackend bridging executor and SeaORM +64/-0

Introduce SqliteBackend bridging executor and SeaORM

• Adds a DatabaseExecutor implementation that delegates to SqliteExecutor while exposing a SeaORM DatabaseConnection via downcasting for ORM-enabled repositories.

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

org_entity_repository.rsUse SeaORM for organization CRUD when available +98/-84

Use SeaORM for organization CRUD when available

• Adds an optional SeaORM connection and uses it for organization create/get/list/update when constructed via 'new_with_sea'. Switches raw-SQL row conversions to centralized FromRow and uses 'Error::not_found_or' for option-to-error mapping.

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

provider.rsConnect SeaORM + implement DatabaseProvider factories +79/-3

Connect SeaORM + implement DatabaseProvider factories

• Connects a SeaORM DatabaseConnection alongside the existing sqlite pool and implements create_* repository factory methods. Detects SqliteBackend via downcasting to pass SeaORM connection into the org repository.

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

mod.rsAdd SeaORM entities module +55/-0

Add SeaORM entities module

• Introduces the sqlite SeaORM entity module namespace.

crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs

organization.rsAdd SeaORM Organization model +17/-0

Add SeaORM Organization model

• Defines a SeaORM entity for the organizations table using the 'sea_entity!' macro.

crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs

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

Add LocalFileSystemProvider and register it

• Implements FileSystemProvider using std::fs and registers it under the 'local' provider name.

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

mod.rsAdd TokioTaskRunnerProvider and register it +38/-0

Add TokioTaskRunnerProvider and register it

• Implements TaskRunnerProvider by delegating to tokio::spawn and registers it under the 'tokio' provider name.

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

Bug fix (1) +18 / -10
validate.rsUse writeln! for validation output (flush-safe) +18/-10

Use writeln! for validation output (flush-safe)

• Replaces println! calls with writeln!(stdout, ...) to improve output flushing and formatting consistency, and removes the clippy print_stdout allow.

crates/mcb/src/cli/validate.rs

Refactor (31) +864 / -703
indexing_service.rsDecouple indexing from std::fs and tokio::spawn +47/-34

Decouple indexing from std::fs and tokio::spawn

• Injects 'FileSystemProvider' for directory traversal and file reads, and 'TaskRunnerProvider' for spawning background indexing tasks. Removes 'ignore::WalkBuilder' usage in favor of provider-driven traversal.

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

api_key.rsCo-locate ApiKey schema with the entity +24/-0

Co-locate ApiKey schema with the entity

• Defines ApiKey table schema inline using 'impl_table_schema!', replacing the legacy schema module.

crates/mcb-domain/src/entities/api_key.rs

organization.rsCo-locate Organization schema with the entity +14/-0

Co-locate Organization schema with the entity

• Defines Organization table schema inline using 'impl_table_schema!'.

crates/mcb-domain/src/entities/organization.rs

team.rsCo-locate Team/TeamMember schema with the entity +38/-0

Co-locate Team/TeamMember schema with the entity

• Defines Team and TeamMember schemas inline using 'impl_table_schema!'.

crates/mcb-domain/src/entities/team.rs

user.rsCo-locate User schema with the entity +24/-0

Co-locate User schema with the entity

• Defines User table schema inline using 'impl_table_schema!'.

crates/mcb-domain/src/entities/user.rs

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

Add arc_getters! macro for DI containers

• Introduces 'arc_getters!' to generate Arc-cloning getter methods for DI structs, reducing boilerplate in infrastructure AppContext.

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

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

Export new DI macro module

• Registers the new 'di' macros module so it can be used by other crates.

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

schema.rsAdd impl_table_schema! macro +96/-0

Add impl_table_schema! macro

• Adds 'impl_table_schema!' to implement 'HasTableSchema' using compact column/index/fk/unique specs, enabling entity-driven schema definitions.

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

mod.rsRe-export new FS/task ports; drop HTTP port exports +5/-5

Re-export new FS/task ports; drop HTTP port exports

• Adds 'DirEntry', 'FileSystemProvider', and 'TaskRunnerProvider' exports and removes 'HttpClientProvider' exports after deleting the port.

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

http.rsRemove HttpClientProvider port +0/-62

Remove HttpClientProvider port

• Deletes the reqwest-based HTTP client provider port that is no longer used.

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

mod.rsWire new fs and task provider modules +5/-3

Wire new fs and task provider modules

• Adds 'fs' and 'task' provider modules and removes the old 'http' module wiring.

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

api_keys.rsRemove legacy api_keys schema module +0/-40

Remove legacy api_keys schema module

• Deletes the standalone schema module now replaced by entity-driven schema definitions.

crates/mcb-domain/src/schema/api_keys.rs

definition.rsSupport entity-driven schema entries alongside legacy modules +70/-57

Support entity-driven schema entries alongside legacy modules

• Refactors schema assembly to include 'HasTableSchema' entities (Organization/User/Team/TeamMember/ApiKey) while keeping legacy schema modules for other tables.

crates/mcb-domain/src/schema/definition.rs

mod.rsUpdate schema module exports after migration +3/-5

Update schema module exports after migration

• Removes exports for schema modules that were migrated to entity-driven schema definitions.

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

organizations.rsRemove legacy organizations schema module +0/-34

Remove legacy organizations schema module

• Deletes the module superseded by 'impl_table_schema!' in the Organization entity.

crates/mcb-domain/src/schema/organizations.rs

team_members.rsRemove legacy team_members schema module +0/-34

Remove legacy team_members schema module

• Deletes the module superseded by 'impl_table_schema!' in TeamMember.

crates/mcb-domain/src/schema/team_members.rs

teams.rsRemove legacy teams schema module +0/-28

Remove legacy teams schema module

• Deletes the module superseded by 'impl_table_schema!' in Team.

crates/mcb-domain/src/schema/teams.rs

users.rsRemove legacy users schema module +0/-36

Remove legacy users schema module

• Deletes the module superseded by 'impl_table_schema!' in the User entity.

crates/mcb-domain/src/schema/users.rs

system.rsDecouple event bus buffer size from providers crate +4/-5

Decouple event bus buffer size from providers crate

• Uses infrastructure-owned constants for event bus buffer size instead of importing provider constants.

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

bootstrap.rsRefactor bootstrap wiring to use resolvers and arc_getters! +71/-200

Refactor bootstrap wiring to use resolvers and arc_getters!

• Replaces many hand-written AppContext getters with 'arc_getters!', resolves event bus/FS/task runner/VCS providers via new resolvers, and delegates repository instantiation to DatabaseProvider factory methods.

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

database_resolver.rsExpose DatabaseProvider instance for factories +12/-4

Expose DatabaseProvider instance for factories

• Adds the ability to resolve the concrete DatabaseProvider so bootstrap can call create_*_repository methods.

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

mod.rsUpdate DI module exports for new resolvers +4/-2

Update DI module exports for new resolvers

• Wires DI module exports to include the added provider resolver types.

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

domain_services.rsPass FS/task providers into constructed services +19/-5

Pass FS/task providers into constructed services

• Updates service module wiring to include FileSystemProvider and TaskRunnerProvider dependencies.

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

vcs.rsIntegrate default VCS wiring with resolver pattern +124/-16

Integrate default VCS wiring with resolver pattern

• Refactors VCS wiring to fit the new VcsProviderResolver/registry approach.

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

macros.rsAdd SQLite provider macros for FromRow and SeaORM entities +51/-0

Add SQLite provider macros for FromRow and SeaORM entities

• Introduces 'from_row_simple!' to generate FromRow impls and 'sea_entity!' to generate SeaORM entities and test schema metadata.

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

mod.rsWire sqlite backend/macros/sea_entities modules +5/-0

Wire sqlite backend/macros/sea_entities modules

• Registers new sqlite submodules required for the ORM-enabled backend and entity generation.

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

plan_entity_repository.rsReplace local row mappers with centralized FromRow +9/-55

Replace local row mappers with centralized FromRow

• Removes per-repository row_to_* helpers and uses Plan/PlanVersion/PlanReview FromRow implementations from row_convert.

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

row_convert.rsCentralize FromRow implementations for many entities +175/-1

Centralize FromRow implementations for many entities

• Adds FromRow impls for org/user/team/team_member/api_key and plan/vcs/worktree entities, consolidating logic previously duplicated across repositories.

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

vcs_entity_repository.rsReplace local row mappers with centralized FromRow +12/-71

Replace local row mappers with centralized FromRow

• Removes per-repository row_to_* helpers and uses centralized FromRow conversions for Repository/Branch/Worktree/Assignment.

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

lib.rsExport new fs and task modules +6/-0

Export new fs and task modules

• Adds the fs and task modules to the providers crate module tree so implementations are linked and registered.

crates/mcb-providers/src/lib.rs

milvus.rsMinor Milvus provider cleanup +3/-6

Minor Milvus provider cleanup

• Small adjustments/cleanup in Milvus provider wiring or error handling.

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

Other (21) +979 / -15
Cargo.tomlAdd sea-orm workspace dependency +3/-0

Add sea-orm workspace dependency

• Adds 'sea-orm' (SQLite + Tokio runtime features) to support the new SeaORM-backed repository path.

Cargo.toml

Cargo.lockLockfile update for dependency changes +338/-4

Lockfile update for dependency changes

• Updates the lockfile to reflect added SeaORM dependencies and removal of unused ones (e.g., reqwest).

Cargo.lock

Cargo.tomlAdjust domain crate dependencies for new ports/macros +0/-2

Adjust domain crate dependencies for new ports/macros

• Updates dependencies to support new provider ports and schema/DI macros.

crates/mcb-domain/Cargo.toml

event_bus.rsAdd registry for event bus providers +20/-0

Add registry for event bus providers

• Defines EventBusProviderConfig and registers a generic resolver/listing mechanism for EventBusProvider implementations.

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

fs.rsAdd registry for file system providers +20/-0

Add registry for file system providers

• Defines FileSystemProviderConfig and registry plumbing for resolving FileSystemProvider implementations.

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

mod.rsRegister new provider registries +4/-0

Register new provider registries

• Adds registry modules for event_bus, fs, task_runner, and vcs.

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

task_runner.rsAdd registry for task runner providers +20/-0

Add registry for task runner providers

• Defines TaskRunnerProviderConfig and registry plumbing for resolving TaskRunnerProvider implementations.

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

vcs.rsAdd registry for VCS providers +20/-0

Add registry for VCS providers

• Defines VcsProviderConfig and registry plumbing for resolving VcsProvider implementations.

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

mod.rsWire schema/entity sync test module +1/-0

Wire schema/entity sync test module

• Registers the new unit test module for schema/entity synchronization.

crates/mcb-domain/tests/unit/mod.rs

schema_entity_sync_tests.rsAdd tests ensuring schema/entity alignment +233/-0

Add tests ensuring schema/entity alignment

• Adds unit tests validating HasTableSchema-generated definitions match expected column specs for migrated entities.

crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs

provider_resolvers.rsAdd resolvers for EventBus/VCS/FS/TaskRunner +165/-1

Add resolvers for EventBus/VCS/FS/TaskRunner

• Implements resolver structs that translate infrastructure config into domain registry configs and resolve providers dynamically.

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

test_factory.rsUpdate DI test factory for new ports +2/-0

Update DI test factory for new ports

• Adjusts DI test helpers to provide the new FileSystemProvider/TaskRunnerProvider dependencies.

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

dispatch_tests.rsUpdate DI integration tests for new bootstrap wiring +55/-0

Update DI integration tests for new bootstrap wiring

• Updates integration tests to account for the new provider resolution flow and AppContext fields.

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

error_ext_tests.rsAdjust error tests for helper usage +11/-3

Adjust error tests for helper usage

• Updates unit tests impacted by error helper changes and refactors.

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

router_tests.rsMinor routing test updates +1/-2

Minor routing test updates

• Adjusts router unit test expectations after refactors.

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

fs_guards.rsUpdate filesystem test utilities +8/-2

Update filesystem test utilities

• Adjusts filesystem guard utilities to align with provider-based filesystem access.

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

workspace.rsUpdate workspace test utilities +4/-1

Update workspace test utilities

• Minor updates to workspace helper utilities used by tests.

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

Cargo.tomlAdd SeaORM and drop unused reqwest +3/-0

Add SeaORM and drop unused reqwest

• Updates provider crate dependencies to include SeaORM for SQLite ORM usage and remove unused reqwest.

crates/mcb-providers/Cargo.toml

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

Register NATS event bus provider in registry

• Adds a registry entry and factory to build NATS event bus providers from EventBusProviderConfig.

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

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

Register Tokio event bus provider in registry

• Adds a registry entry and factory to build Tokio event bus providers from EventBusProviderConfig (including configurable capacity).

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

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

Register git2 VCS provider in registry

• Adds registry entry wiring so the git2 provider can be resolved via the new VCS provider registry.

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

@qodo-code-review

qodo-code-review Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (6) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 36 rules

Grey Divider


Action required

1. Org update overwrites created_at 🐞 Bug ≡ Correctness ⭐ New
Description
The SeaORM update path in SqliteOrgEntityRepository builds an ActiveModel with `created_at:
Set(org.created_at) and uses it for update_org, allowing created_at` to be overwritten from the
passed Organization value. In the server handler, Organization for update is deserialized from
request payload, so a client can accidentally or maliciously change the persisted creation
timestamp.
Code

crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[R143-147]

+        if let Some(ref db) = self.sea_conn {
+            let am = org_to_model(org);
+            am.update(db)
+                .await
+                .map_err(|e| Error::memory_with_source("SeaORM update organization", e))?;
Evidence
The repository’s SeaORM mapping explicitly sets created_at, and the SeaORM update branch is used
when sea_conn is present. The SQLite provider always creates a backend with SeaORM and constructs
the org repository with new_with_sea, and the server handler passes user-deserialized Organization
into update_org.

crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[43-52]
crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[141-162]
crates/mcb-providers/src/database/sqlite/provider.rs[69-79]
crates/mcb-providers/src/database/sqlite/provider.rs[135-146]
crates/mcb-server/src/handlers/entities/org.rs[69-72]

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

### Issue description
`SqliteOrgEntityRepository::update_org` uses SeaORM with an ActiveModel that sets `created_at` from the input entity. This makes `created_at` writable during updates, diverging from the legacy SQL path (which only updates mutable columns) and enabling persisted timestamp corruption.

### Issue Context
- `org_to_model()` sets `created_at: Set(org.created_at)`.
- `update_org()` uses `am.update(db)` when `sea_conn` is present.
- In production wiring, SQLite connects via `SqliteBackend` and `create_org_entity_repository()` selects `new_with_sea`, so the SeaORM path is the default.
- The server update handler deserializes `Organization` from request data without stripping `created_at`.

### Fix Focus Areas
- crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[43-52]
- crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[141-162]
- crates/mcb-providers/src/database/sqlite/provider.rs[69-79]
- crates/mcb-providers/src/database/sqlite/provider.rs[135-146]
- crates/mcb-server/src/handlers/entities/org.rs[69-72]

### How to fix
- When performing the SeaORM update, do not set `created_at` to `Set(...)`.
 - Option A: build a separate ActiveModel for updates where `created_at` is `NotSet`/`Unchanged` and only mutable fields are `Set`.
 - Option B: fetch the existing row first and preserve the DB value explicitly.
- Add a regression test covering the SeaORM path: create org, then call `update_org` with a different `created_at`, and assert `created_at` in DB is unchanged.

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


2. NATS build blocks runtime 🐞 Bug ☼ Reliability
Description
create_nats_event_bus_provider uses tokio::runtime::Handle::block_on to synchronously await an
async connect during provider construction, which can panic or block progress when called from
within the Tokio runtime. init_app resolves the event bus provider inside async bootstrap, so
selecting the NATS provider can prevent the application from starting cleanly.
Code

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

+    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 factory synchronously blocks on an async connect using Handle::block_on, and the
DI bootstrap resolves the event bus provider during an async init_app flow, creating a
blocking-in-runtime hazard when NATS is selected.

crates/mcb-providers/src/events/nats.rs[49-70]
crates/mcb-infrastructure/src/di/bootstrap.rs[305-307]

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` blocks on async initialization using `Handle::block_on`, but the provider is resolved during async bootstrap (`init_app`). This can panic (Tokio blocking-in-runtime constraint) or stall startup while the runtime thread is blocked.

### Issue Context
The event bus registry build API is synchronous (`build: fn(&Config) -> Result<Arc<dyn ...>, String>`), but NATS initialization (`with_options`) is async.

### Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-70]
- crates/mcb-infrastructure/src/di/bootstrap.rs[305-307]

### Suggested fix approaches (pick one)
1) **Lazy-initialize NATS connection**: return a provider that stores `url/subject/client_name` and uses an async `OnceCell` to connect on first `publish/subscribe`.
2) **Make resolution async**: change the registry/resolver API for event bus providers to support async builders (e.g., returning a `BoxFuture`), then `await` in `init_app`.
3) If you must block, do it safely: wrap in `tokio::task::block_in_place` and document the startup cost (still not ideal).

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


3. #[allow(dead_code)] suppresses dead code 📘 Rule violation ⚙ Maintainability
Description
New #[allow(dead_code)] directives were added in production code, bypassing the project’s
dead-code policy and making it easier for unused items to accumulate unnoticed.
Code

crates/mcb-providers/src/database/sqlite/macros.rs[R30-40]

+        #[allow(dead_code)]
+        #[derive(Clone, Debug, PartialEq, Eq, sea_orm::DeriveEntityModel)]
+        #[sea_orm(table_name = $table)]
+        pub struct Model {
+            #[sea_orm(primary_key)]
+            pub $first: $first_ty,
+            $( pub $f: $ty ),*
+        }
+
+        #[allow(dead_code)]
+        #[derive(Copy, Clone, Debug, sea_orm::EnumIter, sea_orm::DeriveRelation)]
Evidence
PR Compliance ID 1208459 disallows adding dead-code suppression and requires removing/using unused
items instead. The diff adds #[allow(dead_code)] in the SQLite provider macros and SeaORM entity
module.

Rule 1208459: No dead code (unused functions, structs, constants)
crates/mcb-providers/src/database/sqlite/macros.rs[30-41]
crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs[6-7]

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

## Issue description
Production code introduces `#[allow(dead_code)]`, which violates the repo policy against dead code and lint-bypassing.

## Issue Context
These suppressions appear to have been added to silence warnings for generated SeaORM entity items/modules.

## Fix Focus Areas
- crates/mcb-providers/src/database/sqlite/macros.rs[30-41]
- crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs[6-7]

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


View more (1)
4. validate.rs ignores stdout errors 📘 Rule violation ☼ Reliability
Description
The CLI validation output uses let _ = writeln!(...), silently discarding I/O errors instead of
propagating or reporting them.
Code

crates/mcb/src/cli/validate.rs[153]

+            let _ = writeln!(std::io::stdout());
Evidence
PR Compliance ID 1208751 forbids silently swallowing errors. The cited line discards the Result
from writing to stdout, hiding failures.

Rule 1208751: Errors must not be silently swallowed or hidden
crates/mcb/src/cli/validate.rs[152-155]
crates/mcb/src/cli/validate.rs[170-178]

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

## Issue description
`writeln!` results are ignored via `let _ = ...`, which can hide output failures (broken pipe, closed stdout, etc.).

## Issue Context
This code is part of the validate CLI output path; errors should be propagated (`?`) or explicitly handled/logged.

## 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



Remediation recommended

5. NATS config defaults to localhost 📘 Rule violation ☼ Reliability ⭐ New
Description
The NATS provider silently falls back to hardcoded defaults (nats://127.0.0.1:4222 and a default
subject) when configuration is missing, instead of failing fast. This can mask misconfiguration and
accidentally connect to unintended infrastructure.
Code

crates/mcb-providers/src/events/nats.rs[R52-61]

+    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());
Evidence
PR Compliance ID 1206783 requires failing fast when required external configuration is missing and
forbids substituting hardcoded defaults. The NATS provider builder uses unwrap_or_else to default
url and subject when missing from config.

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[52-61]

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 NATS event bus provider uses hardcoded defaults when `url`/`subject` are missing, rather than failing fast.

## Issue Context
When a provider is selected, required connection details should be explicitly configured; hardcoded fallbacks can hide deployment mistakes.

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

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


6. Blocking sync FS calls 🐞 Bug ➹ Performance ⭐ New
Description
LocalFileSystemProvider implements async FileSystemProvider methods using synchronous std::fs
operations, which can block Tokio worker threads during directory traversal and file reads. This can
significantly slow indexing and degrade runtime responsiveness under load or on slow filesystems.
Code

crates/mcb-providers/src/fs/mod.rs[R60-71]

+    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 provider’s async methods call std::fs directly, and the indexing service awaits those methods
inside the recursive walk and per-file processing path, meaning this synchronous I/O will execute on
async runtime threads.

crates/mcb-providers/src/fs/mod.rs[58-73]
crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
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 calls (`std::fs::*`) directly inside them. When awaited from Tokio tasks (e.g., indexing), these calls block the runtime worker threads.

### Issue Context
This provider is used by `IndexingServiceImpl` for recursive discovery (`read_dir_entries`) and per-file reads (`read_to_string`), so the blocking behavior can be amplified across many files.

### Fix Focus Areas
- crates/mcb-providers/src/fs/mod.rs[58-73]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
- crates/mcb-application/src/use_cases/indexing_service.rs[427-444]

### How to fix
- Replace synchronous calls with `tokio::fs` equivalents where available (`tokio::fs::read_to_string`, `tokio::fs::read_dir`, `tokio::fs::canonicalize`).
- For operations that don’t have good async equivalents (or if you want to keep std::fs), wrap them in `tokio::task::spawn_blocking` and await the result.
- Ensure directory iteration + `file_type()` is also done in the blocking section (or with Tokio’s async directory entries) to avoid partial blocking.

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


7. Spawn panics without runtime 🐞 Bug ☼ Reliability ⭐ New
Description
TokioTaskRunnerProvider::spawn calls tokio::spawn and always returns Ok(()), so if called on a
thread without an entered Tokio runtime it will panic instead of returning an error. Callers (e.g.,
IndexingServiceImpl) attempt to handle Err from spawn, but this implementation cannot report that
failure mode.
Code

crates/mcb-providers/src/task/mod.rs[R34-36]

+    fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
+        tokio::spawn(task);
+        Ok(())
Evidence
The TaskRunnerProvider interface is explicitly fallible, and the indexing service checks for an Err
return; however the Tokio implementation unconditionally calls tokio::spawn (panic on missing
runtime) and always returns Ok.

crates/mcb-domain/src/ports/providers/task.rs[5-12]
crates/mcb-providers/src/task/mod.rs[33-38]
crates/mcb-application/src/use_cases/indexing_service.rs[299-307]

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

### Issue description
`TokioTaskRunnerProvider::spawn` can panic when no Tokio runtime is active on the calling thread, but the port contract is fallible (`Result<()>`). This makes error handling ineffective and can crash the process in alternate composition roots/tests/future call sites.

### Issue Context
The port explicitly documents returning an error if the task cannot be spawned, and `IndexingServiceImpl` checks for `Err` from `spawn()`.

### Fix Focus Areas
- crates/mcb-providers/src/task/mod.rs[33-38]
- crates/mcb-domain/src/ports/providers/task.rs[5-11]
- crates/mcb-application/src/use_cases/indexing_service.rs[299-307]

### How to fix
- Use `tokio::runtime::Handle::try_current()` inside `spawn()`.
 - If it fails, return a domain `Error` (or map to the existing `Result` type) instead of panicking.
 - If it succeeds, call `handle.spawn(task)`.
- (Optional) Consider capturing/logging the JoinHandle if you want to observe panics in the background task.

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


View more (5)
8. Old VCS factory left 📘 Rule violation ⚙ Maintainability
Description
A new registry-based VCS provider selection mechanism was added, but the older mcb-providers
default_vcs_provider() factory remains present as a parallel implementation, increasing the risk
of divergence and unused legacy paths.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R126-127]

+pub fn default_vcs_provider() -> Result<Arc<dyn VcsProvider>> {
+    Ok(Arc::new(DynamicVcsProvider::from_registry()?))
Evidence
PR Compliance ID 1206727 requires removing superseded implementations when adding their replacement.
The new infrastructure default_vcs_provider() is now registry-based, while the older
mcb-providers factory implementation still exists as a separate code path.

Rule 1206727: Remove superseded code in the same change that adds its replacement
crates/mcb-infrastructure/src/di/vcs.rs[17-29]
crates/mcb-providers/src/vcs/factory.rs[10-14]

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 PR introduces a new registry-driven VCS provider mechanism in infrastructure DI, but the older provider factory in `mcb-providers` remains, leaving two competing ways to construct the default VCS provider.

## Issue Context
This violates the expectation that superseded implementations are removed in the same change that introduces their replacement (unless both paths remain intentionally reachable and documented).

## Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[17-31]
- crates/mcb-providers/src/vcs/factory.rs[10-14]
- crates/mcb-providers/src/vcs/mod.rs[1-40]

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


9. VCS repo reopening overhead 🐞 Bug ➹ Performance
Description
DynamicVcsProvider calls open_repository again for each VCS operation via
provider_and_repo_for_path(repo.path()), re-discovering the provider and re-opening the repo
repeatedly. This adds avoidable per-call overhead and scales poorly when multiple VCS providers are
registered.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R69-72]

+    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
+    }
Evidence
DynamicVcsProvider resolves a concrete provider by calling open_repository in
provider_and_repo_for_path and repeats that resolution for operations like list_branches. The
git2 implementation of open_repository performs multiple repository metadata reads (root commit
hash, default branch, branch list), which will be repeated under this design.

crates/mcb-infrastructure/src/di/vcs.rs[33-92]
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` reopens repositories on every operation (`list_branches`, `commit_history`, `list_files`, `read_file`, `diff_refs`) by re-running provider discovery + `open_repository`. For git2 this recomputes root hash / branches / default branch and reopens libgit2 state each time.

### Issue Context
The wrapper currently has no stable mapping from `VcsRepository` to the concrete provider that opened it.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[33-106]
- crates/mcb-providers/src/vcs/git2_provider.rs[119-135]

### Suggested fix approaches
- **Add provider identity to `VcsRepository`** (e.g., `provider_name: String`) and dispatch directly without reopening.
- **Cache resolution** inside `DynamicVcsProvider` keyed by canonicalized path (e.g., `RwLock<HashMap<PathBuf, Arc<dyn VcsProvider>>>`), so subsequent calls avoid probing and avoid calling `open_repository`.
- If reopening is unavoidable, at least avoid probing multiple providers by caching the first successful provider per path.

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


10. missing_docs allow lacks justification 📘 Rule violation ⚙ Maintainability
Description
Static-analysis suppression directives for missing_docs were added without an inline justification
comment, reducing auditability of why the lint is being suppressed.
Code

crates/mcb-domain/src/registry/vcs.rs[1]

+#![allow(missing_docs)]
Evidence
PR Compliance ID 1206895 requires every suppression directive (including Rust #[allow(...)]) to
have an inline justification. The cited lines add missing_docs suppressions without any attached
justification comment.

Rule 1206895: Require inline justification comments for all static-analysis suppression directives
crates/mcb-domain/src/registry/vcs.rs[1-1]
crates/mcb-infrastructure/src/di/vcs.rs[12-15]
crates/mcb-infrastructure/src/di/mod.rs[49-50]

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(missing_docs)]` / `#[allow(missing_docs)]` suppressions were added without a specific justification comment on the same line (or immediately next line).

## Issue Context
The compliance rule requires each suppression to carry a concrete reason (e.g., "generated registry boilerplate; public API documented in X").

## Fix Focus Areas
- crates/mcb-domain/src/registry/vcs.rs[1-1]
- crates/mcb-infrastructure/src/di/vcs.rs[12-15]
- crates/mcb-infrastructure/src/di/mod.rs[49-50]

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


11. Indexing loses ignore filtering 🐞 Bug ➹ Performance
Description
discover_files now performs a manual directory traversal and only skips directories listed in
SKIP_DIRS, which removes ignore-aware walking behavior from the previous approach. This can cause
indexing to traverse and embed many additional files (generated/vendor/etc.), increasing runtime and
index noise.
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 new discover_files implementation explicitly traverses directories via read_dir_entries and
only applies the SKIP_DIRS name check; this is a narrower filter than an ignore-aware walker and
will include any other directories/files not covered by SKIP_DIRS.

crates/mcb-application/src/use_cases/indexing_service.rs[192-226]
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` now walks the filesystem via `read_dir_entries` and filters only by `SKIP_DIRS` + extension. Any previous ignore-aware behavior (e.g., ignore files / repo ignore rules) is no longer applied during traversal.

### Issue Context
`SKIP_DIRS` is a fixed list and cannot cover repo-specific ignore needs.

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

### Suggested fix approaches
- Reintroduce an ignore-aware walker (e.g., `ignore` crate) for traversal, while still using `FileSystemProvider` for file reads.
- Alternatively, extend `FileSystemProvider` with an ignore-aware walking API (provider-specific implementation can use `ignore::WalkBuilder` for local FS).
- Ensure traversal continues to honor `SKIP_DIRS` in addition to ignore rules.

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


12. git2_provider.rs import order broken 📘 Rule violation ⚙ Maintainability
Description
A std import (use std::sync::Arc;) is placed after non-std imports, violating the required
grouped/ordered Rust import convention.
Code

crates/mcb-providers/src/vcs/git2_provider.rs[20]

+use std::sync::Arc;
Evidence
PR Compliance ID 1207880 mandates import grouping with std first. The file has `use
std::sync::Arc; after mcb_domain` imports, breaking the required ordering.

Rule 1207880: Enforce grouped and ordered Rust imports: std, external crates, mcb_*, then local modules
crates/mcb-providers/src/vcs/git2_provider.rs[6-21]

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

## Issue description
Rust `use` statements are not grouped/ordered as required: `std` imports must come before external crates, then `mcb_*`, then local modules.

## Issue Context
`use std::sync::Arc;` appears after `mcb_domain` imports.

## Fix Focus Areas
- crates/mcb-providers/src/vcs/git2_provider.rs[6-21]

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


Grey Divider

Previous review results

Review updated until commit 248006a ⚖️ Balanced

Results up to commit 248006a ⚖️ Balanced


🐞 Bugs (3) 📘 Rule violations (5) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. #[allow(dead_code)] suppresses dead code 📘 Rule violation ⚙ Maintainability
Description
New #[allow(dead_code)] directives were added in production code, bypassing the project’s
dead-code policy and making it easier for unused items to accumulate unnoticed.
Code

crates/mcb-providers/src/database/sqlite/macros.rs[R30-40]

+        #[allow(dead_code)]
+        #[derive(Clone, Debug, PartialEq, Eq, sea_orm::DeriveEntityModel)]
+        #[sea_orm(table_name = $table)]
+        pub struct Model {
+            #[sea_orm(primary_key)]
+            pub $first: $first_ty,
+            $( pub $f: $ty ),*
+        }
+
+        #[allow(dead_code)]
+        #[derive(Copy, Clone, Debug, sea_orm::EnumIter, sea_orm::DeriveRelation)]
Evidence
PR Compliance ID 1208459 disallows adding dead-code suppression and requires removing/using unused
items instead. The diff adds #[allow(dead_code)] in the SQLite provider macros and SeaORM entity
module.

Rule 1208459: No dead code (unused functions, structs, constants)
crates/mcb-providers/src/database/sqlite/macros.rs[30-41]
crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs[6-7]

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

## Issue description
Production code introduces `#[allow(dead_code)]`, which violates the repo policy against dead code and lint-bypassing.

## Issue Context
These suppressions appear to have been added to silence warnings for generated SeaORM entity items/modules.

## Fix Focus Areas
- crates/mcb-providers/src/database/sqlite/macros.rs[30-41]
- crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs[6-7]

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


2. validate.rs ignores stdout errors 📘 Rule violation ☼ Reliability
Description
The CLI validation output uses let _ = writeln!(...), silently discarding I/O errors instead of
propagating or reporting them.
Code

crates/mcb/src/cli/validate.rs[153]

+            let _ = writeln!(std::io::stdout());
Evidence
PR Compliance ID 1208751 forbids silently swallowing errors. The cited line discards the Result
from writing to stdout, hiding failures.

Rule 1208751: Errors must not be silently swallowed or hidden
crates/mcb/src/cli/validate.rs[152-155]
crates/mcb/src/cli/validate.rs[170-178]

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

## Issue description
`writeln!` results are ignored via `let _ = ...`, which can hide output failures (broken pipe, closed stdout, etc.).

## Issue Context
This code is part of the validate CLI output path; errors should be propagated (`?`) or explicitly handled/logged.

## 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


3. NATS build blocks runtime 🐞 Bug ☼ Reliability
Description
create_nats_event_bus_provider uses tokio::runtime::Handle::block_on to synchronously await an
async connect during provider construction, which can panic or block progress when called from
within the Tokio runtime. init_app resolves the event bus provider inside async bootstrap, so
selecting the NATS provider can prevent the application from starting cleanly.
Code

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

+    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 factory synchronously blocks on an async connect using Handle::block_on, and the
DI bootstrap resolves the event bus provider during an async init_app flow, creating a
blocking-in-runtime hazard when NATS is selected.

crates/mcb-providers/src/events/nats.rs[49-70]
crates/mcb-infrastructure/src/di/bootstrap.rs[305-307]

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` blocks on async initialization using `Handle::block_on`, but the provider is resolved during async bootstrap (`init_app`). This can panic (Tokio blocking-in-runtime constraint) or stall startup while the runtime thread is blocked.

### Issue Context
The event bus registry build API is synchronous (`build: fn(&Config) -> Result<Arc<dyn ...>, String>`), but NATS initialization (`with_options`) is async.

### Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-70]
- crates/mcb-infrastructure/src/di/bootstrap.rs[305-307]

### Suggested fix approaches (pick one)
1) **Lazy-initialize NATS connection**: return a provider that stores `url/subject/client_name` and uses an async `OnceCell` to connect on first `publish/subscribe`.
2) **Make resolution async**: change the registry/resolver API for event bus providers to support async builders (e.g., returning a `BoxFuture`), then `await` in `init_app`.
3) If you must block, do it safely: wrap in `tokio::task::block_in_place` and document the startup cost (still not ideal).

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



Remediation recommended
4. missing_docs allow lacks justification 📘 Rule violation ⚙ Maintainability
Description
Static-analysis suppression directives for missing_docs were added without an inline justification
comment, reducing auditability of why the lint is being suppressed.
Code

crates/mcb-domain/src/registry/vcs.rs[1]

+#![allow(missing_docs)]
Evidence
PR Compliance ID 1206895 requires every suppression directive (including Rust #[allow(...)]) to
have an inline justification. The cited lines add missing_docs suppressions without any attached
justification comment.

Rule 1206895: Require inline justification comments for all static-analysis suppression directives
crates/mcb-domain/src/registry/vcs.rs[1-1]
crates/mcb-infrastructure/src/di/vcs.rs[12-15]
crates/mcb-infrastructure/src/di/mod.rs[49-50]

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(missing_docs)]` / `#[allow(missing_docs)]` suppressions were added without a specific justification comment on the same line (or immediately next line).

## Issue Context
The compliance rule requires each suppression to carry a concrete reason (e.g., "generated registry boilerplate; public API documented in X").

## Fix Focus Areas
- crates/mcb-domain/src/registry/vcs.rs[1-1]
- crates/mcb-infrastructure/src/di/vcs.rs[12-15]
- crates/mcb-infrastructure/src/di/mod.rs[49-50]

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


5. git2_provider.rs import order broken 📘 Rule violation ⚙ Maintainability
Description
A std import (use std::sync::Arc;) is placed after non-std imports, violating the required
grouped/ordered Rust import convention.
Code

crates/mcb-providers/src/vcs/git2_provider.rs[20]

+use std::sync::Arc;
Evidence
PR Compliance ID 1207880 mandates import grouping with std first. The file has `use
std::sync::Arc; after mcb_domain` imports, breaking the required ordering.

Rule 1207880: Enforce grouped and ordered Rust imports: std, external crates, mcb_*, then local modules
crates/mcb-providers/src/vcs/git2_provider.rs[6-21]

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

## Issue description
Rust `use` statements are not grouped/ordered as required: `std` imports must come before external crates, then `mcb_*`, then local modules.

## Issue Context
`use std::sync::Arc;` appears after `mcb_domain` imports.

## Fix Focus Areas
- crates/mcb-providers/src/vcs/git2_provider.rs[6-21]

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


6. Old VCS factory left 📘 Rule violation ⚙ Maintainability
Description
A new registry-based VCS provider selection mechanism was added, but the older mcb-providers
default_vcs_provider() factory remains present as a parallel implementation, increasing the risk
of divergence and unused legacy paths.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R126-127]

+pub fn default_vcs_provider() -> Result<Arc<dyn VcsProvider>> {
+    Ok(Arc::new(DynamicVcsProvider::from_registry()?))
Evidence
PR Compliance ID 1206727 requires removing superseded implementations when adding their replacement.
The new infrastructure default_vcs_provider() is now registry-based, while the older
mcb-providers factory implementation still exists as a separate code path.

Rule 1206727: Remove superseded code in the same change that adds its replacement
crates/mcb-infrastructure/src/di/vcs.rs[17-29]
crates/mcb-providers/src/vcs/factory.rs[10-14]

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 PR introduces a new registry-driven VCS provider mechanism in infrastructure DI, but the older provider factory in `mcb-providers` remains, leaving two competing ways to construct the default VCS provider.

## Issue Context
This violates the expectation that superseded implementations are removed in the same change that introduces their replacement (unless both paths remain intentionally reachable and documented).

## Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[17-31]
- crates/mcb-providers/src/vcs/factory.rs[10-14]
- crates/mcb-providers/src/vcs/mod.rs[1-40]

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


View more (2)
7. VCS repo reopening overhead 🐞 Bug ➹ Performance
Description
DynamicVcsProvider calls open_repository again for each VCS operation via
provider_and_repo_for_path(repo.path()), re-discovering the provider and re-opening the repo
repeatedly. This adds avoidable per-call overhead and scales poorly when multiple VCS providers are
registered.
Code

crates/mcb-infrastructure/src/di/vcs.rs[R69-72]

+    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
+    }
Evidence
DynamicVcsProvider resolves a concrete provider by calling open_repository in
provider_and_repo_for_path and repeats that resolution for operations like list_branches. The
git2 implementation of open_repository performs multiple repository metadata reads (root commit
hash, default branch, branch list), which will be repeated under this design.

crates/mcb-infrastructure/src/di/vcs.rs[33-92]
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` reopens repositories on every operation (`list_branches`, `commit_history`, `list_files`, `read_file`, `diff_refs`) by re-running provider discovery + `open_repository`. For git2 this recomputes root hash / branches / default branch and reopens libgit2 state each time.

### Issue Context
The wrapper currently has no stable mapping from `VcsRepository` to the concrete provider that opened it.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[33-106]
- crates/mcb-providers/src/vcs/git2_provider.rs[119-135]

### Suggested fix approaches
- **Add provider identity to `VcsRepository`** (e.g., `provider_name: String`) and dispatch directly without reopening.
- **Cache resolution** inside `DynamicVcsProvider` keyed by canonicalized path (e.g., `RwLock<HashMap<PathBuf, Arc<dyn VcsProvider>>>`), so subsequent calls avoid probing and avoid calling `open_repository`.
- If reopening is unavoidable, at least avoid probing multiple providers by caching the first successful provider per path.

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


8. Indexing loses ignore filtering 🐞 Bug ➹ Performance
Description
discover_files now performs a manual directory traversal and only skips directories listed in
SKIP_DIRS, which removes ignore-aware walking behavior from the previous approach. This can cause
indexing to traverse and embed many additional files (generated/vendor/etc.), increasing runtime and
index noise.
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 new discover_files implementation explicitly traverses directories via read_dir_entries and
only applies the SKIP_DIRS name check; this is a narrower filter than an ignore-aware walker and
will include any other directories/files not covered by SKIP_DIRS.

crates/mcb-application/src/use_cases/indexing_service.rs[192-226]
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` now walks the filesystem via `read_dir_entries` and filters only by `SKIP_DIRS` + extension. Any previous ignore-aware behavior (e.g., ignore files / repo ignore rules) is no longer applied during traversal.

### Issue Context
`SKIP_DIRS` is a fixed list and cannot cover repo-specific ignore needs.

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

### Suggested fix approaches
- Reintroduce an ignore-aware walker (e.g., `ignore` crate) for traversal, while still using `FileSystemProvider` for file reads.
- Alternatively, extend `FileSystemProvider` with an ignore-aware walking API (provider-specific implementation can use `ignore::WalkBuilder` for local FS).
- Ensure traversal continues to honor `SKIP_DIRS` in addition to ignore rules.

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


Qodo Logo

Comment on lines +30 to +40
#[allow(dead_code)]
#[derive(Clone, Debug, PartialEq, Eq, sea_orm::DeriveEntityModel)]
#[sea_orm(table_name = $table)]
pub struct Model {
#[sea_orm(primary_key)]
pub $first: $first_ty,
$( pub $f: $ty ),*
}

#[allow(dead_code)]
#[derive(Copy, Clone, Debug, sea_orm::EnumIter, sea_orm::DeriveRelation)]

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(dead_code)] suppresses dead code 📘 Rule violation ⚙ Maintainability

New #[allow(dead_code)] directives were added in production code, bypassing the project’s
dead-code policy and making it easier for unused items to accumulate unnoticed.
Agent Prompt
## Issue description
Production code introduces `#[allow(dead_code)]`, which violates the repo policy against dead code and lint-bypassing.

## Issue Context
These suppressions appear to have been added to silence warnings for generated SeaORM entity items/modules.

## Fix Focus Areas
- crates/mcb-providers/src/database/sqlite/macros.rs[30-41]
- crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs[6-7]

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

@@ -0,0 +1,20 @@
#![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. missing_docs allow lacks justification 📘 Rule violation ⚙ Maintainability

Static-analysis suppression directives for missing_docs were added without an inline justification
comment, reducing auditability of why the lint is being suppressed.
Agent Prompt
## Issue description
`#![allow(missing_docs)]` / `#[allow(missing_docs)]` suppressions were added without a specific justification comment on the same line (or immediately next line).

## Issue Context
The compliance rule requires each suppression to carry a concrete reason (e.g., "generated registry boilerplate; public API documented in X").

## Fix Focus Areas
- crates/mcb-domain/src/registry/vcs.rs[1-1]
- crates/mcb-infrastructure/src/di/vcs.rs[12-15]
- crates/mcb-infrastructure/src/di/mod.rs[49-50]

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

error::{Error, Result},
ports::VcsProvider,
};
use std::sync::Arc;

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

3. git2_provider.rs import order broken 📘 Rule violation ⚙ Maintainability

A std import (use std::sync::Arc;) is placed after non-std imports, violating the required
grouped/ordered Rust import convention.
Agent Prompt
## Issue description
Rust `use` statements are not grouped/ordered as required: `std` imports must come before external crates, then `mcb_*`, then local modules.

## Issue Context
`use std::sync::Arc;` appears after `mcb_domain` imports.

## Fix Focus Areas
- crates/mcb-providers/src/vcs/git2_provider.rs[6-21]

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


if has_violations {
println!();
let _ = writeln!(std::io::stdout());

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. validate.rs ignores stdout errors 📘 Rule violation ☼ Reliability

The CLI validation output uses let _ = writeln!(...), silently discarding I/O errors instead of
propagating or reporting them.
Agent Prompt
## Issue description
`writeln!` results are ignored via `let _ = ...`, which can hide output failures (broken pipe, closed stdout, etc.).

## Issue Context
This code is part of the validate CLI output path; errors should be propagated (`?`) or explicitly handled/logged.

## 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 +126 to +127
pub fn default_vcs_provider() -> Result<Arc<dyn VcsProvider>> {
Ok(Arc::new(DynamicVcsProvider::from_registry()?))

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. Old vcs factory left 📘 Rule violation ⚙ Maintainability

A new registry-based VCS provider selection mechanism was added, but the older mcb-providers
default_vcs_provider() factory remains present as a parallel implementation, increasing the risk
of divergence and unused legacy paths.
Agent Prompt
## Issue description
The PR introduces a new registry-driven VCS provider mechanism in infrastructure DI, but the older provider factory in `mcb-providers` remains, leaving two competing ways to construct the default VCS provider.

## Issue Context
This violates the expectation that superseded implementations are removed in the same change that introduces their replacement (unless both paths remain intentionally reachable and documented).

## Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[17-31]
- crates/mcb-providers/src/vcs/factory.rs[10-14]
- crates/mcb-providers/src/vcs/mod.rs[1-40]

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

Comment on lines +64 to +70
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

6. Nats build blocks runtime 🐞 Bug ☼ Reliability

create_nats_event_bus_provider uses tokio::runtime::Handle::block_on to synchronously await an
async connect during provider construction, which can panic or block progress when called from
within the Tokio runtime. init_app resolves the event bus provider inside async bootstrap, so
selecting the NATS provider can prevent the application from starting cleanly.
Agent Prompt
### Issue description
`create_nats_event_bus_provider` blocks on async initialization using `Handle::block_on`, but the provider is resolved during async bootstrap (`init_app`). This can panic (Tokio blocking-in-runtime constraint) or stall startup while the runtime thread is blocked.

### Issue Context
The event bus registry build API is synchronous (`build: fn(&Config) -> Result<Arc<dyn ...>, String>`), but NATS initialization (`with_options`) is async.

### Fix Focus Areas
- crates/mcb-providers/src/events/nats.rs[49-70]
- crates/mcb-infrastructure/src/di/bootstrap.rs[305-307]

### Suggested fix approaches (pick one)
1) **Lazy-initialize NATS connection**: return a provider that stores `url/subject/client_name` and uses an async `OnceCell` to connect on first `publish/subscribe`.
2) **Make resolution async**: change the registry/resolver API for event bus providers to support async builders (e.g., returning a `BoxFuture`), then `await` in `init_app`.
3) If you must block, do it safely: wrap in `tokio::task::block_in_place` and document the startup cost (still not ideal).

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

Comment on lines +69 to +72
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
}

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. Vcs repo reopening overhead 🐞 Bug ➹ Performance

DynamicVcsProvider calls open_repository again for each VCS operation via
provider_and_repo_for_path(repo.path()), re-discovering the provider and re-opening the repo
repeatedly. This adds avoidable per-call overhead and scales poorly when multiple VCS providers are
registered.
Agent Prompt
### Issue description
`DynamicVcsProvider` reopens repositories on every operation (`list_branches`, `commit_history`, `list_files`, `read_file`, `diff_refs`) by re-running provider discovery + `open_repository`. For git2 this recomputes root hash / branches / default branch and reopens libgit2 state each time.

### Issue Context
The wrapper currently has no stable mapping from `VcsRepository` to the concrete provider that opened it.

### Fix Focus Areas
- crates/mcb-infrastructure/src/di/vcs.rs[33-106]
- crates/mcb-providers/src/vcs/git2_provider.rs[119-135]

### Suggested fix approaches
- **Add provider identity to `VcsRepository`** (e.g., `provider_name: String`) and dispatch directly without reopening.
- **Cache resolution** inside `DynamicVcsProvider` keyed by canonicalized path (e.g., `RwLock<HashMap<PathBuf, Arc<dyn VcsProvider>>>`), so subsequent calls avoid probing and avoid calling `open_repository`.
- If reopening is unavoidable, at least avoid probing multiple providers by caching the first successful provider per path.

ⓘ 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.

Remediation recommended

8. Indexing loses ignore filtering 🐞 Bug ➹ Performance

discover_files now performs a manual directory traversal and only skips directories listed in
SKIP_DIRS, which removes ignore-aware walking behavior from the previous approach. This can cause
indexing to traverse and embed many additional files (generated/vendor/etc.), increasing runtime and
index noise.
Agent Prompt
### Issue description
`IndexingServiceImpl::discover_files` now walks the filesystem via `read_dir_entries` and filters only by `SKIP_DIRS` + extension. Any previous ignore-aware behavior (e.g., ignore files / repo ignore rules) is no longer applied during traversal.

### Issue Context
`SKIP_DIRS` is a fixed list and cannot cover repo-specific ignore needs.

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

### Suggested fix approaches
- Reintroduce an ignore-aware walker (e.g., `ignore` crate) for traversal, while still using `FileSystemProvider` for file reads.
- Alternatively, extend `FileSystemProvider` with an ignore-aware walking API (provider-specific implementation can use `ignore::WalkBuilder` for local FS).
- Ensure traversal continues to honor `SKIP_DIRS` in addition to ignore rules.

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

Comment on lines +52 to +61
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());

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. Nats config defaults to localhost 📘 Rule violation ☼ Reliability

The NATS provider silently falls back to hardcoded defaults (nats://127.0.0.1:4222 and a default
subject) when configuration is missing, instead of failing fast. This can mask misconfiguration and
accidentally connect to unintended infrastructure.
Agent Prompt
## Issue description
The NATS event bus provider uses hardcoded defaults when `url`/`subject` are missing, rather than failing fast.

## Issue Context
When a provider is selected, required connection details should be explicitly configured; hardcoded fallbacks can hide deployment mistakes.

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

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

Comment on lines +60 to +71
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

2. Blocking sync fs calls 🐞 Bug ➹ Performance

LocalFileSystemProvider implements async FileSystemProvider methods using synchronous std::fs
operations, which can block Tokio worker threads during directory traversal and file reads. This can
significantly slow indexing and degrade runtime responsiveness under load or on slow filesystems.
Agent Prompt
### Issue description
`LocalFileSystemProvider` exposes `async` APIs but performs synchronous filesystem calls (`std::fs::*`) directly inside them. When awaited from Tokio tasks (e.g., indexing), these calls block the runtime worker threads.

### Issue Context
This provider is used by `IndexingServiceImpl` for recursive discovery (`read_dir_entries`) and per-file reads (`read_to_string`), so the blocking behavior can be amplified across many files.

### Fix Focus Areas
- crates/mcb-providers/src/fs/mod.rs[58-73]
- crates/mcb-application/src/use_cases/indexing_service.rs[192-230]
- crates/mcb-application/src/use_cases/indexing_service.rs[427-444]

### How to fix
- Replace synchronous calls with `tokio::fs` equivalents where available (`tokio::fs::read_to_string`, `tokio::fs::read_dir`, `tokio::fs::canonicalize`).
- For operations that don’t have good async equivalents (or if you want to keep std::fs), wrap them in `tokio::task::spawn_blocking` and await the result.
- Ensure directory iteration + `file_type()` is also done in the blocking section (or with Tokio’s async directory entries) to avoid partial blocking.

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

Comment on lines +143 to +147
if let Some(ref db) = self.sea_conn {
let am = org_to_model(org);
am.update(db)
.await
.map_err(|e| Error::memory_with_source("SeaORM update organization", 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

3. Org update overwrites created_at 🐞 Bug ≡ Correctness

The SeaORM update path in SqliteOrgEntityRepository builds an ActiveModel with `created_at:
Set(org.created_at) and uses it for update_org, allowing created_at` to be overwritten from the
passed Organization value. In the server handler, Organization for update is deserialized from
request payload, so a client can accidentally or maliciously change the persisted creation
timestamp.
Agent Prompt
### Issue description
`SqliteOrgEntityRepository::update_org` uses SeaORM with an ActiveModel that sets `created_at` from the input entity. This makes `created_at` writable during updates, diverging from the legacy SQL path (which only updates mutable columns) and enabling persisted timestamp corruption.

### Issue Context
- `org_to_model()` sets `created_at: Set(org.created_at)`.
- `update_org()` uses `am.update(db)` when `sea_conn` is present.
- In production wiring, SQLite connects via `SqliteBackend` and `create_org_entity_repository()` selects `new_with_sea`, so the SeaORM path is the default.
- The server update handler deserializes `Organization` from request data without stripping `created_at`.

### Fix Focus Areas
- crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[43-52]
- crates/mcb-providers/src/database/sqlite/org_entity_repository.rs[141-162]
- crates/mcb-providers/src/database/sqlite/provider.rs[69-79]
- crates/mcb-providers/src/database/sqlite/provider.rs[135-146]
- crates/mcb-server/src/handlers/entities/org.rs[69-72]

### How to fix
- When performing the SeaORM update, do not set `created_at` to `Set(...)`.
  - Option A: build a separate ActiveModel for updates where `created_at` is `NotSet`/`Unchanged` and only mutable fields are `Set`.
  - Option B: fetch the existing row first and preserve the DB value explicitly.
- Add a regression test covering the SeaORM path: create org, then call `update_org` with a different `created_at`, and assert `created_at` in DB is unchanged.

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

Comment on lines +34 to +36
fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
tokio::spawn(task);
Ok(())

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. Spawn panics without runtime 🐞 Bug ☼ Reliability

TokioTaskRunnerProvider::spawn calls tokio::spawn and always returns Ok(()), so if called on a
thread without an entered Tokio runtime it will panic instead of returning an error. Callers (e.g.,
IndexingServiceImpl) attempt to handle Err from spawn, but this implementation cannot report that
failure mode.
Agent Prompt
### Issue description
`TokioTaskRunnerProvider::spawn` can panic when no Tokio runtime is active on the calling thread, but the port contract is fallible (`Result<()>`). This makes error handling ineffective and can crash the process in alternate composition roots/tests/future call sites.

### Issue Context
The port explicitly documents returning an error if the task cannot be spawned, and `IndexingServiceImpl` checks for `Err` from `spawn()`.

### Fix Focus Areas
- crates/mcb-providers/src/task/mod.rs[33-38]
- crates/mcb-domain/src/ports/providers/task.rs[5-11]
- crates/mcb-application/src/use_cases/indexing_service.rs[299-307]

### How to fix
- Use `tokio::runtime::Handle::try_current()` inside `spawn()`.
  - If it fails, return a domain `Error` (or map to the existing `Result` type) instead of panicking.
  - If it succeeds, call `handle.spawn(task)`.
- (Optional) Consider capturing/logging the JoinHandle if you want to observe panics in the background task.

ⓘ 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 248006a

@coderabbitai coderabbitai 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.

Actionable comments posted: 23

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
crates/mcb-infrastructure/src/di/bootstrap.rs (1)

226-249: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Inconsistent error context on newly-added resolver calls.

embedding_resolver/vector_store_resolver/cache_resolver/language_resolver wrap resolution failures with Error::configuration(format!("X: {e}")) for a labeled, diagnosable error, but the newly added event_bus_resolver.resolve_from_config()?, fs_resolver.resolve_from_config()?, and task_runner_resolver.resolve_from_config()? (lines 306, 314, 317) propagate bare errors without labeling which provider failed. This makes bootstrap failures harder to diagnose in production logs.

♻️ Proposed fix: add labeled context to the new resolver calls
-    let event_bus: Arc<dyn EventBusProvider> = event_bus_resolver.resolve_from_config()?;
+    let event_bus: Arc<dyn EventBusProvider> = event_bus_resolver
+        .resolve_from_config()
+        .map_err(|e| mcb_domain::error::Error::configuration(format!("EventBus: {e}")))?;
     ...
-    let file_system_provider: Arc<dyn FileSystemProvider> = fs_resolver.resolve_from_config()?;
+    let file_system_provider: Arc<dyn FileSystemProvider> = fs_resolver
+        .resolve_from_config()
+        .map_err(|e| mcb_domain::error::Error::configuration(format!("FileSystem: {e}")))?;
     let task_runner_resolver = TaskRunnerProviderResolver::new(Arc::clone(&config));
-    let task_runner_provider: Arc<dyn TaskRunnerProvider> =
-        task_runner_resolver.resolve_from_config()?;
+    let task_runner_provider: Arc<dyn TaskRunnerProvider> = task_runner_resolver
+        .resolve_from_config()
+        .map_err(|e| mcb_domain::error::Error::configuration(format!("TaskRunner: {e}")))?;

Also applies to: 305-317

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-infrastructure/src/di/bootstrap.rs` around lines 226 - 249, Update
the newly added event_bus_resolver, fs_resolver, and task_runner_resolver
resolve_from_config calls to map failures through
mcb_domain::error::Error::configuration with distinct “EventBus:”,
“Filesystem:”, and “TaskRunner:” labels, matching the existing
embedding_provider, vector_store_provider, cache_provider, and language_provider
error-context pattern.
crates/mcb-application/src/use_cases/indexing_service.rs (2)

277-317: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Task spawn failure silently leaves the operation stuck "in progress" while reporting success.

start_operation registers operation_id at line 278 before the spawn attempt. If task_runner_provider.spawn(task) fails (line 305), only a warn! is emitted — the function still returns Ok(IndexingResult{ status: INDEXING_STATUS_STARTED, operation_id: Some(operation_id), .. }). Since complete_operation is only invoked from inside run_indexing_task (which never runs), get_status() will report is_indexing: true indefinitely and the caller has no way to know indexing never started.

🐛 Proposed fix: fail the operation and surface the error when spawn fails
         if let Err(e) = self.task_runner_provider.spawn(task) {
             warn!("Failed to spawn indexing background task: {}", e);
+            self.indexing_ops.complete_operation(&operation_id);
+            return Ok(IndexingResult {
+                files_processed: 0,
+                chunks_created: 0,
+                files_skipped: 0,
+                errors: vec![format!("Failed to spawn indexing task: {e}")],
+                operation_id: Some(operation_id),
+                status: "failed".to_owned(),
+            });
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-application/src/use_cases/indexing_service.rs` around lines 277 -
317, Update the task spawn failure branch in the indexing method around
indexing_ops.start_operation and run_indexing_task so it marks operation_id as
failed or otherwise completes/removes the in-progress operation, then returns an
error instead of logging a warning and returning INDEXING_STATUS_STARTED.
Preserve the immediate success response only when task_runner_provider.spawn
succeeds.

192-230: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Restore safe filesystem traversal semantics.

The stack visit does not deduplicate paths and only skips directories by name, so any symlink loop is an unbounded traversal. The entry type also has no is_symlink flag, and LocalFileSystemProvider only reports is_file/is_dir, so callers cannot avoid following symlink directories. Add a visited-set or depth cap and either preserve symlink awareness in DirEntry/read_dir_entries or explicitly skip directory symlinks if ignored links are the intended contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-application/src/use_cases/indexing_service.rs` around lines 192 -
230, Update discover_files to prevent unbounded traversal by tracking visited
directory paths or enforcing a traversal depth limit, while preserving recursive
discovery. Extend DirEntry and LocalFileSystemProvider::read_dir_entries to
expose symlink status, then explicitly skip directory symlinks (or otherwise
enforce the intended ignored-link contract) before pushing entries onto dirs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/mcb-domain/src/entities/api_key.rs`:
- Around line 30-52: Add schema-sync test coverage for the ApiKey table
definition, asserting the expected table columns, indexes, and both user_id and
org_id foreign keys through ApiKey::table_def() or Schema::definition(). Follow
the existing schema assertion pattern and ensure the test detects drift in all
declared ApiKey schema elements.

In `@crates/mcb-domain/src/entities/team.rs`:
- Around line 80-98: Update schema_entity_sync_tests.rs to explicitly exempt
computed or non-persisted entity fields from schema synchronization checks,
including TeamMember::id derived from team_id and user_id. Preserve validation
for all persisted fields while ensuring this intentional field is not reported
as schema drift.

In `@crates/mcb-domain/src/ports/infrastructure/database.rs`:
- Line 82: Remove the missing_docs suppression in database.rs and add
documentation to every public factory method in the database infrastructure
port. In macros.rs, stop generating allow(dead_code) from sea_entity! and make
the generated items legitimately reachable through the module’s public API or
usages. Then remove the module-level dead_code suppression in
sea_entities/mod.rs and resolve all resulting warnings without adding new
suppression directives.

In `@crates/mcb-domain/src/registry/event_bus.rs`:
- Line 1: Remove the blanket missing-docs suppression and document each public
provider configuration type and its provider and extra fields. Apply this in
crates/mcb-domain/src/registry/event_bus.rs:1,
crates/mcb-domain/src/registry/fs.rs:1,
crates/mcb-domain/src/registry/task_runner.rs:1, and
crates/mcb-domain/src/registry/vcs.rs:1 for EventBusProviderConfig,
FileSystemProviderConfig, TaskRunnerProviderConfig, and VcsProviderConfig
respectively; alternatively, retain each directive only with a one-line
guideline-compliant justification.
- Around line 1-20: Introduce a shared define_provider_config! macro that
generates the standard provider config struct with provider and extra fields,
then replace the duplicated definitions in
crates/mcb-domain/src/registry/event_bus.rs:1-20,
crates/mcb-domain/src/registry/fs.rs:1-20,
crates/mcb-domain/src/registry/task_runner.rs:1-20, and
crates/mcb-domain/src/registry/vcs.rs:1-20 with macro invocations for
EventBusProviderConfig, FileSystemProviderConfig, TaskRunnerProviderConfig, and
VcsProviderConfig respectively; preserve each file’s existing
impl_config_builder! and impl_registry! usage.

In `@crates/mcb-domain/src/schema/definition.rs`:
- Around line 49-79: Extend schema_entity_sync_tests.rs with FK, index, and
unique-constraint synchronization assertions for User, Team, TeamMember, and
ApiKey, matching the existing Organization coverage. Ensure each migrated
entity’s expected metadata is compared against its legacy schema output so
missing or dropped definitions fail CI.

In `@crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs`:
- Around line 221-232: Remove the #[test] annotation from
report_divergence_mitigation and relocate its printed guidance into appropriate
static documentation, such as a doc comment or README. Do not retain a test that
only emits instructional text and performs no schema interaction or assertions.
- Around line 11-79: Convert the diagnostic-only tests print_full_schema_report
and detect_schema_only_fields into regression checks that fail when
schema/entity divergences are detected. In detect_schema_only_fields, assert
that the known schema-only fields such as repositories.origin_context,
worktrees.org_id/project_id/origin_context, and branches.origin_context are
absent or otherwise match the module’s expected entity mapping, while retaining
useful diagnostics in failure messages. Replace or supplement
print_full_schema_report’s unconditional reporting with assertions for the
schema invariants it is intended to validate.

In `@crates/mcb-infrastructure/src/di/provider_resolvers.rs`:
- Around line 406-409: Update VcsProviderResolver::resolve_from_config,
FileSystemProviderResolver::resolve_from_config, and
TaskRunnerProviderResolver::resolve_from_config to read their provider names
from self.config.system.infrastructure, matching
EventBusProviderResolver::resolve_from_config, instead of discarding config or
calling hardcoded defaults. Add any missing fields to the typed configuration
model and populate them in development.yaml, test.yaml, and production.yaml,
preserving the existing provider-resolution behavior for configured values.

In `@crates/mcb-infrastructure/src/di/vcs.rs`:
- Around line 62-67: Update VcsRepositoryProvider::repository_id to resolve the
provider that owns or manages the supplied repo instead of unconditionally using
self.providers.first(). Preserve the repo.id() fallback when no matching
provider exists, and delegate repository_id only to the identified provider.
- Around line 12-15: Add a one-line documented justification to the missing_docs
suppression on DynamicVcsProvider and the corresponding default_vcs_provider
declaration, preserving both suppressions while explaining why documentation is
intentionally omitted.
- Around line 33-123: Cache the provider selected by provider_and_repo_for_path
per repository path, using synchronized path-to-Arc<dyn VcsProvider> state on
DynamicVcsProvider. Update provider_and_repo_for_path to consult the cache
before iterating providers and store successful resolutions, then ensure
list_branches, commit_history, list_files, read_file, and diff_refs reuse the
cached provider without reopening the repository solely to rediscover it.

In `@crates/mcb-providers/src/database/sqlite/org_entity_repository.rs`:
- Around line 21-28: The SQLite repository and row-conversion modules exceed the
source-size guideline and should be split into focused submodules. In
crates/mcb-providers/src/database/sqlite/org_entity_repository.rs#L21-L28,
extract organization, user, team, and API-key functionality into per-entity
modules, keeping SeaORM-versus-executor dispatch localized to the organization
module; update the shared repository exports and preserve existing behavior. In
crates/mcb-providers/src/database/sqlite/row_convert.rs#L244-L409, move the
FromRow implementations into domain modules such as org, plan, and vcs, then
re-export them through row_convert/mod.rs without changing conversion behavior.
- Around line 66-85: Add tests that construct SqliteOrgEntityRepository via
new_with_sea (or an equivalent provider-backed setup) and exercise create_org,
get_org, list_orgs, update_org, and delete_org through the SeaORM connection
path. Keep existing new(...) coverage intact and assert the operations’ expected
results.
- Around line 43-64: Update org_to_model so created_at is not marked Set when
constructing the ActiveModel used by update_org; preserve creation-time
assignment through the insert-specific path while ensuring SeaORM updates leave
the immutable timestamp untouched.

In `@crates/mcb-providers/src/database/sqlite/provider.rs`:
- Around line 139-145: The organization repository construction in provider.rs
must use an explicit typed backend/provider capability instead of downcasting
through Any or silently falling back to SqliteOrgEntityRepository::new; update
the repository factory contract so the SeaORM-backed path is selected
declaratively and invalid backend combinations are not accepted. In backend.rs,
remove the Any-based escape hatch and its related implementation once the typed
factory contract owns this capability.
- Around line 73-78: The connect flow currently opens a separate SeaORM
connection that may not share the schema initialized by connect_and_init. Update
connect to reuse the initialized SQLx pool for the SeaORM backend, or
consistently configure both connections with a shared-cache in-memory URI,
ensuring SqliteOrgEntityRepository::create_org sees the initialized
organizations table.

In `@crates/mcb-providers/src/events/nats.rs`:
- Around line 52-62: The NATS provider factory currently hardcodes the fallback
URL in its configuration parsing. Add a typed NATS URL field to the
configuration model, populate it in every profile configuration, and update the
URL selection in the NATS provider factory to use that field instead of the
literal fallback; keep the existing override behavior for explicitly supplied
values.
- Around line 49-71: Update create_nats_event_bus_provider and the provider
resolution flow used by EventBusProviderResolver::resolve_from_config() and
async init_app() so NATS construction does not call Tokio Handle::block_on while
a runtime is active. Propagate the async factory through the registry, or return
a lazily connecting provider, while preserving the existing URL, subject,
client_name, and error behavior.

In `@crates/mcb-providers/src/fs/mod.rs`:
- Around line 34-73: Update LocalFileSystemProvider::read_dir_entries and its
read_dir_sync helper so blocking directory I/O does not run directly on Tokio
worker threads. Either implement directory iteration with tokio::fs or execute
the existing read_dir_sync work through tokio::task::spawn_blocking, preserving
the current DirEntry construction and Error conversion behavior.

In `@crates/mcb-providers/src/lib.rs`:
- Around line 91-96: Remove the unjustified missing-documentation suppressions
by documenting the affected provider modules and symbols. In
crates/mcb-providers/src/lib.rs lines 91-96, document the fs and task modules;
in crates/mcb-providers/src/fs/mod.rs lines 23-32, document
LocalFileSystemProvider and its impl; and in
crates/mcb-providers/src/task/mod.rs lines 21-31, document
TokioTaskRunnerProvider and its impl. If any suppression remains, add the
required one-line justification directly with that directive.

In `@crates/mcb-providers/src/task/mod.rs`:
- Around line 33-38: Update TokioTaskRunnerProvider::spawn to retain and observe
the JoinHandle returned by tokio::spawn, ensuring task panics or join failures
are surfaced through the existing error-reporting mechanism instead of being
silently discarded. Preserve the current Result<()> API and background-task
behavior for successful tasks.

In `@crates/mcb/src/cli/validate.rs`:
- Line 153: Update the text-printing helpers used by validate execution to
return io::Result, including the writeln! calls around the affected output
paths, and propagate each stdout write failure with ?. Do not discard errors; if
broken pipes are intentionally tolerated, explicitly handle only that error
while propagating all others.

---

Outside diff comments:
In `@crates/mcb-application/src/use_cases/indexing_service.rs`:
- Around line 277-317: Update the task spawn failure branch in the indexing
method around indexing_ops.start_operation and run_indexing_task so it marks
operation_id as failed or otherwise completes/removes the in-progress operation,
then returns an error instead of logging a warning and returning
INDEXING_STATUS_STARTED. Preserve the immediate success response only when
task_runner_provider.spawn succeeds.
- Around line 192-230: Update discover_files to prevent unbounded traversal by
tracking visited directory paths or enforcing a traversal depth limit, while
preserving recursive discovery. Extend DirEntry and
LocalFileSystemProvider::read_dir_entries to expose symlink status, then
explicitly skip directory symlinks (or otherwise enforce the intended
ignored-link contract) before pushing entries onto dirs.

In `@crates/mcb-infrastructure/src/di/bootstrap.rs`:
- Around line 226-249: Update the newly added event_bus_resolver, fs_resolver,
and task_runner_resolver resolve_from_config calls to map failures through
mcb_domain::error::Error::configuration with distinct “EventBus:”,
“Filesystem:”, and “TaskRunner:” labels, matching the existing
embedding_provider, vector_store_provider, cache_provider, and language_provider
error-context pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 11952fe0-0ef5-4a92-87f1-f8375059f0a7

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (63)
  • Cargo.toml
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/Cargo.toml
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/macros/schema.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/task_runner.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-domain/src/schema/api_keys.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-domain/src/schema/organizations.rs
  • crates/mcb-domain/src/schema/team_members.rs
  • crates/mcb-domain/src/schema/teams.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-domain/src/schema/users.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • 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/test_factory.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_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/workspace.rs
  • crates/mcb-providers/Cargo.toml
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.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/task/mod.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb/src/cli/validate.rs
💤 Files with no reviewable changes (7)
  • crates/mcb-domain/src/schema/api_keys.rs
  • crates/mcb-domain/src/schema/organizations.rs
  • crates/mcb-domain/src/ports/providers/http.rs
  • crates/mcb-domain/src/schema/team_members.rs
  • crates/mcb-domain/src/schema/teams.rs
  • crates/mcb-domain/src/schema/users.rs
  • crates/mcb-domain/Cargo.toml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.toml

📄 CodeRabbit inference engine (.cursor/rules/mcb.mdc)

Enforce strict Rust lints: unsafe_code = "deny", dead_code = "deny", unused_imports = "deny"

Files:

  • crates/mcb-providers/Cargo.toml
  • Cargo.toml
**/*.{rs,toml}

📄 CodeRabbit inference engine (AGENTS.md)

No TODOs, stubs, fakes, fallbacks, compat wrappers, or 'temporary' workarounds. No suppression directives (# type: ignore, blanket # noqa, @ts-ignore, eslint-disable, etc.) and no escape-hatch typing (Any, bare object, unchecked casts) unless carrying a one-line documented justification.

Files:

  • crates/mcb-providers/Cargo.toml
  • Cargo.toml
  • crates/mcb-domain/src/registry/event_bus.rs
  • crates/mcb-infrastructure/tests/utils/workspace.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-infrastructure/src/di/test_factory.rs
  • crates/mcb-domain/src/ports/providers/task.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-providers/src/lib.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-infrastructure/tests/utils/fs_guards.rs
  • crates/mcb-domain/src/registry/mod.rs
  • crates/mcb-domain/src/registry/fs.rs
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb-infrastructure/tests/unit/routing/router_tests.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-providers/src/events/nats.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/registry/task_runner.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/task/mod.rs
  • crates/mcb-providers/src/fs/mod.rs
  • crates/mcb-domain/src/macros/schema.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-infrastructure/src/config/types/system.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-providers/src/events/tokio.rs
  • crates/mcb/src/cli/validate.rs
  • crates/mcb-domain/src/ports/mod.rs
  • crates/mcb-infrastructure/src/di/mod.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-domain/src/ports/infrastructure/database.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • crates/mcb-infrastructure/src/di/database_resolver.rs
  • crates/mcb-infrastructure/src/di/provider_resolvers.rs
  • crates/mcb-domain/src/ports/providers/mod.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-infrastructure/src/di/modules/domain_services.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.rs
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/src/ports/providers/fs.rs
  • crates/mcb-infrastructure/src/di/bootstrap.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
Cargo.toml

📄 CodeRabbit inference engine (AGENTS.md)

Version, MSRV, workspace members, lint policy are defined in Cargo.toml. When a static document disagrees with Cargo.toml or code, trust the executable source first and update the doc.

Files:

  • Cargo.toml
**/*.rs

📄 CodeRabbit inference engine (.windsurfrules)

**/*.rs: Error handling: Use Error::vcs("msg") constructors, never unwrap()/expect() in production code
Lints: Set unsafe_code = "deny" and dead_code = "deny". Zero clippy warnings required
Testing: Run make test (1700+ tests). New logic must include tests
MVI 200: Keep source files under approximately 200 lines; split into submodules when growing larger

**/*.rs: Use thiserror constructors for error handling (e.g., Error::vcs("msg")) and never use unwrap() in production code
Use linkme for compile-time dependency injection and Handle pattern for hot-swap dependency injection, as specified in ADR-050
Keep source files under ~200 lines; split into submodules when growing beyond that size
Fix all warnings and clippy issues and leave tests passing after every change cycle

**/*.rs: Use the most restrictive type that compiles in Rust. No Any, no bare object, no suppression of type errors. Fix types at the source.
Depend on abstractions (protocols/interfaces); inject collaborators; no hidden globals or hard-wired construction inside business logic.
Use the most restrictive type that compiles. No Any, no bare object, no suppression of type errors. Fix types at the source; depend on declared contracts.
Enforce dependency rules: mcb-domain has zero internal dependencies; mcb-providers implements domain ports; mcb-infrastructure handles composition; mcb-server is entrypoint; mcb-utils is a leaf crate.
Keep imports ordered: std, external crates, mcb_* crates, local modules.

Files:

  • crates/mcb-domain/src/registry/event_bus.rs
  • crates/mcb-infrastructure/tests/utils/workspace.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-infrastructure/src/di/test_factory.rs
  • crates/mcb-domain/src/ports/providers/task.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-providers/src/lib.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-infrastructure/tests/utils/fs_guards.rs
  • crates/mcb-domain/src/registry/mod.rs
  • crates/mcb-domain/src/registry/fs.rs
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb-infrastructure/tests/unit/routing/router_tests.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-providers/src/events/nats.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/registry/task_runner.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/task/mod.rs
  • crates/mcb-providers/src/fs/mod.rs
  • crates/mcb-domain/src/macros/schema.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-infrastructure/src/config/types/system.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-providers/src/events/tokio.rs
  • crates/mcb/src/cli/validate.rs
  • crates/mcb-domain/src/ports/mod.rs
  • crates/mcb-infrastructure/src/di/mod.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-domain/src/ports/infrastructure/database.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • crates/mcb-infrastructure/src/di/database_resolver.rs
  • crates/mcb-infrastructure/src/di/provider_resolvers.rs
  • crates/mcb-domain/src/ports/providers/mod.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-infrastructure/src/di/modules/domain_services.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.rs
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/src/ports/providers/fs.rs
  • crates/mcb-infrastructure/src/di/bootstrap.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
**/*.{rs,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Do not hardcode configuration values in code. Add fields to the typed config model and populate every profile (development.yaml, test.yaml, production.yaml).

Files:

  • crates/mcb-domain/src/registry/event_bus.rs
  • crates/mcb-infrastructure/tests/utils/workspace.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-infrastructure/src/di/test_factory.rs
  • crates/mcb-domain/src/ports/providers/task.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-providers/src/lib.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-infrastructure/tests/utils/fs_guards.rs
  • crates/mcb-domain/src/registry/mod.rs
  • crates/mcb-domain/src/registry/fs.rs
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb-infrastructure/tests/unit/routing/router_tests.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-providers/src/events/nats.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/registry/task_runner.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/task/mod.rs
  • crates/mcb-providers/src/fs/mod.rs
  • crates/mcb-domain/src/macros/schema.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-infrastructure/src/config/types/system.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-providers/src/events/tokio.rs
  • crates/mcb/src/cli/validate.rs
  • crates/mcb-domain/src/ports/mod.rs
  • crates/mcb-infrastructure/src/di/mod.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-domain/src/ports/infrastructure/database.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • crates/mcb-infrastructure/src/di/database_resolver.rs
  • crates/mcb-infrastructure/src/di/provider_resolvers.rs
  • crates/mcb-domain/src/ports/providers/mod.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-infrastructure/src/di/modules/domain_services.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.rs
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/src/ports/providers/fs.rs
  • crates/mcb-infrastructure/src/di/bootstrap.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Use Error constructors and Result aliases from mcb-domain; do not build raw domain errors by hand.
Use ? for propagation. No unwrap(), expect(), panic!(), todo!(), or unimplemented!() in production paths.
Keep first-party source files compact; split modules before they become difficult to review.

Files:

  • crates/mcb-domain/src/registry/event_bus.rs
  • crates/mcb-infrastructure/tests/utils/workspace.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs
  • crates/mcb-domain/src/entities/organization.rs
  • crates/mcb-infrastructure/src/di/test_factory.rs
  • crates/mcb-domain/src/ports/providers/task.rs
  • crates/mcb-domain/src/macros/di.rs
  • crates/mcb-domain/src/entities/user.rs
  • crates/mcb-providers/src/lib.rs
  • crates/mcb-domain/src/registry/vcs.rs
  • crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs
  • crates/mcb-infrastructure/tests/utils/fs_guards.rs
  • crates/mcb-domain/src/registry/mod.rs
  • crates/mcb-domain/src/registry/fs.rs
  • crates/mcb-providers/src/database/sqlite/backend.rs
  • crates/mcb-domain/src/entities/api_key.rs
  • crates/mcb-providers/src/vector_store/milvus.rs
  • crates/mcb-infrastructure/tests/unit/routing/router_tests.rs
  • crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs
  • crates/mcb-domain/tests/unit/mod.rs
  • crates/mcb-providers/src/events/nats.rs
  • crates/mcb-domain/src/macros/mod.rs
  • crates/mcb-domain/src/registry/task_runner.rs
  • crates/mcb-providers/src/database/sqlite/macros.rs
  • crates/mcb-domain/src/entities/team.rs
  • crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs
  • crates/mcb-providers/src/vcs/git2_provider.rs
  • crates/mcb-providers/src/task/mod.rs
  • crates/mcb-providers/src/fs/mod.rs
  • crates/mcb-domain/src/macros/schema.rs
  • crates/mcb-providers/src/database/sqlite/mod.rs
  • crates/mcb-infrastructure/src/config/types/system.rs
  • crates/mcb-domain/src/schema/types.rs
  • crates/mcb-providers/src/events/tokio.rs
  • crates/mcb/src/cli/validate.rs
  • crates/mcb-domain/src/ports/mod.rs
  • crates/mcb-infrastructure/src/di/mod.rs
  • crates/mcb-domain/src/schema/mod.rs
  • crates/mcb-infrastructure/src/di/vcs.rs
  • crates/mcb-domain/src/ports/infrastructure/database.rs
  • crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs
  • crates/mcb-infrastructure/src/di/database_resolver.rs
  • crates/mcb-infrastructure/src/di/provider_resolvers.rs
  • crates/mcb-domain/src/ports/providers/mod.rs
  • crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs
  • crates/mcb-domain/src/schema/definition.rs
  • crates/mcb-providers/src/database/sqlite/provider.rs
  • crates/mcb-providers/src/database/sqlite/row_convert.rs
  • crates/mcb-infrastructure/src/di/modules/domain_services.rs
  • crates/mcb-providers/src/database/sqlite/vcs_entity_repository.rs
  • crates/mcb-application/src/use_cases/indexing_service.rs
  • crates/mcb-domain/src/ports/providers/fs.rs
  • crates/mcb-infrastructure/src/di/bootstrap.rs
  • crates/mcb-providers/src/database/sqlite/org_entity_repository.rs
🔇 Additional comments (40)
crates/mcb-infrastructure/tests/unit/error/error_ext_tests.rs (1)

28-42: LGTM!

crates/mcb-infrastructure/tests/unit/routing/router_tests.rs (1)

102-102: LGTM!

crates/mcb-infrastructure/tests/utils/fs_guards.rs (1)

10-13: LGTM!

Also applies to: 33-36

crates/mcb-infrastructure/tests/utils/workspace.rs (1)

3-6: LGTM!

crates/mcb-providers/src/vector_store/milvus.rs (1)

430-432: LGTM!

crates/mcb/src/cli/validate.rs (1)

1-3: LGTM!

Also applies to: 115-115

crates/mcb-infrastructure/src/di/vcs.rs (1)

125-128: LGTM!

crates/mcb-infrastructure/tests/integration/di/dispatch_tests.rs (1)

9-9: LGTM!

Also applies to: 199-252

crates/mcb-providers/src/vcs/git2_provider.rs (1)

10-10: LGTM!

Also applies to: 20-33

crates/mcb-providers/src/events/tokio.rs (1)

49-67: LGTM!

crates/mcb-domain/tests/unit/mod.rs (1)

11-11: LGTM!

crates/mcb-providers/src/database/sqlite/mod.rs (1)

11-14: LGTM!

Also applies to: 25-29

crates/mcb-providers/src/database/sqlite/plan_entity_repository.rs (1)

7-12: LGTM!

Also applies to: 59-75, 139-152, 185-198

crates/mcb-providers/src/database/sqlite/vcs_entity_repository.rs (1)

9-16: LGTM!

Also applies to: 75-90, 173-185, 265-277, 354-369

crates/mcb-providers/src/database/sqlite/sea_entities/organization.rs (1)

5-16: 🎯 Functional Correctness

Keep the SeaORM prelude import.

sea_entity! expands attributes like #[sea_orm(table_name = $table)] inside this module, so the sea_orm path comes from this import. Removing it would make the generated macro output fail to resolve under the workspace unused_imports = "deny".

			> Likely an incorrect or invalid review comment.
crates/mcb-domain/src/entities/organization.rs (1)

47-60: LGTM!

crates/mcb-domain/src/entities/team.rs (1)

61-78: LGTM!

crates/mcb-domain/src/entities/user.rs (1)

53-76: LGTM!

Cargo.toml (1)

322-324: LGTM!

crates/mcb-providers/Cargo.toml (1)

130-132: LGTM!

crates/mcb-providers/src/database/sqlite/org_entity_repository.rs (1)

89-140: LGTM!

Also applies to: 166-442

crates/mcb-domain/src/macros/di.rs (1)

6-40: 🎯 Functional Correctness

No change needed.

crates/mcb-domain/src/macros/mod.rs (1)

21-23: LGTM!

crates/mcb-domain/src/registry/mod.rs (1)

59-64: LGTM!

crates/mcb-domain/src/schema/mod.rs (1)

4-30: LGTM!

crates/mcb-domain/src/schema/types.rs (1)

119-140: LGTM!

crates/mcb-domain/src/macros/schema.rs (1)

204-217: 🎯 Functional Correctness

No action needed.

Current impl_table_schema! entity invocations use the full form or existing shorthand arms; no current schema call uses columns + indexes + foreign_keys without unique_constraints.

crates/mcb-domain/src/ports/mod.rs (1)

62-66: See consolidated comment on HTTP client port removal (anchored at crates/mcb-domain/src/ports/providers/mod.rs).

crates/mcb-application/src/use_cases/indexing_service.rs (2)

111-126: See consolidated comment on undocumented new fields under #[allow(missing_docs)] (anchored here).


447-452: 🎯 Functional Correctness

No change needed.

The workspace declares Rust 1.92 / edition 2024, so the let-chained if let Some(repo) = ... && !repo.has_changed(...).await? syntax is supported.

crates/mcb-infrastructure/src/di/bootstrap.rs (1)

1-413: See consolidated comment on MVI-200 file-length violations (anchored here).

crates/mcb-infrastructure/src/di/provider_resolvers.rs (1)

1-496: See consolidated comment on MVI-200 file-length violations.

crates/mcb-domain/src/ports/providers/fs.rs (1)

1-24: LGTM!

crates/mcb-domain/src/ports/providers/task.rs (1)

1-13: LGTM!

crates/mcb-infrastructure/src/config/types/system.rs (1)

14-15: LGTM!

Also applies to: 123-123, 148-148

crates/mcb-infrastructure/src/di/database_resolver.rs (1)

37-53: LGTM!

crates/mcb-infrastructure/src/di/mod.rs (1)

49-49: LGTM!

Also applies to: 64-66

crates/mcb-infrastructure/src/di/modules/domain_services.rs (1)

100-142: Dependency wiring for file_system_provider/task_runner_provider through ServiceDependenciesIndexingServiceInputsbuild_indexing_service/create_indexing_service is correct and matches downstream usage in AppContext/test_factory.rs. See consolidated comments for the undocumented-fields and file-length concerns.

Also applies to: 171-224, 261-283

crates/mcb-infrastructure/src/di/test_factory.rs (1)

64-65: LGTM!

crates/mcb-domain/src/ports/providers/mod.rs (1)

33-33: 🎯 Functional Correctness

No remaining consumers of the removed HTTP client port.

Comment on lines +30 to +52
crate::impl_table_schema!(ApiKey, "api_keys",
columns: [
("id", Text, pk),
("user_id", Text),
("org_id", Text),
("key_hash", Text),
("name", Text),
("scopes_json", Text),
("expires_at", Integer, nullable),
("created_at", Integer),
("revoked_at", Integer, nullable),
],
indexes: [
"idx_api_keys_user" => ["user_id"],
"idx_api_keys_org" => ["org_id"],
"idx_api_keys_key_hash" => ["key_hash"],
],
foreign_keys: [
("user_id", "users", "id"),
("org_id", "organizations", "id"),
],
unique_constraints: [],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add schema-sync coverage for ApiKey.

This adds columns, indexes, and foreign keys without a corresponding ApiKey::table_def()/Schema::definition() assertion. Add a test covering the table, indexes, and both foreign keys.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-domain/src/entities/api_key.rs` around lines 30 - 52, Add
schema-sync test coverage for the ApiKey table definition, asserting the
expected table columns, indexes, and both user_id and org_id foreign keys
through ApiKey::table_def() or Schema::definition(). Follow the existing schema
assertion pattern and ensure the test detects drift in all declared ApiKey
schema elements.

Source: Coding guidelines

Comment on lines +80 to +98
crate::impl_table_schema!(TeamMember, "team_members",
columns: [
("team_id", Text, pk),
("user_id", Text, pk),
("role", Text),
("joined_at", Integer),
],
indexes: [
"idx_team_members_team" => ["team_id"],
"idx_team_members_user" => ["user_id"],
],
foreign_keys: [
("team_id", "teams", "id"),
("user_id", "users", "id"),
],
unique_constraints: [
["team_id", "user_id"],
],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd schema.rs crates/mcb-domain/src/macros
fd schema_entity_sync_tests.rs crates/mcb-domain
cat -n crates/mcb-domain/src/macros/schema.rs 2>/dev/null | sed -n '1,200p'
cat -n crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs 2>/dev/null | sed -n '1,200p'

Repository: marlonsc/mcb

Length of output: 16450


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files 'crates/mcb-domain/src/entities/team.rs' 'crates/mcb-domain/src/entities/*.rs' 'crates/mcb-domain/src/schema/**' 'crates/mcb-domain/src/schema.rs' 'crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs' 'crates/mcb-domain/src/macros/schema.rs'

echo "== team.rs outline =="
ast-grep outline crates/mcb-domain/src/entities/team.rs || true

echo "== team.rs relevant =="
cat -n crates/mcb-domain/src/entities/team.rs | sed -n '1,220p'

echo "== schema entity sync tests =="
cat -n crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs | sed -n '1,360p'

echo "== Schema and HasTableSchema usages =="
rg -n "struct Schema|impl Schema|fn definition|HasTableSchema|impl_table_schema|team_members|TeamMember" crates/mcb-domain/src crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs

Repository: marlonsc/mcb

Length of output: 21238


Add an explicit test allowance for computed schema fields.

TeamMember::id is derived from team_id:user_id and intentionally has no team_members column. Since entity-owned schema metadata is now the source of truth, add/expand schema_entity_sync_tests.rs with an explicit exemption for computed/non-persisted fields so future schema/entity sync runs don’t treat this as a drift failure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-domain/src/entities/team.rs` around lines 80 - 98, Update
schema_entity_sync_tests.rs to explicitly exempt computed or non-persisted
entity fields from schema synchronization checks, including TeamMember::id
derived from team_id and user_id. Preserve validation for all persisted fields
while ensuring this intentional field is not reported as schema drift.


/// Provider factory for database connections with schema initialization.
#[async_trait]
#[allow(missing_docs)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Remove lint suppressions and satisfy the underlying lints.

  • crates/mcb-domain/src/ports/infrastructure/database.rs#L82-L82: document each public factory method instead of suppressing missing_docs.
  • crates/mcb-providers/src/database/sqlite/macros.rs#L30-L40: stop emitting allow(dead_code) from sea_entity!; make generated items legitimately reachable.
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs#L6-L6: remove the module-level allow(dead_code) after fixing reachability.

As per coding guidelines, “No suppression directives” and “Fix all warnings and clippy issues.”

📍 Affects 3 files
  • crates/mcb-domain/src/ports/infrastructure/database.rs#L82-L82 (this comment)
  • crates/mcb-providers/src/database/sqlite/macros.rs#L30-L40
  • crates/mcb-providers/src/database/sqlite/sea_entities/mod.rs#L6-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-domain/src/ports/infrastructure/database.rs` at line 82, Remove
the missing_docs suppression in database.rs and add documentation to every
public factory method in the database infrastructure port. In macros.rs, stop
generating allow(dead_code) from sea_entity! and make the generated items
legitimately reachable through the module’s public API or usages. Then remove
the module-level dead_code suppression in sea_entities/mod.rs and resolve all
resulting warnings without adding new suppression directives.

Source: Coding guidelines

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

#![allow(missing_docs)] added without justification in 4 new files. Each new registry file suppresses the missing-docs lint on its public ProviderConfig struct instead of documenting the fields or justifying the suppression inline.

  • crates/mcb-domain/src/registry/event_bus.rs#L1-L1: remove the blanket allow and add doc comments for EventBusProviderConfig/provider/extra, or add a one-line justification per guideline.
  • crates/mcb-domain/src/registry/fs.rs#L1-L1: same for FileSystemProviderConfig.
  • crates/mcb-domain/src/registry/task_runner.rs#L1-L1: same for TaskRunnerProviderConfig.
  • crates/mcb-domain/src/registry/vcs.rs#L1-L1: same for VcsProviderConfig.

As per coding guidelines, **/*.{rs,toml}: "No suppression directives ... unless carrying a one-line documented justification."

📍 Affects 4 files
  • crates/mcb-domain/src/registry/event_bus.rs#L1-L1 (this comment)
  • crates/mcb-domain/src/registry/fs.rs#L1-L1
  • crates/mcb-domain/src/registry/task_runner.rs#L1-L1
  • crates/mcb-domain/src/registry/vcs.rs#L1-L1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-domain/src/registry/event_bus.rs` at line 1, Remove the blanket
missing-docs suppression and document each public provider configuration type
and its provider and extra fields. Apply this in
crates/mcb-domain/src/registry/event_bus.rs:1,
crates/mcb-domain/src/registry/fs.rs:1,
crates/mcb-domain/src/registry/task_runner.rs:1, and
crates/mcb-domain/src/registry/vcs.rs:1 for EventBusProviderConfig,
FileSystemProviderConfig, TaskRunnerProviderConfig, and VcsProviderConfig
respectively; alternatively, retain each directive only with a one-line
guideline-compliant justification.

Source: Coding guidelines

Comment on lines +1 to +20
#![allow(missing_docs)]

use std::collections::HashMap;

#[derive(Debug, Clone, Default)]
pub struct EventBusProviderConfig {
pub provider: String,
pub extra: HashMap<String, String>,
}

crate::impl_config_builder!(EventBusProviderConfig {});

crate::impl_registry!(
provider_trait: crate::ports::EventBusProvider,
config_type: EventBusProviderConfig,
entry_type: EventBusProviderEntry,
slice_name: EVENT_BUS_PROVIDERS,
resolve_fn: resolve_event_bus_provider,
list_fn: list_event_bus_providers
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Four new registry files are byte-for-byte identical boilerplate aside from type names. Each defines a {X}ProviderConfig { provider: String, extra: HashMap<String, String> } struct and the same impl_config_builder!/impl_registry! invocation shape; a single macro generating the struct too would remove the remaining duplication that impl_config_builder!/impl_registry! don't already cover.

  • crates/mcb-domain/src/registry/event_bus.rs#L1-L20: fold the EventBusProviderConfig struct definition into a shared define_provider_config!(EventBusProviderConfig)-style macro (mirroring impl_config_builder!/impl_registry!).
  • crates/mcb-domain/src/registry/fs.rs#L1-L20: same for FileSystemProviderConfig.
  • crates/mcb-domain/src/registry/task_runner.rs#L1-L20: same for TaskRunnerProviderConfig.
  • crates/mcb-domain/src/registry/vcs.rs#L1-L20: same for VcsProviderConfig.
📍 Affects 4 files
  • crates/mcb-domain/src/registry/event_bus.rs#L1-L20 (this comment)
  • crates/mcb-domain/src/registry/fs.rs#L1-L20
  • crates/mcb-domain/src/registry/task_runner.rs#L1-L20
  • crates/mcb-domain/src/registry/vcs.rs#L1-L20
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-domain/src/registry/event_bus.rs` around lines 1 - 20, Introduce a
shared define_provider_config! macro that generates the standard provider config
struct with provider and extra fields, then replace the duplicated definitions
in crates/mcb-domain/src/registry/event_bus.rs:1-20,
crates/mcb-domain/src/registry/fs.rs:1-20,
crates/mcb-domain/src/registry/task_runner.rs:1-20, and
crates/mcb-domain/src/registry/vcs.rs:1-20 with macro invocations for
EventBusProviderConfig, FileSystemProviderConfig, TaskRunnerProviderConfig, and
VcsProviderConfig respectively; preserve each file’s existing
impl_config_builder! and impl_registry! usage.

Comment on lines +52 to +62
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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded config defaults embedded in provider code.

The NATS URL ("nats://127.0.0.1:4222") is a literal fallback baked into the factory rather than sourced from the typed config model / profile files.

As per coding guidelines, "Do not hardcode configuration values in code. Add fields to the typed config model and populate every profile (development.yaml, test.yaml, production.yaml)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-providers/src/events/nats.rs` around lines 52 - 62, The NATS
provider factory currently hardcodes the fallback URL in its configuration
parsing. Add a typed NATS URL field to the configuration model, populate it in
every profile configuration, and update the URL selection in the NATS provider
factory to use that field instead of the literal fallback; keep the existing
override behavior for explicitly supplied values.

Source: Coding guidelines

Comment on lines +34 to +73
fn read_dir_sync(path: &Path) -> Result<Vec<DirEntry>, Error> {
let entries = std::fs::read_dir(path).map_err(|e| {
Error::internal(format!("Failed to read directory {}: {e}", path.display()))
})?;

let mut out = Vec::new();
for entry in entries {
let entry = entry
.map_err(|e| Error::internal(format!("Failed to read directory entry: {e}")))?;
let file_type = entry
.file_type()
.map_err(|e| Error::internal(format!("Failed to read file type: {e}")))?;

out.push(DirEntry {
path: entry.path(),
is_file: file_type.is_file(),
is_dir: file_type.is_dir(),
});
}

Ok(out)
}
}

#[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

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'mod\.rs$' . | sed 's#^\./##' | rg 'crates/mcb-providers/src/fs/mod.rs|indexing_service|fs' || true

echo "== fs provider outline =="
ast-grep outline crates/mcb-providers/src/fs/mod.rs --view expanded || true

echo "== fs provider relevant sections =="
cat -n crates/mcb-providers/src/fs/mod.rs | sed -n '1,120p'

echo "== searches =="
rg -n "FileSystemProvider|read_dir_entries|read_to_string|canonicalize_path|indexing_service|Tokio|tokio" crates -S || true

Repository: marlonsc/mcb

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== mcb-providers package =="
cat -n crates/mcb-providers/Cargo.toml | sed -n '1,120p'

echo "== domain ports filesystem trait =="
cat -n crates/mcb-domain/src/ports/providers/fs.rs | sed -n '1,120p'

echo "== indexing service file candidates =="
fd -i 'indexing_service\.rs$' crates || true
rg -n "struct .*IndexingService|impl .*IndexingService|read_dir_entries|read_to_string|canonicalize_path|file_system_provider|FileSystemProvider" crates/mcb-infrastructure crates/mcb-server crates/mcb-domain crates/mcb-providers -S --glob '*.rs' | head -200

Repository: marlonsc/mcb

Length of output: 15581


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== indexing service outline =="
ast-grep outline crates/mcb-application/src/use_cases/indexing_service.rs --view expanded || true

echo "== relevant indexing service sections =="
wc -l crates/mcb-application/src/use_cases/indexing_service.rs
cat -n crates/mcb-application/src/use_cases/indexing_service.rs | sed -n '1,320p'

echo "== workspace tokio features =="
rg -n '^\[workspace\.dependencies\]|^\ttokio\s*=|features\s*=' Cargo.toml crates/mcb-infrastructure/Cargo.toml crates/mcb-server/Cargo.toml -S

Repository: marlonsc/mcb

Length of output: 18617


🌐 Web query:

tokio async runtime documentation blocking IO std fs read_to_string worker threads

💡 Result:

In the Tokio asynchronous runtime, tokio::fs::read_to_string is the asynchronous equivalent of std::fs::read_to_string [1][2]. Because most operating systems lack native asynchronous filesystem APIs, Tokio implements tokio::fs::read_to_string by internally dispatching the blocking operation to a dedicated thread pool using spawn_blocking [3][4][2]. Key technical considerations include: 1. Blocking the Worker Threads: If you use the synchronous std::fs::read_to_string directly inside an async function, you will block the specific Tokio worker thread currently executing that task [5][6]. This prevents the executor from driving other pending futures on that same thread, significantly degrading concurrency and performance [7][5][8][6]. 2. Role of spawn_blocking: By using tokio::fs::read_to_string().await, the operation offloads the blocking I/O work to Tokio's dedicated blocking thread pool [3][4][2]. This allows the async worker thread to yield and continue processing other tasks while waiting for the filesystem operation to complete [6]. 3. Performance Impact: Although tokio::fs avoids blocking the executor's worker threads, it still incurs the overhead of dispatching work to the blocking thread pool [3][4]. For large numbers of small I/O operations, this overhead can be significant [3][6]. To optimize, it is recommended to batch operations into fewer spawn_blocking calls when possible, rather than executing many small, individual blocking operations [3][4]. 4. When to use std::fs: You should generally only use std::fs (synchronous) outside of an asynchronous context, such as during application startup or within code specifically designated to run on a non-async thread [6]. Inside an async function, always prefer tokio::fs or manually wrap blocking code in tokio::task::spawn_blocking [7][6].

Citations:


Move filesystem I/O out of async worker threads.

read_dir_entries uses blocking std::fs::read_dir and file_type() inside async index_codebase/discover_files, so indexing can block a Tokio worker while other async tasks await. Use tokio::fs/asynchronous directory reads, or wrap the blocking directory reads in tokio::task::spawn_blocking.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-providers/src/fs/mod.rs` around lines 34 - 73, Update
LocalFileSystemProvider::read_dir_entries and its read_dir_sync helper so
blocking directory I/O does not run directly on Tokio worker threads. Either
implement directory iteration with tokio::fs or execute the existing
read_dir_sync work through tokio::task::spawn_blocking, preserving the current
DirEntry construction and Error conversion behavior.

Comment on lines +91 to +96
#[allow(missing_docs)]
pub mod fs;

#[allow(missing_docs)]
pub mod task;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Unjustified #[allow(missing_docs)] suppressions across new provider code. All three sites add #[allow(missing_docs)] without the one-line justification the coding guidelines require for suppression directives.

  • crates/mcb-providers/src/lib.rs#L91-L96: either document the fs/task modules or add a one-line justification for the suppression.
  • crates/mcb-providers/src/fs/mod.rs#L23-L32: add doc comments to LocalFileSystemProvider and its impl block, or justify the suppression inline.
  • crates/mcb-providers/src/task/mod.rs#L21-L31: add doc comments to TokioTaskRunnerProvider and its impl block, or justify the suppression inline.

As per coding guidelines, "No suppression directives ... unless carrying a one-line documented justification."

📍 Affects 3 files
  • crates/mcb-providers/src/lib.rs#L91-L96 (this comment)
  • crates/mcb-providers/src/fs/mod.rs#L23-L32
  • crates/mcb-providers/src/task/mod.rs#L21-L31
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-providers/src/lib.rs` around lines 91 - 96, Remove the unjustified
missing-documentation suppressions by documenting the affected provider modules
and symbols. In crates/mcb-providers/src/lib.rs lines 91-96, document the fs and
task modules; in crates/mcb-providers/src/fs/mod.rs lines 23-32, document
LocalFileSystemProvider and its impl; and in
crates/mcb-providers/src/task/mod.rs lines 21-31, document
TokioTaskRunnerProvider and its impl. If any suppression remains, add the
required one-line justification directly with that directive.

Source: Coding guidelines

Comment on lines +33 to +38
impl TaskRunnerProvider for TokioTaskRunnerProvider {
fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
tokio::spawn(task);
Ok(())
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Spawned task panics are silently discarded.

tokio::spawn(task)'s JoinHandle is dropped, so a panic inside task is never observed by anything — no log, no propagated error. Since this is the generic task-runner port used for background work (e.g., indexing), a failing task disappears silently.

🔍 Proposed fix to surface join errors
     fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
-        tokio::spawn(task);
-        Ok(())
+        let handle = tokio::spawn(task);
+        tokio::spawn(async move {
+            if let Err(e) = handle.await {
+                tracing::error!("spawned task panicked or was cancelled: {e}");
+            }
+        });
+        Ok(())
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl TaskRunnerProvider for TokioTaskRunnerProvider {
fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
tokio::spawn(task);
Ok(())
}
}
impl TaskRunnerProvider for TokioTaskRunnerProvider {
fn spawn(&self, task: BoxFuture<'static, ()>) -> Result<()> {
let handle = tokio::spawn(task);
tokio::spawn(async move {
if let Err(e) = handle.await {
tracing::error!("spawned task panicked or was cancelled: {e}");
}
});
Ok(())
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb-providers/src/task/mod.rs` around lines 33 - 38, Update
TokioTaskRunnerProvider::spawn to retain and observe the JoinHandle returned by
tokio::spawn, ensuring task panics or join failures are surfaced through the
existing error-reporting mechanism instead of being silently discarded. Preserve
the current Result<()> API and background-task behavior for successful tasks.


if has_violations {
println!();
let _ = writeln!(std::io::stdout());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Propagate stdout write errors instead of discarding them.

These let _ = writeln!(...) calls can silently truncate validation output while execute still succeeds. Return io::Result from the text-printing helpers and propagate it with ?; if broken pipes are intentionally tolerated, handle only that specific error. As per coding guidelines: Use ? for propagation.

Also applies to: 170-197

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mcb/src/cli/validate.rs` at line 153, Update the text-printing helpers
used by validate execution to return io::Result, including the writeln! calls
around the affected output paths, and propagate each stdout write failure with
?. Do not discard errors; if broken pipes are intentionally tolerated,
explicitly handle only that error while propagating all others.

Source: Coding guidelines

@marlon-costa-dc
marlon-costa-dc enabled auto-merge (squash) July 29, 2026 03:08

@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 64 files

Confidence score: 2/5

  • The highest-risk path is runtime/provider initialization in crates/mcb-providers/src/events/nats.rs, crates/mcb-providers/src/events/tokio.rs, and crates/mcb-providers/src/task/mod.rs: selecting NATS can panic via Handle::block_on, zero-capacity broadcast config can panic, and task spawning can panic outside Tokio, turning recoverable startup errors into crashes—keep these paths fully async, validate capacity (>0), and use Handle::try_current() to return errors instead of panicking.
  • Database write behavior in crates/mcb-providers/src/database/sqlite/macros.rs and crates/mcb-providers/src/database/sqlite/provider.rs can fail in normal flows: organizations.id is treated as auto-increment despite being canonical non-autoincrement, and in-memory mode can route SeaORM to a different empty DB (no such table: organizations)—set the PK metadata to non-autoincrement and reuse the initialized pool across both access paths.
  • Data integrity can drift in crates/mcb-domain/src/entities/user.rs and crates/mcb-domain/src/entities/api_key.rs: upgrades may keep duplicate/orphaned user rows and API keys can reference users from another org, which can leak or misattribute data—add/verify migrations for unique+FK constraints and enforce the (user_id, org_id) relationship at schema level.
  • Indexing reliability is at risk in crates/mcb-application/src/use_cases/indexing_service.rs and crates/mcb-providers/src/fs/mod.rs: spawn failures can still report started and leave operations stuck, blocking std::fs calls can stall Tokio workers, and ignore rules are bypassed so excluded/generated files get indexed—propagate spawn errors with cleanup, move file I/O to tokio::fs/spawn_blocking, and restore .gitignore/.ignore filtering.
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 async startup panics because this factory calls `Handle::block_on` while `init_app` is already running on the Tokio runtime. The NATS construction path should stay async (for example, via an async registry factory) or defer connection initialization; wrapping it in the current runtime's synchronous `block_on` cannot safely bridge this factory contract.</violation>
</file>

<file name="crates/mcb-providers/src/database/sqlite/macros.rs">

<violation number="1" location="crates/mcb-providers/src/database/sqlite/macros.rs:29">
P2: Generated entities cannot represent canonical composite primary keys because this pattern only designates `$first`; `team_members` and `issue_label_assignments` each have two PK columns. Per-column PK metadata would prevent SeaORM identity operations from becoming incorrect as this LOC-reduction helper is applied to the remaining tables.</violation>

<violation number="2" location="crates/mcb-providers/src/database/sqlite/macros.rs:34">
P1: Organization inserts through the normal `SqliteBackend`/SeaORM path use a generated String PK that SeaORM treats as auto-incrementing, although canonical `organizations.id` is not auto-incremented. Mark this PK `auto_increment = false` (or parameterize it) so ActiveModel insert semantics match the schema.</violation>

<violation number="3" location="crates/mcb-providers/src/database/sqlite/macros.rs:46">
P2: The advertised schema-parity metadata omits PK, nullability, uniqueness, and auto-increment attributes, so entity/schema tests pass despite materially different SeaORM behavior. Including these attributes in the generated metadata would make the synchronization check cover the schema semantics it is meant to protect.</violation>
</file>

<file name="crates/mcb-domain/src/entities/user.rs">

<violation number="1" location="crates/mcb-domain/src/entities/user.rs:74">
P1: Upgraded databases still permit duplicate `(org_id, email)` rows and orphaned `org_id`s because these declarations only affect newly created tables, while `migrate_and_verify_schema` validates columns only. Consider migrating/verifying constraints or forcing a controlled rebuild before accepting the existing schema.</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:202">
P1: Files excluded by `.gitignore`, `.ignore`, or Git exclude rules are now indexed because this raw directory traversal only checks the four names in `SKIP_DIRS`. That can ingest generated/vendor files and content users deliberately excluded from the repository (including ignored secrets), increasing index size and potentially exposing unintended content. The provider-based discovery should preserve the prior `WalkBuilder` ignore semantics, either through an ignore-aware filesystem API or equivalent filtering here.</violation>

<violation number="2" location="crates/mcb-application/src/use_cases/indexing_service.rs:202">
P2: A single unreadable or concurrently removed entry now causes every sibling in that directory to be omitted from indexing. Because `read_dir_entries` collapses entry-level failures into one directory-level error, this loop cannot continue past the bad entry as the previous walker did. Consider returning per-entry results or making the provider retain valid entries while reporting individual failures.</violation>

<violation number="3" location="crates/mcb-application/src/use_cases/indexing_service.rs:305">
P1: When `TaskRunnerProvider::spawn` returns `Err`, the caller still receives a successful `started` result while the operation remains active forever and no indexing occurs. Cleaning up the operation and propagating the spawn error keeps polling state and the API result truthful.</violation>
</file>

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

<violation number="1" location="crates/mcb-providers/src/fs/mod.rs:17">
P3: The new local provider and its distributed-slice registration have no automated coverage, leaving success/error behavior and `"local"` resolution vulnerable to silent regressions. Temp-directory tests for all three methods plus one registry-resolution test would cover this integration boundary.</violation>

<violation number="2" location="crates/mcb-providers/src/fs/mod.rs:61">
P1: Indexing can stall a Tokio executor worker because every async provider method performs blocking `std::fs` I/O directly. Using `tokio::fs` or moving the complete synchronous operations into `spawn_blocking` would keep directory traversal and per-file reads off executor workers.</violation>

<violation number="3" location="crates/mcb-providers/src/fs/mod.rs:62">
P2: Filesystem failures are mislabeled as internal errors and flattened into strings, so callers and telemetry cannot identify I/O failures or inspect their source chain. Wrapping each failure with `Error::io_with_source(..., e)` would preserve the intended error semantics and context.</violation>
</file>

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

<violation number="1" location="crates/mcb-providers/src/events/tokio.rs:52">
P1: A configured event-bus capacity of `0` now panics during provider resolution when `broadcast::channel(0)` is constructed. Since capacity is externally configurable and represented as an unrestricted `usize`, the factory should reject zero with its `Result` rather than allowing startup to abort; parsing errors should likewise be returned instead of silently falling back.</violation>
</file>

<file name="crates/mcb-domain/src/entities/api_key.rs">

<violation number="1" location="crates/mcb-domain/src/entities/api_key.rs:48">
P1: An API key can be persisted under one organization while referencing a user from another, because these independent foreign keys validate existence but not the user/org relationship. Consider enforcing `(user_id, org_id)` against the user's organization (or deriving `org_id` through `users`) to preserve the tenant boundary.</violation>
</file>

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

<violation number="1" location="crates/mcb-providers/src/task/mod.rs:35">
P2: Calling this provider outside an entered Tokio runtime panics instead of returning the `Err` promised by `TaskRunnerProvider::spawn`, bypassing the caller's failure handling. Using `Handle::try_current()` preserves the trait contract and lets `indexing_service` log the spawn failure.</violation>
</file>

<file name="crates/mcb-providers/src/database/sqlite/provider.rs">

<violation number="1" location="crates/mcb-providers/src/database/sqlite/provider.rs:73">
P2: With an in-memory SQLite path, organization CRUD now targets a second empty database and fails with `no such table: organizations`. Reusing the initialized SQLx pool for SeaORM also keeps both repository paths on the same connection configuration.</violation>

<violation number="2" location="crates/mcb-providers/src/database/sqlite/provider.rs:144">
P3: Production composition now switches organization CRUD to the SeaORM branch, but existing org and DI tests still exercise only the executor branch. A provider-level CRUD test using `connect` and `create_org_entity_repository` would cover this newly activated path and its interaction with the shared executor.</violation>
</file>

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

<violation number="1" location="crates/mcb-infrastructure/src/di/provider_resolvers.rs:351">
P2: NATS startup silently ignores the configured `connection_timeout_ms` and `max_reconnect_attempts`: this translation drops both fields, and the NATS registry builder consequently uses default connect options. The resolver/provider configuration contract should carry and apply both documented settings (or remove/reject them) so operational configuration is not accepted as a no-op.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/provider_resolvers.rs:358">
P2: A configured `capacity = 0` now crashes startup when this value reaches `broadcast::channel(0)`. Since the loader currently accepts zero, validating it here (or in config validation) and returning `Error::configuration` would keep invalid event-bus settings on the normal error path.</violation>
</file>

<file name="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs">

<violation number="1" location="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs:12">
P3: Three tests have no assertions and will always pass: print_full_schema_report, detect_schema_only_fields, and report_divergence_mitigation. They print diagnostic info but never fail, which undermines test reliability and gives false confidence. Either add assertions, convert to debug helpers (e.g. #[cfg(debug_assertions)] with a non-test name), or remove them.</violation>

<violation number="2" location="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs:52">
P2: Schema/entity divergences do not fail this test: each detected mismatch is only printed, so CI still reports success (and normally hides the captured output). If these fields are intentionally outstanding, this should be a non-test report; otherwise the collected mismatches should be asserted empty so future drift is actually detected.</violation>

<violation number="3" location="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs:167">
P3: Misleading assertion message: the message claims the labels column should be Text (JSON) but the assertion only checks whether a column named "labels" exists in the schema — it never verifies the type. Update the message or add a type assertion.</violation>

<violation number="4" location="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs:180">
P2: This “sync” check compares `Organization::table_def()` with another call to that exact same method: `Schema::definition()` registers `from_entity!(Organization)`. It therefore passes even if the manually listed schema diverges from the `Organization` fields. An independent expectation (or compile-time/generated field metadata) is needed for this to test entity/schema synchronization; otherwise this test only verifies the aggregator wiring.</violation>

<violation number="5" location="crates/mcb-domain/tests/unit/schema_entity_sync_tests.rs:217">
P2: Index comparison only checks count equality (assert_eq!(trait_indexes.len(), def_indexes.len())), not name or column content. Two different indexes with the same count would pass undetected. Compare each index's name and columns for a thorough sync check.</violation>
</file>

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

<violation number="1" location="crates/mcb-infrastructure/src/di/vcs.rs:21">
P2: One registered provider that cannot build from an empty/default config prevents startup even when another VCS provider is usable. The composite could retain successfully constructed providers and fail only when none can initialize, while preserving initialization errors for diagnostics.</violation>

<violation number="2" location="crates/mcb-infrastructure/src/di/vcs.rs:40">
P2: Every branch/history/file/diff request now performs a full metadata-producing repository open solely to rediscover the provider, then the concrete operation opens it again. Retaining the provider selected during the initial open/discovery, or adding a cheap capability probe, would avoid repeated history and branch scans.</violation>

<violation number="3" location="crates/mcb-infrastructure/src/di/vcs.rs:42">
P2: When no provider opens a path, callers receive only the last registry provider's error, potentially hiding the actionable failure from the provider that recognized the repository. Preserve/aggregate probe errors, or continue only for an explicit “not this provider” result.</violation>

<violation number="4" location="crates/mcb-infrastructure/src/di/vcs.rs:63">
P2: With multiple registered VCS implementations, `repository_id` is dispatched to the first registry provider rather than the provider that opened this repository. That ordering is unrelated to the repository’s VCS type and can produce an invalid ID or provider-specific failure. Since `VcsRepository` already carries the ID assigned by its opening provider, returning that stored ID avoids cross-provider dispatch here.</violation>

<violation number="5" location="crates/mcb-infrastructure/src/di/vcs.rs:95">
P3: The default provider now reports `dynamic` instead of the actual VCS type (`git` today), so callers can no longer use `vcs_name()` as specified. Forward the concrete name when unambiguous, or evolve the API to report a type per repository for a multi-VCS composite.</violation>
</file>

<file name="crates/mcb-providers/src/database/sqlite/backend.rs">

<violation number="1" location="crates/mcb-providers/src/database/sqlite/backend.rs:28">
P2: A caller can pair handles for different SQLite databases, causing organization CRUD to use `sea_conn` while the rest of the repository uses `executor`. Since the type cannot validate this invariant, consider deriving both handles from one configuration or making `new` crate-private.</violation>
</file>

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

<violation number="1" location="crates/mcb-infrastructure/src/di/bootstrap.rs:306">
P2: Switching the supplied configuration to `provider = "nats"` passes the default empty `nats_url` to the client instead of using the provider's fallback, causing event-bus initialization to fail. Empty optional URLs should be normalized to `None` or rejected with a targeted configuration error before resolution.</violation>
</file>

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

<violation number="1" location="crates/mcb/src/cli/validate.rs:180">
P2: Text-mode validation can now return success even when stdout fails and the report is only partially or not at all emitted, because all of these `writeln!` results are discarded. JSON mode already propagates the same I/O failure. Consider returning `io::Result<()>` from the text printing helpers and propagating it through `execute` (or explicitly handling only `BrokenPipe` if that is the intended exception).</violation>
</file>

<file name="crates/mcb-providers/src/database/sqlite/org_entity_repository.rs">

<violation number="1" location="crates/mcb-providers/src/database/sqlite/org_entity_repository.rs:76">
P3: Production organization CRUD now follows the SeaORM branches, but the repository tests still construct only `new` and leave every new branch unexecuted. A `new_with_sea` CRUD test should cover updates and the existing foreign-key delete behavior against the production connection setup.</violation>

<violation number="2" location="crates/mcb-providers/src/database/sqlite/org_entity_repository.rs:93">
P2: Organization updates through `new_with_sea` can overwrite the immutable `created_at` value, whereas the executor implementation deliberately leaves it unchanged. `org_to_model` marks every field, including `created_at`, as `Set` before `ActiveModel::update`; consider leaving `created_at` as `NotSet` for updates (or using a separate update model) so both repository paths have the same semantics.</violation>
</file>

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

<violation number="1" location="crates/mcb-infrastructure/src/di/database_resolver.rs:38">
P2: Bootstrap now constructs the configured database provider twice, and connects with a different instance from the one used to create repositories. Consider passing the already-resolved provider into the connection path or returning the provider and executor together, avoiding duplicate construction and preserving provider instance state.</violation>
</file>

<file name="crates/mcb-providers/src/database/sqlite/row_convert.rs">

<violation number="1" location="crates/mcb-providers/src/database/sqlite/row_convert.rs:276">
P2: Distinct memberships can receive the same `TeamMemberId` when either source ID contains `:`, because the two strings are concatenated with an ambiguous delimiter. Consider encoding the pair unambiguously (for example, length-prefixing each value or serializing a tuple), while accounting for compatibility with IDs already exposed to clients.</violation>
</file>

<file name="Cargo.toml">

<violation number="1" location="Cargo.toml:323">
P3: This SQLite-only integration pulls SeaORM's default datatype features, adding several unused dependency trees and avoidable compile cost. Disabling defaults and explicitly retaining `macros` keeps the derive-based entities working with a smaller dependency surface.</violation>
</file>

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

<violation number="1" location="crates/mcb-domain/src/ports/providers/fs.rs:11">
P3: A `DirEntry` can represent the impossible state “file and directory,” which makes provider mistakes silently alter discovery because `discover_files` prioritizes `is_dir`. A single file-kind enum (`File`, `Directory`, `Other`) would encode the valid states and make consumers exhaustive.</violation>

<violation number="2" location="crates/mcb-domain/src/ports/providers/fs.rs:22">
P3: `canonicalize_path` is currently dead API, but every filesystem provider or test double must still implement it. Removing it from the port and local adapter would keep the new abstraction limited to operations the application uses.</violation>
</file>

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

<violation number="1" location="crates/mcb-domain/src/macros/di.rs:16">
P3: Getter generation is duplicated across all four parsing arms, so future signature, documentation, or body changes can easily diverge between aliases and list endings. A private `@getter` helper arm plus one comma-separated entry parser would centralize the generated method.</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 now omits both new providers, and the struct-wide allow also hides future missing field docs. Prefer documenting `file_system_provider` and `task_runner_provider` (including the `# Fields` list) instead of suppressing `missing_docs` for the whole struct.</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 async startup panics because this factory calls Handle::block_on while init_app is already running on the Tokio runtime. The NATS construction path should stay async (for example, via an async registry factory) or defer connection initialization; wrapping it in the current runtime's synchronous block_on cannot safely bridge this factory contract.

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 async startup panics because this factory calls `Handle::block_on` while `init_app` is already running on the Tokio runtime. The NATS construction path should stay async (for example, via an async registry factory) or defer connection initialization; wrapping it in the current runtime's synchronous `block_on` cannot safely bridge this factory contract.</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

#[derive(Clone, Debug, PartialEq, Eq, sea_orm::DeriveEntityModel)]
#[sea_orm(table_name = $table)]
pub struct Model {
#[sea_orm(primary_key)]

@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: Organization inserts through the normal SqliteBackend/SeaORM path use a generated String PK that SeaORM treats as auto-incrementing, although canonical organizations.id is not auto-incremented. Mark this PK auto_increment = false (or parameterize it) so ActiveModel insert semantics match the schema.

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

<comment>Organization inserts through the normal `SqliteBackend`/SeaORM path use a generated String PK that SeaORM treats as auto-incrementing, although canonical `organizations.id` is not auto-incremented. Mark this PK `auto_increment = false` (or parameterize it) so ActiveModel insert semantics match the schema.</comment>

<file context>
@@ -0,0 +1,51 @@
+        #[derive(Clone, Debug, PartialEq, Eq, sea_orm::DeriveEntityModel)]
+        #[sea_orm(table_name = $table)]
+        pub struct Model {
+            #[sea_orm(primary_key)]
+            pub $first: $first_ty,
+            $( pub $f: $ty ),*
</file context>
Suggested change
#[sea_orm(primary_key)]
#[sea_orm(primary_key, auto_increment = false)]
Fix with cubic

("org_id", "organizations", "id"),
],
unique_constraints: [
["org_id", "email"],

@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: Upgraded databases still permit duplicate (org_id, email) rows and orphaned org_ids because these declarations only affect newly created tables, while migrate_and_verify_schema validates columns only. Consider migrating/verifying constraints or forcing a controlled rebuild before accepting the existing schema.

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

<comment>Upgraded databases still permit duplicate `(org_id, email)` rows and orphaned `org_id`s because these declarations only affect newly created tables, while `migrate_and_verify_schema` validates columns only. Consider migrating/verifying constraints or forcing a controlled rebuild before accepting the existing schema.</comment>

<file context>
@@ -50,3 +50,27 @@ pub enum UserRole {
+        ("org_id", "organizations", "id"),
+    ],
+    unique_constraints: [
+        ["org_id", "email"],
+    ],
+);
</file context>
Fix with cubic

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

@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: When TaskRunnerProvider::spawn returns Err, the caller still receives a successful started result while the operation remains active forever and no indexing occurs. Cleaning up the operation and propagating the spawn error keeps polling state and the API result truthful.

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 305:

<comment>When `TaskRunnerProvider::spawn` returns `Err`, the caller still receives a successful `started` result while the operation remains active forever and no indexing occurs. Cleaning up the operation and propagating the spawn error keeps polling state and the API result truthful.</comment>

<file context>
@@ -286,10 +298,14 @@ impl IndexingServiceInterface for IndexingServiceImpl {
             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);
+        }
</file context>
Suggested change
if let Err(e) = self.task_runner_provider.spawn(task) {
warn!("Failed to spawn indexing background task: {}", e);
}
if let Err(e) = self.task_runner_provider.spawn(task) {
self.indexing_ops.complete_operation(&operation_id);
return Err(e);
}
Fix with cubic

@@ -0,0 +1,73 @@
use std::path::{Path, PathBuf};

@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: Indexing can stall a Tokio executor worker because every async provider method performs blocking std::fs I/O directly. Using tokio::fs or moving the complete synchronous operations into spawn_blocking would keep directory traversal and per-file reads off executor workers.

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

<comment>Indexing can stall a Tokio executor worker because every async provider method performs blocking `std::fs` I/O directly. Using `tokio::fs` or moving the complete synchronous operations into `spawn_blocking` would keep directory traversal and per-file reads off executor workers.</comment>

<file context>
@@ -0,0 +1,73 @@
+#[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())))
+    }
</file context>
Fix with cubic

@@ -0,0 +1,73 @@
use std::path::{Path, PathBuf};

@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 local provider and its distributed-slice registration have no automated coverage, leaving success/error behavior and "local" resolution vulnerable to silent regressions. Temp-directory tests for all three methods plus one registry-resolution test would cover this integration boundary.

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

<comment>The new local provider and its distributed-slice registration have no automated coverage, leaving success/error behavior and `"local"` resolution vulnerable to silent regressions. Temp-directory tests for all three methods plus one registry-resolution test would cover this integration boundary.</comment>

<file context>
@@ -0,0 +1,73 @@
+}
+
+#[linkme::distributed_slice(FILE_SYSTEM_PROVIDERS)]
+static LOCAL_FILE_SYSTEM_PROVIDER: FileSystemProviderEntry = FileSystemProviderEntry {
+    name: "local",
+    description: "Local file-system provider",
</file context>
Fix with cubic

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DirEntry {
pub path: PathBuf,
pub is_file: bool,

@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: A DirEntry can represent the impossible state “file and directory,” which makes provider mistakes silently alter discovery because discover_files prioritizes is_dir. A single file-kind enum (File, Directory, Other) would encode the valid states and make consumers exhaustive.

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

<comment>A `DirEntry` can represent the impossible state “file and directory,” which makes provider mistakes silently alter discovery because `discover_files` prioritizes `is_dir`. A single file-kind enum (`File`, `Directory`, `Other`) would encode the valid states and make consumers exhaustive.</comment>

<file context>
@@ -0,0 +1,23 @@
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct DirEntry {
+    pub path: PathBuf,
+    pub is_file: bool,
+    pub is_dir: bool,
+}
</file context>
Fix with cubic

@@ -0,0 +1,41 @@
/// Generates `Arc`-cloning getter methods for DI container fields.

@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: Getter generation is duplicated across all four parsing arms, so future signature, documentation, or body changes can easily diverge between aliases and list endings. A private @getter helper arm plus one comma-separated entry parser would centralize the generated method.

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 16:

<comment>Getter generation is duplicated across all four parsing arms, so future signature, documentation, or body changes can easily diverge between aliases and list endings. A private `@getter` helper arm plus one comma-separated entry parser would centralize the generated method.</comment>

<file context>
@@ -0,0 +1,41 @@
+        $crate::arc_getters! { $($rest)* }
+    };
+
+    ($name:ident : $ty:ty => $field:ident $(= $impl:ty)? $(,)?) => {
+        #[doc = concat!("Get `", stringify!($name), "`.")]
+        #[must_use]
</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 now omits both new providers, and the struct-wide allow also hides future missing field docs. Prefer documenting file_system_provider and task_runner_provider (including the # Fields list) instead of suppressing missing_docs for the whole struct.

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 now omits both new providers, and the struct-wide allow also hides future missing field docs. Prefer documenting `file_system_provider` and `task_runner_provider` (including the `# Fields` list) instead of suppressing `missing_docs` for the whole struct.</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


async fn read_dir_entries(&self, path: &Path) -> std::result::Result<Vec<DirEntry>, Error>;

async fn canonicalize_path(&self, path: &Path) -> std::result::Result<PathBuf, Error>;

@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: canonicalize_path is currently dead API, but every filesystem provider or test double must still implement it. Removing it from the port and local adapter would keep the new abstraction limited to operations the application uses.

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

<comment>`canonicalize_path` is currently dead API, but every filesystem provider or test double must still implement it. Removing it from the port and local adapter would keep the new abstraction limited to operations the application uses.</comment>

<file context>
@@ -0,0 +1,23 @@
+
+    async fn read_dir_entries(&self, path: &Path) -> std::result::Result<Vec<DirEntry>, Error>;
+
+    async fn canonicalize_path(&self, path: &Path) -> std::result::Result<PathBuf, Error>;
+}
</file context>
Fix with cubic

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants